commit d99651df6ad7549dcc3124111542f321224d0b48
parent aa4226b5a472edc630dee16c19806f7d3d0e5939
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 18:39:23 -0400
common: YouTube spawns carry --sleep-requests 1
The 429 investigation (~/reports/release-7/data/q-429-report.md, finding
1) found YouTube had no request-level pacing at all: each video attempt
fires 2-4 requests back to back, and only the between-downloads sleep
spaced anything. PLATFORM_ARGS gains `youtube: ["--sleep-requests", "1"]`
beside rumble's entry; a channel's own ytdlpExtraArgs still wins because
it comes after. The comment above the table now says what the pace is and
that a spawn passing its own --sleep-requests 1 (metadata scan, clip
window) carries it twice, harmlessly.
Tests updated deliberately: channelArgs.test.ts (youtube gets the pace,
a platform with no entry gets nothing, a channel override wins) and
umtool's platform-args.test.mjs (+1 case: youtube carries the pace, a URL
on no known platform carries nothing; the YouTube clip fetch and
simulate now carry the pace).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
3 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/common/ytdlp/channelArgs.test.ts b/common/ytdlp/channelArgs.test.ts
@@ -19,15 +19,34 @@ test("a rumble URL gets the four platform args", () => {
);
});
-test("youtube gets no platform args", () => {
+test("youtube gets the request pace and nothing else (release 9, B3)", () => {
assert.deepEqual(
channelExtraArgs(cfg({ url: "https://www.youtube.com/@x" })),
- [],
+ ["--sleep-requests", "1"],
);
- assert.deepEqual(platformArgs("youtube"), []);
+ assert.deepEqual(platformArgs("youtube"), ["--sleep-requests", "1"]);
assert.deepEqual(platformArgs(null), []);
});
+test("a platform with no entry gets no platform args", () => {
+ assert.deepEqual(platformArgs("twitch"), []);
+ assert.deepEqual(
+ channelExtraArgs(cfg({ url: "https://example.com/feed" })),
+ [],
+ );
+});
+
+test("a youtube channel's own --sleep-requests comes after, and wins", () => {
+ const args = channelExtraArgs(
+ cfg({
+ url: "https://www.youtube.com/@x",
+ ytdlpExtraArgs: ["--sleep-requests", "3"],
+ }),
+ );
+ assert.deepEqual(args, ["--sleep-requests", "1", "--sleep-requests", "3"]);
+ assert.equal(args.lastIndexOf("--sleep-requests"), args.length - 2);
+});
+
test("an explicit config.platform wins over the URL's host", () => {
assert.deepEqual(
channelExtraArgs(
diff --git a/common/ytdlp/platformArgs.mjs b/common/ytdlp/platformArgs.mjs
@@ -15,13 +15,29 @@ import { detectPlatform } from "../lib/detectPlatform.mjs";
// AFTER these, so a channel override wins (yt-dlp is last-flag-wins for
// `--sleep-requests` and `--impersonate`).
//
+// Every entry paces at the REQUEST level with `--sleep-requests 1`: one
+// second between the HTTP requests a single yt-dlp process makes (listing
+// pages, the prefetch, subtitle fetches, retries). That is a different layer
+// from `sleepBetweenDownloadsSeconds`, which only spaces whole per-video
+// downloads apart. A spawn that already passes its own `--sleep-requests 1`
+// (metadataScan.ts, fetchWindowManaged.ts) now carries it twice — harmless,
+// yt-dlp keeps the last.
+//
// rumble: every request 403s at Cloudflare without a browser TLS fingerprint
// (yt-dlp #17496); a probe on 2026-09-24 got 200 with `--impersonate chrome`
// and 403 without. `--sleep-requests 1` paces the listing walk — the
// the-quartering-rumble full sweep 429'd at page 155 unpaced the same day.
+//
+// youtube: the 2026-09-25 429 investigation
+// (~/reports/release-7/data/q-429-report.md, finding 1) found YouTube had NO
+// request-level pacing at all — each video attempt fires 2–4 requests
+// (prefetch, subtitles, retries) back to back, and the two channels that 429'd
+// were the two most-downloaded. Mirrors rumble's pace; a channel's own
+// `ytdlpExtraArgs` still wins because it comes after.
/** @type {Readonly<Partial<Record<Platform, readonly string[]>>>} */
export const PLATFORM_ARGS = Object.freeze({
rumble: Object.freeze(["--impersonate", "chrome", "--sleep-requests", "1"]),
+ youtube: Object.freeze(["--sleep-requests", "1"]),
});
/**
diff --git a/umtool/report-to-video/platform-args.test.mjs b/umtool/report-to-video/platform-args.test.mjs
@@ -30,16 +30,27 @@ test("a Rumble clip fetch carries the rumble platform args, before the retry's e
assert.equal(argv[argv.indexOf("--download-sections") + 1], "*12.00-20.50");
});
-test("a YouTube clip fetch gets no platform args", () => {
+test("a YouTube clip fetch gets youtube's request pace and no impersonation", () => {
const argv = clipFetchArgs({ url: YOUTUBE, from: 1, to: 2, fmt: "b", dest: "/x/out.mp4" });
- assert.deepEqual(slice(argv, "--force-keyframes-at-cuts", "-f"), []);
+ assert.deepEqual(slice(argv, "--force-keyframes-at-cuts", "-f"), ["--sleep-requests", "1"]);
assert.ok(!argv.includes("--impersonate"));
});
-test("the availability simulate carries the platform args for Rumble only", () => {
+test("youtube carries --sleep-requests 1; a URL on no known platform carries nothing", () => {
+ assert.deepEqual([...PLATFORM_ARGS.youtube], ["--sleep-requests", "1"]);
+ const other = "https://example.com/some/video";
+ assert.deepEqual(slice(simulateArgs(other), "--no-warnings", "--"), []);
+ assert.deepEqual(
+ slice(clipFetchArgs({ url: other, from: 1, to: 2, fmt: "b", dest: "/x/o.mp4" }), "--force-keyframes-at-cuts", "-f"),
+ [],
+ );
+});
+
+test("the availability simulate carries each platform's args", () => {
const rumble = simulateArgs(RUMBLE);
assert.deepEqual(slice(rumble, "--no-warnings", "--"), [...PLATFORM_ARGS.rumble]);
assert.deepEqual(rumble.slice(-2), ["--", RUMBLE]);
assert.ok(rumble.includes("--simulate"));
assert.ok(!simulateArgs(YOUTUBE).includes("--impersonate"));
+ assert.deepEqual(slice(simulateArgs(YOUTUBE), "--no-warnings", "--"), ["--sleep-requests", "1"]);
});