mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2026-06-23 11:14:53 +00:00
cache api token to memory, refactor/simplify token code
Authored by: bashonly
This commit is contained in:
parent
6557fc538e
commit
10cd64f82e
@ -7,6 +7,7 @@ from .common import InfoExtractor
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
|
filter_dict,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
join_nonempty,
|
join_nonempty,
|
||||||
@ -25,12 +26,28 @@ from ..utils.traversal import traverse_obj
|
|||||||
|
|
||||||
class ZDFBaseIE(InfoExtractor):
|
class ZDFBaseIE(InfoExtractor):
|
||||||
_GEO_COUNTRIES = ['DE']
|
_GEO_COUNTRIES = ['DE']
|
||||||
|
_TOKEN_CACHE_PARAMS = ('zdf', 'api-token')
|
||||||
|
_token_cache = {}
|
||||||
|
|
||||||
|
def _get_api_token(self):
|
||||||
|
# As of 2025-03, this API is used by the Android app for getting tokens.
|
||||||
|
# An equivalent token could be extracted from the webpage should the API become unavailable.
|
||||||
|
# For now this allows the extractor to avoid dealing with Next.js hydration data.
|
||||||
|
if not self._token_cache:
|
||||||
|
self._token_cache.update(self.cache.load(*self._TOKEN_CACHE_PARAMS, default={}))
|
||||||
|
|
||||||
|
if traverse_obj(self._token_cache, ('expires', {int_or_none}), default=0) < int(time.time()):
|
||||||
|
self._token_cache.update(self._download_json(
|
||||||
|
'https://zdf-prod-futura.zdf.de/mediathekV2/token', None,
|
||||||
|
'Downloading API token', 'Failed to download API token'))
|
||||||
|
self.cache.store(*self._TOKEN_CACHE_PARAMS, self._token_cache)
|
||||||
|
|
||||||
|
return f'{self._token_cache["type"]} {self._token_cache["token"]}'
|
||||||
|
|
||||||
def _call_api(self, url, video_id, item, api_token=None):
|
def _call_api(self, url, video_id, item, api_token=None):
|
||||||
headers = {'Api-Auth': api_token} if api_token else {}
|
|
||||||
return self._download_json(
|
return self._download_json(
|
||||||
url, video_id, note=f'Downloading {item}',
|
url, video_id, f'Downloading {item}', f'Failed to download {item}',
|
||||||
errnote=f'Failed to download {item}', headers=headers)
|
headers=filter_dict({'Api-Auth': api_token}))
|
||||||
|
|
||||||
def _parse_aspect_ratio(self, aspect_ratio):
|
def _parse_aspect_ratio(self, aspect_ratio):
|
||||||
if not aspect_ratio or not isinstance(aspect_ratio, str):
|
if not aspect_ratio or not isinstance(aspect_ratio, str):
|
||||||
@ -128,20 +145,6 @@ class ZDFBaseIE(InfoExtractor):
|
|||||||
'subtitles': self._extract_subtitles(src_captions),
|
'subtitles': self._extract_subtitles(src_captions),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_api_token(self, video_id):
|
|
||||||
# As of 2025-03, this API is used by the Android app for getting tokens.
|
|
||||||
# An equivalent token could be extracted from the webpage should the API become unavailable.
|
|
||||||
# For now this allows the extractor to avoid dealing with Next.js hydration data.
|
|
||||||
TOKEN_CACHE_SECTION = 'zdf'
|
|
||||||
TOKEN_CACHE_KEY = 'api-token'
|
|
||||||
token_data = self.cache.load(TOKEN_CACHE_SECTION, TOKEN_CACHE_KEY)
|
|
||||||
if traverse_obj(token_data, ('expires', {int_or_none}), default=0) < int(time.time()):
|
|
||||||
token_data = self._download_json(
|
|
||||||
'https://zdf-prod-futura.zdf.de/mediathekV2/token', video_id,
|
|
||||||
note='Downloading API token')
|
|
||||||
self.cache.store(TOKEN_CACHE_SECTION, TOKEN_CACHE_KEY, token_data)
|
|
||||||
return f'{token_data["type"]} {token_data["token"]}'
|
|
||||||
|
|
||||||
def _download_graphql(self, item_id, data_desc, query=None, body=None):
|
def _download_graphql(self, item_id, data_desc, query=None, body=None):
|
||||||
if not query and not body:
|
if not query and not body:
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
@ -152,7 +155,7 @@ class ZDFBaseIE(InfoExtractor):
|
|||||||
'https://api.zdf.de/graphql', item_id, note=f'Downloading {data_desc}',
|
'https://api.zdf.de/graphql', item_id, note=f'Downloading {data_desc}',
|
||||||
errnote=f'Failed to download {data_desc}', query=query,
|
errnote=f'Failed to download {data_desc}', query=query,
|
||||||
data=json.dumps(body).encode() if body else None, headers={
|
data=json.dumps(body).encode() if body else None, headers={
|
||||||
'Api-Auth': self._get_api_token(item_id),
|
'Api-Auth': self._get_api_token(),
|
||||||
'Apollo-Require-Preflight': True,
|
'Apollo-Require-Preflight': True,
|
||||||
'Content-Type': 'application/json' if body else None,
|
'Content-Type': 'application/json' if body else None,
|
||||||
})
|
})
|
||||||
@ -502,7 +505,7 @@ query VideoByCanonical($canonical: String!) {
|
|||||||
ptmd_url = traverse_obj(document, (
|
ptmd_url = traverse_obj(document, (
|
||||||
('streamApiUrlAndroid', ('streams', 0, 'streamApiUrlAndroid')),
|
('streamApiUrlAndroid', ('streams', 0, 'streamApiUrlAndroid')),
|
||||||
{url_or_none}, any))
|
{url_or_none}, any))
|
||||||
ptmd_data = self._extract_ptmd_urls(ptmd_url, document_id, self._get_api_token(document_id))
|
ptmd_data = self._extract_ptmd_urls(ptmd_url, document_id, self._get_api_token())
|
||||||
|
|
||||||
thumbnails = []
|
thumbnails = []
|
||||||
for thumbnail_key, thumbnail in traverse_obj(document, ('teaserBild', {dict.items})):
|
for thumbnail_key, thumbnail in traverse_obj(document, ('teaserBild', {dict.items})):
|
||||||
@ -548,8 +551,7 @@ query VideoByCanonical($canonical: String!) {
|
|||||||
}))
|
}))
|
||||||
aspect_ratio = traverse_obj(ptmd_nodes, (
|
aspect_ratio = traverse_obj(ptmd_nodes, (
|
||||||
..., 'aspectRatio', {self._parse_aspect_ratio}, any))
|
..., 'aspectRatio', {self._parse_aspect_ratio}, any))
|
||||||
ptmd_result = self._extract_ptmd(
|
ptmd_result = self._extract_ptmd(ptmd_info, video_id, self._get_api_token(), aspect_ratio)
|
||||||
ptmd_info, video_id, self._get_api_token(video_id), aspect_ratio)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user