Repository: yt-dlp/yt-dlp

Issue:
Support for Le Figaro Videos
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

- [X] I understand that I will be **blocked** if I remove or skip any mandatory\* field

### Checklist

- [X] I'm reporting a new site support request
- [X] I've verified that I'm running yt-dlp version **2023.01.06** ([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 none of provided URLs [violate any copyrights](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
- [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 am willing to share it if required

### Region

Worldwide it seems

### Example URLs

- Single Video: https://video.lefigaro.fr/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-9/
- Single Video: https://video.lefigaro.fr/figaro/video/suivez-en-direct-le-club-le-figaro-politique-avec-yves-threard-9/

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

These are interesting videos provided by the French newspaper Le Figaro. They are about different subjects, French politics, French culture, international politics, and philosophy. They do not require a subscription to the newspaper and would be interesting to archive, if possible. It is possible to download the video directly from the website in a browser, but the command line would be much more convenient.

### 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
[generic] Extracting URL: https://video.lefigaro.fr/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1/
[generic] suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1: Extracting information
[debug] Looking for embeds
[debug] Identified a JSON LD
[generic] Extracting URL: https://video.lefigaro.fr/embed/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1/#__youtubedl_smuggle=%7B%22force_videoid%22%3A+%22suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1%22%2C+%22to_generic%22%3A+true%2C+%22http_headers%22%3A+%7B%22Referer%22%3A+%22https%3A%2F%2Fvideo.lefigaro.fr%2Ffigaro%2Fvideo%2Fsuivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1%2F%22%7D%7D
[generic] suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1: Downloading webpage
[generic] suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1: Extracting information
[debug] Looking for embeds
ERROR: Unsupported URL: https://video.lefigaro.fr/embed/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1/
Traceback (most recent call last):
  File "/home/vagrant/.local/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1502, in wrapper
    return func(self, *args, **kwargs)
  File "/home/vagrant/.local/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1578, in __extract_info
    ie_result = ie.extract(url)
  File "/home/vagrant/.local/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 680, in extract
    ie_result = self._real_extract(url)
  File "/home/vagrant/.local/lib/python3.10/site-packages/yt_dlp/extractor/generic.py", line 2523, in _real_extract
    raise UnsupportedError(url)
yt_dlp.utils.UnsupportedError: Unsupported URL: https://video.lefigaro.fr/embed/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-1/
```



Discussion:

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
@@ -914,6 +914,10 @@
     LePlaylistIE,
     LetvCloudIE,
 )
+from .lefigaro import (
+    LeFigaroVideoEmbedIE,
+    LeFigaroVideoSectionIE,
+)
 from .lego import LEGOIE
 from .lemonde import LemondeIE
 from .lenta import LentaIE
diff --git a/yt_dlp/extractor/lefigaro.py b/yt_dlp/extractor/lefigaro.py
new file mode 100644
--- /dev/null
+++ b/yt_dlp/extractor/lefigaro.py
@@ -0,0 +1,135 @@
+import json
+import math
+
+from .common import InfoExtractor
+from ..utils import (
+    InAdvancePagedList,
+    traverse_obj,
+)
+
+
+class LeFigaroVideoEmbedIE(InfoExtractor):
+    _VALID_URL = r'https?://video\.lefigaro\.fr/embed/[^?#]+/(?P<id>[\w-]+)'
+
+    _TESTS = [{
+        'url': 'https://video.lefigaro.fr/embed/figaro/video/les-francais-ne-veulent-ils-plus-travailler-suivez-en-direct-le-club-le-figaro-idees/',
+        'md5': 'e94de44cd80818084352fcf8de1ce82c',
+        'info_dict': {
+            'id': 'g9j7Eovo',
+            'title': 'Les Français ne veulent-ils plus travailler ? Retrouvez Le Club Le Figaro Idées',
+            'description': 'md5:862b8813148ba4bf10763a65a69dfe41',
+            'upload_date': '20230216',
+            'timestamp': 1676581615,
+            'duration': 3076,
+            'thumbnail': r're:^https?://[^?#]+\.(?:jpeg|jpg)',
+            'ext': 'mp4',
+        },
+    }, {
+        'url': 'https://video.lefigaro.fr/embed/figaro/video/intelligence-artificielle-faut-il-sen-mefier/',
+        'md5': '0b3f10332b812034b3a3eda1ef877c5f',
+        'info_dict': {
+            'id': 'LeAgybyc',
+            'title': 'Intelligence artificielle : faut-il s’en méfier ?',
+            'description': 'md5:249d136e3e5934a67c8cb704f8abf4d2',
+            'upload_date': '20230124',
+            'timestamp': 1674584477,
+            'duration': 860,
+            'thumbnail': r're:^https?://[^?#]+\.(?:jpeg|jpg)',
+            'ext': 'mp4',
+        },
+    }]
+
+    _WEBPAGE_TESTS = [{
+        'url': 'https://video.lefigaro.fr/figaro/video/suivez-en-direct-le-club-le-figaro-international-avec-philippe-gelie-9/',
+        'md5': '3972ddf2d5f8b98699f191687258e2f9',
+        'info_dict': {
+            'id': 'QChnbPYA',
+            'title': 'Où en est le couple franco-allemand ? Retrouvez Le Club Le Figaro International',
+            'description': 'md5:6f47235b7e7c93b366fd8ebfa10572ac',
+            'upload_date': '20230123',
+            'timestamp': 1674503575,
+            'duration': 3153,
+            'thumbnail': r're:^https?://[^?#]+\.(?:jpeg|jpg)',
+            'age_limit': 0,
+            'ext': 'mp4',
+        },
+    }, {
+        'url': 'https://video.lefigaro.fr/figaro/video/la-philosophe-nathalie-sarthou-lajus-est-linvitee-du-figaro-live/',
+        'md5': '3ac0a0769546ee6be41ab52caea5d9a9',
+        'info_dict': {
+            'id': 'QJzqoNbf',
+            'title': 'La philosophe Nathalie Sarthou-Lajus est l’invitée du Figaro Live',
+            'description': 'md5:c586793bb72e726c83aa257f99a8c8c4',
+            'upload_date': '20230217',
+            'timestamp': 1676661986,
+            'duration': 1558,
+            'thumbnail': r're:^https?://[^?#]+\.(?:jpeg|jpg)',
+            'age_limit': 0,
+            'ext': 'mp4',
+        },
+    }]
+
+    def _real_extract(self, url):
+        display_id = self._match_id(url)
+        webpage = self._download_webpage(url, display_id)
+
+        player_data = self._search_nextjs_data(webpage, display_id)['props']['pageProps']['pageData']['playerData']
+
+        return self.url_result(
+            f'jwplatform:{player_data["videoId"]}', title=player_data.get('title'),
+            description=player_data.get('description'), thumbnail=player_data.get('poster'))
+
+
+class LeFigaroVideoSectionIE(InfoExtractor):
+    _VALID_URL = r'https?://video\.lefigaro\.fr/figaro/(?P<id>[\w-]+)/?(?:[#?]|$)'
+
+    _TESTS = [{
+        'url': 'https://video.lefigaro.fr/figaro/le-club-le-figaro-idees/',
+        'info_dict': {
+            'id': 'le-club-le-figaro-idees',
+            'title': 'Le Club Le Figaro Idées',
+        },
+        'playlist_mincount': 14,
+    }, {
+        'url': 'https://video.lefigaro.fr/figaro/factu/',
+        'info_dict': {
+            'id': 'factu',
+            'title': 'Factu',
+        },
+        'playlist_mincount': 519,
+    }]
+
+    _PAGE_SIZE = 20
+
+    def _get_api_response(self, display_id, page_num, note=None):
+        return self._download_json(
+            'https://api-graphql.lefigaro.fr/graphql', display_id, note=note,
+            query={
+                'id': 'flive-website_UpdateListPage_1fb260f996bca2d78960805ac382544186b3225f5bedb43ad08b9b8abef79af6',
+                'variables': json.dumps({
+                    'slug': display_id,
+                    'videosLimit': self._PAGE_SIZE,
+                    'sort': 'DESC',
+                    'order': 'PUBLISHED_AT',
+                    'page': page_num,
+                }).encode(),
+            })
+
+    def _real_extract(self, url):
+        display_id = self._match_id(url)
+        initial_response = self._get_api_response(display_id, page_num=1)['data']['playlist']
+
+        def page_func(page_num):
+            api_response = self._get_api_response(display_id, page_num + 1, note=f'Downloading page {page_num + 1}')
+
+            return [self.url_result(
+                video['embedUrl'], LeFigaroVideoEmbedIE, **traverse_obj(video, {
+                    'title': 'name',
+                    'description': 'description',
+                    'thumbnail': 'thumbnailUrl',
+                })) for video in api_response['data']['playlist']['jsonLd'][0]['itemListElement']]
+
+        entries = InAdvancePagedList(
+            page_func, math.ceil(initial_response['videoCount'] / self._PAGE_SIZE), self._PAGE_SIZE)
+
+        return self.playlist_result(entries, playlist_id=display_id, playlist_title=initial_response.get('title'))


Test patch: