mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
fetchart: get local art for as-is imports (GC-339)
This commit is contained in:
parent
d807b3fbf1
commit
77cbb19564
4 changed files with 60 additions and 12 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue