1
0
mirror of https://github.com/yt-dlp/yt-dlp synced 2025-12-16 06:05:41 +07:00

Compare commits

...

4 Commits

Author SHA1 Message Date
bashonly
f3c255b63b [ie/DiscoveryNetworksDe] Restore original display_id (#14958)
Fix 10dea209d2

Authored by: bashonly
2025-11-09 03:45:26 +00:00
bashonly
646904cd3a [build] Bump musllinux Python version to 3.14 (#14623)
Authored by: bashonly
2025-11-09 01:33:30 +00:00
Pierce Brooks
a0bda3b786 [ie/mux] Add extractor (#14914)
Closes #14913
Authored by: PierceLBrooks, seproDev

Co-authored-by: sepro <sepro@sepr0.com>
2025-11-09 00:44:10 +01:00
sepro
228ae9f0f2 [ie/BunnyCdn] Fix extractor (#14954)
Authored by: seproDev
2025-11-09 00:40:37 +01:00
5 changed files with 119 additions and 9 deletions

View File

@@ -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'])

View File

@@ -1197,6 +1197,7 @@
MusicdexPlaylistIE,
MusicdexSongIE,
)
from .mux import MuxIE
from .mx3 import (
Mx3IE,
Mx3NeoIE,

View File

@@ -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':

View File

@@ -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
View 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,
}