diff --git a/yt_dlp/extractor/adultswim.py b/yt_dlp/extractor/adultswim.py index 2c83701e78..a399c3ae36 100644 --- a/yt_dlp/extractor/adultswim.py +++ b/yt_dlp/extractor/adultswim.py @@ -84,6 +84,8 @@ class AdultSwimIE(TurnerBaseIE): 'skip': '404 Not Found', }] + _SOFTWARE_STATEMENT = 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwNjg5ZmU2My00OTc5LTQxZmQtYWYxNC1hYjVlNmJjNWVkZWIiLCJuYmYiOjE1MzcxOTA2NzQsImlzcyI6ImF1dGguYWRvYmUuY29tIiwiaWF0IjoxNTM3MTkwNjc0fQ.Xl3AEduM0s1TxDQ6-XssdKIiLm261hhsEv1C1yo_nitIajZThSI9rXILqtIzO0aujoHhdzUnu_dUCq9ffiSBzEG632tTa1la-5tegHtce80cMhewBN4n2t8n9O5tiaPx8MPY8ALdm5wS7QzWE6DO_LTJKgE8Bl7Yv-CWJT4q4SywtNiQWLVOuhBRnDyfsRezxRwptw8qTn9dv5ZzUrVJaby5fDZ_nOncMKvegOgaKd5KEuCAGQ-mg-PSuValMjGuf6FwDguGaK7IyI5Y2oOrzXmD4Dj7q4WBg8w9QoZhtLeAU56mcsGILolku2R5FHlVLO9xhjResyt-pfmegOkpSw' + def _real_extract(self, url): show_path, episode_path = self._match_valid_url(url).groups() display_id = episode_path or show_path @@ -152,7 +154,7 @@ class AdultSwimIE(TurnerBaseIE): # CDN_TOKEN_APP_ID from: # https://d2gg02c3xr550i.cloudfront.net/assets/asvp.e9c8bef24322d060ef87.bundle.js 'appId': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6ImFzLXR2ZS1kZXNrdG9wLXB0enQ2bSIsInByb2R1Y3QiOiJ0dmUiLCJuZXR3b3JrIjoiYXMiLCJwbGF0Zm9ybSI6ImRlc2t0b3AiLCJpYXQiOjE1MzI3MDIyNzl9.BzSCk-WYOZ2GMCIaeVb8zWnzhlgnXuJTCu0jGp_VaZE', - }, { + }, self._SOFTWARE_STATEMENT, { 'url': url, 'site_name': 'AdultSwim', 'auth_required': auth, diff --git a/yt_dlp/extractor/nba.py b/yt_dlp/extractor/nba.py index 91ae1d14c6..c87046d8ef 100644 --- a/yt_dlp/extractor/nba.py +++ b/yt_dlp/extractor/nba.py @@ -19,7 +19,8 @@ from ..utils import ( class NBACVPBaseIE(TurnerBaseIE): def _extract_nba_cvp_info(self, path, video_id, fatal=False): return self._extract_cvp_info( - f'http://secure.nba.com/{path}', video_id, { + # XXX: The 3rd argument (None) needs to be the AdobePass software_statement + f'http://secure.nba.com/{path}', video_id, None, { 'default': { 'media_src': 'http://nba.cdn.turner.com/nba/big', }, @@ -94,6 +95,7 @@ class NBAWatchBaseIE(NBACVPBaseIE): class NBAWatchEmbedIE(NBAWatchBaseIE): + _WORKING = False IE_NAME = 'nba:watch:embed' _VALID_URL = NBAWatchBaseIE._VALID_URL_BASE + r'embed\?.*?\bid=(?P\d+)' _TESTS = [{ @@ -115,6 +117,7 @@ class NBAWatchEmbedIE(NBAWatchBaseIE): class NBAWatchIE(NBAWatchBaseIE): + _WORKING = False IE_NAME = 'nba:watch' _VALID_URL = NBAWatchBaseIE._VALID_URL_BASE + r'(?:nba/)?video/(?P.+?(?=/index\.html)|(?:[^/]+/)*[^/?#&]+)' _TESTS = [{ @@ -167,6 +170,7 @@ class NBAWatchIE(NBAWatchBaseIE): class NBAWatchCollectionIE(NBAWatchBaseIE): + _WORKING = False IE_NAME = 'nba:watch:collection' _VALID_URL = NBAWatchBaseIE._VALID_URL_BASE + r'list/collection/(?P[^/?#&]+)' _TESTS = [{ @@ -336,6 +340,7 @@ class NBABaseIE(NBACVPBaseIE): class NBAEmbedIE(NBABaseIE): + _WORKING = False IE_NAME = 'nba:embed' _VALID_URL = r'https?://secure\.nba\.com/assets/amp/include/video/(?:topI|i)frame\.html\?.*?\bcontentId=(?P[^?#&]+)' _TESTS = [{ @@ -358,6 +363,7 @@ class NBAEmbedIE(NBABaseIE): class NBAIE(NBABaseIE): + _WORKING = False IE_NAME = 'nba' _VALID_URL = NBABaseIE._VALID_URL_BASE + f'(?!{NBABaseIE._CHANNEL_PATH_REGEX})video/(?P(?:[^/]+/)*[^/?#&]+)' _TESTS = [{ @@ -385,6 +391,7 @@ class NBAIE(NBABaseIE): class NBAChannelIE(NBABaseIE): + _WORKING = False IE_NAME = 'nba:channel' _VALID_URL = NBABaseIE._VALID_URL_BASE + f'(?:{NBABaseIE._CHANNEL_PATH_REGEX})/(?P[^/?#&]+)' _TESTS = [{ diff --git a/yt_dlp/extractor/tbs.py b/yt_dlp/extractor/tbs.py index 9b9aa50d37..80534731e1 100644 --- a/yt_dlp/extractor/tbs.py +++ b/yt_dlp/extractor/tbs.py @@ -32,6 +32,10 @@ class TBSIE(TurnerBaseIE): 'url': 'http://www.tntdrama.com/movies/star-wars-a-new-hope', 'only_matching': True, }] + _SOFTWARE_STATEMENT_MAP = { + 'tbs': 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJkZTA0NTYxZS1iMTFhLTRlYTgtYTg5NC01NjI3MGM1NmM2MWIiLCJuYmYiOjE1MzcxODkzOTAsImlzcyI6ImF1dGguYWRvYmUuY29tIiwiaWF0IjoxNTM3MTg5MzkwfQ.Z7ny66kaqNDdCHf9Y9KsV12LrBxrLkGGxlYe2XGm6qsw2T-k1OCKC1TMzeqiZP735292MMRAQkcJDKrMIzNbAuf9nCdIcv4kE1E2nqUnjPMBduC1bHffZp8zlllyrN2ElDwM8Vhwv_5nElLRwWGEt0Kaq6KJAMZA__WDxKWC18T-wVtsOZWXQpDqO7nByhfj2t-Z8c3TUNVsA_wHgNXlkzJCZ16F2b7yGLT5ZhLPupOScd3MXC5iPh19HSVIok22h8_F_noTmGzmMnIRQi6bWYWK2zC7TQ_MsYHfv7V6EaG5m1RKZTV6JAwwoJQF_9ByzarLV1DGwZxD9-eQdqswvg', + 'tntdrama': 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwOTMxYTU4OS1jZjEzLTRmNjMtYTJmYy03MzhjMjE1NWU5NjEiLCJuYmYiOjE1MzcxOTA4MjcsImlzcyI6ImF1dGguYWRvYmUuY29tIiwiaWF0IjoxNTM3MTkwODI3fQ.AucKvtws7oekTXi80_zX4-BlgJD9GLvlOI9FlBCjdlx7Pa3eJ0AqbogynKMiatMbnLOTMHGjd7tTiq422unmZjBz70dhePAe9BbW0dIo7oQ57vZ-VBYw_tWYRPmON61MwAbLVlqROD3n_zURs85S8TlkQx9aNx9x_riGGELjd8l05CVa_pOluNhYvuIFn6wmrASOKI1hNEblBDWh468UWP571-fe4zzi0rlYeeHd-cjvtWvOB3bQsWrUVbK4pRmqvzEH59j0vNF-ihJF9HncmUicYONe47Mib3elfMok23v4dB1_UAlQY_oawfNcynmEnJQCcqFmbHdEwTW6gMiYsA', + } def _real_extract(self, url): site, path, display_id = self._match_valid_url(url).groups() @@ -48,7 +52,7 @@ class TBSIE(TurnerBaseIE): drupal_settings['ngtv_token_url']).query) info = self._extract_ngtv_info( - media_id, tokenizer_query, { + media_id, tokenizer_query, self._SOFTWARE_STATEMENT_MAP[site], { 'url': url, 'site_name': site[:3].upper(), 'auth_required': video_data.get('authRequired') == '1' or is_live, diff --git a/yt_dlp/extractor/teamcoco.py b/yt_dlp/extractor/teamcoco.py index a94ff9b332..ae8fd7a442 100644 --- a/yt_dlp/extractor/teamcoco.py +++ b/yt_dlp/extractor/teamcoco.py @@ -156,6 +156,7 @@ class TeamcocoIE(TeamcocoBaseIE): class ConanClassicIE(TeamcocoBaseIE): + _WORKING = False _VALID_URL = r'https?://(?:(?:www\.)?conanclassic|conan25\.teamcoco)\.com/(?P([^/]+/)*[^/?#]+)' _TESTS = [{ 'url': 'https://conanclassic.com/video/ice-cube-kevin-hart-conan-share-lyft', @@ -263,7 +264,7 @@ class ConanClassicIE(TeamcocoBaseIE): info.update(self._extract_ngtv_info(media_id, { 'accessToken': token, 'accessTokenType': 'jws', - })) + }, None)) # TODO: the None arg needs to be the AdobePass software_statement else: formats, subtitles = self._get_formats_and_subtitles( traverse_obj(response, ('data', 'findRecordVideoMetadata')), video_id) diff --git a/yt_dlp/extractor/trutv.py b/yt_dlp/extractor/trutv.py index cbfe67af25..c1d0cb0d14 100644 --- a/yt_dlp/extractor/trutv.py +++ b/yt_dlp/extractor/trutv.py @@ -20,6 +20,7 @@ class TruTVIE(TurnerBaseIE): 'skip_download': True, }, } + _SOFTWARE_STATEMENT = 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhYzQyOTkwMi0xMDYzLTQyNTQtYWJlYS1iZTY2ODM4MTVmZGIiLCJuYmYiOjE1MzcxOTA4NjgsImlzcyI6ImF1dGguYWRvYmUuY29tIiwiaWF0IjoxNTM3MTkwODY4fQ.ewXl5LDMDvvx3nDXV4jCdSwUq_sOluKoOVsIjznAo6Zo4zrGe9rjlZ9DOmQKW66g6VRMexJsJ5vM1EkY8TC5-YcQw_BclK1FPGO1rH3Wf7tX_l0b1BVbSJQKIj9UgqDp_QbGcBXz24kN4So3U22mhs6di9PYyyfG68ccKL2iRprcVKWCslIHwUF-T7FaEqb0K57auilxeW1PONG2m-lIAcZ62DUwqXDWvw0CRoWI08aVVqkkhnXaSsQfLs5Ph1Pfh9Oq3g_epUm9Ss45mq6XM7gbOb5omTcKLADRKK-PJVB_JXnZnlsXbG0ttKE1cTKJ738qu7j4aipYTf-W0nKF5Q' def _real_extract(self, url): series_slug, clip_slug, video_id = self._match_valid_url(url).groups() @@ -39,7 +40,7 @@ class TruTVIE(TurnerBaseIE): title = video_data['title'].strip() info = self._extract_ngtv_info( - media_id, {}, { + media_id, {}, self._SOFTWARE_STATEMENT, { 'url': url, 'site_name': 'truTV', 'auth_required': video_data.get('isAuthRequired'), diff --git a/yt_dlp/extractor/turner.py b/yt_dlp/extractor/turner.py index 8b79a8ba9a..4493705e99 100644 --- a/yt_dlp/extractor/turner.py +++ b/yt_dlp/extractor/turner.py @@ -22,7 +22,7 @@ class TurnerBaseIE(AdobePassIE): def _extract_timestamp(self, video_data): return int_or_none(xpath_attr(video_data, 'dateCreated', 'uts')) - def _add_akamai_spe_token(self, tokenizer_src, video_url, content_id, ap_data, custom_tokenizer_query=None): + def _add_akamai_spe_token(self, tokenizer_src, video_url, content_id, ap_data, software_statement, custom_tokenizer_query=None): secure_path = self._search_regex(r'https?://[^/]+(.+/)', video_url, 'secure path') + '*' token = self._AKAMAI_SPE_TOKEN_CACHE.get(secure_path) if not token: @@ -34,7 +34,8 @@ class TurnerBaseIE(AdobePassIE): else: query['videoId'] = content_id if ap_data.get('auth_required'): - query['accessToken'] = self._extract_mvpd_auth(ap_data['url'], content_id, ap_data['site_name'], ap_data['site_name']) + query['accessToken'] = self._extract_mvpd_auth( + ap_data['url'], content_id, ap_data['site_name'], ap_data['site_name'], software_statement) auth = self._download_xml( tokenizer_src, content_id, query=query) error_msg = xpath_text(auth, 'error/msg') @@ -46,7 +47,7 @@ class TurnerBaseIE(AdobePassIE): self._AKAMAI_SPE_TOKEN_CACHE[secure_path] = token return video_url + '?hdnea=' + token - def _extract_cvp_info(self, data_src, video_id, path_data={}, ap_data={}, fatal=False): + def _extract_cvp_info(self, data_src, video_id, software_statement, path_data={}, ap_data={}, fatal=False): video_data = self._download_xml( data_src, video_id, transform_source=lambda s: fix_xml_ampersands(s).strip(), @@ -101,7 +102,7 @@ class TurnerBaseIE(AdobePassIE): video_url = self._add_akamai_spe_token( secure_path_data['tokenizer_src'], secure_path_data['media_src'] + video_url, - content_id, ap_data) + content_id, ap_data, software_statement) elif not re.match('https?://', video_url): base_path_data = path_data.get(ext, path_data.get('default', {})) media_src = base_path_data.get('media_src') @@ -215,10 +216,12 @@ class TurnerBaseIE(AdobePassIE): 'is_live': is_live, } - def _extract_ngtv_info(self, media_id, tokenizer_query, ap_data=None): + def _extract_ngtv_info(self, media_id, tokenizer_query, software_statement, ap_data=None): + if not isinstance(ap_data, dict): + ap_data = {} is_live = ap_data.get('is_live') streams_data = self._download_json( - f'http://medium.ngtv.io/media/{media_id}/tv', + f'https://medium.ngtv.io/media/{media_id}/tv', media_id)['media']['tv'] duration = None chapters = [] @@ -230,8 +233,8 @@ class TurnerBaseIE(AdobePassIE): continue if stream_data.get('playlistProtection') == 'spe': m3u8_url = self._add_akamai_spe_token( - 'http://token.ngtv.io/token/token_spe', - m3u8_url, media_id, ap_data or {}, tokenizer_query) + 'https://token.ngtv.io/token/token_spe', + m3u8_url, media_id, ap_data, software_statement, tokenizer_query) formats.extend(self._extract_m3u8_formats( m3u8_url, media_id, 'mp4', m3u8_id='hls', live=is_live, fatal=False))