// The per-channel argv every yt-dlp invocation in this repo appends: the cookie // source when the resolved policy calls for one, then the platform's fixed // args (PLATFORM_ARGS) at the platform's CURRENT pace, then the channel's own // `ytdlpExtraArgs` verbatim. // // Lifted out of ytdlp/downloadOneManaged.ts (where it was `channelConfigArgs`, // which still delegates here) because the clip-window fetch has to honour the // same two things and must not grow its own idea of them: an operator who set // `--limit-rate` on a channel meant it for every byte that channel costs, not // only for the bytes a full download costs. // // This is the ONE builder. `configArgs` (runYtdlp.ts), the metadata scan, the // quick availability check and the new-channel probe all go through it (the // probe has no config yet, so it calls `pacedPlatformArgs` directly). Before // release 5 there were four copies and the probe had none, so a Rumble // channel could not even be created. // // THE PACE ADAPTS (release 17, slice RL). `--sleep-requests` is the platform's // static value until a platform-level rate limit doubles it; the current value // lives in the download lane's persisted state (`platformPace`, see // jobs/platformBackoff.ts) and is read here synchronously through // `livePlatformPaceSeconds`, so EVERY spawn against the platform — listing, // prefetch, primary, availability, metadata scan, clip — slows down together. // The channel's own args still come last and win. import type { ChannelConfig } from "../lib/channelConfig"; import { cookieArgs } from "../lib/cookiePolicy"; import { detectPlatform, type Platform } from "../lib/platform"; import { livePlatformPaceSeconds } from "../jobs/autoQueueState"; // The platform args table lives in `platformArgs.mjs` (plain JS so umtool's // `.mjs` scripts can import the same copy); re-exported here for TS callers. import { platformArgs, staticSleepRequestsSeconds, withSleepRequests, } from "./platformArgs.mjs"; export { PLATFORM_ARGS, platformArgs, platformArgsForUrl, PLATFORM_MIN_GAP_SECONDS, platformMinGapSeconds, PLATFORM_IMPORT_MIN_GAP_SECONDS, platformImportMinGapSeconds, staticSleepRequestsSeconds, withSleepRequests, } from "./platformArgs.mjs"; export function channelPlatform( config: Pick, ): Platform | null { return config.platform ?? detectPlatform(config.url); } // The config to build a VIDEO's argv from: the channel's, with the platform // taken from the video's own URL when that names a different one. A channel // may hold another platform's videos — community-notes has no url or platform // of its own and holds Rumble videos — and a Rumble request without Rumble's // args (`--impersonate`) is refused at Cloudflare. The request goes where the // URL points, so the URL's platform decides the args and the pace. export function configForVideoUrl(config: ChannelConfig, videoUrl: string): ChannelConfig { const platform = detectPlatform(videoUrl); if (!platform || platform === channelPlatform(config)) return config; return { ...config, platform, url: videoUrl }; } // The key the pacing state is kept under — the same one the rate-limit // cooldown uses (`detectPlatform(url) ?? "unknown"`, autoRunner/runYtdlp), so a // 429 recorded by any path paces every path. export function pacingPlatformKey(config: Pick): string { return detectPlatform(config.url) ?? "unknown"; } // The platform's current `--sleep-requests`: the adaptive pace while one is // recorded, else the static value. export function channelPaceSeconds( config: Pick, ): number { return ( livePlatformPaceSeconds(pacingPlatformKey(config)) ?? staticSleepRequestsSeconds(channelPlatform(config)) ); } // PLATFORM_ARGS for a platform at a given pace (default: its current one) — // for the spawns that have a URL and no channel config. export function pacedPlatformArgs( platform: Platform | null | undefined, paceSeconds: number | undefined = platform ? livePlatformPaceSeconds(platform) : undefined, ): string[] { return withSleepRequests(platformArgs(platform), paceSeconds); } export function channelExtraArgs( config: ChannelConfig, cookies?: string, // The pace to apply; default: the platform's current one. Tests pass it. paceSeconds: number | undefined = livePlatformPaceSeconds( pacingPlatformKey(config), ), ): string[] { const args: string[] = []; args.push(...cookieArgs(cookies)); args.push(...withSleepRequests(platformArgs(channelPlatform(config)), paceSeconds)); if (config.ytdlpExtraArgs?.length) args.push(...config.ytdlpExtraArgs); return args; }