Repository: streamlink/streamlink

Issue:
plugins.nbcnews: Plugin fix
The stream.nbcnews.com API host used in the plugin recently stopped working. It fixes of plugin for the new API endpoint.


Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/nbcnews.py b/src/streamlink/plugins/nbcnews.py
--- a/src/streamlink/plugins/nbcnews.py
+++ b/src/streamlink/plugins/nbcnews.py
@@ -15,80 +15,74 @@
 
 
 @pluginmatcher(re.compile(
-    r'https?://(?:www\.)?nbcnews\.com/now'
+    r"https?://(?:www\.)?nbcnews\.com/now"
 ))
 class NBCNews(Plugin):
-    json_data_re = re.compile(
-        r'<script type="application/ld\+json">({.*?})</script>'
-    )
-    api_url = 'https://stream.nbcnews.com/data/live_sources_{}.json'
-    token_url = 'https://tokens.playmakerservices.com/'
-
-    api_schema = validate.Schema(
-        validate.parse_json(),
-        {
-            'videoSources': [{
-                'sourceUrl': validate.url(),
-                'type': validate.text,
-            }],
-        },
-        validate.get('videoSources'),
-        validate.get(0),
-    )
-
-    token_schema = validate.Schema(
-        validate.parse_json(),
-        {'akamai': [{
-            'tokenizedUrl': validate.url(),
-        }]},
-        validate.get('akamai'),
-        validate.get(0),
-        validate.get('tokenizedUrl'),
-    )
-
-    json_data_schema = validate.Schema(
-        validate.transform(json_data_re.search),
-        validate.any(None, validate.all(
-            validate.get(1),
-            validate.parse_json(),
-            {"embedUrl": validate.url()},
-            validate.get("embedUrl"),
-            validate.transform(lambda url: url.split("/")[-1])
-        ))
-    )
+    URL_API = "https://api-leap.nbcsports.com/feeds/assets/{}?application=NBCNews&format=nbc-player&platform=desktop"
+    URL_TOKEN = "https://tokens.playmakerservices.com/"
 
     title = "NBC News Now"
 
     def _get_streams(self):
-        video_id = self.session.http.get(self.url, schema=self.json_data_schema)
-        if video_id is None:
+        self.id = self.session.http.get(
+            self.url,
+            schema=validate.Schema(
+                validate.parse_html(),
+                validate.xml_xpath_string(".//script[@type='application/ld+json'][1]/text()"),
+                validate.any(None, validate.all(
+                    str,
+                    validate.parse_json(),
+                    {"embedUrl": validate.url()},
+                    validate.get("embedUrl"),
+                    validate.transform(lambda embed_url: embed_url.split("/")[-1])
+                ))
+            ),
+        )
+        if self.id is None:
             return
-        log.debug('API ID: {0}'.format(video_id))
+        log.debug(f"API ID: {self.id}")
 
-        api_url = self.api_url.format(video_id)
-        stream = self.session.http.get(api_url, schema=self.api_schema)
-        log.trace('{0!r}'.format(stream))
-        if stream['type'].lower() != 'live':
-            log.error('Invalid stream type "{0}"'.format(stream['type']))
-            return
+        stream = self.session.http.get(
+            self.URL_API.format(self.id),
+            schema=validate.Schema(
+                validate.parse_json(),
+                {
+                    "videoSources": [{
+                        "cdnSources": {
+                            "primary": [{
+                                "sourceUrl": validate.url(path=validate.endswith(".m3u8")),
+                            }],
+                        },
+                    }],
+                },
+                validate.get(("videoSources", 0, "cdnSources", "primary", 0, "sourceUrl")),
+            ),
+        )
 
-        json_post_data = {
-            'requestorId': 'nbcnews',
-            'pid': video_id,
-            'application': 'NBCSports',
-            'version': 'v1',
-            'platform': 'desktop',
-            'token': '',
-            'resourceId': '',
-            'inPath': 'false',
-            'authenticationType': 'unauth',
-            'cdn': 'akamai',
-            'url': stream['sourceUrl'],
-        }
         url = self.session.http.post(
-            self.token_url,
-            json=json_post_data,
-            schema=self.token_schema,
+            self.URL_TOKEN,
+            json={
+                "requestorId": "nbcnews",
+                "pid": self.id,
+                "application": "NBCSports",
+                "version": "v1",
+                "platform": "desktop",
+                "token": "",
+                "resourceId": "",
+                "inPath": "false",
+                "authenticationType": "unauth",
+                "cdn": "akamai",
+                "url": stream,
+            },
+            schema=validate.Schema(
+                validate.parse_json(),
+                {
+                    "akamai": [{
+                        "tokenizedUrl": validate.url(),
+                    }],
+                },
+                validate.get(("akamai", 0, "tokenizedUrl")),
+            ),
         )
         return HLSStream.parse_variant_playlist(self.session, url)
 


Test patch: