diff options
| author | Astounds <kirito@disroot.org> | 2026-03-27 20:47:44 -0500 |
|---|---|---|
| committer | Astounds <kirito@disroot.org> | 2026-03-27 20:47:44 -0500 |
| commit | 22c72aa842efa6d1dca3bb95eeb47122537ce12a (patch) | |
| tree | a94cf15bd0d7748db0532f56ddefde1fda74a33d /youtube/watch.py | |
| parent | 56ecd6cb1b461bd3622c669936050fa7e4d83542 (diff) | |
| download | yt-local-22c72aa842efa6d1dca3bb95eeb47122537ce12a.tar.lz yt-local-22c72aa842efa6d1dca3bb95eeb47122537ce12a.tar.xz yt-local-22c72aa842efa6d1dca3bb95eeb47122537ce12a.zip | |
remove yt-dlp, fix captions PO Token issue, fix 429 retry logic
- Remove yt-dlp entirely (modules, routes, settings, dependency)
Was blocking page loads by running synchronously in gevent
- Fix captions: use Android client caption URLs (no PO Token needed)
instead of web timedtext URLs that YouTube now blocks
- Fix 429 retry: fail immediately without Tor (same IP = pointless retry)
Was causing ~27s delays with exponential backoff
- Accept ytdlp_enabled as legacy setting to avoid warning on startup
Diffstat (limited to 'youtube/watch.py')
| -rw-r--r-- | youtube/watch.py | 93 |
1 files changed, 44 insertions, 49 deletions
diff --git a/youtube/watch.py b/youtube/watch.py index b76a462..2fbc1fc 100644 --- a/youtube/watch.py +++ b/youtube/watch.py @@ -180,8 +180,34 @@ def make_caption_src(info, lang, auto=False, trans_lang=None): label += ' (Automatic)' if trans_lang: label += ' -> ' + trans_lang + + # Try to use Android caption URL directly (no PO Token needed) + caption_url = None + for track in info.get('_android_caption_tracks', []): + track_lang = track.get('languageCode', '') + track_kind = track.get('kind', '') + if track_lang == lang and ( + (auto and track_kind == 'asr') or + (not auto and track_kind != 'asr') + ): + caption_url = track.get('baseUrl') + break + + if caption_url: + # Add format + if '&fmt=' in caption_url: + caption_url = re.sub(r'&fmt=[^&]*', '&fmt=vtt', caption_url) + else: + caption_url += '&fmt=vtt' + if trans_lang: + caption_url += '&tlang=' + trans_lang + url = util.prefix_url(caption_url) + else: + # Fallback to old method + url = util.prefix_url(yt_data_extract.get_caption_url(info, lang, 'vtt', auto, trans_lang)) + return { - 'url': util.prefix_url(yt_data_extract.get_caption_url(info, lang, 'vtt', auto, trans_lang)), + 'url': url, 'label': label, 'srclang': trans_lang[0:2] if trans_lang else lang[0:2], 'on': False, @@ -387,6 +413,19 @@ def extract_info(video_id, use_invidious, playlist_id=None, index=None): info = tasks[0].value or {} player_response = tasks[1].value or {} + # Save android_vr caption tracks (no PO Token needed for these URLs) + if isinstance(player_response, str): + try: + pr_data = json.loads(player_response) + except Exception: + pr_data = {} + else: + pr_data = player_response or {} + android_caption_tracks = yt_data_extract.deep_get( + pr_data, 'captions', 'playerCaptionsTracklistRenderer', + 'captionTracks', default=[]) + info['_android_caption_tracks'] = android_caption_tracks + yt_data_extract.update_with_new_urls(info, player_response) # Fallback to 'ios' if no valid URLs are found @@ -696,30 +735,6 @@ def get_watch_page(video_id=None): pair_sources = source_info['pair_sources'] uni_idx, pair_idx = source_info['uni_idx'], source_info['pair_idx'] - # Extract audio tracks using yt-dlp for multi-language support - audio_tracks = [] - try: - from youtube import ytdlp_integration - logger.info(f'Extracting audio tracks for video: {video_id}') - ytdlp_info = ytdlp_integration.extract_video_info_ytdlp(video_id) - audio_tracks = ytdlp_info.get('audio_tracks', []) - - if audio_tracks: - logger.info(f'✓ Found {len(audio_tracks)} audio tracks:') - for i, track in enumerate(audio_tracks[:10], 1): # Log first 10 - logger.info(f' [{i}] {track["language_name"]} ({track["language"]}) - ' - f'bitrate: {track.get("audio_bitrate", "N/A")}k, ' - f'codec: {track.get("acodec", "N/A")}, ' - f'format_id: {track.get("format_id", "N/A")}') - if len(audio_tracks) > 10: - logger.info(f' ... and {len(audio_tracks) - 10} more') - else: - logger.warning(f'No audio tracks found for video {video_id}') - - except Exception as e: - logger.error(f'Failed to extract audio tracks: {e}', exc_info=True) - audio_tracks = [] - pair_quality = yt_data_extract.deep_get(pair_sources, pair_idx, 'quality') uni_quality = yt_data_extract.deep_get(uni_sources, uni_idx, 'quality') @@ -843,9 +858,7 @@ def get_watch_page(video_id=None): 'playlist': info['playlist'], 'related': info['related_videos'], 'playability_error': info['playability_error'], - 'audio_tracks': audio_tracks, }, - audio_tracks = audio_tracks, font_family = youtube.font_choices[settings.font], # for embed page **source_info, using_pair_sources = using_pair_sources, @@ -854,16 +867,13 @@ def get_watch_page(video_id=None): @yt_app.route('/api/<path:dummy>') def get_captions(dummy): + url = 'https://www.youtube.com' + request.full_path try: - result = util.fetch_url('https://www.youtube.com' + request.full_path) + result = util.fetch_url(url, headers=util.mobile_ua) result = result.replace(b"align:start position:0%", b"") - return result - except util.FetchError as e: - # Return empty captions gracefully instead of error page - logger.warning(f'Failed to fetch captions: {e}') - return flask.Response(b'WEBVTT\n\n', mimetype='text/vtt', status=200) + return flask.Response(result, mimetype='text/vtt') except Exception as e: - logger.error(f'Unexpected error fetching captions: {e}') + logger.debug(f'Caption fetch failed: {e}') return flask.Response(b'WEBVTT\n\n', mimetype='text/vtt', status=200) @@ -929,18 +939,3 @@ def get_transcript(caption_path): return flask.Response(result.encode('utf-8'), mimetype='text/plain;charset=UTF-8') - - -# ============================================================================ -# yt-dlp Integration Routes -# ============================================================================ - -@yt_app.route('/ytl-api/video-with-audio/<video_id>') -def proxy_video_with_audio(video_id): - """ - Proxy para servir video con audio específico usando yt-dlp - """ - from youtube import ytdlp_proxy - audio_lang = request.args.get('lang', 'en') - max_quality = int(request.args.get('quality', 720)) - return ytdlp_proxy.stream_video_with_audio(video_id, audio_lang, max_quality) |
