Archilyzer · Source

archilyzer

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

commit 3ceadf7606a69ca9b95e9a7a00622c2344e3bce9
parent 02318dfc0e426f2bc020b0c5ffb4ec5fed9e92f6
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri, 25 Sep 2026 18:47:40 -0400

common: a new video dir rebases the channel's title memo instead of dropping it

The source-3 title memo was keyed by data/'s mtime and dropped whole when
it changed, so every new dir a download lane added re-read every
metadata.info.json on the channel (8,049 on the-quartering-rumble). It is
now rebased: one readdir keeps the titles of the dirs still present and
only new dirs are read; a removed dir's title goes with it (release 8
review, V). videoTitles.test.ts: the memo case asserts 1 read for a new
dir and a fall-through for a removed one.

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

Diffstat:
Mcommon/controller/videoTitles.test.ts | 28++++++++++++++++++++++------
Mcommon/controller/videoTitles.ts | 25++++++++++++++++++++++---
2 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/common/controller/videoTitles.test.ts b/common/controller/videoTitles.test.ts @@ -289,22 +289,38 @@ test("metadata titles are memoized per channel until data/ changes", async () => assert.equal(second.get("b")?.title, "Bravo"); assert.equal(videoTitleMetadataReadCount() - r1, 0); - // A new video dir changes data/'s mtime: the channel's memo is dropped and - // every id is read again ("a" now has no file, so no title). + // A new video dir changes data/'s mtime: the memo is REBASED — the dirs + // still there keep their titles and only the new one is read (release 8 + // review, V). "a"'s dir is still there, so its memoized title stands. await writeInfo(paths, "c", { id: "c", title: "Charlie" }); const r2 = videoTitleMetadataReadCount(); const third = await readChannelVideoTitles(paths, SLUG, ["a", "b", "c"]); - assert.equal(third.has("a"), false); + assert.equal(third.get("a")?.title, "Alpha"); + assert.equal(third.get("b")?.title, "Bravo"); assert.equal(third.get("c")?.title, "Charlie"); - assert.equal(videoTitleMetadataReadCount() - r2, 3); + assert.equal(videoTitleMetadataReadCount() - r2, 1); + + // A REMOVED dir takes its title with it: the rebase keeps only the dirs + // still present, so "a" falls through, is read, and has no title. + await rm(path.join(dataDir, "a"), { recursive: true, force: true }); + // Two changes inside one filesystem timestamp tick would share an mtime; + // pin a distinct one so the rebase certainly fires. + const later = new Date("2026-01-02T00:00:00Z"); + await utimes(dataDir, later, later); + const r3 = videoTitleMetadataReadCount(); + const fourth = await readChannelVideoTitles(paths, SLUG, ["a", "b", "c"]); + assert.equal(fourth.has("a"), false); + assert.equal(fourth.get("c")?.title, "Charlie"); + assert.equal(videoTitleMetadataReadCount() - r3, 1); // A miss is not memoized: a title that lands later is found. + await mkdir(path.join(dataDir, "a"), { recursive: true }); await writeFile( path.join(dataDir, "a", "metadata.info.json"), JSON.stringify({ id: "a", title: "Alpha again" }), ); - const fourth = await readChannelVideoTitles(paths, SLUG, ["a"]); - assert.equal(fourth.get("a")?.title, "Alpha again"); + const fifth = await readChannelVideoTitles(paths, SLUG, ["a"]); + assert.equal(fifth.get("a")?.title, "Alpha again"); } finally { resetVideoTitleMemo(); await rm(dir, { recursive: true, force: true }); diff --git a/common/controller/videoTitles.ts b/common/controller/videoTitles.ts @@ -29,7 +29,7 @@ // invariant). import { existsSync } from "node:fs"; -import { open as openFile, stat } from "node:fs/promises"; +import { open as openFile, readdir, stat } from "node:fs/promises"; import path from "node:path"; import { open } from "lmdb"; import type { Paths } from "../lib/paths"; @@ -156,7 +156,10 @@ async function readMetadataTitle(videoDir: string): Promise<string | null> { // 8,049 such dirs: 5,024 ms cold / 224 ms warm per render, measured 2026-09-25. // // Per channel, keyed by the `data/` directory's mtime: adding or removing a -// video dir changes it and drops the channel's entry. Rewriting a +// video dir changes it, and the entry is then REBASED rather than dropped — the +// titles of the dirs still there carry over (one readdir, no file reads), and +// only the new dirs are read (release 8 review, V: a busy download lane adds a +// dir every few minutes, and each one used to re-read all 8,049). Rewriting a // metadata.info.json INSIDE an existing dir does not — accepted, because a // video's title does not change after download. Misses (no file yet, e.g. a dir // mid-download) are NOT memoized, so a title that lands later is picked up. @@ -196,8 +199,24 @@ async function memoForChannel( } const memo = titleMemo(); let entry = memo.get(key); - if (!entry || entry.dataDirMtimeMs !== mtimeMs) { + if (!entry) { entry = { dataDirMtimeMs: mtimeMs, titles: new Map() }; + } else if (entry.dataDirMtimeMs !== mtimeMs) { + // Keep the titles of the dirs that are still there; a removed dir's title + // goes with it, so a re-listed id falls through to the other sources. + let present: Set<string> | null = null; + try { + present = new Set(await readdir(dataDir)); + } catch { + present = null; + } + const kept = new Map<string, string>(); + if (present) { + for (const [id, title] of entry.titles) { + if (present.has(id)) kept.set(id, title); + } + } + entry = { dataDirMtimeMs: mtimeMs, titles: kept }; } // Re-insert so iteration order is least-recently-used first. memo.delete(key);