We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ITV Player unable to open URL
- [x] This is a plugin issue and I have read the contribution guidelines.


### Description

ITV Player/Hub for the main ITV channel do not open and give an error message.


### Reproduction steps / Explicit stream URLs to test

Type "streamlink itv.com/hub/itv".

Other ITV Hub URL's such as hub/itv2 or hub/citv continue to work.

### Log output

[cli][info] Found matching plugin itvplayer for URL itv.com/hub/itv
error: Unable to open URL: http://cdn.dar.itv.com/private/aHR0cDovL2NzbS1lLnBsYXkuZGFyLml0di5jb20vY3NtL2V4dGxpdmUvMjQ3MTYyMTUubTN1OD95by5hYz10cnVlJnlvLmFkPWZhbHNlJnBtPWZyZWUmcmFuZG9tPTU2OTk0ODU2ODc0ODA1NzE0MjAmYXJlYT1pdHZwbGF5ZXIuc2ltdWxjYXN0JnNvdXJjZT1zaW11bGNhc3QmaGRldmlkPXgmc2l6ZT1wYXR0ZXJuJnN1cGVydGFnPWRmbHQsc2ltJnVzPWFubyZzaXRlPWl0diZvc3Zlcj0xMCZjaGFuYnJhbmQ9aXR2MSZvcz13aW5kb3dzJmhtb2Q9NjYmcHY9YnJvd3Nlci40LjEmcGxheWVyPWh0bWw1LmRlc2t0b3AmZG09bmxpJmhtYW49Y2hyb21lJnRkdXI9MjE2MDAmdHBhcnRzPTEmYXJwPXgmZGVmYXVsdD1EZWZhdWx0UGF0dGVybiZwbGlzdD1zaW1hZHJlcGxhY2VtZW50JnBsZmNpZD1zaW1hZHJlcGxhY2VtZW50/endpoint?hdnea=st=1563543051~exp=1563564651~acl=/*~data=nohubplus~hmac=0ebaf6d4f22f101378a6c7ed3903c1999f6b28efd29ee05763e5ec7bf0d27896&yo.channel=24716215 (404 Client Error:  for url: http://csm-e.play.dar.itv.com/csm/extlive/24716215.m3u8?yo.ac=true&yo.ad=false&pm=free&random=5699485687480571420&area=itvplayer.simulcast&source=simulcast&hdevid=x&size=pattern&supertag=dflt,sim&us=ano&site=itv&osver=10&chanbrand=itv1&os=windows&hmod=66&pv=browser.4.1&player=html5.desktop&dm=nli&hman=chrome&tdur=21600&tparts=1&arp=x&default=DefaultPattern&plist=simadreplacement&plfcid=simadreplacement)


### Additional comments, screenshots, etc.

I have noticed the site plays a ITV Hub promo when starting a livestream on the website itself before loading that stream. This does not occur for the other channels.

--- 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/itvplayer.py`
Content:
```
1 import json
2 import logging
3 import re
4 
5 from streamlink.compat import urljoin
6 from streamlink.plugin import Plugin
7 from streamlink.plugin.api import useragents, validate
8 from streamlink.plugin.api.utils import itertags
9 from streamlink.stream import HLSStream
10 
11 log = logging.getLogger(__name__)
12 
13 
14 class ITVPlayer(Plugin):
15     _url_re = re.compile(r"https?://(?:www.)?itv.com/hub/(?P<stream>.+)")
16     _video_info_schema = validate.Schema({
17         "StatusCode": 200,
18         "AdditionalInfo": {
19             "Message": validate.any(None, validate.text)
20         },
21         "Playlist": {
22             "VideoType": validate.text,
23             "Video": {
24                 "Subtitles": validate.any(None, [{
25                     "Href": validate.url(),
26                 }]),
27                 "Base": validate.url(),
28                 "MediaFiles": [
29                     {"Href": validate.text,
30                      "KeyServiceUrl": validate.any(None, validate.url())}
31                 ]
32             }
33         }
34     })
35 
36     @classmethod
37     def can_handle_url(cls, url):
38         match = cls._url_re.match(url)
39         return match is not None
40 
41     @property
42     def device_info(self):
43         return {"user": {},
44                 "device": {"manufacturer": "Chrome", "model": "66",
45                            "os": {"name": "Windows", "version": "10", "type": "desktop"}},
46                 "client": {"version": "4.1", "id": "browser"},
47                 "variantAvailability": {"featureset": {"min": ["hls", "aes"],
48                                                        "max": ["hls", "aes"]},
49                                         "platformTag": "dotcom"}}
50 
51     def video_info(self):
52         page = self.session.http.get(self.url)
53         for div in itertags(page.text, 'div'):
54             if div.attributes.get("id") == "video":
55                 return div.attributes
56 
57     def _get_streams(self):
58         """
59             Find all the streams for the ITV url
60             :return: Mapping of quality to stream
61         """
62         self.session.http.headers.update({"User-Agent": useragents.FIREFOX})
63         video_info = self.video_info()
64         video_info_url = video_info.get("data-html5-playlist") or video_info.get("data-video-id")
65 
66         res = self.session.http.post(video_info_url,
67                         data=json.dumps(self.device_info),
68                         headers={"hmac": video_info.get("data-video-hmac")})
69         data = self.session.http.json(res, schema=self._video_info_schema)
70 
71         log.debug("Video ID info response: {0}".format(data))
72 
73         stype = data['Playlist']['VideoType']
74 
75         for media in data['Playlist']['Video']['MediaFiles']:
76             url = urljoin(data['Playlist']['Video']['Base'], media['Href'])
77             name_fmt = "{pixels}_{bitrate}" if stype == "CATCHUP" else None
78             for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items():
79                 yield s
80 
81 
82 
83 __plugin__ = ITVPlayer
84
```
--- 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/itvplayer.py b/src/streamlink/plugins/itvplayer.py
--- a/src/streamlink/plugins/itvplayer.py
+++ b/src/streamlink/plugins/itvplayer.py
@@ -6,13 +6,14 @@
 from streamlink.plugin import Plugin
 from streamlink.plugin.api import useragents, validate
 from streamlink.plugin.api.utils import itertags
-from streamlink.stream import HLSStream
+from streamlink.stream import HLSStream, RTMPStream
 
 log = logging.getLogger(__name__)
 
 
 class ITVPlayer(Plugin):
     _url_re = re.compile(r"https?://(?:www.)?itv.com/hub/(?P<stream>.+)")
+    swf_url = "https://mediaplayer.itv.com/2.19.5%2Bbuild.a23aa62b1e/ITVMediaPlayer.swf"
     _video_info_schema = validate.Schema({
         "StatusCode": 200,
         "AdditionalInfo": {
@@ -35,8 +36,7 @@
 
     @classmethod
     def can_handle_url(cls, url):
-        match = cls._url_re.match(url)
-        return match is not None
+        return cls._url_re.match(url) is not None
 
     @property
     def device_info(self):
@@ -54,18 +54,11 @@
             if div.attributes.get("id") == "video":
                 return div.attributes
 
-    def _get_streams(self):
-        """
-            Find all the streams for the ITV url
-            :return: Mapping of quality to stream
-        """
-        self.session.http.headers.update({"User-Agent": useragents.FIREFOX})
+    def _get_html5_streams(self, video_info_url):
         video_info = self.video_info()
-        video_info_url = video_info.get("data-html5-playlist") or video_info.get("data-video-id")
-
         res = self.session.http.post(video_info_url,
-                        data=json.dumps(self.device_info),
-                        headers={"hmac": video_info.get("data-video-hmac")})
+                                     data=json.dumps(self.device_info),
+                                     headers={"hmac": video_info.get("data-video-hmac")})
         data = self.session.http.json(res, schema=self._video_info_schema)
 
         log.debug("Video ID info response: {0}".format(data))
@@ -78,6 +71,31 @@
             for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items():
                 yield s
 
+    def _get_rtmp_streams(self, video_info_url):
+        log.debug("XML data path: {0}".format(video_info_url))
+        res = self.session.http.get(video_info_url)
+        playlist = self.session.http.xml(res, ignore_ns=True)
+        mediafiles = playlist.find(".//Playlist/VideoEntries/Video/MediaFiles")
+        playpath = mediafiles.find("./MediaFile/URL")
+        return {"live": RTMPStream(self.session, {"rtmp": mediafiles.attrib.get("base"),
+                                                  "playpath": playpath.text,
+                                                  "live": True,
+                                                  "swfVfy": self.swf_url
+                                                  })}
+
+    def _get_streams(self):
+        """
+            Find all the streams for the ITV url
+            :return: Mapping of quality to stream
+        """
+        self.session.http.headers.update({"User-Agent": useragents.FIREFOX})
+        stream = self._url_re.match(self.url).group("stream")
+        video_info = self.video_info()
+        video_info_url = video_info.get("data-video-id" if stream.lower() in ("itv", "itv4") else "data-html5-playlist")
+        if video_info_url.endswith(".xml"):
+            return self._get_rtmp_streams(video_info_url)
+        else:
+            return self._get_html5_streams(video_info_url)
 
 
 __plugin__ = ITVPlayer

{"golden_diff": "diff --git a/src/streamlink/plugins/itvplayer.py b/src/streamlink/plugins/itvplayer.py\n--- a/src/streamlink/plugins/itvplayer.py\n+++ b/src/streamlink/plugins/itvplayer.py\n@@ -6,13 +6,14 @@\n from streamlink.plugin import Plugin\n from streamlink.plugin.api import useragents, validate\n from streamlink.plugin.api.utils import itertags\n-from streamlink.stream import HLSStream\n+from streamlink.stream import HLSStream, RTMPStream\n \n log = logging.getLogger(__name__)\n \n \n class ITVPlayer(Plugin):\n     _url_re = re.compile(r\"https?://(?:www.)?itv.com/hub/(?P<stream>.+)\")\n+    swf_url = \"https://mediaplayer.itv.com/2.19.5%2Bbuild.a23aa62b1e/ITVMediaPlayer.swf\"\n     _video_info_schema = validate.Schema({\n         \"StatusCode\": 200,\n         \"AdditionalInfo\": {\n@@ -35,8 +36,7 @@\n \n     @classmethod\n     def can_handle_url(cls, url):\n-        match = cls._url_re.match(url)\n-        return match is not None\n+        return cls._url_re.match(url) is not None\n \n     @property\n     def device_info(self):\n@@ -54,18 +54,11 @@\n             if div.attributes.get(\"id\") == \"video\":\n                 return div.attributes\n \n-    def _get_streams(self):\n-        \"\"\"\n-            Find all the streams for the ITV url\n-            :return: Mapping of quality to stream\n-        \"\"\"\n-        self.session.http.headers.update({\"User-Agent\": useragents.FIREFOX})\n+    def _get_html5_streams(self, video_info_url):\n         video_info = self.video_info()\n-        video_info_url = video_info.get(\"data-html5-playlist\") or video_info.get(\"data-video-id\")\n-\n         res = self.session.http.post(video_info_url,\n-                        data=json.dumps(self.device_info),\n-                        headers={\"hmac\": video_info.get(\"data-video-hmac\")})\n+                                     data=json.dumps(self.device_info),\n+                                     headers={\"hmac\": video_info.get(\"data-video-hmac\")})\n         data = self.session.http.json(res, schema=self._video_info_schema)\n \n         log.debug(\"Video ID info response: {0}\".format(data))\n@@ -78,6 +71,31 @@\n             for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items():\n                 yield s\n \n+    def _get_rtmp_streams(self, video_info_url):\n+        log.debug(\"XML data path: {0}\".format(video_info_url))\n+        res = self.session.http.get(video_info_url)\n+        playlist = self.session.http.xml(res, ignore_ns=True)\n+        mediafiles = playlist.find(\".//Playlist/VideoEntries/Video/MediaFiles\")\n+        playpath = mediafiles.find(\"./MediaFile/URL\")\n+        return {\"live\": RTMPStream(self.session, {\"rtmp\": mediafiles.attrib.get(\"base\"),\n+                                                  \"playpath\": playpath.text,\n+                                                  \"live\": True,\n+                                                  \"swfVfy\": self.swf_url\n+                                                  })}\n+\n+    def _get_streams(self):\n+        \"\"\"\n+            Find all the streams for the ITV url\n+            :return: Mapping of quality to stream\n+        \"\"\"\n+        self.session.http.headers.update({\"User-Agent\": useragents.FIREFOX})\n+        stream = self._url_re.match(self.url).group(\"stream\")\n+        video_info = self.video_info()\n+        video_info_url = video_info.get(\"data-video-id\" if stream.lower() in (\"itv\", \"itv4\") else \"data-html5-playlist\")\n+        if video_info_url.endswith(\".xml\"):\n+            return self._get_rtmp_streams(video_info_url)\n+        else:\n+            return self._get_html5_streams(video_info_url)\n \n \n __plugin__ = ITVPlayer\n", "issue": "ITV Player unable to open URL\n- [x] This is a plugin issue and I have read the contribution guidelines.\r\n\r\n\r\n### Description\r\n\r\nITV Player/Hub for the main ITV channel do not open and give an error message.\r\n\r\n\r\n### Reproduction steps / Explicit stream URLs to test\r\n\r\nType \"streamlink itv.com/hub/itv\".\r\n\r\nOther ITV Hub URL's such as hub/itv2 or hub/citv continue to work.\r\n\r\n### Log output\r\n\r\n[cli][info] Found matching plugin itvplayer for URL itv.com/hub/itv\r\nerror: Unable to open URL: http://cdn.dar.itv.com/private/aHR0cDovL2NzbS1lLnBsYXkuZGFyLml0di5jb20vY3NtL2V4dGxpdmUvMjQ3MTYyMTUubTN1OD95by5hYz10cnVlJnlvLmFkPWZhbHNlJnBtPWZyZWUmcmFuZG9tPTU2OTk0ODU2ODc0ODA1NzE0MjAmYXJlYT1pdHZwbGF5ZXIuc2ltdWxjYXN0JnNvdXJjZT1zaW11bGNhc3QmaGRldmlkPXgmc2l6ZT1wYXR0ZXJuJnN1cGVydGFnPWRmbHQsc2ltJnVzPWFubyZzaXRlPWl0diZvc3Zlcj0xMCZjaGFuYnJhbmQ9aXR2MSZvcz13aW5kb3dzJmhtb2Q9NjYmcHY9YnJvd3Nlci40LjEmcGxheWVyPWh0bWw1LmRlc2t0b3AmZG09bmxpJmhtYW49Y2hyb21lJnRkdXI9MjE2MDAmdHBhcnRzPTEmYXJwPXgmZGVmYXVsdD1EZWZhdWx0UGF0dGVybiZwbGlzdD1zaW1hZHJlcGxhY2VtZW50JnBsZmNpZD1zaW1hZHJlcGxhY2VtZW50/endpoint?hdnea=st=1563543051~exp=1563564651~acl=/*~data=nohubplus~hmac=0ebaf6d4f22f101378a6c7ed3903c1999f6b28efd29ee05763e5ec7bf0d27896&yo.channel=24716215 (404 Client Error:  for url: http://csm-e.play.dar.itv.com/csm/extlive/24716215.m3u8?yo.ac=true&yo.ad=false&pm=free&random=5699485687480571420&area=itvplayer.simulcast&source=simulcast&hdevid=x&size=pattern&supertag=dflt,sim&us=ano&site=itv&osver=10&chanbrand=itv1&os=windows&hmod=66&pv=browser.4.1&player=html5.desktop&dm=nli&hman=chrome&tdur=21600&tparts=1&arp=x&default=DefaultPattern&plist=simadreplacement&plfcid=simadreplacement)\r\n\r\n\r\n### Additional comments, screenshots, etc.\r\n\r\nI have noticed the site plays a ITV Hub promo when starting a livestream on the website itself before loading that stream. This does not occur for the other channels.\n", "before_files": [{"content": "import json\nimport logging\nimport re\n\nfrom streamlink.compat import urljoin\nfrom streamlink.plugin import Plugin\nfrom streamlink.plugin.api import useragents, validate\nfrom streamlink.plugin.api.utils import itertags\nfrom streamlink.stream import HLSStream\n\nlog = logging.getLogger(__name__)\n\n\nclass ITVPlayer(Plugin):\n    _url_re = re.compile(r\"https?://(?:www.)?itv.com/hub/(?P<stream>.+)\")\n    _video_info_schema = validate.Schema({\n        \"StatusCode\": 200,\n        \"AdditionalInfo\": {\n            \"Message\": validate.any(None, validate.text)\n        },\n        \"Playlist\": {\n            \"VideoType\": validate.text,\n            \"Video\": {\n                \"Subtitles\": validate.any(None, [{\n                    \"Href\": validate.url(),\n                }]),\n                \"Base\": validate.url(),\n                \"MediaFiles\": [\n                    {\"Href\": validate.text,\n                     \"KeyServiceUrl\": validate.any(None, validate.url())}\n                ]\n            }\n        }\n    })\n\n    @classmethod\n    def can_handle_url(cls, url):\n        match = cls._url_re.match(url)\n        return match is not None\n\n    @property\n    def device_info(self):\n        return {\"user\": {},\n                \"device\": {\"manufacturer\": \"Chrome\", \"model\": \"66\",\n                           \"os\": {\"name\": \"Windows\", \"version\": \"10\", \"type\": \"desktop\"}},\n                \"client\": {\"version\": \"4.1\", \"id\": \"browser\"},\n                \"variantAvailability\": {\"featureset\": {\"min\": [\"hls\", \"aes\"],\n                                                       \"max\": [\"hls\", \"aes\"]},\n                                        \"platformTag\": \"dotcom\"}}\n\n    def video_info(self):\n        page = self.session.http.get(self.url)\n        for div in itertags(page.text, 'div'):\n            if div.attributes.get(\"id\") == \"video\":\n                return div.attributes\n\n    def _get_streams(self):\n        \"\"\"\n            Find all the streams for the ITV url\n            :return: Mapping of quality to stream\n        \"\"\"\n        self.session.http.headers.update({\"User-Agent\": useragents.FIREFOX})\n        video_info = self.video_info()\n        video_info_url = video_info.get(\"data-html5-playlist\") or video_info.get(\"data-video-id\")\n\n        res = self.session.http.post(video_info_url,\n                        data=json.dumps(self.device_info),\n                        headers={\"hmac\": video_info.get(\"data-video-hmac\")})\n        data = self.session.http.json(res, schema=self._video_info_schema)\n\n        log.debug(\"Video ID info response: {0}\".format(data))\n\n        stype = data['Playlist']['VideoType']\n\n        for media in data['Playlist']['Video']['MediaFiles']:\n            url = urljoin(data['Playlist']['Video']['Base'], media['Href'])\n            name_fmt = \"{pixels}_{bitrate}\" if stype == \"CATCHUP\" else None\n            for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items():\n                yield s\n\n\n\n__plugin__ = ITVPlayer\n", "path": "src/streamlink/plugins/itvplayer.py"}], "after_files": [{"content": "import json\nimport logging\nimport re\n\nfrom streamlink.compat import urljoin\nfrom streamlink.plugin import Plugin\nfrom streamlink.plugin.api import useragents, validate\nfrom streamlink.plugin.api.utils import itertags\nfrom streamlink.stream import HLSStream, RTMPStream\n\nlog = logging.getLogger(__name__)\n\n\nclass ITVPlayer(Plugin):\n    _url_re = re.compile(r\"https?://(?:www.)?itv.com/hub/(?P<stream>.+)\")\n    swf_url = \"https://mediaplayer.itv.com/2.19.5%2Bbuild.a23aa62b1e/ITVMediaPlayer.swf\"\n    _video_info_schema = validate.Schema({\n        \"StatusCode\": 200,\n        \"AdditionalInfo\": {\n            \"Message\": validate.any(None, validate.text)\n        },\n        \"Playlist\": {\n            \"VideoType\": validate.text,\n            \"Video\": {\n                \"Subtitles\": validate.any(None, [{\n                    \"Href\": validate.url(),\n                }]),\n                \"Base\": validate.url(),\n                \"MediaFiles\": [\n                    {\"Href\": validate.text,\n                     \"KeyServiceUrl\": validate.any(None, validate.url())}\n                ]\n            }\n        }\n    })\n\n    @classmethod\n    def can_handle_url(cls, url):\n        return cls._url_re.match(url) is not None\n\n    @property\n    def device_info(self):\n        return {\"user\": {},\n                \"device\": {\"manufacturer\": \"Chrome\", \"model\": \"66\",\n                           \"os\": {\"name\": \"Windows\", \"version\": \"10\", \"type\": \"desktop\"}},\n                \"client\": {\"version\": \"4.1\", \"id\": \"browser\"},\n                \"variantAvailability\": {\"featureset\": {\"min\": [\"hls\", \"aes\"],\n                                                       \"max\": [\"hls\", \"aes\"]},\n                                        \"platformTag\": \"dotcom\"}}\n\n    def video_info(self):\n        page = self.session.http.get(self.url)\n        for div in itertags(page.text, 'div'):\n            if div.attributes.get(\"id\") == \"video\":\n                return div.attributes\n\n    def _get_html5_streams(self, video_info_url):\n        video_info = self.video_info()\n        res = self.session.http.post(video_info_url,\n                                     data=json.dumps(self.device_info),\n                                     headers={\"hmac\": video_info.get(\"data-video-hmac\")})\n        data = self.session.http.json(res, schema=self._video_info_schema)\n\n        log.debug(\"Video ID info response: {0}\".format(data))\n\n        stype = data['Playlist']['VideoType']\n\n        for media in data['Playlist']['Video']['MediaFiles']:\n            url = urljoin(data['Playlist']['Video']['Base'], media['Href'])\n            name_fmt = \"{pixels}_{bitrate}\" if stype == \"CATCHUP\" else None\n            for s in HLSStream.parse_variant_playlist(self.session, url, name_fmt=name_fmt).items():\n                yield s\n\n    def _get_rtmp_streams(self, video_info_url):\n        log.debug(\"XML data path: {0}\".format(video_info_url))\n        res = self.session.http.get(video_info_url)\n        playlist = self.session.http.xml(res, ignore_ns=True)\n        mediafiles = playlist.find(\".//Playlist/VideoEntries/Video/MediaFiles\")\n        playpath = mediafiles.find(\"./MediaFile/URL\")\n        return {\"live\": RTMPStream(self.session, {\"rtmp\": mediafiles.attrib.get(\"base\"),\n                                                  \"playpath\": playpath.text,\n                                                  \"live\": True,\n                                                  \"swfVfy\": self.swf_url\n                                                  })}\n\n    def _get_streams(self):\n        \"\"\"\n            Find all the streams for the ITV url\n            :return: Mapping of quality to stream\n        \"\"\"\n        self.session.http.headers.update({\"User-Agent\": useragents.FIREFOX})\n        stream = self._url_re.match(self.url).group(\"stream\")\n        video_info = self.video_info()\n        video_info_url = video_info.get(\"data-video-id\" if stream.lower() in (\"itv\", \"itv4\") else \"data-html5-playlist\")\n        if video_info_url.endswith(\".xml\"):\n            return self._get_rtmp_streams(video_info_url)\n        else:\n            return self._get_html5_streams(video_info_url)\n\n\n__plugin__ = ITVPlayer\n", "path": "src/streamlink/plugins/itvplayer.py"}]}