diff --git a/Makefile b/Makefile index c4431027fa..b4daeb88a4 100644 --- a/Makefile +++ b/Makefile @@ -202,9 +202,9 @@ CONTRIBUTORS: Changelog.md # The following EJS_-prefixed variables are auto-generated by devscripts/update_ejs.py # DO NOT EDIT! -EJS_VERSION = 0.5.0 -EJS_WHEEL_NAME = yt_dlp_ejs-0.5.0-py3-none-any.whl -EJS_WHEEL_HASH = sha256:674fc0efea741d3100cdf3f0f9e123150715ee41edf47ea7a62fbdeda204bdec +EJS_VERSION = 0.7.0 +EJS_WHEEL_NAME = yt_dlp_ejs-0.7.0-py3-none-any.whl +EJS_WHEEL_HASH = sha256:967e9cbe114ddfd046ff4668af18b1827b4597e2e47a83deea668a355828c798 EJS_PY_FOLDERS = yt_dlp_ejs yt_dlp_ejs/yt yt_dlp_ejs/yt/solver EJS_PY_FILES = yt_dlp_ejs/__init__.py yt_dlp_ejs/_version.py yt_dlp_ejs/yt/__init__.py yt_dlp_ejs/yt/solver/__init__.py EJS_JS_FOLDERS = yt_dlp_ejs/yt/solver diff --git a/README.md b/README.md index 10a783abf8..9571e3f140 100644 --- a/README.md +++ b/README.md @@ -1862,11 +1862,11 @@ The following extractors use this feature: * `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and [auto-translated subtitles](https://github.com/yt-dlp/yt-dlp/issues/4090#issuecomment-1158102032) respectively * `player_client`: Clients to extract video data from. The currently available clients are `web`, `web_safari`, `web_embedded`, `web_music`, `web_creator`, `mweb`, `ios`, `android`, `android_vr`, `tv`, `tv_downgraded`, and `tv_simply`. By default, `android_vr,web,web_safari` is used. If no JavaScript runtime/engine is available, then only `android_vr` is used. If logged-in cookies are passed to yt-dlp, then `tv_downgraded,web,web_safari` is used for free accounts and `tv_downgraded,web_creator,web` is used for premium accounts. The `web_music` client is added for `music.youtube.com` URLs when logged-in cookies are used. The `web_embedded` client is added for age-restricted videos but only successfully works around the age-restriction sometimes (e.g. if the video is embeddable), and may be added as a fallback if `android_vr` is unable to access a video. The `web_creator` client is added for age-restricted videos if account age-verification is required. Some clients, such as `web_creator` and `web_music`, require a `po_token` for their formats to be downloadable. Some clients, such as `web_creator`, will only work with authentication. Not all clients support authentication via cookies. You can use `default` for the default clients, or you can use `all` for all clients (not recommended). You can prefix a client with `-` to exclude it, e.g. `youtube:player_client=default,-web` * `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`. Using these will not skip any network requests, and in some cases will result in additional network requests. Currently, the default is `player_response`; however, typically these are for testing purposes only +* `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. Neither is skipped by default; however, if a `player_js_version` value other than `actual` is used, then `webpage_skip=player_response` is implied * `webpage_client`: Client to use for the video webpage request. One of `web` or `web_safari` (default) * `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`, `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`). Currently, the default is to force `20514@9f4cc5e4`. 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`. Using any other value will imply `webpage_skip=player_response` * `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` * A `max-depth` value of `1` will discard all replies, regardless of the `max-replies` or `max-replies-per-thread` values given diff --git a/pyproject.toml b/pyproject.toml index 7a4e860862..8c8371f783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ default = [ "requests>=2.32.2,<3", "urllib3>=2.0.2,<3", "websockets>=13.0", - "yt-dlp-ejs==0.5.0", + "yt-dlp-ejs==0.7.0", ] curl-cffi = [ "curl-cffi>=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.15; implementation_name=='cpython'", diff --git a/test/test_jsc/test_ejs_integration.py b/test/test_jsc/test_ejs_integration.py index 64ecffd8fa..30aee1b694 100644 --- a/test/test_jsc/test_ejs_integration.py +++ b/test/test_jsc/test_ejs_integration.py @@ -53,117 +53,49 @@ class Challenge: CHALLENGES: list[Challenge] = [ - Challenge('3d3ba064', Variant.tce, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': 'qmtUsIz04xxiNW', - '4GMrWHyKI5cEvhDO': 'N9gmEX7YhKTSmw', + # 20518 + Challenge('edc3ba07', Variant.tv, JsChallengeType.N, { + 'BQoJvGBkC2nj1ZZLK-': '-m-se9fQVnvEofLx', }), - Challenge('3d3ba064', Variant.tce, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3gqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNG_1kNyBf6HPuAuCduh-a7O', - }), - Challenge('5ec65609', Variant.tce, JsChallengeType.N, { - '0eRGgQWJGfT5rFHFj': '4SvMpDQH-vBJCw', - }), - Challenge('5ec65609', Variant.tce, JsChallengeType.SIG, { - 'AAJAJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grH0rTMICA1mmDc0HoXgW3CAiAQQ4=CspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ=I': - 'AJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grI0rTMICA1mmDc0HoXgW3CAiAQQ4HCspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ==', - }), - Challenge('6742b2b9', Variant.tce, JsChallengeType.N, { - '_HPB-7GFg1VTkn9u': 'qUAsPryAO_ByYg', - 'K1t_fcB6phzuq2SF': 'Y7PcOt3VE62mog', - }), - Challenge('6742b2b9', Variant.tce, JsChallengeType.SIG, { - 'MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA': - 'AJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHM8EjoCACIEQnz-nKN5RgG6iUTnNJC58csYPSrnS_SzricuUMJZGM', - }), - Challenge('2b83d2e0', Variant.main, JsChallengeType.N, { - '0eRGgQWJGfT5rFHFj': 'euHbygrCMLksxd', - }), - Challenge('2b83d2e0', Variant.main, JsChallengeType.SIG, { - 'MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJA': - '-MGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKnMznQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJ', - }), - Challenge('638ec5c6', Variant.main, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': '1qov8-KM-yH', - }), - Challenge('638ec5c6', Variant.main, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'MhudCuAuP-6fByOk1_GNXN7gNHHShjyXS2VOgsEItAJz0tipeav0OmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt', - }), - # c1c87fb0: tce variant broke sig solving; n and main variant are added only for regression testing - Challenge('c1c87fb0', Variant.main, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': 'jCHBK5GuAFNa2', - }), - Challenge('c1c87fb0', Variant.main, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3DqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNGa1kOyBf6HPuAuCduh-_', - }), - Challenge('c1c87fb0', Variant.tce, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': 'jCHBK5GuAFNa2', - }), - Challenge('c1c87fb0', Variant.tce, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3DqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNGa1kOyBf6HPuAuCduh-_', - }), - # 4e51e895: main variant broke sig solving; n challenge is added only for regression testing - Challenge('4e51e895', Variant.main, JsChallengeType.N, { - '0eRGgQWJGfT5rFHFj': 't5kO23_msekBur', - }), - Challenge('4e51e895', Variant.main, JsChallengeType.SIG, { - 'AL6p_8AwdY9yAhRzK8rYA_9n97Kizf7_9n97Kizf7_9n97Kizf7_9n97Kizf7_9n97Kizf7_9n97Kizf7': - 'AwdY9yAhRzK8rYA_9n97Kizf7_9n97Kizf7_9n9pKizf7_9n97Kizf7_9n97Kizf7_9n97Kizf7', - }), - # 42c5570b: tce variant broke sig solving; n challenge is added only for regression testing - Challenge('42c5570b', Variant.tce, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': 'CRoXjB-R-R', - }), - Challenge('42c5570b', Variant.tce, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'EN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavcOmNdYN-wUtgEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt', - }), - # 54bd1de4: tce variant broke sig solving; n challenge is added only for regression testing - Challenge('54bd1de4', Variant.tce, JsChallengeType.N, { - 'ZdZIqFPQK-Ty8wId': 'ka-slAQ31sijFN', - }), - Challenge('54bd1de4', Variant.tce, JsChallengeType.SIG, { - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt': - 'gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0titeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtp', - }), - # 94667337: tce and es6 variants broke sig solving; n and main/tv variants are added only for regression testing - Challenge('94667337', Variant.main, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': 'ib1ShEOGoFXIIw', - }), - Challenge('94667337', Variant.main, JsChallengeType.SIG, { + Challenge('edc3ba07', Variant.tv, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'AJEij0EwRgIhAI0KExTgjfPk-MPM9MNdzyyPRtzBM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=', + 'zwg=wgwCHlydB9zg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlh5mt5OX-8MB=tRPyyEdAM9MPM-kPfjgTxEK0IAhIgRwE0jiz', }), - Challenge('94667337', Variant.tv, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': 'ib1ShEOGoFXIIw', + # 20521 + Challenge('316b61b4', Variant.tv, JsChallengeType.N, { + 'IlLiA21ny7gqA2m4p37': 'GchRcsUC_WmnhOUVGV', }), - Challenge('94667337', Variant.tv, JsChallengeType.SIG, { + Challenge('316b61b4', Variant.tv, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'AJEij0EwRgIhAI0KExTgjfPk-MPM9MNdzyyPRtzBM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=', + 'tJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRN=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwz', }), - Challenge('94667337', Variant.es6, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': 'ib1ShEOGoFXIIw', + # 20522 + Challenge('74edf1a3', Variant.tv, JsChallengeType.N, { + 'IlLiA21ny7gqA2m4p37': '9nRTxrbM1f0yHg', + 'eabGFpsUKuWHXGh6FR4': 'izmYqDEY6kl7Sg', + 'eabGF/ps%UK=uWHXGh6FR4': 'LACmqlhaBpiPlgE-a', }), - Challenge('94667337', Variant.es6, JsChallengeType.SIG, { + Challenge('74edf1a3', Variant.tv, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'AJEij0EwRgIhAI0KExTgjfPk-MPM9MNdzyyPRtzBM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=', + 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hzMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzl', + '\x00\x01\x02%\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49': + '\x00\x01\x02%\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x40\x41\x42\x49\x44\x45\x46\x47\x48\x43', }), - Challenge('94667337', Variant.tce, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': 'ib1ShEOGoFXIIw', + # 20523 + Challenge('901741ab', Variant.tv, JsChallengeType.N, { + 'BQoJvGBkC2nj1ZZLK-': 'UMPovvBZRh-sjb', }), - Challenge('94667337', Variant.tce, JsChallengeType.SIG, { + Challenge('901741ab', Variant.tv, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'AJEij0EwRgIhAI0KExTgjfPk-MPM9MNdzyyPRtzBM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=', + 'wgwCHlydB9Hg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlhwmt5ON-8MB=5RPyyzdAM9MPM-kPfjgTxEK0IAhIgRwE0jiEJA', }), - Challenge('94667337', Variant.es6_tce, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': 'ib1ShEOGoFXIIw', + # 20524 + Challenge('e7573094', Variant.tv, JsChallengeType.N, { + 'IlLiA21ny7gqA2m4p37': '3KuQ3235dojTSjo4', }), - Challenge('94667337', Variant.es6_tce, JsChallengeType.SIG, { + Challenge('e7573094', Variant.tv, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'AJEij0EwRgIhAI0KExTgjfPk-MPM9MNdzyyPRtzBM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=', + 'yEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyNPRt=BM8-XO5tm5hlMCSVNAiEAvpeP3CURqZJSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=g', }), ] diff --git a/yt_dlp/extractor/youtube/_video.py b/yt_dlp/extractor/youtube/_video.py index 1ebef33676..04510da25d 100644 --- a/yt_dlp/extractor/youtube/_video.py +++ b/yt_dlp/extractor/youtube/_video.py @@ -1875,12 +1875,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'params': {'skip_download': True}, }] - @property - def _skipped_webpage_data(self): - # XXX: player_response as a default is a TEMPORARY workaround for pinning _DEFAULT_PLAYER_JS_VERSION - return self._configuration_arg('webpage_skip', default=['player_response']) - - _DEFAULT_PLAYER_JS_VERSION = '20514@9f4cc5e4' + _DEFAULT_PLAYER_JS_VERSION = 'actual' _DEFAULT_PLAYER_JS_VARIANT = 'tv' _PLAYER_JS_VARIANT_MAP = { 'main': 'player_ias.vflset/en_US/base.js', @@ -1897,6 +1892,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor): } _INVERSE_PLAYER_JS_VARIANT_MAP = {v: k for k, v in _PLAYER_JS_VARIANT_MAP.items()} + @functools.cached_property + def _player_js_version(self): + return self._configuration_arg('player_js_version', [None])[0] or self._DEFAULT_PLAYER_JS_VERSION + + @functools.cached_property + def _skipped_webpage_data(self): + skipped = set(self._configuration_arg('webpage_skip')) + # If forcing a player version, the webpage player response must be skipped + if self._player_js_version != 'actual': + skipped.add('player_response') + return skipped + @classmethod def suitable(cls, url): from yt_dlp.utils import parse_qs @@ -2084,15 +2091,14 @@ class YoutubeIE(YoutubeBaseInfoExtractor): time.sleep(max(0, FETCH_SPAN + fetch_time - time.time())) def _get_player_js_version(self): - player_js_version = self._configuration_arg('player_js_version', [''])[0] or self._DEFAULT_PLAYER_JS_VERSION - if player_js_version == 'actual': + if self._player_js_version == 'actual': return None, None - if not re.fullmatch(r'[0-9]{5,}@[0-9a-f]{8,}', player_js_version): + if not re.fullmatch(r'[0-9]{5,}@[0-9a-f]{8,}', self._player_js_version): self.report_warning( - f'Invalid player JS version "{player_js_version}" specified. ' + f'Invalid player JS version "{self._player_js_version}" specified. ' f'It should be "actual" or in the format of STS@HASH', only_once=True) return None, None - return player_js_version.split('@') + return self._player_js_version.split('@') def _construct_player_url(self, *, player_id=None, player_url=None): assert player_id or player_url, '_construct_player_url must take one of player_id or player_url' diff --git a/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py b/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py index 46e52dc94a..e23738eb28 100644 --- a/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py +++ b/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py @@ -1,10 +1,10 @@ # This file is generated by devscripts/update_ejs.py. DO NOT MODIFY! -VERSION = '0.5.0' +VERSION = '0.7.0' HASHES = { 'yt.solver.bun.lib.js': '6ff45e94de9f0ea936a183c48173cfa9ce526ee4b7544cd556428427c1dd53c8073ef0174e79b320252bf0e7c64b0032cc1cf9c4358f3fda59033b7caa01c241', - 'yt.solver.core.js': '9742868113d7b0c29e24a95c8eb2c2bec7cdf95513dc7f55f523ba053c0ecf2af7dcb0138b1d933578304f0dda633a6b3bfff64e912b4c547b99dad083428c4b', - 'yt.solver.core.min.js': 'aee8c3354cfd535809c871c2a517d03231f89cd184e903af82ee274bcc2e90991ef19cb3f65f2ccc858c4963856ea87f8692fe16d71209f4fc7f41c44b828e36', + 'yt.solver.core.js': '84e91a8ae91684272d11f1ef0970c757e9fec9ab277fb415b976c156163dde6ae2a9857c19c1ee21c9dcd01e2f89071098a1de2dc3072cf3ceeded84537db5e4', + 'yt.solver.core.min.js': 'd965ec01dcf44a0a9dea43f5935141c788471de9e8def5bf70d0b88ca656b79ca983d3e595f84b788d921dc98b900b7bf7380e9775ccb3b70a87c865482c71e3', 'yt.solver.deno.lib.js': '9c8ee3ab6c23e443a5a951e3ac73c6b8c1c8fb34335e7058a07bf99d349be5573611de00536dcd03ecd3cf34014c4e9b536081de37af3637c5390c6a6fd6a0f0', 'yt.solver.lib.js': '1ee3753a8222fc855f5c39db30a9ccbb7967dbe1fb810e86dc9a89aa073a0907f294c720e9b65427d560a35aa1ce6af19ef854d9126a05ca00afe03f72047733', 'yt.solver.lib.min.js': '8420c259ad16e99ce004e4651ac1bcabb53b4457bf5668a97a9359be9a998a789fee8ab124ee17f91a2ea8fd84e0f2b2fc8eabcaf0b16a186ba734cf422ad053', diff --git a/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js b/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js index 7d37310537..31a20a0ff6 100644 --- a/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js +++ b/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js @@ -39,284 +39,8 @@ var jsc = (function (meriyah, astring) { function isOneOf(value, ...of) { return of.includes(value); } - function _optionalChain$2(ops) { - let lastAccessLHS = undefined; - let value = ops[0]; - let i = 1; - while (i < ops.length) { - const op = ops[i]; - const fn = ops[i + 1]; - i += 2; - if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { - return undefined; - } - if (op === 'access' || op === 'optionalAccess') { - lastAccessLHS = value; - value = fn(value); - } else if (op === 'call' || op === 'optionalCall') { - value = fn((...args) => value.call(lastAccessLHS, ...args)); - lastAccessLHS = undefined; - } - } - return value; - } - const nsig = { - type: 'CallExpression', - callee: { or: [{ type: 'Identifier' }, { type: 'SequenceExpression' }] }, - arguments: [ - {}, - { - type: 'CallExpression', - callee: { type: 'Identifier', name: 'decodeURIComponent' }, - arguments: [{}], - }, - ], - }; - const nsigAssignment = { - type: 'AssignmentExpression', - left: { type: 'Identifier' }, - operator: '=', - right: nsig, - }; - const nsigDeclarator = { - type: 'VariableDeclarator', - id: { type: 'Identifier' }, - init: nsig, - }; - const logicalExpression = { - type: 'ExpressionStatement', - expression: { - type: 'LogicalExpression', - left: { type: 'Identifier' }, - right: { - type: 'SequenceExpression', - expressions: [ - { - type: 'AssignmentExpression', - left: { type: 'Identifier' }, - operator: '=', - right: { - type: 'CallExpression', - callee: { type: 'Identifier' }, - arguments: { - or: [ - [ - { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'decodeURIComponent', - }, - arguments: [{ type: 'Identifier' }], - optional: false, - }, - ], - [ - { type: 'Literal' }, - { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'decodeURIComponent', - }, - arguments: [{ type: 'Identifier' }], - optional: false, - }, - ], - [ - { type: 'Literal' }, - { type: 'Literal' }, - { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: 'decodeURIComponent', - }, - arguments: [{ type: 'Identifier' }], - optional: false, - }, - ], - ], - }, - optional: false, - }, - }, - { type: 'CallExpression' }, - ], - }, - operator: '&&', - }, - }; - const identifier$1 = { - or: [ - { - type: 'ExpressionStatement', - expression: { - type: 'AssignmentExpression', - operator: '=', - left: { or: [{ type: 'Identifier' }, { type: 'MemberExpression' }] }, - right: { type: 'FunctionExpression' }, - }, - }, - { type: 'FunctionDeclaration' }, - { - type: 'VariableDeclaration', - declarations: { - anykey: [ - { - type: 'VariableDeclarator', - init: { type: 'FunctionExpression' }, - }, - ], - }, - }, - ], - }; - function extract$1(node) { - const blocks = []; - if (matchesStructure(node, identifier$1)) { - if ( - node.type === 'ExpressionStatement' && - node.expression.type === 'AssignmentExpression' && - node.expression.right.type === 'FunctionExpression' && - node.expression.right.params.length >= 3 - ) { - blocks.push(node.expression.right.body); - } else if (node.type === 'VariableDeclaration') { - for (const decl of node.declarations) { - if ( - _optionalChain$2([ - decl, - 'access', - (_) => _.init, - 'optionalAccess', - (_2) => _2.type, - ]) === 'FunctionExpression' && - decl.init.params.length >= 3 - ) { - blocks.push(decl.init.body); - } - } - } else if ( - node.type === 'FunctionDeclaration' && - node.params.length >= 3 - ) { - blocks.push(node.body); - } else { - return null; - } - } else if ( - node.type === 'ExpressionStatement' && - node.expression.type === 'SequenceExpression' - ) { - for (const expr of node.expression.expressions) { - if ( - expr.type === 'AssignmentExpression' && - expr.right.type === 'FunctionExpression' && - expr.right.params.length === 3 - ) { - blocks.push(expr.right.body); - } - } - } else { - return null; - } - for (const block of blocks) { - let call = null; - for (const stmt of block.body) { - if (matchesStructure(stmt, logicalExpression)) { - if ( - stmt.type === 'ExpressionStatement' && - stmt.expression.type === 'LogicalExpression' && - stmt.expression.right.type === 'SequenceExpression' && - stmt.expression.right.expressions[0].type === - 'AssignmentExpression' && - stmt.expression.right.expressions[0].right.type === 'CallExpression' - ) { - call = stmt.expression.right.expressions[0].right; - } - } else if (stmt.type === 'IfStatement') { - let consequent = stmt.consequent; - while (consequent.type === 'LabeledStatement') { - consequent = consequent.body; - } - if (consequent.type !== 'BlockStatement') { - continue; - } - for (const n of consequent.body) { - if (n.type !== 'VariableDeclaration') { - continue; - } - for (const decl of n.declarations) { - if ( - matchesStructure(decl, nsigDeclarator) && - _optionalChain$2([ - decl, - 'access', - (_3) => _3.init, - 'optionalAccess', - (_4) => _4.type, - ]) === 'CallExpression' - ) { - call = decl.init; - break; - } - } - if (call) { - break; - } - } - } else if (stmt.type === 'ExpressionStatement') { - if ( - stmt.expression.type !== 'LogicalExpression' || - stmt.expression.operator !== '&&' || - stmt.expression.right.type !== 'SequenceExpression' - ) { - continue; - } - for (const expr of stmt.expression.right.expressions) { - if (matchesStructure(expr, nsigAssignment) && expr.type) { - if ( - expr.type === 'AssignmentExpression' && - expr.right.type === 'CallExpression' - ) { - call = expr.right; - break; - } - } - } - } - if (call) { - break; - } - } - if (!call) { - continue; - } - return { - type: 'ArrowFunctionExpression', - params: [{ type: 'Identifier', name: 'sig' }], - body: { - type: 'CallExpression', - callee: call.callee, - arguments: call.arguments.map((arg) => { - if ( - arg.type === 'CallExpression' && - arg.callee.type === 'Identifier' && - arg.callee.name === 'decodeURIComponent' - ) { - return { type: 'Identifier', name: 'sig' }; - } - return arg; - }), - optional: false, - }, - async: false, - expression: false, - generator: false, - }; - } - return null; + function generateArrowFunction(data) { + return meriyah.parse(data).body[0].expression; } function _optionalChain$1(ops) { let lastAccessLHS = undefined; @@ -341,153 +65,114 @@ var jsc = (function (meriyah, astring) { } const identifier = { or: [ + { + type: 'ExpressionStatement', + expression: { + type: 'AssignmentExpression', + operator: '=', + left: { or: [{ type: 'Identifier' }, { type: 'MemberExpression' }] }, + right: { type: 'FunctionExpression', async: false }, + }, + }, + { type: 'FunctionDeclaration', async: false, id: { type: 'Identifier' } }, { type: 'VariableDeclaration', - kind: 'var', declarations: { anykey: [ { type: 'VariableDeclarator', - id: { type: 'Identifier' }, - init: { - type: 'ArrayExpression', - elements: [{ type: 'Identifier' }], - }, + init: { type: 'FunctionExpression', async: false }, }, ], }, }, - { - type: 'ExpressionStatement', - expression: { - type: 'AssignmentExpression', - left: { type: 'Identifier' }, - operator: '=', - right: { - type: 'ArrayExpression', - elements: [{ type: 'Identifier' }], - }, - }, - }, ], }; - const catchBlockBody = [ - { - type: 'ReturnStatement', - argument: { - type: 'BinaryExpression', - left: { - type: 'MemberExpression', - object: { type: 'Identifier' }, - computed: true, - property: { type: 'Literal' }, - optional: false, - }, - right: { type: 'Identifier' }, - operator: '+', + const asdasd = { + type: 'ExpressionStatement', + expression: { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { type: 'Identifier' }, + property: {}, + optional: false, }, + arguments: [ + { type: 'Literal', value: 'alr' }, + { type: 'Literal', value: 'yes' }, + ], + optional: false, }, - ]; + }; function extract(node) { if (!matchesStructure(node, identifier)) { - let name = null; - let block = null; - switch (node.type) { - case 'ExpressionStatement': { - if ( - node.expression.type === 'AssignmentExpression' && - node.expression.left.type === 'Identifier' && - node.expression.right.type === 'FunctionExpression' && - node.expression.right.params.length === 1 - ) { - name = node.expression.left.name; - block = node.expression.right.body; - } - break; - } - case 'FunctionDeclaration': { - if (node.params.length === 1) { - name = _optionalChain$1([ - node, - 'access', - (_) => _.id, - 'optionalAccess', - (_2) => _2.name, - ]); - block = node.body; - } - break; - } - } - if (!block || !name) { - return null; - } - const tryNode = block.body.at(-2); - if ( - _optionalChain$1([tryNode, 'optionalAccess', (_3) => _3.type]) !== - 'TryStatement' || - _optionalChain$1([ - tryNode, - 'access', - (_4) => _4.handler, - 'optionalAccess', - (_5) => _5.type, - ]) !== 'CatchClause' - ) { - return null; - } - const catchBody = tryNode.handler.body.body; - if (matchesStructure(catchBody, catchBlockBody)) { - return makeSolverFuncFromName(name); - } return null; } - if (node.type === 'VariableDeclaration') { - for (const declaration of node.declarations) { - if ( - declaration.type !== 'VariableDeclarator' || - !declaration.init || - declaration.init.type !== 'ArrayExpression' || - declaration.init.elements.length !== 1 - ) { - continue; - } - const [firstElement] = declaration.init.elements; - if (firstElement && firstElement.type === 'Identifier') { - return makeSolverFuncFromName(firstElement.name); - } + const options = []; + if (node.type === 'FunctionDeclaration') { + if ( + node.id && + _optionalChain$1([ + node, + 'access', + (_) => _.body, + 'optionalAccess', + (_2) => _2.body, + ]) + ) { + options.push({ + name: node.id, + statements: _optionalChain$1([ + node, + 'access', + (_3) => _3.body, + 'optionalAccess', + (_4) => _4.body, + ]), + }); } } else if (node.type === 'ExpressionStatement') { - const expr = node.expression; - if ( - expr.type === 'AssignmentExpression' && - expr.left.type === 'Identifier' && - expr.operator === '=' && - expr.right.type === 'ArrayExpression' && - expr.right.elements.length === 1 - ) { - const [firstElement] = expr.right.elements; - if (firstElement && firstElement.type === 'Identifier') { - return makeSolverFuncFromName(firstElement.name); + if (node.expression.type !== 'AssignmentExpression') { + return null; + } + const name = node.expression.left; + const body = _optionalChain$1([ + node.expression.right, + 'optionalAccess', + (_5) => _5.body, + 'optionalAccess', + (_6) => _6.body, + ]); + if (name && body) { + options.push({ name: name, statements: body }); + } + } else if (node.type === 'VariableDeclaration') { + for (const declaration of node.declarations) { + const name = declaration.id; + const body = _optionalChain$1([ + declaration.init, + 'optionalAccess', + (_7) => _7.body, + 'optionalAccess', + (_8) => _8.body, + ]); + if (name && body) { + options.push({ name: name, statements: body }); } } } + for (const { name: name, statements: statements } of options) { + if (matchesStructure(statements, { anykey: [asdasd] })) { + return createSolver(name); + } + } return null; } - function makeSolverFuncFromName(name) { - return { - type: 'ArrowFunctionExpression', - params: [{ type: 'Identifier', name: 'n' }], - body: { - type: 'CallExpression', - callee: { type: 'Identifier', name: name }, - arguments: [{ type: 'Identifier', name: 'n' }], - optional: false, - }, - async: false, - expression: false, - generator: false, - }; + function createSolver(expression) { + return generateArrowFunction( + `\n({sig, n}) => {\n const url = (${astring.generate(expression)})("https://youtube.com/watch?v=yt-dlp-wins", "s", sig ? encodeURIComponent(sig) : undefined);\n url.set("n", n);\n const proto = Object.getPrototypeOf(url);\n const keys = Object.keys(proto).concat(Object.getOwnPropertyNames(proto));\n for (const key of keys) {\n if (!["constructor", "set", "get", "clone"].includes(key)) {\n url[key]();\n break;\n }\n }\n const s = url.get("s");\n return {\n sig: s ? decodeURIComponent(s) : null,\n n: url.get("n") ?? null,\n };\n}\n`, + ); } const setupNodes = meriyah.parse( `\nif (typeof globalThis.XMLHttpRequest === "undefined") {\n globalThis.XMLHttpRequest = { prototype: {} };\n}\nconst window = Object.create(null);\nif (typeof URL === "undefined") {\n window.location = {\n hash: "",\n host: "www.youtube.com",\n hostname: "www.youtube.com",\n href: "https://www.youtube.com/watch?v=yt-dlp-wins",\n origin: "https://www.youtube.com",\n password: "",\n pathname: "/watch",\n port: "",\n protocol: "https:",\n search: "?v=yt-dlp-wins",\n username: "",\n };\n} else {\n window.location = new URL("https://www.youtube.com/watch?v=yt-dlp-wins");\n}\nif (typeof globalThis.document === "undefined") {\n globalThis.document = Object.create(null);\n}\nif (typeof globalThis.navigator === "undefined") {\n globalThis.navigator = Object.create(null);\n}\nif (typeof globalThis.self === "undefined") {\n globalThis.self = globalThis;\n}\n`, @@ -585,235 +270,59 @@ var jsc = (function (meriyah, astring) { function getSolutions(statements) { const found = { n: [], sig: [] }; for (const statement of statements) { - const n = extract(statement); - if (n) { - found.n.push(n); - } - const sig = extract$1(statement); - if (sig) { - found.sig.push(sig); + const result = extract(statement); + if (result) { + found.n.push(makeSolver(result, { type: 'Identifier', name: 'n' })); + found.sig.push(makeSolver(result, { type: 'Identifier', name: 'sig' })); } } return found; } + function makeSolver(result, ident) { + return { + type: 'ArrowFunctionExpression', + params: [ident], + body: { + type: 'MemberExpression', + object: { + type: 'CallExpression', + callee: result, + arguments: [ + { + type: 'ObjectExpression', + properties: [ + { + type: 'Property', + key: ident, + value: ident, + kind: 'init', + computed: false, + method: false, + shorthand: true, + }, + ], + }, + ], + optional: false, + }, + computed: false, + property: ident, + optional: false, + }, + async: false, + expression: true, + generator: false, + }; + } function getFromPrepared(code) { const resultObj = { n: null, sig: null }; Function('_result', code)(resultObj); return resultObj; } function multiTry(generators) { - return { - type: 'ArrowFunctionExpression', - params: [{ type: 'Identifier', name: '_input' }], - body: { - type: 'BlockStatement', - body: [ - { - type: 'VariableDeclaration', - kind: 'const', - declarations: [ - { - type: 'VariableDeclarator', - id: { type: 'Identifier', name: '_results' }, - init: { - type: 'NewExpression', - callee: { type: 'Identifier', name: 'Set' }, - arguments: [], - }, - }, - ], - }, - { - type: 'ForOfStatement', - left: { - type: 'VariableDeclaration', - kind: 'const', - declarations: [ - { - type: 'VariableDeclarator', - id: { type: 'Identifier', name: '_generator' }, - init: null, - }, - ], - }, - right: { type: 'ArrayExpression', elements: generators }, - body: { - type: 'BlockStatement', - body: [ - { - type: 'TryStatement', - block: { - type: 'BlockStatement', - body: [ - { - type: 'ExpressionStatement', - expression: { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { type: 'Identifier', name: '_results' }, - computed: false, - property: { type: 'Identifier', name: 'add' }, - optional: false, - }, - arguments: [ - { - type: 'CallExpression', - callee: { - type: 'Identifier', - name: '_generator', - }, - arguments: [ - { type: 'Identifier', name: '_input' }, - ], - optional: false, - }, - ], - optional: false, - }, - }, - ], - }, - handler: { - type: 'CatchClause', - param: null, - body: { type: 'BlockStatement', body: [] }, - }, - finalizer: null, - }, - ], - }, - await: false, - }, - { - type: 'IfStatement', - test: { - type: 'UnaryExpression', - operator: '!', - argument: { - type: 'MemberExpression', - object: { type: 'Identifier', name: '_results' }, - computed: false, - property: { type: 'Identifier', name: 'size' }, - optional: false, - }, - prefix: true, - }, - consequent: { - type: 'BlockStatement', - body: [ - { - type: 'ThrowStatement', - argument: { - type: 'TemplateLiteral', - expressions: [], - quasis: [ - { - type: 'TemplateElement', - value: { cooked: 'no solutions', raw: 'no solutions' }, - tail: true, - }, - ], - }, - }, - ], - }, - alternate: null, - }, - { - type: 'IfStatement', - test: { - type: 'BinaryExpression', - left: { - type: 'MemberExpression', - object: { type: 'Identifier', name: '_results' }, - computed: false, - property: { type: 'Identifier', name: 'size' }, - optional: false, - }, - right: { type: 'Literal', value: 1 }, - operator: '!==', - }, - consequent: { - type: 'BlockStatement', - body: [ - { - type: 'ThrowStatement', - argument: { - type: 'TemplateLiteral', - expressions: [ - { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { type: 'Identifier', name: '_results' }, - computed: false, - property: { type: 'Identifier', name: 'join' }, - optional: false, - }, - arguments: [{ type: 'Literal', value: ', ' }], - optional: false, - }, - ], - quasis: [ - { - type: 'TemplateElement', - value: { - cooked: 'invalid solutions: ', - raw: 'invalid solutions: ', - }, - tail: false, - }, - { - type: 'TemplateElement', - value: { cooked: '', raw: '' }, - tail: true, - }, - ], - }, - }, - ], - }, - alternate: null, - }, - { - type: 'ReturnStatement', - argument: { - type: 'MemberExpression', - object: { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { type: 'Identifier', name: '_results' }, - computed: false, - property: { type: 'Identifier', name: 'values' }, - optional: false, - }, - arguments: [], - optional: false, - }, - computed: false, - property: { type: 'Identifier', name: 'next' }, - optional: false, - }, - arguments: [], - optional: false, - }, - computed: false, - property: { type: 'Identifier', name: 'value' }, - optional: false, - }, - }, - ], - }, - async: false, - expression: false, - generator: false, - }; + return generateArrowFunction( + `\n(_input) => {\n const _results = new Set();\n const errors = [];\n for (const _generator of ${astring.generate({ type: 'ArrayExpression', elements: generators })}) {\n try {\n _results.add(_generator(_input));\n } catch (e) {\n errors.push(e);\n }\n }\n if (!_results.size) {\n throw \`no solutions: \${errors.join(", ")}\`;\n }\n if (_results.size !== 1) {\n throw \`invalid solutions: \${[..._results].map(x => JSON.stringify(x)).join(", ")}\`;\n }\n return _results.values().next().value;\n}\n`, + ); } function main(input) { const preprocessedPlayer =