Repository: yt-dlp/yt-dlp

Issue:
"No video formats found" error on TubiTV
### 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 broken site
- [X] I've verified that I'm running yt-dlp version **2022.09.01** ([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/yt-dlp/yt-dlp/wiki/FAQ#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

USA

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

Tried to download https://tubitv.com/movies/685451 and got a "No video formats found!" error. Reporting as requested.

### 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
[debug] Command-line config: ['-vU', '--embed-subs', '--remux-video', 'mkv', 'https://tubitv.com/movies/685451']
[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.09.01 [5d7c7d6] (pip)
[debug] Python 3.9.14 (CPython 64bit) - FreeBSD-12.3-RELEASE-p6-amd64-64bit-ELF (libc 7)
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg 4.4.2 (setts), ffprobe 4.4.2
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.09.14, mutagen-1.45.1, websockets-10.3
[debug] Proxy map: {}
[debug] Loaded 1670 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.09.01, Current version: 2022.09.01
yt-dlp is up to date (2022.09.01)
[debug] Using fake IP 6.228.51.200 (US) as X-Forwarded-For
[debug] [TubiTv] Extracting URL: https://tubitv.com/movies/685451
[TubiTv] 685451: Downloading JSON metadata
[info] 685451: Downloading subtitles: English
ERROR: [TubiTv] 685451: 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/melissa/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1459, in wrapper
    return func(self, *args, **kwargs)
  File "/home/melissa/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1556, in __extract_info
    return self.process_ie_result(ie_result, download, extra_info)
  File "/home/melissa/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1614, in process_ie_result
    ie_result = self.process_video_result(ie_result, download=download)
  File "/home/melissa/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 2546, in process_video_result
    self.raise_no_formats(info_dict)
  File "/home/melissa/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1025, in raise_no_formats
    raise ExtractorError(msg, video_id=info['id'], ie=info['extractor'],
yt_dlp.utils.ExtractorError: [TubiTv] 685451: 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
```



Discussion:
it's DRM protected
> it's DRM protected

Does the tubi extractor need a fix like the paramount one did?
> > it's DRM protected
> 
> Does the tubi extractor need a fix like the paramount one did?

there's nothing to fix, yt-dlp does not download DRM protected content
see this thread for reference - https://github.com/yt-dlp/yt-dlp/issues/2727
Duplicate of https://github.com/yt-dlp/yt-dlp/issues/266 https://github.com/yt-dlp/yt-dlp/issues/2727
I shouldn't have closed the other issues. I now think it's better to keep DRM issues open if yt-dlp gives the wrong error message. It can be closed when the extractor is updated to detect the case properly. Otherwise duplicates of the same will just keep on coming
> > > it's DRM protected
> > 
> > 
> > Does the tubi extractor need a fix like the paramount one did?
> 
> there's nothing to fix, yt-dlp does not download DRM protected content see this thread for reference - #2727

I don't mean fix as in make it possible to download DRM content. I mean fix as in improve DRM detection and correctly alert the user about it. As in, like the fix that was just done for the paramount extractor, on the other ticket I recently opened about this same issue. Hence the "like the paramount one" reference. I can tag you in that other ticket if this is still unclear.

Resolving patch:
diff --git a/yt_dlp/extractor/tubitv.py b/yt_dlp/extractor/tubitv.py
--- a/yt_dlp/extractor/tubitv.py
+++ b/yt_dlp/extractor/tubitv.py
@@ -22,6 +22,19 @@ class TubiTvIE(InfoExtractor):
     _NETRC_MACHINE = 'tubitv'
     _GEO_COUNTRIES = ['US']
     _TESTS = [{
+        'url': 'https://tubitv.com/movies/383676/tracker',
+        'md5': '566fa0f76870302d11af0de89511d3f0',
+        'info_dict': {
+            'id': '383676',
+            'ext': 'mp4',
+            'title': 'Tracker',
+            'description': 'md5:ff320baf43d0ad2655e538c1d5cd9706',
+            'uploader_id': 'f866e2677ea2f0dff719788e4f7f9195',
+            'release_year': 2010,
+            'thumbnail': r're:^https?://.+\.(jpe?g|png)$',
+            'duration': 6122,
+        },
+    }, {
         'url': 'http://tubitv.com/video/283829/the_comedian_at_the_friday',
         'md5': '43ac06be9326f41912dc64ccf7a80320',
         'info_dict': {
@@ -31,12 +44,10 @@ class TubiTvIE(InfoExtractor):
             'description': 'A stand up comedian is forced to look at the decisions in his life while on a one week trip to the west coast.',
             'uploader_id': 'bc168bee0d18dd1cb3b86c68706ab434',
         },
+        'skip': 'Content Unavailable'
     }, {
         'url': 'http://tubitv.com/tv-shows/321886/s01_e01_on_nom_stories',
         'only_matching': True,
-    }, {
-        'url': 'http://tubitv.com/movies/383676/tracker',
-        'only_matching': True,
     }, {
         'url': 'https://tubitv.com/movies/560057/penitentiary?start=true',
         'info_dict': {
@@ -47,11 +58,13 @@ class TubiTvIE(InfoExtractor):
             'uploader_id': 'd8fed30d4f24fcb22ec294421b9defc2',
             'release_year': 1979,
         },
-        'params': {
-            'skip_download': True,
-        },
+        'skip': 'Content Unavailable'
     }]
 
+    # DRM formats are included only to raise appropriate error
+    _UNPLAYABLE_FORMATS = ('hlsv6_widevine', 'hlsv6_widevine_nonclearlead', 'hlsv6_playready_psshv0',
+                           'hlsv6_fairplay', 'dash_widevine', 'dash_widevine_nonclearlead')
+
     def _perform_login(self, username, password):
         self.report_login()
         form_data = {
@@ -69,17 +82,26 @@ def _perform_login(self, username, password):
 
     def _real_extract(self, url):
         video_id = self._match_id(url)
-        video_data = self._download_json(
-            'https://tubitv.com/oz/videos/%s/content?video_resources=dash&video_resources=hlsv3&video_resources=hlsv6' % video_id, video_id)
+        video_data = self._download_json(f'https://tubitv.com/oz/videos/{video_id}/content', video_id, query={
+            'video_resources': ['dash', 'hlsv3', 'hlsv6', *self._UNPLAYABLE_FORMATS],
+        })
         title = video_data['title']
 
         formats = []
+        drm_formats = False
 
         for resource in video_data['video_resources']:
             if resource['type'] in ('dash', ):
                 formats += self._extract_mpd_formats(resource['manifest']['url'], video_id, mpd_id=resource['type'], fatal=False)
             elif resource['type'] in ('hlsv3', 'hlsv6'):
                 formats += self._extract_m3u8_formats(resource['manifest']['url'], video_id, 'mp4', m3u8_id=resource['type'], fatal=False)
+            elif resource['type'] in self._UNPLAYABLE_FORMATS:
+                drm_formats = True
+
+        if not formats and drm_formats:
+            self.report_drm(video_id)
+        elif not formats and not video_data.get('policy_match'):  # policy_match is False if content was removed
+            raise ExtractorError('This content is currently unavailable', expected=True)
 
         self._sort_formats(formats)
 


Test patch: