From 817eaff8b1699dbc9cdab6685321b62f1eebe05d Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Mon, 18 May 2026 23:22:26 -0400 Subject: [PATCH] feat: add getMediaInfo to executor.js using ffprobe JSON output Exposes video stream fps/codec/resolution and container duration/size so the proxy worker can populate asset metadata after transcoding. --- services/worker/src/ffmpeg/executor.js | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/services/worker/src/ffmpeg/executor.js b/services/worker/src/ffmpeg/executor.js index 1da4c5f..4a0501a 100644 --- a/services/worker/src/ffmpeg/executor.js +++ b/services/worker/src/ffmpeg/executor.js @@ -36,6 +36,41 @@ export const getMediaDuration = async (inputPath) => { return parseFloat(stdout.trim()); }; +/** + * Return structured media metadata for an input file. + * Result shape: + * { fps, codec, resolution, durationMs, fileSizeBytes } + * Any field may be null if ffprobe cannot determine it. + */ +export const getMediaInfo = async (inputPath) => { + const args = [ + '-v', 'quiet', + '-print_format', 'json', + '-show_streams', + '-show_format', + inputPath, + ]; + const { stdout } = await runFFprobe(args); + const info = JSON.parse(stdout); + + const videoStream = (info.streams || []).find(s => s.codec_type === 'video'); + const fmt = info.format || {}; + + let fps = null; + if (videoStream?.r_frame_rate) { + const [num, den] = videoStream.r_frame_rate.split('/').map(Number); + if (den > 0) fps = Math.round((num / den) * 1000) / 1000; + } + + return { + fps, + codec: videoStream?.codec_name || null, + resolution: videoStream ? `${videoStream.width}x${videoStream.height}` : null, + durationMs: fmt.duration ? Math.round(parseFloat(fmt.duration) * 1000) : null, + fileSizeBytes: fmt.size ? parseInt(fmt.size, 10) : null, + }; +}; + export const extractFrameAtTime = async (inputPath, outputPath, timeCode) => { const args = [ '-ss', timeCode,