Repository: streamlink/streamlink

Issue:
huya live not work
### 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

url with pure-digit doesn't work.

### Debug log

```text
X:\>streamlink https://www.huya.com/688062
[cli][info] Found matching plugin huya for URL https://www.huya.com/688062
error: No playable streams found on this URL: https://www.huya.com/688062

X:\>streamlink https://www.huya.com/dongerlol
[cli][info] Found matching plugin huya for URL https://www.huya.com/dongerlol
Available streams: source_tx (worst), source_hw, source_al, source_bd (best)

X:\>streamlink https://www.huya.com/688062
[cli][info] Found matching plugin huya for URL https://www.huya.com/688062
error: No playable streams found on this URL: https://www.huya.com/688062

X:\>streamlink https://www.huya.com/dongerlol
[cli][info] Found matching plugin huya for URL https://www.huya.com/dongerlol
Available streams: source_tx (worst), source_hw, source_al, source_bd (best)

X:\>streamlink https://www.huya.com/mengxing1221
[cli][info] Found matching plugin huya for URL https://www.huya.com/mengxing1221
Available streams: source_tx (worst), source_hw, source_al, source_bd (best)

X:\>streamlink https://www.huya.com/2337957882
[cli][info] Found matching plugin huya for URL https://www.huya.com/2337957882
error: No playable streams found on this URL: https://www.huya.com/2337957882
```



Discussion:

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
@@ -8,74 +8,96 @@
 import logging
 import re
 from html import unescape as html_unescape
-from typing import Any, Dict
+from typing import Dict
 
 from streamlink.plugin import Plugin, pluginmatcher
 from streamlink.plugin.api import validate
 from streamlink.stream.http import HTTPStream
-from streamlink.utils.parse import parse_json
 
 log = logging.getLogger(__name__)
 
 
 @pluginmatcher(re.compile(
-    r'https?://(?:www\.)?huya\.com/(?P<channel>[^/]+)'
+    r"https?://(?:www\.)?huya\.com/(?P<channel>[^/]+)"
 ))
 class Huya(Plugin):
-    _re_stream = re.compile(r'"stream"\s?:\s?"([^"]+)"')
-    _schema_data = validate.Schema(
-        {
-            # 'status': int,
-            # 'msg': validate.any(None, str),
-            'data': [{
-                'gameStreamInfoList': [{
-                    'sCdnType': str,
-                    'sStreamName': str,
-                    'sFlvUrl': str,
-                    'sFlvUrlSuffix': str,
-                    'sFlvAntiCode': validate.all(str, validate.transform(lambda v: html_unescape(v))),
-                    # 'sHlsUrl': str,
-                    # 'sHlsUrlSuffix': str,
-                    # 'sHlsAntiCode': validate.all(str, validate.transform(lambda v: html_unescape(v))),
-                    validate.optional('iIsMultiStream'): int,
-                    'iPCPriorityRate': int,
-                }]
-            }],
-            # 'vMultiStreamInfo': [{
-            #    'sDisplayName': str,
-            #    'iBitRate': int,
-            # }],
-        },
-        validate.get('data'),
-        validate.get(0),
-        validate.get('gameStreamInfoList'),
-    )
-    QUALITY_WEIGHTS: Dict[str, Any] = {}
+    QUALITY_WEIGHTS: Dict[str, int] = {}
 
     @classmethod
     def stream_weight(cls, key):
         weight = cls.QUALITY_WEIGHTS.get(key)
         if weight:
-            return weight, 'huya'
+            return weight, "huya"
 
-        return Plugin.stream_weight(key)
+        return super().stream_weight(key)
 
     def _get_streams(self):
-        res = self.session.http.get(self.url)
-        data = self._re_stream.search(res.text)
-
+        data = self.session.http.get(self.url, schema=validate.Schema(
+            validate.parse_html(),
+            validate.xml_xpath_string(".//script[contains(text(),'var hyPlayerConfig = {')][1]/text()"),
+            validate.any(None, validate.transform(
+                re.compile(r"""(?P<q>"?)stream(?P=q)\s*:\s*(?:"(?P<base64>.+?)"|(?P<json>\{.+?})\s*}\s*;)""").search,
+            )),
+            validate.any(None, validate.all(
+                validate.any(
+                    validate.all(
+                        validate.get("base64"),
+                        str,
+                        validate.transform(base64.b64decode),
+                    ),
+                    validate.all(
+                        validate.get("json"),
+                        str,
+                    ),
+                ),
+                validate.parse_json(),
+                {
+                    "data": [{
+                        "gameLiveInfo": {
+                            "liveId": int,
+                            "nick": str,
+                            "roomName": str,
+                        },
+                        "gameStreamInfoList": [validate.all(
+                            {
+                                "sCdnType": str,
+                                "iPCPriorityRate": int,
+                                "sStreamName": str,
+                                "sFlvUrl": str,
+                                "sFlvUrlSuffix": str,
+                                "sFlvAntiCode": validate.all(str, validate.transform(lambda v: html_unescape(v))),
+                            },
+                            validate.union_get(
+                                "sCdnType",
+                                "iPCPriorityRate",
+                                "sStreamName",
+                                "sFlvUrl",
+                                "sFlvUrlSuffix",
+                                "sFlvAntiCode",
+                            )),
+                        ],
+                    }],
+                },
+                validate.get(("data", 0)),
+                validate.union_get(
+                    ("gameLiveInfo", "liveId"),
+                    ("gameLiveInfo", "nick"),
+                    ("gameLiveInfo", "roomName"),
+                    "gameStreamInfoList",
+                ),
+            )),
+        ))
         if not data:
             return
 
-        data = parse_json(base64.b64decode(data.group(1)), schema=self._schema_data)
-        for info in data:
-            log.trace(f'{info!r}')
-            flv_url = f'{info["sFlvUrl"]}/{info["sStreamName"]}.{info["sFlvUrlSuffix"]}?{info["sFlvAntiCode"]}'
-            name = f'source_{info["sCdnType"].lower()}'
-            self.QUALITY_WEIGHTS[name] = info['iPCPriorityRate']
-            yield name, HTTPStream(self.session, flv_url)
+        self.id, self.author, self.title, streamdata = data
+
+        for cdntype, priority, streamname, flvurl, suffix, anticode in streamdata:
+            name = f"source_{cdntype.lower()}"
+            self.QUALITY_WEIGHTS[name] = priority
+            yield name, HTTPStream(self.session, f"{flvurl}/{streamname}.{suffix}?{anticode}")
 
-        log.debug(f'QUALITY_WEIGHTS: {self.QUALITY_WEIGHTS!r}')
+        log.debug(f"QUALITY_WEIGHTS: {self.QUALITY_WEIGHTS!r}")
 
 
 __plugin__ = Huya


Test patch:
diff --git a/tests/plugins/test_huya.py b/tests/plugins/test_huya.py
--- a/tests/plugins/test_huya.py
+++ b/tests/plugins/test_huya.py
@@ -6,11 +6,13 @@ class TestPluginCanHandleUrlHuya(PluginCanHandleUrl):
     __plugin__ = Huya
 
     should_match = [
-        'http://www.huya.com/123123123',
-        'http://www.huya.com/name',
-        'https://www.huya.com/123123123',
+        "http://www.huya.com/123123123",
+        "http://www.huya.com/name",
+        "https://www.huya.com/123123123",
+        "https://www.huya.com/name",
     ]
 
     should_not_match = [
-        'http://www.huya.com',
+        "http://www.huya.com",
+        "https://www.huya.com",
     ]