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

Code before:
class MujRozhlasIE(RozhlasBaseIE):
    _VALID_URL = r'https?://(?:www\.)?mujrozhlas\.cz/(?:[^/]+/)*(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://www.mujrozhlas.cz/vykopavky/ach-jo-zase-teleci-rizek-je-mnohem-min-cesky-nez-jsme-si-mysleli',
        'info_dict': {'id': '10739193',
                      'title': 'Ach jo, zase to telecí! Řízek je mnohem míň český, než jsme si mysleli',
                      'description': 'md5:db7141e9caaedc9041ec7cefb9a62908',
                      'timestamp': 1684915200,
                      'modified_timestamp': 1684922446,
                      'series': 'Vykopávky',
                      'thumbnail': 'https://portal.rozhlas.cz/sites/default/files/images/84377046610af6ddc54d910b1dd7a22b.jpg',
                      'channel_id': 'radio-wave',
                      'upload_date': '20230524',
                      'modified_date': '20230524'},
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mujrozhlas.cz/cetba-na-pokracovani/zena-v-polarni-noci-z-fascinujiciho-deniku-vyjimecne-odvazne-christiane',
        'info_dict': {'id': '260da22a-1cde-3718-a1d9-4515f864c0ab',
                      'title': 'Žena v polární noci. Z fascinujícího deníku výjimečné a odvážné Christiane Ritterové',
                      'description': 'md5:f462de4b948bc91f7192af10bde3c279'},
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mujrozhlas.cz/nespavci',
        'info_dict': {'id': '09db9b37-d0f4-368c-986a-d3439f741f08', 'title': 'Nespavci',
                      'description': 'md5:c430adcbf9e2b9eac88b745881e814dc'},
        'params': {'skip_download': True}}
    ]

    _API_ROOT = 'https://api.mujrozhlas.cz'

    def _extract_audio_entry(self, entry):
        audio_id = traverse_obj(entry, ('meta', 'ga', 'contentId'), )

        title = traverse_obj(entry, ('attributes', 'title'))

        if audio_id is None or title is None:
            return None  # audio no longer available

        thumbnail = traverse_obj(entry, ('attributes', 'asset', 'url'))
        # these can be pretty large, e.g. https://api.mujrozhlas.cz/episodes/8ad147a0-d971-3fb2-9e6b-552a60f335fb
        # with a 2100x1400 jpeg

        timestamp = unified_timestamp(traverse_obj(entry, ('attributes', 'since')))
        modified_timestamp = unified_timestamp(traverse_obj(entry, ('attributes', 'updated')))

        formats = self._extract_audio(entry['attributes'], audio_id)

        return {
            'id': audio_id,
            'title': title,
            'formats': formats,

Review comment (suggestion):
Are these playlists? Add `playlist_count` or `playlist_mincount` to them. Also would help to add notes/comments of which test is a `show` and which test is a `serial`

Code after:
        'info_dict': {
            'id': '10739193',
            'ext': 'mp3',
            'title': 'Ach jo, zase to telecí! Řízek je mnohem míň český, než jsme si mysleli',
            'description': 'md5:db7141e9caaedc9041ec7cefb9a62908',
            'timestamp': 1684915200,
            'modified_timestamp': 1684922446,
            'series': 'Vykopávky',
            'thumbnail': 'https://portal.rozhlas.cz/sites/default/files/images/84377046610af6ddc54d910b1dd7a22b.jpg',
            'channel_id': 'radio-wave',
            'upload_date': '20230524',
            'modified_date': '20230524',
        },
    }, {
        # serial extraction
        'url': 'https://www.mujrozhlas.cz/radiokniha/jaroslava-janackova-pribeh-tajemneho-psani-o-pramenech-genezi-babicky',
        'playlist_mincount': 7,
        'info_dict': {
            'id': 'bb2b5f4e-ffb4-35a6-a34a-046aa62d6f6b',
            'title': 'Jaroslava Janáčková: Příběh tajemného psaní. O pramenech a genezi Babičky',
            'description': 'md5:7434d8fac39ac9fee6df098e11dfb1be',
        },
    }, {
        # show extraction
        'url': 'https://www.mujrozhlas.cz/nespavci',
        'playlist_mincount': 14,
        'info_dict': {
            'id': '09db9b37-d0f4-368c-986a-d3439f741f08',
            'title': 'Nespavci',
            'description': 'md5:c430adcbf9e2b9eac88b745881e814dc',
        },
    }]

    def _call_api(self, path, item_id, msg='API JSON'):
        return self._download_json(
            f'https://api.mujrozhlas.cz/{path}/{item_id}', item_id,
            note=f'Downloading {msg}', errnote=f'Failed to download {msg}')['data']

    def _extract_audio_entry(self, entry):
        audio_id = entry['meta']['ga']['contentId']

        return {
            'id': audio_id,
            'formats': self._extract_formats(entry['attributes'], audio_id),
            **traverse_obj(entry, {
                'title': ('attributes', 'title'),
                'description': ('attributes', 'description'),
                'episode_number': ('attributes', 'part'),
                'series': ('attributes', 'mirroredShow', 'title'),
                'chapter': ('attributes', 'mirroredSerial', 'title'),
                'artist': ('meta', 'ga', 'contentAuthor'),