yt-dlp 2025.12.18.235942.dev0__py3-none-any.whl → 2025.12.20.232942.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 (22) hide show
  1. yt_dlp/YoutubeDL.py +4 -0
  2. yt_dlp/extractor/_extractors.py +2 -0
  3. yt_dlp/extractor/bandcamp.py +40 -41
  4. yt_dlp/extractor/dropbox.py +4 -1
  5. yt_dlp/extractor/filmarchiv.py +52 -0
  6. yt_dlp/extractor/lazy_extractors.py +23 -10
  7. yt_dlp/extractor/nebula.py +53 -0
  8. yt_dlp/extractor/neteasemusic.py +4 -1
  9. yt_dlp/extractor/tubitv.py +4 -1
  10. yt_dlp/extractor/yahoo.py +52 -115
  11. yt_dlp/utils/_utils.py +4 -2
  12. yt_dlp/version.py +3 -3
  13. {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/METADATA +1 -1
  14. {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/RECORD +22 -21
  15. {yt_dlp-2025.12.18.235942.dev0.data → yt_dlp-2025.12.20.232942.dev0.data}/data/share/bash-completion/completions/yt-dlp +0 -0
  16. {yt_dlp-2025.12.18.235942.dev0.data → yt_dlp-2025.12.20.232942.dev0.data}/data/share/doc/yt_dlp/README.txt +0 -0
  17. {yt_dlp-2025.12.18.235942.dev0.data → yt_dlp-2025.12.20.232942.dev0.data}/data/share/fish/vendor_completions.d/yt-dlp.fish +0 -0
  18. {yt_dlp-2025.12.18.235942.dev0.data → yt_dlp-2025.12.20.232942.dev0.data}/data/share/man/man1/yt-dlp.1 +0 -0
  19. {yt_dlp-2025.12.18.235942.dev0.data → yt_dlp-2025.12.20.232942.dev0.data}/data/share/zsh/site-functions/_yt-dlp +0 -0
  20. {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/WHEEL +0 -0
  21. {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/entry_points.txt +0 -0
  22. {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/licenses/LICENSE +0 -0
yt_dlp/YoutubeDL.py CHANGED
@@ -3026,6 +3026,10 @@ class YoutubeDL:
3026
3026
  format_selector = self.format_selector
3027
3027
  while True:
3028
3028
  if interactive_format_selection:
3029
+ if not formats:
3030
+ # Bypass interactive format selection if no formats & --ignore-no-formats-error
3031
+ formats_to_download = None
3032
+ break
3029
3033
  req_format = input(self._format_screen('\nEnter format selector ', self.Styles.EMPHASIS)
3030
3034
  + '(Press ENTER for default, or Ctrl+C to quit)'
3031
3035
  + self._format_screen(': ', self.Styles.EMPHASIS))
@@ -638,6 +638,7 @@ from .fc2 import (
638
638
  )
639
639
  from .fczenit import FczenitIE
640
640
  from .fifa import FifaIE
641
+ from .filmarchiv import FilmArchivIE
641
642
  from .filmon import (
642
643
  FilmOnChannelIE,
643
644
  FilmOnIE,
@@ -1278,6 +1279,7 @@ from .nebula import (
1278
1279
  NebulaChannelIE,
1279
1280
  NebulaClassIE,
1280
1281
  NebulaIE,
1282
+ NebulaSeasonIE,
1281
1283
  NebulaSubscriptionsIE,
1282
1284
  )
1283
1285
  from .nekohacker import NekoHackerIE
@@ -5,16 +5,18 @@ import time
5
5
 
6
6
  from .common import InfoExtractor
7
7
  from ..utils import (
8
- KNOWN_EXTENSIONS,
9
8
  ExtractorError,
10
9
  clean_html,
11
10
  extract_attributes,
12
11
  float_or_none,
12
+ format_field,
13
13
  int_or_none,
14
+ join_nonempty,
14
15
  parse_filesize,
16
+ parse_qs,
15
17
  str_or_none,
18
+ strftime_or_none,
16
19
  try_get,
17
- unified_strdate,
18
20
  unified_timestamp,
19
21
  update_url_query,
20
22
  url_or_none,
@@ -411,70 +413,67 @@ class BandcampAlbumIE(BandcampIE): # XXX: Do not subclass from concrete IE
411
413
 
412
414
  class BandcampWeeklyIE(BandcampIE): # XXX: Do not subclass from concrete IE
413
415
  IE_NAME = 'Bandcamp:weekly'
414
- _VALID_URL = r'https?://(?:www\.)?bandcamp\.com/?\?(?:.*?&)?show=(?P<id>\d+)'
416
+ _VALID_URL = r'https?://(?:www\.)?bandcamp\.com/radio/?\?(?:[^#]+&)?show=(?P<id>\d+)'
415
417
  _TESTS = [{
416
- 'url': 'https://bandcamp.com/?show=224',
418
+ 'url': 'https://bandcamp.com/radio?show=224',
417
419
  'md5': '61acc9a002bed93986b91168aa3ab433',
418
420
  'info_dict': {
419
421
  'id': '224',
420
422
  'ext': 'mp3',
421
- 'title': 'BC Weekly April 4th 2017 - Magic Moments',
423
+ 'title': 'Bandcamp Weekly, 2017-04-04',
422
424
  'description': 'md5:5d48150916e8e02d030623a48512c874',
423
- 'duration': 5829.77,
424
- 'release_date': '20170404',
425
+ 'thumbnail': 'https://f4.bcbits.com/img/9982549_0.jpg',
425
426
  'series': 'Bandcamp Weekly',
426
- 'episode': 'Magic Moments',
427
427
  'episode_id': '224',
428
+ 'release_timestamp': 1491264000,
429
+ 'release_date': '20170404',
430
+ 'duration': 5829.77,
428
431
  },
429
432
  'params': {
430
433
  'format': 'mp3-128',
431
434
  },
432
435
  }, {
433
- 'url': 'https://bandcamp.com/?blah/blah@&show=228',
436
+ 'url': 'https://bandcamp.com/radio/?foo=bar&show=224',
434
437
  'only_matching': True,
435
438
  }]
436
439
 
437
440
  def _real_extract(self, url):
438
441
  show_id = self._match_id(url)
439
- webpage = self._download_webpage(url, show_id)
442
+ audio_data = self._download_json(
443
+ 'https://bandcamp.com/api/bcradio_api/1/get_show',
444
+ show_id, 'Downloading radio show JSON',
445
+ data=json.dumps({'id': show_id}).encode(),
446
+ headers={'Content-Type': 'application/json'})['radioShowAudio']
440
447
 
441
- blob = self._extract_data_attr(webpage, show_id, 'blob')
448
+ stream_url = audio_data['streamUrl']
449
+ format_id = traverse_obj(stream_url, ({parse_qs}, 'enc', -1))
450
+ encoding, _, bitrate_str = (format_id or '').partition('-')
442
451
 
443
- show = blob['bcw_data'][show_id]
452
+ webpage = self._download_webpage(url, show_id, fatal=False)
453
+ metadata = traverse_obj(
454
+ self._extract_data_attr(webpage, show_id, 'blob', fatal=False),
455
+ ('appData', 'shows', lambda _, v: str(v['showId']) == show_id, any)) or {}
444
456
 
445
- formats = []
446
- for format_id, format_url in show['audio_stream'].items():
447
- if not url_or_none(format_url):
448
- continue
449
- for known_ext in KNOWN_EXTENSIONS:
450
- if known_ext in format_id:
451
- ext = known_ext
452
- break
453
- else:
454
- ext = None
455
- formats.append({
456
- 'format_id': format_id,
457
- 'url': format_url,
458
- 'ext': ext,
459
- 'vcodec': 'none',
460
- })
461
-
462
- title = show.get('audio_title') or 'Bandcamp Weekly'
463
- subtitle = show.get('subtitle')
464
- if subtitle:
465
- title += f' - {subtitle}'
457
+ series_title = audio_data.get('title') or metadata.get('title')
458
+ release_timestamp = unified_timestamp(audio_data.get('date')) or unified_timestamp(metadata.get('date'))
466
459
 
467
460
  return {
468
461
  'id': show_id,
469
- 'title': title,
470
- 'description': show.get('desc') or show.get('short_desc'),
471
- 'duration': float_or_none(show.get('audio_duration')),
472
- 'is_live': False,
473
- 'release_date': unified_strdate(show.get('published_date')),
474
- 'series': 'Bandcamp Weekly',
475
- 'episode': show.get('subtitle'),
476
462
  'episode_id': show_id,
477
- 'formats': formats,
463
+ 'title': join_nonempty(series_title, strftime_or_none(release_timestamp, '%Y-%m-%d'), delim=', '),
464
+ 'series': series_title,
465
+ 'thumbnail': format_field(metadata, 'imageId', 'https://f4.bcbits.com/img/%s_0.jpg', default=None),
466
+ 'description': metadata.get('desc') or metadata.get('short_desc'),
467
+ 'duration': float_or_none(audio_data.get('duration')),
468
+ 'release_timestamp': release_timestamp,
469
+ 'formats': [{
470
+ 'url': stream_url,
471
+ 'format_id': format_id,
472
+ 'ext': encoding or 'mp3',
473
+ 'acodec': encoding or None,
474
+ 'vcodec': 'none',
475
+ 'abr': int_or_none(bitrate_str),
476
+ }],
478
477
  }
479
478
 
480
479
 
@@ -14,7 +14,7 @@ from ..utils import (
14
14
 
15
15
 
16
16
  class DropboxIE(InfoExtractor):
17
- _VALID_URL = r'https?://(?:www\.)?dropbox\.com/(?:(?:e/)?scl/fi|sh?)/(?P<id>\w+)'
17
+ _VALID_URL = r'https?://(?:www\.)?dropbox\.com/(?:(?:e/)?scl/f[io]|sh?)/(?P<id>\w+)'
18
18
  _TESTS = [
19
19
  {
20
20
  'url': 'https://www.dropbox.com/s/nelirfsxnmcfbfh/youtube-dl%20test%20video%20%27%C3%A4%22BaW_jenozKc.mp4?dl=0',
@@ -35,6 +35,9 @@ class DropboxIE(InfoExtractor):
35
35
  }, {
36
36
  'url': 'https://www.dropbox.com/e/scl/fi/r2kd2skcy5ylbbta5y1pz/DJI_0003.MP4?dl=0&rlkey=wcdgqangn7t3lnmmv6li9mu9h',
37
37
  'only_matching': True,
38
+ }, {
39
+ 'url': 'https://www.dropbox.com/scl/fo/zjfqse5txqfd7twa8iewj/AOfZzSYWUSKle2HD7XF7kzQ/A-BEAT%20C.mp4?rlkey=6tg3jkp4tv6a5vt58a6dag0mm&dl=0',
40
+ 'only_matching': True,
38
41
  },
39
42
  ]
40
43
 
@@ -0,0 +1,52 @@
1
+ from .common import InfoExtractor
2
+ from ..utils import clean_html
3
+ from ..utils.traversal import (
4
+ find_element,
5
+ find_elements,
6
+ traverse_obj,
7
+ )
8
+
9
+
10
+ class FilmArchivIE(InfoExtractor):
11
+ IE_DESC = 'FILMARCHIV ON'
12
+ _VALID_URL = r'https?://(?:www\.)?filmarchiv\.at/de/filmarchiv-on/video/(?P<id>f_[0-9a-zA-Z]{5,})'
13
+ _TESTS = [{
14
+ 'url': 'https://www.filmarchiv.at/de/filmarchiv-on/video/f_0305p7xKrXUPBwoNE9x6mh',
15
+ 'md5': '54a6596f6a84624531866008a77fa27a',
16
+ 'info_dict': {
17
+ 'id': 'f_0305p7xKrXUPBwoNE9x6mh',
18
+ 'ext': 'mp4',
19
+ 'title': 'Der Wurstelprater zur Kaiserzeit',
20
+ 'description': 'md5:9843f92df5cc9a4975cee7aabcf6e3b2',
21
+ 'thumbnail': r're:https://cdn\.filmarchiv\.at/f_0305/p7xKrXUPBwoNE9x6mh_v1/poster\.jpg',
22
+ },
23
+ }, {
24
+ 'url': 'https://www.filmarchiv.at/de/filmarchiv-on/video/f_0306vI3wO0tJIsfrqYFQXF',
25
+ 'md5': '595385d7f54cb6529140ee8de7d1c3c7',
26
+ 'info_dict': {
27
+ 'id': 'f_0306vI3wO0tJIsfrqYFQXF',
28
+ 'ext': 'mp4',
29
+ 'title': 'Vor 70 Jahren: Wettgehen der Briefträger in Wien',
30
+ 'description': 'md5:b2a2e4230923cd1969d471c552e62811',
31
+ 'thumbnail': r're:https://cdn\.filmarchiv\.at/f_0306/vI3wO0tJIsfrqYFQXF_v1/poster\.jpg',
32
+ },
33
+ }]
34
+
35
+ def _real_extract(self, url):
36
+ media_id = self._match_id(url)
37
+ webpage = self._download_webpage(url, media_id)
38
+ path = '/'.join((media_id[:6], media_id[6:]))
39
+ formats, subtitles = self._extract_m3u8_formats_and_subtitles(
40
+ f'https://cdn.filmarchiv.at/{path}_v1_sv1/playlist.m3u8', media_id)
41
+
42
+ return {
43
+ 'id': media_id,
44
+ 'title': traverse_obj(webpage, ({find_element(tag='title-div')}, {clean_html})),
45
+ 'description': traverse_obj(webpage, (
46
+ {find_elements(tag='div', attr='class', value=r'.*\bborder-base-content\b', regex=True)}, ...,
47
+ {find_elements(tag='div', attr='class', value=r'.*\bprose\b', html=False, regex=True)}, ...,
48
+ {clean_html}, any)),
49
+ 'thumbnail': f'https://cdn.filmarchiv.at/{path}_v1/poster.jpg',
50
+ 'formats': formats,
51
+ 'subtitles': subtitles,
52
+ }