|
29 | 29 | from beets import ui
|
30 | 30 | from beets import util
|
31 | 31 | from beets import config
|
| 32 | +from beets.mediafile import _image_mime_type |
32 | 33 | from beets.util.artresizer import ArtResizer
|
33 | 34 | from beets.util import confit
|
34 | 35 | from beets.util import syspath, bytestring_path
|
@@ -231,21 +232,39 @@ def fetch_image(self, candidate, extra):
|
231 | 232 | message=u'downloading image')) as resp:
|
232 | 233 | ct = resp.headers.get('Content-Type', None)
|
233 | 234 | if ct not in CONTENT_TYPES:
|
234 |
| - self._log.debug( |
235 |
| - u'not a supported image: {}', |
236 |
| - resp.headers.get('Content-Type') or u'no content type', |
237 |
| - ) |
| 235 | + self._log.debug(u'not a supported image: {}', |
| 236 | + ct or u'no content type') |
238 | 237 | candidate.path = None
|
239 | 238 | return
|
240 | 239 |
|
241 |
| - # Generate a temporary file with the correct extension. |
242 |
| - with NamedTemporaryFile(suffix=b'.' + CONTENT_TYPES[ct][0], |
243 |
| - delete=False) as fh: |
244 |
| - for chunk in resp.iter_content(chunk_size=1024): |
| 240 | + # Generate a temporary file and guess the extension based on |
| 241 | + # the Content-Type header. This may be wrong for badly |
| 242 | + # configured servers. E.g. fanart.tv only allows .jpg uploads |
| 243 | + # and ALWAYS returns a image/jpeg Content-Type, but other |
| 244 | + # formats with a erroneous .jp(e)g extension apparently can |
| 245 | + # sneak through the upload filter. Therefore validate the type |
| 246 | + # using the file magic. |
| 247 | + data = resp.iter_content(chunk_size=1024) |
| 248 | + try: |
| 249 | + chunk = next(data) |
| 250 | + except StopIteration: |
| 251 | + pass |
| 252 | + else: |
| 253 | + real_ct = _image_mime_type(chunk) |
| 254 | + ext = b'.' + CONTENT_TYPES[real_ct][0] |
| 255 | + if real_ct != ct: |
| 256 | + self._log.warn(u'Server specified {}, but returned a ' |
| 257 | + u'{} image. Correcting the extension ' |
| 258 | + u'to {}', |
| 259 | + ct, real_ct, ext) |
| 260 | + with NamedTemporaryFile(suffix=ext, delete=False) as fh: |
245 | 261 | fh.write(chunk)
|
246 |
| - self._log.debug(u'downloaded art to: {0}', |
247 |
| - util.displayable_path(fh.name)) |
248 |
| - candidate.path = util.bytestring_path(fh.name) |
| 262 | + for chunk in data: |
| 263 | + fh.write(chunk) |
| 264 | + self._log.debug(u'downloaded art to: {0}', |
| 265 | + util.displayable_path(fh.name)) |
| 266 | + candidate.path = util.bytestring_path(fh.name) |
| 267 | + |
249 | 268 | return
|
250 | 269 |
|
251 | 270 | except (IOError, requests.RequestException, TypeError) as exc:
|
|
0 commit comments