Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/youtube.py (Python)

Code before:
            require_js_player = self._get_default_ytcfg(client).get('REQUIRE_JS_PLAYER')
            if 'js' in self._configuration_arg('player_skip'):
                require_js_player = False
                player_url = None

            if not player_url and not tried_iframe_fallback and require_js_player:
                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:
                if last_error:
                    self.report_warning(last_error)
                last_error = 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}")')
                    last_error = ValueError("Could not extract any player response (perhaps your IP is being blocked by Youtube?)")
                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)

            # creator clients can bypass AGE_VERIFICATION_REQUIRED if logged in
            if variant == 'embedded' and self._is_unplayable(pr) and self.is_authenticated:
                append_client(f'{base_client}_creator')
            elif self._is_agegated(pr):
                if variant == 'tv_embedded':
                    append_client(f'{base_client}_embedded')
                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)
        return prs, player_url

    def _needs_live_processing(self, live_status, duration):

Review comment (bug):
IMO we shouldn't raise like this; we should instead fix #7715, the format selection bug that is causing `best` to select storyboards as a fallback, and then we will properly raise for no formats.

we could add the IP block note to the warning though, something like this?
```suggestion
                        f'Skipping player response from {client} client'
                        + f'(got player response for video "{pr_video_id}" instead of "{video_id}" possibly due to IP address ban by Youtube)')
```

Code after:
            if 'configs' not in self._configuration_arg('player_skip') and client != 'web':
                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)
            require_js_player = self._get_default_ytcfg(client).get('REQUIRE_JS_PLAYER')
            if 'js' in self._configuration_arg('player_skip'):
                require_js_player = False
                player_url = None

            if not player_url and not tried_iframe_fallback and require_js_player:
                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)
                continue

            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:
                append_client(f'{base_client}_creator')
            elif self._is_agegated(pr):
                if variant == 'tv_embedded':
                    append_client(f'{base_client}_embedded')
                elif not variant:
                    append_client(f'tv_embedded.{base_client}', f'{base_client}_embedded')

        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