fix(render): scale timeout for long video encodes (#2244)

This commit is contained in:
Miguel Ángel
2026-07-11 18:32:48 -04:00
committed by GitHub
parent 687883124f
commit 9d91c2a23e
2 changed files with 30 additions and 2 deletions
@@ -154,6 +154,24 @@ describe("gif encode args", () => {
});
describe("runEncodeStage config plumbing", () => {
it("scales the encode timeout for long compositions", async () => {
const { runEncodeStage } = await import("./encodeStage.js");
await runEncodeStage(
makeInput({
job: {
...makeInput().job,
duration: 754.8,
},
engineConfig: { ffmpegEncodeTimeout: 600_000 },
}),
);
expect(encodeFramesFromDirMock.mock.calls[0]?.[5]).toEqual({
ffmpegEncodeTimeout: 3_019_200,
});
});
it("prefers engine config supplied by the orchestrator", async () => {
const { runEncodeStage } = await import("./encodeStage.js");
const orchestratorEngineConfig = { ffmpegEncodeTimeout: 54_321 };
@@ -277,6 +277,16 @@ export async function runEncodeStage(input: EncodeStageInput): Promise<EncodeSta
// ── Stage 5: Encode ───────────────────────────────────────────────
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
// ffmpegEncodeTimeout is a total wall-clock cap, not an inactivity timeout.
// A fixed ten-minute cap reliably kills long high-quality disk-frame encodes
// that are still making progress. Preserve larger operator overrides while
// guaranteeing four seconds of encode budget per second of source video.
const scaledEncodeTimeout = Math.ceil((job.duration ?? 0) * 4_000);
const videoEngineCfg =
scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout
? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout }
: engineCfg;
const frameExt = needsAlpha ? "png" : "jpg";
const framePattern = `frame_%06d.${frameExt}`;
const encoderOpts = {
@@ -305,7 +315,7 @@ export async function runEncodeStage(input: EncodeStageInput): Promise<EncodeSta
encoderOpts,
chunkedEncodeSize,
abortSignal,
engineCfg,
videoEngineCfg,
)
: await encodeFramesFromDir(
framesDir,
@@ -313,7 +323,7 @@ export async function runEncodeStage(input: EncodeStageInput): Promise<EncodeSta
videoOnlyPath,
encoderOpts,
abortSignal,
engineCfg,
videoEngineCfg,
);
assertNotAborted();