We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
plugins.svtplay: Cannot resolve playable stream
### 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

When trying to play for example https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD

It does not seem to parse the information correctly anymore to resolve a playable stream. This plugin compared to the one for tv4play is parsing the HTML page in order to resolve where to go if you are on the program page instead of the episode page.

This lookup resolves to nothing anymore:

```
latest_episode_url_re = re.compile(r'''
        class="play_titlepage__latest-video"\s+href="(?P<url>[^"]+)"
    ''', re.VERBOSE)
```

When debugging I find that this https://api.svt.se/video/KA2BmZD resolves the JSON.

```
{
  "svtId": "KA2BmZD",
  "programVersionId": "1400537-002A",
  "contentDuration": 3506,
  "blockedForChildren": false,
  "live": false,
  "programTitle": "Draknästet",
  "episodeTitle": "Avsnitt 2",
   ...
  }
```

With the following changes it resolves the video_id correctly from the HTML:
```
latest_episode_vod_id = re.compile(r'''
        data-rt="top-area-play-button"\s+href=".*(?:id=)(?P<video_id>[^"]+)"
    ''', re.VERBOSE)
```

If you are directly on the play page of the site you get the vod_id in the URL as a parameter. So I have refactored to support both.

Now it finds the vod_id and initiates the dash stream worker but it still doesn't run.

I get Exception in thread Thread-DASHStreamWorker and this one seems a little more tricky to figure out.

```
[cli][info] Opening stream: 1080p (dash)
[stream.dash][debug] Opening DASH reader for: 0 (video/mp4)
[stream.dash][debug] Opening DASH reader for: 5 (audio/mp4)
[stream.dash_manifest][debug] Generating segment timeline for static playlist (id=0))
[stream.dash_manifest][debug] Generating segment timeline for static playlist (id=5))
[cli][error] Try 1/1: Could not open stream <Stream()> (Could not open stream: cannot use FFMPEG)
[stream.ffmpegmux][debug] Closing ffmpeg thread
error: Could not open stream <Stream()>, tried 1 times, exiting
[stream.dash][debug] Download of segment: https://svt-vod-8r.akamaized.net/d0/se/20210723/841d135d-fb92-4acb-9dff-898c1db4af30/cmaf-video-avc-1920x1080p25-3089/cmaf-video-avc-1920x1080p25-3089-init.mp4 complete
Exception in thread Thread-DASHStreamWorker:
Traceback (most recent call last):
  File "/usr/lib64/python3.9/threading.py", line 973, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.9/site-packages/streamlink-2.1.2+79.g2b9ca5d.dirty-py3.9.egg/streamlink/stream/segmented.py", line 87, in run
    self.writer.put(segment)
  File "/usr/local/lib/python3.9/site-packages/streamlink-2.1.2+79.g2b9ca5d.dirty-py3.9.egg/streamlink/stream/segmented.py", line 140, in put
    future = self.executor.submit(self.fetch, segment, retries=self.retries)
  File "/usr/lib64/python3.9/concurrent/futures/thread.py", line 163, in submit
    raise RuntimeError('cannot schedule new futures after '
RuntimeError: cannot schedule new futures after interpreter shutdown
```


### Debug log

```text
[cli][debug] OS:         Linux-5.13.12-200.fc34.x86_64-x86_64-with-glibc2.33
[cli][debug] Python:     3.9.6
[cli][debug] Streamlink: 2.3.0
[cli][debug] Requests(2.25.1), Socks(1.7.1), Websocket(0.57.0)
[cli][debug] Arguments:
[cli][debug]  url=https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD
[cli][debug]  stream=['best']
[cli][debug]  --loglevel=debug
[cli][debug]  --player=mpv
[cli][debug]  --verbose-player=True
[cli][debug]  --player-passthrough=['hls']
[cli][debug]  --hls-segment-threads=2
[cli][info] Found matching plugin svtplay for URL https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD
[plugins.svtplay][debug] Path=/
error: No playable streams found on this URL: https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD
```


