image-tool / frontend /src /api /facerestoreClient.ts
JunyoungK7's picture
Claude Sonnet 4.6
Deploy to HF Spaces: Docker setup, CPU inference, env-gated config
a3953cb
Raw History Blame Contribute Delete
3.11 kB
/**
* 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<FaceRestoreResult> {
const response = await fetch(
`${FACERESTORE_API_BASE}/facerestore/result/${jobId}`,
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<FaceRestoreResult>;
}