mirror of
https://github.com/yt-dlp/yt-dlp
synced 2025-12-16 06:05:41 +07:00
Compare commits
4 Commits
f87cfadb5c
...
f3c255b63b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3c255b63b | ||
|
|
646904cd3a | ||
|
|
a0bda3b786 | ||
|
|
228ae9f0f2 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -153,10 +153,12 @@ jobs:
|
||||
'os': 'musllinux',
|
||||
'arch': 'x86_64',
|
||||
'runner': 'ubuntu-24.04',
|
||||
'python_version': '3.14',
|
||||
}, {
|
||||
'os': 'musllinux',
|
||||
'arch': 'aarch64',
|
||||
'runner': 'ubuntu-24.04-arm',
|
||||
'python_version': '3.14',
|
||||
}],
|
||||
}
|
||||
INPUTS = json.loads(os.environ['INPUTS'])
|
||||
|
||||
@@ -1197,6 +1197,7 @@
|
||||
MusicdexPlaylistIE,
|
||||
MusicdexSongIE,
|
||||
)
|
||||
from .mux import MuxIE
|
||||
from .mx3 import (
|
||||
Mx3IE,
|
||||
Mx3NeoIE,
|
||||
|
||||
@@ -39,7 +39,7 @@ class BunnyCdnIE(InfoExtractor):
|
||||
'timestamp': 1691145748,
|
||||
'thumbnail': r're:^https?://.*\.b-cdn\.net/32e34c4b-0d72-437c-9abb-05e67657da34/thumbnail_9172dc16\.jpg',
|
||||
'duration': 106.0,
|
||||
'description': 'md5:981a3e899a5c78352b21ed8b2f1efd81',
|
||||
'description': 'md5:11452bcb31f379ee3eaf1234d3264e44',
|
||||
'upload_date': '20230804',
|
||||
'title': 'Sanela ist Teil der #arbeitsmarktkraft',
|
||||
},
|
||||
@@ -58,6 +58,20 @@ class BunnyCdnIE(InfoExtractor):
|
||||
'thumbnail': r're:^https?://.*\.b-cdn\.net/2e8545ec-509d-4571-b855-4cf0235ccd75/thumbnail\.jpg',
|
||||
},
|
||||
'params': {'skip_download': True},
|
||||
}, {
|
||||
# Requires any Referer
|
||||
'url': 'https://iframe.mediadelivery.net/embed/289162/6372f5a3-68df-4ef7-a115-e1110186c477',
|
||||
'info_dict': {
|
||||
'id': '6372f5a3-68df-4ef7-a115-e1110186c477',
|
||||
'ext': 'mp4',
|
||||
'title': '12-Creating Small Asset Blockouts -Timelapse.mp4',
|
||||
'description': '',
|
||||
'duration': 263.0,
|
||||
'timestamp': 1724485440,
|
||||
'upload_date': '20240824',
|
||||
'thumbnail': r're:^https?://.*\.b-cdn\.net/6372f5a3-68df-4ef7-a115-e1110186c477/thumbnail\.jpg',
|
||||
},
|
||||
'params': {'skip_download': True},
|
||||
}]
|
||||
_WEBPAGE_TESTS = [{
|
||||
# Stream requires Referer
|
||||
@@ -100,7 +114,7 @@ def _real_extract(self, url):
|
||||
video_id, library_id = self._match_valid_url(url).group('id', 'library_id')
|
||||
webpage = self._download_webpage(
|
||||
f'https://iframe.mediadelivery.net/embed/{library_id}/{video_id}', video_id,
|
||||
headers=traverse_obj(smuggled_data, {'Referer': 'Referer'}),
|
||||
headers={'Referer': smuggled_data.get('Referer') or 'https://iframe.mediadelivery.net/'},
|
||||
query=traverse_obj(parse_qs(url), {'token': 'token', 'expires': 'expires'}))
|
||||
|
||||
if html_title := self._html_extract_title(webpage, default=None) == '403':
|
||||
|
||||
@@ -1063,7 +1063,7 @@ class DiscoveryNetworksDeIE(DiscoveryPlusBaseIE):
|
||||
'ext': 'mp4',
|
||||
'title': 'German Gold',
|
||||
'description': 'md5:f3073306553a8d9b40e6ac4cdbf09fc6',
|
||||
'display_id': 'german-gold',
|
||||
'display_id': 'goldrausch-in-australien/german-gold',
|
||||
'episode': 'Episode 1',
|
||||
'episode_number': 1,
|
||||
'season': 'Season 5',
|
||||
@@ -1112,7 +1112,7 @@ class DiscoveryNetworksDeIE(DiscoveryPlusBaseIE):
|
||||
'ext': 'mp4',
|
||||
'title': '24 Stunden auf der Feuerwache 3',
|
||||
'description': 'md5:f3084ef6170bfb79f9a6e0c030e09330',
|
||||
'display_id': '24-stunden-auf-der-feuerwache-3',
|
||||
'display_id': 'feuerwache-3-alarm-in-muenchen/24-stunden-auf-der-feuerwache-3',
|
||||
'episode': 'Episode 1',
|
||||
'episode_number': 1,
|
||||
'season': 'Season 1',
|
||||
@@ -1134,7 +1134,7 @@ class DiscoveryNetworksDeIE(DiscoveryPlusBaseIE):
|
||||
'ext': 'mp4',
|
||||
'title': 'Der Poltergeist im Kostümladen',
|
||||
'description': 'md5:20b52b9736a0a3a7873d19a238fad7fc',
|
||||
'display_id': 'der-poltergeist-im-kostumladen',
|
||||
'display_id': 'ghost-adventures/der-poltergeist-im-kostumladen',
|
||||
'episode': 'Episode 1',
|
||||
'episode_number': 1,
|
||||
'season': 'Season 25',
|
||||
@@ -1156,7 +1156,7 @@ class DiscoveryNetworksDeIE(DiscoveryPlusBaseIE):
|
||||
'ext': 'mp4',
|
||||
'title': 'Das Geheimnis meines Bruders',
|
||||
'description': 'md5:3167550bb582eb9c92875c86a0a20882',
|
||||
'display_id': 'das-geheimnis-meines-bruders',
|
||||
'display_id': 'evil-gesichter-des-boesen/das-geheimnis-meines-bruders',
|
||||
'episode': 'Episode 1',
|
||||
'episode_number': 1,
|
||||
'season': 'Season 1',
|
||||
@@ -1175,18 +1175,19 @@ class DiscoveryNetworksDeIE(DiscoveryPlusBaseIE):
|
||||
|
||||
def _real_extract(self, url):
|
||||
domain, programme, alternate_id = self._match_valid_url(url).groups()
|
||||
display_id = f'{programme}/{alternate_id}'
|
||||
meta = self._download_json(
|
||||
f'https://de-api.loma-cms.com/feloma/videos/{alternate_id}/',
|
||||
alternate_id, query={
|
||||
display_id, query={
|
||||
'environment': domain.split('.')[0],
|
||||
'v': '2',
|
||||
'filter[show.slug]': programme,
|
||||
}, fatal=False)
|
||||
video_id = traverse_obj(meta, ('uid', {str}, {lambda s: s[-7:]})) or alternate_id
|
||||
video_id = traverse_obj(meta, ('uid', {str}, {lambda s: s[-7:]})) or display_id
|
||||
|
||||
disco_api_info = self._get_disco_api_info(
|
||||
url, video_id, 'eu1-prod.disco-api.com', domain.replace('.', ''), 'DE')
|
||||
disco_api_info['display_id'] = alternate_id
|
||||
disco_api_info['display_id'] = display_id
|
||||
disco_api_info['categories'] = traverse_obj(meta, (
|
||||
'taxonomies', lambda _, v: v['category'] == 'genre', 'title', {str.strip}, filter, all, filter))
|
||||
|
||||
|
||||
92
yt_dlp/extractor/mux.py
Normal file
92
yt_dlp/extractor/mux.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import re
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import (
|
||||
extract_attributes,
|
||||
filter_dict,
|
||||
parse_qs,
|
||||
smuggle_url,
|
||||
unsmuggle_url,
|
||||
update_url_query,
|
||||
)
|
||||
from ..utils.traversal import traverse_obj
|
||||
|
||||
|
||||
class MuxIE(InfoExtractor):
|
||||
_VALID_URL = r'https?://(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)'
|
||||
_EMBED_REGEX = [r'<iframe\b[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:stream\.new/v|player\.mux\.com)/(?P<id>[A-Za-z0-9-]+)[^"\']+)']
|
||||
_TESTS = [{
|
||||
'url': 'https://stream.new/v/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j/embed',
|
||||
'info_dict': {
|
||||
'ext': 'mp4',
|
||||
'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
|
||||
'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
|
||||
},
|
||||
}, {
|
||||
'url': 'https://player.mux.com/OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
|
||||
'info_dict': {
|
||||
'ext': 'mp4',
|
||||
'id': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
|
||||
'title': 'OCtRWZiZqKvLbnZ32WSEYiGNvHdAmB01j',
|
||||
},
|
||||
}]
|
||||
_WEBPAGE_TESTS = [{
|
||||
# iframe embed
|
||||
'url': 'https://www.redbrickai.com/blog/2025-07-14-FAST-brush',
|
||||
'info_dict': {
|
||||
'ext': 'mp4',
|
||||
'id': 'cXhzAiW1AmsHY01eRbEYFcTEAn0102aGN8sbt8JprP6Dfw',
|
||||
'title': 'cXhzAiW1AmsHY01eRbEYFcTEAn0102aGN8sbt8JprP6Dfw',
|
||||
},
|
||||
}, {
|
||||
# mux-player embed
|
||||
'url': 'https://muxvideo.2coders.com/download/',
|
||||
'info_dict': {
|
||||
'ext': 'mp4',
|
||||
'id': 'JBuasdg35Hw7tYmTe9k68QLPQKixL300YsWHDz5Flit8',
|
||||
'title': 'JBuasdg35Hw7tYmTe9k68QLPQKixL300YsWHDz5Flit8',
|
||||
},
|
||||
}, {
|
||||
# mux-player with title metadata
|
||||
'url': 'https://datastar-todomvc.cross.stream/',
|
||||
'info_dict': {
|
||||
'ext': 'mp4',
|
||||
'id': 'KX01ZSZ8CXv5SVfVwMZKJTcuBcUQmo1ReS9U5JjoHm4k',
|
||||
'title': 'TodoMVC with Datastar Tutorial',
|
||||
},
|
||||
}]
|
||||
|
||||
@classmethod
|
||||
def _extract_embed_urls(cls, url, webpage):
|
||||
yield from super()._extract_embed_urls(url, webpage)
|
||||
for mux_player in re.findall(r'<mux-(?:player|video)\b[^>]*\bplayback-id=[^>]+>', webpage):
|
||||
attrs = extract_attributes(mux_player)
|
||||
playback_id = attrs.get('playback-id')
|
||||
if not playback_id:
|
||||
continue
|
||||
token = attrs.get('playback-token') or traverse_obj(playback_id, ({parse_qs}, 'token', -1))
|
||||
playback_id = playback_id.partition('?')[0]
|
||||
|
||||
embed_url = update_url_query(
|
||||
f'https://player.mux.com/{playback_id}',
|
||||
filter_dict({'playback-token': token}))
|
||||
if title := attrs.get('metadata-video-title'):
|
||||
embed_url = smuggle_url(embed_url, {'title': title})
|
||||
yield embed_url
|
||||
|
||||
def _real_extract(self, url):
|
||||
url, smuggled_data = unsmuggle_url(url, {})
|
||||
video_id = self._match_id(url)
|
||||
|
||||
token = traverse_obj(parse_qs(url), ('playback-token', -1))
|
||||
|
||||
formats, subtitles = self._extract_m3u8_formats_and_subtitles(
|
||||
f'https://stream.mux.com/{video_id}.m3u8', video_id, 'mp4',
|
||||
query=filter_dict({'token': token}))
|
||||
|
||||
return {
|
||||
'id': video_id,
|
||||
'title': smuggled_data.get('title') or video_id,
|
||||
'formats': formats,
|
||||
'subtitles': subtitles,
|
||||
}
|
||||
Reference in New Issue
Block a user