Fetchart: fix tests and fetachart logic

This commit is contained in:
wordofglass 2016-04-13 22:56:18 +02:00
parent a4994d2bf8
commit 1cc4d11baf
2 changed files with 56 additions and 52 deletions

View file

@ -53,7 +53,8 @@ class Candidate():
MATCH_EXACT = 0
MATCH_FALLBACK = 1
def __init__(self, path=None, url=None, source=u'', match=None):
def __init__(self, log, path=None, url=None, source=u'', match=None):
self._log = log
self.path = path
self.url = url
self.source = source
@ -173,6 +174,9 @@ class ArtSource(RequestMixin):
def get(self, album, extra):
raise NotImplementedError()
def _candidate(self, **kwargs):
return Candidate(source=self.NAME, log=self._log, **kwargs)
def fetch_image(self, candidate, extra):
raise NotImplementedError()
@ -228,7 +232,8 @@ class RemoteArtSource(ArtSource):
class CoverArtArchive(RemoteArtSource):
"""Cover Art Archive"""
NAME = u"Cover Art Archive"
URL = 'http://coverartarchive.org/release/{mbid}/front'
GROUP_URL = 'http://coverartarchive.org/release-group/{mbid}/front'
@ -237,17 +242,16 @@ class CoverArtArchive(RemoteArtSource):
using album MusicBrainz release ID and release group ID.
"""
if album.mb_albumid:
yield Candidate(url=self.URL.format(mbid=album.mb_albumid),
source=u'coverartarchive.org',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=self.URL.format(mbid=album.mb_albumid),
match=Candidate.MATCH_EXACT)
if album.mb_releasegroupid:
yield Candidate(
yield self._candidate(
url=self.GROUP_URL.format(mbid=album.mb_releasegroupid),
source=u'coverartarchive.org',
match=Candidate.MATCH_FALLBACK)
class Amazon(RemoteArtSource):
NAME = u"Amazon"
URL = 'http://images.amazon.com/images/P/%s.%02i.LZZZZZZZ.jpg'
INDICES = (1, 2)
@ -256,13 +260,12 @@ class Amazon(RemoteArtSource):
"""
if album.asin:
for index in self.INDICES:
yield Candidate(url=self.URL % (album.asin, index),
source=u'Amazon',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=self.URL % (album.asin, index),
match=Candidate.MATCH_EXACT)
class AlbumArtOrg(RemoteArtSource):
"""AlbumArt.org scraper"""
NAME = u"AlbumArt.org scraper"
URL = 'http://www.albumart.org/index_detail.php'
PAT = r'href\s*=\s*"([^>"]*)"[^>]*title\s*=\s*"View larger image"'
@ -283,14 +286,13 @@ class AlbumArtOrg(RemoteArtSource):
m = re.search(self.PAT, resp.text)
if m:
image_url = m.group(1)
yield Candidate(url=image_url,
source=u'AlbumArt.org',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=image_url, match=Candidate.MATCH_EXACT)
else:
self._log.debug(u'no image found on page')
class GoogleImages(RemoteArtSource):
NAME = u"Google Images"
URL = u'https://www.googleapis.com/customsearch/v1'
def get(self, album, extra):
@ -322,13 +324,13 @@ class GoogleImages(RemoteArtSource):
if 'items' in data.keys():
for item in data['items']:
yield Candidate(url=item['link'],
source=u'Google images',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=item['link'],
match=Candidate.MATCH_EXACT)
class ITunesStore(RemoteArtSource):
# Art from the iTunes Store.
NAME = u"iTunes Store"
def get(self, album, extra):
"""Return art URL from iTunes Store given an album title.
"""
@ -354,9 +356,7 @@ class ITunesStore(RemoteArtSource):
if itunes_album.get_artwork()['100']:
small_url = itunes_album.get_artwork()['100']
big_url = small_url.replace('100x100', '1200x1200')
yield Candidate(url=big_url,
source=u'iTunes Store',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=big_url, match=Candidate.MATCH_EXACT)
else:
self._log.debug(u'album has no artwork in iTunes Store')
except IndexError:
@ -364,7 +364,7 @@ class ITunesStore(RemoteArtSource):
class Wikipedia(RemoteArtSource):
# Art from Wikipedia (queried through DBpedia)
NAME = u"Wikipedia (queried through DBpedia)"
DBPEDIA_URL = 'http://dbpedia.org/sparql'
WIKIPEDIA_URL = 'http://en.wikipedia.org/w/api.php'
SPARQL_QUERY = '''PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
@ -481,16 +481,16 @@ class Wikipedia(RemoteArtSource):
results = data['query']['pages']
for _, result in results.iteritems():
image_url = result['imageinfo'][0]['url']
yield Candidate(url=image_url,
source=u'Wikipedia',
match=Candidate.MATCH_EXACT)
yield self._candidate(url=image_url,
match=Candidate.MATCH_EXACT)
except (ValueError, KeyError, IndexError):
self._log.debug(u'wikipedia: error scraping imageinfo')
return
class FileSystem(LocalArtSource):
"""Art from the filesystem"""
NAME = u"Filesystem"
@staticmethod
def filename_priority(filename, cover_names):
"""Sort order for image names.
@ -505,6 +505,8 @@ class FileSystem(LocalArtSource):
"""Look for album art files in the specified directories.
"""
paths = extra['paths']
if not paths:
return
cover_names = extra['cover_names']
cover_pat = br"(\b|_)({0})(\b|_)".format(b'|'.join(cover_names))
cautious = extra['cautious']
@ -523,27 +525,29 @@ class FileSystem(LocalArtSource):
# Look for "preferred" filenames.
images = sorted(images,
lambda x: self.filename_priority(x, cover_names))
key=lambda x:
self.filename_priority(x, cover_names))
remaining = []
for fn in images:
if re.search(cover_pat, os.path.splitext(fn)[0], re.I):
self._log.debug(u'using well-named art file {0}',
util.displayable_path(fn))
yield Candidate(path=os.path.join(path, fn),
source=u'Filesystem',
match=Candidate.MATCH_EXACT)
yield self._candidate(path=os.path.join(path, fn),
match=Candidate.MATCH_EXACT)
else:
remaining.append(fn)
# Fall back to any image in the folder.
if images and not cautious:
if remaining and not cautious:
self._log.debug(u'using fallback art file {0}',
util.displayable_path(images[0]))
yield Candidate(path=os.path.join(path, images[0]),
source=u'Filesystem',
match=Candidate.MATCH_FALLBACK)
util.displayable_path(remaining[0]))
yield self._candidate(path=os.path.join(path, remaining[0]),
match=Candidate.MATCH_FALLBACK)
# Try each source in turn.
SOURCES_ALL = [u'filesysytem',
SOURCES_ALL = [u'filesystem',
u'coverart', u'itunes', u'amazon', u'albumart',
u'wikipedia', u'google']
@ -698,8 +702,9 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
source.fetch_image(candidate, extra)
if candidate.validate(extra):
out = candidate
self._log.debug(u'using {0.LOC_STR} image {1}'
.format(source, out.path))
self._log.debug(
u'using {0.LOC_STR} image {1}'.format(
source, util.displayable_path(out.path)))
break
if out:
break

View file

@ -59,17 +59,18 @@ class FetchImageTest(UseThePlugin):
super(FetchImageTest, self).setUp()
self.dpath = os.path.join(self.temp_dir, 'arttest')
self.source = fetchart.RemoteArtSource(logger, self.plugin.config)
self.extra = {'maxwidth': 0}
def test_invalid_type_returns_none(self):
self.mock_response('image/watercolour')
candidate = fetchart.Candidate(url=self.URL)
self.source.fetch_image(candidate)
candidate = fetchart.Candidate(logger, url=self.URL)
self.source.fetch_image(candidate, self.extra)
self.assertEqual(candidate.path, None)
def test_jpeg_type_returns_path(self):
self.mock_response('image/jpeg')
candidate = fetchart.Candidate(url=self.URL)
self.source.fetch_image(candidate)
candidate = fetchart.Candidate(logger, url=self.URL)
self.source.fetch_image(candidate, self.extra)
self.assertNotEqual(candidate.path, None)
@ -82,7 +83,7 @@ class FSArtTest(UseThePlugin):
self.source = fetchart.FileSystem(logger, self.plugin.config)
self.extra = {'cautious': False,
'cover_names': ('art',),
'paths': self.dpath}
'paths': [self.dpath]}
def test_finds_jpg_in_directory(self):
_common.touch(os.path.join(self.dpath, 'a.jpg'))
@ -152,7 +153,7 @@ class CombinedTest(UseThePlugin):
def test_main_interface_returns_none_for_missing_asin_and_path(self):
album = _common.Bag()
candidate = self.plugin.art_for_album(album, None)
self.assertIsNotNone(candidate)
self.assertIsNone(candidate)
def test_main_interface_gives_precedence_to_fs_art(self):
_common.touch(os.path.join(self.dpath, 'art.jpg'))
@ -192,8 +193,7 @@ class CombinedTest(UseThePlugin):
def test_local_only_does_not_access_network(self):
album = _common.Bag(mb_albumid=self.MBID, asin=self.ASIN)
with self.assertRaises(StopIteration):
self.plugin.art_for_album(album, local_only=True)
self.plugin.art_for_album(album, None, local_only=True)
self.assertEqual(len(responses.calls), 0)
def test_local_only_gets_fs_image(self):
@ -288,7 +288,7 @@ class ArtImporterTest(UseThePlugin):
self.art_file = os.path.join(self.temp_dir, 'tmpcover.jpg')
_common.touch(self.art_file)
self.old_afa = self.plugin.art_for_album
self.afa_response = fetchart.Candidate(path=self.art_file)
self.afa_response = fetchart.Candidate(logger, path=self.art_file)
def art_for_album(i, p, local_only=False):
return self.afa_response
@ -379,7 +379,7 @@ class ArtImporterTest(UseThePlugin):
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)
self.afa_response = fetchart.Candidate(path=artdest)
self.afa_response = fetchart.Candidate(logger, path=artdest)
self._fetch_art(True)
def test_fetch_art_if_imported_file_deleted(self):
@ -409,9 +409,9 @@ class ArtForAlbumTest(UseThePlugin):
self.old_fs_source_get = fetchart.FileSystem.get
def fs_source_get(album, paths, *_):
if paths:
yield fetchart.Candidate(path=self.image_file)
def fs_source_get(_self, album, extra):
if extra['paths']:
yield fetchart.Candidate(logger, path=self.image_file)
fetchart.FileSystem.get = fs_source_get
@ -429,7 +429,6 @@ class ArtForAlbumTest(UseThePlugin):
self.assertNotEqual(candidate, None)
self.assertEqual(candidate.path, self.image_file)
self.assertExists(candidate.path)
return candidate.path
else:
self.assertIsNone(candidate)