We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ImageMagick applies maxwidth to longest edge instead of to width
The [current invocation](https://github.com/beetbox/beets/blob/2120cf68c61649c22c14f20d83bd28d758720557/beets/util/artresizer.py#L96-L100) of ImageMagick applies the ```maxwidth``` parameter of ```fetchart``` to an image's longer edge instead of to its width. Possible solution suggested by Adrian [on the forum](https://discourse.beets.io/t/fetchart-taming-cover-art-resolution/206/12?u=dorade).




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `beets/util/artresizer.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # This file is part of beets.
3 # Copyright 2016, Fabrice Laporte
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 
16 """Abstraction layer to resize images using PIL, ImageMagick, or a
17 public resizing proxy if neither is available.
18 """
19 from __future__ import division, absolute_import, print_function
20 
21 import subprocess
22 import os
23 import re
24 from tempfile import NamedTemporaryFile
25 from six.moves.urllib.parse import urlencode
26 from beets import logging
27 from beets import util
28 import six
29 
30 # Resizing methods
31 PIL = 1
32 IMAGEMAGICK = 2
33 WEBPROXY = 3
34 
35 if util.SNI_SUPPORTED:
36     PROXY_URL = 'https://images.weserv.nl/'
37 else:
38     PROXY_URL = 'http://images.weserv.nl/'
39 
40 log = logging.getLogger('beets')
41 
42 
43 def resize_url(url, maxwidth):
44     """Return a proxied image URL that resizes the original image to
45     maxwidth (preserving aspect ratio).
46     """
47     return '{0}?{1}'.format(PROXY_URL, urlencode({
48         'url': url.replace('http://', ''),
49         'w': maxwidth,
50     }))
51 
52 
53 def temp_file_for(path):
54     """Return an unused filename with the same extension as the
55     specified path.
56     """
57     ext = os.path.splitext(path)[1]
58     with NamedTemporaryFile(suffix=util.py3_path(ext), delete=False) as f:
59         return util.bytestring_path(f.name)
60 
61 
62 def pil_resize(maxwidth, path_in, path_out=None):
63     """Resize using Python Imaging Library (PIL).  Return the output path
64     of resized image.
65     """
66     path_out = path_out or temp_file_for(path_in)
67     from PIL import Image
68     log.debug(u'artresizer: PIL resizing {0} to {1}',
69               util.displayable_path(path_in), util.displayable_path(path_out))
70 
71     try:
72         im = Image.open(util.syspath(path_in))
73         size = maxwidth, maxwidth
74         im.thumbnail(size, Image.ANTIALIAS)
75         im.save(path_out)
76         return path_out
77     except IOError:
78         log.error(u"PIL cannot create thumbnail for '{0}'",
79                   util.displayable_path(path_in))
80         return path_in
81 
82 
83 def im_resize(maxwidth, path_in, path_out=None):
84     """Resize using ImageMagick's ``convert`` tool.
85     Return the output path of resized image.
86     """
87     path_out = path_out or temp_file_for(path_in)
88     log.debug(u'artresizer: ImageMagick resizing {0} to {1}',
89               util.displayable_path(path_in), util.displayable_path(path_out))
90 
91     # "-resize widthxheight>" shrinks images with dimension(s) larger
92     # than the corresponding width and/or height dimension(s). The >
93     # "only shrink" flag is prefixed by ^ escape char for Windows
94     # compatibility.
95     try:
96         util.command_output([
97             'convert', util.syspath(path_in, prefix=False),
98             '-resize', '{0}x^>'.format(maxwidth),
99             util.syspath(path_out, prefix=False),
100         ])
101     except subprocess.CalledProcessError:
102         log.warning(u'artresizer: IM convert failed for {0}',
103                     util.displayable_path(path_in))
104         return path_in
105     return path_out
106 
107 
108 BACKEND_FUNCS = {
109     PIL: pil_resize,
110     IMAGEMAGICK: im_resize,
111 }
112 
113 
114 def pil_getsize(path_in):
115     from PIL import Image
116     try:
117         im = Image.open(util.syspath(path_in))
118         return im.size
119     except IOError as exc:
120         log.error(u"PIL could not read file {}: {}",
121                   util.displayable_path(path_in), exc)
122 
123 
124 def im_getsize(path_in):
125     cmd = ['identify', '-format', '%w %h',
126            util.syspath(path_in, prefix=False)]
127     try:
128         out = util.command_output(cmd)
129     except subprocess.CalledProcessError as exc:
130         log.warning(u'ImageMagick size query failed')
131         log.debug(
132             u'`convert` exited with (status {}) when '
133             u'getting size with command {}:\n{}',
134             exc.returncode, cmd, exc.output.strip()
135         )
136         return
137     try:
138         return tuple(map(int, out.split(b' ')))
139     except IndexError:
140         log.warning(u'Could not understand IM output: {0!r}', out)
141 
142 
143 BACKEND_GET_SIZE = {
144     PIL: pil_getsize,
145     IMAGEMAGICK: im_getsize,
146 }
147 
148 
149 class Shareable(type):
150     """A pseudo-singleton metaclass that allows both shared and
151     non-shared instances. The ``MyClass.shared`` property holds a
152     lazily-created shared instance of ``MyClass`` while calling
153     ``MyClass()`` to construct a new object works as usual.
154     """
155     def __init__(self, name, bases, dict):
156         super(Shareable, self).__init__(name, bases, dict)
157         self._instance = None
158 
159     @property
160     def shared(self):
161         if self._instance is None:
162             self._instance = self()
163         return self._instance
164 
165 
166 class ArtResizer(six.with_metaclass(Shareable, object)):
167     """A singleton class that performs image resizes.
168     """
169 
170     def __init__(self):
171         """Create a resizer object with an inferred method.
172         """
173         self.method = self._check_method()
174         log.debug(u"artresizer: method is {0}", self.method)
175         self.can_compare = self._can_compare()
176 
177     def resize(self, maxwidth, path_in, path_out=None):
178         """Manipulate an image file according to the method, returning a
179         new path. For PIL or IMAGEMAGIC methods, resizes the image to a
180         temporary file. For WEBPROXY, returns `path_in` unmodified.
181         """
182         if self.local:
183             func = BACKEND_FUNCS[self.method[0]]
184             return func(maxwidth, path_in, path_out)
185         else:
186             return path_in
187 
188     def proxy_url(self, maxwidth, url):
189         """Modifies an image URL according the method, returning a new
190         URL. For WEBPROXY, a URL on the proxy server is returned.
191         Otherwise, the URL is returned unmodified.
192         """
193         if self.local:
194             return url
195         else:
196             return resize_url(url, maxwidth)
197 
198     @property
199     def local(self):
200         """A boolean indicating whether the resizing method is performed
201         locally (i.e., PIL or ImageMagick).
202         """
203         return self.method[0] in BACKEND_FUNCS
204 
205     def get_size(self, path_in):
206         """Return the size of an image file as an int couple (width, height)
207         in pixels.
208 
209         Only available locally
210         """
211         if self.local:
212             func = BACKEND_GET_SIZE[self.method[0]]
213             return func(path_in)
214 
215     def _can_compare(self):
216         """A boolean indicating whether image comparison is available"""
217 
218         return self.method[0] == IMAGEMAGICK and self.method[1] > (6, 8, 7)
219 
220     @staticmethod
221     def _check_method():
222         """Return a tuple indicating an available method and its version."""
223         version = get_im_version()
224         if version:
225             return IMAGEMAGICK, version
226 
227         version = get_pil_version()
228         if version:
229             return PIL, version
230 
231         return WEBPROXY, (0)
232 
233 
234 def get_im_version():
235     """Return Image Magick version or None if it is unavailable
236     Try invoking ImageMagick's "convert".
237     """
238     try:
239         out = util.command_output(['convert', '--version'])
240 
241         if b'imagemagick' in out.lower():
242             pattern = br".+ (\d+)\.(\d+)\.(\d+).*"
243             match = re.search(pattern, out)
244             if match:
245                 return (int(match.group(1)),
246                         int(match.group(2)),
247                         int(match.group(3)))
248             return (0,)
249 
250     except (subprocess.CalledProcessError, OSError) as exc:
251         log.debug(u'ImageMagick check `convert --version` failed: {}', exc)
252         return None
253 
254 
255 def get_pil_version():
256     """Return Image Magick version or None if it is unavailable
257     Try importing PIL."""
258     try:
259         __import__('PIL', fromlist=[str('Image')])
260         return (0,)
261     except ImportError:
262         return None
263
```
--- 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/beets/util/artresizer.py b/beets/util/artresizer.py
--- a/beets/util/artresizer.py
+++ b/beets/util/artresizer.py
@@ -88,14 +88,13 @@
     log.debug(u'artresizer: ImageMagick resizing {0} to {1}',
               util.displayable_path(path_in), util.displayable_path(path_out))
 
-    # "-resize widthxheight>" shrinks images with dimension(s) larger
-    # than the corresponding width and/or height dimension(s). The >
-    # "only shrink" flag is prefixed by ^ escape char for Windows
-    # compatibility.
+    # "-resize WIDTHx>" shrinks images with the width larger
+    # than the given width while maintaining the aspect ratio
+    # with regards to the height.
     try:
         util.command_output([
             'convert', util.syspath(path_in, prefix=False),
-            '-resize', '{0}x^>'.format(maxwidth),
+            '-resize', '{0}x>'.format(maxwidth),
             util.syspath(path_out, prefix=False),
         ])
     except subprocess.CalledProcessError:

{"golden_diff": "diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py\n--- a/beets/util/artresizer.py\n+++ b/beets/util/artresizer.py\n@@ -88,14 +88,13 @@\n     log.debug(u'artresizer: ImageMagick resizing {0} to {1}',\n               util.displayable_path(path_in), util.displayable_path(path_out))\n \n-    # \"-resize widthxheight>\" shrinks images with dimension(s) larger\n-    # than the corresponding width and/or height dimension(s). The >\n-    # \"only shrink\" flag is prefixed by ^ escape char for Windows\n-    # compatibility.\n+    # \"-resize WIDTHx>\" shrinks images with the width larger\n+    # than the given width while maintaining the aspect ratio\n+    # with regards to the height.\n     try:\n         util.command_output([\n             'convert', util.syspath(path_in, prefix=False),\n-            '-resize', '{0}x^>'.format(maxwidth),\n+            '-resize', '{0}x>'.format(maxwidth),\n             util.syspath(path_out, prefix=False),\n         ])\n     except subprocess.CalledProcessError:\n", "issue": "ImageMagick applies maxwidth to longest edge instead of to width\nThe [current invocation](https://github.com/beetbox/beets/blob/2120cf68c61649c22c14f20d83bd28d758720557/beets/util/artresizer.py#L96-L100) of ImageMagick applies the ```maxwidth``` parameter of ```fetchart``` to an image's longer edge instead of to its width. Possible solution suggested by Adrian [on the forum](https://discourse.beets.io/t/fetchart-taming-cover-art-resolution/206/12?u=dorade).\r\n\r\n\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# This file is part of beets.\n# Copyright 2016, Fabrice Laporte\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\"\"\"Abstraction layer to resize images using PIL, ImageMagick, or a\npublic resizing proxy if neither is available.\n\"\"\"\nfrom __future__ import division, absolute_import, print_function\n\nimport subprocess\nimport os\nimport re\nfrom tempfile import NamedTemporaryFile\nfrom six.moves.urllib.parse import urlencode\nfrom beets import logging\nfrom beets import util\nimport six\n\n# Resizing methods\nPIL = 1\nIMAGEMAGICK = 2\nWEBPROXY = 3\n\nif util.SNI_SUPPORTED:\n    PROXY_URL = 'https://images.weserv.nl/'\nelse:\n    PROXY_URL = 'http://images.weserv.nl/'\n\nlog = logging.getLogger('beets')\n\n\ndef resize_url(url, maxwidth):\n    \"\"\"Return a proxied image URL that resizes the original image to\n    maxwidth (preserving aspect ratio).\n    \"\"\"\n    return '{0}?{1}'.format(PROXY_URL, urlencode({\n        'url': url.replace('http://', ''),\n        'w': maxwidth,\n    }))\n\n\ndef temp_file_for(path):\n    \"\"\"Return an unused filename with the same extension as the\n    specified path.\n    \"\"\"\n    ext = os.path.splitext(path)[1]\n    with NamedTemporaryFile(suffix=util.py3_path(ext), delete=False) as f:\n        return util.bytestring_path(f.name)\n\n\ndef pil_resize(maxwidth, path_in, path_out=None):\n    \"\"\"Resize using Python Imaging Library (PIL).  Return the output path\n    of resized image.\n    \"\"\"\n    path_out = path_out or temp_file_for(path_in)\n    from PIL import Image\n    log.debug(u'artresizer: PIL resizing {0} to {1}',\n              util.displayable_path(path_in), util.displayable_path(path_out))\n\n    try:\n        im = Image.open(util.syspath(path_in))\n        size = maxwidth, maxwidth\n        im.thumbnail(size, Image.ANTIALIAS)\n        im.save(path_out)\n        return path_out\n    except IOError:\n        log.error(u\"PIL cannot create thumbnail for '{0}'\",\n                  util.displayable_path(path_in))\n        return path_in\n\n\ndef im_resize(maxwidth, path_in, path_out=None):\n    \"\"\"Resize using ImageMagick's ``convert`` tool.\n    Return the output path of resized image.\n    \"\"\"\n    path_out = path_out or temp_file_for(path_in)\n    log.debug(u'artresizer: ImageMagick resizing {0} to {1}',\n              util.displayable_path(path_in), util.displayable_path(path_out))\n\n    # \"-resize widthxheight>\" shrinks images with dimension(s) larger\n    # than the corresponding width and/or height dimension(s). The >\n    # \"only shrink\" flag is prefixed by ^ escape char for Windows\n    # compatibility.\n    try:\n        util.command_output([\n            'convert', util.syspath(path_in, prefix=False),\n            '-resize', '{0}x^>'.format(maxwidth),\n            util.syspath(path_out, prefix=False),\n        ])\n    except subprocess.CalledProcessError:\n        log.warning(u'artresizer: IM convert failed for {0}',\n                    util.displayable_path(path_in))\n        return path_in\n    return path_out\n\n\nBACKEND_FUNCS = {\n    PIL: pil_resize,\n    IMAGEMAGICK: im_resize,\n}\n\n\ndef pil_getsize(path_in):\n    from PIL import Image\n    try:\n        im = Image.open(util.syspath(path_in))\n        return im.size\n    except IOError as exc:\n        log.error(u\"PIL could not read file {}: {}\",\n                  util.displayable_path(path_in), exc)\n\n\ndef im_getsize(path_in):\n    cmd = ['identify', '-format', '%w %h',\n           util.syspath(path_in, prefix=False)]\n    try:\n        out = util.command_output(cmd)\n    except subprocess.CalledProcessError as exc:\n        log.warning(u'ImageMagick size query failed')\n        log.debug(\n            u'`convert` exited with (status {}) when '\n            u'getting size with command {}:\\n{}',\n            exc.returncode, cmd, exc.output.strip()\n        )\n        return\n    try:\n        return tuple(map(int, out.split(b' ')))\n    except IndexError:\n        log.warning(u'Could not understand IM output: {0!r}', out)\n\n\nBACKEND_GET_SIZE = {\n    PIL: pil_getsize,\n    IMAGEMAGICK: im_getsize,\n}\n\n\nclass Shareable(type):\n    \"\"\"A pseudo-singleton metaclass that allows both shared and\n    non-shared instances. The ``MyClass.shared`` property holds a\n    lazily-created shared instance of ``MyClass`` while calling\n    ``MyClass()`` to construct a new object works as usual.\n    \"\"\"\n    def __init__(self, name, bases, dict):\n        super(Shareable, self).__init__(name, bases, dict)\n        self._instance = None\n\n    @property\n    def shared(self):\n        if self._instance is None:\n            self._instance = self()\n        return self._instance\n\n\nclass ArtResizer(six.with_metaclass(Shareable, object)):\n    \"\"\"A singleton class that performs image resizes.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Create a resizer object with an inferred method.\n        \"\"\"\n        self.method = self._check_method()\n        log.debug(u\"artresizer: method is {0}\", self.method)\n        self.can_compare = self._can_compare()\n\n    def resize(self, maxwidth, path_in, path_out=None):\n        \"\"\"Manipulate an image file according to the method, returning a\n        new path. For PIL or IMAGEMAGIC methods, resizes the image to a\n        temporary file. For WEBPROXY, returns `path_in` unmodified.\n        \"\"\"\n        if self.local:\n            func = BACKEND_FUNCS[self.method[0]]\n            return func(maxwidth, path_in, path_out)\n        else:\n            return path_in\n\n    def proxy_url(self, maxwidth, url):\n        \"\"\"Modifies an image URL according the method, returning a new\n        URL. For WEBPROXY, a URL on the proxy server is returned.\n        Otherwise, the URL is returned unmodified.\n        \"\"\"\n        if self.local:\n            return url\n        else:\n            return resize_url(url, maxwidth)\n\n    @property\n    def local(self):\n        \"\"\"A boolean indicating whether the resizing method is performed\n        locally (i.e., PIL or ImageMagick).\n        \"\"\"\n        return self.method[0] in BACKEND_FUNCS\n\n    def get_size(self, path_in):\n        \"\"\"Return the size of an image file as an int couple (width, height)\n        in pixels.\n\n        Only available locally\n        \"\"\"\n        if self.local:\n            func = BACKEND_GET_SIZE[self.method[0]]\n            return func(path_in)\n\n    def _can_compare(self):\n        \"\"\"A boolean indicating whether image comparison is available\"\"\"\n\n        return self.method[0] == IMAGEMAGICK and self.method[1] > (6, 8, 7)\n\n    @staticmethod\n    def _check_method():\n        \"\"\"Return a tuple indicating an available method and its version.\"\"\"\n        version = get_im_version()\n        if version:\n            return IMAGEMAGICK, version\n\n        version = get_pil_version()\n        if version:\n            return PIL, version\n\n        return WEBPROXY, (0)\n\n\ndef get_im_version():\n    \"\"\"Return Image Magick version or None if it is unavailable\n    Try invoking ImageMagick's \"convert\".\n    \"\"\"\n    try:\n        out = util.command_output(['convert', '--version'])\n\n        if b'imagemagick' in out.lower():\n            pattern = br\".+ (\\d+)\\.(\\d+)\\.(\\d+).*\"\n            match = re.search(pattern, out)\n            if match:\n                return (int(match.group(1)),\n                        int(match.group(2)),\n                        int(match.group(3)))\n            return (0,)\n\n    except (subprocess.CalledProcessError, OSError) as exc:\n        log.debug(u'ImageMagick check `convert --version` failed: {}', exc)\n        return None\n\n\ndef get_pil_version():\n    \"\"\"Return Image Magick version or None if it is unavailable\n    Try importing PIL.\"\"\"\n    try:\n        __import__('PIL', fromlist=[str('Image')])\n        return (0,)\n    except ImportError:\n        return None\n", "path": "beets/util/artresizer.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# This file is part of beets.\n# Copyright 2016, Fabrice Laporte\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\"\"\"Abstraction layer to resize images using PIL, ImageMagick, or a\npublic resizing proxy if neither is available.\n\"\"\"\nfrom __future__ import division, absolute_import, print_function\n\nimport subprocess\nimport os\nimport re\nfrom tempfile import NamedTemporaryFile\nfrom six.moves.urllib.parse import urlencode\nfrom beets import logging\nfrom beets import util\nimport six\n\n# Resizing methods\nPIL = 1\nIMAGEMAGICK = 2\nWEBPROXY = 3\n\nif util.SNI_SUPPORTED:\n    PROXY_URL = 'https://images.weserv.nl/'\nelse:\n    PROXY_URL = 'http://images.weserv.nl/'\n\nlog = logging.getLogger('beets')\n\n\ndef resize_url(url, maxwidth):\n    \"\"\"Return a proxied image URL that resizes the original image to\n    maxwidth (preserving aspect ratio).\n    \"\"\"\n    return '{0}?{1}'.format(PROXY_URL, urlencode({\n        'url': url.replace('http://', ''),\n        'w': maxwidth,\n    }))\n\n\ndef temp_file_for(path):\n    \"\"\"Return an unused filename with the same extension as the\n    specified path.\n    \"\"\"\n    ext = os.path.splitext(path)[1]\n    with NamedTemporaryFile(suffix=util.py3_path(ext), delete=False) as f:\n        return util.bytestring_path(f.name)\n\n\ndef pil_resize(maxwidth, path_in, path_out=None):\n    \"\"\"Resize using Python Imaging Library (PIL).  Return the output path\n    of resized image.\n    \"\"\"\n    path_out = path_out or temp_file_for(path_in)\n    from PIL import Image\n    log.debug(u'artresizer: PIL resizing {0} to {1}',\n              util.displayable_path(path_in), util.displayable_path(path_out))\n\n    try:\n        im = Image.open(util.syspath(path_in))\n        size = maxwidth, maxwidth\n        im.thumbnail(size, Image.ANTIALIAS)\n        im.save(path_out)\n        return path_out\n    except IOError:\n        log.error(u\"PIL cannot create thumbnail for '{0}'\",\n                  util.displayable_path(path_in))\n        return path_in\n\n\ndef im_resize(maxwidth, path_in, path_out=None):\n    \"\"\"Resize using ImageMagick's ``convert`` tool.\n    Return the output path of resized image.\n    \"\"\"\n    path_out = path_out or temp_file_for(path_in)\n    log.debug(u'artresizer: ImageMagick resizing {0} to {1}',\n              util.displayable_path(path_in), util.displayable_path(path_out))\n\n    # \"-resize WIDTHx>\" shrinks images with the width larger\n    # than the given width while maintaining the aspect ratio\n    # with regards to the height.\n    try:\n        util.command_output([\n            'convert', util.syspath(path_in, prefix=False),\n            '-resize', '{0}x>'.format(maxwidth),\n            util.syspath(path_out, prefix=False),\n        ])\n    except subprocess.CalledProcessError:\n        log.warning(u'artresizer: IM convert failed for {0}',\n                    util.displayable_path(path_in))\n        return path_in\n    return path_out\n\n\nBACKEND_FUNCS = {\n    PIL: pil_resize,\n    IMAGEMAGICK: im_resize,\n}\n\n\ndef pil_getsize(path_in):\n    from PIL import Image\n    try:\n        im = Image.open(util.syspath(path_in))\n        return im.size\n    except IOError as exc:\n        log.error(u\"PIL could not read file {}: {}\",\n                  util.displayable_path(path_in), exc)\n\n\ndef im_getsize(path_in):\n    cmd = ['identify', '-format', '%w %h',\n           util.syspath(path_in, prefix=False)]\n    try:\n        out = util.command_output(cmd)\n    except subprocess.CalledProcessError as exc:\n        log.warning(u'ImageMagick size query failed')\n        log.debug(\n            u'`convert` exited with (status {}) when '\n            u'getting size with command {}:\\n{}',\n            exc.returncode, cmd, exc.output.strip()\n        )\n        return\n    try:\n        return tuple(map(int, out.split(b' ')))\n    except IndexError:\n        log.warning(u'Could not understand IM output: {0!r}', out)\n\n\nBACKEND_GET_SIZE = {\n    PIL: pil_getsize,\n    IMAGEMAGICK: im_getsize,\n}\n\n\nclass Shareable(type):\n    \"\"\"A pseudo-singleton metaclass that allows both shared and\n    non-shared instances. The ``MyClass.shared`` property holds a\n    lazily-created shared instance of ``MyClass`` while calling\n    ``MyClass()`` to construct a new object works as usual.\n    \"\"\"\n    def __init__(self, name, bases, dict):\n        super(Shareable, self).__init__(name, bases, dict)\n        self._instance = None\n\n    @property\n    def shared(self):\n        if self._instance is None:\n            self._instance = self()\n        return self._instance\n\n\nclass ArtResizer(six.with_metaclass(Shareable, object)):\n    \"\"\"A singleton class that performs image resizes.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Create a resizer object with an inferred method.\n        \"\"\"\n        self.method = self._check_method()\n        log.debug(u\"artresizer: method is {0}\", self.method)\n        self.can_compare = self._can_compare()\n\n    def resize(self, maxwidth, path_in, path_out=None):\n        \"\"\"Manipulate an image file according to the method, returning a\n        new path. For PIL or IMAGEMAGIC methods, resizes the image to a\n        temporary file. For WEBPROXY, returns `path_in` unmodified.\n        \"\"\"\n        if self.local:\n            func = BACKEND_FUNCS[self.method[0]]\n            return func(maxwidth, path_in, path_out)\n        else:\n            return path_in\n\n    def proxy_url(self, maxwidth, url):\n        \"\"\"Modifies an image URL according the method, returning a new\n        URL. For WEBPROXY, a URL on the proxy server is returned.\n        Otherwise, the URL is returned unmodified.\n        \"\"\"\n        if self.local:\n            return url\n        else:\n            return resize_url(url, maxwidth)\n\n    @property\n    def local(self):\n        \"\"\"A boolean indicating whether the resizing method is performed\n        locally (i.e., PIL or ImageMagick).\n        \"\"\"\n        return self.method[0] in BACKEND_FUNCS\n\n    def get_size(self, path_in):\n        \"\"\"Return the size of an image file as an int couple (width, height)\n        in pixels.\n\n        Only available locally\n        \"\"\"\n        if self.local:\n            func = BACKEND_GET_SIZE[self.method[0]]\n            return func(path_in)\n\n    def _can_compare(self):\n        \"\"\"A boolean indicating whether image comparison is available\"\"\"\n\n        return self.method[0] == IMAGEMAGICK and self.method[1] > (6, 8, 7)\n\n    @staticmethod\n    def _check_method():\n        \"\"\"Return a tuple indicating an available method and its version.\"\"\"\n        version = get_im_version()\n        if version:\n            return IMAGEMAGICK, version\n\n        version = get_pil_version()\n        if version:\n            return PIL, version\n\n        return WEBPROXY, (0)\n\n\ndef get_im_version():\n    \"\"\"Return Image Magick version or None if it is unavailable\n    Try invoking ImageMagick's \"convert\".\n    \"\"\"\n    try:\n        out = util.command_output(['convert', '--version'])\n\n        if b'imagemagick' in out.lower():\n            pattern = br\".+ (\\d+)\\.(\\d+)\\.(\\d+).*\"\n            match = re.search(pattern, out)\n            if match:\n                return (int(match.group(1)),\n                        int(match.group(2)),\n                        int(match.group(3)))\n            return (0,)\n\n    except (subprocess.CalledProcessError, OSError) as exc:\n        log.debug(u'ImageMagick check `convert --version` failed: {}', exc)\n        return None\n\n\ndef get_pil_version():\n    \"\"\"Return Image Magick version or None if it is unavailable\n    Try importing PIL.\"\"\"\n    try:\n        __import__('PIL', fromlist=[str('Image')])\n        return (0,)\n    except ImportError:\n        return None\n", "path": "beets/util/artresizer.py"}]}