yt-dlp 2025.11.15.232912.dev0__py3-none-any.whl → 2025.11.18.232918.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. yt_dlp/downloader/common.py +2 -1
  2. yt_dlp/extractor/_extractors.py +9 -1
  3. yt_dlp/extractor/frontro.py +164 -0
  4. yt_dlp/extractor/lazy_extractors.py +53 -5
  5. yt_dlp/extractor/mave.py +119 -36
  6. yt_dlp/extractor/soundcloud.py +7 -1
  7. yt_dlp/extractor/thisoldhouse.py +33 -37
  8. yt_dlp/extractor/yfanefa.py +67 -0
  9. yt_dlp/extractor/youtube/_video.py +12 -4
  10. yt_dlp/networking/_urllib.py +2 -0
  11. yt_dlp/version.py +3 -3
  12. {yt_dlp-2025.11.15.232912.dev0.dist-info → yt_dlp-2025.11.18.232918.dev0.dist-info}/METADATA +1 -1
  13. {yt_dlp-2025.11.15.232912.dev0.dist-info → yt_dlp-2025.11.18.232918.dev0.dist-info}/RECORD +21 -19
  14. {yt_dlp-2025.11.15.232912.dev0.data → yt_dlp-2025.11.18.232918.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
  15. {yt_dlp-2025.11.15.232912.dev0.data → yt_dlp-2025.11.18.232918.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
  16. {yt_dlp-2025.11.15.232912.dev0.data → yt_dlp-2025.11.18.232918.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
  17. {yt_dlp-2025.11.15.232912.dev0.data → yt_dlp-2025.11.18.232918.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
  18. {yt_dlp-2025.11.15.232912.dev0.data → yt_dlp-2025.11.18.232918.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
  19. {yt_dlp-2025.11.15.232912.dev0.dist-info → yt_dlp-2025.11.18.232918.dev0.dist-info}/WHEEL +0 -0
  20. {yt_dlp-2025.11.15.232912.dev0.dist-info → yt_dlp-2025.11.18.232918.dev0.dist-info}/entry_points.txt +0 -0
  21. {yt_dlp-2025.11.15.232912.dev0.dist-info → yt_dlp-2025.11.18.232918.dev0.dist-info}/licenses/LICENSE +0 -0
yt_dlp/extractor/mave.py CHANGED
@@ -1,7 +1,9 @@
1
- import re
1
+ import functools
2
+ import math
2
3
 
3
4
  from .common import InfoExtractor
4
5
  from ..utils import (
6
+ InAdvancePagedList,
5
7
  clean_html,
6
8
  int_or_none,
7
9
  parse_iso8601,
@@ -10,15 +12,64 @@ from ..utils import (
10
12
  from ..utils.traversal import require, traverse_obj
11
13
 
12
14
 
13
- class MaveIE(InfoExtractor):
14
- _VALID_URL = r'https?://(?P<channel>[\w-]+)\.mave\.digital/(?P<id>ep-\d+)'
15
+ class MaveBaseIE(InfoExtractor):
16
+ _API_BASE_URL = 'https://api.mave.digital/v1/website'
17
+ _API_BASE_STORAGE_URL = 'https://store.cloud.mts.ru/mave/'
18
+
19
+ def _load_channel_meta(self, channel_id, display_id):
20
+ return traverse_obj(self._download_json(
21
+ f'{self._API_BASE_URL}/{channel_id}/', display_id,
22
+ note='Downloading channel metadata'), 'podcast')
23
+
24
+ def _load_episode_meta(self, channel_id, episode_code, display_id):
25
+ return self._download_json(
26
+ f'{self._API_BASE_URL}/{channel_id}/episodes/{episode_code}',
27
+ display_id, note='Downloading episode metadata')
28
+
29
+ def _create_entry(self, channel_id, channel_meta, episode_meta):
30
+ episode_code = traverse_obj(episode_meta, ('code', {int}, {require('episode code')}))
31
+ return {
32
+ 'display_id': f'{channel_id}-{episode_code}',
33
+ 'extractor_key': MaveIE.ie_key(),
34
+ 'extractor': MaveIE.IE_NAME,
35
+ 'webpage_url': f'https://{channel_id}.mave.digital/ep-{episode_code}',
36
+ 'channel_id': channel_id,
37
+ 'channel_url': f'https://{channel_id}.mave.digital/',
38
+ 'vcodec': 'none',
39
+ **traverse_obj(episode_meta, {
40
+ 'id': ('id', {str}),
41
+ 'url': ('audio', {urljoin(self._API_BASE_STORAGE_URL)}),
42
+ 'title': ('title', {str}),
43
+ 'description': ('description', {clean_html}),
44
+ 'thumbnail': ('image', {urljoin(self._API_BASE_STORAGE_URL)}),
45
+ 'duration': ('duration', {int_or_none}),
46
+ 'season_number': ('season', {int_or_none}),
47
+ 'episode_number': ('number', {int_or_none}),
48
+ 'view_count': ('listenings', {int_or_none}),
49
+ 'like_count': ('reactions', lambda _, v: v['type'] == 'like', 'count', {int_or_none}, any),
50
+ 'dislike_count': ('reactions', lambda _, v: v['type'] == 'dislike', 'count', {int_or_none}, any),
51
+ 'age_limit': ('is_explicit', {bool}, {lambda x: 18 if x else None}),
52
+ 'timestamp': ('publish_date', {parse_iso8601}),
53
+ }),
54
+ **traverse_obj(channel_meta, {
55
+ 'series_id': ('id', {str}),
56
+ 'series': ('title', {str}),
57
+ 'channel': ('title', {str}),
58
+ 'uploader': ('author', {str}),
59
+ }),
60
+ }
61
+
62
+
63
+ class MaveIE(MaveBaseIE):
64
+ IE_NAME = 'mave'
65
+ _VALID_URL = r'https?://(?P<channel_id>[\w-]+)\.mave\.digital/ep-(?P<episode_code>\d+)'
15
66
  _TESTS = [{
16
67
  'url': 'https://ochenlichnoe.mave.digital/ep-25',
17
68
  'md5': 'aa3e513ef588b4366df1520657cbc10c',
18
69
  'info_dict': {
19
70
  'id': '4035f587-914b-44b6-aa5a-d76685ad9bc2',
20
71
  'ext': 'mp3',
21
- 'display_id': 'ochenlichnoe-ep-25',
72
+ 'display_id': 'ochenlichnoe-25',
22
73
  'title': 'Между мной и миром: психология самооценки',
23
74
  'description': 'md5:4b7463baaccb6982f326bce5c700382a',
24
75
  'uploader': 'Самарский университет',
@@ -45,7 +96,7 @@ class MaveIE(InfoExtractor):
45
96
  'info_dict': {
46
97
  'id': '41898bb5-ff57-4797-9236-37a8e537aa21',
47
98
  'ext': 'mp3',
48
- 'display_id': 'budem-ep-12',
99
+ 'display_id': 'budem-12',
49
100
  'title': 'Екатерина Михайлова: "Горе от ума" не про женщин написана',
50
101
  'description': 'md5:fa3bdd59ee829dfaf16e3efcb13f1d19',
51
102
  'uploader': 'Полина Цветкова+Евгения Акопова',
@@ -68,40 +119,72 @@ class MaveIE(InfoExtractor):
68
119
  'upload_date': '20241230',
69
120
  },
70
121
  }]
71
- _API_BASE_URL = 'https://api.mave.digital/'
72
122
 
73
123
  def _real_extract(self, url):
74
- channel_id, slug = self._match_valid_url(url).group('channel', 'id')
75
- display_id = f'{channel_id}-{slug}'
76
- webpage = self._download_webpage(url, display_id)
77
- data = traverse_obj(
78
- self._search_nuxt_json(webpage, display_id),
79
- ('data', lambda _, v: v['activeEpisodeData'], any, {require('podcast data')}))
124
+ channel_id, episode_code = self._match_valid_url(url).group(
125
+ 'channel_id', 'episode_code')
126
+ display_id = f'{channel_id}-{episode_code}'
127
+
128
+ channel_meta = self._load_channel_meta(channel_id, display_id)
129
+ episode_meta = self._load_episode_meta(channel_id, episode_code, display_id)
130
+
131
+ return self._create_entry(channel_id, channel_meta, episode_meta)
132
+
133
+
134
+ class MaveChannelIE(MaveBaseIE):
135
+ IE_NAME = 'mave:channel'
136
+ _VALID_URL = r'https?://(?P<id>[\w-]+)\.mave\.digital/?(?:$|[?#])'
137
+ _TESTS = [{
138
+ 'url': 'https://budem.mave.digital/',
139
+ 'info_dict': {
140
+ 'id': 'budem',
141
+ 'title': 'Все там будем',
142
+ 'description': 'md5:f04ae12a42be0f1d765c5e326b41987a',
143
+ },
144
+ 'playlist_mincount': 15,
145
+ }, {
146
+ 'url': 'https://ochenlichnoe.mave.digital/',
147
+ 'info_dict': {
148
+ 'id': 'ochenlichnoe',
149
+ 'title': 'Очень личное',
150
+ 'description': 'md5:ee36a6a52546b91b487fe08c552fdbb2',
151
+ },
152
+ 'playlist_mincount': 20,
153
+ }, {
154
+ 'url': 'https://geekcity.mave.digital/',
155
+ 'info_dict': {
156
+ 'id': 'geekcity',
157
+ 'title': 'Мужчины в трико',
158
+ 'description': 'md5:4164d425d60a0d97abdce9d1f6f8e049',
159
+ },
160
+ 'playlist_mincount': 80,
161
+ }]
162
+ _PAGE_SIZE = 50
163
+
164
+ def _entries(self, channel_id, channel_meta, page_num):
165
+ page_data = self._download_json(
166
+ f'{self._API_BASE_URL}/{channel_id}/episodes', channel_id, query={
167
+ 'view': 'all',
168
+ 'page': page_num + 1,
169
+ 'sort': 'newest',
170
+ 'format': 'all',
171
+ }, note=f'Downloading page {page_num + 1}')
172
+ for ep in traverse_obj(page_data, ('episodes', lambda _, v: v['audio'] and v['id'])):
173
+ yield self._create_entry(channel_id, channel_meta, ep)
174
+
175
+ def _real_extract(self, url):
176
+ channel_id = self._match_id(url)
177
+
178
+ channel_meta = self._load_channel_meta(channel_id, channel_id)
80
179
 
81
180
  return {
82
- 'display_id': display_id,
83
- 'channel_id': channel_id,
84
- 'channel_url': f'https://{channel_id}.mave.digital/',
85
- 'vcodec': 'none',
86
- 'thumbnail': re.sub(r'_\d+(?=\.(?:jpg|png))', '', self._og_search_thumbnail(webpage, default='')) or None,
87
- **traverse_obj(data, ('activeEpisodeData', {
88
- 'url': ('audio', {urljoin(self._API_BASE_URL)}),
89
- 'id': ('id', {str}),
181
+ '_type': 'playlist',
182
+ 'id': channel_id,
183
+ **traverse_obj(channel_meta, {
90
184
  'title': ('title', {str}),
91
- 'description': ('description', {clean_html}),
92
- 'duration': ('duration', {int_or_none}),
93
- 'season_number': ('season', {int_or_none}),
94
- 'episode_number': ('number', {int_or_none}),
95
- 'view_count': ('listenings', {int_or_none}),
96
- 'like_count': ('reactions', lambda _, v: v['type'] == 'like', 'count', {int_or_none}, any),
97
- 'dislike_count': ('reactions', lambda _, v: v['type'] == 'dislike', 'count', {int_or_none}, any),
98
- 'age_limit': ('is_explicit', {bool}, {lambda x: 18 if x else None}),
99
- 'timestamp': ('publish_date', {parse_iso8601}),
100
- })),
101
- **traverse_obj(data, ('podcast', 'podcast', {
102
- 'series_id': ('id', {str}),
103
- 'series': ('title', {str}),
104
- 'channel': ('title', {str}),
105
- 'uploader': ('author', {str}),
106
- })),
185
+ 'description': ('description', {str}),
186
+ }),
187
+ 'entries': InAdvancePagedList(
188
+ functools.partial(self._entries, channel_id, channel_meta),
189
+ math.ceil(channel_meta['episodes_count'] / self._PAGE_SIZE), self._PAGE_SIZE),
107
190
  }
@@ -1064,7 +1064,7 @@ class SoundcloudRelatedIE(SoundcloudPagedPlaylistBaseIE):
1064
1064
 
1065
1065
 
1066
1066
  class SoundcloudPlaylistIE(SoundcloudPlaylistBaseIE):
1067
- _VALID_URL = r'https?://api(?:-v2)?\.soundcloud\.com/playlists/(?P<id>[0-9]+)(?:/?\?secret_token=(?P<token>[^&]+?))?$'
1067
+ _VALID_URL = r'https?://api(?:-v2)?\.soundcloud\.com/playlists/(?:soundcloud(?:%3A|:)playlists(?:%3A|:))?(?P<id>[0-9]+)(?:/?\?secret_token=(?P<token>[^&]+?))?$'
1068
1068
  IE_NAME = 'soundcloud:playlist'
1069
1069
  _TESTS = [{
1070
1070
  'url': 'https://api.soundcloud.com/playlists/4110309',
@@ -1079,6 +1079,12 @@ class SoundcloudPlaylistIE(SoundcloudPlaylistBaseIE):
1079
1079
  'album': 'TILT Brass - Bowery Poetry Club, August \'03 [Non-Site SCR 02]',
1080
1080
  },
1081
1081
  'playlist_count': 6,
1082
+ }, {
1083
+ 'url': 'https://api.soundcloud.com/playlists/soundcloud%3Aplaylists%3A1759227795',
1084
+ 'only_matching': True,
1085
+ }, {
1086
+ 'url': 'https://api.soundcloud.com/playlists/soundcloud:playlists:2104769627?secret_token=s-wmpCLuExeYX',
1087
+ 'only_matching': True,
1082
1088
  }]
1083
1089
 
1084
1090
  def _real_extract(self, url):
@@ -1,18 +1,17 @@
1
- import json
1
+ import urllib.parse
2
2
 
3
3
  from .brightcove import BrightcoveNewIE
4
4
  from .common import InfoExtractor
5
5
  from .zype import ZypeIE
6
6
  from ..networking import HEADRequest
7
- from ..networking.exceptions import HTTPError
8
7
  from ..utils import (
9
8
  ExtractorError,
10
9
  filter_dict,
11
10
  parse_qs,
12
11
  smuggle_url,
13
- try_call,
14
12
  urlencode_postdata,
15
13
  )
14
+ from ..utils.traversal import traverse_obj
16
15
 
17
16
 
18
17
  class ThisOldHouseIE(InfoExtractor):
@@ -77,46 +76,43 @@ class ThisOldHouseIE(InfoExtractor):
77
76
  'only_matching': True,
78
77
  }]
79
78
 
80
- _LOGIN_URL = 'https://login.thisoldhouse.com/usernamepassword/login'
81
-
82
79
  def _perform_login(self, username, password):
83
- self._request_webpage(
84
- HEADRequest('https://www.thisoldhouse.com/insider'), None, 'Requesting session cookies')
85
- urlh = self._request_webpage(
86
- 'https://www.thisoldhouse.com/wp-login.php', None, 'Requesting login info',
87
- errnote='Unable to login', query={'redirect_to': 'https://www.thisoldhouse.com/insider'})
80
+ login_page = self._download_webpage(
81
+ 'https://www.thisoldhouse.com/insider-login', None, 'Downloading login page')
82
+ hidden_inputs = self._hidden_inputs(login_page)
83
+ response = self._download_json(
84
+ 'https://www.thisoldhouse.com/wp-admin/admin-ajax.php', None, 'Logging in',
85
+ headers={
86
+ 'Accept': 'application/json',
87
+ 'X-Requested-With': 'XMLHttpRequest',
88
+ }, data=urlencode_postdata(filter_dict({
89
+ 'action': 'onebill_subscriber_login',
90
+ 'email': username,
91
+ 'password': password,
92
+ 'pricingPlanTerm': hidden_inputs['pricing_plan_term'],
93
+ 'utm_parameters': hidden_inputs.get('utm_parameters'),
94
+ 'nonce': hidden_inputs['mdcr_onebill_login_nonce'],
95
+ })))
88
96
 
89
- try:
90
- auth_form = self._download_webpage(
91
- self._LOGIN_URL, None, 'Submitting credentials', headers={
92
- 'Content-Type': 'application/json',
93
- 'Referer': urlh.url,
94
- }, data=json.dumps(filter_dict({
95
- **{('client_id' if k == 'client' else k): v[0] for k, v in parse_qs(urlh.url).items()},
96
- 'tenant': 'thisoldhouse',
97
- 'username': username,
98
- 'password': password,
99
- 'popup_options': {},
100
- 'sso': True,
101
- '_csrf': try_call(lambda: self._get_cookies(self._LOGIN_URL)['_csrf'].value),
102
- '_intstate': 'deprecated',
103
- }), separators=(',', ':')).encode())
104
- except ExtractorError as e:
105
- if isinstance(e.cause, HTTPError) and e.cause.status == 401:
97
+ message = traverse_obj(response, ('data', 'message', {str}))
98
+ if not response['success']:
99
+ if message and 'Something went wrong' in message:
106
100
  raise ExtractorError('Invalid username or password', expected=True)
107
- raise
108
-
109
- self._request_webpage(
110
- 'https://login.thisoldhouse.com/login/callback', None, 'Completing login',
111
- data=urlencode_postdata(self._hidden_inputs(auth_form)))
101
+ raise ExtractorError(message or 'Login was unsuccessful')
102
+ if message and 'Your subscription is not active' in message:
103
+ self.report_warning(
104
+ f'{self.IE_NAME} said your subscription is not active. '
105
+ f'If your subscription is active, this could be caused by too many sign-ins, '
106
+ f'and you should instead try using {self._login_hint(method="cookies")[4:]}')
107
+ else:
108
+ self.write_debug(f'{self.IE_NAME} said: {message}')
112
109
 
113
110
  def _real_extract(self, url):
114
111
  display_id = self._match_id(url)
115
- webpage = self._download_webpage(url, display_id)
116
- if 'To Unlock This content' in webpage:
117
- self.raise_login_required(
118
- 'This video is only available for subscribers. '
119
- 'Note that --cookies-from-browser may not work due to this site using session cookies')
112
+ webpage, urlh = self._download_webpage_handle(url, display_id)
113
+ # If login response says inactive subscription, site redirects to frontpage for Insider content
114
+ if 'To Unlock This content' in webpage or urllib.parse.urlparse(urlh.url).path in ('', '/'):
115
+ self.raise_login_required('This video is only available for subscribers')
120
116
 
121
117
  video_url, video_id = self._search_regex(
122
118
  r'<iframe[^>]+src=[\'"]((?:https?:)?//(?:www\.)?thisoldhouse\.(?:chorus\.build|com)/videos/zype/([0-9a-f]{24})[^\'"]*)[\'"]',
@@ -0,0 +1,67 @@
1
+ from .common import InfoExtractor
2
+ from ..utils import (
3
+ determine_ext,
4
+ int_or_none,
5
+ join_nonempty,
6
+ remove_end,
7
+ url_or_none,
8
+ )
9
+ from ..utils.traversal import traverse_obj
10
+
11
+
12
+ class YfanefaIE(InfoExtractor):
13
+ IE_NAME = 'yfanefa'
14
+ _VALID_URL = r'https?://(?:www\.)?yfanefa\.com/(?P<id>[^?#]+)'
15
+ _TESTS = [{
16
+ 'url': 'https://www.yfanefa.com/record/2717',
17
+ 'info_dict': {
18
+ 'id': 'record-2717',
19
+ 'ext': 'mp4',
20
+ 'title': 'THE HALLAMSHIRE RIFLES LEAVING SHEFFIELD, 1914',
21
+ 'duration': 5239,
22
+ 'thumbnail': r're:https://media\.yfanefa\.com/storage/v1/file/',
23
+ },
24
+ }, {
25
+ 'url': 'https://www.yfanefa.com/news/53',
26
+ 'info_dict': {
27
+ 'id': 'news-53',
28
+ 'ext': 'mp4',
29
+ 'title': 'Memory Bank: Bradford Launch',
30
+ 'thumbnail': r're:https://media\.yfanefa\.com/storage/v1/file/',
31
+ },
32
+ }, {
33
+ 'url': 'https://www.yfanefa.com/evaluating_nature_matters',
34
+ 'info_dict': {
35
+ 'id': 'evaluating_nature_matters',
36
+ 'ext': 'mp4',
37
+ 'title': 'Evaluating Nature Matters',
38
+ 'thumbnail': r're:https://media\.yfanefa\.com/storage/v1/file/',
39
+ },
40
+ }]
41
+
42
+ def _real_extract(self, url):
43
+ video_id = self._match_id(url)
44
+
45
+ webpage = self._download_webpage(url, video_id)
46
+ player_data = self._search_json(
47
+ r'iwPlayer\.options\["[\w.]+"\]\s*=', webpage, 'player options', video_id)
48
+
49
+ formats = []
50
+ video_url = join_nonempty(player_data['url'], player_data.get('signature'), delim='')
51
+ if determine_ext(video_url) == 'm3u8':
52
+ formats = self._extract_m3u8_formats(
53
+ video_url, video_id, 'mp4', m3u8_id='hls')
54
+ else:
55
+ formats = [{'url': video_url, 'ext': 'mp4'}]
56
+
57
+ return {
58
+ 'id': video_id.strip('/').replace('/', '-'),
59
+ 'title':
60
+ self._og_search_title(webpage, default=None)
61
+ or remove_end(self._html_extract_title(webpage), ' | Yorkshire Film Archive'),
62
+ 'formats': formats,
63
+ **traverse_obj(player_data, {
64
+ 'thumbnail': ('preview', {url_or_none}),
65
+ 'duration': ('duration', {int_or_none}),
66
+ }),
67
+ }
@@ -3150,6 +3150,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
3150
3150
  self._downloader.deprecated_feature('[youtube] include_duplicate_formats extractor argument is deprecated. '
3151
3151
  'Use formats=duplicate extractor argument instead')
3152
3152
 
3153
+ def is_super_resolution(f_url):
3154
+ return '1' in traverse_obj(f_url, ({parse_qs}, 'xtags', ..., {urllib.parse.parse_qs}, 'sr', ...))
3155
+
3153
3156
  def solve_sig(s, spec):
3154
3157
  return ''.join(s[i] for i in spec)
3155
3158
 
@@ -3202,7 +3205,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
3202
3205
  def get_stream_id(fmt_stream):
3203
3206
  return str_or_none(fmt_stream.get('itag')), traverse_obj(fmt_stream, 'audioTrack', 'id'), fmt_stream.get('isDrc')
3204
3207
 
3205
- def process_format_stream(fmt_stream, proto, missing_pot):
3208
+ def process_format_stream(fmt_stream, proto, missing_pot, super_resolution=False):
3206
3209
  itag = str_or_none(fmt_stream.get('itag'))
3207
3210
  audio_track = fmt_stream.get('audioTrack') or {}
3208
3211
  quality = fmt_stream.get('quality')
@@ -3253,10 +3256,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
3253
3256
  dct = {
3254
3257
  'asr': int_or_none(fmt_stream.get('audioSampleRate')),
3255
3258
  'filesize': int_or_none(fmt_stream.get('contentLength')),
3256
- 'format_id': f'{itag}{"-drc" if fmt_stream.get("isDrc") else ""}',
3259
+ 'format_id': join_nonempty(itag, (
3260
+ 'drc' if fmt_stream.get('isDrc')
3261
+ else 'sr' if super_resolution
3262
+ else None)),
3257
3263
  'format_note': join_nonempty(
3258
3264
  join_nonempty(audio_track.get('displayName'), audio_track.get('audioIsDefault') and '(default)', delim=' '),
3259
- name, fmt_stream.get('isDrc') and 'DRC',
3265
+ name, fmt_stream.get('isDrc') and 'DRC', super_resolution and 'AI-upscaled',
3260
3266
  try_get(fmt_stream, lambda x: x['projectionType'].replace('RECTANGULAR', '').lower()),
3261
3267
  try_get(fmt_stream, lambda x: x['spatialAudioType'].replace('SPATIAL_AUDIO_TYPE_', '').lower()),
3262
3268
  is_damaged and 'DAMAGED', missing_pot and 'MISSING POT',
@@ -3342,7 +3348,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
3342
3348
  self.report_warning(msg, video_id, only_once=True)
3343
3349
  continue
3344
3350
 
3345
- fmt = process_format_stream(fmt_stream, proto, missing_pot=require_po_token and not po_token)
3351
+ fmt = process_format_stream(
3352
+ fmt_stream, proto, missing_pot=require_po_token and not po_token,
3353
+ super_resolution=is_super_resolution(fmt_url))
3346
3354
  if not fmt:
3347
3355
  continue
3348
3356
 
@@ -305,6 +305,8 @@ class UrllibResponseAdapter(Response):
305
305
  status=getattr(res, 'status', None) or res.getcode(), reason=getattr(res, 'reason', None))
306
306
 
307
307
  def read(self, amt=None):
308
+ if self.closed:
309
+ return b''
308
310
  try:
309
311
  data = self.fp.read(amt)
310
312
  underlying = getattr(self.fp, 'fp', None)
yt_dlp/version.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # Autogenerated by devscripts/update-version.py
2
2
 
3
- __version__ = '2025.11.15.232912'
3
+ __version__ = '2025.11.18.232918'
4
4
 
5
- RELEASE_GIT_HEAD = 'b333ef1b3f961e292a8bf7052c54b54c81587a17'
5
+ RELEASE_GIT_HEAD = '9daba4f442139ee2537746398afc5ac30b51c28c'
6
6
 
7
7
  VARIANT = 'pip'
8
8
 
@@ -12,4 +12,4 @@ CHANNEL = 'nightly'
12
12
 
13
13
  ORIGIN = 'yt-dlp/yt-dlp-nightly-builds'
14
14
 
15
- _pkg_version = '2025.11.15.232912dev'
15
+ _pkg_version = '2025.11.18.232918dev'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yt-dlp
3
- Version: 2025.11.15.232912.dev0
3
+ Version: 2025.11.18.232918.dev0
4
4
  Summary: A feature-rich command-line audio/video downloader
5
5
  Project-URL: Documentation, https://github.com/yt-dlp/yt-dlp#readme
6
6
  Project-URL: Repository, https://github.com/yt-dlp/yt-dlp
@@ -11,7 +11,7 @@ yt_dlp/options.py,sha256=Icc0JRiKOzITWoMujE_ihEmkCS-uCcie42XhIh8LvS4,100388
11
11
  yt_dlp/plugins.py,sha256=EGmR0ydaahNspGrgszTNX4-YjHe93WOOhcw1gf6PZSs,8215
12
12
  yt_dlp/socks.py,sha256=oAuAfWM6jxI8A5hHDLEKq2U2-k9NyMB_z6nrKzNE9fg,8936
13
13
  yt_dlp/update.py,sha256=sY7gNFBQorzs7sEjRrqL5QOsTBNmGGa_FnpTtbxY1vA,25280
14
- yt_dlp/version.py,sha256=fSLHDlXjNx2jXsCst0YjFkxV2TIcIzkRVwcF_CxlVGg,360
14
+ yt_dlp/version.py,sha256=9zKKbcKfkkFu5Ey3XWP2vxqwd3C_QP2zv6qMENNJ1dk,360
15
15
  yt_dlp/webvtt.py,sha256=ONkXaaNCZcX8pQhJn3iwIKyaQ34BtVDrMEdG6wRNZwM,11451
16
16
  yt_dlp/__pyinstaller/__init__.py,sha256=-c4Zo8nQGKAm8wc_LDscxMtK7zr_YhZwRnC9CMruUBE,72
17
17
  yt_dlp/__pyinstaller/hook-yt_dlp.py,sha256=5Rd0zV2pDskjY1KtT0wsjxv4hStx67sLCjUexsFvFus,1339
@@ -27,7 +27,7 @@ yt_dlp/dependencies/Cryptodome.py,sha256=91_k5HjJ24XM03-Y6-3tWHNr6knebvNIROlbREg
27
27
  yt_dlp/dependencies/__init__.py,sha256=hjIc6wZyo9GVSte4KY-zVr0SGGXRKvUNwhjeHK8Ie3U,2277
28
28
  yt_dlp/downloader/__init__.py,sha256=ymTbNmY6tgAWZJ9yTkIeX3khDViBcdQMPl23Fp36Sso,4518
29
29
  yt_dlp/downloader/bunnycdn.py,sha256=s18-DtSCtlRp1gyIo1B59AbEEeaX45rfWHHmJx0lLQM,1762
30
- yt_dlp/downloader/common.py,sha256=CzwHglE2pKQFNwt9EHJHb_a77lWXIc2pmLXJLtaJt0Q,20589
30
+ yt_dlp/downloader/common.py,sha256=7i5c15Wf88GSR-TO11eQN5Y-GjcodbxnoJe4h6YNRTs,20700
31
31
  yt_dlp/downloader/dash.py,sha256=krbHwJRK8mIKH5WB3W4e8XcFlRm0gFgbmQBHtT8K5Kk,3936
32
32
  yt_dlp/downloader/external.py,sha256=poB_Z1s02IdLmqtCvTycUmaeevqpF8e48_eve2KXR0s,28814
33
33
  yt_dlp/downloader/f4m.py,sha256=ab7pJ_EbrCeWpdYG0JMv7_hRchaYKGMTB1n490uwTWA,15326
@@ -43,7 +43,7 @@ yt_dlp/downloader/rtsp.py,sha256=LenaspFKHde5EkP52oU6jiHYxYferyyGgFPLfm6S5Hs,147
43
43
  yt_dlp/downloader/websocket.py,sha256=G39SkXEIGtUEYaP1_ODXMiZGZgIrFeb3wqlfVypcHUM,1772
44
44
  yt_dlp/downloader/youtube_live_chat.py,sha256=JLpGIUNNbuM7ZuZMY9A6X3xrRDfs3sWz4tzXLXpa1P4,10875
45
45
  yt_dlp/extractor/__init__.py,sha256=XMV5BpSWbaDXGkkI2sim_iJk7y0BpCgrDcPjwenA1Y0,1764
46
- yt_dlp/extractor/_extractors.py,sha256=wUwgTrln_BsugAbyYz1h8dSb9aZC6BpuWenDoodSD_4,54326
46
+ yt_dlp/extractor/_extractors.py,sha256=G80d7lditiO1CgMG-TMLBENomzRAOOBP_m94jsnNxsc,54449
47
47
  yt_dlp/extractor/abc.py,sha256=rGyouEhGP6gdZlDxkYHHmdnMpaOYoQDXeRMy_JeON1I,18876
48
48
  yt_dlp/extractor/abcnews.py,sha256=STJP1ynxEOWURHQSoT-568klK6NIl7MY4KSjjUK_CAg,6326
49
49
  yt_dlp/extractor/abcotvs.py,sha256=VYLvqEeBr02xOakqx09A5p4e81Ap1Rp2yfcPpNiTu9g,4555
@@ -322,6 +322,7 @@ yt_dlp/extractor/freesound.py,sha256=0z0TlERAktdZlK8Obn3PQdpJpw-KNjG3Ln2dw4ZNyRc
322
322
  yt_dlp/extractor/freespeech.py,sha256=eFTmuzxvziHE5jAdjInNBTNSunNYGa3EyqHeSPa-ZyE,1016
323
323
  yt_dlp/extractor/freetv.py,sha256=AqN2iBuTlRZcFstjEPdVDK3ggy-0sDFBSBAvWPrAZmM,5534
324
324
  yt_dlp/extractor/frontendmasters.py,sha256=rJcbB9fEf7A5FxZ3xMh6DNUPBDG1o36PrcefunUbd7Y,8519
325
+ yt_dlp/extractor/frontro.py,sha256=U_O3Yfzw__ZpaydIdUdNFXvQ43LINaX9EcJeouRBNcY,6405
325
326
  yt_dlp/extractor/fujitv.py,sha256=exYN_5YelBdV6PZyBcipQT5ftAjC12GxFc0-zW8aUzs,3186
326
327
  yt_dlp/extractor/funk.py,sha256=8MyPwtKBv-S49LXHaS0VzAn8ueGDKT5HjN-7mNHxPX8,1798
327
328
  yt_dlp/extractor/funker530.py,sha256=QcA2rjXFdzGC4QQZNbu6rcBi2BLrOfr8C2D4JwR6fCY,3275
@@ -456,7 +457,7 @@ yt_dlp/extractor/la7.py,sha256=GShDLu1N0rS1bY4uIiUkznThvn7gNiwtSgmh7Rs7t08,9435
456
457
  yt_dlp/extractor/laracasts.py,sha256=PzTqAbHXiUqog-mpp2qR_rpKa-sZel4mLyzWTPkbDuc,4587
457
458
  yt_dlp/extractor/lastfm.py,sha256=OpmE-Y-2rcav2r2xaDQzX_EJiltmbbe6fO9VzkLqNWQ,4748
458
459
  yt_dlp/extractor/laxarxames.py,sha256=-YyL-5y4t2L9ptTSLXhvK-SJwvXGqv5l1HfT129zF0c,2773
459
- yt_dlp/extractor/lazy_extractors.py,sha256=THqRIYl_nS359OC19AJ0iOrWjmqd1on8NM52ZYjYHyc,812524
460
+ yt_dlp/extractor/lazy_extractors.py,sha256=AfPOVhNG3sz9dtHIBrjvEcdOsQgjUcaFh4g6mD7DZJk,813989
460
461
  yt_dlp/extractor/lbry.py,sha256=gC9jRRo8wSXc1-6somhW13brAtmWGlJ5_Q0NMhZpKwk,18078
461
462
  yt_dlp/extractor/lci.py,sha256=_0XuoITIt_QFA-6eBNpDXZZfouwUWfcdHQlpAOuiLEs,2131
462
463
  yt_dlp/extractor/lcp.py,sha256=edMA8L-eJZLquDvHcSY4oFWI0C8yGgjqW1tmhhLMJ5U,2279
@@ -500,7 +501,7 @@ yt_dlp/extractor/markiza.py,sha256=y4KwUdjMbmAhi92BngLqTK3kY5QDy7SyCr_8es3hwNA,4
500
501
  yt_dlp/extractor/massengeschmacktv.py,sha256=ajnGffhyJy0qstbInqWckjWdt4UPi0_4vdFLKITCXZ8,2642
501
502
  yt_dlp/extractor/masters.py,sha256=7lEwoCXZtsOg9M8EcS5n-awpTSwbcUdmhJbz_z-lZx4,1449
502
503
  yt_dlp/extractor/matchtv.py,sha256=jheNxouYo0ya1S3Ge_9v4Q43RNdaANe8h62vxbs0qe8,1248
503
- yt_dlp/extractor/mave.py,sha256=I0I2eCSlvbM-xsUkgsQPlPaSQ6llNd-JAFFfKC_gNUk,4656
504
+ yt_dlp/extractor/mave.py,sha256=zgCzUHTLSbIPDsz55Z8AUdfdzmdg9sG22fkp39Dq008,7768
504
505
  yt_dlp/extractor/mbn.py,sha256=XKkbolnM1xFM62B5KJzWsd4fDLf7a000zq0Ielj3Ow0,3897
505
506
  yt_dlp/extractor/mdr.py,sha256=TT4CxNHkkZNHzP4WlYIoZFCKgBYexOwGk4B6j3X-jEA,5103
506
507
  yt_dlp/extractor/medaltv.py,sha256=wucgo6wGUI19YM2Lm771Vv1uqTvrtmSdEvMN2RCwwpA,5980
@@ -805,7 +806,7 @@ yt_dlp/extractor/snotr.py,sha256=z2ugNYgFmi1iuukSgUqv7CimKZyTHei4VrS360dUDoQ,243
805
806
  yt_dlp/extractor/softwhiteunderbelly.py,sha256=tBP-MIb-MRwM9CL51WwjM8vkA5pd8PF_0xV40npgXAg,4023
806
807
  yt_dlp/extractor/sohu.py,sha256=cKU0DMDkBSZbYLeptQ6IKL0XeihBIIo4FSxkLzaR3-Q,11127
807
808
  yt_dlp/extractor/sonyliv.py,sha256=d0F5J3SHu7ifIOZcpjemJJMepMkRyn7yv7V2fhVKwVU,10678
808
- yt_dlp/extractor/soundcloud.py,sha256=htOQGui3jQ3Lh6x5UpvwARJj_Xnym_D9YPbUTb2i2fs,46406
809
+ yt_dlp/extractor/soundcloud.py,sha256=gPxoijhuXAvNtmAwSquBN3aD66T2f9bqjd2J00thwMw,46733
809
810
  yt_dlp/extractor/soundgasm.py,sha256=RrgeWo7fLaJA0esrQhterEN2PKQBJNY7yfAHxIE6268,2352
810
811
  yt_dlp/extractor/southpark.py,sha256=LAD11Or3zHMK8WNdbXPBdXvpGQFGC6_QsjcxeC_h4PQ,15149
811
812
  yt_dlp/extractor/sovietscloset.py,sha256=zaTx8m5rxVJmw0oKn4tnt9CSCCEc3bFF51_b4qxWi_M,7788
@@ -881,7 +882,7 @@ yt_dlp/extractor/thestar.py,sha256=LsMx9W2P3Qsf8NxA9Nth4MCh4RMMsacgafJm67OeD9Q,1
881
882
  yt_dlp/extractor/thesun.py,sha256=RrZwJO75EGhqMZLTAlxDfSO6lRs3Oql_ukVkpFZROBI,1656
882
883
  yt_dlp/extractor/theweatherchannel.py,sha256=MVQOtlNcPX8YB0X8lF-rNJHIMCYQ7DjSuuStfaA7aq8,4110
883
884
  yt_dlp/extractor/thisamericanlife.py,sha256=_2ijnZ3aXYYXosa_GcCysx5qE0lonHJYZVwpD93dmQU,1505
884
- yt_dlp/extractor/thisoldhouse.py,sha256=PyTZhejqFI2qpQQAJrQTIbocpOQ7XC2rau_7fd-_80s,5955
885
+ yt_dlp/extractor/thisoldhouse.py,sha256=YLnhWuuA0xMbY766tVDVKi-iaRwwmgOs8sdD-ydBFMs,5980
885
886
  yt_dlp/extractor/thisvid.py,sha256=dxN-I56UieWt92iS2VSXK7IckobVNQ6BYPgvOJ9vWJI,8628
886
887
  yt_dlp/extractor/threeqsdn.py,sha256=zNHqF2ELqulYpoUCp1YYPlf0tyPS2krsrEUkS0Cw8YQ,6219
887
888
  yt_dlp/extractor/threespeak.py,sha256=agG3Ue0h19dAknJHwrK9a3RBQB4aja-5cx1crkOCIUc,4025
@@ -1037,6 +1038,7 @@ yt_dlp/extractor/yandexmusic.py,sha256=MWrxFAxW2e2wY9-H-8jjmCWqZ3w0Py0O12vOglUtu
1037
1038
  yt_dlp/extractor/yandexvideo.py,sha256=U_bLUG7Xxzd7ddSjcXhP7d4WbDiQCAoqfX7oZoapFKY,17989
1038
1039
  yt_dlp/extractor/yapfiles.py,sha256=wA1T72I2eExJoqC4Ubw4wFHSUzYfgKwN9NhWKDW7DLM,3253
1039
1040
  yt_dlp/extractor/yappy.py,sha256=SZ-b1Gt7SFwS5_r3pnglET5F-5R3T4y5tn6YuoMhHCs,5653
1041
+ yt_dlp/extractor/yfanefa.py,sha256=8M_TsZAij7a2cqENPrUAkWJEy9lEvGf6_ECU1U0_e_E,2320
1040
1042
  yt_dlp/extractor/yle_areena.py,sha256=WYRKxNP2GwdAMXsnPPtgPm7RoJ-KyxgquPaJNP1ASak,8036
1041
1043
  yt_dlp/extractor/youjizz.py,sha256=QziXA39RDdbnyVtPPCoka604_HXf3HRQxlO6tO2d_0o,3025
1042
1044
  yt_dlp/extractor/youku.py,sha256=AT9XUoBempqI9Uhn2OY2ApS837KeGMFBmePiM88Mmj8,10927
@@ -1062,7 +1064,7 @@ yt_dlp/extractor/youtube/_notifications.py,sha256=1nhavzW0e2QWFAWHkfbTU4sSXNp4vU
1062
1064
  yt_dlp/extractor/youtube/_redirect.py,sha256=WWWnGEkfSGBXpZFi_bWY4XcHZ8PDeK7UsndDaTYYhQg,9005
1063
1065
  yt_dlp/extractor/youtube/_search.py,sha256=E9raTPGjUD6mm81WBpT4AsaxyiTBHdNssgzeHwVeNOE,6552
1064
1066
  yt_dlp/extractor/youtube/_tab.py,sha256=NcbpPvJ4XiTDDNBtaLtCZQBKyo2HuNcq_V-AalY8zj8,115736
1065
- yt_dlp/extractor/youtube/_video.py,sha256=UuhqWh6d1D4eHyt7Cj6oz6REMoSBSdfen2xVppjdbYc,206486
1067
+ yt_dlp/extractor/youtube/_video.py,sha256=eufl1QR9LllGqUjgZ4gpQNvYblSK8JFBb4tXM-2JiEQ,206909
1066
1068
  yt_dlp/extractor/youtube/jsc/__init__.py,sha256=HaVFP8ikrLaE-ClAh39-S28WCF4S2KTRaSu7QvA28E8,289
1067
1069
  yt_dlp/extractor/youtube/jsc/_director.py,sha256=92pB-KVSs6plmE5R8gpjkZL9aeoWNR0XTnGOBXMy9go,13167
1068
1070
  yt_dlp/extractor/youtube/jsc/_registry.py,sha256=Vg9GkHKHKKPeRfUQ-XSw01mfx_2Xyodh0SJpwjawYCA,102
@@ -1092,7 +1094,7 @@ yt_dlp/networking/__init__.py,sha256=s8pdgISu2hNJr2EPUy1JIiWzmQJF6024jQhOyiZny08
1092
1094
  yt_dlp/networking/_curlcffi.py,sha256=hL5oSB3oqAL9d_uZlq_cfrrAPoJaZxtKnW82bFqC4sw,13243
1093
1095
  yt_dlp/networking/_helper.py,sha256=98BkGEycv76zwiv0EhgASzDmAtFoZZn7qoL-8eDGB5k,10019
1094
1096
  yt_dlp/networking/_requests.py,sha256=TXNODiGKizkUelCI3YVNrc5xX5jMA2teyfhddj-I0QI,15694
1095
- yt_dlp/networking/_urllib.py,sha256=gFnEvZ5y7982xZHE1bfm3pFqDXrNn8uwZfuccvYEwBY,17081
1097
+ yt_dlp/networking/_urllib.py,sha256=NFTOSV1SSDPY2Xi4a1Q3uMOQ6Jsx5eFTyvLVfI31V94,17128
1096
1098
  yt_dlp/networking/_websockets.py,sha256=ogM0RSGHu9FDyZhB87bCTXWr_JgdIeCph3T6fd7T8Kw,7483
1097
1099
  yt_dlp/networking/common.py,sha256=JkcnXIqMSdxh42s3G-HReU1jnAFcpzvZg31xAEX_e_k,22886
1098
1100
  yt_dlp/networking/exceptions.py,sha256=vaQvsWNksyg2kgsmljrbLs9FSOrQphxBYEZQUSEzlAk,2846
@@ -1118,13 +1120,13 @@ yt_dlp/utils/progress.py,sha256=t9kVvJ0oWuEqRzo9fdFbIhHUBtO_8mg348QwZ1faqLo,3261
1118
1120
  yt_dlp/utils/traversal.py,sha256=64E3RcZ56iSX50RI_HbKdDNftkETMLBaEPX791_b7yQ,18265
1119
1121
  yt_dlp/utils/jslib/__init__.py,sha256=CbdJiRA7Eh5PnjF2V4lDTcg0J0XjBMaaq0H4pCfq9Tk,87
1120
1122
  yt_dlp/utils/jslib/devalue.py,sha256=7DCGK_zUN0ZeV5hwPT06zaRMUxX_hyUyFWqs79rxw24,5621
1121
- yt_dlp-2025.11.15.232912.dev0.data/data/share/bash-completion/completions/yt-dlp,sha256=b0pb9GLseKD27CjnLE6LlhVxhfmQjmyqV6r_CRbd6ko,5989
1122
- yt_dlp-2025.11.15.232912.dev0.data/data/share/doc/yt_dlp/README.txt,sha256=1p-zv2tZPh1gscLG3ceL7GGCzC4TQU8mS9wiqPS-oXE,164662
1123
- yt_dlp-2025.11.15.232912.dev0.data/data/share/fish/vendor_completions.d/yt-dlp.fish,sha256=hLa6lZnm7keENpNCjml9A88hbvefdsdoOpAiaNCVyHo,51488
1124
- yt_dlp-2025.11.15.232912.dev0.data/data/share/man/man1/yt-dlp.1,sha256=C33_xKvu5DD5SScnivPeV2EJEYSeJ1tPXYZnlJBDayc,159125
1125
- yt_dlp-2025.11.15.232912.dev0.data/data/share/zsh/site-functions/_yt-dlp,sha256=pNhu8tT4ZKrksLRI2mXLqarzGGhnOlm_hkCBVhSxLzg,5985
1126
- yt_dlp-2025.11.15.232912.dev0.dist-info/METADATA,sha256=pfbM7fDbHMnsrKn19_elgcP3oSYgyy0glw6OcgJ_wuI,180054
1127
- yt_dlp-2025.11.15.232912.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1128
- yt_dlp-2025.11.15.232912.dev0.dist-info/entry_points.txt,sha256=vWfetvzYgZIwDfMW6BjCe0Cy4pmTZEXRNzxAkfYlRJA,103
1129
- yt_dlp-2025.11.15.232912.dev0.dist-info/licenses/LICENSE,sha256=fhLl30uuEsshWBuhV87SDhmGoFCN0Q0Oikq5pM-U6Fw,1211
1130
- yt_dlp-2025.11.15.232912.dev0.dist-info/RECORD,,
1123
+ yt_dlp-2025.11.18.232918.dev0.data/data/share/bash-completion/completions/yt-dlp,sha256=b0pb9GLseKD27CjnLE6LlhVxhfmQjmyqV6r_CRbd6ko,5989
1124
+ yt_dlp-2025.11.18.232918.dev0.data/data/share/doc/yt_dlp/README.txt,sha256=1p-zv2tZPh1gscLG3ceL7GGCzC4TQU8mS9wiqPS-oXE,164662
1125
+ yt_dlp-2025.11.18.232918.dev0.data/data/share/fish/vendor_completions.d/yt-dlp.fish,sha256=hLa6lZnm7keENpNCjml9A88hbvefdsdoOpAiaNCVyHo,51488
1126
+ yt_dlp-2025.11.18.232918.dev0.data/data/share/man/man1/yt-dlp.1,sha256=C33_xKvu5DD5SScnivPeV2EJEYSeJ1tPXYZnlJBDayc,159125
1127
+ yt_dlp-2025.11.18.232918.dev0.data/data/share/zsh/site-functions/_yt-dlp,sha256=pNhu8tT4ZKrksLRI2mXLqarzGGhnOlm_hkCBVhSxLzg,5985
1128
+ yt_dlp-2025.11.18.232918.dev0.dist-info/METADATA,sha256=k5AcRj2FZrpYxAxpcJ3NA3B9A60XYUuD08TXDRPqX24,180054
1129
+ yt_dlp-2025.11.18.232918.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1130
+ yt_dlp-2025.11.18.232918.dev0.dist-info/entry_points.txt,sha256=vWfetvzYgZIwDfMW6BjCe0Cy4pmTZEXRNzxAkfYlRJA,103
1131
+ yt_dlp-2025.11.18.232918.dev0.dist-info/licenses/LICENSE,sha256=fhLl30uuEsshWBuhV87SDhmGoFCN0Q0Oikq5pM-U6Fw,1211
1132
+ yt_dlp-2025.11.18.232918.dev0.dist-info/RECORD,,