diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdec4d1c9d..29c18723c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -536,7 +536,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: artifact pattern: build-bin-* diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 7dfda9f842..f44da792f8 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -38,7 +38,7 @@ jobs: id-token: write # mandatory for trusted publishing steps: - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: dist name: build-pypi diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index fd7cf4cbae..26be60fe61 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -53,7 +53,7 @@ jobs: id-token: write # mandatory for trusted publishing steps: - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: dist name: build-pypi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee93d499ac..b60a0650a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -236,7 +236,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: path: artifact pattern: build-* diff --git a/Changelog.md b/Changelog.md index 72b46732b5..8737441e86 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,30 @@ # To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master --> +### 2025.10.22 + +#### Important changes +- **A stopgap release with a *TEMPORARY partial* fix for YouTube support** +Some formats may still be unavailable, especially if cookies are passed to yt-dlp. The ***NEXT*** release, expected very soon, **will require an external JS runtime (e.g. Deno)** in order for YouTube downloads to work properly. [Read more](https://github.com/yt-dlp/yt-dlp/issues/14404) +- **The minimum *required* Python version has been raised to 3.10** +Python 3.9 has reached its end-of-life as of October 2025, and yt-dlp has now removed support for it. [Read more](https://github.com/yt-dlp/yt-dlp/issues/13858) + +#### Core changes +- [Remove Python 3.9 support](https://github.com/yt-dlp/yt-dlp/commit/4e6a693057cfaf1ce1f07b019ed3bfce2bf936f6) ([#13861](https://github.com/yt-dlp/yt-dlp/issues/13861)) by [bashonly](https://github.com/bashonly) + +#### Extractor changes +- **appleconnect**: [Rework extractor](https://github.com/yt-dlp/yt-dlp/commit/78748b506f0dca8236ac0045ed7f72f7cf334b62) ([#13229](https://github.com/yt-dlp/yt-dlp/issues/13229)) by [doe1080](https://github.com/doe1080) +- **idagio**: [Support URLs with country codes](https://github.com/yt-dlp/yt-dlp/commit/c9356f308dd3c5f9f494cb40ed14c5df017b4fe0) ([#14655](https://github.com/yt-dlp/yt-dlp/issues/14655)) by [robin-mu](https://github.com/robin-mu) +- **tvnoe**: [Rework Extractor](https://github.com/yt-dlp/yt-dlp/commit/fe5ae54a7b08ebe679f03afdeafbe1cee5784d5b) ([#13369](https://github.com/yt-dlp/yt-dlp/issues/13369)) by [doe1080](https://github.com/doe1080) +- **youtube**: [Use temporary player client workaround](https://github.com/yt-dlp/yt-dlp/commit/2c9091e355a7ba5d1edb69796ecdca48199b77fb) ([#14693](https://github.com/yt-dlp/yt-dlp/issues/14693)) by [gamer191](https://github.com/gamer191) + +#### Misc. changes +- **cleanup** + - Miscellaneous + - [c7bda21](https://github.com/yt-dlp/yt-dlp/commit/c7bda2192aa24afce40fdbbbe056d269aa3b2872) by [bashonly](https://github.com/bashonly), [seproDev](https://github.com/seproDev) + - [de7b3c0](https://github.com/yt-dlp/yt-dlp/commit/de7b3c0705022cb777c5b4b7f0c69c59ad6ff538) by [bashonly](https://github.com/bashonly) +- **docs**: [Update list of maintainers](https://github.com/yt-dlp/yt-dlp/commit/dfc0a84c192a7357dd1768cc345d590253a14fe5) ([#14148](https://github.com/yt-dlp/yt-dlp/issues/14148)) by [bashonly](https://github.com/bashonly), [coletdjnz](https://github.com/coletdjnz), [seproDev](https://github.com/seproDev) + ### 2025.10.14 #### Core changes diff --git a/README.md b/README.md index 214e801463..7b3cd0970d 100644 --- a/README.md +++ b/README.md @@ -1814,12 +1814,12 @@ The following extractors use this feature: #### 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_simply` and `tv_embedded`. By default, `tv,web_safari,web` is used, and `tv,web_creator,web` is used with 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_sdkless`, `android_vr`, `tv`, `tv_simply` and `tv_embedded`. By default, `android_sdkless,tv,web_safari,web` is used. `android_sdkless` is omitted if cookies are passed. If premium cookies are passed, `tv,web_creator,web_safari,web` is used instead. 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_skip`: Skip some network requests that are generally needed for robust extraction. One or more of `configs` (skip client configs), `webpage` (skip initial webpage), `js` (skip js player), `initial_data` (skip initial data/next ep request). While these options can help reduce the number of requests needed or avoid some rate-limiting, they could cause issues such as missing formats or metadata. See [#860](https://github.com/yt-dlp/yt-dlp/pull/860) and [#12826](https://github.com/yt-dlp/yt-dlp/issues/12826) for more details * `webpage_skip`: Skip extraction of embedded webpage data. One or both of `player_response`, `initial_data`. These options are for testing purposes and don't skip any network requests * `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp. * `player_js_variant`: The player javascript variant to use for n/sig deciphering. The known variants are: `main`, `tcc`, `tce`, `es5`, `es6`, `tv`, `tv_es6`, `phone`, `tablet`. 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`. Currently, the default is to force `20348@0004de42`. You can use `actual` to go with what is prescribed by the site +* `player_js_version`: The player javascript version to use for n/sig deciphering, in the format of `signature_timestamp@hash` (e.g. `20348@0004de42`). The default is to use what is prescribed by the site, and can be selected with `actual` * `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side) * `max_comments`: Limit the amount of comments to gather. Comma-separated list of integers representing `max-comments,max-parents,max-replies,max-replies-per-thread`. Default is `all,all,all,all` * E.g. `all,all,1000,10` will get a maximum of 1000 replies total, with up to 10 replies per thread. `1000,all,100` will get a maximum of 1000 comments, with a maximum of 100 replies total diff --git a/devscripts/changelog_override.json b/devscripts/changelog_override.json index bd78058e1e..e906838175 100644 --- a/devscripts/changelog_override.json +++ b/devscripts/changelog_override.json @@ -303,5 +303,10 @@ "action": "add", "when": "4e6a693057cfaf1ce1f07b019ed3bfce2bf936f6", "short": "[priority] **The minimum *required* Python version has been raised to 3.10**\nPython 3.9 has reached its end-of-life as of October 2025, and yt-dlp has now removed support for it. [Read more](https://github.com/yt-dlp/yt-dlp/issues/13858)" + }, + { + "action": "add", + "when": "2c9091e355a7ba5d1edb69796ecdca48199b77fb", + "short": "[priority] **A stopgap release with a *TEMPORARY partial* fix for YouTube support**\nSome formats may still be unavailable, especially if cookies are passed to yt-dlp. The ***NEXT*** release, expected very soon, **will require an external JS runtime (e.g. Deno)** in order for YouTube downloads to work properly. [Read more](https://github.com/yt-dlp/yt-dlp/issues/14404)" } ] diff --git a/supportedsites.md b/supportedsites.md index dad166b635..a546819286 100644 --- a/supportedsites.md +++ b/supportedsites.md @@ -85,7 +85,7 @@ The only reliable way to check if a site is supported is to try it. - **aol.com**: Yahoo screen and movies (**Currently broken**) - **APA** - **Aparat** - - **AppleConnect** + - **apple:​music:connect**: Apple Music Connect - **AppleDaily**: 臺灣蘋果日報 - **ApplePodcasts** - **appletrailers** @@ -1541,7 +1541,7 @@ The only reliable way to check if a site is supported is to try it. - **tvigle**: Интернет-телевидение Tvigle.ru - **TVIPlayer** - **TVN24**: (**Currently broken**) - - **TVNoe**: (**Currently broken**) + - **tvnoe**: Televize Noe - **tvopengr:embed**: tvopen.gr embedded videos - **tvopengr:watch**: tvopen.gr (and ethnos.gr) videos - **tvp**: Telewizja Polska diff --git a/yt_dlp/extractor/idagio.py b/yt_dlp/extractor/idagio.py index 5790606bea..a99c559065 100644 --- a/yt_dlp/extractor/idagio.py +++ b/yt_dlp/extractor/idagio.py @@ -4,7 +4,7 @@ from ..utils.traversal import traverse_obj class IdagioTrackIE(InfoExtractor): - _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/recordings/\d+\?(?:[^#]+&)?trackId=(?P\d+)' + _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com(?:/[a-z]{2})?/recordings/\d+\?(?:[^#]+&)?trackId=(?P\d+)' _TESTS = [{ 'url': 'https://app.idagio.com/recordings/30576934?trackId=30576943', 'md5': '15148bd71804b2450a2508931a116b56', @@ -29,12 +29,14 @@ class IdagioTrackIE(InfoExtractor): 'title': 'I. Adagio sostenuto', 'duration': 316, 'composers': ['Ludwig van Beethoven'], - 'artists': [], 'genres': ['Keyboard', 'Sonata (Keyboard)'], 'track': 'I. Adagio sostenuto', 'timestamp': 1518076337, 'upload_date': '20180208', }, + }, { + 'url': 'https://app.idagio.com/de/recordings/20514467?trackId=20514478&utm_source=pcl', + 'only_matching': True, }] def _real_extract(self, url): @@ -97,7 +99,7 @@ class IdagioPlaylistBaseIE(InfoExtractor): class IdagioRecordingIE(IdagioPlaylistBaseIE): - _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/recordings/(?P\d+)(?![^#]*[&?]trackId=\d+)' + _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com(?:/[a-z]{2})?/recordings/(?P\d+)(?![^#]*[&?]trackId=\d+)' _TESTS = [{ 'url': 'https://app.idagio.com/recordings/30576934', 'info_dict': { @@ -112,6 +114,19 @@ class IdagioRecordingIE(IdagioPlaylistBaseIE): 'upload_date': '20190405', }, 'playlist_count': 15, + }, { + 'url': 'https://app.idagio.com/de/recordings/20514467', + 'info_dict': { + 'id': '20514467', + 'title': 'Sonata for Piano No. 14 in C sharp minor op. 27/2', + 'composers': ['Ludwig van Beethoven'], + 'genres': ['Keyboard', 'Sonata (Keyboard)'], + 'timestamp': 1518076337, + 'upload_date': '20180208', + 'modified_timestamp': 1518076337, + 'modified_date': '20180208', + }, + 'playlist_count': 3, }] _API_URL_TMPL = 'https://api.idagio.com/v2.0/metadata/recordings/{}' @@ -129,7 +144,7 @@ class IdagioRecordingIE(IdagioPlaylistBaseIE): class IdagioAlbumIE(IdagioPlaylistBaseIE): - _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/albums/(?P[\w-]+)' + _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com(?:/[a-z]{2})?/albums/(?P[\w-]+)' _TESTS = [{ 'url': 'https://app.idagio.com/albums/elgar-enigma-variations-in-the-south-serenade-for-strings', 'info_dict': { @@ -137,7 +152,7 @@ class IdagioAlbumIE(IdagioPlaylistBaseIE): 'display_id': 'elgar-enigma-variations-in-the-south-serenade-for-strings', 'title': 'Elgar: Enigma Variations, In the South, Serenade for Strings', 'description': '', - 'thumbnail': 'https://idagio-images.global.ssl.fastly.net/albums/880040420521/main.jpg', + 'thumbnail': r're:https://.+/albums/880040420521/main\.jpg', 'artists': ['Vasily Petrenko', 'Royal Liverpool Philharmonic Orchestra', 'Edward Elgar'], 'timestamp': 1553817600, 'upload_date': '20190329', @@ -146,19 +161,19 @@ class IdagioAlbumIE(IdagioPlaylistBaseIE): }, 'playlist_count': 19, }, { - 'url': 'https://app.idagio.com/albums/brahms-ein-deutsches-requiem-3B403DF6-62D7-4A42-807B-47173F3E0192', + 'url': 'https://app.idagio.com/de/albums/brahms-ein-deutsches-requiem-3B403DF6-62D7-4A42-807B-47173F3E0192', 'info_dict': { 'id': '2862ad4e-4a61-45ad-9ce4-7fcf0c2626fe', 'display_id': 'brahms-ein-deutsches-requiem-3B403DF6-62D7-4A42-807B-47173F3E0192', 'title': 'Brahms: Ein deutsches Requiem', - 'description': '', - 'thumbnail': 'https://idagio-images.global.ssl.fastly.net/albums/3149020954522/main.jpg', - 'tags': ['recent-release'], + 'description': 'GRAMOPHONE CLASSICAL MUSIC AWARDS 2025 Recording of the Year & Choral', + 'thumbnail': r're:https://.+/albums/3149020954522/main\.jpg', 'artists': ['Sabine Devieilhe', 'Stéphane Degout', 'Raphaël Pichon', 'Pygmalion', 'Johannes Brahms'], 'timestamp': 1760054400, 'upload_date': '20251010', - 'modified_timestamp': 1760101611, - 'modified_date': '20251010', + 'modified_timestamp': 1760624868, + 'modified_date': '20251016', + 'tags': ['recommended', 'recent-release'], }, 'playlist_count': 7, }] @@ -179,7 +194,7 @@ class IdagioAlbumIE(IdagioPlaylistBaseIE): class IdagioPlaylistIE(IdagioPlaylistBaseIE): - _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/playlists/(?!personal/)(?P[\w-]+)' + _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com(?:/[a-z]{2})?/playlists/(?!personal/)(?P[\w-]+)' _TESTS = [{ 'url': 'https://app.idagio.com/playlists/beethoven-the-most-beautiful-piano-music', 'info_dict': { @@ -191,6 +206,17 @@ class IdagioPlaylistIE(IdagioPlaylistBaseIE): 'creators': ['IDAGIO'], }, 'playlist_mincount': 16, # one entry is geo-restricted + }, { + 'url': 'https://app.idagio.com/de/playlists/piano-music-for-an-autumn-day', + 'info_dict': { + 'id': 'd70e9c7f-7080-4308-ae0f-f890dddeda82', + 'display_id': 'piano-music-for-an-autumn-day', + 'title': 'Piano Music for an Autumn Day', + 'description': 'Get ready to snuggle up and enjoy all the musical colours of this cosy, autumnal playlist.', + 'thumbnail': r're:https://.+/playlists/d70e9c7f-7080-4308-ae0f-f890dddeda82/main\.jpg', + 'creators': ['IDAGIO'], + }, + 'playlist_count': 35, }] _API_URL_TMPL = 'https://api.idagio.com/v2.0/playlists/{}' _PLAYLIST_ID_KEY = 'display_id' @@ -206,7 +232,7 @@ class IdagioPlaylistIE(IdagioPlaylistBaseIE): class IdagioPersonalPlaylistIE(IdagioPlaylistBaseIE): - _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com/playlists/personal/(?P[\da-f-]+)' + _VALID_URL = r'https?://(?:www\.)?app\.idagio\.com(?:/[a-z]{2})?/playlists/personal/(?P[\da-f-]+)' _TESTS = [{ 'url': 'https://app.idagio.com/playlists/personal/99dad72e-7b3a-45a4-b216-867c08046ed8', 'info_dict': { @@ -220,6 +246,9 @@ class IdagioPersonalPlaylistIE(IdagioPlaylistBaseIE): 'modified_date': '20250819', }, 'playlist_count': 100, + }, { + 'url': 'https://app.idagio.com/de/playlists/personal/99dad72e-7b3a-45a4-b216-867c08046ed8', + 'only_matching': True, }] _API_URL_TMPL = 'https://api.idagio.com/v1.0/personal-playlists/{}' diff --git a/yt_dlp/extractor/youtube/_base.py b/yt_dlp/extractor/youtube/_base.py index ded43b8aea..17e942465d 100644 --- a/yt_dlp/extractor/youtube/_base.py +++ b/yt_dlp/extractor/youtube/_base.py @@ -220,6 +220,20 @@ INNERTUBE_CLIENTS = { }, 'PLAYER_PO_TOKEN_POLICY': PlayerPoTokenPolicy(required=False, recommended=True), }, + # Doesn't require a PoToken for some reason + 'android_sdkless': { + 'INNERTUBE_CONTEXT': { + 'client': { + 'clientName': 'ANDROID', + 'clientVersion': '20.10.38', + 'userAgent': 'com.google.android.youtube/20.10.38 (Linux; U; Android 11) gzip', + 'osName': 'Android', + 'osVersion': '11', + }, + }, + 'INNERTUBE_CONTEXT_CLIENT_NAME': 3, + 'REQUIRE_JS_PLAYER': False, + }, # YouTube Kids videos aren't returned on this client for some reason 'android_vr': { 'INNERTUBE_CONTEXT': { diff --git a/yt_dlp/extractor/youtube/_video.py b/yt_dlp/extractor/youtube/_video.py index 864121e0a3..1fc45dac6f 100644 --- a/yt_dlp/extractor/youtube/_video.py +++ b/yt_dlp/extractor/youtube/_video.py @@ -257,10 +257,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '401': {'ext': 'mp4', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'av01.0.12M.08'}, } _SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'srt', 'vtt') - _DEFAULT_CLIENTS = ('tv', 'web_safari', 'web') + _DEFAULT_CLIENTS = ('android_sdkless', 'tv', 'web_safari', 'web') _DEFAULT_AUTHED_CLIENTS = ('tv', 'web_safari', 'web') # Premium does not require POT (except for subtitles) - _DEFAULT_PREMIUM_CLIENTS = ('tv', 'web_creator', 'web') + _DEFAULT_PREMIUM_CLIENTS = ('tv', 'web_creator', 'web_safari', 'web') _GEO_BYPASS = False @@ -1815,7 +1815,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'params': {'skip_download': True}, }] - _DEFAULT_PLAYER_JS_VERSION = '20348@0004de42' # TODO: revert to 'actual' when n/sig is fixed + _DEFAULT_PLAYER_JS_VERSION = 'actual' _DEFAULT_PLAYER_JS_VARIANT = 'main' _PLAYER_JS_VARIANT_MAP = { 'main': 'player_ias.vflset/en_US/base.js', diff --git a/yt_dlp/version.py b/yt_dlp/version.py index cf553256a8..b068f0f2fa 100644 --- a/yt_dlp/version.py +++ b/yt_dlp/version.py @@ -1,8 +1,8 @@ # Autogenerated by devscripts/update-version.py -__version__ = '2025.10.14' +__version__ = '2025.10.22' -RELEASE_GIT_HEAD = 'a98e7f9f58a9492d2cb216baa59c890ed8ce02f3' +RELEASE_GIT_HEAD = 'c9356f308dd3c5f9f494cb40ed14c5df017b4fe0' VARIANT = None @@ -12,4 +12,4 @@ CHANNEL = 'stable' ORIGIN = 'yt-dlp/yt-dlp' -_pkg_version = '2025.10.14' +_pkg_version = '2025.10.22'