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

Code before:
    }, {
        'url': 'https://www.radiofrance.fr/franceculture/grille-programmes',
        'only_matching': True,
    }]

    def _generate_playlist_entries(self, webpage_url, api_response):
        for entry in api_response['steps']:
            path = traverse_obj(entry, ('expression', 'path'))
            if path is None:
                continue

            yield self.url_result(
                urljoin(webpage_url, f'/{path}'), ie=FranceCultureIE, url_transparent=True, **traverse_obj(entry, {
                    'title': ('expression', 'title'),
                    'thumbnail': ('expression', 'visual', 'src'),
                    'timestamp': 'startTime',
                    'series_id': ('concept', 'id'),
                    'series': ('concept', 'title'),
                }))

    def _real_extract(self, url):
        station, date = self._match_valid_url(url).group('station', 'date')

        api_response = self._download_json(
            f'https://www.radiofrance.fr/api/v2.1/stations/{station}/programs', f'{station}-program',
            query={'date': date} if date is not None else {})

        upload_date = strftime_or_none(api_response.get('date'), '%Y%m%d')
        return self.playlist_result(
            self._generate_playlist_entries(url, api_response), join_nonempty(station, 'program', upload_date),
            upload_date=upload_date)


Review comment (suggestion):
this API endpoint no longer exists / returns 404 error

the site seems to be making requests to `https://www.radiofrance.fr/{station}/grille-programmes/__data.json?date={date}&x-sveltekit-invalidated=0001`, but the returned JSON is nearly incomprehensible

Code after:
        'info_dict': {
            'id': 'francemusique-program-20230318',
            'upload_date': '20230318',
        },
        'playlist_count': 15,
    }, {
        'url': 'https://www.radiofrance.fr/franceculture/grille-programmes',
        'only_matching': True,
    }]

    def _generate_playlist_entries(self, webpage_url, api_response):
        for entry in traverse_obj(api_response, ('steps', lambda _, v: v['expression']['path'])):
            yield self.url_result(
                urljoin(webpage_url, f'/{entry["expression"]["path"]}'), ie=FranceCultureIE,
                url_transparent=True, **traverse_obj(entry, {
                    'title': ('expression', 'title'),
                    'thumbnail': ('expression', 'visual', 'src'),
                    'timestamp': ('startTime', {int_or_none}),
                    'series_id': ('concept', 'id'),
                    'series': ('concept', 'title'),
                }))

    def _real_extract(self, url):
        station, date = self._match_valid_url(url).group('station', 'date')
        webpage = self._download_webpage(url, station)
        grid_data = self._extract_data_from_webpage(webpage, station, 'grid')
        upload_date = strftime_or_none(grid_data.get('date'), '%Y%m%d')

        return self.playlist_result(
            self._generate_playlist_entries(url, grid_data),
            join_nonempty(station, 'program', upload_date), upload_date=upload_date)