From 532461599b7518f4609002cdb314ff2ce9f3a70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 18 Jul 2026 19:25:24 +0000 Subject: [PATCH] fix(producer): preserve normalized audio mux duration --- .../src/services/distributed/assemble.ts | 24 ++++++++++++------- .../render/stages/assembleStage.test.ts | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/producer/src/services/distributed/assemble.ts b/packages/producer/src/services/distributed/assemble.ts index 91f56ed90..2f98ccef9 100644 --- a/packages/producer/src/services/distributed/assemble.ts +++ b/packages/producer/src/services/distributed/assemble.ts @@ -289,8 +289,11 @@ export async function assemble( } // ── 3. Audio: pad-or-trim then mux ──────────────────────────────────── - let audioForMux: string | null = null; - let preserveAudioPrimingEditList = false; + let normalizedAudio: { + path: string; + preserveAudioPrimingEditList: boolean; + durationSeconds: number; + } | null = null; if (audioPath !== null && existsSync(audioPath)) { const paddedAudioPath = join(workDir, "audio-padded.m4a"); const padTrimResult = await padOrTrimAudioToVideoFrameCount({ @@ -302,8 +305,11 @@ export async function assemble( if (!padTrimResult.success) { throw new Error(`[assemble] audio pad/trim failed: ${padTrimResult.error}`); } - audioForMux = paddedAudioPath; - preserveAudioPrimingEditList = padTrimResult.operation === "trim"; + normalizedAudio = { + path: paddedAudioPath, + preserveAudioPrimingEditList: padTrimResult.operation === "trim", + durationSeconds: padTrimResult.targetDurationSeconds, + }; log.info("[assemble] audio normalized for mux", { operation: padTrimResult.operation, targetDurationSeconds: padTrimResult.targetDurationSeconds, @@ -316,17 +322,17 @@ export async function assemble( // because it operates on a `RenderJob` and emits `updateJobStatus` // payloads — the distributed activity has no job to thread through. const muxOutputPath = - audioForMux !== null ? join(workDir, `mux.${plan.dimensions.format}`) : postConcatPath; - if (audioForMux !== null) { + normalizedAudio !== null ? join(workDir, `mux.${plan.dimensions.format}`) : postConcatPath; + if (normalizedAudio !== null) { const muxResult = await muxVideoWithAudio( postConcatPath, - audioForMux, + normalizedAudio.path, muxOutputPath, abortSignal, { audioCodec: "aac", - preserveAudioPrimingEditList, - durationSeconds: padTrimResult.targetDurationSeconds, + preserveAudioPrimingEditList: normalizedAudio.preserveAudioPrimingEditList, + durationSeconds: normalizedAudio.durationSeconds, }, { num: plan.dimensions.fpsNum, den: plan.dimensions.fpsDen }, ); diff --git a/packages/producer/src/services/render/stages/assembleStage.test.ts b/packages/producer/src/services/render/stages/assembleStage.test.ts index ae0696202..a6d392bac 100644 --- a/packages/producer/src/services/render/stages/assembleStage.test.ts +++ b/packages/producer/src/services/render/stages/assembleStage.test.ts @@ -69,7 +69,7 @@ describe("runAssembleStage audio duration parity", () => { "/tmp/audio.duration-normalized.m4a", "/tmp/output.mp4", undefined, - { audioCodec: "aac", preserveAudioPrimingEditList: true }, + { audioCodec: "aac", preserveAudioPrimingEditList: true, durationSeconds: 1 }, { num: 30, den: 1 }, ); });