// 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) — // 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 import { test } from "node:test"; import assert from "node:assert/strict"; import { chmod, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { fetchWindowManaged, HLS_PICKY_RETRY_ARGS } from "./fetchWindowManaged"; import type { Paths } from "../lib/paths"; import type { ChannelConfig } from "../lib/channelConfig"; const ROOT = await mkdtemp(path.join(os.tmpdir(), "fetchwindow-")); const BIN = path.join(ROOT, "fake-ytdlp.mjs"); const ARGS_LOG = path.join(ROOT, "argv.jsonl"); process.env.FAKE_ARGS_LOG = ARGS_LOG; // FAKE_MODE=rumble: refuses like ffmpeg 8 on Rumble's segments unless the // picky option is present. =progressive: fails if the option IS present, as // ffmpeg does against a non-HLS input. =broken: always fails, unrelated. await writeFile( BIN, `#!/usr/bin/env node import { appendFileSync, writeFileSync } from "node:fs"; const args = process.argv.slice(2); appendFileSync(process.env.FAKE_ARGS_LOG, JSON.stringify(args) + "\\n"); const picky = args.includes("ffmpeg_i:-extension_picky 0"); const mode = process.env.FAKE_MODE; const fail = (msg) => { process.stderr.write(msg + "\\nERROR: ffmpeg exited with code 183\\n"); process.exit(1); }; if (mode === "broken") fail("ERROR: something else entirely"); if (mode === "rumble" && !picky) fail("[in#0] URL https://cdn.example/x.tar?r_file=media-0.ts is not in allowed_segment_extensions"); if (mode === "progressive" && picky) fail("Option extension_picky not found."); writeFileSync(args[args.indexOf("-o") + 1], "mp4"); `, ); await chmod(BIN, 0o755); async function setup(name: string) { const videoDir = path.join(ROOT, name, "data", "v1"); await mkdir(videoDir, { recursive: true }); return videoDir; } async function runs(): Promise { return (await readFile(ARGS_LOG, "utf8").catch(() => "")) .trim().split("\n").filter(Boolean).map((l) => JSON.parse(l) as string[]); } function opts(videoDir: string) { return { channelSlug: "c", channelConfig: {} as ChannelConfig, paths: { ytdlpBin: BIN } as unknown as Paths, videoDir, videoId: "v1", videoUrl: "https://rumble.example/v1.html", from: 10, to: 20, provenance: { requestedBy: "test" }, onLog: () => {}, signal: new AbortController().signal, }; } const hasPicky = (argv: string[]) => argv.join(" ").includes(HLS_PICKY_RETRY_ARGS.join(" ")); test("a Rumble window refused for its segment extension is retried once with -extension_picky 0", async () => { process.env.FAKE_MODE = "rumble"; const before = (await runs()).length; const videoDir = await setup("rumble"); const res = await fetchWindowManaged(opts(videoDir)); const mine = (await runs()).slice(before); assert.equal(mine.length, 2); assert.equal(hasPicky(mine[0]), false, "never on the first try"); assert.equal(hasPicky(mine[1]), true); assert.equal(res.cached, false); assert.ok(res.provenance?.ytdlp === undefined); assert.equal(await readFile(res.file, "utf8"), "mp4"); }); test("a progressive source fetches on the first try, without the HLS option", async () => { process.env.FAKE_MODE = "progressive"; const before = (await runs()).length; const res = await fetchWindowManaged(opts(await setup("progressive"))); const mine = (await runs()).slice(before); assert.equal(mine.length, 1); assert.equal(hasPicky(mine[0]), false); assert.equal(res.cached, false); }); test("any other failure is not retried with the HLS option", async () => { process.env.FAKE_MODE = "broken"; const before = (await runs()).length; await assert.rejects(fetchWindowManaged(opts(await setup("broken"))), /yt-dlp failed fetching/); 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"); });