From 446a563647ec268bd716f7231b7350b66c96f617 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 15:40:53 -0400 Subject: [PATCH] =?UTF-8?q?fix(worker):=20conform=20=E2=80=94=20write=20Pr?= =?UTF-8?q?oRes/DNxHR=20to=20MOV,=20not=20MP4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The final concat-demux + encode step erred with: [mp4] Could not find tag for codec prores in stream #0, codec not currently supported in container [out#0/mp4] Could not write header (incorrect codec parameters ?) ProRes and DNxHR live in QuickTime (.mov), not MP4. The output path, S3 key, and asset-row filename were all hardcoded to .mp4. Pick the container from the codec: prores / prores_hq / prores_4444 / dnxhr_hq → mov h264 / h265 / anything else → mp4 outputExt is computed once at the top of the worker (before tmpfile creation) and reused for the temp output, the S3 key (jobs//conformed.), and the assets row's filename column. Co-Authored-By: Claude Opus 4.7 --- services/worker/src/workers/conform.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/services/worker/src/workers/conform.js b/services/worker/src/workers/conform.js index 2c4e8c4..77cb3fc 100644 --- a/services/worker/src/workers/conform.js +++ b/services/worker/src/workers/conform.js @@ -85,7 +85,15 @@ export const conformWorker = async (job) => { const tmpDir = tmpdir(); const segmentsDir = join(tmpDir, `segments-${jobId}`); const segmentListPath = join(tmpDir, `segments-${jobId}.txt`); - const outputPath = join(tmpDir, `output-${jobId}.mp4`); + // Container per codec — ProRes / DNxHR live in QuickTime (MOV); MP4 only + // accepts H.264/H.265 and a handful of others. The earlier .mp4 hard-code + // tripped ffmpeg with: + // [mp4] Could not find tag for codec prores in stream #0, + // codec not currently supported in container + const outputExt = + (codec === 'prores' || codec === 'prores_hq' || codec === 'prores_4444' || codec === 'dnxhr_hq') + ? 'mov' : 'mp4'; + const outputPath = join(tmpDir, `output-${jobId}.${outputExt}`); try { let edits = []; @@ -319,7 +327,7 @@ export const conformWorker = async (job) => { ]); await job.updateProgress(85); - const outputKey = `jobs/${jobId}/conformed.mp4`; + const outputKey = `jobs/${jobId}/conformed.${outputExt}`; console.log(`[conform] Uploading output to ${outputKey}`); await uploadToS3(S3_BUCKET, outputKey, outputPath); @@ -329,7 +337,7 @@ export const conformWorker = async (job) => { VALUES ($1, $2, $3, 'video', 'ready', $4, $5, $6, $7, $8, $9) RETURNING id`, [ projectId || null, - `conformed-${seqName.replace(/[^a-z0-9]/gi, '_')}.mp4`, + `conformed-${seqName.replace(/[^a-z0-9]/gi, '_')}.${outputExt}`, `Conformed: ${seqName}`, outputKey, // Normalise the panel's codec id into the canonical name we store on