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.
- yt_dlp/YoutubeDL.py +4 -0
- yt_dlp/extractor/_extractors.py +2 -0
- yt_dlp/extractor/bandcamp.py +40 -41
- yt_dlp/extractor/dropbox.py +4 -1
- yt_dlp/extractor/filmarchiv.py +52 -0
- yt_dlp/extractor/lazy_extractors.py +23 -10
- yt_dlp/extractor/nebula.py +53 -0
- yt_dlp/extractor/neteasemusic.py +4 -1
- yt_dlp/extractor/tubitv.py +4 -1
- yt_dlp/extractor/yahoo.py +52 -115
- yt_dlp/utils/_utils.py +4 -2
- yt_dlp/version.py +3 -3
- {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/METADATA +1 -1
- {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/RECORD +22 -21
- {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
- {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
- {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
- {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
- {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
- {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/WHEEL +0 -0
- {yt_dlp-2025.12.18.235942.dev0.dist-info → yt_dlp-2025.12.20.232942.dev0.dist-info}/entry_points.txt +0 -0
- {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))
|
yt_dlp/extractor/_extractors.py
CHANGED
|
@@ -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
|
yt_dlp/extractor/bandcamp.py
CHANGED
|
@@ -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/?\?(
|
|
416
|
+
_VALID_URL = r'https?://(?:www\.)?bandcamp\.com/radio/?\?(?:[^#]+&)?show=(?P<id>\d+)'
|
|
415
417
|
_TESTS = [{
|
|
416
|
-
'url': 'https://bandcamp.com
|
|
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': '
|
|
423
|
+
'title': 'Bandcamp Weekly, 2017-04-04',
|
|
422
424
|
'description': 'md5:5d48150916e8e02d030623a48512c874',
|
|
423
|
-
'
|
|
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/?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
446
|
-
|
|
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
|
-
'
|
|
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
|
|
yt_dlp/extractor/dropbox.py
CHANGED
|
@@ -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/
|
|
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
|
+
}
|