Skip to content

Fix YouTube API rate limiting in large playlist imports#1322

Open
renaude wants to merge 4 commits into
museofficial:masterfrom
renaude:bugfix/yt_backoff_concurrency_cap
Open

Fix YouTube API rate limiting in large playlist imports#1322
renaude wants to merge 4 commits into
museofficial:masterfrom
renaude:bugfix/yt_backoff_concurrency_cap

Conversation

@renaude

@renaude renaude commented Jun 5, 2026

Copy link
Copy Markdown

Problem

When importing a large YouTube playlist, getPlaylist fires one getVideosByID HTTP call per 50-item page with no concurrency limit. A 5000-item playlist results in 100 simultaneous YouTube Data API requests, which:

  • Exhausts per-second quota, causing widespread 403/429 responses and partial playlist loads
  • Can saturate Node.js socket limits

Changes

src/services/youtube-api.ts

  • Wrap each getVideosByID call in pLimit(3) so at most 3 video-detail fetches run concurrently regardless of playlist length (p-limit is already a project dependency)
  • Add a retry config to the got instance (3 attempts, exponential backoff 1s/2s/4s, honouring Retry-After header) — this covers search(), getVideo(), and getPlaylist() uniformly so transient 429/5xx errors recover automatically

Test plan

  • Queue a YouTube playlist with 100+ items and confirm it loads without 429 errors
  • Confirm existing single-video and search flows are unaffected
  • Optionally: throttle the YouTube API key temporarily and confirm retries fire with correct delays

🤖 Generated with Claude Code

renaude and others added 3 commits June 6, 2026 01:26
Large playlist imports previously fired one getVideosByID HTTP call per
50-item page with no concurrency limit (100 simultaneous requests for a
5000-item playlist), routinely exhausting YouTube quota and sockets.

- Cap concurrent getVideosByID calls at 3 via p-limit in getPlaylist
- Add got-level retry (3 attempts) with exponential backoff (1s/2s/4s)
  and Retry-After header support, covering search(), getVideo(), and
  getPlaylist() uniformly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use got's RetryObject type for calculateDelay (attemptCount + retryAfter)
- Parenthesise exponentiation to satisfy no-mixed-operators
- Restore single quotes and style to pass eslint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

📦 🤖 A new release has been made for this pull request.

To play around with this PR, pull ghcr.io/museofficial/muse:pr-1322.

Images are available for x86_64 and ARM64.

Latest commit: 072b5f5

@shiftybitshiftr

Copy link
Copy Markdown
Contributor

Autoreview found one real blocker in src/services/youtube-api.ts: the custom Got calculateDelay ignores computedValue. In Got, computedValue === 0 means no retry, so this can retry non-retryable failures or continue past the retry limit. It also multiplies retryAfter by 1000 even though Got already passes that value in milliseconds.

Light fix: either remove the custom calculateDelay and use Got's default, or return computedValue/0 correctly and use retryAfter as-is.

Local lint, typecheck, test, and build passed, but I would not approve until this retry behavior is fixed.

- Return 0 when computedValue === 0 (got signals no retry)
- Drop the * 1000 on retryAfter — got already passes it in milliseconds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants