Repository: beetbox/beets

Issue:
fetchart: Catch PIL file type exception (KeyError) when resizing art
### Problem

(Describe your problem, feature request, or discussion topic here. If you're reporting a bug, please fill out this and the "Setup" section below. Otherwise, you can delete them.)

Running this command in verbose (`-vv`) mode:

```sh
$ beet -vv fetchart -f albumartist:"KMFDM" album:"Amnesia"
user configuration: /home/mycah/.config/beets/config.yaml
data directory: /home/mycah/.config/beets
plugin paths:
Sending event: pluginload
inline: adding item field cdtitle
inline: adding item field multidisc
inline: adding item field initial
inline: adding item field nicelength
inline: adding album field cdtype
inline: adding album field notalbum
library database: /home/mmattox/.beets.db
library directory: /home/mmattox/convertedMusic
Sending event: library_opened
fetchart: trying source filesystem for album KMFDM - Amnesia
fetchart: trying source coverart for album KMFDM - Amnesia
ImageMagick check `convert --version` failed: [Errno 2] No such file or directory: 'convert'
artresizer: method is (1, (0,))
fetchart: downloading image: https://coverartarchive.org/release/b0682581-89f9-430a-8d89-8fc931107a41/front
fetchart: not a supported image: image/x-None
fetchart: downloading image: https://coverartarchive.org/release-group/c9a6bd02-3045-4e9c-a61d-3b41dd2fcf65/front
fetchart: downloaded art to: /tmp/tmp3i69k972.jpg
fetchart: image size: (700, 700)
fetchart: image needs resizing (700 > 600)
fetchart: using remote image /tmp/tmp3i69k972.jpg
artresizer: PIL resizing /tmp/tmp3i69k972.jpg to /tmp/tmpc9k_0qbc.jpg
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1447, in save
    format = EXTENSION[ext]
KeyError: b'.jpg'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1451, in save
    format = EXTENSION[ext]
KeyError: b'.jpg'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/beet", line 9, in <module>
    load_entry_point('beets==1.4.4', 'console_scripts', 'beet')()
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/ui/__init__.py", line 1221, in main
    _raw_main(args)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/ui/__init__.py", line 1208, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 829, in func
    self.batch_fetch_art(lib, lib.albums(ui.decargs(args)), opts.force)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 882, in batch_fetch_art
    candidate = self.art_for_album(album, local_paths)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 865, in art_for_album
    out.resize(self)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 143, in resize
    self.path = ArtResizer.shared.resize(plugin.maxwidth, self.path)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/util/artresizer.py", line 184, in resize
    return func(maxwidth, path_in, path_out)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/util/artresizer.py", line 75, in pil_resize
    im.save(path_out)
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1453, in save
    raise KeyError(ext) # unknown extension
KeyError: b'.jpg'

```

Led to this problem:

```
in my config:

fetchart:
    auto: yes
    minwidth: 350
    maxwidth: 600

```

Here's a link to the music files that trigger the bug (if relevant): 


### Setup

* OS: ubuntu 14.04
* Python version: python 3.4.3
* beets version: 1.4.4
* Turning off plugins made problem go away (yes/no): plugin issue

My configuration (output of `beet config`) is:

```yaml
$ beet config
library: ~/.beets.db

match:
    preferred:
        countries: [US, GB|UK]
        original_year: yes

replace:
    "\u2018": ''''
    "\u2019": ''''
    "\u201C": '"'
    "\u201D": '"'
    "\u2010": '-'
    "\u2011": '-'
    "\u2012": '-'
    "\u2013": '-'
    "\u2014": '-'
    "\u2015": '-'
    "\u2016": '-'
    "\u2026": '...'
item_fields:
    cdtitle: u' - %s' % (disctitle) if disctitle else u''
    multidisc: 1 if disctotal > 1 else 0
    initial: artist.lower().lstrip('[]*')[0]
    nicelength: 'm, s = divmod(length, 60)

        h, m = divmod(m, 60)

        return "%d:%02d:%02d" % (h, m, s)

        '

import:
    log: ~/beets.log
    write: yes
    copy: yes
album_fields:
    cdtype: u'[%s]' % (albumtype.lower()) if albumtype else u''
    notalbum: 0 if albumtype and albumtype.lower() == 'album' else 1
convert:
    formats:
        alac:
            extension: m4a
            command: avconv -i $source -y -vn -acodec alac $dest
        ogg: avconv -i $source -y -vn -acodec libvorbis -aq 3 $dest
        flac: avconv -i $source -y -vn -acodec flac $dest
        wma: avconv -i $source -y -vn -acodec wmav2 -vn $dest
        mp3: avconv -i $source -y -vn -qscale:a 0 $dest
        aac:
            extension: m4a
            command: avconv -i $source -y -vn -acodec libfaac -aq 100 $dest
        opus: avconv -i $source -y -vn -acodec libopus -ab 96k $dest
    max_bitrate: 500
    embed: yes
    dest:
    copy_album_art: yes
    threads: 4
    never_convert_lossy_files: no
    pretend: no
    format: mp3
    tmpdir:
    quiet: no

    paths: {}
    auto: yes
    album_art_maxwidth: 0
pathfields: {}
per_disc_numbering: yes
web:
    include_paths: no
    cors: ''
    port: 8337
    host: 127.0.0.1

paths:
    default: albums/$albumartist/%if{$year,$year - }$album%aunique{albumdisambig label catalognum}%if{$multidisc, (disc$disc$cdtitle)}%if{$notalbum, $cdtype}/$track - $title
    comp: albums/000 Various Artists/%if{$year,$year - }$album%aunique{albumdisambig label catalognum}%if{$multidisc, (disc$disc$cdtitle)}%if{$notalbum, $cdtype}/$track - $artist - $title
    singleton: singles/$initial/$artist - $title

plugins:
- convert
- duplicates
- fromfilename
- fuzzy
- info
- inline
- lastgenre
- mbsync
- random
- scrub
- the
- fetchart
- embedart
asciify_paths: no
directory: ~/convertedMusic
embedart:
    auto: yes
    maxwidth: 0
    compare_threshold: 0
    ifempty: no
    remove_art_file: no
original_date: yes
fetchart:
    auto: yes
    minwidth: 350
    maxwidth: 600
    store_source: no
    sources:
    - filesystem
    - coverart
    - itunes
    - amazon
    - albumart
    enforce_ratio: no
    google_key: REDACTED
    cover_names:
    - cover
    - front
    - art
    - album
    - folder
    fanarttv_key: REDACTED
    cautious: no
    google_engine: 001442825323518660753:hrh5ch1gjzm
fuzzy:
    threshold: 0.7
    prefix: '~'
lastgenre:
    fallback:
    source: album
    force: yes
    count: 1
    prefer_specific: no
    min_weight: 10
    canonical: no
    auto: yes
    whitelist: yes
    separator: ', '
the:
    a: yes
    format: '{0}, {1}'
    strip: no
    patterns: []
    the: yes
scrub:
    auto: yes
duplicates:
    path: no
    copy: ''
    move: ''
    merge: no
    checksum: ''
    strict: no
    tiebreak: {}
    full: no
    delete: no
    count: no
    album: no
    format: ''
    tag: ''
    keys: []
```

fetchart: Catch PIL file type exception (KeyError) when resizing art
### Problem

(Describe your problem, feature request, or discussion topic here. If you're reporting a bug, please fill out this and the "Setup" section below. Otherwise, you can delete them.)

Running this command in verbose (`-vv`) mode:

```sh
$ beet -vv fetchart -f albumartist:"KMFDM" album:"Amnesia"
user configuration: /home/mycah/.config/beets/config.yaml
data directory: /home/mycah/.config/beets
plugin paths:
Sending event: pluginload
inline: adding item field cdtitle
inline: adding item field multidisc
inline: adding item field initial
inline: adding item field nicelength
inline: adding album field cdtype
inline: adding album field notalbum
library database: /home/mmattox/.beets.db
library directory: /home/mmattox/convertedMusic
Sending event: library_opened
fetchart: trying source filesystem for album KMFDM - Amnesia
fetchart: trying source coverart for album KMFDM - Amnesia
ImageMagick check `convert --version` failed: [Errno 2] No such file or directory: 'convert'
artresizer: method is (1, (0,))
fetchart: downloading image: https://coverartarchive.org/release/b0682581-89f9-430a-8d89-8fc931107a41/front
fetchart: not a supported image: image/x-None
fetchart: downloading image: https://coverartarchive.org/release-group/c9a6bd02-3045-4e9c-a61d-3b41dd2fcf65/front
fetchart: downloaded art to: /tmp/tmp3i69k972.jpg
fetchart: image size: (700, 700)
fetchart: image needs resizing (700 > 600)
fetchart: using remote image /tmp/tmp3i69k972.jpg
artresizer: PIL resizing /tmp/tmp3i69k972.jpg to /tmp/tmpc9k_0qbc.jpg
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1447, in save
    format = EXTENSION[ext]
KeyError: b'.jpg'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1451, in save
    format = EXTENSION[ext]
KeyError: b'.jpg'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/beet", line 9, in <module>
    load_entry_point('beets==1.4.4', 'console_scripts', 'beet')()
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/ui/__init__.py", line 1221, in main
    _raw_main(args)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/ui/__init__.py", line 1208, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 829, in func
    self.batch_fetch_art(lib, lib.albums(ui.decargs(args)), opts.force)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 882, in batch_fetch_art
    candidate = self.art_for_album(album, local_paths)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 865, in art_for_album
    out.resize(self)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beetsplug/fetchart.py", line 143, in resize
    self.path = ArtResizer.shared.resize(plugin.maxwidth, self.path)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/util/artresizer.py", line 184, in resize
    return func(maxwidth, path_in, path_out)
  File "/usr/local/lib/python3.4/dist-packages/beets-1.4.4-py3.4.egg/beets/util/artresizer.py", line 75, in pil_resize
    im.save(path_out)
  File "/usr/lib/python3/dist-packages/PIL/Image.py", line 1453, in save
    raise KeyError(ext) # unknown extension
KeyError: b'.jpg'

```

Led to this problem:

```
in my config:

fetchart:
    auto: yes
    minwidth: 350
    maxwidth: 600

```

Here's a link to the music files that trigger the bug (if relevant): 


### Setup

* OS: ubuntu 14.04
* Python version: python 3.4.3
* beets version: 1.4.4
* Turning off plugins made problem go away (yes/no): plugin issue

My configuration (output of `beet config`) is:

```yaml
$ beet config
library: ~/.beets.db

match:
    preferred:
        countries: [US, GB|UK]
        original_year: yes

replace:
    "\u2018": ''''
    "\u2019": ''''
    "\u201C": '"'
    "\u201D": '"'
    "\u2010": '-'
    "\u2011": '-'
    "\u2012": '-'
    "\u2013": '-'
    "\u2014": '-'
    "\u2015": '-'
    "\u2016": '-'
    "\u2026": '...'
item_fields:
    cdtitle: u' - %s' % (disctitle) if disctitle else u''
    multidisc: 1 if disctotal > 1 else 0
    initial: artist.lower().lstrip('[]*')[0]
    nicelength: 'm, s = divmod(length, 60)

        h, m = divmod(m, 60)

        return "%d:%02d:%02d" % (h, m, s)

        '

import:
    log: ~/beets.log
    write: yes
    copy: yes
album_fields:
    cdtype: u'[%s]' % (albumtype.lower()) if albumtype else u''
    notalbum: 0 if albumtype and albumtype.lower() == 'album' else 1
convert:
    formats:
        alac:
            extension: m4a
            command: avconv -i $source -y -vn -acodec alac $dest
        ogg: avconv -i $source -y -vn -acodec libvorbis -aq 3 $dest
        flac: avconv -i $source -y -vn -acodec flac $dest
        wma: avconv -i $source -y -vn -acodec wmav2 -vn $dest
        mp3: avconv -i $source -y -vn -qscale:a 0 $dest
        aac:
            extension: m4a
            command: avconv -i $source -y -vn -acodec libfaac -aq 100 $dest
        opus: avconv -i $source -y -vn -acodec libopus -ab 96k $dest
    max_bitrate: 500
    embed: yes
    dest:
    copy_album_art: yes
    threads: 4
    never_convert_lossy_files: no
    pretend: no
    format: mp3
    tmpdir:
    quiet: no

    paths: {}
    auto: yes
    album_art_maxwidth: 0
pathfields: {}
per_disc_numbering: yes
web:
    include_paths: no
    cors: ''
    port: 8337
    host: 127.0.0.1

paths:
    default: albums/$albumartist/%if{$year,$year - }$album%aunique{albumdisambig label catalognum}%if{$multidisc, (disc$disc$cdtitle)}%if{$notalbum, $cdtype}/$track - $title
    comp: albums/000 Various Artists/%if{$year,$year - }$album%aunique{albumdisambig label catalognum}%if{$multidisc, (disc$disc$cdtitle)}%if{$notalbum, $cdtype}/$track - $artist - $title
    singleton: singles/$initial/$artist - $title

plugins:
- convert
- duplicates
- fromfilename
- fuzzy
- info
- inline
- lastgenre
- mbsync
- random
- scrub
- the
- fetchart
- embedart
asciify_paths: no
directory: ~/convertedMusic
embedart:
    auto: yes
    maxwidth: 0
    compare_threshold: 0
    ifempty: no
    remove_art_file: no
original_date: yes
fetchart:
    auto: yes
    minwidth: 350
    maxwidth: 600
    store_source: no
    sources:
    - filesystem
    - coverart
    - itunes
    - amazon
    - albumart
    enforce_ratio: no
    google_key: REDACTED
    cover_names:
    - cover
    - front
    - art
    - album
    - folder
    fanarttv_key: REDACTED
    cautious: no
    google_engine: 001442825323518660753:hrh5ch1gjzm
fuzzy:
    threshold: 0.7
    prefix: '~'
lastgenre:
    fallback:
    source: album
    force: yes
    count: 1
    prefer_specific: no
    min_weight: 10
    canonical: no
    auto: yes
    whitelist: yes
    separator: ', '
the:
    a: yes
    format: '{0}, {1}'
    strip: no
    patterns: []
    the: yes
scrub:
    auto: yes
duplicates:
    path: no
    copy: ''
    move: ''
    merge: no
    checksum: ''
    strict: no
    tiebreak: {}
    full: no
    delete: no
    count: no
    album: no
    format: ''
    tag: ''
    keys: []
```



Discussion:
Thanks! Looks like PIL behaves poorly when it doesn't have support for JPEG writing. You might try disabling art resizing (or using ImageMagick instead of PIL) for now.
I switched to imagemagick and everything seems fine.  Thought I'd add a bug anyhow.

Thanks!
Thanks! Looks like PIL behaves poorly when it doesn't have support for JPEG writing. You might try disabling art resizing (or using ImageMagick instead of PIL) for now.
I switched to imagemagick and everything seems fine.  Thought I'd add a bug anyhow.

Thanks!

Resolving patch:
diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py
--- a/beets/util/artresizer.py
+++ b/beets/util/artresizer.py
@@ -72,7 +72,7 @@ def pil_resize(maxwidth, path_in, path_out=None):
         im = Image.open(util.syspath(path_in))
         size = maxwidth, maxwidth
         im.thumbnail(size, Image.ANTIALIAS)
-        im.save(path_out)
+        im.save(util.py3_path(path_out))
         return path_out
     except IOError:
         log.error(u"PIL cannot create thumbnail for '{0}'",


Test patch: