Repository: streamlink/streamlink

Issue:
plugins.tvp: No playable streams found on www.tvp.info
### 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.2.0

### Description

It worked in the past but stopped some time ago:

streamlink https://www.tvp.info/72577058/28092023-0823 best --stream-url
error: No playable streams found on this URL: https://www.tvp.info/72577058/28092023-0823


### Debug log

```text
streamlink https://www.tvp.info/72577058/28092023-0823 best --stream-url --loglevel=debug                   
error: No playable streams found on this URL: https://www.tvp.info/72577058/28092023-0823
```



Discussion:

Resolving patch:
diff --git a/src/streamlink/plugins/tvp.py b/src/streamlink/plugins/tvp.py
--- a/src/streamlink/plugins/tvp.py
+++ b/src/streamlink/plugins/tvp.py
@@ -1,7 +1,9 @@
 """
 $description Live TV channels and VODs from TVP, a Polish public, state-owned broadcaster.
+$url stream.tvp.pl
+$url vod.tvp.pl
+$url tvpstream.vod.tvp.pl
 $url tvp.info
-$url tvp.pl
 $type live, vod
 $metadata id
 $metadata title
@@ -23,21 +25,20 @@
 log = logging.getLogger(__name__)
 
 
-@pluginmatcher(re.compile(r"""https?://(?:www\.)?tvp\.info/"""), name="tvp_info")
-@pluginmatcher(re.compile(
-    r"""
-        https?://
-        (?:
-            (?:tvpstream\.vod|stream)\.tvp\.pl(?:/(?:\?channel_id=(?P<channel_id>\d+))?)?$
-            |
-            vod\.tvp\.pl/[^/]+/.+,(?P<vod_id>\d+)$
-        )
-    """,
-    re.VERBOSE,
+@pluginmatcher(name="default", pattern=re.compile(
+    r"https?://(?:stream|tvpstream\.vod)\.tvp\.pl(?:/(?:\?channel_id=(?P<channel_id>\d+))?)?$",
+))
+@pluginmatcher(name="vod", pattern=re.compile(
+    r"https?://vod\.tvp\.pl/[^/]+/.+,(?P<vod_id>\d+)$",
+))
+@pluginmatcher(name="tvp_info", pattern=re.compile(
+    r"https?://(?:www\.)?tvp\.info/",
 ))
 class TVP(Plugin):
     _URL_PLAYER = "https://stream.tvp.pl/sess/TVPlayer2/embed.php"
     _URL_VOD = "https://vod.tvp.pl/api/products/{vod_id}/videos/playlist"
+    _URL_INFO_API_TOKEN = "https://api.tvp.pl/tokenizer/token/{token}"
+    _URL_INFO_API_NEWS = "https://www.tvp.info/api/info/news?device=www&id={id}"
 
     def _get_video_id(self, channel_id: Optional[str]):
         items: List[Tuple[int, int]] = self.session.http.get(
@@ -179,27 +180,44 @@ def _get_tvp_info_vod(self):
             self.url,
             schema=validate.Schema(
                 validate.parse_html(),
-                validate.xml_xpath_string(".//script[contains(text(), 'window.__videoData')][1]/text()"),
+                validate.xml_xpath_string(".//script[contains(text(), 'window.__pageSettings')][1]/text()"),
                 validate.none_or_all(
                     str,
-                    validate.regex(re.compile(r"window\.__videoData\s*=\s*(?P<json>{.*?})\s*;", re.DOTALL)),
+                    validate.regex(re.compile(r"window\.__pageSettings\s*=\s*(?P<json>{.*?})\s*;", re.DOTALL)),
                     validate.get("json"),
                     validate.parse_json(),
                     {
-                        "_id": int,
-                        "title": str,
+                        "type": validate.any("VIDEO", "NEWS"),
+                        "id": int,
                     },
+                    validate.union_get("type", "id"),
                 ),
             ),
         )
         if not data:
             return
 
-        self.id = data.get("_id")
-        self.title = data.get("title")
+        _type, self.id = data
+        if _type == "NEWS":
+            self.id, self.title = self.session.http.get(
+                self._URL_INFO_API_NEWS.format(id=self.id),
+                schema=validate.Schema(
+                    validate.parse_json(),
+                    {
+                        "data": {
+                            "video": {
+                                "title": str,
+                                "_id": int,
+                            },
+                        },
+                    },
+                    validate.get(("data", "video")),
+                    validate.union_get("_id", "title"),
+                ),
+            )
 
         data = self.session.http.get(
-            f"https://api.tvp.pl/tokenizer/token/{self.id}",
+            self._URL_INFO_API_TOKEN.format(token=self.id),
             schema=validate.Schema(
                 validate.parse_json(),
                 {
@@ -225,10 +243,9 @@ def _get_tvp_info_vod(self):
     def _get_streams(self):
         if self.matches["tvp_info"]:
             return self._get_tvp_info_vod()
-        elif self.match["vod_id"]:
+        if self.matches["vod"]:
             return self._get_vod(self.match["vod_id"])
-        else:
-            return self._get_live(self.match["channel_id"])
+        return self._get_live(self.match["channel_id"])
 
 
 __plugin__ = TVP


Test patch:
diff --git a/tests/plugins/test_tvp.py b/tests/plugins/test_tvp.py
--- a/tests/plugins/test_tvp.py
+++ b/tests/plugins/test_tvp.py
@@ -7,31 +7,30 @@ class TestPluginCanHandleUrlTVP(PluginCanHandleUrl):
 
     should_match_groups = [
         # live
-        ("https://stream.tvp.pl", {}),
-        ("https://stream.tvp.pl/", {}),
-        ("https://stream.tvp.pl/?channel_id=63759349", {"channel_id": "63759349"}),
-        ("https://stream.tvp.pl/?channel_id=14812849", {"channel_id": "14812849"}),
+        (("default", "https://stream.tvp.pl"), {}),
+        (("default", "https://stream.tvp.pl/"), {}),
+        (("default", "https://stream.tvp.pl/?channel_id=63759349"), {"channel_id": "63759349"}),
+        (("default", "https://stream.tvp.pl/?channel_id=14812849"), {"channel_id": "14812849"}),
         # old live URLs
-        ("https://tvpstream.vod.tvp.pl", {}),
-        ("https://tvpstream.vod.tvp.pl/", {}),
-        ("https://tvpstream.vod.tvp.pl/?channel_id=63759349", {"channel_id": "63759349"}),
-        ("https://tvpstream.vod.tvp.pl/?channel_id=14812849", {"channel_id": "14812849"}),
+        (("default", "https://tvpstream.vod.tvp.pl"), {}),
+        (("default", "https://tvpstream.vod.tvp.pl/"), {}),
+        (("default", "https://tvpstream.vod.tvp.pl/?channel_id=63759349"), {"channel_id": "63759349"}),
+        (("default", "https://tvpstream.vod.tvp.pl/?channel_id=14812849"), {"channel_id": "14812849"}),
 
         # VOD
         (
-            "https://vod.tvp.pl/filmy-dokumentalne,163/krolowa-wladczyni-i-matka,284734",
+            ("vod", "https://vod.tvp.pl/filmy-dokumentalne,163/krolowa-wladczyni-i-matka,284734"),
             {"vod_id": "284734"},
         ),
         # VOD episode
         (
-            "https://vod.tvp.pl/programy,88/z-davidem-attenborough-dokola-swiata-odcinki,284703/odcinek-2,S01E02,319220",
+            ("vod", "https://vod.tvp.pl/programy,88/z-davidem-attenborough-dokola-swiata-odcinki,284703/odcinek-2,S01E02,319220"),
             {"vod_id": "319220"},
         ),
 
         # tvp.info
-        (("tvp_info", "https://tvp.info/"), {}),
-        (("tvp_info", "https://www.tvp.info/"), {}),
-        (("tvp_info", "https://www.tvp.info/65275202/13012023-0823"), {}),
+        (("tvp_info", "https://www.tvp.info/72577058/28092023-0823"), {}),
+        (("tvp_info", "https://www.tvp.info/73805503/przygotowania-do-uroczystosci-wszystkich-swietych"), {}),
     ]
 
     should_not_match = [