Archilyzer · Source

archilyzer

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

commit 47e09baa689b6f686a8c68b65aab499c21e680bb
parent 5a5201465b7834370cffeb64bf2420dfb6358eb6
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed,  7 Oct 2026 20:31:32 -0400

fetch-window: a rumble.com window sends -extension_picky 0 on the first try

Every Rumble window was refused for its .tar segments and retried, and each
attempt loads the Rumble page again; Cloudflare 403s a share of those loads,
so the retry doubled the refusals. A rumble.com URL now gets the HLS option
first, and an old upload served progressive is retried once without it.

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

Diffstat:
Mcommon/ytdlp/fetchWindowManaged.test.ts | 26+++++++++++++++++++++++++-
Mcommon/ytdlp/fetchWindowManaged.ts | 33++++++++++++++++++++++++---------
Mumtool/docs/quirks.md | 8+++++++-
3 files changed, 56 insertions(+), 11 deletions(-)

diff --git a/common/ytdlp/fetchWindowManaged.test.ts b/common/ytdlp/fetchWindowManaged.test.ts @@ -1,6 +1,7 @@ // fetchWindowManaged against a fake yt-dlp: Rumble's `.tar` HLS segments are // refused by ffmpeg until the one retry adds -extension_picky 0, and the option -// is never passed on a first try (against a progressive URL it is an error). +// is never passed on a first try (against a progressive URL it is an error) — +// except to a rumble.com URL, which gets it first and loses it on a retry. // // Run with: pnpm --filter yt-dlp-transcript-common exec tsx --test ytdlp/fetchWindowManaged.test.ts @@ -98,3 +99,26 @@ test("any other failure is not retried with the HLS option", async () => { const mine = (await runs()).slice(before); assert.equal(mine.length, 1); }); + +const atRumble = (videoDir: string) => ({ ...opts(videoDir), videoUrl: "https://rumble.com/v1-x.html" }); + +test("a rumble.com window sends -extension_picky 0 on the first try: one page load, not two", async () => { + process.env.FAKE_MODE = "rumble"; + const before = (await runs()).length; + const res = await fetchWindowManaged(atRumble(await setup("rumble-first"))); + const mine = (await runs()).slice(before); + assert.equal(mine.length, 1); + assert.equal(hasPicky(mine[0]), true); + assert.equal(await readFile(res.file, "utf8"), "mp4"); +}); + +test("a rumble.com upload served progressive is retried once without the HLS option", async () => { + process.env.FAKE_MODE = "progressive"; + const before = (await runs()).length; + const res = await fetchWindowManaged(atRumble(await setup("rumble-progressive"))); + const mine = (await runs()).slice(before); + assert.equal(mine.length, 2); + assert.equal(hasPicky(mine[0]), true); + assert.equal(hasPicky(mine[1]), false); + assert.equal(await readFile(res.file, "utf8"), "mp4"); +}); diff --git a/common/ytdlp/fetchWindowManaged.ts b/common/ytdlp/fetchWindowManaged.ts @@ -28,6 +28,7 @@ import { type ResolvedCookiePolicy, } from "../lib/cookiePolicy"; import type { ChannelConfig } from "../lib/channelConfig"; +import { detectPlatform } from "../lib/platform"; import type { Paths } from "../lib/paths"; import { clipsDirFor, @@ -56,9 +57,13 @@ export const DEFAULT_CLIP_MAX_HEIGHT = 720; // failed. `-extension_picky 0` lets them through, but it is an option of the HLS // DEMUXER: against a progressive URL (YouTube's googlevideo mp4) ffmpeg aborts // with "Option extension_picky not found". So it is a RETRY on exactly that -// refusal, never a default — umtool's build-video.mjs does the same -// (umtool/docs/quirks.md). +// refusal — umtool's build-video.mjs does the same (umtool/docs/quirks.md) — +// EXCEPT on a rumble.com URL, where it goes on the first try: every attempt +// loads the Rumble page again, and Cloudflare 403s some share of those loads, +// so a retry every Rumble window needs doubled the refusals. An old Rumble +// upload served progressive is the mirror case, retried once without it. export const HLS_EXTENSION_REFUSED = /allowed_segment_extensions|allowed_extensions/; +export const HLS_PICKY_REFUSED = /Option extension_picky not found/; export const HLS_PICKY_RETRY_ARGS = ["--downloader-args", "ffmpeg_i:-extension_picky 0"]; // The clip format selector lives with the download presets (the "video_720" @@ -253,8 +258,9 @@ export async function fetchWindowManaged( ); let cookiesUsed = alwaysCookies(policy); - let args = argsWith(cookiesUsed); - let outcome = await run(cookiesUsed); + let picky = detectPlatform(opts.videoUrl) === "rumble" ? HLS_PICKY_RETRY_ARGS : []; + let args = argsWith(cookiesUsed, picky); + let outcome = await run(cookiesUsed, picky); let backedOff = false; if (outcome.exitCode !== 0) { @@ -273,19 +279,28 @@ export async function fetchWindowManaged( `Window fetch failed with ${availability}; retrying once with cookies.\n`, ); cookiesUsed = retryCookies; - args = argsWith(retryCookies); - outcome = await run(retryCookies); + args = argsWith(retryCookies, picky); + outcome = await run(retryCookies, picky); } } } - if (outcome.exitCode !== 0 && HLS_EXTENSION_REFUSED.test(outcome.stderrTail)) { + if (outcome.exitCode !== 0 && !picky.length && HLS_EXTENSION_REFUSED.test(outcome.stderrTail)) { opts.onLog( `Window fetch: ffmpeg refused the HLS segment extension; retrying once with -extension_picky 0.\n`, ); await rm(part, { force: true }); - args = argsWith(cookiesUsed, HLS_PICKY_RETRY_ARGS); - outcome = await run(cookiesUsed, HLS_PICKY_RETRY_ARGS); + picky = HLS_PICKY_RETRY_ARGS; + args = argsWith(cookiesUsed, picky); + outcome = await run(cookiesUsed, picky); + } else if (outcome.exitCode !== 0 && picky.length && HLS_PICKY_REFUSED.test(outcome.stderrTail)) { + opts.onLog( + `Window fetch: the source is not HLS after all; retrying once without -extension_picky.\n`, + ); + await rm(part, { force: true }); + picky = []; + args = argsWith(cookiesUsed, picky); + outcome = await run(cookiesUsed, picky); } if (outcome.exitCode !== 0) { diff --git a/umtool/docs/quirks.md b/umtool/docs/quirks.md @@ -26,7 +26,13 @@ and Rumble ships no progressive fallback, so every Rumble clip is unbuildable without it. But the option lives on the **HLS demuxer**: pass it against a progressive URL (YouTube's googlevideo mp4) and ffmpeg aborts with "Option extension_picky not found". Adding it unconditionally trades a Rumble failure for -a YouTube one. +a YouTube one. The editor's window fetch keys it on the URL instead: a rumble.com +URL gets it on the FIRST try (a retry loads the Rumble page again, and Cloudflare +403s a share of page loads), any other URL only as the retry. + +**A Rumble video gets Rumble's args from its URL, not its channel.** A channel +with no platform of its own (community-notes) holds Rumble videos; fetched with +that channel's args they went without `--impersonate` and Cloudflare 403'd them. **A 403 backs the platform off in a batch.** `fetch-via-editor.mjs --all` (the editor's `fetch-windows` job) treats one 403 as that clip's failure — a removed