yt-dlp 2026.1.25.233128.dev0__py3-none-any.whl → 2026.1.29__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.
- yt_dlp/YoutubeDL.py +14 -10
- yt_dlp/extractor/_extractors.py +9 -2
- yt_dlp/extractor/boosty.py +45 -16
- yt_dlp/extractor/dailymotion.py +45 -5
- yt_dlp/extractor/err.py +68 -0
- yt_dlp/extractor/facebook.py +57 -1
- yt_dlp/extractor/francetv.py +21 -5
- yt_dlp/extractor/frontro.py +4 -4
- yt_dlp/extractor/lazy_extractors.py +39 -7
- yt_dlp/extractor/lbry.py +1 -0
- yt_dlp/extractor/neteasemusic.py +32 -7
- yt_dlp/extractor/patreon.py +118 -33
- yt_dlp/extractor/pbs.py +18 -0
- yt_dlp/extractor/rumble.py +1 -1
- yt_dlp/extractor/tarangplus.py +2 -1
- yt_dlp/extractor/vimeo.py +16 -1
- yt_dlp/extractor/volejtv.py +149 -22
- yt_dlp/extractor/wat.py +1 -1
- yt_dlp/extractor/whyp.py +15 -11
- yt_dlp/extractor/youtube/_base.py +32 -26
- yt_dlp/extractor/youtube/_video.py +15 -5
- yt_dlp/extractor/youtube/jsc/_builtin/vendor/_info.py +3 -3
- yt_dlp/extractor/youtube/jsc/_builtin/vendor/yt.solver.core.js +66 -13
- yt_dlp/version.py +5 -5
- {yt_dlp-2026.1.25.233128.dev0.data → yt_dlp-2026.1.29.data}/data/share/doc/yt_dlp/README.txt +12 -11
- {yt_dlp-2026.1.25.233128.dev0.data → yt_dlp-2026.1.29.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +1 -1
- {yt_dlp-2026.1.25.233128.dev0.data → yt_dlp-2026.1.29.data}/data/share/man/man1/yt-dlp.1 +11 -10
- {yt_dlp-2026.1.25.233128.dev0.dist-info → yt_dlp-2026.1.29.dist-info}/METADATA +8 -8
- {yt_dlp-2026.1.25.233128.dev0.dist-info → yt_dlp-2026.1.29.dist-info}/RECORD +34 -34
- {yt_dlp-2026.1.25.233128.dev0.data → yt_dlp-2026.1.29.data}/data/share/bash-completion/completions/yt-dlp +0 -0
- {yt_dlp-2026.1.25.233128.dev0.data → yt_dlp-2026.1.29.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
- {yt_dlp-2026.1.25.233128.dev0.dist-info → yt_dlp-2026.1.29.dist-info}/WHEEL +0 -0
- {yt_dlp-2026.1.25.233128.dev0.dist-info → yt_dlp-2026.1.29.dist-info}/entry_points.txt +0 -0
- {yt_dlp-2026.1.25.233128.dev0.dist-info → yt_dlp-2026.1.29.dist-info}/licenses/LICENSE +0 -0
yt_dlp/YoutubeDL.py
CHANGED
|
@@ -1602,8 +1602,10 @@ class YoutubeDL:
|
|
|
1602
1602
|
if ret is NO_DEFAULT:
|
|
1603
1603
|
while True:
|
|
1604
1604
|
filename = self._format_screen(self.prepare_filename(info_dict), self.Styles.FILENAME)
|
|
1605
|
-
|
|
1606
|
-
f'Download "{filename}"? (Y/n): ', self.Styles.EMPHASIS)
|
|
1605
|
+
self.to_screen(
|
|
1606
|
+
self._format_screen(f'Download "{filename}"? (Y/n): ', self.Styles.EMPHASIS),
|
|
1607
|
+
skip_eol=True)
|
|
1608
|
+
reply = input().lower().strip()
|
|
1607
1609
|
if reply in {'y', ''}:
|
|
1608
1610
|
return None
|
|
1609
1611
|
elif reply == 'n':
|
|
@@ -3030,9 +3032,10 @@ class YoutubeDL:
|
|
|
3030
3032
|
# Bypass interactive format selection if no formats & --ignore-no-formats-error
|
|
3031
3033
|
formats_to_download = None
|
|
3032
3034
|
break
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3035
|
+
self.to_screen(self._format_screen('\nEnter format selector ', self.Styles.EMPHASIS)
|
|
3036
|
+
+ '(Press ENTER for default, or Ctrl+C to quit)'
|
|
3037
|
+
+ self._format_screen(': ', self.Styles.EMPHASIS), skip_eol=True)
|
|
3038
|
+
req_format = input()
|
|
3036
3039
|
try:
|
|
3037
3040
|
format_selector = self.build_format_selector(req_format) if req_format else None
|
|
3038
3041
|
except SyntaxError as err:
|
|
@@ -3478,11 +3481,12 @@ class YoutubeDL:
|
|
|
3478
3481
|
if dl_filename is not None:
|
|
3479
3482
|
self.report_file_already_downloaded(dl_filename)
|
|
3480
3483
|
elif fd:
|
|
3481
|
-
|
|
3482
|
-
f['
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3484
|
+
if fd != FFmpegFD and temp_filename != '-':
|
|
3485
|
+
for f in info_dict['requested_formats']:
|
|
3486
|
+
f['filepath'] = fname = prepend_extension(
|
|
3487
|
+
correct_ext(temp_filename, info_dict['ext']),
|
|
3488
|
+
'f{}'.format(f['format_id']), info_dict['ext'])
|
|
3489
|
+
downloaded.append(fname)
|
|
3486
3490
|
info_dict['url'] = '\n'.join(f['url'] for f in info_dict['requested_formats'])
|
|
3487
3491
|
success, real_download = self.dl(temp_filename, info_dict)
|
|
3488
3492
|
info_dict['__real_download'] = real_download
|
yt_dlp/extractor/_extractors.py
CHANGED
|
@@ -564,7 +564,10 @@ from .eroprofile import (
|
|
|
564
564
|
EroProfileAlbumIE,
|
|
565
565
|
EroProfileIE,
|
|
566
566
|
)
|
|
567
|
-
from .err import
|
|
567
|
+
from .err import (
|
|
568
|
+
ERRArhiivIE,
|
|
569
|
+
ERRJupiterIE,
|
|
570
|
+
)
|
|
568
571
|
from .ertgr import (
|
|
569
572
|
ERTFlixCodenameIE,
|
|
570
573
|
ERTFlixIE,
|
|
@@ -2360,7 +2363,11 @@ from .voicy import (
|
|
|
2360
2363
|
VoicyChannelIE,
|
|
2361
2364
|
VoicyIE,
|
|
2362
2365
|
)
|
|
2363
|
-
from .volejtv import
|
|
2366
|
+
from .volejtv import (
|
|
2367
|
+
VolejTVCategoryPlaylistIE,
|
|
2368
|
+
VolejTVClubPlaylistIE,
|
|
2369
|
+
VolejTVIE,
|
|
2370
|
+
)
|
|
2364
2371
|
from .voxmedia import (
|
|
2365
2372
|
VoxMediaIE,
|
|
2366
2373
|
VoxMediaVolumeIE,
|
yt_dlp/extractor/boosty.py
CHANGED
|
@@ -21,21 +21,44 @@ class BoostyIE(InfoExtractor):
|
|
|
21
21
|
'url': 'https://boosty.to/kuplinov/posts/e55d050c-e3bb-4873-a7db-ac7a49b40c38',
|
|
22
22
|
'info_dict': {
|
|
23
23
|
'id': 'd7473824-352e-48e2-ae53-d4aa39459968',
|
|
24
|
-
'title': '
|
|
24
|
+
'title': 'Бан? А! Бан! (Phasmophobia)',
|
|
25
|
+
'alt_title': 'Бан? А! Бан! (Phasmophobia)',
|
|
25
26
|
'channel': 'Kuplinov',
|
|
26
27
|
'channel_id': '7958701',
|
|
27
28
|
'timestamp': 1655031975,
|
|
28
29
|
'upload_date': '20220612',
|
|
29
30
|
'release_timestamp': 1655049000,
|
|
30
31
|
'release_date': '20220612',
|
|
31
|
-
'modified_timestamp':
|
|
32
|
-
'modified_date': '
|
|
32
|
+
'modified_timestamp': 1743328648,
|
|
33
|
+
'modified_date': '20250330',
|
|
33
34
|
'tags': ['куплинов', 'phasmophobia'],
|
|
34
35
|
'like_count': int,
|
|
35
36
|
'ext': 'mp4',
|
|
36
37
|
'duration': 105,
|
|
37
38
|
'view_count': int,
|
|
38
|
-
'thumbnail': r're:^https://
|
|
39
|
+
'thumbnail': r're:^https://iv\.okcdn\.ru/videoPreview\?',
|
|
40
|
+
},
|
|
41
|
+
}, {
|
|
42
|
+
# single ok_video with truncated title
|
|
43
|
+
'url': 'https://boosty.to/kuplinov/posts/cc09b7f9-121e-40b8-9392-4a075ef2ce53',
|
|
44
|
+
'info_dict': {
|
|
45
|
+
'id': 'fb5ea762-6303-4557-9a17-157947326810',
|
|
46
|
+
'title': 'Какая там активность была? Не слышу! Повтори еще пару раз! (Phas',
|
|
47
|
+
'alt_title': 'Какая там активность была? Не слышу! Повтори еще пару раз! (Phasmophobia)',
|
|
48
|
+
'channel': 'Kuplinov',
|
|
49
|
+
'channel_id': '7958701',
|
|
50
|
+
'timestamp': 1655031930,
|
|
51
|
+
'upload_date': '20220612',
|
|
52
|
+
'release_timestamp': 1655048400,
|
|
53
|
+
'release_date': '20220612',
|
|
54
|
+
'modified_timestamp': 1743328616,
|
|
55
|
+
'modified_date': '20250330',
|
|
56
|
+
'tags': ['куплинов', 'phasmophobia'],
|
|
57
|
+
'like_count': int,
|
|
58
|
+
'ext': 'mp4',
|
|
59
|
+
'duration': 39,
|
|
60
|
+
'view_count': int,
|
|
61
|
+
'thumbnail': r're:^https://iv\.okcdn\.ru/videoPreview\?',
|
|
39
62
|
},
|
|
40
63
|
}, {
|
|
41
64
|
# multiple ok_video
|
|
@@ -109,36 +132,41 @@ class BoostyIE(InfoExtractor):
|
|
|
109
132
|
'thumbnail': r're:^https://i\.mycdn\.me/videoPreview\?',
|
|
110
133
|
},
|
|
111
134
|
}],
|
|
135
|
+
'skip': 'post has been deleted',
|
|
112
136
|
}, {
|
|
113
137
|
# single external video (youtube)
|
|
114
|
-
'url': 'https://boosty.to/
|
|
138
|
+
'url': 'https://boosty.to/futuremusicproduction/posts/32a8cae2-3252-49da-b285-0e014bc6e565',
|
|
115
139
|
'info_dict': {
|
|
116
|
-
'id': '
|
|
117
|
-
'title': '
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
140
|
+
'id': '-37FW_YQ3B4',
|
|
141
|
+
'title': 'Afro | Deep House FREE FLP',
|
|
142
|
+
'media_type': 'video',
|
|
143
|
+
'upload_date': '20250829',
|
|
144
|
+
'timestamp': 1756466005,
|
|
145
|
+
'channel': 'Future Music Production',
|
|
146
|
+
'tags': 'count:0',
|
|
121
147
|
'like_count': int,
|
|
122
|
-
'ext': '
|
|
123
|
-
'duration':
|
|
148
|
+
'ext': 'm4a',
|
|
149
|
+
'duration': 170,
|
|
124
150
|
'view_count': int,
|
|
125
151
|
'thumbnail': r're:^https://i\.ytimg\.com/',
|
|
126
152
|
'age_limit': 0,
|
|
127
153
|
'availability': 'public',
|
|
128
154
|
'categories': list,
|
|
129
155
|
'channel_follower_count': int,
|
|
130
|
-
'channel_id': '
|
|
131
|
-
'channel_is_verified': bool,
|
|
156
|
+
'channel_id': 'UCKVYrFBYmci1e-T8NeHw2qg',
|
|
132
157
|
'channel_url': r're:^https://www\.youtube\.com/',
|
|
133
158
|
'comment_count': int,
|
|
134
159
|
'description': str,
|
|
135
|
-
'heatmap': 'count:100',
|
|
136
160
|
'live_status': str,
|
|
137
161
|
'playable_in_embed': bool,
|
|
138
162
|
'uploader': str,
|
|
139
163
|
'uploader_id': str,
|
|
140
164
|
'uploader_url': r're:^https://www\.youtube\.com/',
|
|
141
165
|
},
|
|
166
|
+
'expected_warnings': [
|
|
167
|
+
'Remote components challenge solver script',
|
|
168
|
+
'n challenge solving failed',
|
|
169
|
+
],
|
|
142
170
|
}]
|
|
143
171
|
|
|
144
172
|
_MP4_TYPES = ('tiny', 'lowest', 'low', 'medium', 'high', 'full_hd', 'quad_hd', 'ultra_hd')
|
|
@@ -207,13 +235,14 @@ class BoostyIE(InfoExtractor):
|
|
|
207
235
|
video_id = item.get('id') or post_id
|
|
208
236
|
entries.append({
|
|
209
237
|
'id': video_id,
|
|
238
|
+
'alt_title': post_title,
|
|
210
239
|
'formats': self._extract_formats(item.get('playerUrls'), video_id),
|
|
211
240
|
**common_metadata,
|
|
212
241
|
**traverse_obj(item, {
|
|
213
242
|
'title': ('title', {str}),
|
|
214
243
|
'duration': ('duration', {int_or_none}),
|
|
215
244
|
'view_count': ('viewsCounter', {int_or_none}),
|
|
216
|
-
'thumbnail': (('
|
|
245
|
+
'thumbnail': (('preview', 'defaultPreview'), {url_or_none}),
|
|
217
246
|
}, get_all=False)})
|
|
218
247
|
|
|
219
248
|
if not entries and not post.get('hasAccess'):
|
yt_dlp/extractor/dailymotion.py
CHANGED
|
@@ -366,8 +366,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
366
366
|
|
|
367
367
|
@staticmethod
|
|
368
368
|
def _generate_blockbuster_headers():
|
|
369
|
-
|
|
370
|
-
# See https://github.com/yt-dlp/yt-dlp/issues/15526
|
|
369
|
+
"""Randomize our HTTP header fingerprint to bust the HTTP Error 403 block"""
|
|
371
370
|
|
|
372
371
|
def random_letters(minimum, maximum):
|
|
373
372
|
# Omit vowels so we don't generate valid header names like 'authorization', etc
|
|
@@ -378,6 +377,43 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
378
377
|
for _ in range(random.randint(2, 8))
|
|
379
378
|
}
|
|
380
379
|
|
|
380
|
+
def _extract_dailymotion_m3u8_formats_and_subtitles(self, media_url, video_id, live=False):
|
|
381
|
+
"""See https://github.com/yt-dlp/yt-dlp/issues/15526"""
|
|
382
|
+
|
|
383
|
+
ERROR_NOTE = 'Unable to download m3u8 information'
|
|
384
|
+
last_error = None
|
|
385
|
+
|
|
386
|
+
for note, kwargs in (
|
|
387
|
+
('Downloading m3u8 information', {}),
|
|
388
|
+
('Retrying m3u8 download with randomized headers', {
|
|
389
|
+
'headers': self._generate_blockbuster_headers(),
|
|
390
|
+
}),
|
|
391
|
+
('Retrying m3u8 download with Chrome impersonation', {
|
|
392
|
+
'impersonate': 'chrome',
|
|
393
|
+
'require_impersonation': True,
|
|
394
|
+
}),
|
|
395
|
+
('Retrying m3u8 download with Firefox impersonation', {
|
|
396
|
+
'impersonate': 'firefox',
|
|
397
|
+
'require_impersonation': True,
|
|
398
|
+
}),
|
|
399
|
+
):
|
|
400
|
+
try:
|
|
401
|
+
m3u8_doc = self._download_webpage(media_url, video_id, note, ERROR_NOTE, **kwargs)
|
|
402
|
+
break
|
|
403
|
+
except ExtractorError as e:
|
|
404
|
+
last_error = e.orig_msg
|
|
405
|
+
self.write_debug(f'{video_id}: {last_error}')
|
|
406
|
+
else:
|
|
407
|
+
if 'impersonation' not in last_error:
|
|
408
|
+
self.report_warning(last_error, video_id=video_id)
|
|
409
|
+
last_error = None
|
|
410
|
+
return [], {}, last_error
|
|
411
|
+
|
|
412
|
+
formats, subtitles = self._parse_m3u8_formats_and_subtitles(
|
|
413
|
+
m3u8_doc, media_url, 'mp4', m3u8_id='hls', live=live, fatal=False)
|
|
414
|
+
|
|
415
|
+
return formats, subtitles, last_error
|
|
416
|
+
|
|
381
417
|
def _real_extract(self, url):
|
|
382
418
|
url, smuggled_data = unsmuggle_url(url)
|
|
383
419
|
video_id, is_playlist, playlist_id = self._match_valid_url(url).group('id', 'is_playlist', 'playlist_id')
|
|
@@ -431,6 +467,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
431
467
|
is_live = media.get('isOnAir')
|
|
432
468
|
formats = []
|
|
433
469
|
subtitles = {}
|
|
470
|
+
expected_error = None
|
|
434
471
|
|
|
435
472
|
for quality, media_list in metadata['qualities'].items():
|
|
436
473
|
for m in media_list:
|
|
@@ -439,9 +476,8 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
439
476
|
if not media_url or media_type == 'application/vnd.lumberjack.manifest':
|
|
440
477
|
continue
|
|
441
478
|
if media_type == 'application/x-mpegURL':
|
|
442
|
-
fmt, subs = self.
|
|
443
|
-
media_url, video_id,
|
|
444
|
-
fatal=False, headers=self._generate_blockbuster_headers())
|
|
479
|
+
fmt, subs, expected_error = self._extract_dailymotion_m3u8_formats_and_subtitles(
|
|
480
|
+
media_url, video_id, live=is_live)
|
|
445
481
|
formats.extend(fmt)
|
|
446
482
|
self._merge_subtitles(subs, target=subtitles)
|
|
447
483
|
else:
|
|
@@ -458,6 +494,10 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|
|
458
494
|
'width': width,
|
|
459
495
|
})
|
|
460
496
|
formats.append(f)
|
|
497
|
+
|
|
498
|
+
if not formats and expected_error:
|
|
499
|
+
self.raise_no_formats(expected_error, expected=True)
|
|
500
|
+
|
|
461
501
|
for f in formats:
|
|
462
502
|
f['url'] = f['url'].split('#')[0]
|
|
463
503
|
if not f.get('fps') and f['format_id'].endswith('@60'):
|
yt_dlp/extractor/err.py
CHANGED
|
@@ -2,6 +2,7 @@ from .common import InfoExtractor
|
|
|
2
2
|
from ..utils import (
|
|
3
3
|
clean_html,
|
|
4
4
|
int_or_none,
|
|
5
|
+
parse_iso8601,
|
|
5
6
|
str_or_none,
|
|
6
7
|
url_or_none,
|
|
7
8
|
)
|
|
@@ -222,3 +223,70 @@ class ERRJupiterIE(InfoExtractor):
|
|
|
222
223
|
'episode_id': ('id', {str_or_none}),
|
|
223
224
|
}) if data.get('type') == 'episode' else {}),
|
|
224
225
|
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ERRArhiivIE(InfoExtractor):
|
|
229
|
+
_VALID_URL = r'https://arhiiv\.err\.ee/video/(?:vaata/)?(?P<id>[^/?#]+)'
|
|
230
|
+
_TESTS = [{
|
|
231
|
+
'url': 'https://arhiiv.err.ee/video/kontsertpalad',
|
|
232
|
+
'info_dict': {
|
|
233
|
+
'id': 'kontsertpalad',
|
|
234
|
+
'ext': 'mp4',
|
|
235
|
+
'title': 'Kontsertpalad: 255 | L. Beethoveni sonaat c-moll, "Pateetiline"',
|
|
236
|
+
'description': 'md5:a70f4ff23c3618f3be63f704bccef063',
|
|
237
|
+
'series': 'Kontsertpalad',
|
|
238
|
+
'episode_id': 255,
|
|
239
|
+
'timestamp': 1666152162,
|
|
240
|
+
'upload_date': '20221019',
|
|
241
|
+
'release_year': 1970,
|
|
242
|
+
'modified_timestamp': 1718620982,
|
|
243
|
+
'modified_date': '20240617',
|
|
244
|
+
},
|
|
245
|
+
'params': {'skip_download': 'm3u8'},
|
|
246
|
+
}, {
|
|
247
|
+
'url': 'https://arhiiv.err.ee/video/vaata/koalitsioonileppe-allkirjastamine',
|
|
248
|
+
'info_dict': {
|
|
249
|
+
'id': 'koalitsioonileppe-allkirjastamine',
|
|
250
|
+
'ext': 'mp4',
|
|
251
|
+
'title': 'Koalitsioonileppe allkirjastamine',
|
|
252
|
+
'timestamp': 1710728222,
|
|
253
|
+
'upload_date': '20240318',
|
|
254
|
+
'release_timestamp': 1611532800,
|
|
255
|
+
'release_date': '20210125',
|
|
256
|
+
},
|
|
257
|
+
'params': {'skip_download': 'm3u8'},
|
|
258
|
+
}]
|
|
259
|
+
|
|
260
|
+
def _real_extract(self, url):
|
|
261
|
+
video_id = self._match_id(url)
|
|
262
|
+
data = self._download_json(
|
|
263
|
+
f'https://arhiiv.err.ee/api/v1/content/video/{video_id}', video_id)
|
|
264
|
+
|
|
265
|
+
formats, subtitles = [], {}
|
|
266
|
+
if hls_url := traverse_obj(data, ('media', 'src', 'hls', {url_or_none})):
|
|
267
|
+
fmts, subs = self._extract_m3u8_formats_and_subtitles(
|
|
268
|
+
hls_url, video_id, 'mp4', m3u8_id='hls', fatal=False)
|
|
269
|
+
formats.extend(fmts)
|
|
270
|
+
self._merge_subtitles(subs, target=subtitles)
|
|
271
|
+
if dash_url := traverse_obj(data, ('media', 'src', 'dash', {url_or_none})):
|
|
272
|
+
fmts, subs = self._extract_mpd_formats_and_subtitles(
|
|
273
|
+
dash_url, video_id, mpd_id='dash', fatal=False)
|
|
274
|
+
formats.extend(fmts)
|
|
275
|
+
self._merge_subtitles(subs, target=subtitles)
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
'id': video_id,
|
|
279
|
+
'formats': formats,
|
|
280
|
+
'subtitles': subtitles,
|
|
281
|
+
**traverse_obj(data, ('info', {
|
|
282
|
+
'title': ('title', {str}),
|
|
283
|
+
'series': ('seriesTitle', {str}, filter),
|
|
284
|
+
'series_id': ('seriesId', {str}, filter),
|
|
285
|
+
'episode_id': ('episode', {int_or_none}),
|
|
286
|
+
'description': ('synopsis', {str}, filter),
|
|
287
|
+
'timestamp': ('uploadDate', {parse_iso8601}),
|
|
288
|
+
'modified_timestamp': ('dateModified', {parse_iso8601}),
|
|
289
|
+
'release_timestamp': ('date', {parse_iso8601}),
|
|
290
|
+
'release_year': ('year', {int_or_none}),
|
|
291
|
+
})),
|
|
292
|
+
}
|
yt_dlp/extractor/facebook.py
CHANGED
|
@@ -4,6 +4,7 @@ import urllib.parse
|
|
|
4
4
|
|
|
5
5
|
from .common import InfoExtractor
|
|
6
6
|
from ..compat import compat_etree_fromstring
|
|
7
|
+
from ..networking.exceptions import HTTPError
|
|
7
8
|
from ..utils import (
|
|
8
9
|
ExtractorError,
|
|
9
10
|
clean_html,
|
|
@@ -1017,6 +1018,7 @@ class FacebookAdsIE(InfoExtractor):
|
|
|
1017
1018
|
'upload_date': '20240812',
|
|
1018
1019
|
'like_count': int,
|
|
1019
1020
|
},
|
|
1021
|
+
'skip': 'Invalid URL',
|
|
1020
1022
|
}, {
|
|
1021
1023
|
'url': 'https://www.facebook.com/ads/library/?id=893637265423481',
|
|
1022
1024
|
'info_dict': {
|
|
@@ -1031,6 +1033,33 @@ class FacebookAdsIE(InfoExtractor):
|
|
|
1031
1033
|
},
|
|
1032
1034
|
'playlist_count': 3,
|
|
1033
1035
|
'skip': 'Invalid URL',
|
|
1036
|
+
}, {
|
|
1037
|
+
'url': 'https://www.facebook.com/ads/library/?id=312304267031140',
|
|
1038
|
+
'info_dict': {
|
|
1039
|
+
'id': '312304267031140',
|
|
1040
|
+
'title': 'Casper Wave Hybrid Mattress',
|
|
1041
|
+
'uploader': 'Casper',
|
|
1042
|
+
'uploader_id': '224110981099062',
|
|
1043
|
+
'uploader_url': 'https://www.facebook.com/Casper/',
|
|
1044
|
+
'timestamp': 1766299837,
|
|
1045
|
+
'upload_date': '20251221',
|
|
1046
|
+
'like_count': int,
|
|
1047
|
+
},
|
|
1048
|
+
'playlist_count': 2,
|
|
1049
|
+
}, {
|
|
1050
|
+
'url': 'https://www.facebook.com/ads/library/?id=874812092000430',
|
|
1051
|
+
'info_dict': {
|
|
1052
|
+
'id': '874812092000430',
|
|
1053
|
+
'title': 'TikTok',
|
|
1054
|
+
'uploader': 'Case \u00e0 Chocs',
|
|
1055
|
+
'uploader_id': '112960472096793',
|
|
1056
|
+
'uploader_url': 'https://www.facebook.com/Caseachocs/',
|
|
1057
|
+
'timestamp': 1768498293,
|
|
1058
|
+
'upload_date': '20260115',
|
|
1059
|
+
'like_count': int,
|
|
1060
|
+
'description': 'md5:f02a255fcf7dce6ed40e9494cf4bc49a',
|
|
1061
|
+
},
|
|
1062
|
+
'playlist_count': 3,
|
|
1034
1063
|
}, {
|
|
1035
1064
|
'url': 'https://es-la.facebook.com/ads/library/?id=901230958115569',
|
|
1036
1065
|
'only_matching': True,
|
|
@@ -1060,9 +1089,36 @@ class FacebookAdsIE(InfoExtractor):
|
|
|
1060
1089
|
})
|
|
1061
1090
|
return formats
|
|
1062
1091
|
|
|
1092
|
+
def _download_fb_webpage_and_verify(self, url, video_id):
|
|
1093
|
+
# See https://github.com/yt-dlp/yt-dlp/issues/15577
|
|
1094
|
+
|
|
1095
|
+
try:
|
|
1096
|
+
return self._download_webpage(url, video_id)
|
|
1097
|
+
except ExtractorError as e:
|
|
1098
|
+
if (
|
|
1099
|
+
not isinstance(e.cause, HTTPError)
|
|
1100
|
+
or e.cause.status != 403
|
|
1101
|
+
or e.cause.reason != 'Client challenge'
|
|
1102
|
+
):
|
|
1103
|
+
raise
|
|
1104
|
+
error_page = self._webpage_read_content(e.cause.response, url, video_id)
|
|
1105
|
+
|
|
1106
|
+
self.write_debug('Received a client challenge response')
|
|
1107
|
+
|
|
1108
|
+
challenge_path = self._search_regex(
|
|
1109
|
+
r'fetch\s*\(\s*["\'](/__rd_verify[^"\']+)["\']',
|
|
1110
|
+
error_page, 'challenge path')
|
|
1111
|
+
|
|
1112
|
+
# Successful response will set the necessary cookie
|
|
1113
|
+
self._request_webpage(
|
|
1114
|
+
urljoin(url, challenge_path), video_id, 'Requesting verification cookie',
|
|
1115
|
+
'Unable to get verification cookie', data=b'')
|
|
1116
|
+
|
|
1117
|
+
return self._download_webpage(url, video_id)
|
|
1118
|
+
|
|
1063
1119
|
def _real_extract(self, url):
|
|
1064
1120
|
video_id = self._match_id(url)
|
|
1065
|
-
webpage = self.
|
|
1121
|
+
webpage = self._download_fb_webpage_and_verify(url, video_id)
|
|
1066
1122
|
|
|
1067
1123
|
post_data = traverse_obj(
|
|
1068
1124
|
re.findall(r'data-sjs>({.*?ScheduledServerJS.*?})</script>', webpage), (..., {json.loads}))
|
yt_dlp/extractor/francetv.py
CHANGED
|
@@ -371,15 +371,16 @@ class FranceTVSiteIE(FranceTVBaseInfoExtractor):
|
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|
374
|
-
IE_NAME = '
|
|
375
|
-
|
|
374
|
+
IE_NAME = 'franceinfo'
|
|
375
|
+
IE_DESC = 'franceinfo.fr (formerly francetvinfo.fr)'
|
|
376
|
+
_VALID_URL = r'https?://(?:www|mobile|france3-regions)\.france(?:tv)?info.fr/(?:[^/?#]+/)*(?P<id>[^/?#&.]+)'
|
|
376
377
|
|
|
377
378
|
_TESTS = [{
|
|
378
379
|
'url': 'https://www.francetvinfo.fr/replay-jt/france-3/soir-3/jt-grand-soir-3-jeudi-22-aout-2019_3561461.html',
|
|
379
380
|
'info_dict': {
|
|
380
381
|
'id': 'd12458ee-5062-48fe-bfdd-a30d6a01b793',
|
|
381
382
|
'ext': 'mp4',
|
|
382
|
-
'title': 'Soir 3',
|
|
383
|
+
'title': 'Soir 3 - Émission du jeudi 22 août 2019',
|
|
383
384
|
'upload_date': '20190822',
|
|
384
385
|
'timestamp': 1566510730,
|
|
385
386
|
'thumbnail': r're:^https?://.*\.jpe?g$',
|
|
@@ -398,7 +399,7 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|
|
398
399
|
'info_dict': {
|
|
399
400
|
'id': '7d204c9e-a2d3-11eb-9e4c-000d3a23d482',
|
|
400
401
|
'ext': 'mp4',
|
|
401
|
-
'title': 'Covid-19 : une situation catastrophique à New Dehli
|
|
402
|
+
'title': 'Journal 20h00 - Covid-19 : une situation catastrophique à New Dehli',
|
|
402
403
|
'thumbnail': r're:^https?://.*\.jpe?g$',
|
|
403
404
|
'duration': 76,
|
|
404
405
|
'timestamp': 1619028518,
|
|
@@ -438,6 +439,18 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|
|
438
439
|
'thumbnail': r're:https://[^/?#]+/v/[^/?#]+/x1080',
|
|
439
440
|
},
|
|
440
441
|
'add_ie': ['Dailymotion'],
|
|
442
|
+
'skip': 'Broken Dailymotion link',
|
|
443
|
+
}, {
|
|
444
|
+
'url': 'https://www.franceinfo.fr/monde/usa/presidentielle/donald-trump/etats-unis-un-risque-d-embrasement-apres-la-mort-d-un-manifestant_7764542.html',
|
|
445
|
+
'info_dict': {
|
|
446
|
+
'id': 'f920fcc2-fa20-11f0-ac98-57a09c50f7ce',
|
|
447
|
+
'ext': 'mp4',
|
|
448
|
+
'title': 'Affaires sensibles - Manifestant tué Le risque d\'embrasement',
|
|
449
|
+
'duration': 118,
|
|
450
|
+
'thumbnail': r're:https?://.+/.+\.jpg',
|
|
451
|
+
'timestamp': 1769367756,
|
|
452
|
+
'upload_date': '20260125',
|
|
453
|
+
},
|
|
441
454
|
}, {
|
|
442
455
|
'url': 'http://france3-regions.francetvinfo.fr/limousin/emissions/jt-1213-limousin',
|
|
443
456
|
'only_matching': True,
|
|
@@ -445,6 +458,9 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|
|
445
458
|
# "<figure id=" pattern (#28792)
|
|
446
459
|
'url': 'https://www.francetvinfo.fr/culture/patrimoine/incendie-de-notre-dame-de-paris/notre-dame-de-paris-de-l-incendie-de-la-cathedrale-a-sa-reconstruction_4372291.html',
|
|
447
460
|
'only_matching': True,
|
|
461
|
+
}, {
|
|
462
|
+
'url': 'https://www.franceinfo.fr/replay-jt/france-2/20-heures/robert-de-niro-portrait-d-un-monument-du-cinema_7245456.html',
|
|
463
|
+
'only_matching': True,
|
|
448
464
|
}]
|
|
449
465
|
|
|
450
466
|
def _real_extract(self, url):
|
|
@@ -460,7 +476,7 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor):
|
|
|
460
476
|
|
|
461
477
|
video_id = (
|
|
462
478
|
traverse_obj(webpage, (
|
|
463
|
-
{find_element(tag='button', attr='data-cy', value='francetv-player-wrapper', html=True)},
|
|
479
|
+
{find_element(tag='(button|div)', attr='data-cy', value='francetv-player-wrapper', html=True, regex=True)},
|
|
464
480
|
{extract_attributes}, 'id'))
|
|
465
481
|
or self._search_regex(
|
|
466
482
|
(r'player\.load[^;]+src:\s*["\']([^"\']+)',
|
yt_dlp/extractor/frontro.py
CHANGED
|
@@ -104,9 +104,9 @@ class FrontroGroupBaseIE(FrontoBaseIE):
|
|
|
104
104
|
class TheChosenIE(FrontroVideoBaseIE):
|
|
105
105
|
_CHANNEL_ID = '12884901895'
|
|
106
106
|
|
|
107
|
-
_VALID_URL = r'https?://(?:www\.)?watch\.thechosen\.tv/
|
|
107
|
+
_VALID_URL = r'https?://(?:www\.)?watch\.thechosen\.tv/watch/(?P<id>[0-9]+)'
|
|
108
108
|
_TESTS = [{
|
|
109
|
-
'url': 'https://watch.thechosen.tv/
|
|
109
|
+
'url': 'https://watch.thechosen.tv/watch/184683594325',
|
|
110
110
|
'md5': '3f878b689588c71b38ec9943c54ff5b0',
|
|
111
111
|
'info_dict': {
|
|
112
112
|
'id': '184683594325',
|
|
@@ -124,7 +124,7 @@ class TheChosenIE(FrontroVideoBaseIE):
|
|
|
124
124
|
'modified_date': str,
|
|
125
125
|
},
|
|
126
126
|
}, {
|
|
127
|
-
'url': 'https://watch.thechosen.tv/
|
|
127
|
+
'url': 'https://watch.thechosen.tv/watch/184683596189',
|
|
128
128
|
'md5': 'd581562f9d29ce82f5b7770415334151',
|
|
129
129
|
'info_dict': {
|
|
130
130
|
'id': '184683596189',
|
|
@@ -147,7 +147,7 @@ class TheChosenIE(FrontroVideoBaseIE):
|
|
|
147
147
|
class TheChosenGroupIE(FrontroGroupBaseIE):
|
|
148
148
|
_CHANNEL_ID = '12884901895'
|
|
149
149
|
_VIDEO_EXTRACTOR = TheChosenIE
|
|
150
|
-
_VIDEO_URL_TMPL = 'https://watch.thechosen.tv/
|
|
150
|
+
_VIDEO_URL_TMPL = 'https://watch.thechosen.tv/watch/%s'
|
|
151
151
|
|
|
152
152
|
_VALID_URL = r'https?://(?:www\.)?watch\.thechosen\.tv/group/(?P<id>[0-9]+)'
|
|
153
153
|
_TESTS = [{
|