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

Code before:
            'duration': 3159,
            'timestamp': 1758340800,
            'release_timestamp': 1758254400,
            'release_date': '20250919',
            'upload_date': '20250920',
        },
    }]

    def _extract_webpage_data(self, url, video_id):
        webpage = self._download_webpage(url, video_id)
        preloaded_state = self._search_json(
            r'window\.__PRELOADED_STATE__\s*=', webpage, 'preloaded state',
            video_id, transform_source=js_to_json)

        return traverse_obj(preloaded_state, (
            ('podcastDetailData', 'showDetailData'), ..., 'episodes',
            lambda _, v: str(v['clipID']) == video_id, any, {require('episode data')}))

    def _real_extract(self, url):
        video_id = self._match_id(url)

        data = traverse_obj(self._download_json(
            f'https://www.cbc.ca/listen/api/v1/clips/{video_id}', video_id, fatal=False), 'data', {dict})
        if not data:
            self.report_warning('Api returned no data. Falling back to webpage parsing')
            data = self._extract_webpage_data(url, video_id)

        return {
            'id': video_id,
            **traverse_obj(data, {
                'url': (('src', 'url'), {url_or_none}, any),
                'title': ('title', {str}),
                'description': ('description', {str}),
                'release_timestamp': ('releasedAt', {int_or_none(scale=1000)}),
                'timestamp': ('airdate', {int_or_none(scale=1000)}),
                'duration': ('duration', {int_or_none}),
            }),
        }


Review comment (bug):
```suggestion
            self.report_warning('API failed to return data. Falling back to webpage parsing')
            webpage = self._download_webpage(url, video_id)
            preloaded_state = self._search_json(
                r'window\.__PRELOADED_STATE__\s*=', webpage, 'preloaded state',
                video_id, transform_source=js_to_json)
            data = traverse_obj(preloaded_state, (
                ('podcastDetailData', 'showDetailData'), ..., 'episodes',
                lambda _, v: str(v['clipID']) == video_id, any, {require('episode data')}))
```

Code after:
            'duration': 3159,
            'timestamp': 1758340800,
            'release_timestamp': 1758254400,
            'release_date': '20250919',
            'upload_date': '20250920',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)

        response = self._download_json(
            f'https://www.cbc.ca/listen/api/v1/clips/{video_id}', video_id, fatal=False)
        data = traverse_obj(response, ('data', {dict}))
        if not data:
            self.report_warning('API failed to return data. Falling back to webpage parsing')
            webpage = self._download_webpage(url, video_id)
            preloaded_state = self._search_json(
                r'window\.__PRELOADED_STATE__\s*=', webpage, 'preloaded state',
                video_id, transform_source=js_to_json)
            data = traverse_obj(preloaded_state, (
                ('podcastDetailData', 'showDetailData'), ..., 'episodes',
                lambda _, v: str(v['clipID']) == video_id, any, {require('episode data')}))

        return {
            'id': video_id,
            **traverse_obj(data, {
                'url': (('src', 'url'), {url_or_none}, any),
                'title': ('title', {str}),
                'description': ('description', {str}),
                'release_timestamp': ('releasedAt', {int_or_none(scale=1000)}),
                'timestamp': ('airdate', {int_or_none(scale=1000)}),
                'duration': ('duration', {int_or_none}),
            }),
        }