summaryrefslogtreecommitdiffstats
path: root/yt_dlp/extractor/youtube.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-05 09:07:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-05 09:07:17 +0000
commit423ad025db8992beb1cfde9800c4bbc37426fc82 (patch)
treec6f676600b2362994be97673158ceb77996e6f71 /yt_dlp/extractor/youtube.py
parentAdding debian version 2024.07.09-1. (diff)
downloadyt-dlp-423ad025db8992beb1cfde9800c4bbc37426fc82.tar.xz
yt-dlp-423ad025db8992beb1cfde9800c4bbc37426fc82.zip
Merging upstream version 2024.07.16.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'yt_dlp/extractor/youtube.py')
-rw-r--r--yt_dlp/extractor/youtube.py41
1 files changed, 31 insertions, 10 deletions
diff --git a/yt_dlp/extractor/youtube.py b/yt_dlp/extractor/youtube.py
index 1c0a70d..53aca38 100644
--- a/yt_dlp/extractor/youtube.py
+++ b/yt_dlp/extractor/youtube.py
@@ -270,7 +270,7 @@ def build_innertube_clients():
THIRD_PARTY = {
'embedUrl': 'https://www.youtube.com/', # Can be any valid URL
}
- BASE_CLIENTS = ('ios', 'android', 'web', 'tv', 'mweb')
+ BASE_CLIENTS = ('ios', 'web', 'tv', 'mweb', 'android')
priority = qualities(BASE_CLIENTS[::-1])
for client, ytcfg in tuple(INNERTUBE_CLIENTS.items()):
@@ -1294,6 +1294,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
'401': {'ext': 'mp4', 'height': 2160, 'format_note': 'DASH video', 'vcodec': 'av01.0.12M.08'},
}
_SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'vtt')
+ _POTOKEN_EXPERIMENTS = ('51217476', '51217102')
_GEO_BYPASS = False
@@ -3142,7 +3143,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
def _extract_n_function_code(self, video_id, player_url):
player_id = self._extract_player_info(player_url)
- func_code = self.cache.load('youtube-nsig', player_id, min_ver='2022.09.1')
+ func_code = self.cache.load('youtube-nsig', player_id, min_ver='2024.07.09')
jscode = func_code or self._load_player(video_id, player_url)
jsi = JSInterpreter(jscode)
@@ -3701,8 +3702,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
return pr_id
def _extract_player_responses(self, clients, video_id, webpage, master_ytcfg, smuggled_data):
- initial_pr = None
+ initial_pr = ignore_initial_response = None
if webpage:
+ if 'web' in clients:
+ experiments = traverse_obj(master_ytcfg, (
+ 'WEB_PLAYER_CONTEXT_CONFIGS', ..., 'serializedExperimentIds', {lambda x: x.split(',')}, ...))
+ if all(x in experiments for x in self._POTOKEN_EXPERIMENTS):
+ self.report_warning(
+ 'Webpage contains broken formats (poToken experiment detected). Ignoring initial player response')
+ ignore_initial_response = True
initial_pr = self._search_json(
self._YT_INITIAL_PLAYER_RESPONSE_RE, webpage, 'initial player response', video_id, fatal=False)
@@ -3732,8 +3740,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
skipped_clients = {}
while clients:
client, base_client, variant = _split_innertube_client(clients.pop())
- player_ytcfg = master_ytcfg if client == 'web' else {}
- if 'configs' not in self._configuration_arg('player_skip') and client != 'web':
+ player_ytcfg = {}
+ if client == 'web':
+ player_ytcfg = self._get_default_ytcfg() if ignore_initial_response else master_ytcfg
+ elif 'configs' not in self._configuration_arg('player_skip'):
player_ytcfg = self._download_ytcfg(client, video_id) or player_ytcfg
player_url = player_url or self._extract_player_url(master_ytcfg, player_ytcfg, webpage=webpage)
@@ -3746,11 +3756,22 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
player_url = self._download_player_url(video_id)
tried_iframe_fallback = True
- try:
- pr = initial_pr if client == 'web' and initial_pr else self._extract_player_response(
- client, video_id, player_ytcfg or master_ytcfg, player_ytcfg, player_url if require_js_player else None, initial_pr, smuggled_data)
- except ExtractorError as e:
- self.report_warning(e)
+ pr = initial_pr if client == 'web' and not ignore_initial_response else None
+ for retry in self.RetryManager(fatal=False):
+ try:
+ pr = pr or self._extract_player_response(
+ client, video_id, player_ytcfg or master_ytcfg, player_ytcfg,
+ player_url if require_js_player else None, initial_pr, smuggled_data)
+ except ExtractorError as e:
+ self.report_warning(e)
+ break
+ experiments = traverse_obj(pr, (
+ 'responseContext', 'serviceTrackingParams', lambda _, v: v['service'] == 'GFEEDBACK',
+ 'params', lambda _, v: v['key'] == 'e', 'value', {lambda x: x.split(',')}, ...))
+ if all(x in experiments for x in self._POTOKEN_EXPERIMENTS):
+ pr = None
+ retry.error = ExtractorError('API returned broken formats (poToken experiment detected)', expected=True)
+ if not pr:
continue
if pr_id := self._invalid_player_response(pr, video_id):