yt-dlp: [FranceTVSite] [FranceTV] No video formats found! (HTTP Error 400: Bad Request) france.tv

DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

  • I understand that I will be blocked if I intentionally remove or skip any mandatory* field

Checklist

Region

France

Provide a description that is worded well enough to be understood

Downloading videos from france.tv is not possible anymore (as of 29/02/2024 - 10:00 UTC)

Unable to download JSON metadata: HTTP Error 400: Bad Request ERROR: ‘NoneType’ object has no attribute ‘strip’

Provide verbose output that clearly demonstrates the problem

  • Run your yt-dlp command with -vU flag added (yt-dlp -vU <your command line>)
  • If using API, add 'verbose': True to YoutubeDL params instead
  • Copy the WHOLE output (starting with [debug] Command-line config) and insert it below

Complete Verbose Output

laurent@inoculum:Vidéos$ yt-dlp -vU -F https://www.france.tv/france-2/laissez-vous-guider/laissez-vous-guider-saison-1/5706441-les-merveilles-de-l-egypte-antique.html
[debug] Command-line config: ['-vU', '-F', 'https://www.france.tv/france-2/laissez-vous-guider/laissez-vous-guider-saison-1/5706441-les-merveilles-de-l-egypte-antique.html']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version stable@2023.12.30 from yt-dlp/yt-dlp [f10589e34] (zip)
[debug] Python 3.11.2 (CPython x86_64 64bit) - Linux-6.1.0-17-amd64-x86_64-with-glibc2.36 (OpenSSL 3.0.11 19 Sep 2023, glibc 2.36)
[debug] exe versions: ffmpeg 5.1.4-0 (setts), ffprobe 5.1.4-0
[debug] Optional libraries: brotli-1.0.9, certifi-2022.09.24, mutagen-1.46.0, requests-2.28.1, secretstorage-3.3.3, sqlite3-3.40.1, urllib3-1.26.12
[debug] Proxy map: {}
[debug] Request Handlers: urllib
[debug] Loaded 1798 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: stable@2023.12.30 from yt-dlp/yt-dlp
yt-dlp is up to date (stable@2023.12.30 from yt-dlp/yt-dlp)
[FranceTVSite] Extracting URL: https://www.france.tv/france-2/laissez-vous-guider/laissez-vous-guider-saison-1/5706441-les-merveilles-de-l-egypte-antique.html
[FranceTVSite] 5706441-les-merveilles-de-l-egypte-antique: Downloading webpage
[FranceTV] Extracting URL: francetv:906e5b03-5cb8-4d65-9924-dc60f13f7c5b
[FranceTV] 906e5b03-5cb8-4d65-9924-dc60f13f7c5b: Downloading desktop video JSON
WARNING: [FranceTV] Unable to download JSON metadata: HTTP Error 400: Bad Request
[FranceTV] 906e5b03-5cb8-4d65-9924-dc60f13f7c5b: Downloading mobile video JSON
WARNING: [FranceTV] Unable to download JSON metadata: HTTP Error 400: Bad Request
[debug] Extractor gave empty title. Creating a generic title
ERROR: [FranceTV] 906e5b03-5cb8-4d65-9924-dc60f13f7c5b: No video formats found!; 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
Traceback (most recent call last):
  File "/home/laurent/.local/bin/yt-dlp/yt_dlp/YoutubeDL.py", line 1587, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/laurent/.local/bin/yt-dlp/yt_dlp/YoutubeDL.py", line 1743, in __extract_info
    return self.process_ie_result(ie_result, download, extra_info)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/laurent/.local/bin/yt-dlp/yt_dlp/YoutubeDL.py", line 1802, in process_ie_result
    ie_result = self.process_video_result(ie_result, download=download)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/laurent/.local/bin/yt-dlp/yt_dlp/YoutubeDL.py", line 2781, in process_video_result
    self.raise_no_formats(info_dict)
  File "/home/laurent/.local/bin/yt-dlp/yt_dlp/YoutubeDL.py", line 1085, in raise_no_formats
    raise ExtractorError(msg, video_id=info['id'], ie=info['extractor'],
yt_dlp.utils.ExtractorError: [FranceTV] 906e5b03-5cb8-4d65-9924-dc60f13f7c5b: No video formats found!; 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

About this issue

  • Original URL
  • State: closed
  • Created 4 months ago
  • Reactions: 16
  • Comments: 45 (17 by maintainers)

Commits related to this issue

Most upvoted comments

new patch for some in-region users to test with:

diff --git a/yt_dlp/extractor/francetv.py b/yt_dlp/extractor/francetv.py
index 0ceecde74..bc9fa9fc0 100644
--- a/yt_dlp/extractor/francetv.py
+++ b/yt_dlp/extractor/francetv.py
@@ -1,21 +1,29 @@
+import urllib.parse
+
 from .common import InfoExtractor
 from .dailymotion import DailymotionIE
 from ..utils import (
     ExtractorError,
     determine_ext,
+    filter_dict,
     format_field,
     int_or_none,
     join_nonempty,
     parse_iso8601,
     parse_qs,
+    smuggle_url,
+    unsmuggle_url,
+    url_or_none,
 )
 
 
 class FranceTVBaseInfoExtractor(InfoExtractor):
-    def _make_url_result(self, video_or_full_id, catalog=None):
+    def _make_url_result(self, video_or_full_id, catalog=None, url=None):
         full_id = 'francetv:%s' % video_or_full_id
         if '@' not in video_or_full_id and catalog:
             full_id += '@%s' % catalog
+        if url:
+            full_id = smuggle_url(full_id, {'origin': urllib.parse.urlparse(url).hostname})
         return self.url_result(
             full_id, ie=FranceTVIE.ie_key(),
             video_id=video_or_full_id.split('@')[0])
@@ -76,7 +84,7 @@ class FranceTVIE(InfoExtractor):
         'only_matching': True,
     }]
 
-    def _extract_video(self, video_id, catalogue=None):
+    def _extract_video(self, video_id, catalogue=None, origin=None):
         # Videos are identified by idDiffusion so catalogue part is optional.
         # However when provided, some extra formats may be returned so we pass
         # it if available.
@@ -94,10 +102,11 @@ def _extract_video(self, video_id, catalogue=None):
         for device_type in ('desktop', 'mobile'):
             dinfo = self._download_json(
                 'https://player.webservices.francetelevisions.fr/v1/videos/%s' % video_id,
-                video_id, 'Downloading %s video JSON' % device_type, query={
+                video_id, 'Downloading %s video JSON' % device_type, query=filter_dict({
                     'device_type': device_type,
                     'browser': 'chrome',
-                }, fatal=False)
+                    'domain': origin,
+                }), fatal=False)
 
             if not dinfo:
                 continue
@@ -130,18 +139,17 @@ def _extract_video(self, video_id, catalogue=None):
         subtitles = {}
         for video in videos:
             format_id = video.get('format')
+            video_url = url_or_none(video.get('url'))
+            if not video_url:
+                continue
 
-            video_url = None
             if video.get('workflow') == 'token-akamai':
-                token_url = video.get('token')
-                if token_url:
+                if token_url := url_or_none(video.get('token')):
                     token_json = self._download_json(
-                        token_url, video_id,
-                        'Downloading signed %s manifest URL' % format_id)
-                    if token_json:
-                        video_url = token_json.get('url')
-            if not video_url:
-                video_url = video.get('url')
+                        token_url, video_id, f'Downloading signed {format_id} manifest URL',
+                        fatal=False, query={'format': 'json', 'url': video_url}) or {}
+                    if tokenized_url := url_or_none(token_json.get('url')):
+                        video_url = tokenized_url
 
             ext = determine_ext(video_url)
             if ext == 'f4m':
@@ -213,6 +221,7 @@ def _extract_video(self, video_id, catalogue=None):
         }
 
     def _real_extract(self, url):
+        url, smuggled_data = unsmuggle_url(url, {})
         mobj = self._match_valid_url(url)
         video_id = mobj.group('id')
         catalog = mobj.group('catalog')
@@ -224,7 +233,7 @@ def _real_extract(self, url):
             if not video_id:
                 raise ExtractorError('Invalid URL', expected=True)
 
-        return self._extract_video(video_id, catalog)
+        return self._extract_video(video_id, catalog, origin=smuggled_data.get('origin'))
 
 
 class FranceTVSiteIE(FranceTVBaseInfoExtractor):
@@ -314,7 +323,7 @@ def _real_extract(self, url):
                 r'(?:href=|player\.setVideo\(\s*)"http://videos?\.francetv\.fr/video/([^@]+@[^"]+)"',
                 webpage, 'video ID').split('@')
 
-        return self._make_url_result(video_id, catalogue)
+        return self._make_url_result(video_id, catalogue, url=url)
 
 
 class FranceTVInfoIE(FranceTVBaseInfoExtractor):
@@ -405,4 +414,4 @@ def _real_extract(self, url):
              r'(?:data-id|<figure[^<]+\bid)=["\']([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})'),
             webpage, 'video id')
 
-        return self._make_url_result(video_id)
+        return self._make_url_result(video_id, url=url)

I made a new pre-release for testing, which exe/zip users can update to with this command:

yt-dlp --update-to bashonly/yt-dlp@francetv

(if you already tried my last build you need to run this again)

@Gigoince No. But that pre-release build is equivalent to the latest nightly release with the francetv patch applied on top. The day after #9333 is merged into master you would be able to --update-to nightly

It does work ! \o/

Thanks a million !

To everyone who updated to my pre-release tag build: All fixes have been merged into master, so you can run yt-dlp --update-to nightly to get back on the proper channel

@bashonly OK, Great ! Thanks a lot !

@guy-teube Nope, the video is DRM protected. The 422 error is indicating that there is no HLS available for this video, only DRM-protected DASH.

@pokemaster974 yes, run yt-dlp --update-to bashonly/yt-dlp@francetv to try out #9347

@Gigoince all good, thank you for testing.

#9347 seems to work fine. (Tested on several links, including the one provided)

I had not realised that the link I had tried was a series, all apologies.