Fix YouTube downloads failing with "Requested format not available"

Root cause: two issues compounding.

1. extractor_args with player_client: ['android', 'web'] + skip: ['hls', 'dash']
   stripped all real audio formats. Android client returns HLS/DASH streams,
   skip removes them, leaving only storyboard thumbnails. bestaudio then fails
   because there's nothing valid to select.

2. Browser cookies (cookiesfrombrowser) cause authenticated YouTube sessions
   to return restricted format data for some videos. Same video works fine
   without cookies.

Fix:
- Removed all hardcoded player_client and skip overrides from 4 locations
  (download opts, connection check, search, retry). Let yt-dlp use its own
  defaults which are updated with each release.
- Retry strategy: attempt 1 uses cookies (respects user setting), attempt 2
  drops cookies (fixes auth-restricted formats), attempt 3 uses format 'best'
  as last resort.
- Updated user_agent to Chrome 131.
This commit is contained in:
Broque Thomas 2026-03-26 19:07:50 -07:00
parent de5e3f6b3f
commit 6a41f5c0d7

View file

@ -148,14 +148,7 @@ class YouTubeClient:
'preferredquality': '320', 'preferredquality': '320',
}], }],
'progress_hooks': [self._progress_hook], # Track download progress 'progress_hooks': [self._progress_hook], # Track download progress
# Bot detection bypass options 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'extractor_args': {
'youtube': {
'player_client': ['android', 'web'], # Try multiple clients
'skip': ['hls', 'dash'], # Skip problematic formats
}
},
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'age_limit': None, # Don't skip age-restricted 'age_limit': None, # Don't skip age-restricted
} }
@ -226,14 +219,7 @@ class YouTubeClient:
'quiet': True, 'quiet': True,
'no_warnings': True, 'no_warnings': True,
'extract_flat': True, # Don't download, just extract info 'extract_flat': True, # Don't download, just extract info
# Bot detection bypass 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'extractor_args': {
'youtube': {
'player_client': ['android', 'web'],
'skip': ['hls', 'dash'],
}
},
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
} }
with yt_dlp.YoutubeDL(ydl_opts) as ydl: with yt_dlp.YoutubeDL(ydl_opts) as ydl:
@ -597,14 +583,7 @@ class YouTubeClient:
'no_warnings': True, 'no_warnings': True,
'extract_flat': True, # Fast mode: Don't fetch formats (massive speedup) 'extract_flat': True, # Fast mode: Don't fetch formats (massive speedup)
'default_search': 'ytsearch', 'default_search': 'ytsearch',
# Bot detection bypass (same as download options) 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'extractor_args': {
'youtube': {
'player_client': ['android', 'web'],
'skip': ['hls', 'dash'],
}
},
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
} }
# Add cookie support for search (avoids bot detection) # Add cookie support for search (avoids bot detection)
@ -956,19 +935,22 @@ class YouTubeClient:
download_opts['format'] = 'bestaudio/best' download_opts['format'] = 'bestaudio/best'
download_opts['noplaylist'] = True download_opts['noplaylist'] = True
# On retry, try different player client # On retry, try different strategies
if attempt == 1: if attempt == 1:
logger.info(f"🔄 Retry {attempt + 1}/{max_retries} with different player client") # Drop browser cookies — authenticated sessions sometimes get restricted formats
download_opts['extractor_args'] = { if 'cookiesfrombrowser' in download_opts:
'youtube': { logger.info(f"🔄 Retry {attempt + 1}/{max_retries} without browser cookies")
'player_client': ['web'], # Try web-only on retry download_opts.pop('cookiesfrombrowser', None)
'skip': ['hls', 'dash'], else:
logger.info(f"🔄 Retry {attempt + 1}/{max_retries} with web_creator client")
download_opts['extractor_args'] = {
'youtube': { 'player_client': ['web_creator'] }
} }
}
elif attempt >= 2: elif attempt >= 2:
logger.info(f"🔄 Retry {attempt + 1}/{max_retries} with 'best' format (video fallback)") logger.info(f"🔄 Retry {attempt + 1}/{max_retries} with 'best' format (video fallback)")
download_opts['format'] = 'best' # Fallback to best available (including video) download_opts['format'] = 'best'
download_opts.pop('extractor_args', None) # Reset extractor args download_opts.pop('cookiesfrombrowser', None)
download_opts.pop('extractor_args', None)
# Perform download # Perform download