prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Can't Download Picarto Video
  ## Bug Report
  
  - [X ] I have read the contribution guidelines. 
  
  ### Description
  
  Picarto.tv plugin doesn't seem to download any recorded streams anymore
  
  ### Expected / Actual behavior
  
  Expected it to download video, didn't
  
  ### Reproduction steps / Explicit stream URLs to test
  
  1. streamlink -o Amarynceus.flv https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv
  
  ### Logs
  
  
  ```
  streamlink -o Amarynceus.flv https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv -l debug
  [cli][debug] OS:         Linux-4.15.0-24-generic-x86_64-with-Ubuntu-18.04-bionic
  [cli][debug] Python:     2.7.15rc1
  [cli][debug] Streamlink: 0.14.2+24.g6bf654a2
  [cli][debug] Requests(2.19.1), Socks(1.6.7), Websocket(0.48.0)
  [cli][info] Found matching plugin picarto for URL https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv
  [plugin.picarto][debug] Possible VOD stream...
  [plugin.picarto][warning] Probably a VOD stream but no VOD found?
  [plugin.picarto][error] The channel Amarynceus_2018.07.04.22.55.48.flv does not exist
  error: No playable streams found on this URL: https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv
  ```
  
  --- 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/picarto.py`
  Content:
  ```
  1 from __future__ import print_function
  2 
  3 import re
  4 import json
  5 
  6 from streamlink.plugin import Plugin
  7 from streamlink.stream import HLSStream
  8 from streamlink.stream import RTMPStream
  9 
  10 
  11 class Picarto(Plugin):
  12     CHANNEL_API_URL = "https://api.picarto.tv/v1/channel/name/{channel}"
  13     VIDEO_API_URL = "https://picarto.tv/process/channel"
  14     RTMP_URL = "rtmp://{server}:1935/play/"
  15     RTMP_PLAYPATH = "golive+{channel}?token={token}"
  16     HLS_URL = "https://{server}/hls/{channel}/index.m3u8?token={token}"
  17 
  18     # Regex for all usable URLs
  19     _url_re = re.compile(r"""
  20         https?://(?:\w+\.)?picarto\.tv/(?:videopopout/)?([^&?/]+)
  21     """, re.VERBOSE)
  22 
  23     # Regex for VOD extraction
  24     _vod_re = re.compile(r'''vod: "(https?://[\S]+?/index.m3u8)",''')
  25 
  26     @classmethod
  27     def can_handle_url(cls, url):
  28         return cls._url_re.match(url) is not None
  29 
  30     def _create_hls_stream(self, server, channel, token):
  31         streams = HLSStream.parse_variant_playlist(self.session,
  32                                                    self.HLS_URL.format(
  33                                                        server=server,
  34                                                        channel=channel,
  35                                                        token=token),
  36                                                    verify=False)
  37         if len(streams) > 1:
  38             self.logger.debug("Multiple HLS streams found")
  39             return streams
  40         elif len(streams) == 0:
  41             self.logger.warning("No HLS streams found when expected")
  42             return {}
  43         else:
  44             # one HLS streams, rename it to live
  45             return {"live": list(streams.values())[0]}
  46 
  47     def _create_flash_stream(self, server, channel, token):
  48         params = {
  49             "rtmp": self.RTMP_URL.format(server=server),
  50             "playpath": self.RTMP_PLAYPATH.format(token=token, channel=channel)
  51         }
  52         return RTMPStream(self.session, params=params)
  53 
  54     def _get_vod_stream(self, page):
  55         m = self._vod_re.search(page.text)
  56         if m:
  57             return HLSStream.parse_variant_playlist(self.session, m.group(1))
  58 
  59     def _get_streams(self):
  60         url_channel_name = self._url_re.match(self.url).group(1)
  61 
  62         # Handle VODs first, since their "channel name" is different
  63         if url_channel_name.endswith(".flv"):
  64             self.logger.debug("Possible VOD stream...")
  65             page = self.session.http.get(self.url)
  66             vod_streams = self._get_vod_stream(page)
  67             if vod_streams:
  68                 for s in vod_streams.items():
  69                     yield s
  70                 return
  71             else:
  72                 self.logger.warning("Probably a VOD stream but no VOD found?")
  73 
  74         ci = self.session.http.get(self.CHANNEL_API_URL.format(channel=url_channel_name), raise_for_status=False)
  75 
  76         if ci.status_code == 404:
  77             self.logger.error("The channel {0} does not exist".format(url_channel_name))
  78             return
  79 
  80         channel_api_json = json.loads(ci.text)
  81 
  82         if channel_api_json["online"] != True:
  83             self.logger.error("The channel {0} is currently offline".format(url_channel_name))
  84             return
  85 
  86         server = None
  87         token = "public"
  88         channel = channel_api_json["name"]
  89 
  90         # Extract preferred edge server and available techs from the undocumented channel API
  91         channel_server_res = self.session.http.post(self.VIDEO_API_URL, data={"loadbalancinginfo": channel})
  92         info_json = json.loads(channel_server_res.text)
  93         pref = info_json["preferedEdge"]
  94         for i in info_json["edges"]:
  95             if i["id"] == pref:
  96                 server = i["ep"]
  97                 break
  98         self.logger.debug("Using load balancing server {0} : {1} for channel {2}",
  99                           pref,
  100                           server,
  101                           channel)
  102 
  103         for i in info_json["techs"]:
  104             if i["label"] == "HLS":
  105                 for s in self._create_hls_stream(server, channel, token).items():
  106                     yield s
  107             elif i["label"] == "RTMP Flash":
  108                 stream = self._create_flash_stream(server, channel, token)
  109                 yield "live", stream
  110 
  111 
  112 __plugin__ = Picarto
  113
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/streamlink/plugins/picarto.py b/src/streamlink/plugins/picarto.py
  --- a/src/streamlink/plugins/picarto.py
  +++ b/src/streamlink/plugins/picarto.py
  @@ -4,8 +4,10 @@
   import json
   
   from streamlink.plugin import Plugin
  +from streamlink.plugin.api import validate
   from streamlink.stream import HLSStream
   from streamlink.stream import RTMPStream
  +from streamlink.utils import parse_json
   
   
   class Picarto(Plugin):
  @@ -15,13 +17,29 @@
       RTMP_PLAYPATH = "golive+{channel}?token={token}"
       HLS_URL = "https://{server}/hls/{channel}/index.m3u8?token={token}"
   
  +
       # Regex for all usable URLs
       _url_re = re.compile(r"""
           https?://(?:\w+\.)?picarto\.tv/(?:videopopout/)?([^&?/]+)
       """, re.VERBOSE)
   
       # Regex for VOD extraction
  -    _vod_re = re.compile(r'''vod: "(https?://[\S]+?/index.m3u8)",''')
  +    _vod_re = re.compile(r'''(?<=#vod-player", )(\{.*?\})''')
  +
  +    data_schema = validate.Schema(
  +        validate.transform(_vod_re.search),
  +        validate.any(
  +            None,
  +            validate.all(
  +                validate.get(0),
  +                validate.transform(parse_json),
  +                {
  +                    "vod": validate.url(),
  +                }
  +            )
  +        )
  +    )
  +
   
       @classmethod
       def can_handle_url(cls, url):
  @@ -52,9 +70,10 @@
           return RTMPStream(self.session, params=params)
   
       def _get_vod_stream(self, page):
  -        m = self._vod_re.search(page.text)
  -        if m:
  -            return HLSStream.parse_variant_playlist(self.session, m.group(1))
  +        data = self.data_schema.validate(page.text)
  +
  +        if data:
  +            return HLSStream.parse_variant_playlist(self.session, data["vod"])
   
       def _get_streams(self):
           url_channel_name = self._url_re.match(self.url).group(1)
  
verification_info: {"golden_diff": "diff --git a/src/streamlink/plugins/picarto.py b/src/streamlink/plugins/picarto.py\n--- a/src/streamlink/plugins/picarto.py\n+++ b/src/streamlink/plugins/picarto.py\n@@ -4,8 +4,10 @@\n import json\n \n from streamlink.plugin import Plugin\n+from streamlink.plugin.api import validate\n from streamlink.stream import HLSStream\n from streamlink.stream import RTMPStream\n+from streamlink.utils import parse_json\n \n \n class Picarto(Plugin):\n@@ -15,13 +17,29 @@\n     RTMP_PLAYPATH = \"golive+{channel}?token={token}\"\n     HLS_URL = \"https://{server}/hls/{channel}/index.m3u8?token={token}\"\n \n+\n     # Regex for all usable URLs\n     _url_re = re.compile(r\"\"\"\n         https?://(?:\\w+\\.)?picarto\\.tv/(?:videopopout/)?([^&?/]+)\n     \"\"\", re.VERBOSE)\n \n     # Regex for VOD extraction\n-    _vod_re = re.compile(r'''vod: \"(https?://[\\S]+?/index.m3u8)\",''')\n+    _vod_re = re.compile(r'''(?<=#vod-player\", )(\\{.*?\\})''')\n+\n+    data_schema = validate.Schema(\n+        validate.transform(_vod_re.search),\n+        validate.any(\n+            None,\n+            validate.all(\n+                validate.get(0),\n+                validate.transform(parse_json),\n+                {\n+                    \"vod\": validate.url(),\n+                }\n+            )\n+        )\n+    )\n+\n \n     @classmethod\n     def can_handle_url(cls, url):\n@@ -52,9 +70,10 @@\n         return RTMPStream(self.session, params=params)\n \n     def _get_vod_stream(self, page):\n-        m = self._vod_re.search(page.text)\n-        if m:\n-            return HLSStream.parse_variant_playlist(self.session, m.group(1))\n+        data = self.data_schema.validate(page.text)\n+\n+        if data:\n+            return HLSStream.parse_variant_playlist(self.session, data[\"vod\"])\n \n     def _get_streams(self):\n         url_channel_name = self._url_re.match(self.url).group(1)\n", "issue": "Can't Download Picarto Video\n## Bug Report\r\n\r\n- [X ] I have read the contribution guidelines. \r\n\r\n### Description\r\n\r\nPicarto.tv plugin doesn't seem to download any recorded streams anymore\r\n\r\n### Expected / Actual behavior\r\n\r\nExpected it to download video, didn't\r\n\r\n### Reproduction steps / Explicit stream URLs to test\r\n\r\n1. streamlink -o Amarynceus.flv https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv\r\n\r\n### Logs\r\n\r\n\r\n```\r\nstreamlink -o Amarynceus.flv https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv -l debug\r\n[cli][debug] OS:         Linux-4.15.0-24-generic-x86_64-with-Ubuntu-18.04-bionic\r\n[cli][debug] Python:     2.7.15rc1\r\n[cli][debug] Streamlink: 0.14.2+24.g6bf654a2\r\n[cli][debug] Requests(2.19.1), Socks(1.6.7), Websocket(0.48.0)\r\n[cli][info] Found matching plugin picarto for URL https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv\r\n[plugin.picarto][debug] Possible VOD stream...\r\n[plugin.picarto][warning] Probably a VOD stream but no VOD found?\r\n[plugin.picarto][error] The channel Amarynceus_2018.07.04.22.55.48.flv does not exist\r\nerror: No playable streams found on this URL: https://picarto.tv/videopopout/Amarynceus_2018.07.04.22.55.48.flv\r\n```\n", "before_files": [{"content": "from __future__ import print_function\n\nimport re\nimport json\n\nfrom streamlink.plugin import Plugin\nfrom streamlink.stream import HLSStream\nfrom streamlink.stream import RTMPStream\n\n\nclass Picarto(Plugin):\n    CHANNEL_API_URL = \"https://api.picarto.tv/v1/channel/name/{channel}\"\n    VIDEO_API_URL = \"https://picarto.tv/process/channel\"\n    RTMP_URL = \"rtmp://{server}:1935/play/\"\n    RTMP_PLAYPATH = \"golive+{channel}?token={token}\"\n    HLS_URL = \"https://{server}/hls/{channel}/index.m3u8?token={token}\"\n\n    # Regex for all usable URLs\n    _url_re = re.compile(r\"\"\"\n        https?://(?:\\w+\\.)?picarto\\.tv/(?:videopopout/)?([^&?/]+)\n    \"\"\", re.VERBOSE)\n\n    # Regex for VOD extraction\n    _vod_re = re.compile(r'''vod: \"(https?://[\\S]+?/index.m3u8)\",''')\n\n    @classmethod\n    def can_handle_url(cls, url):\n        return cls._url_re.match(url) is not None\n\n    def _create_hls_stream(self, server, channel, token):\n        streams = HLSStream.parse_variant_playlist(self.session,\n                                                   self.HLS_URL.format(\n                                                       server=server,\n                                                       channel=channel,\n                                                       token=token),\n                                                   verify=False)\n        if len(streams) > 1:\n            self.logger.debug(\"Multiple HLS streams found\")\n            return streams\n        elif len(streams) == 0:\n            self.logger.warning(\"No HLS streams found when expected\")\n            return {}\n        else:\n            # one HLS streams, rename it to live\n            return {\"live\": list(streams.values())[0]}\n\n    def _create_flash_stream(self, server, channel, token):\n        params = {\n            \"rtmp\": self.RTMP_URL.format(server=server),\n            \"playpath\": self.RTMP_PLAYPATH.format(token=token, channel=channel)\n        }\n        return RTMPStream(self.session, params=params)\n\n    def _get_vod_stream(self, page):\n        m = self._vod_re.search(page.text)\n        if m:\n            return HLSStream.parse_variant_playlist(self.session, m.group(1))\n\n    def _get_streams(self):\n        url_channel_name = self._url_re.match(self.url).group(1)\n\n        # Handle VODs first, since their \"channel name\" is different\n        if url_channel_name.endswith(\".flv\"):\n            self.logger.debug(\"Possible VOD stream...\")\n            page = self.session.http.get(self.url)\n            vod_streams = self._get_vod_stream(page)\n            if vod_streams:\n                for s in vod_streams.items():\n                    yield s\n                return\n            else:\n                self.logger.warning(\"Probably a VOD stream but no VOD found?\")\n\n        ci = self.session.http.get(self.CHANNEL_API_URL.format(channel=url_channel_name), raise_for_status=False)\n\n        if ci.status_code == 404:\n            self.logger.error(\"The channel {0} does not exist\".format(url_channel_name))\n            return\n\n        channel_api_json = json.loads(ci.text)\n\n        if channel_api_json[\"online\"] != True:\n            self.logger.error(\"The channel {0} is currently offline\".format(url_channel_name))\n            return\n\n        server = None\n        token = \"public\"\n        channel = channel_api_json[\"name\"]\n\n        # Extract preferred edge server and available techs from the undocumented channel API\n        channel_server_res = self.session.http.post(self.VIDEO_API_URL, data={\"loadbalancinginfo\": channel})\n        info_json = json.loads(channel_server_res.text)\n        pref = info_json[\"preferedEdge\"]\n        for i in info_json[\"edges\"]:\n            if i[\"id\"] == pref:\n                server = i[\"ep\"]\n                break\n        self.logger.debug(\"Using load balancing server {0} : {1} for channel {2}\",\n                          pref,\n                          server,\n                          channel)\n\n        for i in info_json[\"techs\"]:\n            if i[\"label\"] == \"HLS\":\n                for s in self._create_hls_stream(server, channel, token).items():\n                    yield s\n            elif i[\"label\"] == \"RTMP Flash\":\n                stream = self._create_flash_stream(server, channel, token)\n                yield \"live\", stream\n\n\n__plugin__ = Picarto\n", "path": "src/streamlink/plugins/picarto.py"}], "after_files": [{"content": "from __future__ import print_function\n\nimport re\nimport json\n\nfrom streamlink.plugin import Plugin\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream import HLSStream\nfrom streamlink.stream import RTMPStream\nfrom streamlink.utils import parse_json\n\n\nclass Picarto(Plugin):\n    CHANNEL_API_URL = \"https://api.picarto.tv/v1/channel/name/{channel}\"\n    VIDEO_API_URL = \"https://picarto.tv/process/channel\"\n    RTMP_URL = \"rtmp://{server}:1935/play/\"\n    RTMP_PLAYPATH = \"golive+{channel}?token={token}\"\n    HLS_URL = \"https://{server}/hls/{channel}/index.m3u8?token={token}\"\n\n\n    # Regex for all usable URLs\n    _url_re = re.compile(r\"\"\"\n        https?://(?:\\w+\\.)?picarto\\.tv/(?:videopopout/)?([^&?/]+)\n    \"\"\", re.VERBOSE)\n\n    # Regex for VOD extraction\n    _vod_re = re.compile(r'''(?<=#vod-player\", )(\\{.*?\\})''')\n\n    data_schema = validate.Schema(\n        validate.transform(_vod_re.search),\n        validate.any(\n            None,\n            validate.all(\n                validate.get(0),\n                validate.transform(parse_json),\n                {\n                    \"vod\": validate.url(),\n                }\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    def _create_hls_stream(self, server, channel, token):\n        streams = HLSStream.parse_variant_playlist(self.session,\n                                                   self.HLS_URL.format(\n                                                       server=server,\n                                                       channel=channel,\n                                                       token=token),\n                                                   verify=False)\n        if len(streams) > 1:\n            self.logger.debug(\"Multiple HLS streams found\")\n            return streams\n        elif len(streams) == 0:\n            self.logger.warning(\"No HLS streams found when expected\")\n            return {}\n        else:\n            # one HLS streams, rename it to live\n            return {\"live\": list(streams.values())[0]}\n\n    def _create_flash_stream(self, server, channel, token):\n        params = {\n            \"rtmp\": self.RTMP_URL.format(server=server),\n            \"playpath\": self.RTMP_PLAYPATH.format(token=token, channel=channel)\n        }\n        return RTMPStream(self.session, params=params)\n\n    def _get_vod_stream(self, page):\n        data = self.data_schema.validate(page.text)\n\n        if data:\n            return HLSStream.parse_variant_playlist(self.session, data[\"vod\"])\n\n    def _get_streams(self):\n        url_channel_name = self._url_re.match(self.url).group(1)\n\n        # Handle VODs first, since their \"channel name\" is different\n        if url_channel_name.endswith(\".flv\"):\n            self.logger.debug(\"Possible VOD stream...\")\n            page = self.session.http.get(self.url)\n            vod_streams = self._get_vod_stream(page)\n            if vod_streams:\n                for s in vod_streams.items():\n                    yield s\n                return\n            else:\n                self.logger.warning(\"Probably a VOD stream but no VOD found?\")\n\n        ci = self.session.http.get(self.CHANNEL_API_URL.format(channel=url_channel_name), raise_for_status=False)\n\n        if ci.status_code == 404:\n            self.logger.error(\"The channel {0} does not exist\".format(url_channel_name))\n            return\n\n        channel_api_json = json.loads(ci.text)\n\n        if channel_api_json[\"online\"] != True:\n            self.logger.error(\"The channel {0} is currently offline\".format(url_channel_name))\n            return\n\n        server = None\n        token = \"public\"\n        channel = channel_api_json[\"name\"]\n\n        # Extract preferred edge server and available techs from the undocumented channel API\n        channel_server_res = self.session.http.post(self.VIDEO_API_URL, data={\"loadbalancinginfo\": channel})\n        info_json = json.loads(channel_server_res.text)\n        pref = info_json[\"preferedEdge\"]\n        for i in info_json[\"edges\"]:\n            if i[\"id\"] == pref:\n                server = i[\"ep\"]\n                break\n        self.logger.debug(\"Using load balancing server {0} : {1} for channel {2}\",\n                          pref,\n                          server,\n                          channel)\n\n        for i in info_json[\"techs\"]:\n            if i[\"label\"] == \"HLS\":\n                for s in self._create_hls_stream(server, channel, token).items():\n                    yield s\n            elif i[\"label\"] == \"RTMP Flash\":\n                stream = self._create_flash_stream(server, channel, token)\n                yield \"live\", stream\n\n\n__plugin__ = Picarto\n", "path": "src/streamlink/plugins/picarto.py"}]}