From 2e6c79e3605d2e18739ee35f1e03813fe757beac Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Fri, 16 Oct 2026 18:33:09 +0800 Subject: [PATCH] Still lane: size the looped picture to the audio's real length --- server/media-cache.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/media-cache.js b/server/media-cache.js index e945f12..4199594 100644 --- a/server/media-cache.js +++ b/server/media-cache.js @@ -523,11 +523,15 @@ export function createMediaCache({ const niced = (args) => (nice ? run(nice, ['-n', '19', ffmpeg, ...args]) : run(ffmpeg, args)); try { const at = String(Math.min(30, Math.floor(row.duration / 2))); + // The picture must last as long as the audio; -shortest cuts a 1 fps loop + // a few seconds early, so read the audio stream's own length. + const pa = await run(ffprobe, ['-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=duration', '-of', 'csv=p=0', src]); + const seconds = Number(String(pa.stdout).trim()) > 0 ? Number(String(pa.stdout).trim()) : row.duration; const f = await niced(['-v', 'error', '-nostdin', '-y', '-ss', at, '-i', src, '-frames:v', '1', '-vf', 'scale=-2:min(720\\,ih)', frame]); if (f.code !== 0) throw new Error('still frame grab failed: ' + tail(f.stderr)); const r = await niced(['-v', 'error', '-nostdin', '-y', '-loop', '1', '-framerate', '1', '-i', frame, '-i', src, - '-map', '0:v', '-map', '1:a:0', '-t', String(row.duration), '-shortest', + '-map', '0:v', '-map', '1:a:0', '-t', String(seconds), '-c:v', 'libx264', '-preset', 'medium', '-crf', String(transcode.crf), '-tune', 'stillimage', '-profile:v', 'high', '-level:v', '4.0', '-pix_fmt', 'yuv420p', '-g', '600', '-threads', threads, '-c:a', 'copy', '-movflags', '+faststart', '-f', 'mp4', out]);