import urllib.parse from .common import InfoExtractor from ..utils import ( determine_ext, int_or_none, join_nonempty, mimetype2ext, parse_qs, unified_strdate, url_or_none, ) from ..utils.traversal import traverse_obj class FirstTVIE(InfoExtractor): IE_NAME = '1tv' IE_DESC = 'Первый канал' _VALID_URL = r'https?://(?:www\.)?(?:sport)?1tv\.ru/(?:[^/?#]+/)+(?P[^/?#]+)' _TESTS = [{ # single format; has item.id 'url': 'https://www.1tv.ru/shows/naedine-so-vsemi/vypuski/gost-lyudmila-senchina-naedine-so-vsemi-vypusk-ot-12-02-2015', 'md5': '8011ae8e88ff4150107ab9c5a8f5b659', 'info_dict': { 'id': '40049', 'ext': 'mp4', 'title': 'Гость Людмила Сенчина. Наедине со всеми. Выпуск от 12.02.2015', 'thumbnail': r're:https?://.+/.+\.jpg', 'upload_date': '20150212', 'duration': 2694, }, 'params': {'skip_download': 'm3u8'}, }, { # multiple formats; has item.id 'url': 'https://www.1tv.ru/shows/dobroe-utro/pro-zdorove/vesennyaya-allergiya-dobroe-utro-fragment-vypuska-ot-07042016', 'info_dict': { 'id': '364746', 'ext': 'mp4', 'title': 'Весенняя аллергия. Доброе утро. Фрагмент выпуска от 07.04.2016', 'thumbnail': r're:https?://.+/.+\.jpg', 'upload_date': '20160407', 'duration': 179, 'formats': 'mincount:3', }, 'params': {'skip_download': 'm3u8'}, }, { 'url': 'https://www.1tv.ru/news/issue/2016-12-01/14:00', 'info_dict': { 'id': '14:00', 'title': 'Выпуск программы «Время» в 20:00 1 декабря 2016 года. Новости. Первый канал', 'thumbnail': 'https://static.1tv.ru/uploads/photo/image/8/big/338448_big_8fc7eb236f.jpg', }, 'playlist_count': 13, }, { # has timestamp; has item.uid but not item.id 'url': 'https://www.1tv.ru/shows/segodnya-vecherom/vypuski/avtory-odnogo-hita-segodnya-vecherom-vypusk-ot-03-05-2025', 'info_dict': { 'id': '270411', 'ext': 'mp4', 'title': 'Авторы одного хита. Сегодня вечером. Выпуск от 03.05.2025', 'thumbnail': r're:https?://.+/.+\.jpg', 'timestamp': 1746286020, 'upload_date': '20250503', }, 'params': {'skip_download': 'm3u8'}, }, { 'url': 'http://www.1tv.ru/shows/tochvtoch-supersezon/vystupleniya/evgeniy-dyatlov-vladimir-vysockiy-koni-priveredlivye-toch-v-toch-supersezon-fragment-vypuska-ot-06-11-2016', 'only_matching': True, }, { 'url': 'https://www.sport1tv.ru/sport/chempionat-rossii-po-figurnomu-kataniyu-2025', 'only_matching': True, }] def _entries(self, items): for item in items: video_id = str(item.get('id') or item['uid']) formats, subtitles = [], {} for f in traverse_obj(item, ('sources', lambda _, v: url_or_none(v['src']))): src = f['src'] ext = mimetype2ext(f.get('type'), default=determine_ext(src)) if ext == 'm3u8': fmts, subs = self._extract_m3u8_formats_and_subtitles( src, video_id, 'mp4', m3u8_id='hls', fatal=False) elif ext == 'mpd': fmts, subs = self._extract_mpd_formats_and_subtitles( src, video_id, mpd_id='dash', fatal=False) else: tbr = self._search_regex(fr'_(\d{{3,}})\.{ext}', src, 'tbr', default=None) formats.append({ 'url': src, 'ext': ext, 'format_id': join_nonempty('http', ext, tbr), 'tbr': int_or_none(tbr), # quality metadata of http formats may be incorrect 'quality': -10, }) continue formats.extend(fmts) self._merge_subtitles(subs, target=subtitles) yield { **traverse_obj(item, { 'title': ('title', {str}), 'thumbnail': ('poster', {url_or_none}), 'timestamp': ('dvr_begin_at', {int_or_none}), 'upload_date': ('date_air', {unified_strdate}), 'duration': ('duration', {int_or_none}), }), 'id': video_id, '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(?:(?!\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))