We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
plugins.rtve: Live streams not working.
### 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

As for today, live streams from RTVE is not working.

### Debug log

```text
[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.rtve.es/play/videos/directo/la-1/
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Streamlink\ffmpeg\ffmpeg.exe
[cli][info] Found matching plugin rtve for URL https://www.rtve.es/play/videos/directo/la-1/
error: No playable streams found on this URL: https://www.rtve.es/play/videos/directo/la-1/
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/streamlink/plugins/rtve.py`
Content:
```
1 """
2 $description Live TV channels and video on-demand service from RTVE, a Spanish public, state-owned broadcaster.
3 $url rtve.es
4 $type live, vod
5 $region Spain
6 """
7 
8 import logging
9 import re
10 from base64 import b64decode
11 from io import BytesIO
12 from typing import Iterator, Sequence, Tuple
13 from urllib.parse import urlparse
14 
15 from streamlink.plugin import Plugin, pluginargument, pluginmatcher
16 from streamlink.plugin.api import validate
17 from streamlink.stream.ffmpegmux import MuxedStream
18 from streamlink.stream.hls import HLSStream
19 from streamlink.stream.http import HTTPStream
20 from streamlink.utils.url import update_scheme
21 
22 log = logging.getLogger(__name__)
23 
24 
25 class Base64Reader:
26     def __init__(self, data: str):
27         stream = BytesIO(b64decode(data))
28 
29         def _iterate():
30             while True:
31                 chunk = stream.read(1)
32                 if len(chunk) == 0:  # pragma: no cover
33                     return
34                 yield ord(chunk)
35 
36         self._iterator: Iterator[int] = _iterate()
37 
38     def read(self, num: int) -> Sequence[int]:
39         res = []
40         for _ in range(num):
41             item = next(self._iterator, None)
42             if item is None:  # pragma: no cover
43                 break
44             res.append(item)
45         return res
46 
47     def skip(self, num: int) -> None:
48         self.read(num)
49 
50     def read_chars(self, num: int) -> str:
51         return "".join(chr(item) for item in self.read(num))
52 
53     def read_int(self) -> int:
54         a, b, c, d = self.read(4)
55         return a << 24 | b << 16 | c << 8 | d
56 
57     def read_chunk(self) -> Tuple[str, Sequence[int]]:
58         size = self.read_int()
59         chunktype = self.read_chars(4)
60         chunkdata = self.read(size)
61         if len(chunkdata) != size:  # pragma: no cover
62             raise ValueError("Invalid chunk length")
63         self.skip(4)
64         return chunktype, chunkdata
65 
66 
67 class ZTNR:
68     @staticmethod
69     def _get_alphabet(text: str) -> str:
70         res = []
71         j = 0
72         k = 0
73         for char in text:
74             if k > 0:
75                 k -= 1
76             else:
77                 res.append(char)
78                 j = (j + 1) % 4
79                 k = j
80         return "".join(res)
81 
82     @staticmethod
83     def _get_url(text: str, alphabet: str) -> str:
84         res = []
85         j = 0
86         n = 0
87         k = 3
88         cont = 0
89         for char in text:
90             if j == 0:
91                 n = int(char) * 10
92                 j = 1
93             elif k > 0:
94                 k -= 1
95             else:
96                 res.append(alphabet[n + int(char)])
97                 j = 0
98                 k = cont % 4
99                 cont += 1
100         return "".join(res)
101 
102     @classmethod
103     def _get_source(cls, alphabet: str, data: str) -> str:
104         return cls._get_url(data, cls._get_alphabet(alphabet))
105 
106     @classmethod
107     def translate(cls, data: str) -> Iterator[Tuple[str, str]]:
108         reader = Base64Reader(data.replace("\n", ""))
109         reader.skip(8)
110         chunk_type, chunk_data = reader.read_chunk()
111         while chunk_type != "IEND":
112             if chunk_type == "tEXt":
113                 content = "".join(chr(item) for item in chunk_data if item > 0)
114                 if "#" not in content or "%%" not in content:  # pragma: no cover
115                     continue
116                 alphabet, content = content.split("#", 1)
117                 quality, content = content.split("%%", 1)
118                 yield quality, cls._get_source(alphabet, content)
119             chunk_type, chunk_data = reader.read_chunk()
120 
121 
122 @pluginmatcher(re.compile(
123     r"https?://(?:www\.)?rtve\.es/play/videos/.+"
124 ))
125 @pluginargument(
126     "mux-subtitles",
127     is_global=True,
128 )
129 class Rtve(Plugin):
130     URL_VIDEOS = "https://ztnr.rtve.es/ztnr/movil/thumbnail/rtveplayw/videos/{id}.png?q=v2"
131     URL_SUBTITLES = "https://www.rtve.es/api/videos/{id}/subtitulos.json"
132 
133     def _get_streams(self):
134         self.id = self.session.http.get(self.url, schema=validate.Schema(
135             re.compile(r"\bdata-setup='({.+?})'", re.DOTALL),
136             validate.none_or_all(
137                 validate.get(1),
138                 validate.parse_json(),
139                 {
140                     "idAsset": validate.any(int, validate.all(str, validate.transform(int))),
141                 },
142                 validate.get("idAsset"),
143             ),
144         ))
145         if not self.id:
146             return
147 
148         urls = self.session.http.get(
149             self.URL_VIDEOS.format(id=self.id),
150             schema=validate.Schema(
151                 validate.transform(ZTNR.translate),
152                 validate.transform(list),
153                 [(str, validate.url())],
154             ),
155         )
156 
157         url = next((url for _, url in urls if urlparse(url).path.endswith(".m3u8")), None)
158         if not url:
159             url = next((url for _, url in urls if urlparse(url).path.endswith(".mp4")), None)
160             if url:
161                 yield "vod", HTTPStream(self.session, url)
162             return
163 
164         streams = HLSStream.parse_variant_playlist(self.session, url).items()
165 
166         if self.options.get("mux-subtitles"):
167             subs = self.session.http.get(
168                 self.URL_SUBTITLES.format(id=self.id),
169                 schema=validate.Schema(
170                     validate.parse_json(),
171                     {
172                         "page": {
173                             "items": [{
174                                 "lang": str,
175                                 "src": validate.url(),
176                             }]
177                         }
178                     },
179                     validate.get(("page", "items")),
180                 ),
181             )
182             if subs:
183                 subtitles = {
184                     s["lang"]: HTTPStream(self.session, update_scheme("https://", s["src"], force=True))
185                     for s in subs
186                 }
187                 for quality, stream in streams:
188                     yield quality, MuxedStream(self.session, stream, subtitles=subtitles)
189                 return
190 
191         yield from streams
192 
193 
194 __plugin__ = Rtve
195
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/streamlink/plugins/rtve.py b/src/streamlink/plugins/rtve.py
--- a/src/streamlink/plugins/rtve.py
+++ b/src/streamlink/plugins/rtve.py
@@ -127,6 +127,7 @@
     is_global=True,
 )
 class Rtve(Plugin):
+    URL_M3U8 = "https://ztnr.rtve.es/ztnr/{id}.m3u8"
     URL_VIDEOS = "https://ztnr.rtve.es/ztnr/movil/thumbnail/rtveplayw/videos/{id}.png?q=v2"
     URL_SUBTITLES = "https://www.rtve.es/api/videos/{id}/subtitulos.json"
 
@@ -145,6 +146,8 @@
         if not self.id:
             return
 
+        # check obfuscated stream URLs via self.URL_VIDEOS and ZTNR.translate() first
+        # self.URL_M3U8 appears to be valid for all streams, but doesn't provide any content in same cases
         urls = self.session.http.get(
             self.URL_VIDEOS.format(id=self.id),
             schema=validate.Schema(
@@ -154,12 +157,16 @@
             ),
         )
 
-        url = next((url for _, url in urls if urlparse(url).path.endswith(".m3u8")), None)
-        if not url:
-            url = next((url for _, url in urls if urlparse(url).path.endswith(".mp4")), None)
-            if url:
-                yield "vod", HTTPStream(self.session, url)
-            return
+        # then fall back to self.URL_M3U8
+        if not urls:
+            url = self.URL_M3U8.format(id=self.id)
+        else:
+            url = next((url for _, url in urls if urlparse(url).path.endswith(".m3u8")), None)
+            if not url:
+                url = next((url for _, url in urls if urlparse(url).path.endswith(".mp4")), None)
+                if url:
+                    yield "vod", HTTPStream(self.session, url)
+                return
 
         streams = HLSStream.parse_variant_playlist(self.session, url).items()
 
@@ -173,8 +180,8 @@
                             "items": [{
                                 "lang": str,
                                 "src": validate.url(),
-                            }]
-                        }
+                            }],
+                        },
                     },
                     validate.get(("page", "items")),
                 ),

{"golden_diff": "diff --git a/src/streamlink/plugins/rtve.py b/src/streamlink/plugins/rtve.py\n--- a/src/streamlink/plugins/rtve.py\n+++ b/src/streamlink/plugins/rtve.py\n@@ -127,6 +127,7 @@\n     is_global=True,\n )\n class Rtve(Plugin):\n+    URL_M3U8 = \"https://ztnr.rtve.es/ztnr/{id}.m3u8\"\n     URL_VIDEOS = \"https://ztnr.rtve.es/ztnr/movil/thumbnail/rtveplayw/videos/{id}.png?q=v2\"\n     URL_SUBTITLES = \"https://www.rtve.es/api/videos/{id}/subtitulos.json\"\n \n@@ -145,6 +146,8 @@\n         if not self.id:\n             return\n \n+        # check obfuscated stream URLs via self.URL_VIDEOS and ZTNR.translate() first\n+        # self.URL_M3U8 appears to be valid for all streams, but doesn't provide any content in same cases\n         urls = self.session.http.get(\n             self.URL_VIDEOS.format(id=self.id),\n             schema=validate.Schema(\n@@ -154,12 +157,16 @@\n             ),\n         )\n \n-        url = next((url for _, url in urls if urlparse(url).path.endswith(\".m3u8\")), None)\n-        if not url:\n-            url = next((url for _, url in urls if urlparse(url).path.endswith(\".mp4\")), None)\n-            if url:\n-                yield \"vod\", HTTPStream(self.session, url)\n-            return\n+        # then fall back to self.URL_M3U8\n+        if not urls:\n+            url = self.URL_M3U8.format(id=self.id)\n+        else:\n+            url = next((url for _, url in urls if urlparse(url).path.endswith(\".m3u8\")), None)\n+            if not url:\n+                url = next((url for _, url in urls if urlparse(url).path.endswith(\".mp4\")), None)\n+                if url:\n+                    yield \"vod\", HTTPStream(self.session, url)\n+                return\n \n         streams = HLSStream.parse_variant_playlist(self.session, url).items()\n \n@@ -173,8 +180,8 @@\n                             \"items\": [{\n                                 \"lang\": str,\n                                 \"src\": validate.url(),\n-                            }]\n-                        }\n+                            }],\n+                        },\n                     },\n                     validate.get((\"page\", \"items\")),\n                 ),\n", "issue": "plugins.rtve: Live streams not working.\n### Checklist\n\n- [X] This is a plugin issue and not a different kind of issue\n- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)\n- [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)\n- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)\n\n### Streamlink version\n\nLatest stable release\n\n### Description\n\nAs for today, live streams from RTVE is not working.\n\n### Debug log\n\n```text\n[cli][debug] OS:         Windows 10\r\n[cli][debug] Python:     3.10.6\r\n[cli][debug] Streamlink: 4.3.0\r\n[cli][debug] Dependencies:\r\n[cli][debug]  isodate: 0.6.1\r\n[cli][debug]  lxml: 4.9.1\r\n[cli][debug]  pycountry: 22.3.5\r\n[cli][debug]  pycryptodome: 3.15.0\r\n[cli][debug]  PySocks: 1.7.1\r\n[cli][debug]  requests: 2.28.1\r\n[cli][debug]  websocket-client: 1.3.3\r\n[cli][debug] Arguments:\r\n[cli][debug]  url=https://www.rtve.es/play/videos/directo/la-1/\r\n[cli][debug]  stream=['best']\r\n[cli][debug]  --loglevel=debug\r\n[cli][debug]  --ffmpeg-ffmpeg=C:\\Program Files\\Streamlink\\ffmpeg\\ffmpeg.exe\r\n[cli][info] Found matching plugin rtve for URL https://www.rtve.es/play/videos/directo/la-1/\r\nerror: No playable streams found on this URL: https://www.rtve.es/play/videos/directo/la-1/\n```\n\n", "before_files": [{"content": "\"\"\"\n$description Live TV channels and video on-demand service from RTVE, a Spanish public, state-owned broadcaster.\n$url rtve.es\n$type live, vod\n$region Spain\n\"\"\"\n\nimport logging\nimport re\nfrom base64 import b64decode\nfrom io import BytesIO\nfrom typing import Iterator, Sequence, Tuple\nfrom urllib.parse import urlparse\n\nfrom streamlink.plugin import Plugin, pluginargument, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream.ffmpegmux import MuxedStream\nfrom streamlink.stream.hls import HLSStream\nfrom streamlink.stream.http import HTTPStream\nfrom streamlink.utils.url import update_scheme\n\nlog = logging.getLogger(__name__)\n\n\nclass Base64Reader:\n    def __init__(self, data: str):\n        stream = BytesIO(b64decode(data))\n\n        def _iterate():\n            while True:\n                chunk = stream.read(1)\n                if len(chunk) == 0:  # pragma: no cover\n                    return\n                yield ord(chunk)\n\n        self._iterator: Iterator[int] = _iterate()\n\n    def read(self, num: int) -> Sequence[int]:\n        res = []\n        for _ in range(num):\n            item = next(self._iterator, None)\n            if item is None:  # pragma: no cover\n                break\n            res.append(item)\n        return res\n\n    def skip(self, num: int) -> None:\n        self.read(num)\n\n    def read_chars(self, num: int) -> str:\n        return \"\".join(chr(item) for item in self.read(num))\n\n    def read_int(self) -> int:\n        a, b, c, d = self.read(4)\n        return a << 24 | b << 16 | c << 8 | d\n\n    def read_chunk(self) -> Tuple[str, Sequence[int]]:\n        size = self.read_int()\n        chunktype = self.read_chars(4)\n        chunkdata = self.read(size)\n        if len(chunkdata) != size:  # pragma: no cover\n            raise ValueError(\"Invalid chunk length\")\n        self.skip(4)\n        return chunktype, chunkdata\n\n\nclass ZTNR:\n    @staticmethod\n    def _get_alphabet(text: str) -> str:\n        res = []\n        j = 0\n        k = 0\n        for char in text:\n            if k > 0:\n                k -= 1\n            else:\n                res.append(char)\n                j = (j + 1) % 4\n                k = j\n        return \"\".join(res)\n\n    @staticmethod\n    def _get_url(text: str, alphabet: str) -> str:\n        res = []\n        j = 0\n        n = 0\n        k = 3\n        cont = 0\n        for char in text:\n            if j == 0:\n                n = int(char) * 10\n                j = 1\n            elif k > 0:\n                k -= 1\n            else:\n                res.append(alphabet[n + int(char)])\n                j = 0\n                k = cont % 4\n                cont += 1\n        return \"\".join(res)\n\n    @classmethod\n    def _get_source(cls, alphabet: str, data: str) -> str:\n        return cls._get_url(data, cls._get_alphabet(alphabet))\n\n    @classmethod\n    def translate(cls, data: str) -> Iterator[Tuple[str, str]]:\n        reader = Base64Reader(data.replace(\"\\n\", \"\"))\n        reader.skip(8)\n        chunk_type, chunk_data = reader.read_chunk()\n        while chunk_type != \"IEND\":\n            if chunk_type == \"tEXt\":\n                content = \"\".join(chr(item) for item in chunk_data if item > 0)\n                if \"#\" not in content or \"%%\" not in content:  # pragma: no cover\n                    continue\n                alphabet, content = content.split(\"#\", 1)\n                quality, content = content.split(\"%%\", 1)\n                yield quality, cls._get_source(alphabet, content)\n            chunk_type, chunk_data = reader.read_chunk()\n\n\n@pluginmatcher(re.compile(\n    r\"https?://(?:www\\.)?rtve\\.es/play/videos/.+\"\n))\n@pluginargument(\n    \"mux-subtitles\",\n    is_global=True,\n)\nclass Rtve(Plugin):\n    URL_VIDEOS = \"https://ztnr.rtve.es/ztnr/movil/thumbnail/rtveplayw/videos/{id}.png?q=v2\"\n    URL_SUBTITLES = \"https://www.rtve.es/api/videos/{id}/subtitulos.json\"\n\n    def _get_streams(self):\n        self.id = self.session.http.get(self.url, schema=validate.Schema(\n            re.compile(r\"\\bdata-setup='({.+?})'\", re.DOTALL),\n            validate.none_or_all(\n                validate.get(1),\n                validate.parse_json(),\n                {\n                    \"idAsset\": validate.any(int, validate.all(str, validate.transform(int))),\n                },\n                validate.get(\"idAsset\"),\n            ),\n        ))\n        if not self.id:\n            return\n\n        urls = self.session.http.get(\n            self.URL_VIDEOS.format(id=self.id),\n            schema=validate.Schema(\n                validate.transform(ZTNR.translate),\n                validate.transform(list),\n                [(str, validate.url())],\n            ),\n        )\n\n        url = next((url for _, url in urls if urlparse(url).path.endswith(\".m3u8\")), None)\n        if not url:\n            url = next((url for _, url in urls if urlparse(url).path.endswith(\".mp4\")), None)\n            if url:\n                yield \"vod\", HTTPStream(self.session, url)\n            return\n\n        streams = HLSStream.parse_variant_playlist(self.session, url).items()\n\n        if self.options.get(\"mux-subtitles\"):\n            subs = self.session.http.get(\n                self.URL_SUBTITLES.format(id=self.id),\n                schema=validate.Schema(\n                    validate.parse_json(),\n                    {\n                        \"page\": {\n                            \"items\": [{\n                                \"lang\": str,\n                                \"src\": validate.url(),\n                            }]\n                        }\n                    },\n                    validate.get((\"page\", \"items\")),\n                ),\n            )\n            if subs:\n                subtitles = {\n                    s[\"lang\"]: HTTPStream(self.session, update_scheme(\"https://\", s[\"src\"], force=True))\n                    for s in subs\n                }\n                for quality, stream in streams:\n                    yield quality, MuxedStream(self.session, stream, subtitles=subtitles)\n                return\n\n        yield from streams\n\n\n__plugin__ = Rtve\n", "path": "src/streamlink/plugins/rtve.py"}], "after_files": [{"content": "\"\"\"\n$description Live TV channels and video on-demand service from RTVE, a Spanish public, state-owned broadcaster.\n$url rtve.es\n$type live, vod\n$region Spain\n\"\"\"\n\nimport logging\nimport re\nfrom base64 import b64decode\nfrom io import BytesIO\nfrom typing import Iterator, Sequence, Tuple\nfrom urllib.parse import urlparse\n\nfrom streamlink.plugin import Plugin, pluginargument, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream.ffmpegmux import MuxedStream\nfrom streamlink.stream.hls import HLSStream\nfrom streamlink.stream.http import HTTPStream\nfrom streamlink.utils.url import update_scheme\n\nlog = logging.getLogger(__name__)\n\n\nclass Base64Reader:\n    def __init__(self, data: str):\n        stream = BytesIO(b64decode(data))\n\n        def _iterate():\n            while True:\n                chunk = stream.read(1)\n                if len(chunk) == 0:  # pragma: no cover\n                    return\n                yield ord(chunk)\n\n        self._iterator: Iterator[int] = _iterate()\n\n    def read(self, num: int) -> Sequence[int]:\n        res = []\n        for _ in range(num):\n            item = next(self._iterator, None)\n            if item is None:  # pragma: no cover\n                break\n            res.append(item)\n        return res\n\n    def skip(self, num: int) -> None:\n        self.read(num)\n\n    def read_chars(self, num: int) -> str:\n        return \"\".join(chr(item) for item in self.read(num))\n\n    def read_int(self) -> int:\n        a, b, c, d = self.read(4)\n        return a << 24 | b << 16 | c << 8 | d\n\n    def read_chunk(self) -> Tuple[str, Sequence[int]]:\n        size = self.read_int()\n        chunktype = self.read_chars(4)\n        chunkdata = self.read(size)\n        if len(chunkdata) != size:  # pragma: no cover\n            raise ValueError(\"Invalid chunk length\")\n        self.skip(4)\n        return chunktype, chunkdata\n\n\nclass ZTNR:\n    @staticmethod\n    def _get_alphabet(text: str) -> str:\n        res = []\n        j = 0\n        k = 0\n        for char in text:\n            if k > 0:\n                k -= 1\n            else:\n                res.append(char)\n                j = (j + 1) % 4\n                k = j\n        return \"\".join(res)\n\n    @staticmethod\n    def _get_url(text: str, alphabet: str) -> str:\n        res = []\n        j = 0\n        n = 0\n        k = 3\n        cont = 0\n        for char in text:\n            if j == 0:\n                n = int(char) * 10\n                j = 1\n            elif k > 0:\n                k -= 1\n            else:\n                res.append(alphabet[n + int(char)])\n                j = 0\n                k = cont % 4\n                cont += 1\n        return \"\".join(res)\n\n    @classmethod\n    def _get_source(cls, alphabet: str, data: str) -> str:\n        return cls._get_url(data, cls._get_alphabet(alphabet))\n\n    @classmethod\n    def translate(cls, data: str) -> Iterator[Tuple[str, str]]:\n        reader = Base64Reader(data.replace(\"\\n\", \"\"))\n        reader.skip(8)\n        chunk_type, chunk_data = reader.read_chunk()\n        while chunk_type != \"IEND\":\n            if chunk_type == \"tEXt\":\n                content = \"\".join(chr(item) for item in chunk_data if item > 0)\n                if \"#\" not in content or \"%%\" not in content:  # pragma: no cover\n                    continue\n                alphabet, content = content.split(\"#\", 1)\n                quality, content = content.split(\"%%\", 1)\n                yield quality, cls._get_source(alphabet, content)\n            chunk_type, chunk_data = reader.read_chunk()\n\n\n@pluginmatcher(re.compile(\n    r\"https?://(?:www\\.)?rtve\\.es/play/videos/.+\"\n))\n@pluginargument(\n    \"mux-subtitles\",\n    is_global=True,\n)\nclass Rtve(Plugin):\n    URL_M3U8 = \"https://ztnr.rtve.es/ztnr/{id}.m3u8\"\n    URL_VIDEOS = \"https://ztnr.rtve.es/ztnr/movil/thumbnail/rtveplayw/videos/{id}.png?q=v2\"\n    URL_SUBTITLES = \"https://www.rtve.es/api/videos/{id}/subtitulos.json\"\n\n    def _get_streams(self):\n        self.id = self.session.http.get(self.url, schema=validate.Schema(\n            re.compile(r\"\\bdata-setup='({.+?})'\", re.DOTALL),\n            validate.none_or_all(\n                validate.get(1),\n                validate.parse_json(),\n                {\n                    \"idAsset\": validate.any(int, validate.all(str, validate.transform(int))),\n                },\n                validate.get(\"idAsset\"),\n            ),\n        ))\n        if not self.id:\n            return\n\n        # check obfuscated stream URLs via self.URL_VIDEOS and ZTNR.translate() first\n        # self.URL_M3U8 appears to be valid for all streams, but doesn't provide any content in same cases\n        urls = self.session.http.get(\n            self.URL_VIDEOS.format(id=self.id),\n            schema=validate.Schema(\n                validate.transform(ZTNR.translate),\n                validate.transform(list),\n                [(str, validate.url())],\n            ),\n        )\n\n        # then fall back to self.URL_M3U8\n        if not urls:\n            url = self.URL_M3U8.format(id=self.id)\n        else:\n            url = next((url for _, url in urls if urlparse(url).path.endswith(\".m3u8\")), None)\n            if not url:\n                url = next((url for _, url in urls if urlparse(url).path.endswith(\".mp4\")), None)\n                if url:\n                    yield \"vod\", HTTPStream(self.session, url)\n                return\n\n        streams = HLSStream.parse_variant_playlist(self.session, url).items()\n\n        if self.options.get(\"mux-subtitles\"):\n            subs = self.session.http.get(\n                self.URL_SUBTITLES.format(id=self.id),\n                schema=validate.Schema(\n                    validate.parse_json(),\n                    {\n                        \"page\": {\n                            \"items\": [{\n                                \"lang\": str,\n                                \"src\": validate.url(),\n                            }],\n                        },\n                    },\n                    validate.get((\"page\", \"items\")),\n                ),\n            )\n            if subs:\n                subtitles = {\n                    s[\"lang\"]: HTTPStream(self.session, update_scheme(\"https://\", s[\"src\"], force=True))\n                    for s in subs\n                }\n                for quality, stream in streams:\n                    yield quality, MuxedStream(self.session, stream, subtitles=subtitles)\n                return\n\n        yield from streams\n\n\n__plugin__ = Rtve\n", "path": "src/streamlink/plugins/rtve.py"}]}