Archilyzer · Source

archilyzer

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

commit dacea8bc1e3af286de25babd6225055c1c76bf79
parent 62d441921ca3b54fd5303b9c127e3a994f78ab29
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu,  1 Oct 2026 16:17:38 -0400

report-to-video: the teaser segment's key carries the encode's parameters, so a crf, preset or audio change re-encodes it with the clips

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/report-to-video/README.md | 7++++---
Mumtool/report-to-video/build-video.mjs | 17+++++++++++------
Mumtool/report-to-video/chrome-teaser.test.mjs | 13++++++++++---
3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/umtool/report-to-video/README.md b/umtool/report-to-video/README.md @@ -488,9 +488,10 @@ cut: the cut measures −17.7 LUFS integrated, the teaser −19.6. `hits: false` digital silence, as a card's is. **When it is rebuilt.** The segment is re-encoded only when its key — the -frames' render key and the whole sound graph — differs from the one recorded -beside it (`<id>.teaser.json`). Any build that reaches a teaser rebuilds it -when its words, motion or sound changed: a full build, `--only <id>`, and +frames' render key, the whole sound graph and the encode's parameters (`crf`, +`preset`, `audioBitrate`, `audioRate`, `audioChannels`) — differs from the one +recorded beside it (`<id>.teaser.json`). Any build that reaches a teaser +rebuilds it when its words, motion, sound or encoding changed: a full build, `--only <id>`, and **`--chrome-only`**, which builds teaser segments (they are chrome — graphics made from the manifest, nothing fetched) while still rebuilding no clip. `verify-build` checks each teaser's frame count and that its segment was diff --git a/umtool/report-to-video/build-video.mjs b/umtool/report-to-video/build-video.mjs @@ -1116,12 +1116,17 @@ export function teaserEncodeArgs({ framesDir, seconds, render, audio, outPath }) } /** - * A teaser segment's key: its frames' render key and its sound's whole graph - * (every hit's time and parameters, `hits: false`'s silence, the level). A - * segment whose recorded key differs is re-encoded. + * A teaser segment's key: its frames' render key, its sound's whole graph + * (every hit's time and parameters, `hits: false`'s silence, the level) and + * the encode's parameters (`encodeArgs`: crf, preset, the audio's bitrate, + * rate and channels -- the graph says "stereo" whatever `audioChannels` is). + * A segment whose recorded key differs is re-encoded, as every clip is when + * one of those changes. */ -export const teaserSegmentKey = (framesKey, audioGraph) => - createHash("sha256").update(JSON.stringify({ v: 1, frames: framesKey, audio: audioGraph })).digest("hex"); +export const teaserSegmentKey = (framesKey, audioGraph, render) => + createHash("sha256") + .update(JSON.stringify({ v: 2, frames: framesKey, audio: audioGraph, encode: encodeArgs(render) })) + .digest("hex"); /** * Compose and render the teaser (cached by compose-chrome's key), then encode @@ -1142,7 +1147,7 @@ async function buildTeaserSegment(entry, { manifestPath, render, outDir, variant }); const seconds = Number(entry.seconds); const audio = teaserAudioGraph(teaserHits(entry), { seconds, render }); - const key = teaserSegmentKey(r.key, audio); + const key = teaserSegmentKey(r.key, audio, render); const seg = path.join(outDir, "segments", `${entry.id}.mp4`); const recPath = teaserRecordPath(seg); const rec = await readFile(recPath, "utf8").then(JSON.parse, () => null); diff --git a/umtool/report-to-video/chrome-teaser.test.mjs b/umtool/report-to-video/chrome-teaser.test.mjs @@ -225,9 +225,16 @@ test("the cache key: the composed page changes with the words, the segment key w // The sound is in the segment's key: hits on and off are different segments. const on = teaserAudioGraph(teaserHits(FERRET), { seconds: 7, render: RENDER }); const off = teaserAudioGraph(teaserHits({ ...FERRET, hits: false }), { seconds: 7, render: RENDER }); - assert.notEqual(teaserSegmentKey(a.key, on), teaserSegmentKey(a.key, off)); - assert.notEqual(teaserSegmentKey(a.key, on), teaserSegmentKey(b.key, on)); - assert.equal(teaserSegmentKey(a.key, on), teaserSegmentKey(again.key, on)); + const key = (k, g, r = RENDER) => teaserSegmentKey(k, g, r); + assert.notEqual(key(a.key, on), key(a.key, off)); + assert.notEqual(key(a.key, on), key(b.key, on)); + assert.equal(key(a.key, on), key(again.key, on)); + // So are the encode's parameters: a rebuild that re-encodes every clip + // re-encodes the teaser too. + assert.equal(key(a.key, on), key(a.key, on, { ...RENDER })); + for (const change of [{ crf: 30 }, { preset: "veryslow" }, { audioBitrate: "96k" }, { audioChannels: 6 }, { audioRate: 44100 }]) { + assert.notEqual(key(a.key, on), key(a.key, on, { ...RENDER, ...change }), JSON.stringify(change)); + } } finally { rmSync(dir, { recursive: true, force: true }); }