From 2204cee6d8301e491d8455a2c54fd0e1b23468f5 Mon Sep 17 00:00:00 2001 From: bashonly <88596187+bashonly@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:23:00 -0600 Subject: [PATCH] [ie/youtube] Add more known player JS variants (#15975) Authored by: bashonly --- README.md | 2 +- test/test_jsc/test_ejs_integration.py | 3 +++ yt_dlp/extractor/youtube/_video.py | 19 ++++++------------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cbb118ee0a..3d96340050 100644 --- a/README.md +++ b/README.md @@ -1864,7 +1864,7 @@ The following extractors use this feature: * `player_skip`: Skip some network requests that are generally needed for robust extraction. One or more of `configs` (skip client configs), `webpage` (skip initial webpage), `js` (skip js player), `initial_data` (skip initial data/next ep request). While these options can help reduce the number of requests needed or avoid some rate-limiting, they could cause issues such as missing formats or metadata. See [#860](https://github.com/yt-dlp/yt-dlp/pull/860) and [#12826](https://github.com/yt-dlp/yt-dlp/issues/12826) for more details * `webpage_skip`: Skip extraction of embedded webpage data. One or both of `player_response`, `initial_data`. These options are for testing purposes and don't skip any network requests * `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp. -* `player_js_variant`: The player javascript variant to use for n/sig deciphering. The known variants are: `main`, `tcc`, `tce`, `es5`, `es6`, `tv`, `tv_es6`, `phone`. The default is `tv`, and the others are for debugging purposes. You can use `actual` to go with what is prescribed by the site +* `player_js_variant`: The player javascript variant to use for n/sig deciphering. The known variants are: `main`, `tcc`, `tce`, `es5`, `es6`, `es6_tcc`, `es6_tce`, `tv`, `tv_es6`, `phone`, `house`. The default is `tv`, and the others are for debugging purposes. You can use `actual` to go with what is prescribed by the site * `player_js_version`: The player javascript version to use for n/sig deciphering, in the format of `signature_timestamp@hash` (e.g. `20348@0004de42`). The default is to use what is prescribed by the site, and can be selected with `actual` * `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side) * `max_comments`: Limit the amount of comments to gather. Comma-separated list of integers representing `max-comments,max-parents,max-replies,max-replies-per-thread,max-depth`. Default is `all,all,all,all,all` diff --git a/test/test_jsc/test_ejs_integration.py b/test/test_jsc/test_ejs_integration.py index 665763ae9f..07c1a9b242 100644 --- a/test/test_jsc/test_ejs_integration.py +++ b/test/test_jsc/test_ejs_integration.py @@ -33,9 +33,12 @@ class Variant(enum.Enum): tce = 'player_ias_tce.vflset/en_US/base.js' es5 = 'player_es5.vflset/en_US/base.js' es6 = 'player_es6.vflset/en_US/base.js' + es6_tcc = 'player_es6_tcc.vflset/en_US/base.js' + es6_tce = 'player_es6_tce.vflset/en_US/base.js' tv = 'tv-player-ias.vflset/tv-player-ias.js' tv_es6 = 'tv-player-es6.vflset/tv-player-es6.js' phone = 'player-plasma-ias-phone-en_US.vflset/base.js' + house = 'house_brand_player.vflset/en_US/base.js' @dataclasses.dataclass diff --git a/yt_dlp/extractor/youtube/_video.py b/yt_dlp/extractor/youtube/_video.py index e69e8bc4bf..cc6e2239a1 100644 --- a/yt_dlp/extractor/youtube/_video.py +++ b/yt_dlp/extractor/youtube/_video.py @@ -139,11 +139,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor): ] _RETURN_TYPE = 'video' # XXX: How to handle multifeed? - _PLAYER_INFO_RE = ( - r'/s/player/(?P[a-zA-Z0-9_-]{8,})/(?:tv-)?player', - r'/(?P[a-zA-Z0-9_-]{8,})/player(?:_ias\.vflset(?:/[a-zA-Z]{2,3}_[a-zA-Z]{2,3})?|-plasma-ias-(?:phone|tablet)-[a-z]{2}_[A-Z]{2}\.vflset)/base\.js$', - r'\b(?Pvfl[a-zA-Z0-9_-]+)\b.*?\.js$', - ) _SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'srt', 'vtt') _DEFAULT_CLIENTS = ('android_vr', 'web', 'web_safari') _DEFAULT_JSLESS_CLIENTS = ('android_vr',) @@ -1886,10 +1881,12 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'tce': 'player_ias_tce.vflset/en_US/base.js', 'es5': 'player_es5.vflset/en_US/base.js', 'es6': 'player_es6.vflset/en_US/base.js', + 'es6_tcc': 'player_es6_tcc.vflset/en_US/base.js', + 'es6_tce': 'player_es6_tce.vflset/en_US/base.js', 'tv': 'tv-player-ias.vflset/tv-player-ias.js', 'tv_es6': 'tv-player-es6.vflset/tv-player-es6.js', 'phone': 'player-plasma-ias-phone-en_US.vflset/base.js', - 'tablet': 'player-plasma-ias-tablet-en_US.vflset/base.js', # Dead since 19712d96 (2025.11.06) + 'house': 'house_brand_player.vflset/en_US/base.js', # Used by Google Drive } _INVERSE_PLAYER_JS_VARIANT_MAP = {v: k for k, v in _PLAYER_JS_VARIANT_MAP.items()} @@ -2179,13 +2176,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor): @classmethod def _extract_player_info(cls, player_url): - for player_re in cls._PLAYER_INFO_RE: - id_m = re.search(player_re, player_url) - if id_m: - break - else: - raise ExtractorError(f'Cannot identify player {player_url!r}') - return id_m.group('id') + if m := re.search(r'/s/player/(?P[a-fA-F0-9]{8,})/', player_url): + return m.group('id') + raise ExtractorError(f'Cannot identify player {player_url!r}') def _load_player(self, video_id, player_url, fatal=True): player_js_key = self._player_js_cache_key(player_url)