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

Code before:
        original_clients = clients
        clients = clients[::-1]
        creator_client_appended = False

        while clients:
            client = clients.pop()
            player_ytcfg = master_ytcfg if client == 'web' else {}
            if 'configs' not in self._configuration_arg('player_skip'):
                player_ytcfg = self._extract_player_ytcfg(client, video_id) or player_ytcfg

            pr = (
                initial_pr if client == 'web' and initial_pr
                else self._extract_player_response(
                    client, video_id, player_ytcfg or master_ytcfg, player_ytcfg, identity_token, player_url, initial_pr))
            if pr:
                yield pr

            if self._is_agegated(pr):
                client = f'{client}_agegate'
                if client in INNERTUBE_CLIENTS and client not in original_clients:
                    clients.append(client)

                # Try web creator (studio) client if session cookies provided (bypass for "AGE_VERIFICATION_REQUIRED")
                if self._generate_sapisidhash_header() is not None and not creator_client_appended:
                    clients.append("web_creator")
                    creator_client_appended = True

        # 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 and 'web' not in original_clients:
            initial_pr['streamingData'] = None
            yield initial_pr

    def _extract_formats(self, streaming_data, video_id, player_url, is_live):
        itags, stream_ids = [], []
        itag_qualities, res_qualities = {}, {}
        q = qualities([
            # Normally tiny is the smallest video-only formats. But
            # audio-only formats with unknown quality may get tagged as tiny
            'tiny',
            'audio_quality_ultralow', 'audio_quality_low', 'audio_quality_medium', 'audio_quality_high',  # Audio only formats
            'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'hd2160', 'hd2880', 'highres'
        ])
        streaming_formats = traverse_obj(streaming_data, (..., ('formats', 'adaptiveFormats'), ...), default=[])

        for fmt in streaming_formats:
            if fmt.get('targetDurationSec') or fmt.get('drmFamilies'):
                continue


Review comment (suggestion):
github wont let me comment on the above line. The suggestion is meant to replace lines 2499-2506

```py
                client = f'{client}_creator' if self._generate_sapisidhash_header() else f'{client}_agegate'
                if client in INNERTUBE_CLIENTS and client not in original_clients:
                    clients.append(client)
```

Isn't this enough? This will only fallback to creator if cookies are given and `web` throws an age-gate error. Also, in that case, the normal age-gate bypass is not attempted (should it be?). If we can add `android_creator`/`ios_creator`, the fallbacks will automatically extend to them

Code after:
        }.get(client)
        if not url:
            return {}
        webpage = self._download_webpage(url, video_id, fatal=False, note=f'Downloading {client} config')
        return self.extract_ytcfg(video_id, webpage) or {}

    def _extract_player_responses(self, clients, video_id, webpage, master_ytcfg, player_url, identity_token):
        initial_pr = None
        if webpage:
            initial_pr = self._extract_yt_initial_variable(
                webpage, self._YT_INITIAL_PLAYER_RESPONSE_RE,
                video_id, 'initial player response')

        original_clients = clients
        clients = clients[::-1]

        def append_client(client_name):
            if client_name in INNERTUBE_CLIENTS and client_name not in original_clients:
                clients.append(client_name)

        while clients:
            client = clients.pop()
            player_ytcfg = master_ytcfg if client == 'web' else {}
            if 'configs' not in self._configuration_arg('player_skip'):
                player_ytcfg = self._extract_player_ytcfg(client, video_id) or player_ytcfg

            pr = (
                initial_pr if client == 'web' and initial_pr
                else self._extract_player_response(
                    client, video_id, player_ytcfg or master_ytcfg, player_ytcfg, identity_token, player_url, initial_pr))
            if pr:
                yield pr

            # creator clients can bypass AGE_VERIFICATION_REQUIRED if logged in
            if client.endswith('_agegate') and self._is_unplayable(pr) and self._generate_sapisidhash_header():
                append_client(client.replace('_agegate', '_creator'))
            elif self._is_agegated(pr):
                append_client(f'{client}_agegate')

        # 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 and 'web' not in original_clients:
            initial_pr['streamingData'] = None
            yield initial_pr

    def _extract_formats(self, streaming_data, video_id, player_url, is_live):
        itags, stream_ids = [], []
        itag_qualities, res_qualities = {}, {}
        q = qualities([