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

This commit is contained in:
Adrian Sampson 2012-06-24 17:41:37 -07:00
parent d807b3fbf1
commit 77cbb19564
4 changed files with 60 additions and 12 deletions

View file

@ -20,6 +20,7 @@ import logging
import os
from beets.plugins import BeetsPlugin
from beets import importer
IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg']
COVER_NAMES = ['cover', 'front', 'art', 'album', 'folder']
@ -131,28 +132,37 @@ 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:
# All sources failed.
log.debug('No ASIN available: no art found.')
return None
@ -172,8 +182,18 @@ class FetchArtPlugin(BeetsPlugin):
# 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 file

@ -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

View file

@ -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 file

@ -79,6 +79,7 @@ class CombinedTest(unittest.TestCase):
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,7 +117,6 @@ class CombinedTest(unittest.TestCase):
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')
@ -124,7 +124,6 @@ class CombinedTest(unittest.TestCase):
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 @@ class CombinedTest(unittest.TestCase):
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 @@ class ArtImporterTest(unittest.TestCase, _common.ExtraAsserts):
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 @@ class ArtImporterTest(unittest.TestCase, _common.ExtraAsserts):
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 @@ class ArtImporterTest(unittest.TestCase, _common.ExtraAsserts):
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)