yt-briefing 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -91,6 +91,7 @@ const ytdlpArgs = [
91
91
  '--sub-langs', subLangs,
92
92
  '--sub-format', 'vtt',
93
93
  '--ignore-errors', // continue if one language track fails (e.g. 429 on pl for en-only video)
94
+ '--extractor-retries', '3', // yt-dlp's own retry on the extractor-level bot-check / 429
94
95
  '--no-warnings',
95
96
  '--quiet',
96
97
  '-o', outTemplate,
@@ -98,7 +99,26 @@ const ytdlpArgs = [
98
99
  if (proxyUrl)
99
100
  ytdlpArgs.push('--proxy', proxyUrl);
100
101
  ytdlpArgs.push(`https://www.youtube.com/watch?v=${videoId}`);
101
- const result = spawnSync(YT_DLP, ytdlpArgs, { encoding: 'utf8', timeout: 60_000 });
102
+ // The first request through a cold WARP/shared egress IP intermittently trips YouTube's
103
+ // "Sign in to confirm you're not a bot" check; the very next request seconds later succeeds
104
+ // (the extractor session warms up). Without a retry that transient flake exits 2, which the
105
+ // sweep treats as fatal (emit → exit) — so a cold `/yt` needed two calls to get going. Retry
106
+ // ONLY on rate-limit-with-no-subtitles: a genuine IP block still exhausts the attempts and
107
+ // exits 2, and real tooling errors / captionless videos fall straight through untouched.
108
+ const sleepSync = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
109
+ let result;
110
+ for (let attempt = 1; attempt <= 3; attempt++) {
111
+ result = spawnSync(YT_DLP, ytdlpArgs, { encoding: 'utf8', timeout: 60_000 });
112
+ if (result.error)
113
+ break; // spawn / ENOENT — retrying won't help
114
+ const stderr = (result.stderr ?? '').toLowerCase();
115
+ const limited = stderr.includes('sign in') || stderr.includes('429') || stderr.includes('too many') || stderr.includes('captcha');
116
+ const gotSubs = existsSync(tmpDir) && readdirSync(tmpDir).some(f => f.endsWith('.vtt'));
117
+ if (gotSubs || !limited)
118
+ break; // success, or a non-rate-limit outcome → stop
119
+ if (attempt < 3)
120
+ sleepSync(attempt * 2000); // 2s, then 4s backoff between cold retries
121
+ }
102
122
  const cleanup = () => { try {
103
123
  rmSync(tmpDir, { recursive: true });
104
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yt-briefing",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "A self-learning YouTube briefing engine: it sweeps the channels you follow, filters noise in two stages (title, then transcript), summarizes the rest in your language, and adapts to your ratings — one video at a time.",
5
5
  "type": "module",
6
6
  "bin": {