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

Code before:

        return info_dict


class TVPVODBaseIE(InfoExtractor):
    _API_BASE_URL = 'https://vod.tvp.pl/api/products'

    def _call_api(self, resource, video_id, query={}, **kwargs):
        return self._download_json(
            f'{self._API_BASE_URL}/{resource}', video_id,
            query={'lang': 'pl', 'platform': 'BROWSER', **query}, **kwargs)

    def _parse_video(self, video, with_url=True):
        info_dict = {
            'id': str(video['id']),
            'title': video.get('title'),
            'description': clean_html(video.get('lead') or video.get('description')),
            'age_limit': int_or_none(video.get('rating')),
            'duration': int_or_none(video.get('duration')),
            'episode_number': int_or_none(video.get('number')),
            'series': traverse_obj(video, ('season', 'serial', 'title')),
            'thumbnails': [{'url': u}
                           for u in traverse_obj(video,
                           ('images', ..., ..., 'url'), expected_type=url_or_none)
                           if u is not None],
        }
        if with_url:
            info_dict.update({
                '_type': 'url',
                'url': video['webUrl'],
                'ie_key': TVPVODVideoIE.ie_key(),
            })
        return info_dict


class TVPVODVideoIE(TVPVODBaseIE):
    IE_NAME = 'tvp:vod'
    _VALID_URL = r'https?://vod\.tvp\.pl/[a-z\d-]+,\d+/[a-z\d-]+(?<!-odcinki)(?:-odcinki,\d+/odcinek-\d+,S\d+E\d+)?,(?P<id>\d+)(?:\?[^#]+)?(?:#.+)?$'

    _TESTS = [{
        'url': 'https://vod.tvp.pl/dla-dzieci,24/laboratorium-alchemika-odcinki,309338/odcinek-24,S01E24,311357',
        'info_dict': {
            'id': '311357',
            'ext': 'mp4',
            'title': 'Tusze termiczne. Jak zobaczyć niewidoczne. Odcinek 24',
            'description': 'md5:1d4098d3e537092ccbac1abf49b7cd4c',
            'duration': 300,
            'episode_number': 24,
            'episode': 'Episode 24',
            'age_limit': 0,
            'series': 'Laboratorium alchemika',

Review comment (suggestion):
Actually, this whole thing can be simplified with new traverse_obj features.
```suggestion
        info_dict = traverse_obj(video, {
            'id': ('id', {str_or_none}),
            'title': 'title',
            'age_limit': ('rating', {int_or_none}),
            'duration': ('duration', {int_or_none}),
            'episode_number': ('number', {int_or_none}),
            'series': ('season', 'serial', 'title', {str}),
            'thumbnails': ('images', ..., ..., {'url': ('url', {url_or_none})}),
        })
        info_dict['description'] = traverse_obj(video, 'lead', 'description', expected_type=clean_html)
```


Code after:

        return info_dict


class TVPVODBaseIE(InfoExtractor):
    _API_BASE_URL = 'https://vod.tvp.pl/api/products'

    def _call_api(self, resource, video_id, query={}, **kwargs):
        is_valid = lambda x: 200 <= x < 300
        document, urlh = self._download_json_handle(
            f'{self._API_BASE_URL}/{resource}', video_id,
            query={'lang': 'pl', 'platform': 'BROWSER', **query},
            expected_status=lambda x: is_valid(x) or 400 <= x < 500, **kwargs)
        if is_valid(urlh.status):
            return document
        raise ExtractorError(f'Woronicza said: {document.get("code")} (HTTP {urlh.status})')

    def _parse_video(self, video, with_url=True):
        info_dict = traverse_obj(video, {
            'id': ('id', {str_or_none}),
            'title': 'title',
            'age_limit': ('rating', {int_or_none}),
            'duration': ('duration', {int_or_none}),
            'episode_number': ('number', {int_or_none}),
            'series': ('season', 'serial', 'title', {str_or_none}),
            'thumbnails': ('images', ..., ..., {'url': ('url', {url_or_none})}),
        })
        info_dict['description'] = clean_html(dict_get(video, ('lead', 'description')))
        if with_url:
            info_dict.update({
                '_type': 'url',
                'url': video['webUrl'],
                'ie_key': TVPVODVideoIE.ie_key(),
            })
        return info_dict


class TVPVODVideoIE(TVPVODBaseIE):
    IE_NAME = 'tvp:vod'
    _VALID_URL = r'https?://vod\.tvp\.pl/[a-z\d-]+,\d+/[a-z\d-]+(?<!-odcinki)(?:-odcinki,\d+/odcinek-\d+,S\d+E\d+)?,(?P<id>\d+)(?:\?[^#]+)?(?:#.+)?$'

    _TESTS = [{
        'url': 'https://vod.tvp.pl/dla-dzieci,24/laboratorium-alchemika-odcinki,309338/odcinek-24,S01E24,311357',
        'info_dict': {
            'id': '311357',
            'ext': 'mp4',
            'title': 'Tusze termiczne. Jak zobaczyć niewidoczne. Odcinek 24',
            'description': 'md5:1d4098d3e537092ccbac1abf49b7cd4c',
            'duration': 300,
            'episode_number': 24,
            'episode': 'Episode 24',