Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package yt-dlp for openSUSE:Factory checked in at 2026-03-18 16:50:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yt-dlp (Old) and /work/SRC/openSUSE:Factory/.yt-dlp.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yt-dlp" Wed Mar 18 16:50:21 2026 rev:90 rq:1340810 version:2026.03.17 Changes: -------- --- /work/SRC/openSUSE:Factory/yt-dlp/yt-dlp.changes 2026-03-13 21:21:49.681775823 +0100 +++ /work/SRC/openSUSE:Factory/.yt-dlp.new.8177/yt-dlp.changes 2026-03-18 16:51:51.968441302 +0100 @@ -1,0 +2,7 @@ +Wed Mar 18 01:07:53 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 2026.03.17 + * Always respect webpage_client extractor-arg + * Fix --live-from-start support + +------------------------------------------------------------------- Old: ---- yt_dlp_ejs-0.7.0-py3-none-any.whl New: ---- yt_dlp_ejs-0.8.0-py3-none-any.whl ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yt-dlp.spec ++++++ --- /var/tmp/diff_new_pack.GbX2c2/_old 2026-03-18 16:51:53.800516812 +0100 +++ /var/tmp/diff_new_pack.GbX2c2/_new 2026-03-18 16:51:53.800516812 +0100 @@ -27,8 +27,8 @@ %endif Name: yt-dlp -Version: 2026.03.13 -%define ejsver 0.7.0 +Version: 2026.03.17 +%define ejsver 0.8.0 Release: 0 Summary: Enhanced fork of youtube-dl, a video site downloader for offline watching License: CC-BY-SA-3.0 AND SUSE-Public-Domain ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.GbX2c2/_old 2026-03-18 16:51:53.856519120 +0100 +++ /var/tmp/diff_new_pack.GbX2c2/_new 2026-03-18 16:51:53.860519285 +0100 @@ -1,5 +1,5 @@ -mtime: 1773395486 -commit: 4dcaef1d42b3f31d08e089a22c26f3e8d491326150f53e226d7251a38e21492b +mtime: 1773796113 +commit: d7051c00cd3ab6e41c8a6a8c63f651a5a526b1e7a05f0cf4a5f6c501fc25dadb url: https://src.opensuse.org/jengelh/yt-dlp revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-03-18 02:11:23.000000000 +0100 @@ -0,0 +1 @@ +.osc ++++++ yt-dlp.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/Changelog.md new/yt-dlp/Changelog.md --- old/yt-dlp/Changelog.md 2026-03-13 09:45:29.000000000 +0100 +++ new/yt-dlp/Changelog.md 2026-03-18 00:25:42.000000000 +0100 @@ -4,6 +4,20 @@ # To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master --> +### 2026.03.17 + +#### Extractor changes +- **youtube** + - [Always respect `webpage_client` extractor-arg](https://github.com/yt-dlp/yt-dlp/commit/66c4947e9cb70c9de96f7da75f9acbe4192d6c9d) ([#16250](https://github.com/yt-dlp/yt-dlp/issues/16250)) by [bashonly](https://github.com/bashonly) + - [Fix `--live-from-start` support](https://github.com/yt-dlp/yt-dlp/commit/1b6ec8fc2589a1733a0937270faa4230ce6b1ca5) ([#16254](https://github.com/yt-dlp/yt-dlp/issues/16254)) by [bashonly](https://github.com/bashonly) + - [Update ejs to 0.8.0](https://github.com/yt-dlp/yt-dlp/commit/04d6974f502bbdfaed72c624344f262e30ad9708) ([#16269](https://github.com/yt-dlp/yt-dlp/issues/16269)) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K) + +#### Misc. changes +- **build**: [Use PyInstaller v6.19.0 for Windows](https://github.com/yt-dlp/yt-dlp/commit/7fab4c2b23e16c4a4f94020a37a6bdf8d502be37) ([#16265](https://github.com/yt-dlp/yt-dlp/issues/16265)) by [bashonly](https://github.com/bashonly) +- **ci**: [Bump actions pins](https://github.com/yt-dlp/yt-dlp/commit/4fc768b7f7194a05b13ad3e7bc5bfde84ed9ede7) ([#16252](https://github.com/yt-dlp/yt-dlp/issues/16252)) by [bashonly](https://github.com/bashonly) +- **docs**: [Fix `player_client` extractor-arg documentation](https://github.com/yt-dlp/yt-dlp/commit/e68afb28277b4bee39726dbcbb06801edde9f659) ([#16235](https://github.com/yt-dlp/yt-dlp/issues/16235)) by [bashonly](https://github.com/bashonly) +- **test**: networking: [Mark all CurlCFFIRH tests as flaky for any OS](https://github.com/yt-dlp/yt-dlp/commit/18656b2f2af41a138793c7012a88f467c0d90274) ([#16266](https://github.com/yt-dlp/yt-dlp/issues/16266)) by [bashonly](https://github.com/bashonly) + ### 2026.03.13 #### Extractor changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/Makefile new/yt-dlp/Makefile --- old/yt-dlp/Makefile 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/Makefile 2026-03-18 00:25:33.000000000 +0100 @@ -202,9 +202,9 @@ # The following EJS_-prefixed variables are auto-generated by devscripts/update_ejs.py # DO NOT EDIT! -EJS_VERSION = 0.7.0 -EJS_WHEEL_NAME = yt_dlp_ejs-0.7.0-py3-none-any.whl -EJS_WHEEL_HASH = sha256:967e9cbe114ddfd046ff4668af18b1827b4597e2e47a83deea668a355828c798 +EJS_VERSION = 0.8.0 +EJS_WHEEL_NAME = yt_dlp_ejs-0.8.0-py3-none-any.whl +EJS_WHEEL_HASH = sha256:79300e5fca7f937a1eeede11f0456862c1b41107ce1d726871e0207424f4bdb4 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/README.md new/yt-dlp/README.md --- old/yt-dlp/README.md 2026-03-13 09:45:33.000000000 +0100 +++ new/yt-dlp/README.md 2026-03-18 00:25:46.000000000 +0100 @@ -1860,12 +1860,12 @@ #### youtube * `lang`: Prefer translated metadata (`title`, `description` etc) of this language code (case-sensitive). By default, the video primary language metadata is preferred, with a fallback to `en` translated. See [youtube/_base.py](https://github.com/yt-dlp/yt-dlp/blob/415b4c9f955b1a0391204bd24a7132590e7b3bdb/yt_dlp/extractor/youtube/_base.py#L402-L409) for the list of supported content language codes * `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_crea tor` 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_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_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_safari` is used for free accounts and `tv_downgraded,web_creator` 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 `we b_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_safari` * `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. 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_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 `main`, 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`. 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` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/README.txt new/yt-dlp/README.txt --- old/yt-dlp/README.txt 2026-03-13 09:45:34.000000000 +0100 +++ new/yt-dlp/README.txt 2026-03-18 00:25:48.000000000 +0100 @@ -2313,11 +2313,11 @@ - 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 + tv_simply. By default, android_vr,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 + tv_downgraded,web_safari is used for free accounts and + tv_downgraded,web_creator 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 @@ -2330,7 +2330,7 @@ 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 + exclude it, e.g. youtube:player_client=default,-web_safari - 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 @@ -2349,7 +2349,7 @@ 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 + es6_tcc, es6_tce, tv, tv_es6, phone, house. The default is main, 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/pyproject.toml new/yt-dlp/pyproject.toml --- old/yt-dlp/pyproject.toml 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/pyproject.toml 2026-03-18 00:25:33.000000000 +0100 @@ -55,7 +55,7 @@ "requests>=2.32.2,<3", "urllib3>=2.0.2,<3", "websockets>=13.0", - "yt-dlp-ejs==0.7.0", + "yt-dlp-ejs==0.8.0", ] curl-cffi = [ "curl-cffi>=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.15; implementation_name=='cpython'", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/test/test_jsc/test_ejs_integration.py new/yt-dlp/test/test_jsc/test_ejs_integration.py --- old/yt-dlp/test/test_jsc/test_ejs_integration.py 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/test/test_jsc/test_ejs_integration.py 2026-03-18 00:25:33.000000000 +0100 @@ -53,50 +53,66 @@ CHALLENGES: list[Challenge] = [ - # 20518 - Challenge('edc3ba07', Variant.tv, JsChallengeType.N, { - 'BQoJvGBkC2nj1ZZLK-': '-m-se9fQVnvEofLx', - }), - Challenge('edc3ba07', Variant.tv, JsChallengeType.SIG, { - 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'zwg=wgwCHlydB9zg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlh5mt5OX-8MB=tRPyyEdAM9MPM-kPfjgTxEK0IAhIgRwE0jiz', - }), - # 20521 - Challenge('316b61b4', Variant.tv, JsChallengeType.N, { - 'IlLiA21ny7gqA2m4p37': 'GchRcsUC_WmnhOUVGV', - }), - Challenge('316b61b4', Variant.tv, JsChallengeType.SIG, { - 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': - 'tJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRN=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwz', - }), # 20522 - Challenge('74edf1a3', Variant.tv, JsChallengeType.N, { + Challenge('74edf1a3', Variant.main, JsChallengeType.N, { 'IlLiA21ny7gqA2m4p37': '9nRTxrbM1f0yHg', 'eabGFpsUKuWHXGh6FR4': 'izmYqDEY6kl7Sg', 'eabGF/ps%UK=uWHXGh6FR4': 'LACmqlhaBpiPlgE-a', }), - Challenge('74edf1a3', Variant.tv, JsChallengeType.SIG, { + Challenge('74edf1a3', Variant.main, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': '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', }), # 20523 - Challenge('901741ab', Variant.tv, JsChallengeType.N, { + Challenge('901741ab', Variant.main, JsChallengeType.N, { 'BQoJvGBkC2nj1ZZLK-': 'UMPovvBZRh-sjb', }), - Challenge('901741ab', Variant.tv, JsChallengeType.SIG, { + Challenge('901741ab', Variant.main, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': 'wgwCHlydB9Hg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlhwmt5ON-8MB=5RPyyzdAM9MPM-kPfjgTxEK0IAhIgRwE0jiEJA', }), # 20524 - Challenge('e7573094', Variant.tv, JsChallengeType.N, { + Challenge('e7573094', Variant.main, JsChallengeType.N, { 'IlLiA21ny7gqA2m4p37': '3KuQ3235dojTSjo4', }), - Challenge('e7573094', Variant.tv, JsChallengeType.SIG, { + Challenge('e7573094', Variant.main, JsChallengeType.SIG, { 'NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz': 'yEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyNPRt=BM8-XO5tm5hlMCSVNAiEAvpeP3CURqZJSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=g', }), + # 20525 + Challenge('9fcf08e8', Variant.main, JsChallengeType.N, { + 'Dyc5ALyWiO0VqwCiT': 'H2PLmmAmJsYjKA', + }), + Challenge('9fcf08e8', Variant.main, JsChallengeType.SIG, { + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a': + '\x6a\x69\x68\x67\x66\x65\x64\x63\x62\x61\x60\x5f\x5e\x5d\x5c\x5b\x5a\x59\x58\x57\x56\x55\x54\x53\x52\x51\x50\x4f\x4e\x4d\x4c\x4b\x4a\x49\x48\x47\x46\x45\x44\x43\x42\x41\x40\x3f\x3e\x3d\x3c\x3b\x3a\x39\x38\x37\x36\x35\x34\x33\x32\x31\x30\x2f\x2e\x2d\x2c\x2b\x2a\x29\x28\x27\x26\x25\x24\x23\x22\x21\x20\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10\x0f\x0e\x0d\x0c\x0b\x03\x09\x08\x07\x06\x05\x04\x0a', + }), + # 20527 + Challenge('21cd2156', Variant.main, JsChallengeType.N, { + 'CiOxDbW1WEE8Ti4w': 'ZcBE4klItiC4rQ', + }), + Challenge('21cd2156', Variant.main, JsChallengeType.SIG, { + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a': + '\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x00\x46\x47\x48\x49\x4a\x4b\x6a\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x4c', + }), + # 20528 + Challenge('5e55da5a', Variant.tv, JsChallengeType.N, { + 'FgTvzyq4jKv482R7': 'l26nyYSotkzDxg', + }), + Challenge('5e55da5a', Variant.tv, JsChallengeType.SIG, { + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a': + '\x46\x66\x65\x64\x63\x62\x61\x60\x5f\x5e\x67\x6a\x5b\x5a\x59\x58\x57\x56\x55\x54\x53\x52\x51\x50\x4f\x4e\x4d\x4c\x4b\x4a\x49\x48\x47\x2c\x45\x44\x43\x42\x41\x40\x3f\x3e\x3d\x3c\x3b\x3a\x39\x38\x13\x36\x35\x34\x33\x32\x31\x30\x2f\x2e\x2d\x5d\x2b\x2a\x29\x28\x27\x26\x25\x24\x23\x22\x21\x20\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x0c\x12\x11\x10\x0f\x0e\x0d\x00\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x37', + }), + # 20529 + Challenge('631d3938', Variant.main, JsChallengeType.N, { + 'KBx1qz7jMhxELa8c': 'ttPvh7WIptsgSw', + }), + Challenge('631d3938', Variant.main, JsChallengeType.SIG, { + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66': + '\x19\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x00\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63', + }), ] requests: list[JsChallengeRequest] = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/test/test_networking.py new/yt-dlp/test/test_networking.py --- old/yt-dlp/test/test_networking.py 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/test/test_networking.py 2026-03-18 00:25:33.000000000 +0100 @@ -312,7 +312,7 @@ @pytest.mark.parametrize('handler', ['Urllib', 'Requests', 'CurlCFFI'], indirect=True) [email protected]_flaky('CurlCFFI', os.name == 'nt', reason='segfaults') [email protected]_flaky('CurlCFFI', reason='segfaults') class TestHTTPRequestHandler(TestRequestHandlerBase): def test_verify_cert(self, handler): @@ -1100,7 +1100,7 @@ @pytest.mark.parametrize('handler', ['CurlCFFI'], indirect=True) [email protected]_flaky('CurlCFFI', os.name == 'nt', reason='segfaults') [email protected]_flaky('CurlCFFI', reason='segfaults') class TestCurlCFFIRequestHandler(TestRequestHandlerBase): @pytest.mark.parametrize('params,extensions', [ Binary files old/yt-dlp/yt-dlp and new/yt-dlp/yt-dlp differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt-dlp.1 new/yt-dlp/yt-dlp.1 --- old/yt-dlp/yt-dlp.1 2026-03-13 09:45:35.000000000 +0100 +++ new/yt-dlp/yt-dlp.1 2026-03-18 00:25:49.000000000 +0100 @@ -2715,12 +2715,12 @@ \f[V]web_embedded\f[R], \f[V]web_music\f[R], \f[V]web_creator\f[R], \f[V]mweb\f[R], \f[V]ios\f[R], \f[V]android\f[R], \f[V]android_vr\f[R], \f[V]tv\f[R], \f[V]tv_downgraded\f[R], and \f[V]tv_simply\f[R]. -By default, \f[V]android_vr,web,web_safari\f[R] is used. +By default, \f[V]android_vr,web_safari\f[R] is used. If no JavaScript runtime/engine is available, then only \f[V]android_vr\f[R] is used. If logged-in cookies are passed to yt-dlp, then -\f[V]tv_downgraded,web,web_safari\f[R] is used for free accounts and -\f[V]tv_downgraded,web_creator,web\f[R] is used for premium accounts. +\f[V]tv_downgraded,web_safari\f[R] is used for free accounts and +\f[V]tv_downgraded,web_creator\f[R] is used for premium accounts. The \f[V]web_music\f[R] client is added for \f[V]music.youtube.com\f[R] URLs when logged-in cookies are used. The \f[V]web_embedded\f[R] client is added for age-restricted videos but @@ -2737,7 +2737,7 @@ You can use \f[V]default\f[R] for the default clients, or you can use \f[V]all\f[R] for all clients (not recommended). You can prefix a client with \f[V]-\f[R] to exclude it, e.g. -\f[V]youtube:player_client=default,-web\f[R] +\f[V]youtube:player_client=default,-web_safari\f[R] .IP \[bu] 2 \f[V]player_skip\f[R]: Skip some network requests that are generally needed for robust extraction. @@ -2770,7 +2770,8 @@ The known variants are: \f[V]main\f[R], \f[V]tcc\f[R], \f[V]tce\f[R], \f[V]es5\f[R], \f[V]es6\f[R], \f[V]es6_tcc\f[R], \f[V]es6_tce\f[R], \f[V]tv\f[R], \f[V]tv_es6\f[R], \f[V]phone\f[R], \f[V]house\f[R]. -The default is \f[V]tv\f[R], and the others are for debugging purposes. +The default is \f[V]main\f[R], and the others are for debugging +purposes. You can use \f[V]actual\f[R] to go with what is prescribed by the site .IP \[bu] 2 \f[V]player_js_version\f[R]: The player javascript version to use for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/youtube/_video.py new/yt-dlp/yt_dlp/extractor/youtube/_video.py --- old/yt-dlp/yt_dlp/extractor/youtube/_video.py 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/youtube/_video.py 2026-03-18 00:25:33.000000000 +0100 @@ -1876,7 +1876,7 @@ }] _DEFAULT_PLAYER_JS_VERSION = 'actual' - _DEFAULT_PLAYER_JS_VARIANT = 'tv' + _DEFAULT_PLAYER_JS_VARIANT = 'main' _PLAYER_JS_VARIANT_MAP = { 'main': 'player_ias.vflset/en_US/base.js', 'tcc': 'player_ias_tcc.vflset/en_US/base.js', @@ -1897,6 +1897,16 @@ return self._configuration_arg('player_js_version', [None])[0] or self._DEFAULT_PLAYER_JS_VERSION @functools.cached_property + def _webpage_client(self): + webpage_client = self._configuration_arg('webpage_client', [self._DEFAULT_WEBPAGE_CLIENT])[0] + if webpage_client not in self._WEBPAGE_CLIENTS: + self.report_warning( + f'Invalid webpage_client "{webpage_client}" requested; ' + f'falling back to {self._DEFAULT_WEBPAGE_CLIENT}', only_once=True) + webpage_client = self._DEFAULT_WEBPAGE_CLIENT + return webpage_client + + @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 @@ -1929,13 +1939,13 @@ start_time = time.time() formats = [f for f in formats if f.get('is_from_start')] - def refetch_manifest(format_id, delay): + def refetch_manifest(itag, client_name, delay): nonlocal formats, start_time, is_live if time.time() <= start_time + delay: return _, _, _, _, prs, player_url = self._initial_extract( - url, smuggled_data, webpage_url, 'web', video_id) + url, smuggled_data, webpage_url, self._webpage_client, video_id) video_details = traverse_obj(prs, (..., 'videoDetails'), expected_type=dict) microformats = traverse_obj( prs, (..., 'microformat', 'playerMicroformatRenderer'), @@ -1944,20 +1954,20 @@ is_live = live_status == 'is_live' start_time = time.time() - def mpd_feed(format_id, delay): + def mpd_feed(itag, client_name, delay): """ @returns (manifest_url, manifest_stream_number, is_live) or None """ for retry in self.RetryManager(fatal=False): with lock: - refetch_manifest(format_id, delay) + refetch_manifest(itag, client_name, delay) - f = next((f for f in formats if f['format_id'] == format_id), None) + f = next((f for f in formats if f.get('_itag') == itag and f.get('_client') == client_name), None) if not f: if not is_live: retry.error = f'{video_id}: Video is no longer live' else: - retry.error = f'Cannot find refreshed manifest for format {format_id}{bug_reports_message()}' + retry.error = f'Cannot find refreshed manifest for format {itag}{bug_reports_message()}' continue # Formats from ended premieres will be missing a manifest_url @@ -1970,7 +1980,7 @@ for f in formats: f['is_live'] = is_live - gen = functools.partial(self._live_dash_fragments, video_id, f['format_id'], + gen = functools.partial(self._live_dash_fragments, video_id, f['_itag'], f['_client'], live_start_time, mpd_feed, not is_live and f.copy()) if is_live: f['fragments'] = gen @@ -1979,7 +1989,7 @@ f['fragments'] = LazyList(gen({})) del f['is_from_start'] - def _live_dash_fragments(self, video_id, format_id, live_start_time, mpd_feed, manifestless_orig_fmt, ctx): + def _live_dash_fragments(self, video_id, itag, client_name, live_start_time, mpd_feed, manifestless_orig_fmt, ctx): FETCH_SPAN, MAX_DURATION = 5, 432000 mpd_url, stream_number, is_live = None, None, True @@ -2003,7 +2013,7 @@ old_mpd_url = mpd_url last_error = ctx.pop('last_error', None) expire_fast = immediate or (last_error and isinstance(last_error, HTTPError) and last_error.status == 403) - mpd_url, stream_number, is_live = (mpd_feed(format_id, 5 if expire_fast else 18000) + mpd_url, stream_number, is_live = (mpd_feed(itag, client_name, 5 if expire_fast else 18000) or (mpd_url, stream_number, False)) if not refresh_sequence: if expire_fast and not is_live: @@ -2029,7 +2039,7 @@ _last_seq = int(re.search(r'(?:/|^)sq/(\d+)', fragments[-1]['path']).group(1)) return True, _last_seq - self.write_debug(f'[{video_id}] Generating fragments for format {format_id}') + self.write_debug(f'[{video_id}] Generating fragments for format {itag}') while is_live: fetch_time = time.time() if no_fragment_score > 30: @@ -3737,11 +3747,15 @@ sub[STREAMING_DATA_CLIENT_NAME] = client_name subtitles = self._merge_subtitles(subs, subtitles) # Prioritize HLS subs over DASH for f in formats: - if process_manifest_format(f, 'dash', client_name, f['format_id'], require_po_token and not po_token): + # Save original itag value as format_id because process_manifest_format mutates f + format_id = f['format_id'] + if process_manifest_format(f, 'dash', client_name, format_id, require_po_token and not po_token): f['filesize'] = int_or_none(self._search_regex( r'/clen/(\d+)', f.get('fragment_base_url') or f['url'], 'file size', default=None)) if needs_live_processing: f['is_from_start'] = True + f['_itag'] = format_id + f['_client'] = client_name yield f yield subtitles @@ -3902,15 +3916,9 @@ base_url = self.http_scheme() + '//www.youtube.com/' webpage_url = base_url + 'watch?v=' + video_id - webpage_client = self._configuration_arg('webpage_client', [self._DEFAULT_WEBPAGE_CLIENT])[0] - if webpage_client not in self._WEBPAGE_CLIENTS: - self.report_warning( - f'Invalid webpage_client "{webpage_client}" requested; ' - f'falling back to {self._DEFAULT_WEBPAGE_CLIENT}', only_once=True) - webpage_client = self._DEFAULT_WEBPAGE_CLIENT webpage, webpage_ytcfg, initial_data, is_premium_subscriber, player_responses, player_url = self._initial_extract( - url, smuggled_data, webpage_url, webpage_client, video_id) + url, smuggled_data, webpage_url, self._webpage_client, video_id) playability_statuses = traverse_obj( player_responses, (..., 'playabilityStatus'), expected_type=dict) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py new/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py --- old/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py 2026-03-18 00:25:33.000000000 +0100 @@ -1,10 +1,10 @@ # This file is generated by devscripts/update_ejs.py. DO NOT MODIFY! -VERSION = '0.7.0' +VERSION = '0.8.0' HASHES = { 'yt.solver.bun.lib.js': '6ff45e94de9f0ea936a183c48173cfa9ce526ee4b7544cd556428427c1dd53c8073ef0174e79b320252bf0e7c64b0032cc1cf9c4358f3fda59033b7caa01c241', - 'yt.solver.core.js': '84e91a8ae91684272d11f1ef0970c757e9fec9ab277fb415b976c156163dde6ae2a9857c19c1ee21c9dcd01e2f89071098a1de2dc3072cf3ceeded84537db5e4', - 'yt.solver.core.min.js': 'd965ec01dcf44a0a9dea43f5935141c788471de9e8def5bf70d0b88ca656b79ca983d3e595f84b788d921dc98b900b7bf7380e9775ccb3b70a87c865482c71e3', + 'yt.solver.core.js': 'c163a6f376db6ce3da47d516a28a8f2a0554ae95c58dc766f0a6e2b3894f2cef1ee07fa84beb442fa471aac4f300985added1657c7c94c4d1cfefe68920ab599', + 'yt.solver.core.min.js': 'ee5b307d07f55e91e4723edf5ac205cc877a474187849d757dc1322e38427b157a9d706d510c1723d3670f98e5a3f8cbcde77874a80406bd7204bc9fea30f283', 'yt.solver.deno.lib.js': '9c8ee3ab6c23e443a5a951e3ac73c6b8c1c8fb34335e7058a07bf99d349be5573611de00536dcd03ecd3cf34014c4e9b536081de37af3637c5390c6a6fd6a0f0', 'yt.solver.lib.js': '1ee3753a8222fc855f5c39db30a9ccbb7967dbe1fb810e86dc9a89aa073a0907f294c720e9b65427d560a35aa1ce6af19ef854d9126a05ca00afe03f72047733', 'yt.solver.lib.min.js': '8420c259ad16e99ce004e4651ac1bcabb53b4457bf5668a97a9359be9a998a789fee8ab124ee17f91a2ea8fd84e0f2b2fc8eabcaf0b16a186ba734cf422ad053', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js new/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js --- old/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js 2026-03-13 09:45:22.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js 2026-03-18 00:25:33.000000000 +0100 @@ -175,7 +175,7 @@ ); } 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`, + `\nif (typeof globalThis.XMLHttpRequest === "undefined") {\n globalThis.XMLHttpRequest = { prototype: {} };\n}\nif (typeof URL === "undefined") {\n globalThis.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 globalThis.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}\nif (typeof globalThis.window === "undefined") {\n globalThis. window = globalThis;\n}\n`, ).body; function _optionalChain(ops) { let lastAccessLHS = undefined; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/version.py new/yt-dlp/yt_dlp/version.py --- old/yt-dlp/yt_dlp/version.py 2026-03-13 09:45:29.000000000 +0100 +++ new/yt-dlp/yt_dlp/version.py 2026-03-18 00:25:41.000000000 +0100 @@ -1,8 +1,8 @@ # Autogenerated by devscripts/update-version.py -__version__ = '2026.03.13' +__version__ = '2026.03.17' -RELEASE_GIT_HEAD = '92f1d99dbe1e10d942ef0963f625dbc5bc0768aa' +RELEASE_GIT_HEAD = '04d6974f502bbdfaed72c624344f262e30ad9708' VARIANT = None @@ -12,4 +12,4 @@ ORIGIN = 'yt-dlp/yt-dlp' -_pkg_version = '2026.03.13' +_pkg_version = '2026.03.17'