--- 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/svtplay.py`
Content:
```
1 import logging
2 import re
3 from urllib.parse import urljoin
4 
5 from streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher
6 from streamlink.plugin.api import validate
7 from streamlink.stream import DASHStream, HTTPStream
8 from streamlink.stream.ffmpegmux import MuxedStream
9 
10 log = logging.getLogger(__name__)
11 
12 
13 @pluginmatcher(re.compile(
14     r'https?://(?:www\.)?(?:svtplay|oppetarkiv)\.se(/(kanaler/)?)'
15 ))
16 class SVTPlay(Plugin):
17     api_url = 'https://api.svt.se/videoplayer-api/video/{0}'
18 
19     author = None
20     category = None
21     title = None
22 
23     latest_episode_url_re = re.compile(r'''
24         class="play_titlepage__latest-video"\s+href="(?P<url>[^"]+)"
25     ''', re.VERBOSE)
26 
27     live_id_re = re.compile(r'.*/(?P<live_id>[^?]+)')
28 
29     vod_id_re = re.compile(r'''
30         (?:DATA_LAKE\s+=\s+{"content":{"id":|"svtId":|data-video-id=)
31         "(?P<vod_id>[^"]+)"
32     ''', re.VERBOSE)
33 
34     _video_schema = validate.Schema({
35         validate.optional('programTitle'): validate.text,
36         validate.optional('episodeTitle'): validate.text,
37         'videoReferences': [{
38             'url': validate.url(),
39             'format': validate.text,
40         }],
41         validate.optional('subtitleReferences'): [{
42             'url': validate.url(),
43             'format': validate.text,
44         }],
45     })
46 
47     arguments = PluginArguments(
48         PluginArgument("mux-subtitles", is_global=True)
49     )
50 
51     def get_author(self):
52         if self.author is not None:
53             return self.author
54 
55     def get_category(self):
56         if self.category is not None:
57             return self.category
58 
59     def get_title(self):
60         if self.title is not None:
61             return self.title
62 
63     def _set_metadata(self, data, category):
64         if 'programTitle' in data:
65             self.author = data['programTitle']
66 
67         self.category = category
68 
69         if 'episodeTitle' in data:
70             self.title = data['episodeTitle']
71 
72     def _get_live(self, path):
73         match = self.live_id_re.search(path)
74         if match is None:
75             return
76 
77         live_id = "ch-{0}".format(match.group('live_id'))
78         log.debug("Live ID={0}".format(live_id))
79 
80         res = self.session.http.get(self.api_url.format(live_id))
81         api_data = self.session.http.json(res, schema=self._video_schema)
82 
83         self._set_metadata(api_data, 'Live')
84 
85         for playlist in api_data['videoReferences']:
86             if playlist['format'] == 'dashhbbtv':
87                 yield from DASHStream.parse_manifest(self.session, playlist['url']).items()
88 
89     def _get_vod(self):
90         res = self.session.http.get(self.url)
91         match = self.latest_episode_url_re.search(res.text)
92         if match:
93             res = self.session.http.get(
94                 urljoin(self.url, match.group('url')),
95             )
96 
97         match = self.vod_id_re.search(res.text)
98         if match is None:
99             return
100 
101         vod_id = match.group('vod_id')
102         log.debug("VOD ID={0}".format(vod_id))
103 
104         res = self.session.http.get(self.api_url.format(vod_id))
105         api_data = self.session.http.json(res, schema=self._video_schema)
106 
107         self._set_metadata(api_data, 'VOD')
108 
109         substreams = {}
110         if 'subtitleReferences' in api_data:
111             for subtitle in api_data['subtitleReferences']:
112                 if subtitle['format'] == 'webvtt':
113                     log.debug("Subtitle={0}".format(subtitle['url']))
114                     substreams[subtitle['format']] = HTTPStream(
115                         self.session,
116                         subtitle['url'],
117                     )
118 
119         for manifest in api_data['videoReferences']:
120             if manifest['format'] == 'dashhbbtv':
121                 for q, s in DASHStream.parse_manifest(self.session, manifest['url']).items():
122                     if self.get_option('mux_subtitles') and substreams:
123                         yield q, MuxedStream(self.session, s, subtitles=substreams)
124                     else:
125                         yield q, s
126 
127     def _get_streams(self):
128         path, live = self.match.groups()
129         log.debug("Path={0}".format(path))
130 
131         if live:
132             return self._get_live(path)
133         else:
134             return self._get_vod()
135 
136 
137 __plugin__ = SVTPlay
138
```
--- 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/svtplay.py b/src/streamlink/plugins/svtplay.py
--- a/src/streamlink/plugins/svtplay.py
+++ b/src/streamlink/plugins/svtplay.py
@@ -1,6 +1,6 @@
 import logging
 import re
-from urllib.parse import urljoin
+from urllib.parse import parse_qsl, urlparse
 
 from streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher
 from streamlink.plugin.api import validate
@@ -21,16 +21,11 @@
     title = None
 
     latest_episode_url_re = re.compile(r'''
-        class="play_titlepage__latest-video"\s+href="(?P<url>[^"]+)"
+        data-rt="top-area-play-button"\s+href="(?P<url>[^"]+)"
     ''', re.VERBOSE)
 
     live_id_re = re.compile(r'.*/(?P<live_id>[^?]+)')
 
-    vod_id_re = re.compile(r'''
-        (?:DATA_LAKE\s+=\s+{"content":{"id":|"svtId":|data-video-id=)
-        "(?P<vod_id>[^"]+)"
-    ''', re.VERBOSE)
-
     _video_schema = validate.Schema({
         validate.optional('programTitle'): validate.text,
         validate.optional('episodeTitle'): validate.text,
@@ -87,18 +82,18 @@
                 yield from DASHStream.parse_manifest(self.session, playlist['url']).items()
 
     def _get_vod(self):
-        res = self.session.http.get(self.url)
-        match = self.latest_episode_url_re.search(res.text)
-        if match:
-            res = self.session.http.get(
-                urljoin(self.url, match.group('url')),
-            )
-
-        match = self.vod_id_re.search(res.text)
-        if match is None:
+        vod_id = self._get_vod_id(self.url)
+
+        if vod_id is None:
+            res = self.session.http.get(self.url)
+            match = self.latest_episode_url_re.search(res.text)
+            if match is None:
+                return
+            vod_id = self._get_vod_id(match.group("url"))
+
+        if vod_id is None:
             return
 
-        vod_id = match.group('vod_id')
         log.debug("VOD ID={0}".format(vod_id))
 
         res = self.session.http.get(self.api_url.format(vod_id))
@@ -124,6 +119,10 @@
                     else:
                         yield q, s
 
+    def _get_vod_id(self, url):
+        qs = dict(parse_qsl(urlparse(url).query))
+        return qs.get("id")
+
     def _get_streams(self):
         path, live = self.match.groups()
         log.debug("Path={0}".format(path))

{"golden_diff": "diff --git a/src/streamlink/plugins/svtplay.py b/src/streamlink/plugins/svtplay.py\n--- a/src/streamlink/plugins/svtplay.py\n+++ b/src/streamlink/plugins/svtplay.py\n@@ -1,6 +1,6 @@\n import logging\n import re\n-from urllib.parse import urljoin\n+from urllib.parse import parse_qsl, urlparse\n \n from streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher\n from streamlink.plugin.api import validate\n@@ -21,16 +21,11 @@\n     title = None\n \n     latest_episode_url_re = re.compile(r'''\n-        class=\"play_titlepage__latest-video\"\\s+href=\"(?P<url>[^\"]+)\"\n+        data-rt=\"top-area-play-button\"\\s+href=\"(?P<url>[^\"]+)\"\n     ''', re.VERBOSE)\n \n     live_id_re = re.compile(r'.*/(?P<live_id>[^?]+)')\n \n-    vod_id_re = re.compile(r'''\n-        (?:DATA_LAKE\\s+=\\s+{\"content\":{\"id\":|\"svtId\":|data-video-id=)\n-        \"(?P<vod_id>[^\"]+)\"\n-    ''', re.VERBOSE)\n-\n     _video_schema = validate.Schema({\n         validate.optional('programTitle'): validate.text,\n         validate.optional('episodeTitle'): validate.text,\n@@ -87,18 +82,18 @@\n                 yield from DASHStream.parse_manifest(self.session, playlist['url']).items()\n \n     def _get_vod(self):\n-        res = self.session.http.get(self.url)\n-        match = self.latest_episode_url_re.search(res.text)\n-        if match:\n-            res = self.session.http.get(\n-                urljoin(self.url, match.group('url')),\n-            )\n-\n-        match = self.vod_id_re.search(res.text)\n-        if match is None:\n+        vod_id = self._get_vod_id(self.url)\n+\n+        if vod_id is None:\n+            res = self.session.http.get(self.url)\n+            match = self.latest_episode_url_re.search(res.text)\n+            if match is None:\n+                return\n+            vod_id = self._get_vod_id(match.group(\"url\"))\n+\n+        if vod_id is None:\n             return\n \n-        vod_id = match.group('vod_id')\n         log.debug(\"VOD ID={0}\".format(vod_id))\n \n         res = self.session.http.get(self.api_url.format(vod_id))\n@@ -124,6 +119,10 @@\n                     else:\n                         yield q, s\n \n+    def _get_vod_id(self, url):\n+        qs = dict(parse_qsl(urlparse(url).query))\n+        return qs.get(\"id\")\n+\n     def _get_streams(self):\n         path, live = self.match.groups()\n         log.debug(\"Path={0}\".format(path))\n", "issue": "plugins.svtplay: Cannot resolve playable stream\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\nWhen trying to play for example https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD\r\n\r\nIt does not seem to parse the information correctly anymore to resolve a playable stream. This plugin compared to the one for tv4play is parsing the HTML page in order to resolve where to go if you are on the program page instead of the episode page.\r\n\r\nThis lookup resolves to nothing anymore:\r\n\r\n```\r\nlatest_episode_url_re = re.compile(r'''\r\n        class=\"play_titlepage__latest-video\"\\s+href=\"(?P<url>[^\"]+)\"\r\n    ''', re.VERBOSE)\r\n```\r\n\r\nWhen debugging I find that this https://api.svt.se/video/KA2BmZD resolves the JSON.\r\n\r\n```\r\n{\r\n  \"svtId\": \"KA2BmZD\",\r\n  \"programVersionId\": \"1400537-002A\",\r\n  \"contentDuration\": 3506,\r\n  \"blockedForChildren\": false,\r\n  \"live\": false,\r\n  \"programTitle\": \"Drakn\u00e4stet\",\r\n  \"episodeTitle\": \"Avsnitt 2\",\r\n   ...\r\n  }\r\n```\r\n\r\nWith the following changes it resolves the video_id correctly from the HTML:\r\n```\r\nlatest_episode_vod_id = re.compile(r'''\r\n        data-rt=\"top-area-play-button\"\\s+href=\".*(?:id=)(?P<video_id>[^\"]+)\"\r\n    ''', re.VERBOSE)\r\n```\r\n\r\nIf you are directly on the play page of the site you get the vod_id in the URL as a parameter. So I have refactored to support both.\r\n\r\nNow it finds the vod_id and initiates the dash stream worker but it still doesn't run.\r\n\r\nI get Exception in thread Thread-DASHStreamWorker and this one seems a little more tricky to figure out.\r\n\r\n```\r\n[cli][info] Opening stream: 1080p (dash)\r\n[stream.dash][debug] Opening DASH reader for: 0 (video/mp4)\r\n[stream.dash][debug] Opening DASH reader for: 5 (audio/mp4)\r\n[stream.dash_manifest][debug] Generating segment timeline for static playlist (id=0))\r\n[stream.dash_manifest][debug] Generating segment timeline for static playlist (id=5))\r\n[cli][error] Try 1/1: Could not open stream <Stream()> (Could not open stream: cannot use FFMPEG)\r\n[stream.ffmpegmux][debug] Closing ffmpeg thread\r\nerror: Could not open stream <Stream()>, tried 1 times, exiting\r\n[stream.dash][debug] Download of segment: https://svt-vod-8r.akamaized.net/d0/se/20210723/841d135d-fb92-4acb-9dff-898c1db4af30/cmaf-video-avc-1920x1080p25-3089/cmaf-video-avc-1920x1080p25-3089-init.mp4 complete\r\nException in thread Thread-DASHStreamWorker:\r\nTraceback (most recent call last):\r\n  File \"/usr/lib64/python3.9/threading.py\", line 973, in _bootstrap_inner\r\n    self.run()\r\n  File \"/usr/local/lib/python3.9/site-packages/streamlink-2.1.2+79.g2b9ca5d.dirty-py3.9.egg/streamlink/stream/segmented.py\", line 87, in run\r\n    self.writer.put(segment)\r\n  File \"/usr/local/lib/python3.9/site-packages/streamlink-2.1.2+79.g2b9ca5d.dirty-py3.9.egg/streamlink/stream/segmented.py\", line 140, in put\r\n    future = self.executor.submit(self.fetch, segment, retries=self.retries)\r\n  File \"/usr/lib64/python3.9/concurrent/futures/thread.py\", line 163, in submit\r\n    raise RuntimeError('cannot schedule new futures after '\r\nRuntimeError: cannot schedule new futures after interpreter shutdown\r\n```\r\n\n\n### Debug log\n\n```text\n[cli][debug] OS:         Linux-5.13.12-200.fc34.x86_64-x86_64-with-glibc2.33\r\n[cli][debug] Python:     3.9.6\r\n[cli][debug] Streamlink: 2.3.0\r\n[cli][debug] Requests(2.25.1), Socks(1.7.1), Websocket(0.57.0)\r\n[cli][debug] Arguments:\r\n[cli][debug]  url=https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD\r\n[cli][debug]  stream=['best']\r\n[cli][debug]  --loglevel=debug\r\n[cli][debug]  --player=mpv\r\n[cli][debug]  --verbose-player=True\r\n[cli][debug]  --player-passthrough=['hls']\r\n[cli][debug]  --hls-segment-threads=2\r\n[cli][info] Found matching plugin svtplay for URL https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD\r\n[plugins.svtplay][debug] Path=/\r\nerror: No playable streams found on this URL: https://www.svtplay.se/video/32279075/draknastet/draknastet-sasong-1-avsnitt-2?id=KA2BmZD\n```\n\n", "before_files": [{"content": "import logging\nimport re\nfrom urllib.parse import urljoin\n\nfrom streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream import DASHStream, HTTPStream\nfrom streamlink.stream.ffmpegmux import MuxedStream\n\nlog = logging.getLogger(__name__)\n\n\n@pluginmatcher(re.compile(\n    r'https?://(?:www\\.)?(?:svtplay|oppetarkiv)\\.se(/(kanaler/)?)'\n))\nclass SVTPlay(Plugin):\n    api_url = 'https://api.svt.se/videoplayer-api/video/{0}'\n\n    author = None\n    category = None\n    title = None\n\n    latest_episode_url_re = re.compile(r'''\n        class=\"play_titlepage__latest-video\"\\s+href=\"(?P<url>[^\"]+)\"\n    ''', re.VERBOSE)\n\n    live_id_re = re.compile(r'.*/(?P<live_id>[^?]+)')\n\n    vod_id_re = re.compile(r'''\n        (?:DATA_LAKE\\s+=\\s+{\"content\":{\"id\":|\"svtId\":|data-video-id=)\n        \"(?P<vod_id>[^\"]+)\"\n    ''', re.VERBOSE)\n\n    _video_schema = validate.Schema({\n        validate.optional('programTitle'): validate.text,\n        validate.optional('episodeTitle'): validate.text,\n        'videoReferences': [{\n            'url': validate.url(),\n            'format': validate.text,\n        }],\n        validate.optional('subtitleReferences'): [{\n            'url': validate.url(),\n            'format': validate.text,\n        }],\n    })\n\n    arguments = PluginArguments(\n        PluginArgument(\"mux-subtitles\", is_global=True)\n    )\n\n    def get_author(self):\n        if self.author is not None:\n            return self.author\n\n    def get_category(self):\n        if self.category is not None:\n            return self.category\n\n    def get_title(self):\n        if self.title is not None:\n            return self.title\n\n    def _set_metadata(self, data, category):\n        if 'programTitle' in data:\n            self.author = data['programTitle']\n\n        self.category = category\n\n        if 'episodeTitle' in data:\n            self.title = data['episodeTitle']\n\n    def _get_live(self, path):\n        match = self.live_id_re.search(path)\n        if match is None:\n            return\n\n        live_id = \"ch-{0}\".format(match.group('live_id'))\n        log.debug(\"Live ID={0}\".format(live_id))\n\n        res = self.session.http.get(self.api_url.format(live_id))\n        api_data = self.session.http.json(res, schema=self._video_schema)\n\n        self._set_metadata(api_data, 'Live')\n\n        for playlist in api_data['videoReferences']:\n            if playlist['format'] == 'dashhbbtv':\n                yield from DASHStream.parse_manifest(self.session, playlist['url']).items()\n\n    def _get_vod(self):\n        res = self.session.http.get(self.url)\n        match = self.latest_episode_url_re.search(res.text)\n        if match:\n            res = self.session.http.get(\n                urljoin(self.url, match.group('url')),\n            )\n\n        match = self.vod_id_re.search(res.text)\n        if match is None:\n            return\n\n        vod_id = match.group('vod_id')\n        log.debug(\"VOD ID={0}\".format(vod_id))\n\n        res = self.session.http.get(self.api_url.format(vod_id))\n        api_data = self.session.http.json(res, schema=self._video_schema)\n\n        self._set_metadata(api_data, 'VOD')\n\n        substreams = {}\n        if 'subtitleReferences' in api_data:\n            for subtitle in api_data['subtitleReferences']:\n                if subtitle['format'] == 'webvtt':\n                    log.debug(\"Subtitle={0}\".format(subtitle['url']))\n                    substreams[subtitle['format']] = HTTPStream(\n                        self.session,\n                        subtitle['url'],\n                    )\n\n        for manifest in api_data['videoReferences']:\n            if manifest['format'] == 'dashhbbtv':\n                for q, s in DASHStream.parse_manifest(self.session, manifest['url']).items():\n                    if self.get_option('mux_subtitles') and substreams:\n                        yield q, MuxedStream(self.session, s, subtitles=substreams)\n                    else:\n                        yield q, s\n\n    def _get_streams(self):\n        path, live = self.match.groups()\n        log.debug(\"Path={0}\".format(path))\n\n        if live:\n            return self._get_live(path)\n        else:\n            return self._get_vod()\n\n\n__plugin__ = SVTPlay\n", "path": "src/streamlink/plugins/svtplay.py"}], "after_files": [{"content": "import logging\nimport re\nfrom urllib.parse import parse_qsl, urlparse\n\nfrom streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream import DASHStream, HTTPStream\nfrom streamlink.stream.ffmpegmux import MuxedStream\n\nlog = logging.getLogger(__name__)\n\n\n@pluginmatcher(re.compile(\n    r'https?://(?:www\\.)?(?:svtplay|oppetarkiv)\\.se(/(kanaler/)?)'\n))\nclass SVTPlay(Plugin):\n    api_url = 'https://api.svt.se/videoplayer-api/video/{0}'\n\n    author = None\n    category = None\n    title = None\n\n    latest_episode_url_re = re.compile(r'''\n        data-rt=\"top-area-play-button\"\\s+href=\"(?P<url>[^\"]+)\"\n    ''', re.VERBOSE)\n\n    live_id_re = re.compile(r'.*/(?P<live_id>[^?]+)')\n\n    _video_schema = validate.Schema({\n        validate.optional('programTitle'): validate.text,\n        validate.optional('episodeTitle'): validate.text,\n        'videoReferences': [{\n            'url': validate.url(),\n            'format': validate.text,\n        }],\n        validate.optional('subtitleReferences'): [{\n            'url': validate.url(),\n            'format': validate.text,\n        }],\n    })\n\n    arguments = PluginArguments(\n        PluginArgument(\"mux-subtitles\", is_global=True)\n    )\n\n    def get_author(self):\n        if self.author is not None:\n            return self.author\n\n    def get_category(self):\n        if self.category is not None:\n            return self.category\n\n    def get_title(self):\n        if self.title is not None:\n            return self.title\n\n    def _set_metadata(self, data, category):\n        if 'programTitle' in data:\n            self.author = data['programTitle']\n\n        self.category = category\n\n        if 'episodeTitle' in data:\n            self.title = data['episodeTitle']\n\n    def _get_live(self, path):\n        match = self.live_id_re.search(path)\n        if match is None:\n            return\n\n        live_id = \"ch-{0}\".format(match.group('live_id'))\n        log.debug(\"Live ID={0}\".format(live_id))\n\n        res = self.session.http.get(self.api_url.format(live_id))\n        api_data = self.session.http.json(res, schema=self._video_schema)\n\n        self._set_metadata(api_data, 'Live')\n\n        for playlist in api_data['videoReferences']:\n            if playlist['format'] == 'dashhbbtv':\n                yield from DASHStream.parse_manifest(self.session, playlist['url']).items()\n\n    def _get_vod(self):\n        vod_id = self._get_vod_id(self.url)\n\n        if vod_id is None:\n            res = self.session.http.get(self.url)\n            match = self.latest_episode_url_re.search(res.text)\n            if match is None:\n                return\n            vod_id = self._get_vod_id(match.group(\"url\"))\n\n        if vod_id is None:\n            return\n\n        log.debug(\"VOD ID={0}\".format(vod_id))\n\n        res = self.session.http.get(self.api_url.format(vod_id))\n        api_data = self.session.http.json(res, schema=self._video_schema)\n\n        self._set_metadata(api_data, 'VOD')\n\n        substreams = {}\n        if 'subtitleReferences' in api_data:\n            for subtitle in api_data['subtitleReferences']:\n                if subtitle['format'] == 'webvtt':\n                    log.debug(\"Subtitle={0}\".format(subtitle['url']))\n                    substreams[subtitle['format']] = HTTPStream(\n                        self.session,\n                        subtitle['url'],\n                    )\n\n        for manifest in api_data['videoReferences']:\n            if manifest['format'] == 'dashhbbtv':\n                for q, s in DASHStream.parse_manifest(self.session, manifest['url']).items():\n                    if self.get_option('mux_subtitles') and substreams:\n                        yield q, MuxedStream(self.session, s, subtitles=substreams)\n                    else:\n                        yield q, s\n\n    def _get_vod_id(self, url):\n        qs = dict(parse_qsl(urlparse(url).query))\n        return qs.get(\"id\")\n\n    def _get_streams(self):\n        path, live = self.match.groups()\n        log.debug(\"Path={0}\".format(path))\n\n        if live:\n            return self._get_live(path)\n        else:\n            return self._get_vod()\n\n\n__plugin__ = SVTPlay\n", "path": "src/streamlink/plugins/svtplay.py"}]}