Archilyzer · Source

archilyzer

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

commit 195ac4c3e440459c37feb8ff677474c1dd2d5191
parent 1ab9eaf9a780135271a6d2ebfee73b0cb2607ddb
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sun, 20 Sep 2026 03:10:36 -0400

download filter: no metadata is not a verdict — fail open so the failure is classified

The title filter used to fail CLOSED on a null prefetch: no metadata, no match,
so skip. But a prefetch returns nothing for batch-level reasons far more often
than per-video ones — a 429, a bot check, a dropped connection — and calling
those "skipped-filtered" was actively harmful. The video never reached the real
attempt, so `classifyDownloadFailure` never ran, `onPlatformBackoff` never
fired, and abort-on-error never tripped (runYtdlp.ts ~937 vs ~944). A
rate-limited channel would walk its whole playlist "filtering" every video
while the source refused every request, and record a cooldown for none of it.

Failing open costs nothing the filter cares about: the real attempt runs and
fails for the reason it actually failed, and the filter still decides on the
next pass, when there is something to decide on.

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

Diffstat:
Mcommon/lib/downloadFilters.test.ts | 31++++++++++++++++++++++++-------
Mcommon/lib/downloadFilters.ts | 30++++++++++++++++--------------
2 files changed, 40 insertions(+), 21 deletions(-)

diff --git a/common/lib/downloadFilters.test.ts b/common/lib/downloadFilters.test.ts @@ -172,14 +172,31 @@ test("registry: invalid regex -> inert filter, exactly one log line", () => { assert.match(lines[0], /INERT/); }); -test("registry: null metadata with a filter configured -> retryable skip", () => { - const d = evaluateDownloadFilters( - ctx({ channelConfig: withFilter("guest"), metadata: null }), +test("registry: null metadata is NOT a filter decision (fail open)", () => { + // A prefetch returns nothing for batch-level reasons — 429, bot check, + // network — far more often than per-video ones. Calling that a filter skip + // stopped the video from ever reaching the real attempt, so its failure was + // never classified and no per-platform backoff was ever recorded. + assert.equal( + evaluateDownloadFilters( + ctx({ channelConfig: withFilter("guest"), metadata: null }), + ), + null, + ); + // Including when the filter would have rejected it on its title: we do not + // have the title, so there is nothing to reject it on. + assert.equal( + evaluateDownloadFilters( + ctx({ + channelConfig: { + ...BASE_CONFIG, + downloadFilter: { include: "guest", includeLivestreams: true }, + }, + metadata: null, + }), + ), + null, ); - assert.ok(d); - assert.equal(d.skip, true); - assert.equal(d.filter, "titleFilter"); - assert.match(d.reason, /failing closed/); }); test("registry: null metadata with NO filter -> no skip (fail-open)", () => { diff --git a/common/lib/downloadFilters.ts b/common/lib/downloadFilters.ts @@ -203,12 +203,21 @@ export function titleFilterReason( // a video the operator doesn't want is declined on its own terms rather than // being classified as a live-stream retry. // -// FAILS CLOSED on missing metadata: a prefetch that produced nothing cannot be -// matched, and downloading it anyway would defeat the filter on exactly the -// videos a flaky extractor hides. The skip is an ORDINARY retryable one (it -// lands in the existing skippedByFilter bucket and the next run tries again) — -// nothing here is terminal, because nothing here decides what is settled. The -// settled set is derived from the metadata-scan store. +// FAILS OPEN on missing metadata, like skipLive, and that is a correction. +// +// It used to fail closed — no metadata, no match, so skip. But a prefetch +// returns nothing for BATCH-LEVEL reasons far more often than for per-video +// ones: a 429, a bot check, a network drop. Turning those into +// `skipped-filtered` was actively harmful. The video never reached the real +// attempt, so its failure was never classified (runYtdlp.ts ~937 vs ~944): +// no `classifyDownloadFailure`, no `onPlatformBackoff`, no abort-on-error. A +// rate-limited channel would walk its entire playlist "filtering" every video +// while the source refused every request, and record a cooldown for none of it. +// +// Returning null costs nothing: the real download attempt runs, fails for the +// reason it actually failed, and the existing machinery does its job. The +// filter is not bypassed either — the download writes metadata, and the next +// pass (or the metadata scan) decides with something to decide on. const titleFilter: DownloadFilter = { name: "titleFilter", evaluate(ctx) { @@ -229,14 +238,7 @@ const titleFilter: DownloadFilter = { return null; } const m = ctx.metadata; - if (!m) { - return { - skip: true, - filter: "titleFilter", - reason: - "no metadata to match the download filter against (failing closed)", - }; - } + if (!m) return null; // fail open — see above if (!titleFilterRejects(compiled, m)) return null; return { skip: true,