Repository: streamlink/streamlink

Issue:
plugins.twitch: returns streams while channel is offline
### Checklist

- [X] This is a [plugin issue](https://streamlink.github.io/plugins.html) and not [a different kind of issue](https://github.com/streamlink/streamlink/issues/new/choose)
- [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

streamlink 6.4.2

### Description

The --retry-streams switch seems to be broken on twitch.
I keep running `streamlink --retry-streams X` so I can record VODs of my favorite streamer. Last night, after their stream ended, streamlink kept exiting instead of retrying to record a stream.

Steps to reproduce:
`streamlink twitch.tv/any-offline-streamer best -o "/disk1/share1/twitch/{time}.ts" --retry-streams 5`

Expected behavior: streamlink detects no live stream and keeps on trying to record every 5 seconds.
What actually happens:
```
[stream.hls][error] Unable to open URL: https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8E[...]
[cli][error] Try 1/1: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8E[...]
(No data returned from stream)
error: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8E
[...] tried 1 times, exiting
```


### Debug log

```text
[cli][debug] OS:         Linux-5.10.0-26-amd64-x86_64-with-glibc2.31
[cli][debug] Python:     3.9.2
[cli][debug] OpenSSL:    OpenSSL 1.1.1w  11 Sep 2023
[cli][debug] Streamlink: 6.4.2
[cli][debug] Dependencies:
[cli][debug]  certifi: 2020.6.20
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.16.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.22.2
[cli][debug]  trio-websocket: 0.10.3
[cli][debug]  typing-extensions: 4.7.1
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.4.2
[cli][debug] Arguments:
[cli][debug]  url=twitch.tv/streamername
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=/disk1/share1/twitch/{time}.ts
[cli][debug]  --retry-streams=5.0
[cli][debug]  --twitch-api-header=[('Authorization', 'OAuth oauthkey')]
[cli][info] Found matching plugin twitch for URL twitch.tv/streamername
[plugins.twitch][debug] Getting live HLS streams for streamername
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p60, 1080p60 (best)
[cli][info] Opening stream: 1080p60 (hls)
[cli][info] Writing output to
/disk1/share1/twitch/2023-12-05_06-50-04.ts
[cli][debug] Checking file output
[stream.hls][debug] Reloading playlist
[cli][debug] Pre-buffering 8192 bytes
[stream.hls][error] Unable to open URL: https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8Et7-cohsd9159aO2PPQ6Seos4y4HOvcb72tOtDdRNTj8hpKZlbvCMgSrL0JzJEezXEL7893RUjYW-NTfbZcZOXn19u5IrlV9sBJBFYrZp2tL0VAszcZNOI2TN-hBp3vdd-H2H19i6EcZUEMGjDjt_pdgVd9WX9OTQqPRqmK9I1GN8bOQ7yKXjQiwQPj_5puEfGNPAP-IIqZ6FsMlGykkectRfLsSQ0Cg4W5IkiHnoEVeY6DbalTBUZHoAlmENk6p-sO1iRCunKdFWZ7U9ac0uJn8yBybSTNUwYzV-uYJZuvbbZQxrGHjjCqRXzi_NDGNQQKwm0-vd1QUdSEKdBKwcqgpm-Dv8IZGupIYXwQZOS0nUesayUc0ENP4ELbMn-_R61RD9xJU_xnQq-0BjLuMvzscu4YG74LHXs5O2N114OdrX2nK--GqmF-QDoY3z28F2QTS1QmwKgjFeO0W2zxAczEEJcR-f-_VKZCumbNDwWPI7TViy79nyYAnC64377Js0m7bFO4wn-10y9WvokvkPhpxkgTlUwDJCKrXnGmcnefr1QNQ6U27U0Un3S6heB8KUL_STsDi3zZ7gYx58vCcvFU0v5LsNE2eBjCQnHA7Z0OQQBQXAo1nJKtG1f8xNAQQyXr8gJVZSTjn29a0BGT-AmhF3MpJIHKRV1kFCQOeA8jz8nU6eUms6eOWRrh1uvX1O-IjHNy82zvBA0t8cpxaGtnS_SVH1E81RcbG0YpPtH4lcs1i7yzQpIqPg1eLdvUjrVbPaIvRiaL-VIjLNPIKQ21V-hyoe5ALJzwT5I904WhoM5VDKLPcrcqh3JdE7IAEqCXVzLXdlc3QtMjCUCA.m3u8 (404 Client Error: Not Found for url: https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8Et7-cohsd9159aO2PPQ6Seos4y4HOvcb72tOtDdRNTj8hpKZlbvCMgSrL0JzJEezXEL7893RUjYW-NTfbZcZOXn19u5IrlV9sBJBFYrZp2tL0VAszcZNOI2TN-hBp3vdd-H2H19i6EcZUEMGjDjt_pdgVd9WX9OTQqPRqmK9I1GN8bOQ7yKXjQiwQPj_5puEfGNPAP-IIqZ6FsMlGykkectRfLsSQ0Cg4W5IkiHnoEVeY6DbalTBUZHoAlmENk6p-sO1iRCunKdFWZ7U9ac0uJn8yBybSTNUwYzV-uYJZuvbbZQxrGHjjCqRXzi_NDGNQQKwm0-vd1QUdSEKdBKwcqgpm-Dv8IZGupIYXwQZOS0nUesayUc0ENP4ELbMn-_R61RD9xJU_xnQq-0BjLuMvzscu4YG74LHXs5O2N114OdrX2nK--GqmF-QDoY3z28F2QTS1QmwKgjFeO0W2zxAczEEJcR-f-_VKZCumbNDwWPI7TViy79nyYAnC64377Js0m7bFO4wn-10y9WvokvkPhpxkgTlUwDJCKrXnGmcnefr1QNQ6U27U0Un3S6heB8KUL_STsDi3zZ7gYx58vCcvFU0v5LsNE2eBjCQnHA7Z0OQQBQXAo1nJKtG1f8xNAQQyXr8gJVZSTjn29a0BGT-AmhF3MpJIHKRV1kFCQOeA8jz8nU6eUms6eOWRrh1uvX1O-IjHNy82zvBA0t8cpxaGtnS_SVH1E81RcbG0YpPtH4lcs1i7yzQpIqPg1eLdvUjrVbPaIvRiaL-VIjLNPIKQ21V-hyoe5ALJzwT5I904WhoM5VDKLPcrcqh3JdE7IAEqCXVzLXdlc3QtMjCUCA.m3u8)
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][error] Try 1/1: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8Et7-cohsd9159aO2PPQ6Seos4y4HOvcb72tOtDdRNTj8hpKZlbvCMgSrL0JzJEezXEL7893RUjYW-NTfbZcZOXn19u5IrlV9sBJBFYrZp2tL0VAszcZNOI2TN-hBp3vdd-H2H19i6EcZUEMGjDjt_pdgVd9WX9OTQqPRqmK9I1GN8bOQ7yKXjQiwQPj_5puEfGNPAP-IIqZ6FsMlGykkectRfLsSQ0Cg4W5IkiHnoEVeY6DbalTBUZHoAlmENk6p-sO1iRCunKdFWZ7U9ac0uJn8yBybSTNUwYzV-uYJZuvbbZQxrGHjjCqRXzi_NDGNQQKwm0-vd1QUdSEKdBKwcqgpm-Dv8IZGupIYXwQZOS0nUesayUc0ENP4ELbMn-_R61RD9xJU_xnQq-0BjLuMvzscu4YG74LHXs5O2N114OdrX2nK--GqmF-QDoY3z28F2QTS1QmwKgjFeO0W2zxAczEEJcR-f-_VKZCumbNDwWPI7TViy79nyYAnC64377Js0m7bFO4wn-10y9WvokvkPhpxkgTlUwDJCKrXnGmcnefr1QNQ6U27U0Un3S6heB8KUL_STsDi3zZ7gYx58vCcvFU0v5LsNE2eBjCQnHA7Z0OQQBQXAo1nJKtG1f8xNAQQyXr8gJVZSTjn29a0BGT-AmhF3MpJIHKRV1kFCQOeA8jz8nU6eUms6eOWRrh1uvX1O-IjHNy82zvBA0t8cpxaGtnS_SVH1E81RcbG0YpPtH4lcs1i7yzQpIqPg1eLdvUjrVbPaIvRiaL-VIjLNPIKQ21V-hyoe5ALJzwT5I904WhoM5VDKLPcrcqh3JdE7IAEqCXVzLXdlc3QtMjCUCA.m3u8', 'https://usher.ttvnw.net/api/channel/hls/streamername.m3u8?player=twitchweb&p=17828&type=any&allow_source=true&allow_audio_only=true&allow_spectre=false&sig=e99f77d8cdd6aa3055787fddba62db0d8dddc3d6&token=%7B%22adblock%22%3Afalse%2C%22authorization%22%3A%7B%22forbidden%22%3Afalse%2C%22reason%22%3A%22%22%7D%2C%22blackout_enabled%22%3Afalse%2C%22channel%22%3A%22streamername%22%2C%22channel_id%22%3A38251312%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%2C%22view_until%22%3A1924905600%7D%2C%22ci_gb%22%3Afalse%2C%22geoblock_reason%22%3A%22%22%2C%22device_id%22%3Anull%2C%22expires%22%3A1701756604%2C%22extended_history_allowed%22%3Afalse%2C%22game%22%3A%22%22%2C%22hide_ads%22%3Afalse%2C%22https_required%22%3Atrue%2C%22mature%22%3Afalse%2C%22partner%22%3Afalse%2C%22platform%22%3A%22web%22%2C%22player_type%22%3A%22embed%22%2C%22private%22%3A%7B%22allowed_to_view%22%3Atrue%7D%2C%22privileged%22%3Afalse%2C%22role%22%3A%22%22%2C%22server_ads%22%3Atrue%2C%22show_ads%22%3Atrue%2C%22subscriber%22%3Afalse%2C%22turbo%22%3Afalse%2C%22user_id%22%3A495478723%2C%22user_ip%22%3A%2285.67.13.26%22%2C%22version%22%3A2%7D&fast_bread=True']> (No data returned from stream)
error: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/Ct8Et7-cohsd9159aO2PPQ6Seos4y4HOvcb72tOtDdRNTj8hpKZlbvCMgSrL0JzJEezXEL7893RUjYW-NTfbZcZOXn19u5IrlV9sBJBFYrZp2tL0VAszcZNOI2TN-hBp3vdd-H2H19i6EcZUEMGjDjt_pdgVd9WX9OTQqPRqmK9I1GN8bOQ7yKXjQiwQPj_5puEfGNPAP-IIqZ6FsMlGykkectRfLsSQ0Cg4W5IkiHnoEVeY6DbalTBUZHoAlmENk6p-sO1iRCunKdFWZ7U9ac0uJn8yBybSTNUwYzV-uYJZuvbbZQxrGHjjCqRXzi_NDGNQQKwm0-vd1QUdSEKdBKwcqgpm-Dv8IZGupIYXwQZOS0nUesayUc0ENP4ELbMn-_R61RD9xJU_xnQq-0BjLuMvzscu4YG74LHXs5O2N114OdrX2nK--GqmF-QDoY3z28F2QTS1QmwKgjFeO0W2zxAczEEJcR-f-_VKZCumbNDwWPI7TViy79nyYAnC64377Js0m7bFO4wn-10y9WvokvkPhpxkgTlUwDJCKrXnGmcnefr1QNQ6U27U0Un3S6heB8KUL_STsDi3zZ7gYx58vCcvFU0v5LsNE2eBjCQnHA7Z0OQQBQXAo1nJKtG1f8xNAQQyXr8gJVZSTjn29a0BGT-AmhF3MpJIHKRV1kFCQOeA8jz8nU6eUms6eOWRrh1uvX1O-IjHNy82zvBA0t8cpxaGtnS_SVH1E81RcbG0YpPtH4lcs1i7yzQpIqPg1eLdvUjrVbPaIvRiaL-VIjLNPIKQ21V-hyoe5ALJzwT5I904WhoM5VDKLPcrcqh3JdE7IAEqCXVzLXdlc3QtMjCUCA.m3u8', 'https://usher.ttvnw.net/api/channel/hls/streamername.m3u8?player=twitchweb&p=17828&type=any&allow_source=true&allow_audio_only=true&allow_spectre=false&sig=e99f77d8cdd6aa3055787fddba62db0d8dddc3d6&token=%7B%22adblock%22%3Afalse%2C%22authorization%22%3A%7B%22forbidden%22%3Afalse%2C%22reason%22%3A%22%22%7D%2C%22blackout_enabled%22%3Afalse%2C%22channel%22%3A%22streamername%22%2C%22channel_id%22%3A38251312%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%2C%22view_until%22%3A1924905600%7D%2C%22ci_gb%22%3Afalse%2C%22geoblock_reason%22%3A%22%22%2C%22device_id%22%3Anull%2C%22expires%22%3A1701756604%2C%22extended_history_allowed%22%3Afalse%2C%22game%22%3A%22%22%2C%22hide_ads%22%3Afalse%2C%22https_required%22%3Atrue%2C%22mature%22%3Afalse%2C%22partner%22%3Afalse%2C%22platform%22%3A%22web%22%2C%22player_type%22%3A%22embed%22%2C%22private%22%3A%7B%22allowed_to_view%22%3Atrue%7D%2C%22privileged%22%3Afalse%2C%22role%22%3A%22%22%2C%22server_ads%22%3Atrue%2C%22show_ads%22%3Atrue%2C%22subscriber%22%3Afalse%2C%22turbo%22%3Afalse%2C%22user_id%22%3A495478723%2C%22user_ip%22%3A%2285.67.13.26%22%2C%22version%22%3A2%7D&fast_bread=True']>, tried 1 times, exiting
[cli][info] Closing currently open stream...
```

plugins.twitch: latest update started flooding console with confusing messages
### Checklist

- [X] This is a [plugin issue](https://streamlink.github.io/plugins.html) and not [a different kind of issue](https://github.com/streamlink/streamlink/issues/new/choose)
- [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

6.3.1

### Description

did someone enable a new debug setting or something and how to turn that off if so? the plugin appears to work but it has started flooding the console with messages. its not the end of the world but i would like the console clean like it used to be

### Debug log

```text
[plugins.twitch][error] twirp error not_found: transcode does not exist
[plugins.twitch][error] Could not access HLS playlist
```



Discussion:
`--retry-streams` has nothing to do with any implementation of Streamlink's plugins. It's a feature of `streamlink_cli` which keeps fetching streams from the resolved plugin until ones were found.

As you can see in your debug log output, the plugin has successfully returned a list of streams:
> [cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p60, 1080p60 (best)

The reason why you can't access the streams is that Twitch is returning a 404 HTTP status code when trying to access the HLS media playlist. Since you redacted the channel name, I can't tell you what the issue is. Maybe it's because of your OAuth token, maybe not... You'll have to provide more information.

Actual offline streams are working as intended, as you can see here, as well as in #5656:

```
$ streamlink --no-config -l debug --retry-streams 5 twitch.tv/twitch best
[cli][debug] OS:         Linux-6.6.3-1-git-x86_64-with-glibc2.38
[cli][debug] Python:     3.11.6
[cli][debug] OpenSSL:    OpenSSL 3.1.4 24 Oct 2023
[cli][debug] Streamlink: 6.4.2+2.g71e885e0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.1
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.8.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.6.4
[cli][debug] Arguments:
[cli][debug]  url=twitch.tv/twitch
[cli][debug]  stream=['best']
[cli][debug]  --no-config=True
[cli][debug]  --loglevel=debug
[cli][debug]  --retry-streams=5.0
[cli][info] Found matching plugin twitch for URL twitch.tv/twitch
[plugins.twitch][debug] Getting live HLS streams for twitch
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] Could not access HLS playlist
[cli][info] Waiting for streams, retrying every 5.0 second(s)
[plugins.twitch][debug] Getting live HLS streams for twitch
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] Could not access HLS playlist
[plugins.twitch][debug] Getting live HLS streams for twitch
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] Could not access HLS playlist
^CInterrupted! Exiting...
```

```
$ streamlink --no-config -l debug --retry-streams 5 twitch.tv/lirik best
[cli][debug] OS:         Linux-6.6.3-1-git-x86_64-with-glibc2.38
[cli][debug] Python:     3.11.6
[cli][debug] OpenSSL:    OpenSSL 3.1.4 24 Oct 2023
[cli][debug] Streamlink: 6.4.2+2.g71e885e0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.1
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.8.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.6.4
[cli][debug] Arguments:
[cli][debug]  url=twitch.tv/lirik
[cli][debug]  stream=['best']
[cli][debug]  --no-config=True
[cli][debug]  --loglevel=debug
[cli][debug]  --retry-streams=5.0
[cli][info] Found matching plugin twitch for URL twitch.tv/lirik
[plugins.twitch][debug] Getting live HLS streams for lirik
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] twirp error not_found: transcode does not exist
[cli][info] Waiting for streams, retrying every 5.0 second(s)
[plugins.twitch][debug] Getting live HLS streams for lirik
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] twirp error not_found: transcode does not exist
[plugins.twitch][debug] Getting live HLS streams for lirik
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[plugins.twitch][error] twirp error not_found: transcode does not exist
^CInterrupted! Exiting...
```
Thank you for the reply. I did have my OAuth token in the config. Removed it (as in: moved the config file elsewhere as the only line in it was for the token) for testing and it's still the same error. The channel I am trying with is: twitch.tv/paymoneywubby

```
[cli][debug] OS:         Linux-5.10.0-26-amd64-x86_64-with-glibc2.31
[cli][debug] Python:     3.9.2
[cli][debug] OpenSSL:    OpenSSL 1.1.1w  11 Sep 2023
[cli][debug] Streamlink: 6.4.2
[cli][debug] Dependencies:
[cli][debug]  certifi: 2020.6.20
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.16.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.22.2
[cli][debug]  trio-websocket: 0.10.3
[cli][debug]  typing-extensions: 4.7.1
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.4.2
[cli][debug] Arguments:
[cli][debug]  url=twitch.tv/paymoneywubby
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=/disk1/share1/twitch/{time}.ts
[cli][debug]  --retry-streams=5.0
[cli][info] Found matching plugin twitch for URL twitch.tv/paymoneywubby
[plugins.twitch][debug] Getting live HLS streams for paymoneywubby
[plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[utils.l10n][debug] Language code: en_US
[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p60, 1080p60 (best)
[cli][info] Opening stream: 1080p60 (hls)
[cli][info] Writing output to
/disk1/share1/twitch/2023-12-05_08-00-15.ts
[cli][debug] Checking file output
[stream.hls][debug] Reloading playlist
[cli][debug] Pre-buffering 8192 bytes
[stream.hls][error] Unable to open URL: https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/CtIEZDm7NWyT-thnAUuZBiU69sb15zPPD9XZbKlLNGM8kvad-SgolHdrrncU1szlHTkQv-jjeNG8kWbTjajFkzJqI0vrnDaLh7BleaEqPGenNfXUYngYyPITTncELfAq8sjn0c3yFFFIao7CnoLfbDYC_NqOrDGJ0PAQGQaMnN3xrrIXEFxPOIYaIhtNMsbMZj7q40kVRD8lo4bYdB6IFxD5PQT2WPIJa2ugseh4Rn5NQw-Tf_pRQvhGVxinA_llE02hm-_pF0f2i-2KrXgvOFIqPqYC_7Nxc0OqDTYZsYW-PwFridPkWrT9R1Q7oWzepMFWBoXkhnlHFQGxW1-Efn3HGY5goejUggEFikuw97m4MU5SilCehYv8UbB8sDKsfUFTdwS1fz7Gp4BdcmgjcSj22Xn3oBC4V-JUVpJoV53gc_vTVrjlbHbGnBuF2MBbZz8Tazk53YmWgVU_QE2ZubX1qLCdlbwbRfckm23XADHpuC3sZyxS1CVh76m4ZXA4WGeW2y-LmriJnLj31h1awSpRADjJS0uDFOT8NPPiafZgXaTV6Y9-MdwnB_kJmXAl-gJH0ribLAgDzqzUqW16EzrTPhoWJbAyLfrVkgVqER3A-kmF4dsPvCnSAtwtFXU3aUnqQ9ExxejNJwHzCLnEeJliZf-DkF_mNZ-n4rfnstTORZufe6X_D5-_DVE0jl4iZehoI360WJdZK1tFH7sesGKwzZJoi4zjYNjgdv6tGlngNtKhaWSTfPA0Kz2N51O14llq1SLp_QFEExbFYEkrDQugxYQ6GgywlD_hAAg2-f00s8MgASoJdXMtd2VzdC0yMJQI.m3u8 (404 Client Error: Not Found for url: https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/CtIEZDm7NWyT-thnAUuZBiU69sb15zPPD9XZbKlLNGM8kvad-SgolHdrrncU1szlHTkQv-jjeNG8kWbTjajFkzJqI0vrnDaLh7BleaEqPGenNfXUYngYyPITTncELfAq8sjn0c3yFFFIao7CnoLfbDYC_NqOrDGJ0PAQGQaMnN3xrrIXEFxPOIYaIhtNMsbMZj7q40kVRD8lo4bYdB6IFxD5PQT2WPIJa2ugseh4Rn5NQw-Tf_pRQvhGVxinA_llE02hm-_pF0f2i-2KrXgvOFIqPqYC_7Nxc0OqDTYZsYW-PwFridPkWrT9R1Q7oWzepMFWBoXkhnlHFQGxW1-Efn3HGY5goejUggEFikuw97m4MU5SilCehYv8UbB8sDKsfUFTdwS1fz7Gp4BdcmgjcSj22Xn3oBC4V-JUVpJoV53gc_vTVrjlbHbGnBuF2MBbZz8Tazk53YmWgVU_QE2ZubX1qLCdlbwbRfckm23XADHpuC3sZyxS1CVh76m4ZXA4WGeW2y-LmriJnLj31h1awSpRADjJS0uDFOT8NPPiafZgXaTV6Y9-MdwnB_kJmXAl-gJH0ribLAgDzqzUqW16EzrTPhoWJbAyLfrVkgVqER3A-kmF4dsPvCnSAtwtFXU3aUnqQ9ExxejNJwHzCLnEeJliZf-DkF_mNZ-n4rfnstTORZufe6X_D5-_DVE0jl4iZehoI360WJdZK1tFH7sesGKwzZJoi4zjYNjgdv6tGlngNtKhaWSTfPA0Kz2N51O14llq1SLp_QFEExbFYEkrDQugxYQ6GgywlD_hAAg2-f00s8MgASoJdXMtd2VzdC0yMJQI.m3u8)
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][error] Try 1/1: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/CtIEZDm7NWyT-thnAUuZBiU69sb15zPPD9XZbKlLNGM8kvad-SgolHdrrncU1szlHTkQv-jjeNG8kWbTjajFkzJqI0vrnDaLh7BleaEqPGenNfXUYngYyPITTncELfAq8sjn0c3yFFFIao7CnoLfbDYC_NqOrDGJ0PAQGQaMnN3xrrIXEFxPOIYaIhtNMsbMZj7q40kVRD8lo4bYdB6IFxD5PQT2WPIJa2ugseh4Rn5NQw-Tf_pRQvhGVxinA_llE02hm-_pF0f2i-2KrXgvOFIqPqYC_7Nxc0OqDTYZsYW-PwFridPkWrT9R1Q7oWzepMFWBoXkhnlHFQGxW1-Efn3HGY5goejUggEFikuw97m4MU5SilCehYv8UbB8sDKsfUFTdwS1fz7Gp4BdcmgjcSj22Xn3oBC4V-JUVpJoV53gc_vTVrjlbHbGnBuF2MBbZz8Tazk53YmWgVU_QE2ZubX1qLCdlbwbRfckm23XADHpuC3sZyxS1CVh76m4ZXA4WGeW2y-LmriJnLj31h1awSpRADjJS0uDFOT8NPPiafZgXaTV6Y9-MdwnB_kJmXAl-gJH0ribLAgDzqzUqW16EzrTPhoWJbAyLfrVkgVqER3A-kmF4dsPvCnSAtwtFXU3aUnqQ9ExxejNJwHzCLnEeJliZf-DkF_mNZ-n4rfnstTORZufe6X_D5-_DVE0jl4iZehoI360WJdZK1tFH7sesGKwzZJoi4zjYNjgdv6tGlngNtKhaWSTfPA0Kz2N51O14llq1SLp_QFEExbFYEkrDQugxYQ6GgywlD_hAAg2-f00s8MgASoJdXMtd2VzdC0yMJQI.m3u8', 'https://usher.ttvnw.net/api/channel/hls/paymoneywubby.m3u8?player=twitchweb&p=807153&type=any&allow_source=true&allow_audio_only=true&allow_spectre=false&sig=03af56e0a697256be328c92978b7f3d4993795b6&token=%7B%22adblock%22%3Afalse%2C%22authorization%22%3A%7B%22forbidden%22%3Afalse%2C%22reason%22%3A%22%22%7D%2C%22blackout_enabled%22%3Afalse%2C%22channel%22%3A%22paymoneywubby%22%2C%22channel_id%22%3A38251312%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%2C%22view_until%22%3A1924905600%7D%2C%22ci_gb%22%3Afalse%2C%22geoblock_reason%22%3A%22%22%2C%22device_id%22%3Anull%2C%22expires%22%3A1701760815%2C%22extended_history_allowed%22%3Afalse%2C%22game%22%3A%22%22%2C%22hide_ads%22%3Afalse%2C%22https_required%22%3Atrue%2C%22mature%22%3Afalse%2C%22partner%22%3Afalse%2C%22platform%22%3A%22web%22%2C%22player_type%22%3A%22embed%22%2C%22private%22%3A%7B%22allowed_to_view%22%3Atrue%7D%2C%22privileged%22%3Afalse%2C%22role%22%3A%22%22%2C%22server_ads%22%3Atrue%2C%22show_ads%22%3Atrue%2C%22subscriber%22%3Afalse%2C%22turbo%22%3Afalse%2C%22user_id%22%3Anull%2C%22user_ip%22%3A%2285.67.13.26%22%2C%22version%22%3A2%7D&fast_bread=True']> (No data returned from stream)
error: Could not open stream <TwitchHLSStream ['hls', 'https://video-weaver.fra02.hls.ttvnw.net/v1/playlist/CtIEZDm7NWyT-thnAUuZBiU69sb15zPPD9XZbKlLNGM8kvad-SgolHdrrncU1szlHTkQv-jjeNG8kWbTjajFkzJqI0vrnDaLh7BleaEqPGenNfXUYngYyPITTncELfAq8sjn0c3yFFFIao7CnoLfbDYC_NqOrDGJ0PAQGQaMnN3xrrIXEFxPOIYaIhtNMsbMZj7q40kVRD8lo4bYdB6IFxD5PQT2WPIJa2ugseh4Rn5NQw-Tf_pRQvhGVxinA_llE02hm-_pF0f2i-2KrXgvOFIqPqYC_7Nxc0OqDTYZsYW-PwFridPkWrT9R1Q7oWzepMFWBoXkhnlHFQGxW1-Efn3HGY5goejUggEFikuw97m4MU5SilCehYv8UbB8sDKsfUFTdwS1fz7Gp4BdcmgjcSj22Xn3oBC4V-JUVpJoV53gc_vTVrjlbHbGnBuF2MBbZz8Tazk53YmWgVU_QE2ZubX1qLCdlbwbRfckm23XADHpuC3sZyxS1CVh76m4ZXA4WGeW2y-LmriJnLj31h1awSpRADjJS0uDFOT8NPPiafZgXaTV6Y9-MdwnB_kJmXAl-gJH0ribLAgDzqzUqW16EzrTPhoWJbAyLfrVkgVqER3A-kmF4dsPvCnSAtwtFXU3aUnqQ9ExxejNJwHzCLnEeJliZf-DkF_mNZ-n4rfnstTORZufe6X_D5-_DVE0jl4iZehoI360WJdZK1tFH7sesGKwzZJoi4zjYNjgdv6tGlngNtKhaWSTfPA0Kz2N51O14llq1SLp_QFEExbFYEkrDQugxYQ6GgywlD_hAAg2-f00s8MgASoJdXMtd2VzdC0yMJQI.m3u8', 'https://usher.ttvnw.net/api/channel/hls/paymoneywubby.m3u8?player=twitchweb&p=807153&type=any&allow_source=true&allow_audio_only=true&allow_spectre=false&sig=03af56e0a697256be328c92978b7f3d4993795b6&token=%7B%22adblock%22%3Afalse%2C%22authorization%22%3A%7B%22forbidden%22%3Afalse%2C%22reason%22%3A%22%22%7D%2C%22blackout_enabled%22%3Afalse%2C%22channel%22%3A%22paymoneywubby%22%2C%22channel_id%22%3A38251312%2C%22chansub%22%3A%7B%22restricted_bitrates%22%3A%5B%5D%2C%22view_until%22%3A1924905600%7D%2C%22ci_gb%22%3Afalse%2C%22geoblock_reason%22%3A%22%22%2C%22device_id%22%3Anull%2C%22expires%22%3A1701760815%2C%22extended_history_allowed%22%3Afalse%2C%22game%22%3A%22%22%2C%22hide_ads%22%3Afalse%2C%22https_required%22%3Atrue%2C%22mature%22%3Afalse%2C%22partner%22%3Afalse%2C%22platform%22%3A%22web%22%2C%22player_type%22%3A%22embed%22%2C%22private%22%3A%7B%22allowed_to_view%22%3Atrue%7D%2C%22privileged%22%3Afalse%2C%22role%22%3A%22%22%2C%22server_ads%22%3Atrue%2C%22show_ads%22%3Atrue%2C%22subscriber%22%3Afalse%2C%22turbo%22%3Afalse%2C%22user_id%22%3Anull%2C%22user_ip%22%3A%2285.67.13.26%22%2C%22version%22%3A2%7D&fast_bread=True']>, tried 1 times, exiting
[cli][info] Closing currently open stream...
```

Looks like the online/offline detection via the streaming access token acquirement doesn't work on this channel for some reason. This could be cause they are hosting another channel.

We'll have to get the channel's online status using another GQL query in addition to the access token. Twitch's GQL API is their private API and is undocumented. Let me have a look at what their site is doing...
That should work: `UseLive` / `639d5f11bfb8bf3053b424d9ef650d04c4ebb7d94711d644afb08fe9a0fad5d9`

**online**

```sh
curl -sSL -X POST -H 'Client-Id: kimne78kx3ncx6brgo4mv6wki5h1ko' -d '{
        "operationName": "UseLive",
        "variables": {
            "channelLogin": "shroud"
        },
        "extensions": {
            "persistedQuery": {
                "version": 1,
                "sha256Hash": "639d5f11bfb8bf3053b424d9ef650d04c4ebb7d94711d644afb08fe9a0fad5d9"
            }
        }
    }' https://gql.twitch.tv/gql | jq
```
```json
{
  "data": {
    "user": {
      "id": "37402112",
      "login": "shroud",
      "stream": {
        "id": "43175975723",
        "createdAt": "2023-12-04T23:07:43Z",
        "__typename": "Stream"
      },
      "__typename": "User"
    }
  },
  "extensions": {
    "durationMilliseconds": 39,
    "operationName": "UseLive",
    "requestID": "..."
  }
}
```

**offline**

```sh
curl -sSL -X POST -H 'Client-Id: kimne78kx3ncx6brgo4mv6wki5h1ko' -d '{
        "operationName": "UseLive",
        "variables": {
            "channelLogin": "paymoneywubby"
        },
        "extensions": {
            "persistedQuery": {
                "version": 1,
                "sha256Hash": "639d5f11bfb8bf3053b424d9ef650d04c4ebb7d94711d644afb08fe9a0fad5d9"
            }
        }
    }' https://gql.twitch.tv/gql | jq
```
```json
{
  "data": {
    "user": {
      "id": "38251312",
      "login": "paymoneywubby",
      "stream": null,
      "__typename": "User"
    }
  },
  "extensions": {
    "durationMilliseconds": 39,
    "operationName": "UseLive",
    "requestID": "..."
  }
}
```
Are reruns still a feature on Twitch? The plugin still implements a check if the channel's status is not `live`, but I don't know if it's actually useful anymore. Didn't they remove the rerun feature?
Not sure - in my case, the streams I record don't do reruns, so I wouldn't know, sorry.
See #5704 and give it a test:
https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#pull-request-feedback
Happy to confirm it works! Thank you!

```
[cli][debug] OS:         Linux-5.10.0-26-amd64-x86_64-with-glibc2.31
[cli][debug] Python:     3.9.2
[cli][debug] OpenSSL:    OpenSSL 1.1.1w  11 Sep 2023
[cli][debug] Streamlink: 6.4.2+3.gde794086
[cli][debug] Dependencies:
[cli][debug]  certifi: 2020.6.20
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.1
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.8.0
[cli][debug]  urllib3: 1.26.5
[cli][debug]  websocket-client: 1.7.0
[cli][debug] Arguments:
[cli][debug]  url=twitch.tv/paymoneywubby
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=/disk1/share1/twitch/{time}.ts
[cli][debug]  --retry-streams=5.0
[cli][info] Found matching plugin twitch for URL twitch.tv/paymoneywubby
[plugins.twitch][error] Channel is offline
[cli][info] Waiting for streams, retrying every 5.0 second(s)
[plugins.twitch][error] Channel is offline
[plugins.twitch][error] Channel is offline
[plugins.twitch][error] Channel is offline
Interrupted! Exiting...
```

Let's keep this open until #5704 has been merged.
Whenever i try to load a stream it just comes up with this:

https://i.imgur.com/X1WNJWB.png

Seems to be working now.

I don't see any issues with the plugin. Twitch live streams are working fine, including ad filtering and low latency streaming, as well as VODs. I've checked various channels and qualities.

What you have posted is not a debug log. You completely ignored the issue form/template, so this issue doesn't have any useful information at all apart from the error message Twitch is spitting out. I don't even know which channel you're attempting to watch.

As you can see from the error message, Twitch doesn't let you access the HLS playlist because this specific quality hasn't been encoded (yet?).

Streamlink `6.3.0` fixed the error handling of the Twitch plugin when resolving HLS playlists, which you can read in the changelog:
https://streamlink.github.io/changelog.html#streamlink-6-3-0-2023-10-25

This was fixed because of geo-restricted streams on Twitch with Amazon Prime-Video stuff, exclusive to the US area.

> started flooding

Nothing is flooding unless you've set `--retry-streams`. Streamlink's CLI will keep resolving the streams on error when set.
https://streamlink.github.io/cli.html#cmdoption-retry-streams

----

> Whenever i try to load a stream it just comes up with this:

Please stop posting Twitch GUI stuff here on this issue tracker. It doesn't belong here. This image also doesn't say anything, as well as your post. My guess however is that your player closes on its own immediately when it receives its input. Otherwise you'd see a Streamlink error message. This is a regular exit of the playback, at least from the perspective from Streamlink. The Twitch GUI is just a launcher and isn't involved at all here.
> > started flooding
> 
> Nothing is flooding unless you've set `--retry-streams`. Streamlink's CLI will keep resolving the streams on error when set. https://streamlink.github.io/cli.html#cmdoption-retry-streams
> 
yeah and that is new behavior. since its setup to poll the channel for streams to record there will be a lot of those messages. in the past it would retry silently

Resolving patch:
diff --git a/src/streamlink/plugins/twitch.py b/src/streamlink/plugins/twitch.py
--- a/src/streamlink/plugins/twitch.py
+++ b/src/streamlink/plugins/twitch.py
@@ -495,8 +495,16 @@ def stream_metadata(self, channel):
         )
 
         return self.call(query, schema=validate.Schema(
-            {"data": {"user": {"stream": {"type": str}}}},
-            validate.get(("data", "user", "stream")),
+            {
+                "data": {
+                    "user": validate.none_or_all({
+                        "stream": validate.none_or_all({
+                            "id": str,
+                        }),
+                    }),
+                },
+            },
+            validate.get(("data", "user")),
         ))
 
 
@@ -640,7 +648,7 @@ def decode_client_integrity_token(data: str, schema: Optional[validate.Schema] =
 @pluginargument(
     "disable-reruns",
     action="store_true",
-    help="Do not open the stream if the target channel is currently broadcasting a rerun.",
+    help=argparse.SUPPRESS,
 )
 @pluginargument(
     "low-latency",
@@ -808,22 +816,19 @@ def _access_token(self, is_live, channel_or_vod):
 
         return sig, token, restricted_bitrates
 
-    def _check_for_rerun(self):
-        if not self.options.get("disable_reruns"):
+    def _check_is_live(self) -> bool:
+        data = self.api.stream_metadata(self.channel)
+        if data is None:
+            log.error("Unknown channel")
+            return False
+        if data["stream"] is None:
+            log.error("Channel is offline")
             return False
 
-        try:
-            stream = self.api.stream_metadata(self.channel)
-            if stream["type"] != "live":
-                log.info("Reruns were disabled by command line option")
-                return True
-        except (PluginError, TypeError):
-            pass
-
-        return False
+        return True
 
     def _get_hls_streams_live(self):
-        if self._check_for_rerun():
+        if not self._check_is_live():
             return
 
         # only get the token once the channel has been resolved


Test patch:
diff --git a/tests/plugins/test_twitch.py b/tests/plugins/test_twitch.py
--- a/tests/plugins/test_twitch.py
+++ b/tests/plugins/test_twitch.py
@@ -396,6 +396,53 @@ def test_hls_low_latency_no_ads_reload_time(self):
         assert self.thread.reader.worker.playlist_reload_time == pytest.approx(23 / 3)
 
 
+class TestTwitchIsLive:
+    @pytest.fixture()
+    def plugin(self, request: pytest.FixtureRequest):
+        return Twitch(Streamlink(), "https://twitch.tv/channelname")
+
+    @pytest.fixture()
+    def mock(self, request: pytest.FixtureRequest, requests_mock: rm.Mocker):
+        mock = requests_mock.post("https://gql.twitch.tv/gql", **getattr(request, "param", {"json": {}}))
+        yield mock
+        assert mock.call_count > 0
+        payload = mock.last_request.json()  # type: ignore[union-attr]
+        assert tuple(sorted(payload.keys())) == ("extensions", "operationName", "variables")
+        assert payload.get("operationName") == "StreamMetadata"
+        assert payload.get("extensions") == {
+            "persistedQuery": {
+                "sha256Hash": "1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e",
+                "version": 1,
+            },
+        }
+        assert payload.get("variables") == {"channelLogin": "channelname"}
+
+    @pytest.mark.parametrize(("mock", "expected", "log"), [
+        pytest.param(
+            {"json": {"data": {"user": None}}},
+            False,
+            [("streamlink.plugins.twitch", "error", "Unknown channel")],
+            id="no-user",
+        ),
+        pytest.param(
+            {"json": {"data": {"user": {"stream": None}}}},
+            False,
+            [("streamlink.plugins.twitch", "error", "Channel is offline")],
+            id="no-stream",
+        ),
+        pytest.param(
+            {"json": {"data": {"user": {"stream": {"id": "1234567890"}}}}},
+            True,
+            [],
+            id="is-live",
+        ),
+    ], indirect=["mock"])
+    def test_is_live(self, plugin: Twitch, caplog: pytest.LogCaptureFixture, mock: rm.Mocker, expected: bool, log: list):
+        caplog.set_level(1, "streamlink")
+        assert plugin._check_is_live() is expected
+        assert [(record.name, record.levelname, record.message) for record in caplog.records] == log
+
+
 class TestTwitchAPIAccessToken:
     @pytest.fixture(autouse=True)
     def _client_integrity_token(self, monkeypatch: pytest.MonkeyPatch):
@@ -834,63 +881,3 @@ def test_metadata_clip_no_data(self, mock_request_clip, metadata):
         assert author is None
         assert category is None
         assert title is None
-
-
-@pytest.mark.parametrize(("stream_type", "offline", "disable", "expected", "logs"), [
-    pytest.param(
-        "live",
-        False,
-        True,
-        False,
-        [],
-        id="disable live",
-    ),
-    pytest.param(
-        "rerun",
-        False,
-        True,
-        True,
-        [("streamlink.plugins.twitch", "info", "Reruns were disabled by command line option")],
-        id="disable not live",
-    ),
-    pytest.param(
-        "live",
-        True,
-        True,
-        False,
-        [],
-        id="disable offline",
-    ),
-    pytest.param(
-        "rerun",
-        True,
-        False,
-        False,
-        [],
-        id="enable",
-    ),
-])
-def test_reruns(
-    monkeypatch: pytest.MonkeyPatch,
-    caplog: pytest.LogCaptureFixture,
-    session: Streamlink,
-    stream_type: str,
-    offline: bool,
-    disable: bool,
-    expected: bool,
-    logs: list,
-):
-    caplog.set_level(1, "streamlink")
-    mock_stream_metadata = Mock(return_value=None if offline else {"type": stream_type})
-    monkeypatch.setattr("streamlink.plugins.twitch.TwitchAPI.stream_metadata", mock_stream_metadata)
-
-    # noinspection PyTypeChecker
-    plugin: Twitch = Twitch(session, "https://www.twitch.tv/foo")
-    try:
-        plugin.options.set("disable-reruns", disable)
-        result = plugin._check_for_rerun()
-    finally:
-        plugin.options.clear()
-
-    assert result is expected
-    assert [(record.name, record.levelname, record.message) for record in caplog.records] == logs