From f408e4dd6f0bb715922780246d63f714dd9e1252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 27 Jul 2026 15:05:14 +0000 Subject: [PATCH] music indexer: pick video poster via ffmpeg thumbnail filter (dodge black frames) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixed ~10% frame grab could land on a black/near-black frame for clips that fade in from black (or on a title card). Keep the ~10% seek to skip intros, but select the frame with `thumbnail=n=300` — ffmpeg picks the most representative frame from the batch, which avoids uniform/black frames. Verified on a fade-from-black video: luma ~122 (vs 0 at t=0), and it dodges the fade even when seeking from the start. Co-Authored-By: Claude Opus 4.8 --- src/servers/sidecar/music/indexer.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/servers/sidecar/music/indexer.ts b/src/servers/sidecar/music/indexer.ts index e53a64d4..ddc9fba3 100644 --- a/src/servers/sidecar/music/indexer.ts +++ b/src/servers/sidecar/music/indexer.ts @@ -363,7 +363,11 @@ async function compressCover(srcAbs: string, destAbs: string): Promise } } -/** Grab a representative frame (~10% in, capped at 30s to skip intros) and compress it like a cover. */ +/** + * Poster = a representative frame, compressed like a cover. Seek ~10% in (capped at 30s) to skip + * intros/title cards, then let ffmpeg's `thumbnail` filter pick the most representative frame from the + * next ~300 (avoids black/uniform frames — e.g. a fade-in from black). + */ async function generateVideoPoster(srcAbs: string, destAbs: string, durationSec?: number): Promise { const ts = durationSec && durationSec > 0 ? Math.min(30, Math.max(1, Math.floor(durationSec * 0.1))) : 5; try { @@ -375,10 +379,10 @@ async function generateVideoPoster(srcAbs: string, destAbs: string, durationSec? String(ts), '-i', srcAbs, + '-vf', + `thumbnail=n=300,scale='min(iw,${COVER_MAX_PX})':'min(ih,${COVER_MAX_PX})':force_original_aspect_ratio=decrease`, '-frames:v', '1', - '-vf', - `scale='min(iw,${COVER_MAX_PX})':'min(ih,${COVER_MAX_PX})':force_original_aspect_ratio=decrease`, '-q:v', '5', destAbs,