Repository: streamlink/streamlink

Issue:
plugins.huya: 403 Client Error: Forbidden for url
### 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.5.0

### Description

```
C:\Users\Admin>streamlink --loglevel=debug  https://www.huya.com/lovemia best -o X;\13.flv
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.8.18+
[cli][debug] OpenSSL:    OpenSSL 1.1.1u  30 May 2023
[cli][debug] Streamlink: 6.5.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.9.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.7.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.huya.com/lovemia
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=X;\13.flv
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
[cli][info] Found matching plugin huya for URL https://www.huya.com/lovemia
[plugins.huya][debug] QUALITY_WEIGHTS: {'source_al': 100, 'source_tx': -1, 'source_hw': -1, 'source_hs': 0}
[cli][info] Available streams: source_tx (worst), source_hw, source_hs, source_al (best)
[cli][info] Opening stream: source_al (http)
[cli][info] Writing output to
C:\Users\Admin\X;\13.flv
[cli][debug] Checking file output
File X;\13.flv already exists! Overwrite it? [y/N] y
[cli][debug] Pre-buffering 8192 bytes
[cli][debug] Writing stream to output

[cli][info] Stream ended
[cli][info] Closing currently open stream...
```

```
C:\Users\Admin>streamlink --loglevel=debug  https://www.huya.com/719318  best -o X;\513.flv
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.8.18+
[cli][debug] OpenSSL:    OpenSSL 1.1.1u  30 May 2023
[cli][debug] Streamlink: 6.5.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.9.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.7.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.huya.com/719318
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=X;\513.flv
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
[cli][info] Found matching plugin huya for URL https://www.huya.com/719318
[plugins.huya][debug] QUALITY_WEIGHTS: {'source_al': -1, 'source_tx': -1, 'source_hw': 50, 'source_hs': 50}
[cli][info] Available streams: source_al (worst), source_tx, source_hw, source_hs (best)
[cli][info] Opening stream: source_hs (http)
[cli][info] Writing output to
C:\Users\Admin\X;\513.flv
[cli][debug] Checking file output
[cli][error] Try 1/1: Could not open stream <HTTPStream ['http', 'https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct']> (Could not open stream: Unable to open URL: https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct (403 Client Error: Forbidden for url: https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct))
error: Could not open stream <HTTPStream ['http', 'https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct']>, tried 1 times, exiting
```

### Debug log

```text
C:\Users\Admin>streamlink --loglevel=debug  https://www.huya.com/lovemia best -o X;\13.flv
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.8.18+
[cli][debug] OpenSSL:    OpenSSL 1.1.1u  30 May 2023
[cli][debug] Streamlink: 6.5.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.9.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.7.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.huya.com/lovemia
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=X;\13.flv
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
[cli][info] Found matching plugin huya for URL https://www.huya.com/lovemia
[plugins.huya][debug] QUALITY_WEIGHTS: {'source_al': 100, 'source_tx': -1, 'source_hw': -1, 'source_hs': 0}
[cli][info] Available streams: source_tx (worst), source_hw, source_hs, source_al (best)
[cli][info] Opening stream: source_al (http)
[cli][info] Writing output to
C:\Users\Admin\X;\13.flv
[cli][debug] Checking file output
File X;\13.flv already exists! Overwrite it? [y/N] y
[cli][debug] Pre-buffering 8192 bytes
[cli][debug] Writing stream to output

[cli][info] Stream ended
[cli][info] Closing currently open stream...

C:\Users\Admin>streamlink --loglevel=debug  https://www.huya.com/719318  best -o X;\513.flv
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.8.18+
[cli][debug] OpenSSL:    OpenSSL 1.1.1u  30 May 2023
[cli][debug] Streamlink: 6.5.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2023.11.17
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.3
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.19.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.23.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.9.0
[cli][debug]  urllib3: 2.1.0
[cli][debug]  websocket-client: 1.7.0
[cli][debug] Arguments:
[cli][debug]  url=https://www.huya.com/719318
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --output=X;\513.flv
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
[cli][info] Found matching plugin huya for URL https://www.huya.com/719318
[plugins.huya][debug] QUALITY_WEIGHTS: {'source_al': -1, 'source_tx': -1, 'source_hw': 50, 'source_hs': 50}
[cli][info] Available streams: source_al (worst), source_tx, source_hw, source_hs (best)
[cli][info] Opening stream: source_hs (http)
[cli][info] Writing output to
C:\Users\Admin\X;\513.flv
[cli][debug] Checking file output
[cli][error] Try 1/1: Could not open stream <HTTPStream ['http', 'https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct']> (Could not open stream: Unable to open URL: https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct (403 Client Error: Forbidden for url: https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct))
error: Could not open stream <HTTPStream ['http', 'https://hs.flv.huya.com/src/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsSecret=10a92e90f6d8f76dba67d27906870b08&wsTime=658fb155&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct']>, tried 1 times, exiting
```



Discussion:
Not a streamlink issue.

As you can see in the list of returned streams, those streams are not different qualities, but streams served on different CDNs. They are also sorted by the CDN quality weights returned by their API. Apparently, only one CDN seems to be working for some reason and not even the one with the highest quality weight.

`source_hw` was working perfectly fine:
```
$ streamlink 'https://www.huya.com/719318' source_hw --player-no-close 
[cli][info] Found matching plugin huya for URL https://www.huya.com/719318
[cli][info] Available streams: source_al (worst), source_tx, source_hw, source_hs (best)
[cli][info] Opening stream: source_hw (http)
[cli][info] Starting player: /usr/bin/mpv
[cli][info] Stream ended
[cli][info] Closing currently open stream...
```


Simple check which sends a `HEAD` HTTP request to each stream URL:

```
$ streamlink \
  --json \
  'https://www.huya.com/719318' \
  | jq -r ".streams | to_entries[] | \"\(.key) '\(.value.url)'\"" \
  | xargs -n2 bash -c 'echo "STREAM: $1";curl -I "$2"' --

STREAM: source_al
HTTP/1.1 403 Forbidden
Server: Tengine
Date: Sat, 30 Dec 2023 10:19:43 GMT
Content-Type: text/html
Content-Length: 254
Connection: close
X-Tengine-Type: live
cdncip: redacted
cdnsip: 47.246.48.98
Access-Control-Expose-Headers: alt-svc,cdncip,cdnsip
Access-Control-Allow-Origin: *
Via: cache5.nl2[,0]

STREAM: source_tx
HTTP/1.1 404 Not Found
Server: MC_VCLOUD_LIVE
Date: Sat, 30 Dec 2023 10:19:44 GMT
Content-Length: 0
Connection: keep-alive
Cache-Control: max-age=0
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
X-Tlive-SpanId: 620A216529CA21CA
Access-Control-Expose-Headers: alt-svc,cdncip,cdnsip
cdncip: redacted
cdnsip: 101.33.10.98
X-NWS-LOG-UUID: 2bc8460d-4500-4618-ae90-05b7ad907d89
X-SSL-PROTOCOL: TLSv1.3

STREAM: source_hw
HTTP/1.1 302 Moved Temporarily
Server: openresty
Date: Sat, 30 Dec 2023 10:19:46 GMT
Connection: close
Access-Control-Expose-Headers: alt-svc, cdncip, cdnsip
cdncip: redacted
cdnsip: 2408:8756:d0ff:40::39
Location: https://4c5f25b40eb0e7fd16f4b6a11caeede4.livehwc3.cn/hw.flv.huya.com/huyalive/2240853517-2240853517-9624392570641580032-4481830490-10057-A-0-1.flv?wsTime=658feebd&cdn_redirect_tag=1703931586&cdn_redirect_domain=ed60e6e67f460afe5345e98fff768af4.livehwc3.cn&policy=6&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=huya_live&fs=bgct&cdn_redirect=true&wsSecret=2eb3589f07e256abdc06cd4e3e15b359
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization

STREAM: source_hs
HTTP/2 403 
access-control-allow-methods: GET,POST,OPTIONS
access-control-allow-origin: *
access-control-expose-headers: alt-svc, cdncip, cdnsip
cache-control: no-cache
content-type: video/x-flv
server: Bytedance NSS
via: n045-043-034-227
x-has-token: 29UWy/Pyqhkn6+SCJdomWFOWv+Z3ek+It3lLy+tPcrfz67n5p8zH5Imd
x-server-ip: 45.43.34.227
cdncip: redacted
cdnsip: 2602:ffe4:2:16::7
date: Sat, 30 Dec 2023 10:19:47 GMT
```

The plugin could do the same and check the HTTP status of each available stream, so non-working ones could be filtered out.

Resolving patch:
diff --git a/src/streamlink/plugins/huya.py b/src/streamlink/plugins/huya.py
--- a/src/streamlink/plugins/huya.py
+++ b/src/streamlink/plugins/huya.py
@@ -98,9 +98,13 @@ def _get_streams(self):
         self.id, self.author, self.title, streamdata = data
 
         for cdntype, priority, streamname, flvurl, suffix, anticode in streamdata:
+            url = update_scheme("https://", f"{flvurl}/{streamname}.{suffix}?{anticode}")
+            if self.session.http.head(url, raise_for_status=False).status_code >= 400:
+                continue
+
             name = f"source_{cdntype.lower()}"
             self.QUALITY_WEIGHTS[name] = priority
-            yield name, HTTPStream(self.session, update_scheme("https://", f"{flvurl}/{streamname}.{suffix}?{anticode}"))
+            yield name, HTTPStream(self.session, url)
 
         log.debug(f"QUALITY_WEIGHTS: {self.QUALITY_WEIGHTS!r}")
 


Test patch: