diff --git a/services/worker/src/workers/conform.js b/services/worker/src/workers/conform.js index 77cb3fc..07cbc3e 100644 --- a/services/worker/src/workers/conform.js +++ b/services/worker/src/workers/conform.js @@ -1,6 +1,7 @@ import { join } from 'path'; import { unlink, writeFile, mkdir, rm } from 'fs/promises'; import { tmpdir } from 'os'; +import { Queue } from 'bullmq'; import { query } from '../db/client.js'; import { downloadFromS3, uploadToS3 } from '../s3/client.js'; import { trimSegment, concatSegments, runFFmpeg, getMediaInfo } from '../ffmpeg/executor.js'; @@ -9,6 +10,19 @@ import { XMLParser } from 'fast-xml-parser'; const S3_BUCKET = process.env.S3_BUCKET || 'wild-dragon'; +// Used to queue a proxy build for the conformed output so the library / +// asset viewer has a browser-playable H.264 preview. Without this the +// browser hits MEDIA_ERR_SRC_NOT_SUPPORTED on ProRes / DNxHR outputs. +const parseRedisUrl = (url) => { + try { + const parsed = new URL(url); + return { host: parsed.hostname, port: parseInt(parsed.port, 10) || 6379 }; + } catch { return { host: 'localhost', port: 6379 }; } +}; +const proxyQueue = new Queue('proxy', { + connection: parseRedisUrl(process.env.REDIS_URL || 'redis://queue:6379'), +}); + const xmlParser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_', @@ -353,10 +367,30 @@ export const conformWorker = async (job) => { ] ); - await job.updateProgress(100); - console.log(`[conform] Job ${jobId} complete → asset ${assetRes.rows[0].id}`); + const newAssetId = assetRes.rows[0].id; - return { jobId, outputKey, assetId: assetRes.rows[0].id }; + // Queue a proxy build so the library has a browser-playable H.264 file. + // ProRes / DNxHR masters don't decode in HTML5 video; without this step + // the asset shows MEDIA_ERR_SRC_NOT_SUPPORTED in the player. Mirror the + // ingest pipeline's pattern (services/mam-api/src/routes/assets.js). + try { + const generatedProxyKey = `proxies/${newAssetId}.mp4`; + await proxyQueue.add('generate', { + assetId: newAssetId, + inputKey: outputKey, + outputKey: generatedProxyKey, + }); + console.log(`[conform] queued proxy build for ${newAssetId}`); + } catch (e) { + // Don't fail the conform job if the proxy queue is unreachable — + // the asset still exists, an operator can retrigger the proxy. + console.warn(`[conform] failed to queue proxy for ${newAssetId}: ${e.message}`); + } + + await job.updateProgress(100); + console.log(`[conform] Job ${jobId} complete → asset ${newAssetId}`); + + return { jobId, outputKey, assetId: newAssetId }; } catch (error) { console.error(`[conform] Error in job ${jobId}:`, error);