mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-10-02 15:21:23 +02:00
Compare commits
5 Commits
d63eae7e7f
...
fc2cc626f0
Author | SHA1 | Date | |
---|---|---|---|
|
fc2cc626f0 | ||
|
a2bac6b7ad | ||
|
4b8b0dded8 | ||
|
4a6ff0b47a | ||
|
62c65bfaf8 |
|
@ -1247,7 +1247,10 @@ from .nexx import (
|
||||||
NexxIE,
|
NexxIE,
|
||||||
NexxEmbedIE,
|
NexxEmbedIE,
|
||||||
)
|
)
|
||||||
from .nfb import NFBIE
|
from .nfb import (
|
||||||
|
NFBIE,
|
||||||
|
NFBSeriesIE,
|
||||||
|
)
|
||||||
from .nfhsnetwork import NFHSNetworkIE
|
from .nfhsnetwork import NFHSNetworkIE
|
||||||
from .nfl import (
|
from .nfl import (
|
||||||
NFLIE,
|
NFLIE,
|
||||||
|
@ -1284,6 +1287,7 @@ from .niconico import (
|
||||||
NicovideoTagURLIE,
|
NicovideoTagURLIE,
|
||||||
NiconicoLiveIE,
|
NiconicoLiveIE,
|
||||||
)
|
)
|
||||||
|
from .ninaprotocol import NinaProtocolIE
|
||||||
from .ninecninemedia import (
|
from .ninecninemedia import (
|
||||||
NineCNineMediaIE,
|
NineCNineMediaIE,
|
||||||
CPTwentyFourIE,
|
CPTwentyFourIE,
|
||||||
|
@ -1514,7 +1518,7 @@ from .puhutv import (
|
||||||
PuhuTVSerieIE,
|
PuhuTVSerieIE,
|
||||||
)
|
)
|
||||||
from .pr0gramm import Pr0grammIE
|
from .pr0gramm import Pr0grammIE
|
||||||
from .prankcast import PrankCastIE
|
from .prankcast import PrankCastIE, PrankCastPostIE
|
||||||
from .premiershiprugby import PremiershipRugbyIE
|
from .premiershiprugby import PremiershipRugbyIE
|
||||||
from .presstv import PressTVIE
|
from .presstv import PressTVIE
|
||||||
from .projectveritas import ProjectVeritasIE
|
from .projectveritas import ProjectVeritasIE
|
||||||
|
|
|
@ -67,7 +67,10 @@ class CineverseIE(CineverseBaseIE):
|
||||||
html = self._download_webpage(url, video_id)
|
html = self._download_webpage(url, video_id)
|
||||||
idetails = self._search_nextjs_data(html, video_id)['props']['pageProps']['idetails']
|
idetails = self._search_nextjs_data(html, video_id)['props']['pageProps']['idetails']
|
||||||
|
|
||||||
if idetails.get('err_code') == 1200:
|
err_code = idetails.get('err_code')
|
||||||
|
if err_code == 1002:
|
||||||
|
self.raise_login_required()
|
||||||
|
elif err_code == 1200:
|
||||||
self.raise_geo_restricted(
|
self.raise_geo_restricted(
|
||||||
'This video is not available from your location due to geo restriction. '
|
'This video is not available from your location due to geo restriction. '
|
||||||
'You may be able to bypass it by using the /details/ page instead of the /watch/ page',
|
'You may be able to bypass it by using the /details/ page instead of the /watch/ page',
|
||||||
|
|
|
@ -1,10 +1,54 @@
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
|
merge_dicts,
|
||||||
|
parse_count,
|
||||||
|
url_or_none,
|
||||||
|
urljoin,
|
||||||
|
)
|
||||||
|
from ..utils.traversal import traverse_obj
|
||||||
|
|
||||||
|
|
||||||
class NFBIE(InfoExtractor):
|
class NFBBaseIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?nfb\.ca/film/(?P<id>[^/?#&]+)'
|
_VALID_URL_BASE = r'https?://(?:www\.)?(?P<site>nfb|onf)\.ca'
|
||||||
|
_GEO_COUNTRIES = ['CA']
|
||||||
|
|
||||||
|
def _extract_ep_data(self, webpage, video_id, fatal=False):
|
||||||
|
return self._search_json(
|
||||||
|
r'const\s+episodesData\s*=', webpage, 'episode data', video_id,
|
||||||
|
contains_pattern=r'\[\s*{(?s:.+)}\s*\]', fatal=fatal) or []
|
||||||
|
|
||||||
|
def _extract_ep_info(self, data, video_id, slug=None):
|
||||||
|
info = traverse_obj(data, (lambda _, v: video_id in v['embed_url'], {
|
||||||
|
'description': ('description', {str}),
|
||||||
|
'thumbnail': ('thumbnail_url', {url_or_none}),
|
||||||
|
'uploader': ('data_layer', 'episodeMaker', {str}),
|
||||||
|
'release_year': ('data_layer', 'episodeYear', {int_or_none}),
|
||||||
|
'episode': ('data_layer', 'episodeTitle', {str}),
|
||||||
|
'season': ('data_layer', 'seasonTitle', {str}),
|
||||||
|
'season_number': ('data_layer', 'seasonTitle', {parse_count}),
|
||||||
|
'series': ('data_layer', 'seriesTitle', {str}),
|
||||||
|
}), get_all=False)
|
||||||
|
|
||||||
|
return {
|
||||||
|
**info,
|
||||||
|
'id': video_id,
|
||||||
|
'title': join_nonempty('series', 'episode', from_dict=info, delim=' - '),
|
||||||
|
'episode_number': int_or_none(self._search_regex(
|
||||||
|
r'[/-]e(?:pisode)?-?(\d+)(?:[/-]|$)', slug or video_id, 'episode number', default=None)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NFBIE(NFBBaseIE):
|
||||||
|
IE_NAME = 'nfb'
|
||||||
|
IE_DESC = 'nfb.ca and onf.ca films and episodes'
|
||||||
|
_VALID_URL = [
|
||||||
|
rf'{NFBBaseIE._VALID_URL_BASE}/(?P<type>film)/(?P<id>[^/?#&]+)',
|
||||||
|
rf'{NFBBaseIE._VALID_URL_BASE}/(?P<type>series?)/(?P<id>[^/?#&]+/s(?:ea|ai)son\d+/episode\d+)',
|
||||||
|
]
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
'note': 'NFB film',
|
||||||
'url': 'https://www.nfb.ca/film/trafficopter/',
|
'url': 'https://www.nfb.ca/film/trafficopter/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'trafficopter',
|
'id': 'trafficopter',
|
||||||
|
@ -14,29 +58,192 @@ class NFBIE(InfoExtractor):
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'uploader': 'Barrie Howells',
|
'uploader': 'Barrie Howells',
|
||||||
'release_year': 1972,
|
'release_year': 1972,
|
||||||
|
'duration': 600.0,
|
||||||
},
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'ONF film',
|
||||||
|
'url': 'https://www.onf.ca/film/mal-du-siecle/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'mal-du-siecle',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Le mal du siècle',
|
||||||
|
'description': 'md5:1abf774d77569ebe603419f2d344102b',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
'uploader': 'Catherine Lepage',
|
||||||
|
'release_year': 2019,
|
||||||
|
'duration': 300.0,
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'NFB episode with English title',
|
||||||
|
'url': 'https://www.nfb.ca/series/true-north-inside-the-rise-of-toronto-basketball/season1/episode9/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'true-north-episode9-true-north-finale-making-it',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'True North: Inside the Rise of Toronto Basketball - Finale: Making It',
|
||||||
|
'description': 'We catch up with each player in the midst of their journey as they reflect on their road ahead.',
|
||||||
|
'series': 'True North: Inside the Rise of Toronto Basketball',
|
||||||
|
'release_year': 2018,
|
||||||
|
'season': 'Season 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'Finale: Making It',
|
||||||
|
'episode_number': 9,
|
||||||
|
'uploader': 'Ryan Sidhoo',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'ONF episode with French title',
|
||||||
|
'url': 'https://www.onf.ca/serie/direction-nord-la-montee-du-basketball-a-toronto/saison1/episode9/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'direction-nord-episode-9',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Direction nord – La montée du basketball à Toronto - Finale : Réussir',
|
||||||
|
'description': 'md5:349a57419b71432b97bf6083d92b029d',
|
||||||
|
'series': 'Direction nord – La montée du basketball à Toronto',
|
||||||
|
'release_year': 2018,
|
||||||
|
'season': 'Saison 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'Finale : Réussir',
|
||||||
|
'episode_number': 9,
|
||||||
|
'uploader': 'Ryan Sidhoo',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'NFB episode with French title (needs geo-bypass)',
|
||||||
|
'url': 'https://www.nfb.ca/series/etoile-du-nord/saison1/episode1/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'etoile-du-nord-episode-1-lobservation',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Étoile du Nord - L\'observation',
|
||||||
|
'description': 'md5:161a4617260dee3de70f509b2c9dd21b',
|
||||||
|
'series': 'Étoile du Nord',
|
||||||
|
'release_year': 2023,
|
||||||
|
'season': 'Saison 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'L\'observation',
|
||||||
|
'episode_number': 1,
|
||||||
|
'uploader': 'Patrick Bossé',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'ONF episode with English title (needs geo-bypass)',
|
||||||
|
'url': 'https://www.onf.ca/serie/north-star/season1/episode1/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'north-star-episode-1-observation',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'North Star - Observation',
|
||||||
|
'description': 'md5:c727f370839d8a817392b9e3f23655c7',
|
||||||
|
'series': 'North Star',
|
||||||
|
'release_year': 2023,
|
||||||
|
'season': 'Season 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'Observation',
|
||||||
|
'episode_number': 1,
|
||||||
|
'uploader': 'Patrick Bossé',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'NFB episode with /film/ URL and English title (needs geo-bypass)',
|
||||||
|
'url': 'https://www.nfb.ca/film/north-star-episode-1-observation/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'north-star-episode-1-observation',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'North Star - Observation',
|
||||||
|
'description': 'md5:c727f370839d8a817392b9e3f23655c7',
|
||||||
|
'series': 'North Star',
|
||||||
|
'release_year': 2023,
|
||||||
|
'season': 'Season 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'Observation',
|
||||||
|
'episode_number': 1,
|
||||||
|
'uploader': 'Patrick Bossé',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'ONF episode with /film/ URL and French title (needs geo-bypass)',
|
||||||
|
'url': 'https://www.onf.ca/film/etoile-du-nord-episode-1-lobservation/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'etoile-du-nord-episode-1-lobservation',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Étoile du Nord - L\'observation',
|
||||||
|
'description': 'md5:161a4617260dee3de70f509b2c9dd21b',
|
||||||
|
'series': 'Étoile du Nord',
|
||||||
|
'release_year': 2023,
|
||||||
|
'season': 'Saison 1',
|
||||||
|
'season_number': 1,
|
||||||
|
'episode': 'L\'observation',
|
||||||
|
'episode_number': 1,
|
||||||
|
'uploader': 'Patrick Bossé',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'Season 2 episode w/o episode num in id, extract from json ld',
|
||||||
|
'url': 'https://www.onf.ca/film/liste-des-choses-qui-existent-saison-2-ours',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'liste-des-choses-qui-existent-saison-2-ours',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'La liste des choses qui existent - L\'ours en peluche',
|
||||||
|
'description': 'md5:d5e8d8fc5f3a7385a9cf0f509b37e28a',
|
||||||
|
'series': 'La liste des choses qui existent',
|
||||||
|
'release_year': 2022,
|
||||||
|
'season': 'Saison 2',
|
||||||
|
'season_number': 2,
|
||||||
|
'episode': 'L\'ours en peluche',
|
||||||
|
'episode_number': 12,
|
||||||
|
'uploader': 'Francis Papillon',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
|
}, {
|
||||||
|
'note': 'NFB film /embed/player/ page',
|
||||||
|
'url': 'https://www.nfb.ca/film/afterlife/embed/player/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'afterlife',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Afterlife',
|
||||||
|
'description': 'md5:84951394f594f1fb1e62d9c43242fdf5',
|
||||||
|
'release_year': 1978,
|
||||||
|
'duration': 420.0,
|
||||||
|
'uploader': 'Ishu Patel',
|
||||||
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
|
},
|
||||||
|
'params': {'skip_download': 'm3u8'},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
site, type_, slug = self._match_valid_url(url).group('site', 'type', 'id')
|
||||||
|
# Need to construct the URL since we match /embed/player/ URLs as well
|
||||||
|
webpage, urlh = self._download_webpage_handle(f'https://www.{site}.ca/{type_}/{slug}/', slug)
|
||||||
|
# type_ can change from film to serie(s) after redirect; new slug may have episode number
|
||||||
|
type_, slug = self._match_valid_url(urlh.url).group('type', 'id')
|
||||||
|
|
||||||
webpage = self._download_webpage('https://www.nfb.ca/film/%s/' % video_id, video_id)
|
embed_url = urljoin(f'https://www.{site}.ca', self._html_search_regex(
|
||||||
|
r'<[^>]+\bid=["\']player-iframe["\'][^>]*\bsrc=["\']([^"\']+)', webpage, 'embed url'))
|
||||||
|
video_id = self._match_id(embed_url) # embed url has unique slug
|
||||||
|
player = self._download_webpage(embed_url, video_id, 'Downloading player page')
|
||||||
|
if 'MESSAGE_GEOBLOCKED' in player:
|
||||||
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
|
||||||
|
|
||||||
iframe = self._html_search_regex(
|
formats, subtitles = self._extract_m3u8_formats_and_subtitles(
|
||||||
r'<[^>]+\bid=["\']player-iframe["\'][^>]*src=["\']([^"\']+)',
|
self._html_search_regex(r'source:\s*\'([^\']+)', player, 'm3u8 url'),
|
||||||
webpage, 'iframe', default=None, fatal=True)
|
video_id, 'mp4', m3u8_id='hls')
|
||||||
if iframe.startswith('/'):
|
|
||||||
iframe = f'https://www.nfb.ca{iframe}'
|
|
||||||
|
|
||||||
player = self._download_webpage(iframe, video_id)
|
if dv_source := self._html_search_regex(r'dvSource:\s*\'([^\']+)', player, 'dv', default=None):
|
||||||
|
fmts, subs = self._extract_m3u8_formats_and_subtitles(
|
||||||
|
dv_source, video_id, 'mp4', m3u8_id='dv', preference=-2, fatal=False)
|
||||||
|
for fmt in fmts:
|
||||||
|
fmt['format_note'] = 'described video'
|
||||||
|
formats.extend(fmts)
|
||||||
|
self._merge_subtitles(subs, target=subtitles)
|
||||||
|
|
||||||
source = self._html_search_regex(
|
info = {
|
||||||
r'source:\s*\'([^\']+)',
|
|
||||||
player, 'source', default=None, fatal=True)
|
|
||||||
|
|
||||||
formats, subtitles = self._extract_m3u8_formats_and_subtitles(source, video_id, ext='mp4')
|
|
||||||
|
|
||||||
return {
|
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': self._html_search_regex(
|
'title': self._html_search_regex(
|
||||||
r'<[^>]+\bid=["\']titleHeader["\'][^>]*>\s*<h1[^>]*>\s*([^<]+?)\s*</h1>',
|
r'<[^>]+\bid=["\']titleHeader["\'][^>]*>\s*<h1[^>]*>\s*([^<]+?)\s*</h1>',
|
||||||
|
@ -45,14 +252,49 @@ class NFBIE(InfoExtractor):
|
||||||
r'<[^>]+\bid=["\']tabSynopsis["\'][^>]*>\s*<p[^>]*>\s*([^<]+)',
|
r'<[^>]+\bid=["\']tabSynopsis["\'][^>]*>\s*<p[^>]*>\s*([^<]+)',
|
||||||
webpage, 'description', default=None),
|
webpage, 'description', default=None),
|
||||||
'thumbnail': self._html_search_regex(
|
'thumbnail': self._html_search_regex(
|
||||||
r'poster:\s*\'([^\']+)',
|
r'poster:\s*\'([^\']+)', player, 'thumbnail', default=None),
|
||||||
player, 'thumbnail', default=None),
|
|
||||||
'uploader': self._html_search_regex(
|
'uploader': self._html_search_regex(
|
||||||
r'<[^>]+\bitemprop=["\']name["\'][^>]*>([^<]+)',
|
r'<[^>]+\bitemprop=["\']name["\'][^>]*>([^<]+)', webpage, 'uploader', default=None),
|
||||||
webpage, 'uploader', default=None),
|
|
||||||
'release_year': int_or_none(self._html_search_regex(
|
'release_year': int_or_none(self._html_search_regex(
|
||||||
r'<[^>]+\bitemprop=["\']datePublished["\'][^>]*>([^<]+)',
|
r'<[^>]+\bitemprop=["\']datePublished["\'][^>]*>([^<]+)',
|
||||||
webpage, 'release_year', default=None)),
|
webpage, 'release_year', default=None)),
|
||||||
|
} if type_ == 'film' else self._extract_ep_info(self._extract_ep_data(webpage, video_id, slug), video_id)
|
||||||
|
|
||||||
|
return merge_dicts({
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
}
|
}, info, self._search_json_ld(webpage, video_id, default={}))
|
||||||
|
|
||||||
|
|
||||||
|
class NFBSeriesIE(NFBBaseIE):
|
||||||
|
IE_NAME = 'nfb:series'
|
||||||
|
IE_DESC = 'nfb.ca and onf.ca series'
|
||||||
|
_VALID_URL = rf'{NFBBaseIE._VALID_URL_BASE}/(?P<type>series?)/(?P<id>[^/?#&]+)/?(?:[?#]|$)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.nfb.ca/series/true-north-inside-the-rise-of-toronto-basketball/',
|
||||||
|
'playlist_mincount': 9,
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'true-north-inside-the-rise-of-toronto-basketball',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.onf.ca/serie/la-liste-des-choses-qui-existent-serie/',
|
||||||
|
'playlist_mincount': 26,
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'la-liste-des-choses-qui-existent-serie',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _entries(self, episodes):
|
||||||
|
for episode in traverse_obj(episodes, lambda _, v: NFBIE.suitable(v['embed_url'])):
|
||||||
|
mobj = NFBIE._match_valid_url(episode['embed_url'])
|
||||||
|
yield self.url_result(
|
||||||
|
mobj[0], NFBIE, **self._extract_ep_info([episode], mobj.group('id')))
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
site, type_, series_id = self._match_valid_url(url).group('site', 'type', 'id')
|
||||||
|
season_path = 'saison' if type_ == 'serie' else 'season'
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
f'https://www.{site}.ca/{type_}/{series_id}/{season_path}1/episode1', series_id)
|
||||||
|
episodes = self._extract_ep_data(webpage, series_id, fatal=True)
|
||||||
|
|
||||||
|
return self.playlist_result(self._entries(episodes), series_id)
|
||||||
|
|
225
yt_dlp/extractor/ninaprotocol.py
Normal file
225
yt_dlp/extractor/ninaprotocol.py
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import int_or_none, mimetype2ext, parse_iso8601, url_or_none
|
||||||
|
from ..utils.traversal import traverse_obj
|
||||||
|
|
||||||
|
|
||||||
|
class NinaProtocolIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?ninaprotocol\.com/releases/(?P<id>[^/#?]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://www.ninaprotocol.com/releases/3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ',
|
||||||
|
'title': 'The Spatulas - March Chant',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'channel': 'ppm',
|
||||||
|
'description': 'md5:bb9f9d39d8f786449cd5d0ff7c5772db',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'display_id': 'the-spatulas-march-chant',
|
||||||
|
'upload_date': '20231201',
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
},
|
||||||
|
'playlist': [{
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_1',
|
||||||
|
'title': 'March Chant In April',
|
||||||
|
'track': 'March Chant In April',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 152,
|
||||||
|
'track_number': 1,
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'channel': 'ppm',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'upload_date': '20231201',
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_2',
|
||||||
|
'title': 'Rescue Mission',
|
||||||
|
'track': 'Rescue Mission',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 212,
|
||||||
|
'track_number': 2,
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'channel': 'ppm',
|
||||||
|
'upload_date': '20231201',
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_3',
|
||||||
|
'title': 'Slinger Style',
|
||||||
|
'track': 'Slinger Style',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 179,
|
||||||
|
'track_number': 3,
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'upload_date': '20231201',
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'channel': 'ppm',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_4',
|
||||||
|
'title': 'Psychic Signal',
|
||||||
|
'track': 'Psychic Signal',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 220,
|
||||||
|
'track_number': 4,
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'upload_date': '20231201',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'channel': 'ppm',
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_5',
|
||||||
|
'title': 'Curvy Color',
|
||||||
|
'track': 'Curvy Color',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 148,
|
||||||
|
'track_number': 5,
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
'channel': 'ppm',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'upload_date': '20231201',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3SvsMM3y4oTPZ5DXFJnLkCAqkxz34hjzFxqms1vu9XBJ_6',
|
||||||
|
'title': 'Caveman Star',
|
||||||
|
'track': 'Caveman Star',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'duration': 121,
|
||||||
|
'track_number': 6,
|
||||||
|
'channel_id': '4ceG4zsb7VVxBTGPtZMqDZWGHo3VUg2xRvzC2b17ymWP',
|
||||||
|
'thumbnail': 'https://www.arweave.net/VyZA6CBeUuqP174khvSrD44Eosi3MLVyWN42uaQKg50',
|
||||||
|
'tags': ['punk', 'postpresentmedium', 'cambridge'],
|
||||||
|
'album_artist': 'Post Present Medium ',
|
||||||
|
'uploader': 'ppmrecs',
|
||||||
|
'timestamp': 1701417610,
|
||||||
|
'uploader_id': '2bGjgdKUddJoj2shYGqfNcUfoSoABP21RJoiwGMZDq3A',
|
||||||
|
'album': 'The Spatulas - March Chant',
|
||||||
|
'channel': 'ppm',
|
||||||
|
'upload_date': '20231201',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.ninaprotocol.com/releases/f-g-s-american-shield',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '76PZnJwaMgViQHYfA4NYJXds7CmW6vHQKAtQUxGene6J',
|
||||||
|
'description': 'md5:63f08d5db558b4b36e1896f317062721',
|
||||||
|
'title': 'F.G.S. - American Shield',
|
||||||
|
'uploader_id': 'Ej3rozs11wYqFk1Gs6oggGCkGLz8GzBhmJfnUxf6gPci',
|
||||||
|
'channel_id': '6JuksCZPXuP16wJ1BUfwuukJzh42C7guhLrFPPkVJfyE',
|
||||||
|
'channel': 'tinkscough',
|
||||||
|
'tags': [],
|
||||||
|
'album_artist': 'F.G.S.',
|
||||||
|
'album': 'F.G.S. - American Shield',
|
||||||
|
'thumbnail': 'https://www.arweave.net/YJpgImkXLT9SbpFb576KuZ5pm6bdvs452LMs3Rx6lm8',
|
||||||
|
'display_id': 'f-g-s-american-shield',
|
||||||
|
'uploader': 'flannerysilva',
|
||||||
|
'timestamp': 1702395858,
|
||||||
|
'upload_date': '20231212',
|
||||||
|
},
|
||||||
|
'playlist_count': 1,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.ninaprotocol.com/releases/time-to-figure-things-out',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6Zi1nC5hj6b13NkpxVYwRhFy6mYA7oLBbe9DMrgGDcYh',
|
||||||
|
'display_id': 'time-to-figure-things-out',
|
||||||
|
'description': 'md5:960202ed01c3134bb8958f1008527e35',
|
||||||
|
'timestamp': 1706283607,
|
||||||
|
'title': 'DJ STEPDAD - time to figure things out',
|
||||||
|
'album_artist': 'DJ STEPDAD',
|
||||||
|
'uploader': 'tddvsss',
|
||||||
|
'upload_date': '20240126',
|
||||||
|
'album': 'time to figure things out',
|
||||||
|
'uploader_id': 'AXQNRgTyYsySyAMFDwxzumuGjfmoXshorCesjpquwCBi',
|
||||||
|
'thumbnail': 'https://www.arweave.net/O4i8bcKVqJVZvNeHHFp6r8knpFGh9ZwEgbeYacr4nss',
|
||||||
|
'tags': [],
|
||||||
|
},
|
||||||
|
'playlist_count': 4,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
release = self._download_json(
|
||||||
|
f'https://api.ninaprotocol.com/v1/releases/{video_id}', video_id)['release']
|
||||||
|
|
||||||
|
video_id = release.get('publicKey') or video_id
|
||||||
|
|
||||||
|
common_info = traverse_obj(release, {
|
||||||
|
'album': ('metadata', 'properties', 'title', {str}),
|
||||||
|
'album_artist': ((('hub', 'data'), 'publisherAccount'), 'displayName', {str}),
|
||||||
|
'timestamp': ('datetime', {parse_iso8601}),
|
||||||
|
'thumbnail': ('metadata', 'image', {url_or_none}),
|
||||||
|
'uploader': ('publisherAccount', 'handle', {str}),
|
||||||
|
'uploader_id': ('publisherAccount', 'publicKey', {str}),
|
||||||
|
'channel': ('hub', 'handle', {str}),
|
||||||
|
'channel_id': ('hub', 'publicKey', {str}),
|
||||||
|
}, get_all=False)
|
||||||
|
common_info['tags'] = traverse_obj(release, ('metadata', 'properties', 'tags', ..., {str}))
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for track_num, track in enumerate(traverse_obj(release, (
|
||||||
|
'metadata', 'properties', 'files', lambda _, v: url_or_none(v['uri']))), 1):
|
||||||
|
entries.append({
|
||||||
|
'id': f'{video_id}_{track_num}',
|
||||||
|
'url': track['uri'],
|
||||||
|
**traverse_obj(track, {
|
||||||
|
'title': ('track_title', {str}),
|
||||||
|
'track': ('track_title', {str}),
|
||||||
|
'ext': ('type', {mimetype2ext}),
|
||||||
|
'track_number': ('track', {int_or_none}),
|
||||||
|
'duration': ('duration', {int_or_none}),
|
||||||
|
}),
|
||||||
|
'vcodec': 'none',
|
||||||
|
**common_info,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'playlist',
|
||||||
|
'id': video_id,
|
||||||
|
'entries': entries,
|
||||||
|
**traverse_obj(release, {
|
||||||
|
'display_id': ('slug', {str}),
|
||||||
|
'title': ('metadata', 'name', {str}),
|
||||||
|
'description': ('metadata', 'description', {str}),
|
||||||
|
}),
|
||||||
|
**common_info,
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import parse_iso8601, traverse_obj, try_call
|
from ..utils import float_or_none, parse_iso8601, str_or_none, try_call
|
||||||
|
from ..utils.traversal import traverse_obj
|
||||||
|
|
||||||
|
|
||||||
class PrankCastIE(InfoExtractor):
|
class PrankCastIE(InfoExtractor):
|
||||||
|
@ -64,3 +67,71 @@ class PrankCastIE(InfoExtractor):
|
||||||
'categories': [json_info.get('broadcast_category')],
|
'categories': [json_info.get('broadcast_category')],
|
||||||
'tags': try_call(lambda: json_info['broadcast_tags'].split(','))
|
'tags': try_call(lambda: json_info['broadcast_tags'].split(','))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PrankCastPostIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?prankcast\.com/[^/?#]+/posts/(?P<id>\d+)-(?P<display_id>[^/?#]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://prankcast.com/devonanustart/posts/6214-happy-national-rachel-day-',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6214',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': 'Happy National Rachel Day!',
|
||||||
|
'display_id': 'happy-national-rachel-day-',
|
||||||
|
'timestamp': 1704333938,
|
||||||
|
'uploader': 'Devonanustart',
|
||||||
|
'channel_id': '4',
|
||||||
|
'duration': 13175,
|
||||||
|
'cast': ['Devonanustart'],
|
||||||
|
'description': '',
|
||||||
|
'categories': ['prank call'],
|
||||||
|
'upload_date': '20240104'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'https://prankcast.com/despicabledogs/posts/6217-jake-the-work-crow-',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '6217',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': 'Jake the Work Crow!',
|
||||||
|
'display_id': 'jake-the-work-crow-',
|
||||||
|
'timestamp': 1704346592,
|
||||||
|
'uploader': 'despicabledogs',
|
||||||
|
'channel_id': '957',
|
||||||
|
'duration': 263.287,
|
||||||
|
'cast': ['despicabledogs'],
|
||||||
|
'description': 'https://imgur.com/a/vtxLvKU',
|
||||||
|
'categories': [],
|
||||||
|
'upload_date': '20240104'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id, display_id = self._match_valid_url(url).group('id', 'display_id')
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
post = self._search_nextjs_data(webpage, video_id)['props']['pageProps']['ssr_data_posts']
|
||||||
|
content = self._parse_json(post['post_contents_json'], video_id)[0]
|
||||||
|
|
||||||
|
uploader = post.get('user_name')
|
||||||
|
guests_json = traverse_obj(content, ('guests_json', {json.loads}, {dict})) or {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': post.get('post_title') or self._og_search_title(webpage),
|
||||||
|
'display_id': display_id,
|
||||||
|
'url': content.get('url'),
|
||||||
|
'timestamp': parse_iso8601(content.get('start_date') or content.get('crdate'), ' '),
|
||||||
|
'uploader': uploader,
|
||||||
|
'channel_id': str_or_none(post.get('user_id')),
|
||||||
|
'duration': float_or_none(content.get('duration')),
|
||||||
|
'cast': list(filter(None, [uploader] + traverse_obj(guests_json, (..., 'name')))),
|
||||||
|
'description': post.get('post_body'),
|
||||||
|
'categories': list(filter(None, [content.get('category')])),
|
||||||
|
'tags': try_call(lambda: list(filter('', post['post_tags'].split(',')))),
|
||||||
|
'subtitles': {
|
||||||
|
'live_chat': [{
|
||||||
|
'url': f'https://prankcast.com/api/private/chat/select-broadcast?id={post["content_id"]}&cache=',
|
||||||
|
'ext': 'json',
|
||||||
|
}],
|
||||||
|
} if post.get('content_id') else None
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ from ..utils import (
|
||||||
|
|
||||||
|
|
||||||
class RedTubeIE(InfoExtractor):
|
class RedTubeIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:(?:\w+\.)?redtube\.com/|embed\.redtube\.com/\?.*?\bid=)(?P<id>[0-9]+)'
|
_VALID_URL = r'https?://(?:(?:\w+\.)?redtube\.com(?:\.br)?/|embed\.redtube\.com/\?.*?\bid=)(?P<id>[0-9]+)'
|
||||||
_EMBED_REGEX = [r'<iframe[^>]+?src=["\'](?P<url>(?:https?:)?//embed\.redtube\.com/\?.*?\bid=\d+)']
|
_EMBED_REGEX = [r'<iframe[^>]+?src=["\'](?P<url>(?:https?:)?//embed\.redtube\.com/\?.*?\bid=\d+)']
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.redtube.com/38864951',
|
'url': 'https://www.redtube.com/38864951',
|
||||||
|
@ -35,6 +35,9 @@ class RedTubeIE(InfoExtractor):
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://it.redtube.com/66418',
|
'url': 'http://it.redtube.com/66418',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.redtube.com.br/103224331',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user