Repository: yt-dlp/yt-dlp

Issue:
Trovo Extractor isn't able to consume API
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2021.11.10.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- [X] I've checked that all provided URLs are alive and playable in a browser
- [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

Worldwide

### Description

Hi all,

the Trovo Extractor can't consume the API anymore.

It seems to get no JSON anymore. It's the same for livestreams, single VOD and VOD Playlists.

Best Regards

### Verbose log

```shell
[debug] Command-line config: ['-Uv', 'https://trovo.live/_Catness_']
[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, err utf-8, pref UTF-8
[debug] yt-dlp version 2021.11.10.1
[debug] Python version 3.10.0 (CPython 64bit) - macOS-11.6.1-x86_64-i386-64bit
[debug] exe versions: ffmpeg 4.4.1 (setts), ffprobe 4.4.1, rtmpdump 2.4
[debug] Optional libraries: Cryptodome, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2021.11.10.1)
[debug] [Trovo] Extracting URL: https://trovo.live/_Catness_
[Trovo] _Catness_: Downloading JSON metadata
ERROR: [Trovo] _Catness_: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output. (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 986, in _parse_json
    return json.loads(json_string)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 595, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/trovo.py", line 35, in _real_extract
    live_info = self._download_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 975, in _download_json
    res = self._download_json_handle(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 961, in _download_json_handle
    return self._parse_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 990, in _parse_json
    raise ExtractorError(errmsg, cause=ve)
yt_dlp.utils.ExtractorError: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 986, in _parse_json
    return json.loads(json_string)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 595, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/trovo.py", line 35, in _real_extract
    live_info = self._download_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 975, in _download_json
    res = self._download_json_handle(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 961, in _download_json_handle
    return self._parse_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 990, in _parse_json
    raise ExtractorError(errmsg, cause=ve)
yt_dlp.utils.ExtractorError: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1311, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1336, in __extract_info
    ie_result = ie.extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 621, in extract
    raise type(e)(e.msg, **kwargs)
yt_dlp.utils.ExtractorError: [Trovo] _Catness_: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output. (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
```

Trovo Extractor isn't able to consume API
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2021.11.10.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- [X] I've checked that all provided URLs are alive and playable in a browser
- [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

Worldwide

### Description

Hi all,

the Trovo Extractor can't consume the API anymore.

It seems to get no JSON anymore. It's the same for livestreams, single VOD and VOD Playlists.

Best Regards

### Verbose log

```shell
[debug] Command-line config: ['-Uv', 'https://trovo.live/_Catness_']
[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, err utf-8, pref UTF-8
[debug] yt-dlp version 2021.11.10.1
[debug] Python version 3.10.0 (CPython 64bit) - macOS-11.6.1-x86_64-i386-64bit
[debug] exe versions: ffmpeg 4.4.1 (setts), ffprobe 4.4.1, rtmpdump 2.4
[debug] Optional libraries: Cryptodome, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2021.11.10.1)
[debug] [Trovo] Extracting URL: https://trovo.live/_Catness_
[Trovo] _Catness_: Downloading JSON metadata
ERROR: [Trovo] _Catness_: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output. (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 986, in _parse_json
    return json.loads(json_string)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 595, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/trovo.py", line 35, in _real_extract
    live_info = self._download_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 975, in _download_json
    res = self._download_json_handle(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 961, in _download_json_handle
    return self._parse_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 990, in _parse_json
    raise ExtractorError(errmsg, cause=ve)
yt_dlp.utils.ExtractorError: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 986, in _parse_json
    return json.loads(json_string)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/Cellar/python@3.10/3.10.0_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 595, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/trovo.py", line 35, in _real_extract
    live_info = self._download_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 975, in _download_json
    res = self._download_json_handle(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 961, in _download_json_handle
    return self._parse_json(
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 990, in _parse_json
    raise ExtractorError(errmsg, cause=ve)
yt_dlp.utils.ExtractorError: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1311, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/YoutubeDL.py", line 1336, in __extract_info
    ie_result = ie.extract(url)
  File "/usr/local/Cellar/yt-dlp/2021.11.10.1/libexec/lib/python3.10/site-packages/yt_dlp/extractor/common.py", line 621, in extract
    raise type(e)(e.msg, **kwargs)
yt_dlp.utils.ExtractorError: [Trovo] _Catness_: _Catness_: Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output. (caused by JSONDecodeError('Expecting value: line 2 column 1 (char 1)')); please report this issue on  https://github.com/yt-dlp/yt-dlp . Make sure you are using the latest version; see  https://github.com/yt-dlp/yt-dlp  on how to update. Be sure to call yt-dlp with the --verbose flag and include its complete output.
```



Discussion:
Really strange thing.

Hope someone will find the reason. Requesting API with Paw like the Extractor does works fine. No idea what's wrong here.

Thinks it's something like a blocking mechanism. Using --user-agent and --referer don't help.

Best Regards
Really strange thing.

Hope someone will find the reason. Requesting API with Paw like the Extractor does works fine. No idea what's wrong here.

Thinks it's something like a blocking mechanism. Using --user-agent and --referer don't help.

Best Regards

Resolving patch:
diff --git a/yt_dlp/extractor/trovo.py b/yt_dlp/extractor/trovo.py
--- a/yt_dlp/extractor/trovo.py
+++ b/yt_dlp/extractor/trovo.py
@@ -17,6 +17,11 @@ class TrovoBaseIE(InfoExtractor):
     _VALID_URL_BASE = r'https?://(?:www\.)?trovo\.live/'
     _HEADERS = {'Origin': 'https://trovo.live'}
 
+    def _call_api(self, video_id, query=None, data=None):
+        return self._download_json(
+            'https://gql.trovo.live/', video_id, query=query, data=data,
+            headers={'Accept': 'application/json'})
+
     def _extract_streamer_info(self, data):
         streamer_info = data.get('streamerInfo') or {}
         username = streamer_info.get('userName')
@@ -32,9 +37,8 @@ class TrovoIE(TrovoBaseIE):
 
     def _real_extract(self, url):
         username = self._match_id(url)
-        live_info = self._download_json(
-            'https://gql.trovo.live/', username, query={
-                'query': '''{
+        live_info = self._call_api(username, query={
+            'query': '''{
   getLiveInfo(params: {userName: "%s"}) {
     isLive
     programInfo {
@@ -53,7 +57,7 @@ def _real_extract(self, url):
     }
   }
 }''' % username,
-            })['data']['getLiveInfo']
+        })['data']['getLiveInfo']
         if live_info.get('isLive') == 0:
             raise ExtractorError('%s is offline' % username, expected=True)
         program_info = live_info['programInfo']
@@ -111,15 +115,14 @@ class TrovoVodIE(TrovoBaseIE):
 
     def _real_extract(self, url):
         vid = self._match_id(url)
-        resp = self._download_json(
-            'https://gql.trovo.live/', vid, data=json.dumps([{
-                'query': '''{
+        resp = self._call_api(vid, data=json.dumps([{
+            'query': '''{
   batchGetVodDetailInfo(params: {vids: ["%s"]}) {
     VodDetailInfos
   }
 }''' % vid,
-            }, {
-                'query': '''{
+        }, {
+            'query': '''{
   getCommentList(params: {appInfo: {postID: "%s"}, pageSize: 1000000000, preview: {}}) {
     commentList {
       author {
@@ -133,9 +136,7 @@ def _real_extract(self, url):
     }
   }
 }''' % vid,
-            }]).encode(), headers={
-                'Content-Type': 'application/json',
-            })
+        }]).encode())
         vod_detail_info = resp[0]['data']['batchGetVodDetailInfo']['VodDetailInfos'][vid]
         vod_info = vod_detail_info['vodInfo']
         title = vod_info['title']
@@ -215,7 +216,7 @@ def _entries(self, uid):
 
     def _real_extract(self, url):
         id = self._match_id(url)
-        uid = str(self._download_json('https://gql.trovo.live/', id, query={
+        uid = str(self._call_api(id, query={
             'query': '{getLiveInfo(params:{userName:"%s"}){streamerInfo{uid}}}' % id
         })['data']['getLiveInfo']['streamerInfo']['uid'])
         return self.playlist_result(self._entries(uid), playlist_id=uid)
@@ -237,7 +238,7 @@ class TrovoChannelVodIE(TrovoChannelBaseIE):
     _TYPE = 'video'
 
     def _get_vod_json(self, page, uid):
-        return self._download_json('https://gql.trovo.live/', uid, query={
+        return self._call_api(uid, query={
             'query': self._QUERY % (page, uid)
         })['data']['getChannelLtvVideoInfos']
 
@@ -258,6 +259,6 @@ class TrovoChannelClipIE(TrovoChannelBaseIE):
     _TYPE = 'clip'
 
     def _get_vod_json(self, page, uid):
-        return self._download_json('https://gql.trovo.live/', uid, query={
+        return self._call_api(uid, query={
             'query': self._QUERY % (page, uid)
         })['data']['getChannelClipVideoInfos']


Test patch: