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

Code before:
                query['userId'] = user_id

        page_data = self._download_json(
            f'https://api.ardmediathek.de/page-gateway/pages/ard/item/{display_id}',
            display_id, query=query, headers=headers)

        # For user convenience we use the old contentId instead of the longer crid
        # Ref: https://github.com/yt-dlp/yt-dlp/issues/8731#issuecomment-1874398283
        old_id = traverse_obj(page_data, ('tracking', 'atiCustomVars', 'contentId', {int}))
        if old_id is not None:
            video_id = str(old_id)
            archive_ids = [make_archive_id(ARDBetaMediathekIE, display_id)]
        else:
            self.report_warning(f'Could not extract contentId{bug_reports_message()}')
            video_id = display_id
            archive_ids = None

        player_data = traverse_obj(
            page_data, ('widgets', lambda _, v: v['type'] in ('player_ondemand', 'player_live'), {dict}), get_all=False)
        is_live = player_data.get('type') == 'player_live'
        media_data = traverse_obj(player_data, ('mediaCollection', 'embedded', {dict}))

        if player_data.get('blockedByFsk'):
            self.raise_no_formats(
                'This video is only available after 22:00, '
                'try passing cookies to enable age verification ', expected=True)

        formats = []
        subtitles = {}
        for stream in traverse_obj(media_data, ('streams', ..., {dict})):
            kind = stream.get('kind')
            # Prioritize main stream over sign language and others
            preference = 1 if kind == 'main' else None
            for media in traverse_obj(stream, ('media', lambda _, v: url_or_none(v['url']))):
                media_url = media['url']

                audio_kind = traverse_obj(media, (
                    'audios', 0, 'kind', {str}), default='').replace('standard', '')
                lang_code = traverse_obj(media, ('audios', 0, 'languageCode', {str})) or 'deu'
                lang = join_nonempty(lang_code, audio_kind)
                language_preference = 10 if lang == 'deu' else -10

                if determine_ext(media_url) == 'm3u8':
                    fmts, subs = self._extract_m3u8_formats_and_subtitles(
                        media_url, video_id, m3u8_id=f'hls-{kind}', preference=preference, fatal=False, live=is_live)
                    for f in fmts:
                        f['language'] = lang
                        f['language_preference'] = language_preference
                    formats.extend(fmts)
                    self._merge_subtitles(subs, target=subtitles)
                else:

Review comment (suggestion):
```suggestion
            self.raise_login_required(
                'This video is only available after 22:00. Try passing cookies to enable age verification')
```

Code after:
            f'https://api.ardmediathek.de/page-gateway/pages/ard/item/{display_id}',
            display_id, query=query, headers=headers)

        # For user convenience we use the old contentId instead of the longer crid
        # Ref: https://github.com/yt-dlp/yt-dlp/issues/8731#issuecomment-1874398283
        old_id = traverse_obj(page_data, ('tracking', 'atiCustomVars', 'contentId', {int}))
        if old_id is not None:
            video_id = str(old_id)
            archive_ids = [make_archive_id(ARDBetaMediathekIE, display_id)]
        else:
            self.report_warning(f'Could not extract contentId{bug_reports_message()}')
            video_id = display_id
            archive_ids = None

        player_data = traverse_obj(
            page_data, ('widgets', lambda _, v: v['type'] in ('player_ondemand', 'player_live'), {dict}), get_all=False)
        is_live = player_data.get('type') == 'player_live'
        media_data = traverse_obj(player_data, ('mediaCollection', 'embedded', {dict}))

        if player_data.get('blockedByFsk'):
            self.raise_login_required('This video is only available for age verified users or after 22:00')

        formats = []
        subtitles = {}
        for stream in traverse_obj(media_data, ('streams', ..., {dict})):
            kind = stream.get('kind')
            # Prioritize main stream over sign language and others
            preference = 1 if kind == 'main' else None
            for media in traverse_obj(stream, ('media', lambda _, v: url_or_none(v['url']))):
                media_url = media['url']

                audio_kind = traverse_obj(media, (
                    'audios', 0, 'kind', {str}), default='').replace('standard', '')
                lang_code = traverse_obj(media, ('audios', 0, 'languageCode', {str})) or 'deu'
                lang = join_nonempty(lang_code, audio_kind)
                language_preference = 10 if lang == 'deu' else -10

                if determine_ext(media_url) == 'm3u8':
                    fmts, subs = self._extract_m3u8_formats_and_subtitles(
                        media_url, video_id, m3u8_id=f'hls-{kind}', preference=preference, fatal=False, live=is_live)
                    for f in fmts:
                        f['language'] = lang
                        f['language_preference'] = language_preference
                    formats.extend(fmts)
                    self._merge_subtitles(subs, target=subtitles)
                else:
                    formats.append({
                        'url': media_url,
                        'format_id': f'http-{kind}',
                        'preference': preference,
                        'language': lang,