Repository: yt-dlp/yt-dlp

Issue:
[twitch] ERROR: 'NoneType' object is not subscriptable
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

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

### Checklist

- [X] I'm reporting that yt-dlp is broken on a **supported** site
- [X] I've verified that I'm running yt-dlp version **2023.03.04** ([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 [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and 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

United States

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

What I did:

Tried to download a Twitch stream like this:

```shell
yt-dlp 'https://twitch.tv/gamesdonequick'
```

What I expected to happen: It starts downloading

What happened instead:

```shell
ERROR: 'NoneType' object is not subscriptable
```

* I was successfully using `yt-dlp` with Twitch earlier today.
* The issue is not limited to my IP address AFAICT

### 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] If using API, add `'verbose': True` to `YoutubeDL` params instead
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU', 'https://twitch.tv/gamesdonequick']
[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.03.04 [392389b7d]
[debug] Lazy loading extractors is disabled
[debug] Python 3.11.3 (CPython x86_64 64bit) - Linux-6.2.13-arch1-1-x86_64-with-glibc2.37 (OpenSSL 3.0.9 30 May 2023, glibc 2.37)
[debug] exe versions: ffmpeg 6.0 (setts), ffprobe 6.0, rtmpdump 2.4
[debug] Optional libraries: Cryptodome-3.18.0, brotlicffi-1.0.9.2, certifi-2023.05.07, mutagen-1.46.0, sqlite3-2.6.0, websockets-11.0.3
[debug] Proxy map: {}
[debug] Loaded 1842 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Available version: stable@2023.03.04, Current version: stable@2023.03.04
yt-dlp is up to date (stable@2023.03.04)
[twitch:stream] Extracting URL: https://twitch.tv/gamesdonequick
[twitch:stream] gamesdonequick: Downloading stream GraphQL
[twitch:stream] gamesdonequick: Downloading stream access token GraphQL
ERROR: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/yt_dlp/YoutubeDL.py", line 1552, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/yt_dlp/YoutubeDL.py", line 1628, in __extract_info
    ie_result = ie.extract(url)
                ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/yt_dlp/extractor/common.py", line 698, in extract
    ie_result = self._real_extract(url)
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/yt_dlp/extractor/twitch.py", line 1025, in _real_extract
    token = access_token['value']
            ~~~~~~~~~~~~^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable
```



Discussion:
Related: #7058

It seems that Twitch is now requiring the "integrity" challenge for livestream GraphQL requests as well.
The JSON response that yt-dlp gets: 
```json
{
  "errors": [
    {
      "message": "failed integrity check",
      "path": [
        "streamPlaybackAccessToken"
      ]
    }
  ],
  "data": {
    "streamPlaybackAccessToken": null
  },
  "extensions": {
    "challenge": {
      "type": "integrity"
    },
    "durationMilliseconds": 2,
    "requestID": "..."
  }
}
```
See linked issue for more info

Possibly related: Twitch is now also blocking Firefox from watching livestreams if `privacy.resistFingerprinting` is enabled. Seems both site changes occurred around the same time.
(Previously, Twitch was already blocking Firefox RFP from login, and the [bugzilla report](https://bugzilla.mozilla.org/show_bug.cgi?id=1805101#c0) about that contains some troubling info about what we are up against)
Prior research on this integrity check, including knowledge of why `privacy.resistFingerprinting` is implicated: https://digipres.club/@moralrecordings/109496127240969369

GitHub repo that appears to mention the same fields, and points fingers at which anti-automation framework is involved: https://github.com/unicorn-aio/kpsdk
I guess yt-dlp without a big tech engine is done for. I still hope you guys can restore interop of twitch.tv with noscript/basic (x)html browsers using yt-dlp.
Can confirm, getting the same error when trying to capture a twitch livestream.
Same issue. If a subscription is required I cannot download, whereas I could download before the livestream ended. :(
Same issue
Same issue, even subscribed to a channel and loading cookies. 
https://github.com/streamlink/streamlink/issues/5370#issuecomment-1572576728
https://github.com/m4eba/twitch-dump/commit/2006b88ca8fc57e343357dd3dc415677fa502860

Seems that replacing the ID here
https://github.com/yt-dlp/yt-dlp/blob/c35448b7b14113b35c4415dbfbf488c4731f006f/yt_dlp/extractor/twitch.py#L44
with `ue6666qo983tsx6so1t0vnawi233wa`, works
Can we get a way to override this value with command-line for future changes? Doing `--add-headers Client-Id:value` does not seem to work.
> [streamlink/streamlink#5370 (comment)](https://github.com/streamlink/streamlink/issues/5370#issuecomment-1572576728) [m4eba/twitch-dump@2006b88](https://github.com/m4eba/twitch-dump/commit/2006b88ca8fc57e343357dd3dc415677fa502860)
> 
> Seems that replacing the ID here
> 
> https://github.com/yt-dlp/yt-dlp/blob/c35448b7b14113b35c4415dbfbf488c4731f006f/yt_dlp/extractor/twitch.py#L44
> 
> with `ue6666qo983tsx6so1t0vnawi233wa`, works

Any idea how those of us using the pip version of yt_dlp might be able to do this fix?
I have an automation script for twitch that seems to be broken now because of this API change. I would add my console output, but I don't want to flood the thread.
The switch client id is still unrestricted for now (see its value above).

(working here)
@Gregaras The fix works for me, too. Thank you!
So `--add-header "Client-ID:ue6666qo983tsx6so1t0vnawi233wa"` does not work right now?
> So `--add-header "Client-ID:ue6666qo983tsx6so1t0vnawi233wa"` does not work right now?

No, it does not work currently.
> > [streamlink/streamlink#5370 (comment)](https://github.com/streamlink/streamlink/issues/5370#issuecomment-1572576728) [m4eba/twitch-dump@2006b88](https://github.com/m4eba/twitch-dump/commit/2006b88ca8fc57e343357dd3dc415677fa502860)
> > Seems that replacing the ID here
> > https://github.com/yt-dlp/yt-dlp/blob/c35448b7b14113b35c4415dbfbf488c4731f006f/yt_dlp/extractor/twitch.py#L44
> > 
> > with `ue6666qo983tsx6so1t0vnawi233wa`, works
> 
> Any idea how those of us using the pip version of yt_dlp might be able to do this fix? I have an automation script for twitch that seems to be broken now because of this API change. I would add my console output, but I don't want to flood the thread.

Okay, I found a fix for us pip users. It's really simple.  Run `py -m pip show yt_dlp` and you should get and output that looks kinda like this:
```
Name: yt-dlp
Version: 2023.3.4
Summary: A youtube-dl fork with additional features and patches
Home-page: https://github.com/yt-dlp/yt-dlp
Author:
Author-email:
License:
Location: C:\Users\[YOUR USER NAME]\AppData\Local\Programs\Python\Python311\Lib\site-packages
Requires: brotli, certifi, mutagen, pycryptodomex, websockets
Required-by:
```
Go to that location and modify line 44 in `yt-dlp/yt_dlp/extractor/twitch.py` as @Gregaras said, changing `_CLIENT_ID` in the `TwitchBaseIE()` function to `'ue6666qo983tsx6so1t0vnawi233wa'`. Works for me now, though who can say for how long.
> > > [streamlink/streamlink#5370 (comment)](https://github.com/streamlink/streamlink/issues/5370#issuecomment-1572576728) [m4eba/twitch-dump@2006b88](https://github.com/m4eba/twitch-dump/commit/2006b88ca8fc57e343357dd3dc415677fa502860)
> > > Seems that replacing the ID here
> > > https://github.com/yt-dlp/yt-dlp/blob/c35448b7b14113b35c4415dbfbf488c4731f006f/yt_dlp/extractor/twitch.py#L44
> > > 
> > > with `ue6666qo983tsx6so1t0vnawi233wa`, works
> > 
> > 
> > Any idea how those of us using the pip version of yt_dlp might be able to do this fix? I have an automation script for twitch that seems to be broken now because of this API change. I would add my console output, but I don't want to flood the thread.
> 
> Okay, I found a fix for us pip users. It's really simple. Run `py -m pip show yt_dlp` and you should get and output that looks kinda like this:
> 
> ```
> Name: yt-dlp
> Version: 2023.3.4
> Summary: A youtube-dl fork with additional features and patches
> Home-page: https://github.com/yt-dlp/yt-dlp
> Author:
> Author-email:
> License:
> Location: C:\Users\[YOUR USER NAME]\AppData\Local\Programs\Python\Python311\Lib\site-packages
> Requires: brotli, certifi, mutagen, pycryptodomex, websockets
> Required-by:
> ```
> 
> Go to that location and modify line 44 in `yt-dlp/yt_dlp/extractor/twitch.py` as @Gregaras said, changing `_CLIENT_ID` in the `TwitchBaseIE()` function to `'ue6666qo983tsx6so1t0vnawi233wa'`. Works for me now, though who can say for how long.

Was about to post this, can confirm it is working for the time being.
Got the file path i needed by running `yt-dlp --verbose "https://twitch.tv/streamer_name"`, the output will have a stacktrace with the `twitch.py` filepath at the end.

Resolving patch:
diff --git a/yt_dlp/extractor/twitch.py b/yt_dlp/extractor/twitch.py
--- a/yt_dlp/extractor/twitch.py
+++ b/yt_dlp/extractor/twitch.py
@@ -41,7 +41,6 @@ class TwitchBaseIE(InfoExtractor):
     _USHER_BASE = 'https://usher.ttvnw.net'
     _LOGIN_FORM_URL = 'https://www.twitch.tv/login'
     _LOGIN_POST_URL = 'https://passport.twitch.tv/login'
-    _CLIENT_ID = 'kimne78kx3ncx6brgo4mv6wki5h1ko'
     _NETRC_MACHINE = 'twitch'
 
     _OPERATION_HASHES = {
@@ -58,6 +57,11 @@ class TwitchBaseIE(InfoExtractor):
         'VideoPlayer_VODSeekbarPreviewVideo': '07e99e4d56c5a7c67117a154777b0baf85a5ffefa393b213f4bc712ccaf85dd6',
     }
 
+    @property
+    def _CLIENT_ID(self):
+        return self._configuration_arg(
+            'client_id', ['ue6666qo983tsx6so1t0vnawi233wa'], ie_key=TwitchStreamIE, casesense=True)[0]
+
     def _perform_login(self, username, password):
         def fail(message):
             raise ExtractorError(


Test patch: