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

Code before:
            'series_id': '14694',
            'episode': '次世代リアクション王発掘トーナメント',
            'episode_number': 335,
            'episode_id': '14694_2090934_1000117476',
            'timestamp': 1749547434,
            'upload_date': '20250610',
            'release_timestamp': 1749646802,
            'release_date': '20250611',
            'modified_timestamp': 1749647146,
            'modified_date': '20250611',
            'live_status': 'not_live',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        meta = self._window_app(webpage, 'episode', video_id, fatal=False)
        episode = traverse_obj(meta, ('falcorCache', 'catalog', 'episode', video_id, 'value'))

        return {
            **self._extract_from_streaks_api(
                'tbs', f'ref:{video_id}', headers={'Referer': 'https://cu.tbs.co.jp/'}),
            **traverse_obj(episode, {
                'title': ('title', ..., 'value', {str}, any),
                'cast': ('credit', ..., 'name', ..., 'value', {str}, any, {lambda x: x.split(',')}, filter),
                'categories': ('keywords', ..., {str}, filter, all, filter),
                'description': ('description', ..., 'value', {clean_html}, any),
                'duration': ('tv_episode_info', 'duration', {int_or_none}),
                'episode': ('title', lambda _, v: not v.get('is_phonetic'), 'value', {str}, any),
                'episode_id': ('content_id', {str}),
                'episode_number': ('tv_episode_info', 'episode_number', {int_or_none}),
                'genres': ('genre', ..., {str}, filter, all, filter),
                'release_timestamp': ('pub_date', {unified_timestamp}),
                'series': ('custom_data', 'program_name', {str}),
                'tags': ('tags', ..., {str}, filter, all, filter),
                'thumbnail': ('artwork', ..., 'url', {url_or_none}, any),
                'timestamp': ('created_at', {unified_timestamp}),
                'uploader': ('tv_show_info', 'networks', ..., {str}, any),
            }),
            **traverse_obj(episode, ('tv_episode_info', {
                'duration': ('duration', {int_or_none}),
                'episode_number': ('episode_number', {int_or_none}),
                'series_id': ('show_content_id', {str}),
            })),
            'id': video_id,
        }


class TBSJPProgramIE(TBSJPBaseIE):
    _VALID_URL = r'https?://cu\.tbs\.co\.jp/program/(?P<id>\d+)'

Review comment (suggestion):
filter out `['']`
```suggestion
                'cast': ('credit', ..., 'name', ..., 'value', {clean_html}, any, {lambda x: x.split(',')}, ..., {str.strip}, filter, all, filter),
```

Code after:
            'series_id': '14694',
            'episode': 'クロちゃん、寝て起きたら川のほとりにいてその向こう岸に亡くなった父親がいたら死の淵にいるかと思う説 ほか',
            'episode_number': 341,
            'episode_id': '14694_2094162_1000123656',
            'timestamp': 1753778992,
            'upload_date': '20250729',
            'release_timestamp': 1753880402,
            'release_date': '20250730',
            'modified_timestamp': 1753880741,
            'modified_date': '20250730',
            'live_status': 'not_live',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        meta = self._search_window_app_json(webpage, 'episode', video_id, fatal=False)
        episode = traverse_obj(meta, ('falcorCache', 'catalog', 'episode', video_id, 'value'))

        return {
            **self._extract_from_streaks_api(
                'tbs', f'ref:{video_id}', headers={'Origin': 'https://cu.tbs.co.jp'}),
            **traverse_obj(episode, {
                'title': ('title', ..., 'value', {str}, any),
                'cast': (
                    'credit', ..., 'name', ..., 'value', {clean_html}, any,
                    {lambda x: x.split(',')}, ..., {str.strip}, filter, all, filter),
                'categories': ('keywords', ..., {str}, filter, all, filter),
                'description': ('description', ..., 'value', {clean_html}, any),
                'duration': ('tv_episode_info', 'duration', {int_or_none}),
                'episode': ('title', lambda _, v: not v.get('is_phonetic'), 'value', {str}, any),
                'episode_id': ('content_id', {str}),
                'episode_number': ('tv_episode_info', 'episode_number', {int_or_none}),
                'genres': ('genre', ..., {str}, filter, all, filter),
                'release_timestamp': ('pub_date', {unified_timestamp}),
                'series': ('custom_data', 'program_name', {str}),
                'tags': ('tags', ..., {str}, filter, all, filter),
                'thumbnail': ('artwork', ..., 'url', {url_or_none}, any),
                'timestamp': ('created_at', {unified_timestamp}),
                'uploader': ('tv_show_info', 'networks', ..., {str}, any),
            }),
            **traverse_obj(episode, ('tv_episode_info', {
                'duration': ('duration', {int_or_none}),
                'episode_number': ('episode_number', {int_or_none}),
                'series_id': ('show_content_id', {str}),
            })),
            'id': video_id,
        }