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

Code before:
class VKPlayIE(VKPlayBaseIE):
    _VALID_URL = r'https?://vkplay\.live/(?P<username>[^/]+)/record/(?P<id>[a-f0-9\-]+)'
    _TESTS = [{
        'url': 'https://vkplay.live/zitsmann/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da',
        'info_dict': {
            'id': 'f5e6e3b5-dc52-4d14-965d-0680dd2882da',
            'ext': 'mp4',
            'title': 'Atomic Heart (пробуем!) спасибо подписчику EKZO!',
            'uploader': 'ZitsmanN',
            'uploader_id': 13159830,
            'release_timestamp': 1683461378,
            'release_date': '20230507',
            'thumbnail': r're:https://images.vkplay.live/public_video_stream/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da/preview\?change_time=\d+',
            'duration': 10608,
            'view_count': int,
            'like_count': int,
            'categories': ['Atomic Heart'],
        },
        'params': {'skip_download': True},
    }]

    def _real_extract(self, url):
        username, video_id = self._match_valid_url(url).groups()

        initial_state = self._extract_initial_state(url, video_id)
        record_info = traverse_obj(initial_state, ('record', 'currentRecord', 'data'))
        if not record_info:
            record_info = self._download_json('https://api.vkplay.live/v1/blog/{username}/public_video_stream/record/{video_id}', video_id)
        playurls = traverse_obj(record_info, ('data', 0, 'playerUrls', ..., {
            'url': ('url', {url_or_none}),
            'format_id': ('type', {str_or_none}),
        }))

        return {
            'id': video_id,
            'formats': self._parse_playurls(playurls, video_id),
            **traverse_obj(record_info, {
                'title': ('title', {str}),
                'release_timestamp': ('startTime', {int_or_none}),
                'uploader': ('blog', 'owner', 'nick', {str_or_none}),
                'uploader_id': ('blog', 'owner', 'id', {int_or_none}),
                'thumbnail': ('previewUrl', {url_or_none}),
                'duration': ('duration', {int_or_none}),
                'view_count': ('count', 'views', {int_or_none}),
                'like_count': ('count', 'likes', {int_or_none}),
                'categories': ('category', 'title', {lambda i: [str_or_none(i)]}),
            })
        }


class VKPlayLiveIE(VKPlayBaseIE):

Review comment (suggestion):
if we have a fallback, might be a good idea to check here that we're getting what we're expecting:
```suggestion
        record_info = traverse_obj(initial_state, ('record', 'currentRecord', 'data', {dict}))
```

Code after:
    def _extract_formats(self, stream_info, video_id):
        formats = []
        for stream in traverse_obj(stream_info, (
                'data', 0, 'playerUrls', lambda _, v: url_or_none(v['url']) and v['type'])):
            url = stream['url']
            format_id = str_or_none(stream['type'])
            if format_id in ('hls', 'live_hls', 'live_playback_hls') or '.m3u8' in url:
                formats.extend(self._extract_m3u8_formats(url, video_id, m3u8_id=format_id, fatal=False))
            elif format_id == 'dash':
                formats.extend(self._extract_mpd_formats(url, video_id, mpd_id=format_id, fatal=False))
            elif format_id in ('live_dash', 'live_playback_dash'):
                self.write_debug(f'Not extracting unsupported format "{format_id}"')
            else:
                formats.append({
                    'url': url,
                    'ext': 'mp4',
                    'format_id': format_id,
                    **parse_resolution(self._RESOLUTIONS.get(format_id)),
                })
        return formats

    def _extract_common_meta(self, stream_info):
        return traverse_obj(stream_info, {
            'id': ('id', {str_or_none}),
            'title': ('title', {str}),
            'release_timestamp': ('startTime', {int_or_none}),
            'thumbnail': ('previewUrl', {url_or_none}),
            'view_count': ('count', 'views', {int_or_none}),
            'like_count': ('count', 'likes', {int_or_none}),
            'categories': ('category', 'title', {str}, {lambda x: [x] if x else None}),
            'uploader': (('user', ('blog', 'owner')), 'nick', {str}),
            'uploader_id': (('user', ('blog', 'owner')), 'id', {str_or_none}),
            'duration': ('duration', {int_or_none}),
            'is_live': ('isOnline', {bool}),
            'concurrent_view_count': ('count', 'viewers', {int_or_none}),
        }, get_all=False)


class VKPlayIE(VKPlayBaseIE):
    _VALID_URL = r'https?://vkplay\.live/(?P<username>[^/]+)/record/(?P<id>[a-f0-9\-]+)'
    _TESTS = [{
        'url': 'https://vkplay.live/zitsmann/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da',
        'info_dict': {
            'id': 'f5e6e3b5-dc52-4d14-965d-0680dd2882da',
            'ext': 'mp4',
            'title': 'Atomic Heart (пробуем!) спасибо подписчику EKZO!',
            'uploader': 'ZitsmanN',
            'uploader_id': '13159830',
            'release_timestamp': 1683461378,
            'release_date': '20230507',
            'thumbnail': r're:https://images.vkplay.live/public_video_stream/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da/preview\?change_time=\d+',