prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  plugins.ard_mediathek: rewrite plugin
  Resolves #4191 
  
  One issue I couldn't fix is the text encoding of the metadata which gets messed up by `validate.parse_html()`. See the VOD title down below...
  https://github.com/streamlink/streamlink/blob/175d4748561c7154bb80c5a47dae22039e45d4ce/src/streamlink/utils/parse.py#L54-L55
  
  Some VODs also have a second title, eg. if it's a TV show, but I couldn't be bothered to implement this. Not important.
  
  ----
  
  Das Erste - Live:
  ```
  $ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/daserste/live/Y3JpZDovL2Rhc2Vyc3RlLmRlL0xpdmVzdHJlYW0tRGFzRXJzdGU/' best
  [cli.output][debug] Opening subprocess: mpv "--force-media-title=Das Erste - Das Erste" -
  ```
  
  WDR - Live:
  ```
  $ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/live/Y3JpZDovL3dkci5kZS9CZWl0cmFnLTNkYTY2NGRlLTE4YzItNDY1MC1hNGZmLTRmNjQxNDcyMDcyYg/' best
  [cli.output][debug] Opening subprocess: mpv "--force-media-title=WDR - WDR Fernsehen im Livestream" -
  ```
  
  VOD
  ```
  $ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/video/dokus-im-ersten/wirecard-die-milliarden-luege/das-erste/Y3JpZDovL2Rhc2Vyc3RlLmRlL3JlcG9ydGFnZSBfIGRva3VtZW50YXRpb24gaW0gZXJzdGVuL2NlMjQ0OWM4LTQ4YTUtNGIyNC1iMTdlLWNhOTNjMDQ5OTc4Zg/' best
  [cli.output][debug] Opening subprocess: mpv "--force-media-title=Das Erste - Wirecard - Die Milliarden-LÃ¼ge" -
  ```
  
  </issue>
  <code>
  [start of src/streamlink/plugins/ard_mediathek.py]
  1 import logging
  2 import re
  3 
  4 from streamlink.plugin import Plugin, pluginmatcher
  5 from streamlink.plugin.api import validate
  6 from streamlink.stream.hls import HLSStream
  7 
  8 
  9 log = logging.getLogger(__name__)
  10 
  11 
  12 @pluginmatcher(re.compile(
  13     r"https?://(?:(\w+\.)?ardmediathek\.de/|mediathek\.daserste\.de/)"
  14 ))
  15 class ARDMediathek(Plugin):
  16     def _get_streams(self):
  17         data_json = self.session.http.get(self.url, schema=validate.Schema(
  18             validate.parse_html(),
  19             validate.xml_findtext(".//script[@id='fetchedContextValue'][@type='application/json']"),
  20             validate.any(None, validate.all(
  21                 validate.parse_json(),
  22                 {str: dict},
  23                 validate.transform(lambda obj: list(obj.items())),
  24                 validate.filter(lambda item: item[0].startswith("https://api.ardmediathek.de/page-gateway/pages/")),
  25                 validate.any(validate.get((0, 1)), [])
  26             ))
  27         ))
  28         if not data_json:
  29             return
  30 
  31         schema_data = validate.Schema({
  32             "id": str,
  33             "widgets": validate.all(
  34                 [dict],
  35                 validate.filter(lambda item: item.get("mediaCollection")),
  36                 validate.get(0),
  37                 {
  38                     "geoblocked": bool,
  39                     "publicationService": {
  40                         "name": str,
  41                     },
  42                     "title": str,
  43                     "mediaCollection": {
  44                         "embedded": {
  45                             "_mediaArray": [{
  46                                 "_mediaStreamArray": [{
  47                                     "_quality": validate.any(str, int),
  48                                     "_stream": validate.url()
  49                                 }]
  50                             }]
  51                         }
  52                     }
  53                 }
  54             )
  55         })
  56         data = schema_data.validate(data_json)
  57 
  58         log.debug(f"Found media id: {data['id']}")
  59         data_media = data["widgets"]
  60 
  61         if data_media["geoblocked"]:
  62             log.info("The content is not available in your region")
  63             return
  64 
  65         self.author = data_media["publicationService"]["name"]
  66         self.title = data_media["title"]
  67 
  68         for media in data_media["mediaCollection"]["embedded"]["_mediaArray"]:
  69             for stream in media["_mediaStreamArray"]:
  70                 if stream["_quality"] != "auto" or ".m3u8" not in stream["_stream"]:
  71                     continue
  72                 return HLSStream.parse_variant_playlist(self.session, stream["_stream"])
  73 
  74 
  75 __plugin__ = ARDMediathek
  76
  [end of src/streamlink/plugins/ard_mediathek.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/streamlink/plugins/ard_mediathek.py b/src/streamlink/plugins/ard_mediathek.py
  --- a/src/streamlink/plugins/ard_mediathek.py
  +++ b/src/streamlink/plugins/ard_mediathek.py
  @@ -4,6 +4,7 @@
   from streamlink.plugin import Plugin, pluginmatcher
   from streamlink.plugin.api import validate
   from streamlink.stream.hls import HLSStream
  +from streamlink.stream.http import HTTPStream
   
   
   log = logging.getLogger(__name__)
  @@ -13,6 +14,14 @@
       r"https?://(?:(\w+\.)?ardmediathek\.de/|mediathek\.daserste\.de/)"
   ))
   class ARDMediathek(Plugin):
  +    _QUALITY_MAP = {
  +        4: "1080p",
  +        3: "720p",
  +        2: "540p",
  +        1: "360p",
  +        0: "270p"
  +    }
  +
       def _get_streams(self):
           data_json = self.session.http.get(self.url, schema=validate.Schema(
               validate.parse_html(),
  @@ -34,42 +43,64 @@
                   [dict],
                   validate.filter(lambda item: item.get("mediaCollection")),
                   validate.get(0),
  -                {
  -                    "geoblocked": bool,
  -                    "publicationService": {
  -                        "name": str,
  +                validate.any(None, validate.all(
  +                    {
  +                        "geoblocked": bool,
  +                        "publicationService": {
  +                            "name": str,
  +                        },
  +                        "show": validate.any(None, validate.all(
  +                            {"title": str},
  +                            validate.get("title")
  +                        )),
  +                        "title": str,
  +                        "mediaCollection": {
  +                            "embedded": {
  +                                "_mediaArray": [validate.all(
  +                                    {
  +                                        "_mediaStreamArray": [validate.all(
  +                                            {
  +                                                "_quality": validate.any(str, int),
  +                                                "_stream": validate.url(),
  +                                            },
  +                                            validate.union_get("_quality", "_stream")
  +                                        )]
  +                                    },
  +                                    validate.get("_mediaStreamArray"),
  +                                    validate.transform(dict)
  +                                )]
  +                            }
  +                        },
                       },
  -                    "title": str,
  -                    "mediaCollection": {
  -                        "embedded": {
  -                            "_mediaArray": [{
  -                                "_mediaStreamArray": [{
  -                                    "_quality": validate.any(str, int),
  -                                    "_stream": validate.url()
  -                                }]
  -                            }]
  -                        }
  -                    }
  -                }
  +                    validate.union_get(
  +                        "geoblocked",
  +                        ("mediaCollection", "embedded", "_mediaArray", 0),
  +                        ("publicationService", "name"),
  +                        "title",
  +                        "show",
  +                    )
  +                ))
               )
           })
           data = schema_data.validate(data_json)
   
           log.debug(f"Found media id: {data['id']}")
  -        data_media = data["widgets"]
  +        if not data["widgets"]:
  +            log.info("The content is unavailable")
  +            return
   
  -        if data_media["geoblocked"]:
  +        geoblocked, media, self.author, self.title, show = data["widgets"]
  +        if geoblocked:
               log.info("The content is not available in your region")
               return
  +        if show:
  +            self.title = f"{show}: {self.title}"
   
  -        self.author = data_media["publicationService"]["name"]
  -        self.title = data_media["title"]
  -
  -        for media in data_media["mediaCollection"]["embedded"]["_mediaArray"]:
  -            for stream in media["_mediaStreamArray"]:
  -                if stream["_quality"] != "auto" or ".m3u8" not in stream["_stream"]:
  -                    continue
  -                return HLSStream.parse_variant_playlist(self.session, stream["_stream"])
  +        if media.get("auto"):
  +            yield from HLSStream.parse_variant_playlist(self.session, media.get("auto")).items()
  +        else:
  +            for quality, stream in media.items():
  +                yield self._QUALITY_MAP.get(quality, quality), HTTPStream(self.session, stream)
   
   
   __plugin__ = ARDMediathek
  
verification_info: {"golden_diff": "diff --git a/src/streamlink/plugins/ard_mediathek.py b/src/streamlink/plugins/ard_mediathek.py\n--- a/src/streamlink/plugins/ard_mediathek.py\n+++ b/src/streamlink/plugins/ard_mediathek.py\n@@ -4,6 +4,7 @@\n from streamlink.plugin import Plugin, pluginmatcher\n from streamlink.plugin.api import validate\n from streamlink.stream.hls import HLSStream\n+from streamlink.stream.http import HTTPStream\n \n \n log = logging.getLogger(__name__)\n@@ -13,6 +14,14 @@\n     r\"https?://(?:(\\w+\\.)?ardmediathek\\.de/|mediathek\\.daserste\\.de/)\"\n ))\n class ARDMediathek(Plugin):\n+    _QUALITY_MAP = {\n+        4: \"1080p\",\n+        3: \"720p\",\n+        2: \"540p\",\n+        1: \"360p\",\n+        0: \"270p\"\n+    }\n+\n     def _get_streams(self):\n         data_json = self.session.http.get(self.url, schema=validate.Schema(\n             validate.parse_html(),\n@@ -34,42 +43,64 @@\n                 [dict],\n                 validate.filter(lambda item: item.get(\"mediaCollection\")),\n                 validate.get(0),\n-                {\n-                    \"geoblocked\": bool,\n-                    \"publicationService\": {\n-                        \"name\": str,\n+                validate.any(None, validate.all(\n+                    {\n+                        \"geoblocked\": bool,\n+                        \"publicationService\": {\n+                            \"name\": str,\n+                        },\n+                        \"show\": validate.any(None, validate.all(\n+                            {\"title\": str},\n+                            validate.get(\"title\")\n+                        )),\n+                        \"title\": str,\n+                        \"mediaCollection\": {\n+                            \"embedded\": {\n+                                \"_mediaArray\": [validate.all(\n+                                    {\n+                                        \"_mediaStreamArray\": [validate.all(\n+                                            {\n+                                                \"_quality\": validate.any(str, int),\n+                                                \"_stream\": validate.url(),\n+                                            },\n+                                            validate.union_get(\"_quality\", \"_stream\")\n+                                        )]\n+                                    },\n+                                    validate.get(\"_mediaStreamArray\"),\n+                                    validate.transform(dict)\n+                                )]\n+                            }\n+                        },\n                     },\n-                    \"title\": str,\n-                    \"mediaCollection\": {\n-                        \"embedded\": {\n-                            \"_mediaArray\": [{\n-                                \"_mediaStreamArray\": [{\n-                                    \"_quality\": validate.any(str, int),\n-                                    \"_stream\": validate.url()\n-                                }]\n-                            }]\n-                        }\n-                    }\n-                }\n+                    validate.union_get(\n+                        \"geoblocked\",\n+                        (\"mediaCollection\", \"embedded\", \"_mediaArray\", 0),\n+                        (\"publicationService\", \"name\"),\n+                        \"title\",\n+                        \"show\",\n+                    )\n+                ))\n             )\n         })\n         data = schema_data.validate(data_json)\n \n         log.debug(f\"Found media id: {data['id']}\")\n-        data_media = data[\"widgets\"]\n+        if not data[\"widgets\"]:\n+            log.info(\"The content is unavailable\")\n+            return\n \n-        if data_media[\"geoblocked\"]:\n+        geoblocked, media, self.author, self.title, show = data[\"widgets\"]\n+        if geoblocked:\n             log.info(\"The content is not available in your region\")\n             return\n+        if show:\n+            self.title = f\"{show}: {self.title}\"\n \n-        self.author = data_media[\"publicationService\"][\"name\"]\n-        self.title = data_media[\"title\"]\n-\n-        for media in data_media[\"mediaCollection\"][\"embedded\"][\"_mediaArray\"]:\n-            for stream in media[\"_mediaStreamArray\"]:\n-                if stream[\"_quality\"] != \"auto\" or \".m3u8\" not in stream[\"_stream\"]:\n-                    continue\n-                return HLSStream.parse_variant_playlist(self.session, stream[\"_stream\"])\n+        if media.get(\"auto\"):\n+            yield from HLSStream.parse_variant_playlist(self.session, media.get(\"auto\")).items()\n+        else:\n+            for quality, stream in media.items():\n+                yield self._QUALITY_MAP.get(quality, quality), HTTPStream(self.session, stream)\n \n \n __plugin__ = ARDMediathek\n", "issue": "plugins.ard_mediathek: rewrite plugin\nResolves #4191 \r\n\r\nOne issue I couldn't fix is the text encoding of the metadata which gets messed up by `validate.parse_html()`. See the VOD title down below...\r\nhttps://github.com/streamlink/streamlink/blob/175d4748561c7154bb80c5a47dae22039e45d4ce/src/streamlink/utils/parse.py#L54-L55\r\n\r\nSome VODs also have a second title, eg. if it's a TV show, but I couldn't be bothered to implement this. Not important.\r\n\r\n----\r\n\r\nDas Erste - Live:\r\n```\r\n$ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/daserste/live/Y3JpZDovL2Rhc2Vyc3RlLmRlL0xpdmVzdHJlYW0tRGFzRXJzdGU/' best\r\n[cli.output][debug] Opening subprocess: mpv \"--force-media-title=Das Erste - Das Erste\" -\r\n```\r\n\r\nWDR - Live:\r\n```\r\n$ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/live/Y3JpZDovL3dkci5kZS9CZWl0cmFnLTNkYTY2NGRlLTE4YzItNDY1MC1hNGZmLTRmNjQxNDcyMDcyYg/' best\r\n[cli.output][debug] Opening subprocess: mpv \"--force-media-title=WDR - WDR Fernsehen im Livestream\" -\r\n```\r\n\r\nVOD\r\n```\r\n$ streamlink -l debug --title '{author} - {title}' 'https://www.ardmediathek.de/video/dokus-im-ersten/wirecard-die-milliarden-luege/das-erste/Y3JpZDovL2Rhc2Vyc3RlLmRlL3JlcG9ydGFnZSBfIGRva3VtZW50YXRpb24gaW0gZXJzdGVuL2NlMjQ0OWM4LTQ4YTUtNGIyNC1iMTdlLWNhOTNjMDQ5OTc4Zg/' best\r\n[cli.output][debug] Opening subprocess: mpv \"--force-media-title=Das Erste - Wirecard - Die Milliarden-L\u00c3\u00bcge\" -\r\n```\n", "before_files": [{"content": "import logging\nimport re\n\nfrom streamlink.plugin import Plugin, pluginmatcher\nfrom streamlink.plugin.api import validate\nfrom streamlink.stream.hls import HLSStream\n\n\nlog = logging.getLogger(__name__)\n\n\n@pluginmatcher(re.compile(\n    r\"https?://(?:(\\w+\\.)?ardmediathek\\.de/|mediathek\\.daserste\\.de/)\"\n))\nclass ARDMediathek(Plugin):\n    def _get_streams(self):\n        data_json = self.session.http.get(self.url, schema=validate.Schema(\n            validate.parse_html(),\n            validate.xml_findtext(\".//script[@id='fetchedContextValue'][@type='application/json']\"),\n            validate.any(None, validate.all(\n                validate.parse_json(),\n                {str: dict},\n                validate.transform(lambda obj: list(obj.items())),\n                validate.filter(lambda item: item[0].startswith(\"https://api.ardmediathek.de/page-gateway/pages/\")),\n                validate.any(validate.get((0, 1)), [])\n            ))\n        ))\n        if not data_json:\n            return\n\n        schema_data = validate.Schema({\n            \"id\": str,\n            \"widgets\": validate.all(\n                [dict],\n                validate.filter(lambda item: item.get(\"mediaCollection\")),\n                validate.get(0),\n                {\n                    \"geoblocked\": bool,\n                    \"publicationService\": {\n                        \"name\": str,\n                    },\n                    \"title\": str,\n                    \"mediaCollection\": {\n                        \"embedded\": {\n                            \"_mediaArray\": [{\n                                \"_mediaStreamArray\": [{\n                                    \"_quality\": validate.any(str, int),\n                                    \"_stream\": validate.url()\n                                }]\n                            }]\n                        }\n                    }\n                }\n            )\n        })\n        data = schema_data.validate(data_json)\n\n        log.debug(f\"Found media id: {data['id']}\")\n        data_media = data[\"widgets\"]\n\n        if data_media[\"geoblocked\"]:\n            log.info(\"The content is not available in your region\")\n            return\n\n        self.author = data_media[\"publicationService\"][\"name\"]\n        self.title = data_media[\"title\"]\n\n        for media in data_media[\"mediaCollection\"][\"embedded\"][\"_mediaArray\"]:\n            for stream in media[\"_mediaStreamArray\"]:\n                if stream[\"_quality\"] != \"auto\" or \".m3u8\" not in stream[\"_stream\"]:\n                    continue\n                return HLSStream.parse_variant_playlist(self.session, stream[\"_stream\"])\n\n\n__plugin__ = ARDMediathek\n", "path": "src/streamlink/plugins/ard_mediathek.py"}]}