feat(network): request-throttle controls to avoid YouTube rate-limiting
Re-implements the sleepRequests/sleepInterval/maxSleepInterval feature (--sleep-requests / --sleep-interval / --max-sleep-interval) from the stale feat/binary-management-and-library-scale branch, ported onto current main's structure (zod settingsSchema, decomposed NetworkCard). All default to 0/off, preserving current behaviour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,12 @@ export interface AccessOptions {
|
||||
proxy: string
|
||||
/** yt-dlp --limit-rate value, e.g. '2M'; empty means unlimited */
|
||||
rateLimit: string
|
||||
/** `--sleep-requests` seconds (pause between extraction requests); 0/omitted = off */
|
||||
sleepRequests?: number
|
||||
/** `--sleep-interval` seconds (min pause before each download); 0/omitted = off */
|
||||
sleepInterval?: number
|
||||
/** `--max-sleep-interval` seconds (max of the random pre-download pause); applies only with a non-zero sleepInterval */
|
||||
maxSleepInterval?: number
|
||||
/** absolute path to aria2c.exe; omit to use yt-dlp's built-in downloader */
|
||||
aria2cPath?: string
|
||||
/** aria2c parallel connections (-x/-s), clamped to 1–16; omitted = 16 (L64) */
|
||||
@@ -323,6 +329,19 @@ function accessArgs(a: AccessOptions): string[] {
|
||||
const args: string[] = []
|
||||
if (a.proxy.trim()) args.push('--proxy', a.proxy.trim())
|
||||
if (a.rateLimit.trim()) args.push('--limit-rate', a.rateLimit.trim())
|
||||
// Request throttle — the antidote to YouTube's rate-limit/403 wall on big
|
||||
// channel pulls. `--sleep-requests` spaces out the extraction requests; the
|
||||
// sleep-interval pair brackets a random pause before each download. yt-dlp
|
||||
// requires --sleep-interval to be present for --max-sleep-interval to be
|
||||
// meaningful, and a max below the min is nonsensical, so max is gated on both.
|
||||
const sleepRequests = a.sleepRequests ?? 0
|
||||
if (sleepRequests > 0) args.push('--sleep-requests', String(sleepRequests))
|
||||
const sleepInterval = a.sleepInterval ?? 0
|
||||
if (sleepInterval > 0) {
|
||||
args.push('--sleep-interval', String(sleepInterval))
|
||||
const maxSleep = a.maxSleepInterval ?? 0
|
||||
if (maxSleep > sleepInterval) args.push('--max-sleep-interval', String(maxSleep))
|
||||
}
|
||||
if (a.aria2cPath) {
|
||||
args.push('--downloader', a.aria2cPath, '--downloader-args', aria2cArgs(a.aria2cConnections))
|
||||
}
|
||||
|
||||
@@ -222,6 +222,9 @@ export function buildCommand(opts: StartDownloadOptions, cookiesFile?: string):
|
||||
const access = {
|
||||
proxy: settings.proxy,
|
||||
rateLimit: settings.rateLimit,
|
||||
sleepRequests: settings.sleepRequests,
|
||||
sleepInterval: settings.sleepInterval,
|
||||
maxSleepInterval: settings.maxSleepInterval,
|
||||
aria2cPath,
|
||||
aria2cConnections: settings.aria2cConnections,
|
||||
// L136/M6: incognito attaches no cookies from the browser either.
|
||||
|
||||
@@ -94,6 +94,10 @@ export const SETTINGS_FIELD_SCHEMAS: { [K in keyof Settings]: z.ZodType<Settings
|
||||
defaultVideoQuality: z.enum(VIDEO_QUALITY_OPTIONS),
|
||||
defaultAudioQuality: z.enum(AUDIO_QUALITY_OPTIONS),
|
||||
rateLimit: trimmedWhere((v) => RATE_LIMIT_RE.test(v)),
|
||||
// Throttle seconds (0–300, 0 = off) to dodge YouTube's rate-limit/403 wall.
|
||||
sleepRequests: clampedInt(0, 300),
|
||||
sleepInterval: clampedInt(0, 300),
|
||||
maxSleepInterval: clampedInt(0, 300),
|
||||
// Power-user field; yt-dlp's client list evolves. Accept any trimmed string.
|
||||
youtubePlayerClient: z.string().transform((v) => v.trim()),
|
||||
// Credential-bearing fields — settings.ts encrypts these AFTER parsing.
|
||||
|
||||
Reference in New Issue
Block a user