Archilyzer · Source

archilyzer

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

commit 68bdda4abfc6a81fe8e4d9445fb5489fa5b96716
parent 7370d0bb2ace02bf9efb4043f385247a2a2f9dc6
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sun, 20 Sep 2026 17:49:35 -0400

Make the read route's live counts opt-in

readChannelStat walks every video directory — eleven thousand readdirs on the
largest channel here — and a poll loop asking for a channel's state every few
seconds must not be the thing that hammers the platter. The report's totals
answer the same question from a file, so the walk is behind ?counts=1
(`pnpm ops get channel <slug> --counts`) and the field is named `counts` to say
what it is.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
MRUNNING_IN_DOCKER.md | 4+++-
Meditor/app/api/ops/channel/[slug]/route.ts | 12++++++++++--
Mscripts/archilyzer-ops.mjs | 19+++++++++++++++----
Mscripts/archilyzer-ops.test.mjs | 7+++++++
4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/RUNNING_IN_DOCKER.md b/RUNNING_IN_DOCKER.md @@ -269,7 +269,9 @@ The read side needs no new routes for jobs: `/api/jobs/active`, `/api/jobs/<id>/log`, `/api/scheduler/status` and `/api/auto-queue/status` already exist. `GET /api/ops/channel/<slug>` is the one addition — config, report totals, bucket sizes, priority and, the part no directory listing can -tell you, whether the channel's media is actually **reachable**. +tell you, whether the channel's media is actually **reachable**. Add `--counts` +(`?counts=1`) for the live on-disk counts; it is opt-in because it walks every +video directory, eleven thousand of them on the largest channel here. ### Booting without resuming work diff --git a/editor/app/api/ops/channel/[slug]/route.ts b/editor/app/api/ops/channel/[slug]/route.ts @@ -30,6 +30,12 @@ export const dynamic = "force-dynamic"; // tell an unmounted drive from an empty channel. Every enumerator else swallows // ENOENT on data/ as "no videos", so a script reading counts alone would read an // unmounted platter as a channel that has downloaded nothing. +// +// `?counts=1` ADDS THE LIVE ON-DISK COUNTS, and it is opt-in because +// readChannelStat walks every video directory — eleven thousand readdirs on the +// largest channel here. The report's totals answer the same question from a +// file, and a poll loop asking for them every few seconds must not be the thing +// that hammers the platter. export async function GET( request: Request, { params }: { params: Promise<{ slug: string }> }, @@ -46,9 +52,10 @@ export async function GET( ); } const priority = getSettings().channelPriority; + const wantCounts = new URL(request.url).searchParams.get("counts") === "1"; const [snapshot, stat, media] = await Promise.all([ readChannelSnapshot(paths, slug), - readChannelStat(paths, slug), + wantCounts ? readChannelStat(paths, slug) : Promise.resolve(null), inspectChannelMedia(paths, slug, config), ]); const buckets = snapshot @@ -68,7 +75,8 @@ export async function GET( overrides: overridesOf(priority, slug), rank: rankOf(priority, slug), }, - stat, + // null unless ?counts=1 — see the header. + counts: stat, media, report: snapshot ? { diff --git a/scripts/archilyzer-ops.mjs b/scripts/archilyzer-ops.mjs @@ -9,7 +9,7 @@ // USAGE // // pnpm ops <action> [--json '<body>'] [--wait] [--quiet] -// pnpm ops get channel <slug> +// pnpm ops get channel <slug> [--counts] // pnpm ops list // // ARCHILYZER_EDITOR_URL editor base URL (default http://localhost:3001) @@ -42,7 +42,10 @@ const DEFAULT_URL = "http://localhost:3001"; // an ops API that let a caller assemble arbitrary GET paths would be a proxy, // not an adapter. const GETTERS = { - channel: (slug) => `/api/ops/channel/${encodeURIComponent(slug)}`, + // --counts adds the LIVE on-disk counts, which walk every video directory — + // opt-in for the same reason the route makes it opt-in. + channel: (slug, counts) => + `/api/ops/channel/${encodeURIComponent(slug)}${counts ? "?counts=1" : ""}`, }; const ACTIONS = [ @@ -67,12 +70,15 @@ export function parseArgs(argv) { let json = null; let wait = false; let quiet = false; + let counts = false; for (let i = 0; i < argv.length; i++) { const arg = argv[i]; if (arg === "--wait") { wait = true; } else if (arg === "--quiet") { quiet = true; + } else if (arg === "--counts") { + counts = true; } else if (arg === "--json") { json = argv[++i]; if (json === undefined) { @@ -111,7 +117,12 @@ export function parseArgs(argv) { }; } if (!positional[2]) return { error: `get ${noun}: needs an argument` }; - return { method: "GET", path: GETTERS[noun](positional[2]), wait: false, quiet }; + return { + method: "GET", + path: GETTERS[noun](positional[2], counts), + wait: false, + quiet, + }; } const action = positional[0]; if (!ACTIONS.includes(action)) { @@ -130,7 +141,7 @@ export function parseArgs(argv) { export function usage() { return [ "Usage: pnpm ops <action> [--json '<body>'] [--wait]", - " pnpm ops get channel <slug>", + " pnpm ops get channel <slug> [--counts]", " pnpm ops list", "", `Actions: ${ACTIONS.join(", ")}`, diff --git a/scripts/archilyzer-ops.test.mjs b/scripts/archilyzer-ops.test.mjs @@ -63,3 +63,10 @@ test("get refuses an unknown noun and a missing argument", () => { test("an unknown flag is refused", () => { assert.match(parseArgs(["sync", "--force"]).error, /unknown flag/); }); + +test("get channel --counts asks for the live on-disk counts", () => { + const p = parseArgs(["get", "channel", "x", "--counts"]); + assert.equal(p.path, "/api/ops/channel/x?counts=1"); + // Off by default: the counts walk every video directory. + assert.equal(parseArgs(["get", "channel", "x"]).path, "/api/ops/channel/x"); +});