/** * facerestoreClient.ts — API client for the Face Restoration feature. * * Completely independent of client.ts, upscaleClient.ts, bgremoveClient.ts, * and objectRemovalClient.ts. * Do not import from or share state with any other feature's client. * * URL note * -------- * The backend stores result paths relative to STORAGE_DIR * (e.g. "/facerestore/{folder}/result.jpg"). * The FastAPI StaticFiles mount is at /storage, so the full URL is: * http://localhost:8000/storage/facerestore/{folder}/result.jpg * Use buildFaceRestoreUrl() to convert a stored relative path to a usable URL. */ export const FACERESTORE_API_BASE = import.meta.env.VITE_API_BASE ?? "http://localhost:8000"; /** * Convert a relative result path (e.g. "/facerestore/{folder}/result.jpg") * to the full URL served by the FastAPI /storage StaticFiles mount. */ export function buildFaceRestoreUrl(relPath: string): string { return `${FACERESTORE_API_BASE}/storage${relPath}`; } // --------------------------------------------------------------------------- // Upload // --------------------------------------------------------------------------- /** * POST /facerestore/upload — submit an image for face restoration. * * The backend applies EXIF correction, persists the image, and immediately * kicks off CodeFormer + ArcFace identity-safeguarded restoration. * Returns job_id; poll getFaceRestoreResult() to track progress. */ export type FidelityPreset = "preserve" | "balanced" | "sharpen"; export async function uploadForFaceRestore( file: File, fidelityPreset: FidelityPreset = "balanced", ): Promise<{ job_id: string }> { const formData = new FormData(); formData.append("file", file); formData.append("fidelity_preset", fidelityPreset); const response = await fetch(`${FACERESTORE_API_BASE}/facerestore/upload`, { method: "POST", body: formData, }); if (!response.ok) { let detail = `HTTP ${response.status}`; try { const json = await response.json(); if (json?.detail) detail = json.detail; } catch { /* ignore */ } throw new Error(detail); } return response.json() as Promise<{ job_id: string }>; } // --------------------------------------------------------------------------- // Result polling // --------------------------------------------------------------------------- export interface FaceRestoreResult { job_id: string; status: "pending" | "processing" | "completed" | "failed"; result_image_url: string | null; // relative path — use buildFaceRestoreUrl() error_message: string | null; faces_detected: number; faces_restored: number; faces_rejected: number; } /** * GET /facerestore/result/{job_id} — poll the current status of a face * restoration job. */ export async function getFaceRestoreResult( jobId: string, ): Promise { const response = await fetch( `${FACERESTORE_API_BASE}/facerestore/result/${jobId}`, ); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json() as Promise; }