From 24856538595a3b25c75e1199146fcc82ea812d97 Mon Sep 17 00:00:00 2001 From: Sipherdrakon <64430430+Sipherdrakon@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:46:59 -0500 Subject: [PATCH] [ie/aenetworks] Fix extractor (#14959) Closes #14578 Authored by: Sipherdrakon --- yt_dlp/extractor/aenetworks.py | 79 ++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/yt_dlp/extractor/aenetworks.py b/yt_dlp/extractor/aenetworks.py index a4a5f409ec..5f99ee99e3 100644 --- a/yt_dlp/extractor/aenetworks.py +++ b/yt_dlp/extractor/aenetworks.py @@ -5,10 +5,12 @@ from ..utils import ( ExtractorError, GeoRestrictedError, int_or_none, + make_archive_id, remove_start, - traverse_obj, update_url_query, + url_or_none, ) +from ..utils.traversal import traverse_obj class AENetworksBaseIE(ThePlatformIE): # XXX: Do not subclass from concrete IE @@ -29,6 +31,19 @@ class AENetworksBaseIE(ThePlatformIE): # XXX: Do not subclass from concrete IE 'historyvault.com': (None, 'historyvault', None), 'biography.com': (None, 'biography', None), } + _GRAPHQL_QUERY = ''' + query getUserVideo($videoId: ID!) { + video(id: $videoId) { + title + publicUrl + programId + tvSeasonNumber + tvSeasonEpisodeNumber + series { + title + } + } + }''' def _extract_aen_smil(self, smil_url, video_id, auth=None): query = { @@ -73,19 +88,39 @@ class AENetworksBaseIE(ThePlatformIE): # XXX: Do not subclass from concrete IE def _extract_aetn_info(self, domain, filter_key, filter_value, url): requestor_id, brand, software_statement = self._DOMAIN_MAP[domain] + if filter_key == 'canonical': + webpage = self._download_webpage(url, filter_value) + graphql_video_id = self._search_regex( + r']+\bcontent="[^"]*\btpid/(\d+)"', webpage, + 'id') or self._html_search_meta('videoId', webpage, 'GraphQL video ID', fatal=True) + else: + graphql_video_id = filter_value + result = self._download_json( - f'https://feeds.video.aetnd.com/api/v2/{brand}/videos', - filter_value, query={f'filter[{filter_key}]': filter_value}) - result = traverse_obj( - result, ('results', - lambda k, v: k == 0 and v[filter_key] == filter_value), - get_all=False) - if not result: + 'https://yoga.appsvcs.aetnd.com/', graphql_video_id, + query={ + 'brand': brand, + 'mode': 'live', + 'platform': 'web', + }, + data=json.dumps({ + 'operationName': 'getUserVideo', + 'variables': { + 'videoId': graphql_video_id, + }, + 'query': self._GRAPHQL_QUERY, + }).encode(), + headers={ + 'Content-Type': 'application/json', + }) + + result = traverse_obj(result, ('data', 'video', {dict})) + media_url = traverse_obj(result, ('publicUrl', {url_or_none})) + if not media_url: raise ExtractorError('Show not found in A&E feed (too new?)', expected=True, video_id=remove_start(filter_value, '/')) title = result['title'] - video_id = result['id'] - media_url = result['publicUrl'] + video_id = result['programId'] theplatform_metadata = self._download_theplatform_metadata(self._search_regex( r'https?://link\.theplatform\.com/s/([^?]+)', media_url, 'theplatform_path'), video_id) info = self._parse_theplatform_metadata(theplatform_metadata) @@ -100,9 +135,13 @@ class AENetworksBaseIE(ThePlatformIE): # XXX: Do not subclass from concrete IE info.update(self._extract_aen_smil(media_url, video_id, auth)) info.update({ 'title': title, - 'series': result.get('seriesName'), - 'season_number': int_or_none(result.get('tvSeasonNumber')), - 'episode_number': int_or_none(result.get('tvSeasonEpisodeNumber')), + 'display_id': graphql_video_id, + '_old_archive_ids': [make_archive_id(self, graphql_video_id)], + **traverse_obj(result, { + 'series': ('series', 'title', {str}), + 'season_number': ('tvSeasonNumber', {int_or_none}), + 'episode_number': ('tvSeasonEpisodeNumber', {int_or_none}), + }), }) return info @@ -116,7 +155,7 @@ class AENetworksIE(AENetworksBaseIE): (?:shows/[^/?#]+/)?videos/[^/?#]+ )''' _TESTS = [{ - 'url': 'http://www.history.com/shows/mountain-men/season-1/episode-1', + 'url': 'https://www.history.com/shows/mountain-men/season-1/episode-1', 'info_dict': { 'id': '22253814', 'ext': 'mp4', @@ -139,11 +178,11 @@ class AENetworksIE(AENetworksBaseIE): }, 'params': {'skip_download': 'm3u8'}, 'add_ie': ['ThePlatform'], - 'skip': 'Geo-restricted - This content is not available in your location.', + 'skip': 'This content requires a valid, unexpired auth token', }, { - 'url': 'http://www.aetv.com/shows/duck-dynasty/season-9/episode-1', + 'url': 'https://www.aetv.com/shows/duck-dynasty/season-9/episode-1', 'info_dict': { - 'id': '600587331957', + 'id': '147486', 'ext': 'mp4', 'title': 'Inlawful Entry', 'description': 'md5:57c12115a2b384d883fe64ca50529e08', @@ -160,6 +199,8 @@ class AENetworksIE(AENetworksBaseIE): 'season_number': 9, 'series': 'Duck Dynasty', 'age_limit': 0, + 'display_id': '600587331957', + '_old_archive_ids': ['aenetworks 600587331957'], }, 'params': {'skip_download': 'm3u8'}, 'add_ie': ['ThePlatform'], @@ -186,6 +227,7 @@ class AENetworksIE(AENetworksBaseIE): }, 'params': {'skip_download': 'm3u8'}, 'add_ie': ['ThePlatform'], + 'skip': '404 Not Found', }, { 'url': 'https://www.aetv.com/specials/hunting-jonbenets-killer-the-untold-story', 'info_dict': { @@ -209,6 +251,7 @@ class AENetworksIE(AENetworksBaseIE): }, 'params': {'skip_download': 'm3u8'}, 'add_ie': ['ThePlatform'], + 'skip': 'This content requires a valid, unexpired auth token', }, { 'url': 'http://www.fyi.tv/shows/tiny-house-nation/season-1/episode-8', 'only_matching': True, @@ -259,7 +302,7 @@ class AENetworksListBaseIE(AENetworksBaseIE): domain, slug = self._match_valid_url(url).groups() _, brand, _ = self._DOMAIN_MAP[domain] playlist = self._call_api(self._RESOURCE, slug, brand, self._FIELDS) - base_url = f'http://watch.{domain}' + base_url = f'https://watch.{domain}' entries = [] for item in (playlist.get(self._ITEMS_KEY) or []):