mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
import type {
|
|
BackgroundRemovalProgress,
|
|
BackgroundRemovalResult,
|
|
} from "./editor/propertyPanelTypes";
|
|
|
|
const MEDIA_JOB_RECONNECT_TIMEOUT_MS = 15_000;
|
|
const ABSOLUTE_OR_ROOT_SOURCE_RE = /^(?:[a-z][a-z0-9+.-]*:|\/)/i;
|
|
|
|
function parseSerializedColorGrading(value: string): { lut?: { src?: unknown } } | null {
|
|
try {
|
|
return JSON.parse(value) as { lut?: { src?: unknown } } | null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function readLutSource(value: string | null): string {
|
|
const src = value ? parseSerializedColorGrading(value)?.lut?.src : null;
|
|
return typeof src === "string" ? src.trim() : "";
|
|
}
|
|
|
|
export function hasRelativeLutSource(value: string | null): boolean {
|
|
const src = readLutSource(value);
|
|
return src !== "" && !ABSOLUTE_OR_ROOT_SOURCE_RE.test(src);
|
|
}
|
|
|
|
function parseProgressEvent(event: Event): BackgroundRemovalProgress | Error {
|
|
try {
|
|
return JSON.parse((event as MessageEvent).data) as BackgroundRemovalProgress;
|
|
} catch {
|
|
return new Error("Invalid background-removal progress event");
|
|
}
|
|
}
|
|
|
|
function getCompleteProgressResult(
|
|
progress: BackgroundRemovalProgress,
|
|
): BackgroundRemovalResult | Error {
|
|
if (!progress.outputPath) return new Error("Background removal finished without an output path");
|
|
return {
|
|
outputPath: progress.outputPath,
|
|
backgroundOutputPath: progress.backgroundOutputPath,
|
|
provider: progress.provider,
|
|
};
|
|
}
|
|
|
|
function getTerminalProgressResult(
|
|
progress: BackgroundRemovalProgress,
|
|
): BackgroundRemovalResult | Error | null {
|
|
switch (progress.status) {
|
|
case "complete":
|
|
return getCompleteProgressResult(progress);
|
|
case "failed":
|
|
return new Error(progress.error || "Background removal failed");
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function waitForMediaJob(
|
|
jobId: string,
|
|
onProgress?: (progress: BackgroundRemovalProgress) => void,
|
|
signal?: AbortSignal,
|
|
): Promise<BackgroundRemovalResult> {
|
|
return new Promise((resolve, reject) => {
|
|
if (signal?.aborted) {
|
|
reject(new DOMException("Background removal was cancelled", "AbortError"));
|
|
return;
|
|
}
|
|
const events = new EventSource(`/api/media-jobs/${encodeURIComponent(jobId)}/progress`);
|
|
let settled = false;
|
|
let reconnectTimer: number | null = null;
|
|
|
|
const clearReconnectTimer = () => {
|
|
if (reconnectTimer === null) return;
|
|
window.clearTimeout(reconnectTimer);
|
|
reconnectTimer = null;
|
|
};
|
|
const finish = (callback: () => void) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
clearReconnectTimer();
|
|
signal?.removeEventListener("abort", handleAbort);
|
|
events.close();
|
|
callback();
|
|
};
|
|
const finishReject = (error: Error) => finish(() => reject(error));
|
|
const finishResolve = (result: BackgroundRemovalResult) => finish(() => resolve(result));
|
|
const handleAbort = () => {
|
|
finishReject(new DOMException("Background removal was cancelled", "AbortError"));
|
|
};
|
|
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
|
|
// fallow-ignore-next-line complexity
|
|
events.addEventListener("progress", (event) => {
|
|
const progress = parseProgressEvent(event);
|
|
if (progress instanceof Error) {
|
|
finishReject(progress);
|
|
return;
|
|
}
|
|
clearReconnectTimer();
|
|
onProgress?.(progress);
|
|
const terminalResult = getTerminalProgressResult(progress);
|
|
if (!terminalResult) return;
|
|
if (terminalResult instanceof Error) {
|
|
finishReject(terminalResult);
|
|
} else {
|
|
finishResolve(terminalResult);
|
|
}
|
|
});
|
|
events.onopen = clearReconnectTimer;
|
|
events.onerror = () => {
|
|
if (events.readyState === EventSource.CLOSED) {
|
|
finishReject(new Error("Lost connection to background-removal job"));
|
|
return;
|
|
}
|
|
if (reconnectTimer === null) {
|
|
reconnectTimer = window.setTimeout(() => {
|
|
finishReject(new Error("Lost connection to background-removal job"));
|
|
}, MEDIA_JOB_RECONNECT_TIMEOUT_MS);
|
|
}
|
|
};
|
|
});
|
|
}
|