import { readFile } from "node:fs/promises"; import { test, expect } from "@playwright/test"; import { channelStage, generateReport, readJson, resetData, resolvePath, } from "./helpers"; // THE BOT CHECK, AND THE COOKIES THE SCAN WAS NOT USING. // // Measured 2026-09-20 on the first real metadata scan of a channel: the FIRST // video answered "Sign in to confirm you're not a bot", the run stopped after // 3 s with `stopped: "rate_limit"`, and a per-platform cooldown was recorded — // while the operator had `cookiesFromBrowser` configured the whole time. The // scan passes cookies only in "always" mode, so it had never tried them. // // A block on a cookie-less pass is not a refusal. It is the one thing we have // not tried yet. const CHANNEL = "test-botcheck"; const ROOT = `test-transcripts/channels/${CHANNEL}`; const ALL = [ "botcheck0001", "guestvid0001", "guestvid0002", "plainvid0001", // A Rumble URL, whose NATIVE extractor id (the embed id) is not the canonical // id the rest of the app keys by. The fake reports a deliberately different // one, so this entry only lands under "v2846lb" if the scan canonicalizes // through webpage_url. Keyed natively, nothing on Rumble would ever settle. "v2846lb", ]; type MetadataScan = { entries: Record; errors: Record; lastRun: { scanned: number; errors: number; stopped?: string } | null; }; async function scanInvocations(): Promise { const raw = await readFile( resolvePath(`${ROOT}/fake-ytdlp.invocations`), "utf8", ).catch(() => ""); return raw .split("\n") .map((l) => l.trim()) .filter((l) => l.startsWith("metadata-scan:")); } test("a bot check on a cookie-less pass retries with cookies instead of backing off", async ({ page, }) => { test.setTimeout(180_000); await resetData("metadata-scan-botcheck"); await generateReport(page, CHANNEL); await page.goto(channelStage(CHANNEL, "playlist")); await page.getByRole("button", { name: "Scan metadata" }).click(); const log = page.getByLabel("Scan metadata output"); await expect(log).toContainText( "bot check without cookies — retrying the remaining 5 ids with --cookies-from-browser firefox", { timeout: 60_000 }, ); await expect(log).toContainText("Metadata scan complete", { timeout: 60_000 }); // The retry read everything the blocked pass could not. const scan = await readJson(`${ROOT}/metadata-scan.json`); expect(Object.keys(scan.entries).sort()).toEqual([...ALL].sort()); expect(Object.keys(scan.errors)).toEqual([]); // NOT a refusal: no stop, and no cooldown. A cooldown here would cost the // operator an hour for a problem the retry solved in seconds. expect(scan.lastRun?.stopped).toBeUndefined(); expect(scan.lastRun?.scanned).toBe(5); await expect(log).not.toContainText("STOPPED"); await expect(log).not.toContainText("cooldown has been recorded"); // Exactly two passes, and only the second carried cookies — the first is the // ordinary cookie-less scan, which is what "when-required" means. const invocations = await scanInvocations(); expect(invocations).toHaveLength(2); expect(invocations[0]).toBe("metadata-scan:5 cookies="); expect(invocations[1]).toBe("metadata-scan:5 cookies=firefox"); // And the cooldown really was not recorded: a second scan starts rather than // being refused with the rate-limit notice. await page.getByRole("button", { name: "Scan metadata" }).click(); await expect(log).toContainText("Nothing to scan", { timeout: 60_000 }); });