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

Code before:

        response = self._download_json(
            detail_url, episode_id, 'Downloading extended metadata',
            'Failed to download extended metadata', fatal=False, expected_status=400)
        if not response:
            return {}

        if error := traverse_obj(response, ('error', {dict})):
            self.report_warning(
                'Failed to get extended metadata. API returned '
                f'Error {join_nonempty("statuscode", "message", from_dict=error, delim=": ")}')
            return {}

        station = traverse_obj(response, ('publishedOn', 'broadcastDisplayName'))

        about = response.get('about')
        thumbnails = []
        thumbnails.extend(self._get_thumbnails(traverse_obj(about, ('eyecatch', {dict.items})), ''))
        if eyecatch_list := about.get('eyecatchList'):
            for num, v in enumerate(eyecatch_list):
                thumbnails.extend(self._get_thumbnails(v.items(), join_nonempty('list', num), preference=-2))
        thumbnails.extend(self._get_thumbnails(traverse_obj(about, ('partOfSeries', 'eyecatch', {dict.items})), 'series', preference=-3))

        return filter_dict({
            'description': self._format_description(response),
            'cast': traverse_obj(response, ('misc', 'actList', ..., 'name')),
            'thumbnails': thumbnails,
            **traverse_obj(response, {
                'title': ('name', {str}),
                'timestamp': ('endDate', {unified_timestamp}),
                'release_timestamp': ('startDate', {unified_timestamp}),
                'duration': ('duration', {parse_duration}),
            }),
            **traverse_obj(response, ('identifierGroup', {
                'series': 'radioSeriesName',
                'series_id': 'radioSeriesId',
                'episode': 'radioEpisodeName',
                'episode_id': 'radioEpisodeId',
                'categories': ('genre', ..., ['name1', 'name2'], all, {orderedSet}),
            })),
            'channel': station,
            'uploader': station,
        })

    def _extract_episode_info(self, episode, programme_id, series_meta):
        episode_id = f'{programme_id}_{episode["id"]}'
        aa_vinfo = traverse_obj(episode, ('aa_contents_id', {lambda x: x.split(';')}))
        extended_metadata = self._extract_extended_metadata(episode_id, aa_vinfo)
        fallback_start_time, _, fallback_end_time = traverse_obj(
            aa_vinfo, (4, {str}, {lambda x: (x or '').partition('_')}))


Review comment (suggestion):
```suggestion
            'cast': traverse_obj(response, ('misc', 'actList', ..., 'name', {str})),
```

Code after:
        for num, dct in enumerate(traverse_obj(response, ('about', 'eyecatchList', ...))):
            thumbnails.extend(self._get_thumbnails(dct, None, join_nonempty('list', num), -2))
        thumbnails.extend(
            self._get_thumbnails(response, ('about', 'partOfSeries', 'eyecatch'), 'series', -3))

        return filter_dict({
            'description': self._format_description(response),
            'cast': traverse_obj(response, ('misc', 'actList', ..., 'name', {str})),
            'thumbnails': thumbnails,
            **traverse_obj(response, {
                'title': ('name', {str}),
                'timestamp': ('endDate', {unified_timestamp}),
                'release_timestamp': ('startDate', {unified_timestamp}),
                'duration': ('duration', {parse_duration}),
            }),
            **traverse_obj(response, ('identifierGroup', {
                'series': ('radioSeriesName', {str}),
                'series_id': ('radioSeriesId', {str}),
                'episode': ('radioEpisodeName', {str}),
                'episode_id': ('radioEpisodeId', {str}),
                'categories': ('genre', ..., ['name1', 'name2'], {str}, all, {orderedSet}),
            })),
            'channel': station,
            'uploader': station,
        })

    def _extract_episode_info(self, episode, programme_id, series_meta):
        episode_id = f'{programme_id}_{episode["id"]}'
        aa_vinfo = traverse_obj(episode, ('aa_contents_id', {lambda x: x.split(';')}))
        extended_metadata = self._extract_extended_metadata(episode_id, aa_vinfo)
        fallback_start_time, _, fallback_end_time = traverse_obj(
            aa_vinfo, (4, {str}, {lambda x: (x or '').partition('_')}))

        return {
            **series_meta,
            'id': episode_id,
            'formats': self._extract_m3u8_formats(episode.get('stream_url'), episode_id, fatal=False),
            'container': 'm4a_dash',  # force fixup, AAC-only HLS
            'was_live': True,
            'title': episode.get('program_title'),
            'description': episode.get('program_sub_title'),  # fallback
            'timestamp': unified_timestamp(fallback_end_time),
            'release_timestamp': unified_timestamp(fallback_start_time),
            **extended_metadata,
        }

    def _extract_news_info(self, headline, programme_id, series_meta):
        episode_id = f'{programme_id}_{headline["headline_id"]}'
        episode = traverse_obj(headline, ('file_list', 0, {dict}))

        return {