prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  plugins: add markunmatched
  ## Description
  
  Add a plugin useful for bulk importers.
  
  ## To Do
  
  - [x] Documentation. (If you've add a new command-line flag, for example, find the appropriate page under `docs/` to describe it.)
  - [x] Changelog. (Add an entry to `docs/changelog.rst` near the top of the document.)
  - [ ] Tests. (Encouraged but not strictly required.)
  
  
  </issue>
  <code>
  [start of beetsplug/mbsubmit.py]
  1 # This file is part of beets.
  2 # Copyright 2016, Adrian Sampson and Diego Moreda.
  3 #
  4 # Permission is hereby granted, free of charge, to any person obtaining
  5 # a copy of this software and associated documentation files (the
  6 # "Software"), to deal in the Software without restriction, including
  7 # without limitation the rights to use, copy, modify, merge, publish,
  8 # distribute, sublicense, and/or sell copies of the Software, and to
  9 # permit persons to whom the Software is furnished to do so, subject to
  10 # the following conditions:
  11 #
  12 # The above copyright notice and this permission notice shall be
  13 # included in all copies or substantial portions of the Software.
  14 
  15 """Aid in submitting information to MusicBrainz.
  16 
  17 This plugin allows the user to print track information in a format that is
  18 parseable by the MusicBrainz track parser [1]. Programmatic submitting is not
  19 implemented by MusicBrainz yet.
  20 
  21 [1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings
  22 """
  23 
  24 
  25 from beets import ui
  26 from beets.autotag import Recommendation
  27 from beets.plugins import BeetsPlugin
  28 from beets.ui.commands import PromptChoice
  29 from beetsplug.info import print_data
  30 
  31 
  32 class MBSubmitPlugin(BeetsPlugin):
  33     def __init__(self):
  34         super().__init__()
  35 
  36         self.config.add(
  37             {
  38                 "format": "$track. $title - $artist ($length)",
  39                 "threshold": "medium",
  40             }
  41         )
  42 
  43         # Validate and store threshold.
  44         self.threshold = self.config["threshold"].as_choice(
  45             {
  46                 "none": Recommendation.none,
  47                 "low": Recommendation.low,
  48                 "medium": Recommendation.medium,
  49                 "strong": Recommendation.strong,
  50             }
  51         )
  52 
  53         self.register_listener(
  54             "before_choose_candidate", self.before_choose_candidate_event
  55         )
  56 
  57     def before_choose_candidate_event(self, session, task):
  58         if task.rec <= self.threshold:
  59             return [PromptChoice("p", "Print tracks", self.print_tracks)]
  60 
  61     def print_tracks(self, session, task):
  62         for i in sorted(task.items, key=lambda i: i.track):
  63             print_data(None, i, self.config["format"].as_str())
  64 
  65     def commands(self):
  66         """Add beet UI commands for mbsubmit."""
  67         mbsubmit_cmd = ui.Subcommand(
  68             "mbsubmit", help="Submit Tracks to MusicBrainz"
  69         )
  70 
  71         def func(lib, opts, args):
  72             items = lib.items(ui.decargs(args))
  73             self._mbsubmit(items)
  74 
  75         mbsubmit_cmd.func = func
  76 
  77         return [mbsubmit_cmd]
  78 
  79     def _mbsubmit(self, items):
  80         """Print track information to be submitted to MusicBrainz."""
  81         for i in sorted(items, key=lambda i: i.track):
  82             print_data(None, i, self.config["format"].as_str())
  83
  [end of beetsplug/mbsubmit.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/beetsplug/mbsubmit.py b/beetsplug/mbsubmit.py
  --- a/beetsplug/mbsubmit.py
  +++ b/beetsplug/mbsubmit.py
  @@ -21,11 +21,13 @@
   [1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings
   """
   
  +import subprocess
   
   from beets import ui
   from beets.autotag import Recommendation
   from beets.plugins import BeetsPlugin
   from beets.ui.commands import PromptChoice
  +from beets.util import displayable_path
   from beetsplug.info import print_data
   
   
  @@ -37,6 +39,7 @@
               {
                   "format": "$track. $title - $artist ($length)",
                   "threshold": "medium",
  +                "picard_path": "picard",
               }
           )
   
  @@ -56,7 +59,21 @@
   
       def before_choose_candidate_event(self, session, task):
           if task.rec <= self.threshold:
  -            return [PromptChoice("p", "Print tracks", self.print_tracks)]
  +            return [
  +                PromptChoice("p", "Print tracks", self.print_tracks),
  +                PromptChoice("o", "Open files with Picard", self.picard),
  +            ]
  +
  +    def picard(self, session, task):
  +        paths = []
  +        for p in task.paths:
  +            paths.append(displayable_path(p))
  +        try:
  +            picard_path = self.config["picard_path"].as_str()
  +            subprocess.Popen([picard_path] + paths)
  +            self._log.info("launched picard from\n{}", picard_path)
  +        except OSError as exc:
  +            self._log.error(f"Could not open picard, got error:\n{exc}")
   
       def print_tracks(self, session, task):
           for i in sorted(task.items, key=lambda i: i.track):
  
verification_info: {"golden_diff": "diff --git a/beetsplug/mbsubmit.py b/beetsplug/mbsubmit.py\n--- a/beetsplug/mbsubmit.py\n+++ b/beetsplug/mbsubmit.py\n@@ -21,11 +21,13 @@\n [1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings\n \"\"\"\n \n+import subprocess\n \n from beets import ui\n from beets.autotag import Recommendation\n from beets.plugins import BeetsPlugin\n from beets.ui.commands import PromptChoice\n+from beets.util import displayable_path\n from beetsplug.info import print_data\n \n \n@@ -37,6 +39,7 @@\n             {\n                 \"format\": \"$track. $title - $artist ($length)\",\n                 \"threshold\": \"medium\",\n+                \"picard_path\": \"picard\",\n             }\n         )\n \n@@ -56,7 +59,21 @@\n \n     def before_choose_candidate_event(self, session, task):\n         if task.rec <= self.threshold:\n-            return [PromptChoice(\"p\", \"Print tracks\", self.print_tracks)]\n+            return [\n+                PromptChoice(\"p\", \"Print tracks\", self.print_tracks),\n+                PromptChoice(\"o\", \"Open files with Picard\", self.picard),\n+            ]\n+\n+    def picard(self, session, task):\n+        paths = []\n+        for p in task.paths:\n+            paths.append(displayable_path(p))\n+        try:\n+            picard_path = self.config[\"picard_path\"].as_str()\n+            subprocess.Popen([picard_path] + paths)\n+            self._log.info(\"launched picard from\\n{}\", picard_path)\n+        except OSError as exc:\n+            self._log.error(f\"Could not open picard, got error:\\n{exc}\")\n \n     def print_tracks(self, session, task):\n         for i in sorted(task.items, key=lambda i: i.track):\n", "issue": "plugins: add markunmatched\n## Description\r\n\r\nAdd a plugin useful for bulk importers.\r\n\r\n## To Do\r\n\r\n- [x] Documentation. (If you've add a new command-line flag, for example, find the appropriate page under `docs/` to describe it.)\r\n- [x] Changelog. (Add an entry to `docs/changelog.rst` near the top of the document.)\r\n- [ ] Tests. (Encouraged but not strictly required.)\r\n\n", "before_files": [{"content": "# This file is part of beets.\n# Copyright 2016, Adrian Sampson and Diego Moreda.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n\"\"\"Aid in submitting information to MusicBrainz.\n\nThis plugin allows the user to print track information in a format that is\nparseable by the MusicBrainz track parser [1]. Programmatic submitting is not\nimplemented by MusicBrainz yet.\n\n[1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings\n\"\"\"\n\n\nfrom beets import ui\nfrom beets.autotag import Recommendation\nfrom beets.plugins import BeetsPlugin\nfrom beets.ui.commands import PromptChoice\nfrom beetsplug.info import print_data\n\n\nclass MBSubmitPlugin(BeetsPlugin):\n    def __init__(self):\n        super().__init__()\n\n        self.config.add(\n            {\n                \"format\": \"$track. $title - $artist ($length)\",\n                \"threshold\": \"medium\",\n            }\n        )\n\n        # Validate and store threshold.\n        self.threshold = self.config[\"threshold\"].as_choice(\n            {\n                \"none\": Recommendation.none,\n                \"low\": Recommendation.low,\n                \"medium\": Recommendation.medium,\n                \"strong\": Recommendation.strong,\n            }\n        )\n\n        self.register_listener(\n            \"before_choose_candidate\", self.before_choose_candidate_event\n        )\n\n    def before_choose_candidate_event(self, session, task):\n        if task.rec <= self.threshold:\n            return [PromptChoice(\"p\", \"Print tracks\", self.print_tracks)]\n\n    def print_tracks(self, session, task):\n        for i in sorted(task.items, key=lambda i: i.track):\n            print_data(None, i, self.config[\"format\"].as_str())\n\n    def commands(self):\n        \"\"\"Add beet UI commands for mbsubmit.\"\"\"\n        mbsubmit_cmd = ui.Subcommand(\n            \"mbsubmit\", help=\"Submit Tracks to MusicBrainz\"\n        )\n\n        def func(lib, opts, args):\n            items = lib.items(ui.decargs(args))\n            self._mbsubmit(items)\n\n        mbsubmit_cmd.func = func\n\n        return [mbsubmit_cmd]\n\n    def _mbsubmit(self, items):\n        \"\"\"Print track information to be submitted to MusicBrainz.\"\"\"\n        for i in sorted(items, key=lambda i: i.track):\n            print_data(None, i, self.config[\"format\"].as_str())\n", "path": "beetsplug/mbsubmit.py"}]}