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-02-01 22:05:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yt-dlp (Old) and /work/SRC/openSUSE:Factory/.yt-dlp.new.1995 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yt-dlp" Sun Feb 1 22:05:46 2026 rev:85 rq:1330251 version:2026.01.31 Changes: -------- --- /work/SRC/openSUSE:Factory/yt-dlp/yt-dlp.changes 2026-01-31 16:17:41.129378418 +0100 +++ /work/SRC/openSUSE:Factory/.yt-dlp.new.1995/yt-dlp.changes 2026-02-01 22:07:08.844951490 +0100 @@ -1,0 +2,8 @@ +Sun Feb 1 09:43:09 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 2026.01.31 + * yt: Add `web_embedded` fallback for `android_vr` client + * yt: Remove broken `ios_downgraded` and `tv_embedded` player + clients + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yt-dlp.spec ++++++ --- /var/tmp/diff_new_pack.s2gNEm/_old 2026-02-01 22:07:11.249051890 +0100 +++ /var/tmp/diff_new_pack.s2gNEm/_new 2026-02-01 22:07:11.249051890 +0100 @@ -1,7 +1,7 @@ # # spec file for package yt-dlp # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -27,7 +27,7 @@ %endif Name: yt-dlp -Version: 2026.01.29 +Version: 2026.01.31 %define ejsver 0.4.0 Release: 0 Summary: Enhanced fork of youtube-dl, a video site downloader for offline watching @@ -42,8 +42,8 @@ BuildRequires: fdupes BuildRequires: make >= 4 BuildRequires: python-rpm-macros -BuildRequires: zip BuildRequires: unzip +BuildRequires: zip BuildArch: noarch Obsoletes: yt-dlp-bash-completion < %version-%release Provides: yt-dlp-bash-completion = %version-%release ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.s2gNEm/_old 2026-02-01 22:07:11.289053561 +0100 +++ /var/tmp/diff_new_pack.s2gNEm/_new 2026-02-01 22:07:11.293053728 +0100 @@ -1,5 +1,5 @@ -mtime: 1769795804 -commit: 05dfd3096119342778e353e360063d3c4da18d5fc3b1098aaeb5a4195a0baac8 +mtime: 1769939036 +commit: 9e1bdc6b836ccf67fa5c8414ac201018e108100fff4efb5de7353c2aaa744ca5 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-02-01 10:44:12.000000000 +0100 @@ -0,0 +1 @@ +.osc ++++++ yt-dlp.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/AUTHORS new/yt-dlp/AUTHORS --- old/yt-dlp/AUTHORS 2026-01-29 18:00:51.000000000 +0100 +++ new/yt-dlp/AUTHORS 2026-02-01 00:55:31.000000000 +0100 @@ -1171,6 +1171,7 @@ aviperes axelerometer [email protected] +azdlonky azeem barsnick bashonly @@ -1613,6 +1614,7 @@ theGeekPirate thedenv thegymguy +thematuu theychx thomasmllt tiktok diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/CONTRIBUTORS new/yt-dlp/CONTRIBUTORS --- old/yt-dlp/CONTRIBUTORS 2026-01-29 18:00:50.000000000 +0100 +++ new/yt-dlp/CONTRIBUTORS 2026-02-01 00:55:31.000000000 +0100 @@ -862,3 +862,5 @@ romainreignier Sytm zahlman +azdlonky +thematuu 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-01-29 18:00:45.000000000 +0100 +++ new/yt-dlp/Changelog.md 2026-02-01 00:55:25.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.01.31 + +#### Extractor changes +- **soop**: [Support subscription-only VODs](https://github.com/yt-dlp/yt-dlp/commit/d0bf3d0fc3455d411ae44c0a5dc974dd1481e3aa) ([#15523](https://github.com/yt-dlp/yt-dlp/issues/15523)) by [thematuu](https://github.com/thematuu) +- **unsupported**: [Update unsupported URLs](https://github.com/yt-dlp/yt-dlp/commit/bf5d8c2a663ac690711262aebc733c1b06a54b26) ([#15410](https://github.com/yt-dlp/yt-dlp/issues/15410)) by [bashonly](https://github.com/bashonly) +- **whyp**: [Extract more metadata](https://github.com/yt-dlp/yt-dlp/commit/0d8ee637e83d62edaf22aa85833a51c70d560389) ([#15757](https://github.com/yt-dlp/yt-dlp/issues/15757)) by [azdlonky](https://github.com/azdlonky) +- **youtube** + - [Add `web_embedded` fallback for `android_vr` client](https://github.com/yt-dlp/yt-dlp/commit/bb1c05752c288a81e0e281f1caf5395411936376) ([#15785](https://github.com/yt-dlp/yt-dlp/issues/15785)) by [bashonly](https://github.com/bashonly) + - [Remove broken `ios_downgraded` player client](https://github.com/yt-dlp/yt-dlp/commit/c3674575faa23b20e97be8b73f68b9f7b4cea9ab) ([#15786](https://github.com/yt-dlp/yt-dlp/issues/15786)) by [bashonly](https://github.com/bashonly) + - [Remove broken `tv_embedded` player client](https://github.com/yt-dlp/yt-dlp/commit/8eb794366eb69e7377ff88eed7929c00195c8d74) ([#15787](https://github.com/yt-dlp/yt-dlp/issues/15787)) by [bashonly](https://github.com/bashonly) + +#### Misc. changes +- **cleanup**: Miscellaneous: [9a9a6b6](https://github.com/yt-dlp/yt-dlp/commit/9a9a6b6fe44a30458c1754ef064f354f04a84004) by [bashonly](https://github.com/bashonly) + ### 2026.01.29 #### Core changes 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-01-29 18:00:50.000000000 +0100 +++ new/yt-dlp/README.md 2026-02-01 00:55:30.000000000 +0100 @@ -1860,7 +1860,7 @@ #### 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`, `ios_downgraded`, `android`, `android_vr`, `tv`, `tv_simply`, `tv_downgraded`, and `tv_embedded`. By default, `android_vr,ios_downgraded,web,web_safari` is used. If no JavaScript runtime/engine is available, then `android_vr,ios_downgraded` 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 works if the video is embeddable. The `tv_embedded` and `web_creator` clients are added for age-restricted videos if account age-verification is required. Some clients, such as `web` 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,-ios` +* `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_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. 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-01-29 18:00:51.000000000 +0100 +++ new/yt-dlp/README.txt 2026-02-01 00:55:32.000000000 +0100 @@ -2312,24 +2312,25 @@ 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, ios_downgraded, android, android_vr, tv, - tv_simply, tv_downgraded, and tv_embedded. By default, - android_vr,ios_downgraded,web,web_safari is used. If no JavaScript - runtime/engine is available, then android_vr,ios_downgraded is used. - If logged-in cookies are passed to yt-dlp, then + 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 works if the video is embeddable. The - tv_embedded and web_creator clients are added for age-restricted - videos if account age-verification is required. Some clients, such - as web and web_music, require a po_token for their formats to be - downloadable. Some clients, such as web_creator, will only work with + 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,-ios + 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 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-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/test/test_jsc/test_ejs_integration.py 2026-02-01 00:55:18.000000000 +0100 @@ -36,7 +36,6 @@ 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' @dataclasses.dataclass 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-01-29 18:00:52.000000000 +0100 +++ new/yt-dlp/yt-dlp.1 2026-02-01 00:55:32.000000000 +0100 @@ -2713,30 +2713,31 @@ \f[V]player_client\f[R]: Clients to extract video data from. The currently available clients are \f[V]web\f[R], \f[V]web_safari\f[R], \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]ios_downgraded\f[R], -\f[V]android\f[R], \f[V]android_vr\f[R], \f[V]tv\f[R], -\f[V]tv_simply\f[R], \f[V]tv_downgraded\f[R], and \f[V]tv_embedded\f[R]. -By default, \f[V]android_vr,ios_downgraded,web,web_safari\f[R] is used. -If no JavaScript runtime/engine is available, then -\f[V]android_vr,ios_downgraded\f[R] is used. +\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. +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. 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 -only works if the video is embeddable. -The \f[V]tv_embedded\f[R] and \f[V]web_creator\f[R] clients are added -for age-restricted videos if account age-verification is required. -Some clients, such as \f[V]web\f[R] and \f[V]web_music\f[R], require a -\f[V]po_token\f[R] for their formats to be downloadable. +only successfully works around the age-restriction sometimes (e.g. +if the video is embeddable), and may be added as a fallback if +\f[V]android_vr\f[R] is unable to access a video. +The \f[V]web_creator\f[R] client is added for age-restricted videos if +account age-verification is required. +Some clients, such as \f[V]web_creator\f[R] and \f[V]web_music\f[R], +require a \f[V]po_token\f[R] for their formats to be downloadable. Some clients, such as \f[V]web_creator\f[R], will only work with authentication. Not all clients support authentication via cookies. 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,-ios\f[R] +\f[V]youtube:player_client=default,-web\f[R] .IP \[bu] 2 \f[V]player_skip\f[R]: Skip some network requests that are generally needed for robust extraction. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/downloader/__init__.py new/yt-dlp/yt_dlp/downloader/__init__.py --- old/yt-dlp/yt_dlp/downloader/__init__.py 2026-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/downloader/__init__.py 2026-02-01 00:55:18.000000000 +0100 @@ -36,6 +36,7 @@ from .websocket import WebSocketFragmentFD from .youtube_live_chat import YoutubeLiveChatFD from .bunnycdn import BunnyCdnFD +from .soop import SoopVodFD PROTOCOL_MAP = { 'rtmp': RtmpFD, @@ -56,6 +57,7 @@ 'youtube_live_chat': YoutubeLiveChatFD, 'youtube_live_chat_replay': YoutubeLiveChatFD, 'bunnycdn': BunnyCdnFD, + 'soopvod': SoopVodFD, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/downloader/soop.py new/yt-dlp/yt_dlp/downloader/soop.py --- old/yt-dlp/yt_dlp/downloader/soop.py 1970-01-01 01:00:00.000000000 +0100 +++ new/yt-dlp/yt_dlp/downloader/soop.py 2026-02-01 00:55:18.000000000 +0100 @@ -0,0 +1,61 @@ +import threading +import time + +from .common import FileDownloader +from . import HlsFD +from ..extractor.afreecatv import _cloudfront_auth_request +from ..networking.exceptions import network_exceptions + + +class SoopVodFD(FileDownloader): + """ + Downloads Soop subscription VODs with required cookie refresh requests + Note, this is not a part of public API, and will be removed without notice. + DO NOT USE + """ + + def real_download(self, filename, info_dict): + self.to_screen(f'[{self.FD_NAME}] Downloading Soop subscription VOD HLS') + fd = HlsFD(self.ydl, self.params) + refresh_params = info_dict['_cookie_refresh_params'] + referer_url = info_dict['webpage_url'] + + stop_event = threading.Event() + refresh_thread = threading.Thread( + target=self._cookie_refresh_thread, + args=(stop_event, refresh_params, referer_url), + ) + refresh_thread.start() + + try: + return fd.real_download(filename, info_dict) + finally: + stop_event.set() + + def _cookie_refresh_thread(self, stop_event, refresh_params, referer_url): + m3u8_url = refresh_params['m3u8_url'] + strm_id = refresh_params['strm_id'] + video_id = refresh_params['video_id'] + + def _get_cloudfront_cookie_expiration(m3u8_url): + cookies = self.ydl.cookiejar.get_cookies_for_url(m3u8_url) + return min((cookie.expires for cookie in cookies if 'CloudFront' in cookie.name and cookie.expires), default=0) + + while not stop_event.wait(5): + current_time = time.time() + expiration_time = _get_cloudfront_cookie_expiration(m3u8_url) + last_refresh_check = refresh_params.get('_last_refresh', 0) + + # Cookie TTL is 90 seconds, but let's give ourselves a 15-second cushion + should_refresh = ( + (expiration_time and current_time >= expiration_time - 15) + or (not expiration_time and current_time - last_refresh_check >= 75) + ) + + if should_refresh: + try: + self.ydl.urlopen(_cloudfront_auth_request( + m3u8_url, strm_id, video_id, referer_url)).read() + refresh_params['_last_refresh'] = current_time + except network_exceptions as e: + self.to_screen(f'[{self.FD_NAME}] Cookie refresh attempt failed: {e}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/afreecatv.py new/yt-dlp/yt_dlp/extractor/afreecatv.py --- old/yt-dlp/yt_dlp/extractor/afreecatv.py 2026-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/afreecatv.py 2026-02-01 00:55:18.000000000 +0100 @@ -1,5 +1,6 @@ import datetime as dt import functools +import time from .common import InfoExtractor from ..networking import Request @@ -16,7 +17,23 @@ urlencode_postdata, urljoin, ) -from ..utils.traversal import traverse_obj +from ..utils.traversal import require, traverse_obj + + +def _cloudfront_auth_request(m3u8_url, strm_id, video_id, referer_url): + return Request( + 'https://live.sooplive.co.kr/api/private_auth.php', + method='POST', + headers={ + 'Referer': referer_url, + 'Origin': 'https://vod.sooplive.co.kr', + }, + data=urlencode_postdata({ + 'type': 'vod', + 'strm_id': strm_id, + 'title_no': video_id, + 'url': m3u8_url, + })) class AfreecaTVBaseIE(InfoExtractor): @@ -153,6 +170,13 @@ 'nApiLevel': 10, }))['data'] + initial_refresh_time = 0 + strm_id = None + # For subscriber-only VODs, we need to call private_auth.php to get CloudFront cookies + needs_private_auth = traverse_obj(data, ('sub_upload_type', {str})) + if needs_private_auth: + strm_id = traverse_obj(data, ('bj_id', {str}, {require('stream ID')})) + error_code = traverse_obj(data, ('code', {int})) if error_code == -6221: raise ExtractorError('The VOD does not exist', expected=True) @@ -172,9 +196,23 @@ traverse_obj(data, ('files', lambda _, v: url_or_none(v['file']))), start=1): file_url = file_element['file'] if determine_ext(file_url) == 'm3u8': + if needs_private_auth: + self._request_webpage( + _cloudfront_auth_request(file_url, strm_id, video_id, url), + video_id, 'Requesting CloudFront cookies', 'Failed to get CloudFront cookies') + initial_refresh_time = time.time() formats = self._extract_m3u8_formats( file_url, video_id, 'mp4', m3u8_id='hls', note=f'Downloading part {file_num} m3u8 information') + if needs_private_auth: + for fmt in formats: + fmt['protocol'] = 'soopvod' + fmt['_cookie_refresh_params'] = { + 'm3u8_url': file_url, + 'strm_id': strm_id, + 'video_id': video_id, + '_last_refresh': initial_refresh_time, + } else: formats = [{ 'url': file_url, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/lazy_extractors.py new/yt-dlp/yt_dlp/extractor/lazy_extractors.py --- old/yt-dlp/yt_dlp/extractor/lazy_extractors.py 2026-01-29 18:00:48.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/lazy_extractors.py 2026-02-01 00:55:28.000000000 +0100 @@ -5862,7 +5862,7 @@ class KnownDRMIE(UnsupportedInfoExtractor): _module = 'yt_dlp.extractor.unsupported' IE_NAME = 'DRM' - _VALID_URL = 'https?://(?:www\\.)?(?:play\\.hbomax\\.com|channel(?:4|5)\\.com|peacocktv\\.com|(?:[\\w.]+\\.)?disneyplus\\.com|open\\.spotify\\.com|tvnz\\.co\\.nz|oneplus\\.ch|artstation\\.com/learning/courses|philo\\.com|(?:[\\w.]+\\.)?mech-plus\\.com|aha\\.video|mubi\\.com|vootkids\\.com|nowtv\\.it/watch|tv\\.apple\\.com|primevideo\\.com|hulu\\.com|resource\\.inkryptvideos\\.com|joyn\\.de|amazon\\.(?:\\w{2}\\.)?\\w+/gp/video|music\\.amazon\\.(?:\\w{2}\\.)?\\w+|(?:watch|front)\\.njpwworld\\.com|qub\\.ca/vrai|(?:beta\\.)?crunchyroll\\.com|viki\\.com|deezer\\.com|b-ch\\.com|ctv\\.ca|noovo\\.ca|tsn\\.ca|paramountplus\\.com|(?:m\\.)?(?:sony)?crackle\\.com|cw(?:tv(?:pr)?|seed)\\.com|6play\\.fr|rtlplay\\.be|play\\.rtl\\.hr|rtlmost\\.hu|plus\\.rtl\\.de(?!/podcast/)|mediasetinfinity\\.es)' + _VALID_URL = 'https?://(?:www\\.)?(?:play\\.hbomax\\.com|channel(?:4|5)\\.com|peacocktv\\.com|(?:[\\w.]+\\.)?disneyplus\\.com|open\\.spotify\\.com|tvnz\\.co\\.nz|oneplus\\.ch|artstation\\.com/learning/courses|philo\\.com|(?:[\\w.]+\\.)?mech-plus\\.com|aha\\.video|mubi\\.com|vootkids\\.com|nowtv\\.it/watch|tv\\.apple\\.com|primevideo\\.com|hulu\\.com|resource\\.inkryptvideos\\.com|joyn\\.de|amazon\\.(?:\\w{2}\\.)?\\w+/gp/video|music\\.amazon\\.(?:\\w{2}\\.)?\\w+|(?:watch|front)\\.njpwworld\\.com|qub\\.ca/vrai|(?:beta\\.)?crunchyroll\\.com|viki\\.com|deezer\\.com|b-ch\\.com|ctv\\.ca|noovo\\.ca|tsn\\.ca|paramountplus\\.com|(?:m\\.)?(?:sony)?crackle\\.com|cw(?:tv(?:pr)?|seed)\\.com|6play\\.fr|rtlplay\\.be|play\\.rtl\\.hr|rtlmost\\.hu|plus\\.rtl\\.de(?!/podcast/)|mediasetinfinity\\.es|tv5mondeplus\\.com)' IE_DESC = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/unsupported.py new/yt-dlp/yt_dlp/extractor/unsupported.py --- old/yt-dlp/yt_dlp/extractor/unsupported.py 2026-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/unsupported.py 2026-02-01 00:55:18.000000000 +0100 @@ -66,6 +66,7 @@ r'rtlmost\.hu', r'plus\.rtl\.de(?!/podcast/)', r'mediasetinfinity\.es', + r'tv5mondeplus\.com', ) _TESTS = [{ @@ -226,6 +227,10 @@ }, { 'url': 'https://www.mediasetinfinity.es/', 'only_matching': True, + }, { + # https://github.com/yt-dlp/yt-dlp/issues/14743 + 'url': 'https://www.tv5mondeplus.com/', + 'only_matching': True, }] def _real_extract(self, url): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/whyp.py new/yt-dlp/yt_dlp/extractor/whyp.py --- old/yt-dlp/yt_dlp/extractor/whyp.py 2026-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/whyp.py 2026-02-01 00:55:18.000000000 +0100 @@ -2,6 +2,7 @@ from ..utils import ( float_or_none, int_or_none, + parse_iso8601, str_or_none, traverse_obj, url_or_none, @@ -16,9 +17,12 @@ 'info_dict': { 'id': '18337', 'title': 'Example Track', + 'display_id': 'example-track', 'description': 'md5:e0b1bcf1d267dc1a0f15efff09c8f297', 'ext': 'flac', 'duration': 135.63, + 'timestamp': 1643216583, + 'upload_date': '20220126', 'uploader': 'Brad', 'uploader_id': '1', 'thumbnail': 'https://cdn.whyp.it/6ad0bbd9-577d-42bb-9b61-2a4f57f647eb.jpg', @@ -44,10 +48,12 @@ 'http_headers': {'Referer': 'https://whyp.it/'}, } for prefix in ('audio', 'lossy', 'lossless') if url_or_none(data.get(f'{prefix}_url'))], **traverse_obj(data, { - 'title': 'title', + 'title': ('title', {str}), + 'display_id': ('slug', {str}), 'description': 'description', 'duration': ('duration', {float_or_none}), - 'uploader': ('user', 'username'), + 'timestamp': ('created_at', {parse_iso8601}), + 'uploader': ('user', 'username', {str}), 'uploader_id': ('user', 'id', {str_or_none}), 'thumbnail': ('artwork_url', {url_or_none}), }), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yt-dlp/yt_dlp/extractor/youtube/_base.py new/yt-dlp/yt_dlp/extractor/youtube/_base.py --- old/yt-dlp/yt_dlp/extractor/youtube/_base.py 2026-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/youtube/_base.py 2026-02-01 00:55:18.000000000 +0100 @@ -271,34 +271,6 @@ 'PLAYER_PO_TOKEN_POLICY': PlayerPoTokenPolicy(required=False, recommended=True), 'REQUIRE_JS_PLAYER': False, }, - 'ios_downgraded': { - 'INNERTUBE_CONTEXT': { - 'client': { - 'clientName': 'IOS', - 'clientVersion': '19.49.7', - 'deviceMake': 'Apple', - 'deviceModel': 'iPhone16,2', - 'userAgent': 'com.google.ios.youtube/19.49.7 (iPhone16,2; U; CPU iOS 17_5_1 like Mac OS X;)', - 'osName': 'iPhone', - 'osVersion': '17.5.1.21F90', - }, - }, - 'INNERTUBE_CONTEXT_CLIENT_NAME': 5, - 'GVS_PO_TOKEN_POLICY': { - StreamingProtocol.HTTPS: GvsPoTokenPolicy( - required=True, - recommended=True, - not_required_with_player_token=True, - ), - StreamingProtocol.HLS: GvsPoTokenPolicy( - required=False, - recommended=True, - not_required_with_player_token=True, - ), - }, - 'PLAYER_PO_TOKEN_POLICY': PlayerPoTokenPolicy(required=False, recommended=True), - 'REQUIRE_JS_PLAYER': False, - }, # mweb has 'ultralow' formats # See: https://github.com/yt-dlp/yt-dlp/pull/557 'mweb': { @@ -379,20 +351,6 @@ }, 'INNERTUBE_CONTEXT_CLIENT_NAME': 75, }, - # This client now requires sign-in for every video - # It was previously an age-gate workaround for videos that were `playable_in_embed` - # It may still be useful if signed into an EU account that is not age-verified - 'tv_embedded': { - 'INNERTUBE_CONTEXT': { - 'client': { - 'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER', - 'clientVersion': '2.0', - }, - }, - 'INNERTUBE_CONTEXT_CLIENT_NAME': 85, - 'REQUIRE_AUTH': True, - 'SUPPORTS_COOKIES': True, - }, } 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-01-29 18:00:40.000000000 +0100 +++ new/yt-dlp/yt_dlp/extractor/youtube/_video.py 2026-02-01 00:55:18.000000000 +0100 @@ -145,8 +145,8 @@ r'\b(?P<id>vfl[a-zA-Z0-9_-]+)\b.*?\.js$', ) _SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'srt', 'vtt') - _DEFAULT_CLIENTS = ('android_vr', 'ios_downgraded', 'web', 'web_safari') - _DEFAULT_JSLESS_CLIENTS = ('android_vr', 'ios_downgraded') + _DEFAULT_CLIENTS = ('android_vr', 'web', 'web_safari') + _DEFAULT_JSLESS_CLIENTS = ('android_vr',) _DEFAULT_AUTHED_CLIENTS = ('tv_downgraded', 'web', 'web_safari') # Premium does not require POT (except for subtitles) _DEFAULT_PREMIUM_CLIENTS = ('tv_downgraded', 'web_creator', 'web') @@ -1443,7 +1443,6 @@ 'view_count': int, }, 'params': { - 'extractor_args': {'youtube': {'player_client': ['tv_embedded']}}, 'format': '251-drc', 'skip_download': True, }, @@ -3118,6 +3117,15 @@ else: prs.append(pr) + if ( + # Is this a "made for kids" video that can't be downloaded with android_vr? + client == 'android_vr' and self._is_unplayable(pr) + and webpage and 'made for kids' in webpage + # ...and is a JS runtime is available? + and any(p.is_available() for p in self._jsc_director.providers.values()) + ): + append_client('web_embedded') + # web_embedded can work around age-gate and age-verification for some embeddable videos if self._is_agegated(pr) and variant != 'web_embedded': append_client(f'web_embedded.{base_client}') @@ -3134,9 +3142,8 @@ self.to_screen( f'{video_id}: This video is age-restricted and YouTube is requiring ' 'account age-verification; some formats may be missing', only_once=True) - # tv_embedded can work around the age-verification requirement for embeddable videos # web_creator may work around age-verification for all videos but requires PO token - append_client('tv_embedded', 'web_creator') + append_client('web_creator') status = traverse_obj(pr, ('playabilityStatus', 'status', {str})) if status not in ('OK', 'LIVE_STREAM_OFFLINE', 'AGE_CHECK_REQUIRED', 'AGE_VERIFICATION_REQUIRED'): @@ -3595,10 +3602,6 @@ if client_name == 'web_safari' and proto == 'hls' and live_status != 'is_live': f['source_preference'] -= 1 - # Safeguard against inevitable ios_downgraded client breakage - if client_name == 'ios_downgraded' and proto == 'hls' and live_status != 'is_live': - f['__needs_testing'] = True - if missing_pot: f['format_note'] = join_nonempty(f.get('format_note'), 'MISSING POT', delim=' ') f['source_preference'] -= 20 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-01-29 18:00:45.000000000 +0100 +++ new/yt-dlp/yt_dlp/version.py 2026-02-01 00:55:24.000000000 +0100 @@ -1,8 +1,8 @@ # Autogenerated by devscripts/update-version.py -__version__ = '2026.01.29' +__version__ = '2026.01.31' -RELEASE_GIT_HEAD = '8b275536d945c4b3d07b6c520677922c67a7c10f' +RELEASE_GIT_HEAD = '9a9a6b6fe44a30458c1754ef064f354f04a84004' VARIANT = None @@ -12,4 +12,4 @@ ORIGIN = 'yt-dlp/yt-dlp' -_pkg_version = '2026.01.29' +_pkg_version = '2026.01.31'
