feat(render): real progress %, ETA, and frequent preview during AE renders
The render page already displayed progress/ETA/preview — but the node agent never fed real data: aeRender used fake +5%/10s increments, discarded aerender stdout, and pushed a preview only every 30s. (Plus the deployed agent predated even the progress-reporting wiring.) node-agent (aeRender): - Capture aerender stdout; parse "(N):" current frame + "N frames"/"to N" total. - Real percentage when total is known (5–90%, headroom for transcode/upload), else a smooth time-asymptotic estimate that never sticks — message shows the live frame number either way. - Push a preview frame ~every 8s (was 30s) so the box fills in quickly. render-svc: - GET /v1/renders/:id/progress now computes eta_seconds from started_at + progress (linear extrapolation) instead of returning null. frontend: - Thread eta_seconds → status route → render page; page prefers the server ETA and falls back to the client-observed rate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ interface StatusResponse {
|
||||
progressMessage?: string | null;
|
||||
errorMessage?: string | null;
|
||||
previewB64?: string | null;
|
||||
etaSeconds?: number | null;
|
||||
}
|
||||
|
||||
interface ActiveRender {
|
||||
@@ -180,8 +181,11 @@ export default function RenderPage() {
|
||||
setProgressMessage(data.progressMessage ?? `Rendering… ${p}%`);
|
||||
if (data.previewB64) setPreviewB64(data.previewB64);
|
||||
|
||||
// ETA from the observed progress rate.
|
||||
if (p > 0 && p < 100) {
|
||||
// ETA: prefer the server's estimate (elapsed vs. progress); otherwise fall
|
||||
// back to the client-observed progress rate.
|
||||
if (typeof data.etaSeconds === "number" && data.etaSeconds >= 0 && p < 100) {
|
||||
setEtaSec(data.etaSeconds);
|
||||
} else if (p > 0 && p < 100) {
|
||||
const now = Date.now();
|
||||
const base = etaBaseRef.current;
|
||||
if (!base || p < base.p) {
|
||||
|
||||
@@ -33,5 +33,6 @@ export async function GET(_request: Request, context: RouteContext) {
|
||||
progressMessage: job.progress_message,
|
||||
errorMessage: job.error_message,
|
||||
previewB64: job.preview_b64 ?? null,
|
||||
etaSeconds: job.eta_seconds ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ export interface RenderJobRow {
|
||||
error_message: string | null;
|
||||
/** Base64-encoded PNG preview frame pushed by the node agent. Null until first frame arrives. */
|
||||
preview_b64: string | null;
|
||||
/** Estimated seconds remaining (server extrapolation from elapsed + progress). Null until progressing. */
|
||||
eta_seconds: number | null;
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────────
|
||||
@@ -123,6 +125,7 @@ export async function getRenderJob(
|
||||
progress?: number;
|
||||
message?: string;
|
||||
preview_b64?: string | null;
|
||||
eta_seconds?: number | null;
|
||||
} | null;
|
||||
if (!progress) return null;
|
||||
|
||||
@@ -165,6 +168,7 @@ export async function getRenderJob(
|
||||
error_message:
|
||||
status === "failed" ? (progress.message ?? "Render failed") : null,
|
||||
preview_b64: progress.preview_b64 ?? null,
|
||||
eta_seconds: progress.eta_seconds ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user