Repository: streamlink/streamlink

Issue:
plugins.youtube: --http-cookie not working anymore on members content
### Checklist

- [X] This is a plugin issue and not a different kind of issue
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed plugin issues](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22plugin+issue%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Streamlink version

Latest stable release

### Description

I have been using streamlink for recording members-only livestreams for a while. Using Chrome I get my `--http-cookie` value by copying the cookie value in the Network tab in Dev Tools but recently this does not seem to work. I have been using the same method as before so I am unsure as to what the cause may be. I have tried other CLI utilities like yt-dlp to access locked content using the same cookie (but as a .txt file) and they perform fine. 

### Debug log

```text
[cli][info] Found matching plugin youtube for URL https://www.youtube.com/watch?v=cK_L718jsv4
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
[cli][info] Waiting for streams, retrying every 1.0 second(s)
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
Interrupted! Exiting...
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.9.10
[cli][debug] Streamlink: 3.2.0
[cli][debug] Requests(2.27.1), Socks(1.7.1), Websocket(1.2.3)
[cli][debug] Arguments:
[cli][debug]  url=https://www.youtube.com/watch?v=cK_L718jsv4
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --logfile=D:\Downloads\streamlink temp\log.txt
[cli][debug]  --player=D:\Programs\VLC\vlc.exe
[cli][debug]  --output=D:\Downloads\streamlink temp\Unarchived_Stream 1.mp4
[cli][debug]  --retry-streams=1.0
[cli][debug]  --stream-segment-attempts=100
[cli][debug]  --stream-segment-threads=3
[cli][debug]  --stream-segment-timeout=600.0
[cli][debug]  --stream-timeout=600.0
[cli][debug]  --hls-segment-stream-data=True
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files (x86)\Streamlink\ffmpeg\ffmpeg.exe
[cli][debug]  --http-cookie=[('omitted')]
[cli][info] Found matching plugin youtube for URL https://www.youtube.com/watch?v=cK_L718jsv4
[plugins.youtube][debug] Missing initial player response
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
[cli][info] Waiting for streams, retrying every 1.0 second(s)
[plugins.youtube][debug] Missing initial player response
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
[plugins.youtube][debug] Missing initial player response
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
[plugins.youtube][debug] Missing initial player response
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
```



Discussion:
The youtube plugin doesn't support any kind of authentication. If you're setting custom http headers or cookies, then you're on your own, and if something changes on the site which has been working for you previously and now stopped working, then you'll have to figure it out yourself. Omitting any useful data is also not great if you're looking for help.

> [cli][debug]  --http-cookie=[('omitted')]

This log message doesn't make any sense btw. `--http-cookie` requires `key=value` argument values, and the data is stored as a list of key+value tuples, not as a list of one-element tuples.
https://streamlink.github.io/latest/cli.html#cmdoption-http-cookie
Apologies for omitting it. The log for that part is something like this:

`[cli][debug]  --http-cookie=[('VISITOR_INFO1_LIVE', 'value')]`

The command I usually enter for `--http-cookie` is usually `--http-cookie "VISITOR_INFO1_LIVE=value1; NID=value2; ..."` and so on. This is usually enough to start the recording process in the past, however this is not the case recently. Looking clearly at the debug log now, I see my mistake in entering multiple key=value pairs into one `--http-cookie`, as this would end up being stored as one tuple. But am still confused as to why this incorrect method was working in previous releases.
> But am still confused as to why this incorrect method was working in previous releases.

Because the `Cookie` HTTP header gets set in the `key1=value1; key2=value2; key3=value3` format, so technically, you can set `value1; key2=value2; key3=value3` as the value of `key1` and it won't make a difference when making a request with this specific header. It messes up the client-side though which also stores individual cookies and their values.
I've used it correctly now. My debug log now outputs the cookies correctly but I still get the same response:

```
[cli][debug]  --http-cookie=[('VISITER_INFO1_live,'value1'),('NID','value2')]...[('__Secure-3PSIDCC','valueN')]
...
[plugins.youtube][debug] Missing initial player response
[plugins.youtube][error] Could not get video info - UNPLAYABLE: Join this channel to get access to members-only content like this video, and other exclusive perks.
```
Same for me.

From the debug level logs I'm sure http cookies are loaded, but still fail with the `Could not get video info - UNPLAYABLE: ...` error.

OK, I find the root cause.

The key part is here: [youtube.py](https://github.com/streamlink/streamlink/blob/master/src/streamlink/plugins/youtube.py#L356)

```
data = self._get_data_from_regex(res, self._re_ytInitialPlayerResponse, "initial player response")
```

and the regex is declared in the same file:

```
_re_ytInitialPlayerResponse = re.compile(r"""var\s+ytInitialPlayerResponse\s*=\s*({.*?});\s*var\s+meta\s*=""", re.DOTALL)
```

The question is: **`ytInitialPlayerResponse` is not always followed by `var meta = ...`**
In this case (member only live video), the subsequent script is:
```
;var head = document.getElementsByTagName('head')[0]; var meta = document.createElement('meta'); meta.name = 'referrer'; meta.content = 'origin'; head.appendChild(meta); var noindexMeta = document.createElement('meta'); noindexMeta.name = 'robots'; ...
```

So the regex match should always failed and then it run `_get_data_from_api` function as fallback, finally it returns the last error `UNPLAYABLE:...` (maybe it does not recognize the h5 cookies?)

The simplest fix is just update the regex pattern, at least it works for me at the time.
Suggest adding the `bug` tag back here.

Resolving patch:
diff --git a/src/streamlink/plugins/youtube.py b/src/streamlink/plugins/youtube.py
--- a/src/streamlink/plugins/youtube.py
+++ b/src/streamlink/plugins/youtube.py
@@ -44,7 +44,7 @@
 """, re.VERBOSE))
 class YouTube(Plugin):
     _re_ytInitialData = re.compile(r"""var\s+ytInitialData\s*=\s*({.*?})\s*;\s*</script>""", re.DOTALL)
-    _re_ytInitialPlayerResponse = re.compile(r"""var\s+ytInitialPlayerResponse\s*=\s*({.*?});\s*var\s+meta\s*=""", re.DOTALL)
+    _re_ytInitialPlayerResponse = re.compile(r"""var\s+ytInitialPlayerResponse\s*=\s*({.*?});\s*var\s+\w+\s*=""", re.DOTALL)
     _re_mime_type = re.compile(r"""^(?P<type>\w+)/(?P<container>\w+); codecs="(?P<codecs>.+)"$""")
 
     _url_canonical = "https://www.youtube.com/watch?v={video_id}"


Test patch: