Repository: yt-dlp/yt-dlp

Issue:
[Franceinter/FranceCulture]  Support playlists
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2022.06.29** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [X] I've checked that all provided URLs are playable in a browser with the same IP and same login details
- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required

### Region

anywhere

### Provide a description that is worded well enough to be understood

Test link1:
https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
**Error: Unable to extract audio data**

Test link2: (maybe need a separate issue for this one?)
https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022
**ERROR: Unsupported URL**



### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
=================================================================
Log for Test link 1:
=================================================================
./yt-dlp -vU -F "https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789"
[debug] Command-line config: ['-vU', '-F', 'https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.06.29 [9d339c41e] (zip)
[debug] Plugins: ['SamplePluginIE', 'SamplePluginPP']
[debug] Python 3.9.13 (CPython 64bit) - macOS-12.4-arm64-arm-64bit 
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: avconv -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] Checking exe version: avprobe -bsfs
[debug] exe versions: none
[debug] Optional libraries: sqlite3-2.6.0
[debug] Proxy map: {}
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.06.29, Current version: 2022.06.29
yt-dlp is up to date (2022.06.29)
[debug] [FranceCulture] Extracting URL: https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
[FranceCulture] festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022: Downloading webpage
ERROR: [FranceCulture] 2188789: Unable to extract audio data; please report this issue on  https://github.com/yt-dlp/yt-dlp/issues?q= , filling out the appropriate issue template. Confirm you are on the latest version using  yt-dlp -U
  File "./yt-dlp/yt_dlp/extractor/common.py", line 640, in extract
    ie_result = self._real_extract(url)
  File "./yt-dlp/yt_dlp/extractor/radiofrance.py", line 87, in _real_extract
    video_data = self._search_json('', webpage, 'audio data', display_id, contains_pattern=r'\s*"@type"\s*:\s*"AudioObject"\s*.+')
  File "./yt-dlp/yt_dlp/extractor/common.py", line 1206, in _search_json
    json_string = self._search_regex(
  File "./yt-dlp/yt_dlp/extractor/common.py", line 1192, in _search_regex
    raise RegexNotFoundError('Unable to extract %s' % _name)

=================================================================
Log for Test link 2:
=================================================================
./yt-dlp -vU -F "https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022"
[debug] Command-line config: ['-vU', '-F', 'https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.06.29 [9d339c41e] (zip)
[debug] Plugins: ['SamplePluginIE', 'SamplePluginPP']
[debug] Python 3.9.13 (CPython 64bit) - macOS-12.4-arm64-arm-64bit 
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: avconv -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] Checking exe version: avprobe -bsfs
[debug] exe versions: none
[debug] Optional libraries: sqlite3-2.6.0
[debug] Proxy map: {}
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.06.29, Current version: 2022.06.29
yt-dlp is up to date (2022.06.29)
[debug] [generic] Extracting URL: https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022
[generic] grille-programmes?date=04-07-2022: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] grille-programmes?date=04-07-2022: Downloading webpage
[generic] grille-programmes?date=04-07-2022: Extracting information
[debug] Looking for video embeds
ERROR: Unsupported URL: https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022
Traceback (most recent call last):
  File "./yt-dlp/yt_dlp/YoutubeDL.py", line 1418, in wrapper
    return func(self, *args, **kwargs)
  File "./yt-dlp/yt_dlp/YoutubeDL.py", line 1488, in __extract_info
    ie_result = ie.extract(url)
  File "./yt-dlp/yt_dlp/extractor/common.py", line 640, in extract
    ie_result = self._real_extract(url)
  File "./yt-dlp/yt_dlp/extractor/generic.py", line 4130, in _real_extract
    raise UnsupportedError(url)
yt_dlp.utils.UnsupportedError: Unsupported URL: https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022
```



Discussion:
It seems related to https://github.com/yt-dlp/yt-dlp/pull/4065#issuecomment-1153959809 and https://github.com/yt-dlp/yt-dlp/pull/4065#issuecomment-1158040287
For some reason the first URL works here (latest `master` build):

```
yt-dlp -vU https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
[debug] Command-line config: ['-vU', 'https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789']
[debug] User config "/home/nico/.config/yt-dlp/config": ['-o', '/dev/shm/test-dlp/%(title)s.%(ext)s', '-f', 'bestvideo[height<=1080][ext=mp4][vcodec!*=av01]+bestaudio[ext=m4a]/best[ext=mp4]/best', '--no-mtime', '--convert-thumbnails', 'jpg', '--embed-thumbnail', '--ppa', 'ThumbnailsConvertor:-q:v 1', '--force-ipv4', '--sponsorblock-remove', 'all', '--console-title', '--downloader', 'aria2c', '--downloader-args', 'aria2c:--http-accept-gzip=true -x 10 -s 10 -j 10 -k 1M --log-level=info --file-allocation=none']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.07.03 [385f7f389] (zip)
[debug] Python 3.8.10 (CPython 64bit) - Linux-5.4.0-121-generic-x86_64-with-glibc2.29 (glibc 2.31)
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg N-107246-g59a58d1a85-Nico-20220626 (fdk,setts), ffprobe N-107246-g59a58d1a85-Nico-20220626, phantomjs 2.1.1
[debug] Optional libraries: Cryptodome-3.12.0, brotli-1.0.9, certifi-2019.11.28, mutagen-1.45.1, secretstorage-2.3.1, sqlite3-2.6.0, websockets-10.1
[debug] Proxy map: {}
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.06.29, Current version: 2022.07.03
yt-dlp is up to date (2022.07.03)
[debug] [FranceCulture] Extracting URL: https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
[FranceCulture] festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022: Downloading webpage
[SponsorBlock] SponsorBlock is not supported for FranceCulture
[info] 2188789: Downloading 1 format(s): 0
[info] Downloading video thumbnail 0 ...
[info] Writing video thumbnail 0 to: /dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg
[ThumbnailsConvertor] Not converting thumbnail "/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg"; already is in target format jpg
[debug] Invoking aria2c downloader on "https://media.radiofrance-podcast.net/podcast09/15361-05.07.2022-ITEMA_23080098-2022F23111S0186-22.mp3"
[download] Destination: /dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3
[debug] aria2c command line: aria2c -c --console-log-level=warn --summary-interval=0 --download-result=hide --http-accept-gzip=true --file-allocation=none -x16 -j16 -s16 --min-split-size 1M --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36' --header 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' --header 'Accept-Language: en-us,en;q=0.5' --header 'Sec-Fetch-Mode: navigate' --interface 0.0.0.0 --check-certificate=true --remote-time=false --show-console-readout=true --http-accept-gzip=true -x 10 -s 10 -j 10 -k 1M --log-level=info --file-allocation=none --dir /dev/shm/test-dlp/ --out './Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3.part' --auto-file-renaming=false -- https://media.radiofrance-podcast.net/podcast09/15361-05.07.2022-ITEMA_23080098-2022F23111S0186-22.mp3
[aria2c] Downloaded 168815484 bytes:8.9MiB]                                    
[download] 100% of 160.99MiB in 00:19
[ModifyChapters] SponsorBlock information is unavailable
[EmbedThumbnail] ffmpeg: Adding thumbnail to "/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3"
[debug] ffmpeg command line: ffmpeg -y -loglevel repeat+info -i 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3' -i 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg' -c copy -map 0:0 -map 1:0 -write_id3v1 1 -id3v2_version 3 -metadata:s:v 'title="Album cover"' -metadata:s:v 'comment="Cover (front)"' -movflags +faststart 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.temp.mp3'
```
The second one gives the same error.
The second [link](https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022) is more like a "playlist", and it's not currently supported

So I would call it a feature request instead of a bug


> For some reason the first URL works here (latest `master` build):
> 
> ```python
> yt-dlp -vU https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
> [debug] Command-line config: ['-vU', 'https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789']
> [debug] User config "/home/nico/.config/yt-dlp/config": ['-o', '/dev/shm/test-dlp/%(title)s.%(ext)s', '-f', 'bestvideo[height<=1080][ext=mp4][vcodec!*=av01]+bestaudio[ext=m4a]/best[ext=mp4]/best', '--no-mtime', '--convert-thumbnails', 'jpg', '--embed-thumbnail', '--ppa', 'ThumbnailsConvertor:-q:v 1', '--force-ipv4', '--sponsorblock-remove', 'all', '--console-title', '--downloader', 'aria2c', '--downloader-args', 'aria2c:--http-accept-gzip=true -x 10 -s 10 -j 10 -k 1M --log-level=info --file-allocation=none']
> [debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
> [debug] yt-dlp version 2022.07.03 [385f7f389] (zip)
> [debug] Python 3.8.10 (CPython 64bit) - Linux-5.4.0-121-generic-x86_64-with-glibc2.29 (glibc 2.31)
> [debug] Checking exe version: ffmpeg -bsfs
> [debug] Checking exe version: ffprobe -bsfs
> [debug] exe versions: ffmpeg N-107246-g59a58d1a85-Nico-20220626 (fdk,setts), ffprobe N-107246-g59a58d1a85-Nico-20220626, phantomjs 2.1.1
> [debug] Optional libraries: Cryptodome-3.12.0, brotli-1.0.9, certifi-2019.11.28, mutagen-1.45.1, secretstorage-2.3.1, sqlite3-2.6.0, websockets-10.1
> [debug] Proxy map: {}
> [debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
> Latest version: 2022.06.29, Current version: 2022.07.03
> yt-dlp is up to date (2022.07.03)
> [debug] [FranceCulture] Extracting URL: https://www.radiofrance.fr/franceinter/podcasts/concert-inter/festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022-2188789
> [FranceCulture] festival-jazz-a-vienne-avec-michael-kiwanuka-black-pumas-du-mardi-05-juillet-2022: Downloading webpage
> [SponsorBlock] SponsorBlock is not supported for FranceCulture
> [info] 2188789: Downloading 1 format(s): 0
> [info] Downloading video thumbnail 0 ...
> [info] Writing video thumbnail 0 to: /dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg
> [ThumbnailsConvertor] Not converting thumbnail "/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg"; already is in target format jpg
> [debug] Invoking aria2c downloader on "https://media.radiofrance-podcast.net/podcast09/15361-05.07.2022-ITEMA_23080098-2022F23111S0186-22.mp3"
> [download] Destination: /dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3
> [debug] aria2c command line: aria2c -c --console-log-level=warn --summary-interval=0 --download-result=hide --http-accept-gzip=true --file-allocation=none -x16 -j16 -s16 --min-split-size 1M --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36' --header 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' --header 'Accept-Language: en-us,en;q=0.5' --header 'Sec-Fetch-Mode: navigate' --interface 0.0.0.0 --check-certificate=true --remote-time=false --show-console-readout=true --http-accept-gzip=true -x 10 -s 10 -j 10 -k 1M --log-level=info --file-allocation=none --dir /dev/shm/test-dlp/ --out './Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3.part' --auto-file-renaming=false -- https://media.radiofrance-podcast.net/podcast09/15361-05.07.2022-ITEMA_23080098-2022F23111S0186-22.mp3
> [aria2c] Downloaded 168815484 bytes:8.9MiB]                                    
> [download] 100% of 160.99MiB in 00:19
> [ModifyChapters] SponsorBlock information is unavailable
> [EmbedThumbnail] ffmpeg: Adding thumbnail to "/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3"
> [debug] ffmpeg command line: ffmpeg -y -loglevel repeat+info -i 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.mp3' -i 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.jpg' -c copy -map 0:0 -map 1:0 -write_id3v1 1 -id3v2_version 3 -metadata:s:v 'title="Album cover"' -metadata:s:v 'comment="Cover (front)"' -movflags +faststart 'file:/dev/shm/test-dlp/Festival Jazz à Vienne avec Michael Kiwanuka et Black Pumas.temp.mp3'
> ```
> 
> The second one gives the same error.

Indeed, the first link works for me now as well. Maybe some temporary glitch or my test build got messed up. My apologies for the confusion.

as for the second one - it's "playlist-like" for sure. Should I be creating a new issue for that one and just close this one?
No need, just update the title and description on this one
> as for the second one - it's "playlist-like" for sure.

Indeed, it downloads (very slowly) with this:

`yt-dlp --downloader ffmpeg --hls-use-mpegts https://stream.radiofrance.fr/franceinter/franceinter_hifi.m3u8?id=radiofrance`
Link 2 is just a programme guide with 4 items, no?

Expect a PR at yt-dl for revamped radiofrance.py handling (unpaginated) lists, RF podcasts, francebleu.fr, hyperradio.fr, and deleting/superseding france(culture|inter).py.

The RF audio pages generally have the audio media in a ld+json block, if only the json_ld processing could find it, as well as most of the other desired metadata.
> Link 2 is just a programme guide with 4 items, no?

Yep, but I see +20 items 🤔 

![image](https://user-images.githubusercontent.com/26639800/177858406-478fd4fc-0b0b-4878-8dd2-c3422f089e29.png)


4's wrong (that was the link from the yt-dl issue):
```shellsession
$ python -m youtube_dl -j --flat-playlist 'https://www.radiofrance.fr/franceinter/grille-programmes?date=04-07-2022' | wc -l
18
$
```
Either the prototype is missing some of the items, or the schedule contains duplicate links. **In fact both**:
* 26 programmes, but one with no link = 25
* 22 .../podcasts/... items including 4 duplicates = 18 (as above)
* 3 .../emissions/... items including 1 duplicate = 2

Total = 20 items from 25 links.

Resolving patch:
diff --git a/yt_dlp/extractor/_extractors.py b/yt_dlp/extractor/_extractors.py
--- a/yt_dlp/extractor/_extractors.py
+++ b/yt_dlp/extractor/_extractors.py
@@ -1555,7 +1555,14 @@
 from .radiode import RadioDeIE
 from .radiojavan import RadioJavanIE
 from .radiobremen import RadioBremenIE
-from .radiofrance import FranceCultureIE, RadioFranceIE
+from .radiofrance import (
+    FranceCultureIE,
+    RadioFranceIE,
+    RadioFranceLiveIE,
+    RadioFrancePodcastIE,
+    RadioFranceProfileIE,
+    RadioFranceProgramScheduleIE,
+)
 from .radiozet import RadioZetPodcastIE
 from .radiokapital import (
     RadioKapitalIE,
diff --git a/yt_dlp/extractor/radiofrance.py b/yt_dlp/extractor/radiofrance.py
--- a/yt_dlp/extractor/radiofrance.py
+++ b/yt_dlp/extractor/radiofrance.py
@@ -1,7 +1,18 @@
+import itertools
 import re
+import urllib.parse
 
 from .common import InfoExtractor
-from ..utils import parse_duration, unified_strdate
+from ..utils import (
+    int_or_none,
+    join_nonempty,
+    js_to_json,
+    parse_duration,
+    strftime_or_none,
+    traverse_obj,
+    unified_strdate,
+    urljoin,
+)
 
 
 class RadioFranceIE(InfoExtractor):
@@ -56,8 +67,32 @@ def _real_extract(self, url):
         }
 
 
-class FranceCultureIE(InfoExtractor):
-    _VALID_URL = r'https?://(?:www\.)?radiofrance\.fr/(?:franceculture|fip|francemusique|mouv|franceinter)/podcasts/(?:[^?#]+/)?(?P<display_id>[^?#]+)-(?P<id>\d+)($|[?#])'
+class RadioFranceBaseIE(InfoExtractor):
+    _VALID_URL_BASE = r'https?://(?:www\.)?radiofrance\.fr'
+
+    _STATIONS_RE = '|'.join(map(re.escape, (
+        'franceculture',
+        'franceinfo',
+        'franceinter',
+        'francemusique',
+        'fip',
+        'mouv',
+    )))
+
+    def _extract_data_from_webpage(self, webpage, display_id, key):
+        return traverse_obj(self._search_json(
+            r'\bconst\s+data\s*=', webpage, key, display_id,
+            contains_pattern=r'(\[\{.*?\}\]);', transform_source=js_to_json),
+            (..., 'data', key, {dict}), get_all=False) or {}
+
+
+class FranceCultureIE(RadioFranceBaseIE):
+    _VALID_URL = rf'''(?x)
+        {RadioFranceBaseIE._VALID_URL_BASE}
+        /(?:{RadioFranceBaseIE._STATIONS_RE})
+        /podcasts/(?:[^?#]+/)?(?P<display_id>[^?#]+)-(?P<id>\d{{6,}})(?:$|[?#])
+    '''
+
     _TESTS = [
         {
             'url': 'https://www.radiofrance.fr/franceculture/podcasts/science-en-questions/la-physique-d-einstein-aiderait-elle-a-comprendre-le-cerveau-8440487',
@@ -67,14 +102,30 @@ class FranceCultureIE(InfoExtractor):
                 'ext': 'mp3',
                 'title': 'La physique d’Einstein aiderait-elle à comprendre le cerveau ?',
                 'description': 'Existerait-il un pont conceptuel entre la physique de l’espace-temps et les neurosciences ?',
-                'thumbnail': 'https://cdn.radiofrance.fr/s3/cruiser-production/2022/05/d184e7a3-4827-4494-bf94-04ed7b120db4/1200x630_gettyimages-200171095-001.jpg',
+                'thumbnail': r're:^https?://.*\.(?:jpg|png)',
                 'upload_date': '20220514',
                 'duration': 2750,
             },
         },
+        {
+            'url': 'https://www.radiofrance.fr/franceinter/podcasts/le-7-9-30/le-7-9-30-du-vendredi-10-mars-2023-2107675',
+            'info_dict': {
+                'id': '2107675',
+                'display_id': 'le-7-9-30-du-vendredi-10-mars-2023',
+                'title': 'Inflation alimentaire : comment en sortir ? - Régis Debray et Claude Grange - Cybèle Idelot',
+                'description': 'md5:36ee74351ede77a314fdebb94026b916',
+                'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+                'upload_date': '20230310',
+                'duration': 8977,
+                'ext': 'mp3',
+            },
+        },
         {
             'url': 'https://www.radiofrance.fr/franceinter/podcasts/la-rafle-du-vel-d-hiv-une-affaire-d-etat/les-racines-du-crime-episode-1-3715507',
             'only_matching': True,
+        }, {
+            'url': 'https://www.radiofrance.fr/franceinfo/podcasts/le-billet-sciences/sante-bientot-un-vaccin-contre-l-asthme-allergique-3057200',
+            'only_matching': True,
         }
     ]
 
@@ -89,7 +140,6 @@ def _real_extract(self, url):
             'id': video_id,
             'display_id': display_id,
             'url': video_data['contentUrl'],
-            'ext': video_data.get('encodingFormat'),
             'vcodec': 'none' if video_data.get('encodingFormat') == 'mp3' else None,
             'duration': parse_duration(video_data.get('duration')),
             'title': self._html_search_regex(r'(?s)<h1[^>]*itemprop="[^"]*name[^"]*"[^>]*>(.+?)</h1>',
@@ -102,3 +152,322 @@ def _real_extract(self, url):
             'upload_date': unified_strdate(self._search_regex(
                 r'"datePublished"\s*:\s*"([^"]+)', webpage, 'timestamp', fatal=False))
         }
+
+
+class RadioFranceLiveIE(RadioFranceBaseIE):
+    _VALID_URL = rf'''(?x)
+        https?://(?:www\.)?radiofrance\.fr
+        /(?P<id>{RadioFranceBaseIE._STATIONS_RE})
+        /?(?P<substation_id>radio-[\w-]+)?(?:[#?]|$)
+    '''
+
+    _TESTS = [{
+        'url': 'https://www.radiofrance.fr/franceinter/',
+        'info_dict': {
+            'id': 'franceinter',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/franceculture',
+        'info_dict': {
+            'id': 'franceculture',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv/radio-musique-kids-family',
+        'info_dict': {
+            'id': 'mouv-radio-musique-kids-family',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv/radio-rnb-soul',
+        'info_dict': {
+            'id': 'mouv-radio-rnb-soul',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv/radio-musique-mix',
+        'info_dict': {
+            'id': 'mouv-radio-musique-mix',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/fip/radio-rock',
+        'info_dict': {
+            'id': 'fip-radio-rock',
+            'title': str,
+            'live_status': 'is_live',
+            'ext': 'aac',
+        },
+        'params': {
+            'skip_download': 'Livestream',
+        },
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv',
+        'only_matching': True,
+    }]
+
+    def _real_extract(self, url):
+        station_id, substation_id = self._match_valid_url(url).group('id', 'substation_id')
+
+        if substation_id:
+            webpage = self._download_webpage(url, station_id)
+            api_response = self._extract_data_from_webpage(webpage, station_id, 'webRadioData')
+        else:
+            api_response = self._download_json(
+                f'https://www.radiofrance.fr/{station_id}/api/live', station_id)
+
+        formats, subtitles = [], {}
+        for media_source in traverse_obj(api_response, (('now', None), 'media', 'sources', lambda _, v: v['url'])):
+            if media_source.get('format') == 'hls':
+                fmts, subs = self._extract_m3u8_formats_and_subtitles(media_source['url'], station_id, fatal=False)
+                formats.extend(fmts)
+                self._merge_subtitles(subs, target=subtitles)
+            else:
+                formats.append({
+                    'url': media_source['url'],
+                    'abr': media_source.get('bitrate'),
+                })
+
+        return {
+            'id': join_nonempty(station_id, substation_id),
+            'title': traverse_obj(api_response, ('visual', 'legend')) or join_nonempty(
+                ('now', 'firstLine', 'title'), ('now', 'secondLine', 'title'), from_dict=api_response, delim=' - '),
+            'formats': formats,
+            'subtitles': subtitles,
+            'is_live': True,
+        }
+
+
+class RadioFrancePlaylistBase(RadioFranceBaseIE):
+    """Subclasses must set _METADATA_KEY"""
+
+    def _call_api(self, content_id, cursor, page_num):
+        raise NotImplementedError('This method must be implemented by subclasses')
+
+    def _generate_playlist_entries(self, content_id, content_response):
+        for page_num in itertools.count(2):
+            for entry in content_response['items']:
+                yield self.url_result(
+                    f'https://www.radiofrance.fr/{entry["path"]}', url_transparent=True, **traverse_obj(entry, {
+                        'title': 'title',
+                        'description': 'standFirst',
+                        'timestamp': ('publishedDate', {int_or_none}),
+                        'thumbnail': ('visual', 'src'),
+                    }))
+
+            next_cursor = traverse_obj(content_response, (('pagination', None), 'next'), get_all=False)
+            if not next_cursor:
+                break
+
+            content_response = self._call_api(content_id, next_cursor, page_num)
+
+    def _real_extract(self, url):
+        display_id = self._match_id(url)
+
+        metadata = self._download_json(
+            'https://www.radiofrance.fr/api/v2.1/path', display_id,
+            query={'value': urllib.parse.urlparse(url).path})['content']
+
+        content_id = metadata['id']
+
+        return self.playlist_result(
+            self._generate_playlist_entries(content_id, metadata[self._METADATA_KEY]), content_id,
+            display_id=display_id, **{**traverse_obj(metadata, {
+                'title': 'title',
+                'description': 'standFirst',
+                'thumbnail': ('visual', 'src'),
+            }), **traverse_obj(metadata, {
+                'title': 'name',
+                'description': 'role',
+            })})
+
+
+class RadioFrancePodcastIE(RadioFrancePlaylistBase):
+    _VALID_URL = rf'''(?x)
+        {RadioFranceBaseIE._VALID_URL_BASE}
+        /(?:{RadioFranceBaseIE._STATIONS_RE})
+        /podcasts/(?P<id>[\w-]+)/?(?:[?#]|$)
+    '''
+
+    _TESTS = [{
+        'url': 'https://www.radiofrance.fr/franceinfo/podcasts/le-billet-vert',
+        'info_dict': {
+            'id': 'eaf6ef81-a980-4f1c-a7d1-8a75ecd54b17',
+            'display_id': 'le-billet-vert',
+            'title': 'Le billet sciences',
+            'description': 'md5:eb1007b34b0c0a680daaa71525bbd4c1',
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'playlist_mincount': 11,
+    }, {
+        'url': 'https://www.radiofrance.fr/franceinter/podcasts/jean-marie-le-pen-l-obsession-nationale',
+        'info_dict': {
+            'id': '566fd524-3074-4fbc-ac69-8696f2152a54',
+            'display_id': 'jean-marie-le-pen-l-obsession-nationale',
+            'title': 'Jean-Marie Le Pen, l\'obsession nationale',
+            'description': 'md5:a07c0cfb894f6d07a62d0ad12c4b7d73',
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'playlist_count': 7,
+    }, {
+        'url': 'https://www.radiofrance.fr/franceculture/podcasts/serie-thomas-grjebine',
+        'info_dict': {
+            'id': '63c1ddc9-9f15-457a-98b2-411bac63f48d',
+            'display_id': 'serie-thomas-grjebine',
+            'title': 'Thomas Grjebine',
+        },
+        'playlist_count': 1,
+    }, {
+        'url': 'https://www.radiofrance.fr/fip/podcasts/certains-l-aiment-fip',
+        'info_dict': {
+            'id': '143dff38-e956-4a5d-8576-1c0b7242b99e',
+            'display_id': 'certains-l-aiment-fip',
+            'title': 'Certains l’aiment Fip',
+            'description': 'md5:ff974672ba00d4fd5be80fb001c5b27e',
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'playlist_mincount': 321,
+    }, {
+        'url': 'https://www.radiofrance.fr/franceinter/podcasts/le-7-9',
+        'only_matching': True,
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv/podcasts/dirty-mix',
+        'only_matching': True,
+    }]
+
+    _METADATA_KEY = 'expressions'
+
+    def _call_api(self, podcast_id, cursor, page_num):
+        return self._download_json(
+            f'https://www.radiofrance.fr/api/v2.1/concepts/{podcast_id}/expressions', podcast_id,
+            note=f'Downloading page {page_num}', query={'pageCursor': cursor})
+
+
+class RadioFranceProfileIE(RadioFrancePlaylistBase):
+    _VALID_URL = rf'{RadioFranceBaseIE._VALID_URL_BASE}/personnes/(?P<id>[\w-]+)'
+
+    _TESTS = [{
+        'url': 'https://www.radiofrance.fr/personnes/thomas-pesquet?p=3',
+        'info_dict': {
+            'id': '86c62790-e481-11e2-9f7b-782bcb6744eb',
+            'display_id': 'thomas-pesquet',
+            'title': 'Thomas Pesquet',
+            'description': 'Astronaute à l\'agence spatiale européenne',
+        },
+        'playlist_mincount': 212,
+    }, {
+        'url': 'https://www.radiofrance.fr/personnes/eugenie-bastie',
+        'info_dict': {
+            'id': '9593050b-0183-4972-a0b5-d8f699079e02',
+            'display_id': 'eugenie-bastie',
+            'title': 'Eugénie Bastié',
+            'description': 'Journaliste et essayiste',
+            'thumbnail': r're:^https?://.*\.(?:jpg|png)',
+        },
+        'playlist_mincount': 39,
+    }, {
+        'url': 'https://www.radiofrance.fr/personnes/lea-salame',
+        'only_matching': True,
+    }]
+
+    _METADATA_KEY = 'documents'
+
+    def _call_api(self, profile_id, cursor, page_num):
+        resp = self._download_json(
+            f'https://www.radiofrance.fr/api/v2.1/taxonomy/{profile_id}/documents', profile_id,
+            note=f'Downloading page {page_num}', query={
+                'relation': 'personality',
+                'cursor': cursor,
+            })
+
+        resp['next'] = traverse_obj(resp, ('pagination', 'next'))
+        return resp
+
+
+class RadioFranceProgramScheduleIE(RadioFranceBaseIE):
+    _VALID_URL = rf'''(?x)
+        {RadioFranceBaseIE._VALID_URL_BASE}
+        /(?P<station>{RadioFranceBaseIE._STATIONS_RE})
+        /grille-programmes(?:\?date=(?P<date>[\d-]+))?
+    '''
+
+    _TESTS = [{
+        'url': 'https://www.radiofrance.fr/franceinter/grille-programmes?date=17-02-2023',
+        'info_dict': {
+            'id': 'franceinter-program-20230217',
+            'upload_date': '20230217',
+        },
+        'playlist_count': 25,
+    }, {
+        'url': 'https://www.radiofrance.fr/franceculture/grille-programmes?date=01-02-2023',
+        'info_dict': {
+            'id': 'franceculture-program-20230201',
+            'upload_date': '20230201',
+        },
+        'playlist_count': 25,
+    }, {
+        'url': 'https://www.radiofrance.fr/mouv/grille-programmes?date=19-03-2023',
+        'info_dict': {
+            'id': 'mouv-program-20230319',
+            'upload_date': '20230319',
+        },
+        'playlist_count': 3,
+    }, {
+        'url': 'https://www.radiofrance.fr/francemusique/grille-programmes?date=18-03-2023',
+        '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)


Test patch: