From e2a1b704fcfcb4b66b449b088868913bc588df79 Mon Sep 17 00:00:00 2001 From: bashonly Date: Thu, 27 Mar 2025 14:38:20 -0500 Subject: [PATCH] [ie/youtube] Add `force_js_variant` extractor-arg Authored by: bashonly --- README.md | 1 + yt_dlp/extractor/youtube/_video.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 1ee4223856..fe3634b639 100644 --- a/README.md +++ b/README.md @@ -1782,6 +1782,7 @@ The following extractors use this feature: * `data_sync_id`: Overrides the account Data Sync ID used in Innertube API requests. This may be needed if you are using an account with `youtube:player_skip=webpage,configs` or `youtubetab:skip=webpage` * `visitor_data`: Overrides the Visitor Data used in Innertube API requests. This should be used with `player_skip=webpage,configs` and without cookies. Note: this may have adverse effects if used improperly. If a session from a browser is wanted, you should pass cookies instead (which contain the Visitor ID) * `po_token`: Proof of Origin (PO) Token(s) to use. Comma seperated list of PO Tokens in the format `CLIENT.CONTEXT+PO_TOKEN`, e.g. `youtube:po_token=web.gvs+XXX,web.player=XXX,web_safari.gvs+YYY`. Context can be either `gvs` (Google Video Server URLs) or `player` (Innertube player request) +* `force_js_variant`: The player javascript variant to use for signature and nsig deciphering. The available clients variants are `base`, `tce`, `tv`, `es6`, `phone`, `tablet`. Only `base` is recommended as a possible workaround; the others are mainly for debugging purposes. The default is to use what is prescribed by the site, and can be selected with `false` #### youtubetab (YouTube playlists, channels, feeds, etc.) * `skip`: One or more of `webpage` (skip initial webpage download), `authcheck` (allow the download of playlists requiring authentication when no initial webpage is downloaded. This may cause unwanted behavior, see [#1122](https://github.com/yt-dlp/yt-dlp/pull/1122) for more details) diff --git a/yt_dlp/extractor/youtube/_video.py b/yt_dlp/extractor/youtube/_video.py index e349b36517..f81fc11054 100644 --- a/yt_dlp/extractor/youtube/_video.py +++ b/yt_dlp/extractor/youtube/_video.py @@ -1760,6 +1760,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor): }, ] + _PLAYER_JS_VARIANT_MAP = { + 'base': '/s/player/{}/player_ias.vflset/en_US/base.js', + 'tce': '/s/player/{}/player_ias_tce.vflset/en_US/base.js', + 'tv': '/s/player/{}/tv-player-ias.vflset/tv-player-ias.js', + 'es6': '/s/player/{}/tv-player-es6.vflset/tv-player-es6.js', + 'phone': '/s/player/{}/player-plasma-ias-phone-en_US.vflset/base.js', + 'tablet': '/s/player/{}/player-plasma-ias-tablet-en_US.vflset/base.js', + } + @classmethod def suitable(cls, url): from yt_dlp.utils import parse_qs @@ -1939,6 +1948,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor): get_all=False, expected_type=str) if not player_url: return + js_variant = self._configuration_arg('force_js_variant', [''])[0] + if js_variant not in ('false', ''): + if js_variant not in self._PLAYER_JS_VARIANT_MAP: + raise ExtractorError(f'Invalid JS variant: {js_variant}', expected=True) + player_id = self._extract_player_info(player_url) + self.write_debug(f'Forcing {js_variant} variant for player {player_id}') + player_url = self._PLAYER_JS_VARIANT_MAP[js_variant].format(player_id) return urljoin('https://www.youtube.com', player_url) def _download_player_url(self, video_id, fatal=False):