Permalink
Browse files

fetchart: get local art for as-is imports (GC-339)

  • Loading branch information...
1 parent d807b3f commit 77cbb195645087c4d81d57d51e3d7cce7e926331 @sampsyo sampsyo committed Jun 25, 2012
Showing with 60 additions and 12 deletions.
  1. +27 −7 beetsplug/fetchart.py
  2. +3 −0 docs/changelog.rst
  3. +4 −0 docs/plugins/fetchart.rst
  4. +26 −5 test/test_art.py
View
@@ -20,6 +20,7 @@
import os
from beets.plugins import BeetsPlugin
+from beets import importer
IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg']
COVER_NAMES = ['cover', 'front', 'art', 'album', 'folder']
@@ -131,30 +132,39 @@ def art_in_path(path):
# Try each source in turn.
-def art_for_album(album, path):
+def art_for_album(album, path, local_only=False):
"""Given an AlbumInfo object, returns a path to downloaded art for
- the album (or None if no art is found).
+ the album (or None if no art is found). If `local_only`, then only
+ local image files from the filesystem are returned; no network
+ requests are made.
"""
+ # Local art.
if isinstance(path, basestring):
out = art_in_path(path)
if out:
return out
+ if local_only:
+ # Abort without trying Web sources.
+ return
+ # CoverArtArchive.org.
if album.album_id:
log.debug('Fetching album art for MBID {0}.'.format(album.album_id))
out = caa_art(album.album_id)
if out:
return out
+ # Amazon and AlbumArt.org.
if album.asin:
log.debug('Fetching album art for ASIN %s.' % album.asin)
out = art_for_asin(album.asin)
if out:
return out
return aao_art(album.asin)
- else:
- log.debug('No ASIN available: no art found.')
- return None
+
+ # All sources failed.
+ log.debug('No ASIN available: no art found.')
+ return None
# PLUGIN LOGIC ###############################################################
@@ -172,8 +182,18 @@ def __init__(self):
# Asynchronous; after music is added to the library.
def fetch_art(self, config, task):
"""Find art for the album being imported."""
- if task.should_write_tags() and task.is_album:
- path = art_for_album(task.info, task.path)
+ if task.is_album: # Only fetch art for full albums.
+ if task.choice_flag == importer.action.ASIS:
+ # For as-is imports, don't search Web sources for art.
+ local = True
+ elif task.choice_flag == importer.action.APPLY:
+ # Search everywhere for art.
+ local = False
+ else:
+ # For any other choices (e.g., TRACKS), do nothing.
+ return
+
+ path = art_for_album(task.info, task.path, local_only=local)
if path:
self.art_paths[task] = path
View
@@ -21,6 +21,9 @@ art for your music, enable this plugin after upgrading to beets 1.0b15.
While its coverage is currently spotty, CAA is growing and its images are
generally higher-quality than those from Amazon. You can help out by
`submitting new images to the archive`_.
+* :doc:`/plugins/fetchart`: "As-is" and non-autotagged imports can now have
+ album art imported from the local filesystem (although Web repositories are
+ still not searched in these cases).
* Errors when communicating with MusicBrainz now log an error message instead of
halting the importer.
* Similarly, filesystem manipulation errors now print helpful error messages
@@ -28,6 +28,10 @@ same folder as the music files you're importing. If you have an image file
called "cover," "front," "art," "album," for "folder" alongside your music,
beets will treat it as album art and skip searching any online databases.
+When you choose to apply changes during an import, beets searches all sources
+for album art. For "as-is" imports (and non-autotagged imports using the ``-A``
+flag), beets only looks for art on the local filesystem.
+
Embedding Album Art
-------------------
View
@@ -79,6 +79,7 @@ def setUp(self):
self.old_urlopen = fetchart.urllib.urlopen
fetchart.urllib.urlopen = self._urlopen
self.page_text = ""
+ self.urlopen_called = False
def tearDown(self):
shutil.rmtree(self.dpath)
fetchart.urllib.urlopen = self.old_urlopen
@@ -116,15 +117,13 @@ def test_main_interface_falls_back_to_amazon(self):
self.assertEqual(artpath, 'anotherpath')
def test_main_interface_tries_amazon_before_aao(self):
- self.urlopen_called = False
fetchart.urllib.urlretrieve = \
MockUrlRetrieve('anotherpath', 'image/jpeg')
album = AlbumInfo(None, None, None, None, None, asin='xxxx')
fetchart.art_for_album(album, self.dpath)
self.assertFalse(self.urlopen_called)
def test_main_interface_falls_back_to_aao(self):
- self.urlopen_called = False
fetchart.urllib.urlretrieve = \
MockUrlRetrieve('anotherpath', 'text/html')
album = AlbumInfo(None, None, None, None, None, asin='xxxx')
@@ -139,6 +138,25 @@ def test_main_interface_uses_caa_when_mbid_available(self):
self.assertEqual(artpath, 'anotherpath')
self.assertTrue('coverartarchive.org' in mock_retrieve.fetched)
+ def test_local_only_does_not_access_network(self):
+ mock_retrieve = MockUrlRetrieve('anotherpath', 'image/jpeg')
+ fetchart.urllib.urlretrieve = mock_retrieve
+ album = AlbumInfo(None, 'albumid', None, None, None, asin='xxxx')
+ artpath = fetchart.art_for_album(album, self.dpath, local_only=True)
+ self.assertEqual(artpath, None)
+ self.assertFalse(self.urlopen_called)
+ self.assertFalse(mock_retrieve.fetched)
+
+ def test_local_only_gets_fs_image(self):
+ _common.touch(os.path.join(self.dpath, 'a.jpg'))
+ mock_retrieve = MockUrlRetrieve('anotherpath', 'image/jpeg')
+ fetchart.urllib.urlretrieve = mock_retrieve
+ album = AlbumInfo(None, 'albumid', None, None, None, asin='xxxx')
+ artpath = fetchart.art_for_album(album, self.dpath, local_only=True)
+ self.assertEqual(artpath, os.path.join(self.dpath, 'a.jpg'))
+ self.assertFalse(self.urlopen_called)
+ self.assertFalse(mock_retrieve.fetched)
+
class AAOTest(unittest.TestCase):
def setUp(self):
self.old_urlopen = fetchart.urllib.urlopen
@@ -176,7 +194,10 @@ def setUp(self):
self.art_file = os.path.join(_common.RSRC, 'tmpcover.jpg')
_common.touch(self.art_file)
self.old_afa = fetchart.art_for_album
- fetchart.art_for_album = lambda a, b: self.art_file
+ self.afa_response = self.art_file
+ def art_for_album(i, p, local_only=False):
+ return self.afa_response
+ fetchart.art_for_album = art_for_album
# Test library.
self.libpath = os.path.join(_common.RSRC, 'tmplib.blb')
@@ -241,7 +262,7 @@ def test_fetch_art(self):
self._fetch_art(True)
def test_art_not_found(self):
- fetchart.art_for_album = lambda a, b: None
+ self.afa_response = None
self._fetch_art(False)
def test_no_art_for_singleton(self):
@@ -265,7 +286,7 @@ def test_move_original_file(self):
def test_do_not_delete_original_if_already_in_place(self):
artdest = os.path.join(os.path.dirname(self.i.path), 'cover.jpg')
shutil.copyfile(self.art_file, artdest)
- fetchart.art_for_album = lambda a, b: artdest
+ self.afa_response = artdest
self._fetch_art(True)

0 comments on commit 77cbb19

Please sign in to comment.