Repository: streamlink/streamlink

Issue:
plugins.cbsnews: livestreams are not parsed for local stations
### 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

Livestreams for local CBSNews stations are not parsed by streamlink. Example: https://www.cbsnews.com/losangeles/live/ is a valid livestream (it plays fine in an incognito browser) but streamlink reports no valid plugin to handle the URL.

### Debug log

```text
$streamlink https://www.cbsnews.com/losangeles/live --loglevel debug
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.10.6
[cli][debug] Streamlink: 4.3.0
[cli][debug] Dependencies:
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 4.9.1
[cli][debug]  pycountry: 22.3.5
[cli][debug]  pycryptodome: 3.15.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.28.1
[cli][debug]  websocket-client: 1.3.3
[cli][debug] Arguments:
[cli][debug]  url=https://www.cbsnews.com/losangeles/live
[cli][debug]  --loglevel=debug
[cli][debug]  --player="C:\Program Files\MPC-HC\mpc-hc64.exe"
[cli][debug]  --player-args=/new /play /close
[cli][debug]  --title={author} - {category} - {title}
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
error: No plugin can handle URL: https://www.cbsnews.com/losangeles/live
```



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/cbsnews.py b/src/streamlink/plugins/cbsnews.py
--- a/src/streamlink/plugins/cbsnews.py
+++ b/src/streamlink/plugins/cbsnews.py
@@ -1,5 +1,5 @@
 """
-$description 24-hour live streaming world news channel, based in the United States of America.
+$description 24-hour live-streaming world news channel, based in the United States of America.
 $url cbsnews.com
 $type live
 """
@@ -12,32 +12,33 @@
 
 
 @pluginmatcher(re.compile(
-    r"https?://www\.cbsnews\.com/live/"
+    r"https?://(?:www\.)?cbsnews\.com/(?:\w+/)?live/?"
 ))
 class CBSNews(Plugin):
     def _get_streams(self):
-        items = self.session.http.get(self.url, schema=validate.Schema(
-            re.compile(r"CBSNEWS.defaultPayload = (\{.*)"),
+        data = self.session.http.get(self.url, schema=validate.Schema(
+            re.compile(r"CBSNEWS\.defaultPayload\s*=\s*(\{.*?})\s*\n"),
             validate.none_or_all(
                 validate.get(1),
                 validate.parse_json(),
                 {
-                    "items": [
-                        validate.all(
-                            {
-                                "video": validate.url(),
-                                "format": "application/x-mpegURL",
-                            },
-                            validate.get("video"),
-                        ),
-                    ],
+                    "items": [{
+                        "id": str,
+                        "canonicalTitle": str,
+                        "video": validate.url(),
+                        "format": "application/x-mpegURL",
+                    }],
                 },
-                validate.get("items"),
+                validate.get(("items", 0)),
+                validate.union_get("id", "canonicalTitle", "video"),
             ),
         ))
-        if items:
-            for hls_url in items:
-                yield from HLSStream.parse_variant_playlist(self.session, hls_url).items()
+        if not data:
+            return
+
+        self.id, self.title, hls_url = data
+
+        return HLSStream.parse_variant_playlist(self.session, hls_url)
 
 
 __plugin__ = CBSNews


Test patch:
diff --git a/tests/plugins/test_cbsnews.py b/tests/plugins/test_cbsnews.py
--- a/tests/plugins/test_cbsnews.py
+++ b/tests/plugins/test_cbsnews.py
@@ -6,9 +6,18 @@ class TestPluginCanHandleUrlCBSNews(PluginCanHandleUrl):
     __plugin__ = CBSNews
 
     should_match = [
-        "https://www.cbsnews.com/live/cbs-sports-hq/",
-        "https://www.cbsnews.com/live/cbsn-local-bay-area/",
+        "https://cbsnews.com/live",
+        "https://cbsnews.com/live/cbs-sports-hq",
+        "https://cbsnews.com/sanfrancisco/live",
+        "https://cbsnews.com/live/",
+        "https://cbsnews.com/live/cbs-sports-hq/",
+        "https://cbsnews.com/sanfrancisco/live/",
         "https://www.cbsnews.com/live/",
+        "https://www.cbsnews.com/live/cbs-sports-hq/",
+        "https://www.cbsnews.com/sanfrancisco/live/",
+        "https://www.cbsnews.com/live/#x",
+        "https://www.cbsnews.com/live/cbs-sports-hq/#x",
+        "https://www.cbsnews.com/sanfrancisco/live/#x",
     ]
 
     should_not_match = [