mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-26 09:11:25 +01:00
Compare commits
20 Commits
9c5887313d
...
d3735a9a71
Author | SHA1 | Date | |
---|---|---|---|
|
d3735a9a71 | ||
|
f2a4983df7 | ||
|
bacc31b05a | ||
|
a9f85670d0 | ||
|
090fb58de7 | ||
|
cb0aa20d4f | ||
|
2c49f52c04 | ||
|
4ff1288758 | ||
|
93e65f14dc | ||
|
d40dbdc50b | ||
|
d9e0e023b8 | ||
|
74e26f7599 | ||
|
549d28cd04 | ||
|
3e4523b78c | ||
|
e17e2beea6 | ||
|
b62a7cf725 | ||
|
c386fc0d43 | ||
|
5fea24bda2 | ||
|
8125680192 | ||
|
380027be4e |
|
@ -75,6 +75,12 @@ from .aenetworks import (
|
||||||
HistoryTopicIE,
|
HistoryTopicIE,
|
||||||
)
|
)
|
||||||
from .aeonco import AeonCoIE
|
from .aeonco import AeonCoIE
|
||||||
|
from .afl import (
|
||||||
|
AFCVideoIE,
|
||||||
|
AFLPodcastIE,
|
||||||
|
AFLVideoIE,
|
||||||
|
CarltonFCVideoIE,
|
||||||
|
)
|
||||||
from .afreecatv import (
|
from .afreecatv import (
|
||||||
AfreecaTVCatchStoryIE,
|
AfreecaTVCatchStoryIE,
|
||||||
AfreecaTVIE,
|
AfreecaTVIE,
|
||||||
|
@ -1433,6 +1439,7 @@ from .oftv import (
|
||||||
)
|
)
|
||||||
from .oktoberfesttv import OktoberfestTVIE
|
from .oktoberfesttv import OktoberfestTVIE
|
||||||
from .olympics import OlympicsReplayIE
|
from .olympics import OlympicsReplayIE
|
||||||
|
from .omnyfm import OmnyFMShowIE
|
||||||
from .on24 import On24IE
|
from .on24 import On24IE
|
||||||
from .ondemandkorea import (
|
from .ondemandkorea import (
|
||||||
OnDemandKoreaIE,
|
OnDemandKoreaIE,
|
||||||
|
|
206
yt_dlp/extractor/afl.py
Normal file
206
yt_dlp/extractor/afl.py
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
from .brightcove import BrightcoveNewIE
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from .omnyfm import OmnyFMShowIE
|
||||||
|
from ..utils import (
|
||||||
|
extract_attributes,
|
||||||
|
get_element_by_class,
|
||||||
|
get_element_html_by_attribute,
|
||||||
|
get_element_html_by_id,
|
||||||
|
smuggle_url,
|
||||||
|
str_or_none,
|
||||||
|
traverse_obj,
|
||||||
|
url_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AFLVideoIE(InfoExtractor):
|
||||||
|
IE_NAME = 'afl:video'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?(?:afl|lions)\.com\.au/(?:aflw/)?video/(?P<id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.afl.com.au/aflw/video/1217670/the-w-show-aflws-line-in-the-sand-moment-bonnies-bold-bid',
|
||||||
|
'md5': '7000431c2bd3f96eddb5f63273aea83e',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6361825702112',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'description': 'md5:d1fee2ae8e3ecf486c1f0f7aa19e724b',
|
||||||
|
'upload_date': '20240911',
|
||||||
|
'duration': 1523.28,
|
||||||
|
'tags': 'count:0',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'title': "The W Show: AFLW's 'line in the sand' moment, Bonnie's bold bid",
|
||||||
|
'uploader_id': '6057984922001',
|
||||||
|
'timestamp': 1726038522,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.lions.com.au/video/1655451/team-song-brisbane?videoId=1655451&modal=true&type=video&publishFrom=1726318577001',
|
||||||
|
'md5': '47e8c67e317b48a69787c8bc39c3c591',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6361958949112',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'description': 'md5:c0fb37fcad9ec0f49ac54eb8d76641bd',
|
||||||
|
'upload_date': '20240914',
|
||||||
|
'duration': 41.0,
|
||||||
|
'tags': 'count:0',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'title': 'Team Song: Brisbane',
|
||||||
|
'uploader_id': '6057984922001',
|
||||||
|
'timestamp': 1726318788,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.afl.com.au/video/1217264/bulldogs-season-review-gold-plated-list-going-to-waste-duos-frightening-future?videoId=1217264&modal=true&type=video&publishFrom=1725998400001',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.afl.com.au/video/1210885/wafl-showreel-ef-hamish-davis-highlights?videoId=1210885&modal=true&type=video&publishFrom=1725171238001',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.lions.com.au/video/1657551/svarc-weve-built-up-really-well?videoId=1657551&modal=true&type=video&publishFrom=1726545600001',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
element = get_element_by_class('inline-player__player-container', webpage)
|
||||||
|
attrs = traverse_obj(extract_attributes(element), {
|
||||||
|
'account_id': ('data-account', {str_or_none}),
|
||||||
|
'player_id': ('data-player', {lambda x: f'{x}_default'}, {str_or_none}),
|
||||||
|
'video_id': ('data-video-id', {str_or_none}),
|
||||||
|
})
|
||||||
|
account_id = attrs.get('account_id')
|
||||||
|
player_id = attrs.get('player_id')
|
||||||
|
video_id = attrs.get('video_id')
|
||||||
|
|
||||||
|
video_url = f'https://players.brightcove.net/{account_id}/{player_id}/index.html?videoId={video_id}'
|
||||||
|
video_url = smuggle_url(video_url, {'referrer': url})
|
||||||
|
return self.url_result(video_url, BrightcoveNewIE)
|
||||||
|
|
||||||
|
|
||||||
|
class AFLPodcastIE(InfoExtractor):
|
||||||
|
IE_NAME = 'afl:podcast'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?(?:afl|carltonfc)\.com\.au/(?:aflw/)?podcasts/(?P<id>[\w-]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.afl.com.au/podcasts/between-us',
|
||||||
|
'md5': '7000431c2bd3f96eddb5f63273aea83e',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'e0ab8454-f818-483f-bed1-b156002c021f',
|
||||||
|
'title': 'Between Us',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 7,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.carltonfc.com.au/podcasts/walk-a-mile',
|
||||||
|
'md5': '',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6dbb9b23-7f00-49d4-b44e-aec2017651dc',
|
||||||
|
'title': 'Walk a Mile in Their Shoes',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 3,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.afl.com.au/podcasts/afl-daily',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.carltonfc.com.au/podcasts/summer-sessions',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
element = get_element_by_class('omny-embed', webpage)
|
||||||
|
podcast_url = traverse_obj(extract_attributes(element), ('src', {url_or_none}))
|
||||||
|
return self.url_result(podcast_url, OmnyFMShowIE)
|
||||||
|
|
||||||
|
|
||||||
|
class AFCVideoIE(InfoExtractor):
|
||||||
|
IE_NAME = 'afc:video'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?afc\.com\.au/video/(?P<id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.afc.com.au/video/1657583/girls-academies-be-a-pro?videoId=1657583&modal=true&type=video&publishFrom=1726548621001',
|
||||||
|
'md5': 'd0f4ec78b5a693d95c975ae3aeed8b2d',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6362048189112',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'description': 'md5:5c43f1affe1a0cd8e2192358a49de9cc',
|
||||||
|
'upload_date': '20240917',
|
||||||
|
'duration': 50.48,
|
||||||
|
'tags': 'count:0',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'title': 'Girls Academies – ‘Be a Pro’',
|
||||||
|
'uploader_id': '6057984922001',
|
||||||
|
'timestamp': 1726548942,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.afc.com.au/video/1586280/se10ep16-the-crows-show?videoId=1586280&modal=true&type=video&publishFrom=1719639000001&tagNames=crowsshowepisode',
|
||||||
|
'md5': 'bd9984d62f87b4c2299bb62ffc869189',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6355746458112',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'description': 'md5:4470d107af6e749a8225fd558b98b50b',
|
||||||
|
'upload_date': '20240627',
|
||||||
|
'duration': 1193.64,
|
||||||
|
'tags': 'count:0',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'title': 'SE10EP16 - The Crows Show',
|
||||||
|
'uploader_id': '6057984922001',
|
||||||
|
'timestamp': 1719466601,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.afc.com.au/video/1634706/jones-radiology-injury-update-r24?videoId=1634706&modal=true&type=video&publishFrom=1724126172001',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
video_attrs = extract_attributes(get_element_html_by_id('VideoModal', webpage))
|
||||||
|
player_id = video_attrs['data-player-id'] + '_default'
|
||||||
|
account_id = video_attrs['data-account-id']
|
||||||
|
|
||||||
|
video_element_html = get_element_html_by_attribute('data-id', display_id, webpage, tag='a')
|
||||||
|
if video_element_html:
|
||||||
|
video_data = self._parse_json(extract_attributes(video_element_html)['data-ui-args'], display_id)
|
||||||
|
else:
|
||||||
|
video_data = self._download_json(f'https://aflapi.afc.com.au/content/aflc-adel/video/en/{display_id}', display_id)
|
||||||
|
video_id = video_data['mediaId']
|
||||||
|
|
||||||
|
video_url = f'https://players.brightcove.net/{account_id}/{player_id}/index.html?videoId={video_id}'
|
||||||
|
video_url = smuggle_url(video_url, {'referrer': url})
|
||||||
|
return self.url_result(video_url, BrightcoveNewIE)
|
||||||
|
|
||||||
|
|
||||||
|
class CarltonFCVideoIE(InfoExtractor):
|
||||||
|
IE_NAME = 'carltonfc:video'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?carltonfc\.com\.au/video/(?P<id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.carltonfc.com.au/video/1657596/cripps-on-taking-carlton-to-the-next-level?videoId=1657596&modal=true&type=video&publishFrom=1726555500001',
|
||||||
|
'md5': '67916ea9dd28376365184bb3869a1548',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6362046715112',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'description': 'md5:02eeff6576fcd7c33e18e34b1b0ebf56',
|
||||||
|
'upload_date': '20240917',
|
||||||
|
'duration': 90.44,
|
||||||
|
'tags': 'count:0',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'title': 'Cripps on taking Carlton to the next level',
|
||||||
|
'uploader_id': '6057984922001',
|
||||||
|
'timestamp': 1726550622,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.carltonfc.com.au/video/1658173/the-rundown-impact-of-fans?videoId=1658173&modal=true&type=video&publishFrom=1726630922001',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
video_attrs = extract_attributes(get_element_html_by_id('VideoModal', webpage))
|
||||||
|
player_id = video_attrs['data-player-id'] + '_default'
|
||||||
|
account_id = video_attrs['data-account-id']
|
||||||
|
|
||||||
|
video_element_html = get_element_html_by_attribute('data-id', display_id, webpage)
|
||||||
|
video_data = self._search_json(r'data-ui-args\s*=\s*["\']', video_element_html, 'video-id', display_id)
|
||||||
|
video_id = video_data['mediaId']
|
||||||
|
|
||||||
|
video_url = f'https://players.brightcove.net/{account_id}/{player_id}/index.html?videoId={video_id}'
|
||||||
|
video_url = smuggle_url(video_url, {'referrer': url})
|
||||||
|
return self.url_result(video_url, BrightcoveNewIE)
|
|
@ -205,6 +205,26 @@ class ArchiveOrgIE(InfoExtractor):
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
}, {
|
||||||
|
# The reviewbody is None for one of the reviews; just need to extract data without crashing
|
||||||
|
'url': 'https://archive.org/details/gd95-04-02.sbd.11622.sbeok.shnf/gd95-04-02d1t04.shn',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'gd95-04-02.sbd.11622.sbeok.shnf/gd95-04-02d1t04.shn',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': 'Stuck Inside of Mobile with the Memphis Blues Again',
|
||||||
|
'creators': ['Grateful Dead'],
|
||||||
|
'duration': 338.31,
|
||||||
|
'track': 'Stuck Inside of Mobile with the Memphis Blues Again',
|
||||||
|
'description': 'md5:764348a470b986f1217ffd38d6ac7b72',
|
||||||
|
'display_id': 'gd95-04-02d1t04.shn',
|
||||||
|
'location': 'Pyramid Arena',
|
||||||
|
'uploader': 'jon@archive.org',
|
||||||
|
'album': '1995-04-02 - Pyramid Arena',
|
||||||
|
'upload_date': '20040519',
|
||||||
|
'track_number': 4,
|
||||||
|
'release_date': '19950402',
|
||||||
|
'timestamp': 1084927901,
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -335,7 +355,7 @@ class ArchiveOrgIE(InfoExtractor):
|
||||||
info['comments'].append({
|
info['comments'].append({
|
||||||
'id': review.get('review_id'),
|
'id': review.get('review_id'),
|
||||||
'author': review.get('reviewer'),
|
'author': review.get('reviewer'),
|
||||||
'text': str_or_none(review.get('reviewtitle'), '') + '\n\n' + review.get('reviewbody'),
|
'text': join_nonempty('reviewtitle', 'reviewbody', from_dict=review, delim='\n\n'),
|
||||||
'timestamp': unified_timestamp(review.get('createdate')),
|
'timestamp': unified_timestamp(review.get('createdate')),
|
||||||
'parent': 'root'})
|
'parent': 'root'})
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from ..utils import (
|
||||||
|
|
||||||
|
|
||||||
class ChaturbateIE(InfoExtractor):
|
class ChaturbateIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:[^/]+\.)?chaturbate\.com/(?:fullvideo/?\?.*?\bb=)?(?P<id>[^/?&#]+)'
|
_VALID_URL = r'https?://(?:[^/]+\.)?chaturbate\.(?P<tld>com|eu|global)/(?:fullvideo/?\?.*?\bb=)?(?P<id>[^/?&#]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.chaturbate.com/siswet19/',
|
'url': 'https://www.chaturbate.com/siswet19/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@ -29,15 +29,24 @@ class ChaturbateIE(InfoExtractor):
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://en.chaturbate.com/siswet19/',
|
'url': 'https://en.chaturbate.com/siswet19/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://chaturbate.eu/siswet19/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://chaturbate.eu/fullvideo/?b=caylin',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://chaturbate.global/siswet19/',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_ROOM_OFFLINE = 'Room is currently offline'
|
_ROOM_OFFLINE = 'Room is currently offline'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id, tld = self._match_valid_url(url).group('id', 'tld')
|
||||||
|
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
f'https://chaturbate.com/{video_id}/', video_id,
|
f'https://chaturbate.{tld}/{video_id}/', video_id,
|
||||||
headers=self.geo_verification_headers())
|
headers=self.geo_verification_headers())
|
||||||
|
|
||||||
found_m3u8_urls = []
|
found_m3u8_urls = []
|
||||||
|
|
|
@ -563,13 +563,13 @@ class FacebookIE(InfoExtractor):
|
||||||
return extract_video_data(try_get(
|
return extract_video_data(try_get(
|
||||||
js_data, lambda x: x['jsmods']['instances'], list) or [])
|
js_data, lambda x: x['jsmods']['instances'], list) or [])
|
||||||
|
|
||||||
def extract_dash_manifest(video, formats):
|
def extract_dash_manifest(vid_data, formats, mpd_url=None):
|
||||||
dash_manifest = traverse_obj(
|
dash_manifest = traverse_obj(
|
||||||
video, 'dash_manifest', 'playlist', 'dash_manifest_xml_string', expected_type=str)
|
vid_data, 'dash_manifest', 'playlist', 'dash_manifest_xml_string', 'manifest_xml', expected_type=str)
|
||||||
if dash_manifest:
|
if dash_manifest:
|
||||||
formats.extend(self._parse_mpd_formats(
|
formats.extend(self._parse_mpd_formats(
|
||||||
compat_etree_fromstring(urllib.parse.unquote_plus(dash_manifest)),
|
compat_etree_fromstring(urllib.parse.unquote_plus(dash_manifest)),
|
||||||
mpd_url=url_or_none(video.get('dash_manifest_url'))))
|
mpd_url=url_or_none(video.get('dash_manifest_url')) or mpd_url))
|
||||||
|
|
||||||
def process_formats(info):
|
def process_formats(info):
|
||||||
# Downloads with browser's User-Agent are rate limited. Working around
|
# Downloads with browser's User-Agent are rate limited. Working around
|
||||||
|
@ -619,9 +619,12 @@ class FacebookIE(InfoExtractor):
|
||||||
video = video['creation_story']
|
video = video['creation_story']
|
||||||
video['owner'] = traverse_obj(video, ('short_form_video_context', 'video_owner'))
|
video['owner'] = traverse_obj(video, ('short_form_video_context', 'video_owner'))
|
||||||
video.update(reel_info)
|
video.update(reel_info)
|
||||||
fmt_data = traverse_obj(video, ('videoDeliveryLegacyFields', {dict})) or video
|
|
||||||
formats = []
|
formats = []
|
||||||
q = qualities(['sd', 'hd'])
|
q = qualities(['sd', 'hd'])
|
||||||
|
|
||||||
|
# Legacy formats extraction
|
||||||
|
fmt_data = traverse_obj(video, ('videoDeliveryLegacyFields', {dict})) or video
|
||||||
for key, format_id in (('playable_url', 'sd'), ('playable_url_quality_hd', 'hd'),
|
for key, format_id in (('playable_url', 'sd'), ('playable_url_quality_hd', 'hd'),
|
||||||
('playable_url_dash', ''), ('browser_native_hd_url', 'hd'),
|
('playable_url_dash', ''), ('browser_native_hd_url', 'hd'),
|
||||||
('browser_native_sd_url', 'sd')):
|
('browser_native_sd_url', 'sd')):
|
||||||
|
@ -629,7 +632,7 @@ class FacebookIE(InfoExtractor):
|
||||||
if not playable_url:
|
if not playable_url:
|
||||||
continue
|
continue
|
||||||
if determine_ext(playable_url) == 'mpd':
|
if determine_ext(playable_url) == 'mpd':
|
||||||
formats.extend(self._extract_mpd_formats(playable_url, video_id))
|
formats.extend(self._extract_mpd_formats(playable_url, video_id, fatal=False))
|
||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': format_id,
|
'format_id': format_id,
|
||||||
|
@ -638,6 +641,28 @@ class FacebookIE(InfoExtractor):
|
||||||
'url': playable_url,
|
'url': playable_url,
|
||||||
})
|
})
|
||||||
extract_dash_manifest(fmt_data, formats)
|
extract_dash_manifest(fmt_data, formats)
|
||||||
|
|
||||||
|
# New videoDeliveryResponse formats extraction
|
||||||
|
fmt_data = traverse_obj(video, ('videoDeliveryResponseFragment', 'videoDeliveryResponseResult'))
|
||||||
|
mpd_urls = traverse_obj(fmt_data, ('dash_manifest_urls', ..., 'manifest_url', {url_or_none}))
|
||||||
|
dash_manifests = traverse_obj(fmt_data, ('dash_manifests', lambda _, v: v['manifest_xml']))
|
||||||
|
for idx, dash_manifest in enumerate(dash_manifests):
|
||||||
|
extract_dash_manifest(dash_manifest, formats, mpd_url=traverse_obj(mpd_urls, idx))
|
||||||
|
if not dash_manifests:
|
||||||
|
# Only extract from MPD URLs if the manifests are not already provided
|
||||||
|
for mpd_url in mpd_urls:
|
||||||
|
formats.extend(self._extract_mpd_formats(mpd_url, video_id, fatal=False))
|
||||||
|
for prog_fmt in traverse_obj(fmt_data, ('progressive_urls', lambda _, v: v['progressive_url'])):
|
||||||
|
format_id = traverse_obj(prog_fmt, ('metadata', 'quality', {str.lower}))
|
||||||
|
formats.append({
|
||||||
|
'format_id': format_id,
|
||||||
|
# sd, hd formats w/o resolution info should be deprioritized below DASH
|
||||||
|
'quality': q(format_id) - 3,
|
||||||
|
'url': prog_fmt['progressive_url'],
|
||||||
|
})
|
||||||
|
for m3u8_url in traverse_obj(fmt_data, ('hls_playlist_urls', ..., 'hls_playlist_url', {url_or_none})):
|
||||||
|
formats.extend(self._extract_m3u8_formats(m3u8_url, video_id, 'mp4', fatal=False, m3u8_id='hls'))
|
||||||
|
|
||||||
if not formats:
|
if not formats:
|
||||||
# Do not append false positive entry w/o any formats
|
# Do not append false positive entry w/o any formats
|
||||||
return
|
return
|
||||||
|
|
68
yt_dlp/extractor/omnyfm.py
Normal file
68
yt_dlp/extractor/omnyfm.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import functools
|
||||||
|
import math
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
InAdvancePagedList,
|
||||||
|
clean_html,
|
||||||
|
float_or_none,
|
||||||
|
int_or_none,
|
||||||
|
str_or_none,
|
||||||
|
traverse_obj,
|
||||||
|
unified_strdate,
|
||||||
|
url_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OmnyFMShowIE(InfoExtractor):
|
||||||
|
IE_NAME = 'omnyfm:show'
|
||||||
|
_VALID_URL = r'https?://omny\.fm/shows/(?P<id>[^/]+)'
|
||||||
|
_EMBED_REGEX = [r'<iframe[^>]+?src=(["\'])(?P<url>https?://omny\.fm/shows/.+?)\1']
|
||||||
|
_PAGE_SIZE = 10
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://omny.fm/shows/league-leaders',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'bbe146d4-9bee-4763-b785-ad830009a23f',
|
||||||
|
'title': 'League Leaders with Nicole Livingstone',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 15,
|
||||||
|
}, {
|
||||||
|
'url': 'https://omny.fm/shows/afl-daily',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _fetch_page(self, org_id, playlist_id, page):
|
||||||
|
return self._download_json(f'https://api.omny.fm/orgs/{org_id}/programs/{playlist_id}/clips?cursor={page}&pageSize={self._PAGE_SIZE}', f'{playlist_id}_{page}')
|
||||||
|
|
||||||
|
def _entries(self, org_id, playlist_id, first_page_data, page):
|
||||||
|
data = first_page_data if not page else self._fetch_page(org_id, playlist_id, page + 1)
|
||||||
|
for clip in data.get('Clips', {}):
|
||||||
|
yield traverse_obj(clip, {
|
||||||
|
'id': ('Id', {str_or_none}),
|
||||||
|
'title': ('Title', {str_or_none}),
|
||||||
|
'description': ('Description', {clean_html}),
|
||||||
|
'thumbnail': (('ImageUrl', 'ArtworkUrl'), {url_or_none}, any),
|
||||||
|
'duration': ('DurationSeconds', {float_or_none}),
|
||||||
|
'url': ('AudioUrl', {url_or_none}),
|
||||||
|
'season_number': ('Season', {int_or_none}),
|
||||||
|
'episode_number': ('Episode', {int_or_none}),
|
||||||
|
'timestamp': ('PublishedUtc', {unified_strdate}, {int_or_none}),
|
||||||
|
'filesize': ('PublishedAudioSizeInBytes', {int}),
|
||||||
|
})
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
page_url = 'https://omny.fm/shows/' + display_id
|
||||||
|
webpage = self._download_webpage(page_url, display_id)
|
||||||
|
|
||||||
|
data = self._search_nextjs_data(webpage, display_id)
|
||||||
|
org_id = traverse_obj(data, ('props', 'pageProps', 'program', 'OrganizationId', {str_or_none}))
|
||||||
|
playlist_id = traverse_obj(data, ('props', 'pageProps', 'program', 'Id', {str_or_none}))
|
||||||
|
playlist_count = traverse_obj(data, ('props', 'pageProps', 'program', 'DefaultPlaylist', 'NumberOfClips', {int_or_none}))
|
||||||
|
title = traverse_obj(data, ('props', 'pageProps', 'program', 'Name', {str_or_none}))
|
||||||
|
first_page_data = traverse_obj(data, ('props', 'pageProps', 'clips', {dict}))
|
||||||
|
total_pages = math.ceil(playlist_count / self._PAGE_SIZE)
|
||||||
|
|
||||||
|
return self.playlist_result(InAdvancePagedList(
|
||||||
|
functools.partial(self._entries, org_id, playlist_id, first_page_data),
|
||||||
|
total_pages, self._PAGE_SIZE), playlist_id, title)
|
Loading…
Reference in New Issue
Block a user