prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  artetv: de/fr Livestreams aren't playable anymore
  ### 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 build from the master branch
  
  ### Description
  
  Since about a week the live channels aren't playable anymore. However VODs working fine.
  
  
  ### Debug log
  
  ```text
  streamlink https://www.arte.tv/de/live/ worst -l debug
  [cli][debug] OS:         Linux-5.14.3-arch1-1-x86_64-with-glibc2.33
  [cli][debug] Python:     3.9.7
  [cli][debug] Streamlink: 2.4.0+17.g24c59a2
  [cli][debug] Requests(2.26.0), Socks(1.7.1), Websocket(0.59.0)
  [cli][debug] Arguments:
  [cli][debug]  url=https://www.arte.tv/de/live/
  [cli][debug]  stream=['worst']
  [cli][debug]  --loglevel=debug
  [cli][info] Found matching plugin artetv for URL https://www.arte.tv/de/live/
  error: No playable streams found on this URL: https://www.arte.tv/de/live/
  
  streamlink https://www.arte.tv/fr/direct/ best -l debug
  [cli][debug] OS:         Linux-5.14.3-arch1-1-x86_64-with-glibc2.33
  [cli][debug] Python:     3.9.7
  [cli][debug] Streamlink: 2.4.0+17.g24c59a2
  [cli][debug] Requests(2.26.0), Socks(1.7.1), Websocket(0.59.0)
  [cli][debug] Arguments:
  [cli][debug]  url=https://www.arte.tv/fr/direct/
  [cli][debug]  stream=['best']
  [cli][debug]  --loglevel=debug
  [cli][info] Found matching plugin artetv for URL https://www.arte.tv/fr/direct/
  error: No playable streams found on this URL: https://www.arte.tv/fr/direct/
  ```
  
  plugins.arte: switch to arte.tv v2 API
  The Arte.tv V1 API doens't seem to work anymore for live streams (see #4026).
  
  Both web site and mobile app use the V2 API, which requires an authentication token. The one from the website is used here for this fix.
  
  --- 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/artetv.py`
  Content:
  ```
  1 """Plugin for Arte.tv, bi-lingual art and culture channel."""
  2 
  3 import logging
  4 import re
  5 from operator import itemgetter
  6 
  7 from streamlink.plugin import Plugin, pluginmatcher
  8 from streamlink.plugin.api import validate
  9 from streamlink.stream import HLSStream
  10 
  11 log = logging.getLogger(__name__)
  12 JSON_VOD_URL = "https://api.arte.tv/api/player/v1/config/{0}/{1}?platform=ARTE_NEXT"
  13 JSON_LIVE_URL = "https://api.arte.tv/api/player/v1/livestream/{0}"
  14 
  15 _video_schema = validate.Schema({
  16     "videoJsonPlayer": {
  17         "VSR": validate.any(
  18             [],
  19             {
  20                 validate.text: {
  21                     "height": int,
  22                     "mediaType": validate.text,
  23                     "url": validate.text,
  24                     "versionProg": int,
  25                     "versionLibelle": validate.text
  26                 },
  27             },
  28         )
  29     }
  30 })
  31 
  32 
  33 @pluginmatcher(re.compile(r"""
  34     https?://(?:\w+\.)?arte\.tv/(?:guide/)?
  35     (?P<language>[a-z]{2})/
  36     (?:
  37         (?:videos/)?(?P<video_id>(?!RC-|videos)[^/]+?)/.+
  38         |
  39         (?:direct|live)
  40     )
  41 """, re.VERBOSE))
  42 class ArteTV(Plugin):
  43     def _create_stream(self, streams):
  44         variant, variantname = min([(stream["versionProg"], stream["versionLibelle"]) for stream in streams.values()],
  45                                    key=itemgetter(0))
  46         log.debug(f"Using the '{variantname}' stream variant")
  47         for sname, stream in streams.items():
  48             if stream["versionProg"] == variant:
  49                 if stream["mediaType"] == "hls":
  50                     try:
  51                         streams = HLSStream.parse_variant_playlist(self.session, stream["url"])
  52                         yield from streams.items()
  53                     except OSError as err:
  54                         log.warning(f"Failed to extract HLS streams for {sname}/{stream['versionLibelle']}: {err}")
  55 
  56     def _get_streams(self):
  57         language = self.match.group('language')
  58         video_id = self.match.group('video_id')
  59         if video_id is None:
  60             json_url = JSON_LIVE_URL.format(language)
  61         else:
  62             json_url = JSON_VOD_URL.format(language, video_id)
  63         res = self.session.http.get(json_url)
  64         video = self.session.http.json(res, schema=_video_schema)
  65 
  66         if not video["videoJsonPlayer"]["VSR"]:
  67             return
  68 
  69         vsr = video["videoJsonPlayer"]["VSR"]
  70         return self._create_stream(vsr)
  71 
  72 
  73 __plugin__ = ArteTV
  74
  ```
  --- 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/artetv.py b/src/streamlink/plugins/artetv.py
  --- a/src/streamlink/plugins/artetv.py
  +++ b/src/streamlink/plugins/artetv.py
  @@ -1,5 +1,3 @@
  -"""Plugin for Arte.tv, bi-lingual art and culture channel."""
  -
   import logging
   import re
   from operator import itemgetter
  @@ -9,25 +7,6 @@
   from streamlink.stream import HLSStream
   
   log = logging.getLogger(__name__)
  -JSON_VOD_URL = "https://api.arte.tv/api/player/v1/config/{0}/{1}?platform=ARTE_NEXT"
  -JSON_LIVE_URL = "https://api.arte.tv/api/player/v1/livestream/{0}"
  -
  -_video_schema = validate.Schema({
  -    "videoJsonPlayer": {
  -        "VSR": validate.any(
  -            [],
  -            {
  -                validate.text: {
  -                    "height": int,
  -                    "mediaType": validate.text,
  -                    "url": validate.text,
  -                    "versionProg": int,
  -                    "versionLibelle": validate.text
  -                },
  -            },
  -        )
  -    }
  -})
   
   
   @pluginmatcher(re.compile(r"""
  @@ -40,34 +19,49 @@
       )
   """, re.VERBOSE))
   class ArteTV(Plugin):
  -    def _create_stream(self, streams):
  -        variant, variantname = min([(stream["versionProg"], stream["versionLibelle"]) for stream in streams.values()],
  -                                   key=itemgetter(0))
  -        log.debug(f"Using the '{variantname}' stream variant")
  -        for sname, stream in streams.items():
  -            if stream["versionProg"] == variant:
  -                if stream["mediaType"] == "hls":
  -                    try:
  -                        streams = HLSStream.parse_variant_playlist(self.session, stream["url"])
  -                        yield from streams.items()
  -                    except OSError as err:
  -                        log.warning(f"Failed to extract HLS streams for {sname}/{stream['versionLibelle']}: {err}")
  +    API_URL = "https://api.arte.tv/api/player/v2/config/{0}/{1}"
  +    API_TOKEN = "MzYyZDYyYmM1Y2Q3ZWRlZWFjMmIyZjZjNTRiMGY4MzY4NzBhOWQ5YjE4MGQ1NGFiODJmOTFlZDQwN2FkOTZjMQ"
   
       def _get_streams(self):
  -        language = self.match.group('language')
  -        video_id = self.match.group('video_id')
  -        if video_id is None:
  -            json_url = JSON_LIVE_URL.format(language)
  -        else:
  -            json_url = JSON_VOD_URL.format(language, video_id)
  -        res = self.session.http.get(json_url)
  -        video = self.session.http.json(res, schema=_video_schema)
  +        language = self.match.group("language")
  +        video_id = self.match.group("video_id")
   
  -        if not video["videoJsonPlayer"]["VSR"]:
  +        json_url = self.API_URL.format(language, video_id or "LIVE")
  +        headers = {
  +            "Authorization": f"Bearer {self.API_TOKEN}"
  +        }
  +        streams, metadata = self.session.http.get(json_url, headers=headers, schema=validate.Schema(
  +            validate.parse_json(),
  +            {"data": {"attributes": {
  +                "streams": validate.any(
  +                    [],
  +                    [
  +                        validate.all(
  +                            {
  +                                "url": validate.url(),
  +                                "slot": int,
  +                                "protocol": validate.any("HLS", "HLS_NG"),
  +                            },
  +                            validate.union_get("slot", "protocol", "url")
  +                        )
  +                    ]
  +                ),
  +                "metadata": {
  +                    "title": str,
  +                    "subtitle": validate.any(None, str)
  +                }
  +            }}},
  +            validate.get(("data", "attributes")),
  +            validate.union_get("streams", "metadata")
  +        ))
  +
  +        if not streams:
               return
   
  -        vsr = video["videoJsonPlayer"]["VSR"]
  -        return self._create_stream(vsr)
  +        self.title = f"{metadata['title']} - {metadata['subtitle']}" if metadata["subtitle"] else metadata["title"]
  +
  +        for slot, protocol, url in sorted(streams, key=itemgetter(0)):
  +            return HLSStream.parse_variant_playlist(self.session, url)
   
   
   __plugin__ = ArteTV
  
verification_info: {"golden_diff": "diff --git a/src/streamlink/plugins/artetv.py b/src/streamlink/plugins/artetv.py\n--- a/src/streamlink/plugins/artetv.py\n+++ b/src/streamlink/plugins/artetv.py\n@@ -1,5 +1,3 @@\n-\"\"\"Plugin for Arte.tv, bi-lingual art and culture channel.\"\"\"\n-\n import logging\n import re\n from operator import itemgetter\n@@ -9,25 +7,6 @@\n from streamlink.stream import HLSStream\n \n log = logging.getLogger(__name__)\n-JSON_VOD_URL = \"https://api.arte.tv/api/player/v1/config/{0}/{1}?platform=ARTE_NEXT\"\n-JSON_LIVE_URL = \"https://api.arte.tv/api/player/v1/livestream/{0}\"\n-\n-_video_schema = validate.Schema({\n-    \"videoJsonPlayer\": {\n-        \"VSR\": validate.any(\n-            [],\n-            {\n-                validate.text: {\n-                    \"height\": int,\n-                    \"mediaType\": validate.text,\n-                    \"url\": validate.text,\n-                    \"versionProg\": int,\n-                    \"versionLibelle\": validate.text\n-                },\n-            },\n-        )\n-    }\n-})\n \n \n @pluginmatcher(re.compile(r\"\"\"\n@@ -40,34 +19,49 @@\n     )\n \"\"\", re.VERBOSE))\n class ArteTV(Plugin):\n-    def _create_stream(self, streams):\n-        variant, variantname = min([(stream[\"versionProg\"], stream[\"versionLibelle\"]) for stream in streams.values()],\n-                                   key=itemgetter(0))\n-        log.debug(f\"Using the '{variantname}' stream variant\")\n-        for sname, stream in streams.items():\n-            if stream[\"versionProg\"] == variant:\n-                if stream[\"mediaType\"] == \"hls\":\n-                    try:\n-                        streams = HLSStream.parse_variant_playlist(self.session, stream[\"url\"])\n-                        yield from streams.items()\n-                    except OSError as err:\n-                        log.warning(f\"Failed to extract HLS streams for {sname}/{stream['versionLibelle']}: {err}\")\n+    API_URL = \"https://api.arte.tv/api/player/v2/config/{0}/{1}\"\n+    API_TOKEN = \"MzYyZDYyYmM1Y2Q3ZWRlZWFjMmIyZjZjNTRiMGY4MzY4NzBhOWQ5YjE4MGQ1NGFiODJmOTFlZDQwN2FkOTZjMQ\"\n \n     def _get_streams(self):\n-        language = self.match.group('language')\n-        video_id = self.match.group('video_id')\n-        if video_id is None:\n-            json_url = JSON_LIVE_URL.format(language)\n-        else:\n-            json_url = JSON_VOD_URL.format(language, video_id)\n-        res = self.session.http.get(json_url)\n-        video = self.session.http.json(res, schema=_video_schema)\n+        language = self.match.group(\"language\")\n+        video_id = self.match.group(\"video_id\")\n \n-        if not video[\"videoJsonPlayer\"][\"VSR\"]:\n+        json_url = self.API_URL.format(language, video_id or \"LIVE\")\n+        headers = {\n+            \"Authorization\": f\"Bearer {self.API_TOKEN}\"\n+        }\n+        streams, metadata = self.session.http.get(json_url, headers=headers, schema=validate.Schema(\n+            validate.parse_json(),\n+            {\"data\": {\"attributes\": {\n+                \"streams\": validate.any(\n+                    [],\n+                    [\n+                        validate.all(\n+                            {\n+                                \"url\": validate.url(),\n+                                \"slot\": int,\n+                                \"protocol\": validate.any(\"HLS\", \"HLS_NG\"),\n+                            },\n+                            validate.union_get(\"slot\", \"protocol\", \"url\")\n+                        )\n+                    ]\n+                ),\n+                \"metadata\": {\n+                    \"title\": str,\n+                    \"subtitle\": validate.any(None, str)\n+                }\n+            }}},\n+            validate.get((\"data\", \"attributes\")),\n+            validate.union_get(\"streams\", \"metadata\")\n+        ))\n+\n+        if not streams:\n             return\n \n-        vsr = video[\"videoJsonPlayer\"][\"VSR\"]\n-        return self._create_stream(vsr)\n+        self.title = f\"{metadata['title']} - {metadata['subtitle']}\" if metadata[\"subtitle\"] else metadata[\"title\"]\n+\n+        for slot, protocol, url in sorted(streams, key=itemgetter(0)):\n+            return HLSStream.parse_variant_playlist(self.session, url)\n \n \n __plugin__ = ArteTV\n", "issue": "artetv: de/fr Livestreams aren't playable anymore\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 build from the master branch\n\n### Description\n\nSince about a week the live channels aren't playable anymore. However VODs working fine.\r\n\n\n### Debug log\n\n```text\nstreamlink https://www.arte.tv/de/live/ worst -l debug\r\n[cli][debug] OS:         Linux-5.14.3-arch1-1-x86_64-with-glibc2.33\r\n[cli][debug] Python:     3.9.7\r\n[cli][debug] Streamlink: 2.4.0+17.g24c59a2\r\n[cli][debug] Requests(2.26.0), Socks(1.7.1), Websocket(0.59.0)\r\n[cli][debug] Arguments:\r\n[cli][debug]  url=https://www.arte.tv/de/live/\r\n[cli][debug]  stream=['worst']\r\n[cli][debug]  --loglevel=debug\r\n[cli][info] Found matching plugin artetv for URL https://www.arte.tv/de/live/\r\nerror: No playable streams found on this URL: https://www.arte.tv/de/live/\r\n\r\nstreamlink https://www.arte.tv/fr/direct/ best -l debug\r\n[cli][debug] OS:         Linux-5.14.3-arch1-1-x86_64-with-glibc2.33\r\n[cli][debug] Python:     3.9.7\r\n[cli][debug] Streamlink: 2.4.0+17.g24c59a2\r\n[cli][debug] Requests(2.26.0), Socks(1.7.1), Websocket(0.59.0)\r\n[cli][debug] Arguments:\r\n[cli][debug]  url=https://www.arte.tv/fr/direct/\r\n[cli][debug]  stream=['best']\r\n[cli][debug]  --loglevel=debug\r\n[cli][info] Found matching plugin artetv for URL https://www.arte.tv/fr/direct/\r\nerror: No playable streams found on this URL: https://www.arte.tv/fr/direct/\n```\n\nplugins.arte: switch to arte.tv v2 API\nThe Arte.tv V1 API doens't seem to work anymore for live streams (see #4026).\r\n\r\nBoth web site and mobile app use the V2 API, which requires an authentication token. The one from the website is used here for this fix.\n", "before_files": [{"content": "\"\"\"Plugin for Arte.tv, bi-lingual art and culture channel.\"\"\"\n\nimport logging\nimport re\nfrom operator import itemgetter\n\nfrom streamlink.plugin import Plugin, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream import HLSStream\n\nlog = logging.getLogger(__name__)\nJSON_VOD_URL = \"https://api.arte.tv/api/player/v1/config/{0}/{1}?platform=ARTE_NEXT\"\nJSON_LIVE_URL = \"https://api.arte.tv/api/player/v1/livestream/{0}\"\n\n_video_schema = validate.Schema({\n    \"videoJsonPlayer\": {\n        \"VSR\": validate.any(\n            [],\n            {\n                validate.text: {\n                    \"height\": int,\n                    \"mediaType\": validate.text,\n                    \"url\": validate.text,\n                    \"versionProg\": int,\n                    \"versionLibelle\": validate.text\n                },\n            },\n        )\n    }\n})\n\n\n@pluginmatcher(re.compile(r\"\"\"\n    https?://(?:\\w+\\.)?arte\\.tv/(?:guide/)?\n    (?P<language>[a-z]{2})/\n    (?:\n        (?:videos/)?(?P<video_id>(?!RC-|videos)[^/]+?)/.+\n        |\n        (?:direct|live)\n    )\n\"\"\", re.VERBOSE))\nclass ArteTV(Plugin):\n    def _create_stream(self, streams):\n        variant, variantname = min([(stream[\"versionProg\"], stream[\"versionLibelle\"]) for stream in streams.values()],\n                                   key=itemgetter(0))\n        log.debug(f\"Using the '{variantname}' stream variant\")\n        for sname, stream in streams.items():\n            if stream[\"versionProg\"] == variant:\n                if stream[\"mediaType\"] == \"hls\":\n                    try:\n                        streams = HLSStream.parse_variant_playlist(self.session, stream[\"url\"])\n                        yield from streams.items()\n                    except OSError as err:\n                        log.warning(f\"Failed to extract HLS streams for {sname}/{stream['versionLibelle']}: {err}\")\n\n    def _get_streams(self):\n        language = self.match.group('language')\n        video_id = self.match.group('video_id')\n        if video_id is None:\n            json_url = JSON_LIVE_URL.format(language)\n        else:\n            json_url = JSON_VOD_URL.format(language, video_id)\n        res = self.session.http.get(json_url)\n        video = self.session.http.json(res, schema=_video_schema)\n\n        if not video[\"videoJsonPlayer\"][\"VSR\"]:\n            return\n\n        vsr = video[\"videoJsonPlayer\"][\"VSR\"]\n        return self._create_stream(vsr)\n\n\n__plugin__ = ArteTV\n", "path": "src/streamlink/plugins/artetv.py"}], "after_files": [{"content": "import logging\nimport re\nfrom operator import itemgetter\n\nfrom streamlink.plugin import Plugin, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream import HLSStream\n\nlog = logging.getLogger(__name__)\n\n\n@pluginmatcher(re.compile(r\"\"\"\n    https?://(?:\\w+\\.)?arte\\.tv/(?:guide/)?\n    (?P<language>[a-z]{2})/\n    (?:\n        (?:videos/)?(?P<video_id>(?!RC-|videos)[^/]+?)/.+\n        |\n        (?:direct|live)\n    )\n\"\"\", re.VERBOSE))\nclass ArteTV(Plugin):\n    API_URL = \"https://api.arte.tv/api/player/v2/config/{0}/{1}\"\n    API_TOKEN = \"MzYyZDYyYmM1Y2Q3ZWRlZWFjMmIyZjZjNTRiMGY4MzY4NzBhOWQ5YjE4MGQ1NGFiODJmOTFlZDQwN2FkOTZjMQ\"\n\n    def _get_streams(self):\n        language = self.match.group(\"language\")\n        video_id = self.match.group(\"video_id\")\n\n        json_url = self.API_URL.format(language, video_id or \"LIVE\")\n        headers = {\n            \"Authorization\": f\"Bearer {self.API_TOKEN}\"\n        }\n        streams, metadata = self.session.http.get(json_url, headers=headers, schema=validate.Schema(\n            validate.parse_json(),\n            {\"data\": {\"attributes\": {\n                \"streams\": validate.any(\n                    [],\n                    [\n                        validate.all(\n                            {\n                                \"url\": validate.url(),\n                                \"slot\": int,\n                                \"protocol\": validate.any(\"HLS\", \"HLS_NG\"),\n                            },\n                            validate.union_get(\"slot\", \"protocol\", \"url\")\n                        )\n                    ]\n                ),\n                \"metadata\": {\n                    \"title\": str,\n                    \"subtitle\": validate.any(None, str)\n                }\n            }}},\n            validate.get((\"data\", \"attributes\")),\n            validate.union_get(\"streams\", \"metadata\")\n        ))\n\n        if not streams:\n            return\n\n        self.title = f\"{metadata['title']} - {metadata['subtitle']}\" if metadata[\"subtitle\"] else metadata[\"title\"]\n\n        for slot, protocol, url in sorted(streams, key=itemgetter(0)):\n            return HLSStream.parse_variant_playlist(self.session, url)\n\n\n__plugin__ = ArteTV\n", "path": "src/streamlink/plugins/artetv.py"}]}