Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit c0cd64901dde1f8cbab99c335614ce9f2e3e88cf
parent b0debd619e7a475d45a8dbca895ca502b671fd05
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue, 18 Aug 2026 13:15:58 -0400

make-thumb.mjs: record the background, and fix the scorer ffmpeg 9 broke

Two changes to the same file, both about a thumbnail's BACKGROUND.

`bg` is written beside `bgAt`. A timestamp alone does not say which video it
is a timestamp INTO, and nothing else recorded that -- so four accepted covers
are left with a frame nobody can re-cut. The obvious guess, that a cover's
background is its own song video at bgAt, is false for all four (mean abs diff
53-115 on the corner-free centre strip), and mortal-kombat has bgAt=740.8
against a 148s video. The field is optional exactly as corners[].crop is:
absent means "not recorded", not "no background".

And actionScore's `-vsync 0` is now `-fps_mode passthrough`. ffmpeg 9 removed
-vsync, so every invocation died with "Unrecognized option" -- swallowed by the
caller's catch, which scored 0 for every candidate. "Pick the busiest frame"
had quietly become "pick the first frame that is not dark": the comment two
functions up describes this exact failure from the first time it happened, by a
different route. Measured on the yoshi bed, the picker goes from a flat 0.0
across 24 samples to a best of 15.7, and metal-slug and mortal-kombat move off
a FREE PLAY screen and a ROUND 1 card onto actual gameplay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Diffstat:
Mumtool/song/make-thumb.mjs | 15+++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/umtool/song/make-thumb.mjs b/umtool/song/make-thumb.mjs @@ -111,7 +111,13 @@ const meanLuma = (file, sr) => { const actionScore = (file, sr) => { // two frames 0.2s apart, side by side, so one decode gives motion AND colour const raw = execFileSync("ffmpeg", ["-nostdin", "-v", "error", - "-ss", String(sr), "-i", file, "-frames:v", 2, "-vsync", "0", + // -fps_mode, not -vsync: ffmpeg 9 REMOVED -vsync, and the removal was silent + // here because the throw is swallowed by the caller's catch. Every sample + // scored 0, so "pick the busiest frame" quietly became "pick the first frame + // that is not dark" -- the exact failure the comment above describes, back + // again by a different route. A scorer that cannot fail loudly must at least + // be spelled in options the installed ffmpeg still has. + "-ss", String(sr), "-i", file, "-frames:v", 2, "-fps_mode", "passthrough", "-vf", "fps=5,scale=160:90,format=rgb24", "-f", "rawvideo", "-"], { maxBuffer: 1 << 24 }); const N = 160 * 90 * 3; @@ -244,7 +250,12 @@ execFileSync("ffmpeg", ["-nostdin", "-v", "error", "-y", ...inputs, rmSync(tmp, { recursive: true, force: true }); manifest.thumbs[NAME] = { - out: OUT, bgAt: +bgAt.toFixed(2), + // `bg` alongside `bgAt`: a timestamp on its own does not say which video it is + // a timestamp INTO, and nothing else recorded that. Four accepted covers were + // left with a frame nobody could re-cut -- the obvious guess, that a cover's + // background is its own song video, is false for all four. Optional in exactly + // the way `corners[].crop` is: absent means "not recorded", not "no background". + out: OUT, bg: BG, bgAt: +bgAt.toFixed(2), // THE BOX IS RECORDED NOW. It used to be computed and thrown away -- the // script kept only p[3] and p[4] -- so a corner could be cut and never cut // again the same way, which is the state two accepted corners are in today.