yt-dlp 2026.1.19.233146.dev0__py3-none-any.whl → 2026.1.27.233257.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 (27) hide show
  1. yt_dlp/extractor/_extractors.py +9 -2
  2. yt_dlp/extractor/boosty.py +45 -16
  3. yt_dlp/extractor/dailymotion.py +58 -2
  4. yt_dlp/extractor/err.py +68 -0
  5. yt_dlp/extractor/facebook.py +57 -1
  6. yt_dlp/extractor/francetv.py +21 -5
  7. yt_dlp/extractor/frontro.py +4 -4
  8. yt_dlp/extractor/lazy_extractors.py +39 -7
  9. yt_dlp/extractor/lbry.py +1 -0
  10. yt_dlp/extractor/neteasemusic.py +32 -7
  11. yt_dlp/extractor/patreon.py +118 -33
  12. yt_dlp/extractor/pbs.py +18 -0
  13. yt_dlp/extractor/rumble.py +1 -1
  14. yt_dlp/extractor/tiktok.py +83 -24
  15. yt_dlp/extractor/volejtv.py +149 -22
  16. yt_dlp/extractor/wat.py +1 -1
  17. yt_dlp/version.py +3 -3
  18. {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.27.233257.dev0.dist-info}/METADATA +1 -1
  19. {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.27.233257.dev0.dist-info}/RECORD +27 -27
  20. {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.27.233257.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
  21. {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.27.233257.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
  22. {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.27.233257.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
  23. {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.27.233257.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
  24. {yt_dlp-2026.1.19.233146.dev0.data → yt_dlp-2026.1.27.233257.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
  25. {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.27.233257.dev0.dist-info}/WHEEL +0 -0
  26. {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.27.233257.dev0.dist-info}/entry_points.txt +0 -0
  27. {yt_dlp-2026.1.19.233146.dev0.dist-info → yt_dlp-2026.1.27.233257.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -564,7 +564,10 @@ from .eroprofile import (
564
564
  EroProfileAlbumIE,
565
565
  EroProfileIE,
566
566
  )
567
- from .err import ERRJupiterIE
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 VolejTVIE
2366
+ from .volejtv import (
2367
+ VolejTVCategoryPlaylistIE,
2368
+ VolejTVClubPlaylistIE,
2369
+ VolejTVIE,
2370
+ )
2364
2371
  from .voxmedia import (
2365
2372
  VoxMediaIE,
2366
2373
  VoxMediaVolumeIE,
@@ -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': 'phasma_3',
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': 1668680993,
32
- 'modified_date': '20221117',
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://i\.mycdn\.me/videoPreview\?',
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/denischuzhoy/posts/6094a487-bcec-4cf8-a453-43313b463c38',
138
+ 'url': 'https://boosty.to/futuremusicproduction/posts/32a8cae2-3252-49da-b285-0e014bc6e565',
115
139
  'info_dict': {
116
- 'id': 'EXelTnve5lY',
117
- 'title': 'Послание Президента Федеральному Собранию | Класс народа',
118
- 'upload_date': '20210425',
119
- 'channel': 'Денис Чужой',
120
- 'tags': 'count:10',
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': 'mp4',
123
- 'duration': 816,
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': 'UCCzVNbWZfYpBfyofCCUD_0w',
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': (('previewUrl', 'defaultPreview'), {url_or_none}),
245
+ 'thumbnail': (('preview', 'defaultPreview'), {url_or_none}),
217
246
  }, get_all=False)})
218
247
 
219
248
  if not entries and not post.get('hasAccess'):
@@ -1,5 +1,6 @@
1
1
  import functools
2
2
  import json
3
+ import random
3
4
  import re
4
5
  import urllib.parse
5
6
 
@@ -363,6 +364,56 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
363
364
  continue
364
365
  yield update_url(player_url, query=query_string)
365
366
 
367
+ @staticmethod
368
+ def _generate_blockbuster_headers():
369
+ """Randomize our HTTP header fingerprint to bust the HTTP Error 403 block"""
370
+
371
+ def random_letters(minimum, maximum):
372
+ # Omit vowels so we don't generate valid header names like 'authorization', etc
373
+ return ''.join(random.choices('bcdfghjklmnpqrstvwxz', k=random.randint(minimum, maximum)))
374
+
375
+ return {
376
+ random_letters(8, 24): random_letters(16, 32)
377
+ for _ in range(random.randint(2, 8))
378
+ }
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
+
366
417
  def _real_extract(self, url):
367
418
  url, smuggled_data = unsmuggle_url(url)
368
419
  video_id, is_playlist, playlist_id = self._match_valid_url(url).group('id', 'is_playlist', 'playlist_id')
@@ -416,6 +467,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
416
467
  is_live = media.get('isOnAir')
417
468
  formats = []
418
469
  subtitles = {}
470
+ expected_error = None
419
471
 
420
472
  for quality, media_list in metadata['qualities'].items():
421
473
  for m in media_list:
@@ -424,8 +476,8 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
424
476
  if not media_url or media_type == 'application/vnd.lumberjack.manifest':
425
477
  continue
426
478
  if media_type == 'application/x-mpegURL':
427
- fmt, subs = self._extract_m3u8_formats_and_subtitles(
428
- media_url, video_id, 'mp4', live=is_live, m3u8_id='hls', fatal=False)
479
+ fmt, subs, expected_error = self._extract_dailymotion_m3u8_formats_and_subtitles(
480
+ media_url, video_id, live=is_live)
429
481
  formats.extend(fmt)
430
482
  self._merge_subtitles(subs, target=subtitles)
431
483
  else:
@@ -442,6 +494,10 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
442
494
  'width': width,
443
495
  })
444
496
  formats.append(f)
497
+
498
+ if not formats and expected_error:
499
+ self.raise_no_formats(expected_error, expected=True)
500
+
445
501
  for f in formats:
446
502
  f['url'] = f['url'].split('#')[0]
447
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
+ }
@@ -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._download_webpage(url, video_id)
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}))
@@ -371,15 +371,16 @@ class FranceTVSiteIE(FranceTVBaseInfoExtractor):
371
371
 
372
372
 
373
373
  class FranceTVInfoIE(FranceTVBaseInfoExtractor):
374
- IE_NAME = 'francetvinfo.fr'
375
- _VALID_URL = r'https?://(?:www|mobile|france3-regions)\.francetvinfo\.fr/(?:[^/]+/)*(?P<id>[^/?#&.]+)'
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 - Édition du mercredi 21 avril 2021',
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*["\']([^"\']+)',
@@ -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/video/(?P<id>[0-9]+)'
107
+ _VALID_URL = r'https?://(?:www\.)?watch\.thechosen\.tv/watch/(?P<id>[0-9]+)'
108
108
  _TESTS = [{
109
- 'url': 'https://watch.thechosen.tv/video/184683594325',
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/video/184683596189',
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/video/%s'
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 = [{