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

Code before:
                'formats': formats,
                'subtitles': subtitles,
            }

    def _real_extract(self, url):
        display_id = self._match_id(url)

        webpage = self._download_webpage(url, display_id)
        playlist_url = urllib.parse.urljoin(url, self._html_search_regex(
            r'data-playlist-url=(["\'])(?P<url>(?:(?!\1).)+)\1',
            webpage, 'playlist url', group='url'))

        item_ids = traverse_obj(parse_qs(playlist_url), 'video_id', 'videos_ids[]', 'news_ids[]')
        items = traverse_obj(
            self._download_json(playlist_url, display_id),
            lambda _, v: v['uid'] and (str(v['uid']) in item_ids if item_ids else True))

        return self.playlist_result(
            self._entries(items), display_id, self._og_search_title(webpage, default=None),
            thumbnail=self._og_search_thumbnail(webpage, default=None))


class FirstTVLiveIE(InfoExtractor):
    IE_NAME = '1tv-live'
    IE_DESC = 'Первый канал (прямой эфир)'
    _VALID_URL = r'https?://(?:www\.)?1tv\.ru/live/?'
    _GEO_BYPASS = False
    _GEO_COUNTRIES = ['RU']

    _TESTS = [
        {
            'url': 'https://www.1tv.ru/live',
            'info_dict': {
                'id': 'live',
                'ext': 'mp4',
                'title': 'ПЕРВЫЙ КАНАЛ ПРЯМОЙ ЭФИР СМОТРЕТЬ ОНЛАЙН',
                'is_live': True,
            },
        },
    ]

    def _real_extract(self, url):
        display_id = 'live'
        webpage = self._download_webpage(url, display_id)

        streams_list = self._download_json('https://stream.1tv.ru/api/playlist/1tvch-v1_as_array.json', 'live')
        mpd_url = traverse_obj(streams_list, ('mpd', ..., {url_or_none}, any, {require('mpd url')}))
        hls_url = traverse_obj(streams_list, ('hls', ..., {url_or_none}, any, {require('hls url')}))
        mpd_formats, subtitles = self._extract_mpd_formats_and_subtitles(mpd_url, display_id, mpd_id='dash', fatal=True)
        hls_formats = self._extract_m3u8_formats(hls_url, display_id, 'mp4', m3u8_id='hls', live=True, fatal=False)
        formats = mpd_formats + hls_formats

Review comment (suggestion):
```suggestion
    _VALID_URL = r'https?://(?:www\.)?1tv\.ru/live'
```

Code after:
                'formats': formats,
                'subtitles': subtitles,
            }

    def _real_extract(self, url):
        display_id = self._match_id(url)

        webpage = self._download_webpage(url, display_id)
        playlist_url = urllib.parse.urljoin(url, self._html_search_regex(
            r'data-playlist-url=(["\'])(?P<url>(?:(?!\1).)+)\1',
            webpage, 'playlist url', group='url'))

        item_ids = traverse_obj(parse_qs(playlist_url), 'video_id', 'videos_ids[]', 'news_ids[]')
        items = traverse_obj(
            self._download_json(playlist_url, display_id),
            lambda _, v: v['uid'] and (str(v['uid']) in item_ids if item_ids else True))

        return self.playlist_result(
            self._entries(items), display_id, self._og_search_title(webpage, default=None),
            thumbnail=self._og_search_thumbnail(webpage, default=None))


class FirstTVLiveIE(InfoExtractor):
    IE_NAME = '1tv:live'
    IE_DESC = 'Первый канал (прямой эфир)'
    _VALID_URL = r'https?://(?:www\.)?1tv\.ru/live'

    _TESTS = [{
        'url': 'https://www.1tv.ru/live',
        'info_dict': {
            'id': 'live',
            'ext': 'mp4',
            'title': r're:ПЕРВЫЙ КАНАЛ ПРЯМОЙ ЭФИР СМОТРЕТЬ ОНЛАЙН \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
            'live_status': 'is_live',
        },
        'params': {'skip_download': 'livestream'},
    }]

    def _real_extract(self, url):
        display_id = 'live'
        webpage = self._download_webpage(url, display_id, fatal=False)

        streams_list = self._download_json('https://stream.1tv.ru/api/playlist/1tvch-v1_as_array.json', display_id)
        mpd_url = traverse_obj(streams_list, ('mpd', ..., {url_or_none}, any, {require('mpd url')}))
        # FFmpeg needs to be passed -re to not seek past live window. This is handled by core
        formats, _ = self._extract_mpd_formats_and_subtitles(mpd_url, display_id, mpd_id='dash')

        return {
            'id': display_id,
            'title': self._html_extract_title(webpage),
            'formats': formats,