Skip to content

Commit

Permalink
[ie/youtube] Better error when all player responses are skipped (#9083)
Browse files Browse the repository at this point in the history
Authored by: Grub4K, pukkandan

Co-authored-by: pukkandan <pukkandan.ytdlp@gmail.com>
  • Loading branch information
Grub4K and pukkandan committed Feb 24, 2024
1 parent 464c919 commit 5eedc20
Showing 1 changed file with 35 additions and 33 deletions.
68 changes: 35 additions & 33 deletions yt_dlp/extractor/youtube.py
Expand Up @@ -3640,15 +3640,28 @@ def _get_requested_clients(self, url, smuggled_data):

return orderedSet(requested_clients)

def _invalid_player_response(self, pr, video_id):
# YouTube may return a different video player response than expected.
# See: https://github.com/TeamNewPipe/NewPipe/issues/8713
if (pr_id := traverse_obj(pr, ('videoDetails', 'videoId'))) != video_id:
return pr_id

def _extract_player_responses(self, clients, video_id, webpage, master_ytcfg, smuggled_data):
initial_pr = None
if webpage:
initial_pr = self._search_json(
self._YT_INITIAL_PLAYER_RESPONSE_RE, webpage, 'initial player response', video_id, fatal=False)

prs = []
if initial_pr and not self._invalid_player_response(initial_pr, video_id):
# Android player_response does not have microFormats which are needed for
# extraction of some data. So we return the initial_pr with formats
# stripped out even if not requested by the user
# See: https://github.com/yt-dlp/yt-dlp/issues/501
prs.append({**initial_pr, 'streamingData': None})

all_clients = set(clients)
clients = clients[::-1]
prs = []

def append_client(*client_names):
""" Append the first client name that exists but not already used """
Expand All @@ -3660,18 +3673,9 @@ def append_client(*client_names):
all_clients.add(actual_client)
return

# Android player_response does not have microFormats which are needed for
# extraction of some data. So we return the initial_pr with formats
# stripped out even if not requested by the user
# See: https://github.com/yt-dlp/yt-dlp/issues/501
if initial_pr:
pr = dict(initial_pr)
pr['streamingData'] = None
prs.append(pr)

last_error = None
tried_iframe_fallback = False
player_url = None
skipped_clients = {}
while clients:
client, base_client, variant = _split_innertube_client(clients.pop())
player_ytcfg = master_ytcfg if client == 'web' else {}
Expand All @@ -3692,26 +3696,19 @@ def append_client(*client_names):
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:
if last_error:
self.report_warning(last_error)
last_error = e
self.report_warning(e)
continue

if pr:
# YouTube may return a different video player response than expected.
# See: https://github.com/TeamNewPipe/NewPipe/issues/8713
pr_video_id = traverse_obj(pr, ('videoDetails', 'videoId'))
if pr_video_id and pr_video_id != video_id:
self.report_warning(
f'Skipping player response from {client} client (got player response for video "{pr_video_id}" instead of "{video_id}")' + bug_reports_message())
else:
# Save client name for introspection later
name = short_client_name(client)
sd = traverse_obj(pr, ('streamingData', {dict})) or {}
sd[STREAMING_DATA_CLIENT_NAME] = name
for f in traverse_obj(sd, (('formats', 'adaptiveFormats'), ..., {dict})):
f[STREAMING_DATA_CLIENT_NAME] = name
prs.append(pr)
if pr_id := self._invalid_player_response(pr, video_id):
skipped_clients[client] = pr_id
elif pr:
# Save client name for introspection later
name = short_client_name(client)
sd = traverse_obj(pr, ('streamingData', {dict})) or {}
sd[STREAMING_DATA_CLIENT_NAME] = name
for f in traverse_obj(sd, (('formats', 'adaptiveFormats'), ..., {dict})):
f[STREAMING_DATA_CLIENT_NAME] = name
prs.append(pr)

# creator clients can bypass AGE_VERIFICATION_REQUIRED if logged in
if variant == 'embedded' and self._is_unplayable(pr) and self.is_authenticated:
Expand All @@ -3722,10 +3719,15 @@ def append_client(*client_names):
elif not variant:
append_client(f'tv_embedded.{base_client}', f'{base_client}_embedded')

if last_error:
if not len(prs):
raise last_error
self.report_warning(last_error)
if skipped_clients:
self.report_warning(
f'Skipping player responses from {"/".join(skipped_clients)} clients '
f'(got player responses for video "{"/".join(set(skipped_clients.values()))}" instead of "{video_id}")')
if not prs:
raise ExtractorError(
'All player responses are invalid. Your IP is likely being blocked by Youtube', expected=True)
elif not prs:
raise ExtractorError('Failed to extract any player response')
return prs, player_url

def _needs_live_processing(self, live_status, duration):
Expand Down

0 comments on commit 5eedc20

Please sign in to comment.