Archilyzer · Source

archilyzer

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

commit dae7febb3f553f307ead7a3f42e656d99f236ee2
parent 3879dac23914fa1b5628effe60ec921e891da555
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri, 25 Sep 2026 19:24:13 -0400

common: only a filter-declined video skips the between-downloads sleep (review fix)

Review MED: a per_video failure that never got past the prefetch skipped
the sleep, but YouTube's soft block ("This content isn't available, try
again later") classifies as deleted -> per_video, so a soft-blocked run
would fire prefetches back to back into the block. declinedWithoutMediaFetch
now covers skipped-filtered only; classifyDownloadFailure is unchanged.
managedDownloadsSleep.test.ts: the per-video-at-prefetch case now sleeps.

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

Diffstat:
Mcommon/ytdlp/managedDownloadsSleep.test.ts | 23++++++++++-------------
Mcommon/ytdlp/runYtdlp.ts | 42++++++++++++++++++++----------------------
2 files changed, 30 insertions(+), 35 deletions(-)

diff --git a/common/ytdlp/managedDownloadsSleep.test.ts b/common/ytdlp/managedDownloadsSleep.test.ts @@ -114,8 +114,12 @@ test("a video the download filter declined does not sleep", async () => { assert.equal(await sleepsFor([FILTERED, FILTERED, FILTERED]), 0); }); -test("a per-video failure that never reached a media request does not sleep", async () => { - assert.equal(await sleepsFor([MEMBERS_ONLY, FETCHED]), 0); +// Release 9 review: a per-video failure KEEPS the pace, even one that never +// got past the prefetch. YouTube's soft block ("This content isn't available, +// try again later") classifies as deleted → per_video; skipping the sleep there +// would fire prefetches back to back into the block. +test("a per-video failure at the prefetch still sleeps", async () => { + assert.equal(await sleepsFor([MEMBERS_ONLY, FETCHED]), 1); }); test("the last video never sleeps, whatever it was", async () => { @@ -145,24 +149,17 @@ test("declinedWithoutMediaFetch: a chat-only pass is a fetch", () => { assert.equal( declinedWithoutMediaFetch( outcome("skipped-filtered", [PREFETCH, chatPass]), - undefined, ), false, ); assert.equal( - declinedWithoutMediaFetch( - outcome("chat-only", [PREFETCH, chatPass]), - undefined, - ), + declinedWithoutMediaFetch(outcome("chat-only", [PREFETCH, chatPass])), false, ); - assert.equal(declinedWithoutMediaFetch(FILTERED, undefined), true); - assert.equal(declinedWithoutMediaFetch(MEMBERS_ONLY, "per_video"), true); + assert.equal(declinedWithoutMediaFetch(FILTERED), true); + assert.equal(declinedWithoutMediaFetch(MEMBERS_ONLY), false); assert.equal( - declinedWithoutMediaFetch( - outcome("failed", [PREFETCH], "rate_limit"), - "rate_limit", - ), + declinedWithoutMediaFetch(outcome("failed", [PREFETCH], "rate_limit")), false, ); }); diff --git a/common/ytdlp/runYtdlp.ts b/common/ytdlp/runYtdlp.ts @@ -22,7 +22,6 @@ import { EXCLUDED_FROM_DOWNLOAD, classifyDownloadFailure, type Availability, - type DownloadFailureClass, } from "../lib/availability"; import type { DownloadOutcomeRecord } from "../lib/downloadOutcome"; import { @@ -895,19 +894,21 @@ export type ManagedDownloadsDeps = { sleep?: (ms: number, signal: AbortSignal) => Promise<void>; }; -// True when the video ended without a single media request: every attempt it -// made was the n: 0 metadata prefetch (or its cookie retry), and it ended -// either declined by an app-level filter or failed for a reason that belongs -// to the video (members-only, private, removed). Attempts n >= 1 are the real -// fetches — the download attempts and the chat-only pass — and any of them -// means the source was asked for media. +// True when the download filter declined the video before any media request: +// every attempt it made was the n: 0 metadata prefetch (or its cookie retry) +// and it ended `skipped-filtered`. Attempts n >= 1 are the real fetches — the +// download attempts and the chat-only pass (a failed chat pass leaves the +// status skipped-filtered, and still asked). +// +// A per_video FAILURE is deliberately NOT here, even one that never got past +// the prefetch (release 9 review): YouTube's soft block ("This content isn't +// available, try again later") classifies as `deleted` → per_video, so +// skipping the sleep there would fire prefetches back to back into the block. export function declinedWithoutMediaFetch( outcome: DownloadOutcomeRecord, - failureClass: DownloadFailureClass | undefined, ): boolean { if (outcome.attempts.some((a) => a.n >= 1)) return false; - if (outcome.status === "skipped-filtered") return true; - return outcome.status === "failed" && failureClass === "per_video"; + return outcome.status === "skipped-filtered"; } type ManagedRunResult = { @@ -1050,14 +1051,12 @@ export async function runManagedDownloads( // kept on disk, but not a usable download. Count as skipped (not ok, // not a retryable failure) so the batch summary stays honest. skippedCount++; - } - let failureClass: DownloadFailureClass | undefined; - if (outcome.status === "failed") { + } else if (outcome.status === "failed") { failedCount++; // downloadOneManaged classifies against the full stderr tail; fall // back to the truncated attempt error only if it's somehow absent. const lastAttempt = outcome.attempts[outcome.attempts.length - 1]; - failureClass = + const failureClass = outcome.failureClass ?? classifyDownloadFailure( lastAttempt?.error ?? "", @@ -1086,19 +1085,18 @@ export async function runManagedDownloads( processedCount++; const isLast = processedCount >= urls.length; const willAbortLoop = firstFailure !== null && abortOnError; - // THE SLEEP PACES MEDIA FETCHES, so a video that fetched none does not - // pay for one (release 9, B2). On 2026-09-25 paramount-tactical's - // download-missing slept 193 times for 14 archives: every video the - // download filter declined slept 30 s after nothing but its metadata - // prefetch. A real fetch — success or failure, or a chat-only pass — - // still sleeps, and so does every failure that is not per-video (a - // rate limit above all). + // A VIDEO THE FILTER DECLINED DOES NOT PAY FOR A FETCH IT NEVER MADE + // (release 9, B2). On 2026-09-25 paramount-tactical's download-missing + // slept 193 times for 14 archives: every video the download filter + // declined slept 30 s after nothing but its metadata prefetch. Every + // other outcome still sleeps — a real fetch, success or failure, and + // every failure, per-video ones included (see declinedWithoutMediaFetch). if ( sleepSeconds > 0 && !isLast && !willAbortLoop && !opts.signal.aborted && - !declinedWithoutMediaFetch(outcome, failureClass) + !declinedWithoutMediaFetch(outcome) ) { opts.onLog(`Sleeping ${sleepSeconds}s before next download...\n`); await sleep(sleepSeconds * 1000, opts.signal);