Merge pull request #1941 from wordofglass/fetchart_fanarttv

Fetchart: add fanart.tv source
This commit is contained in:
Adrian Sampson 2016-04-15 11:45:00 -07:00
commit 4691deb5a5
4 changed files with 161 additions and 3 deletions

View file

@ -192,6 +192,58 @@ class GoogleImages(ArtSource):
yield item['link']
class FanartTV(ArtSource):
"""Art from fanart.tv requested using their API"""
API_URL = 'http://webservice.fanart.tv/v3/'
API_ALBUMS = API_URL + 'music/albums/'
PROJECT_KEY = '61a7d0ab4e67162b7a0c7c35915cd48e'
def get(self, album):
if not album.mb_releasegroupid:
return
response = self.request(
self.API_ALBUMS + album.mb_releasegroupid,
headers={
'api-key': self.PROJECT_KEY,
'client-key': self._config['fanarttv_key'].get()
})
try:
data = response.json()
except ValueError:
self._log.debug(u'fanart.tv: error loading response: {}',
response.text)
return
if u'status' in data and data[u'status'] == u'error':
if u'not found' in data[u'error message'].lower():
self._log.debug(u'fanart.tv: no image found')
elif u'api key' in data[u'error message'].lower():
self._log.warning(u'fanart.tv: Invalid API key given, please '
u'enter a valid one in your config file.')
else:
self._log.debug(u'fanart.tv: error on request: {}',
data[u'error message'])
return
matches = []
# can there be more than one releasegroupid per responce?
for mb_releasegroupid in data.get(u'albums', dict()):
if album.mb_releasegroupid == mb_releasegroupid:
# note: there might be more art referenced, e.g. cdart
matches.extend(
data[u'albums'][mb_releasegroupid][u'albumcover'])
# can this actually occur?
else:
self._log.debug(u'fanart.tv: unexpected mb_releasegroupid in '
u'response!')
matches.sort(key=lambda x: x[u'likes'], reverse=True)
for item in matches:
yield item[u'url']
class ITunesStore(ArtSource):
# Art from the iTunes Store.
def get(self, album):
@ -396,7 +448,7 @@ class FileSystem(ArtSource):
# Try each source in turn.
SOURCES_ALL = [u'coverart', u'itunes', u'amazon', u'albumart',
u'wikipedia', u'google']
u'wikipedia', u'google', u'fanarttv']
ART_SOURCES = {
u'coverart': CoverArtArchive,
@ -405,6 +457,7 @@ ART_SOURCES = {
u'amazon': Amazon,
u'wikipedia': Wikipedia,
u'google': GoogleImages,
u'fanarttv': FanartTV,
}
# PLUGIN LOGIC ###############################################################
@ -425,8 +478,10 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
'sources': ['coverart', 'itunes', 'amazon', 'albumart'],
'google_key': None,
'google_engine': u'001442825323518660753:hrh5ch1gjzm',
'fanarttv_key': None
})
self.config['google_key'].redact = True
self.config['fanarttv_key'].redact = True
# Holds paths to downloaded images between fetching them and
# placing them in the filesystem.
@ -447,6 +502,13 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
if not self.config['google_key'].get() and \
u'google' in available_sources:
available_sources.remove(u'google')
if not self.config['fanarttv_key'].get() and \
u'fanarttv' in available_sources:
self._log.warn(
u'fanart.tv source enabled, but no personal API given. This '
u'works as of now, however, fanart.tv prefers users to '
u'register a personal key. Additionaly this makes new art '
u'available shorter after its upload. See the documentation.')
sources_name = plugins.sanitize_choices(
self.config['sources'].as_str_seq(), available_sources)
self.sources = [ART_SOURCES[s](self._log, self.config)

View file

@ -13,6 +13,10 @@ New features:
* :doc:`/plugins/lyrics`: The plugin can now translate the fetched lyrics to a
configured `bing_lang_to` langage. Enabling translation require to register
for a Microsoft Azure Marketplace free account. Thanks to :user:`Kraymer`.
* :doc:`/plugins/fetchart`: Album art can now be fetched from `fanart.tv`_.
Albums are matched using the ``mb_releasegroupid`` tag.
.. _fanart.tv: https://fanart.tv/
Fixes:

View file

@ -50,13 +50,15 @@ file. The available options are:
- **sources**: List of sources to search for images. An asterisk `*` expands
to all available sources.
Default: ``coverart itunes amazon albumart``, i.e., everything but
``wikipedia`` and ``google``. Enable those two sources for more matches at
the cost of some speed.
``wikipedia``, ``google`` and ``fanarttv``. Enable those sources for more
matches at the cost of some speed.
- **google_key**: Your Google API key (to enable the Google Custom Search
backend).
Default: None.
- **google_engine**: The custom search engine to use.
Default: The `beets custom search engine`_, which searches the entire web.
**fanarttv_key**: The personal API key for requesting art from
fanart.tv. See below.
Note: ``minwidth`` and ``enforce_ratio`` options require either `ImageMagick`_
or `Pillow`_.
@ -162,6 +164,21 @@ default engine searches the entire web for cover art.
Note that the Google custom search API is limited to 100 queries per day.
After that, the fetchart plugin will fall back on other declared data sources.
Fanart.tv
'''''''''
Although not strictly necessary right now, you might think about
`registering a personal fanart.tv API key`_. Set the ``fanarttv_key``
configuration option to your key, then add ``fanarttv`` to the list of sources
in your configuration.
.. _registering a personal fanart.tv API key: https://fanart.tv/get-an-api-key/
More detailed information can be found `on their blog`_. Specifically, the
personal key will give you earlier access to new art.
.. _on their blog: https://fanart.tv/2015/01/personal-api-keys/
Embedding Album Art
-------------------

View file

@ -260,6 +260,81 @@ class GoogleImageTest(UseThePlugin):
self.assertEqual(list(result_url), [])
class FanartTVTest(UseThePlugin):
RESPONSE_MULTIPLE = u"""{
"name": "artistname",
"mbid_id": "artistid",
"albums": {
"thereleasegroupid": {
"albumcover": [
{
"id": "24",
"url": "http://example.com/1.jpg",
"likes": "0"
},
{
"id": "42",
"url": "http://example.com/2.jpg",
"likes": "0"
},
{
"id": "23",
"url": "http://example.com/3.jpg",
"likes": "0"
}
],
"cdart": [
{
"id": "123",
"url": "http://example.com/4.jpg",
"likes": "0",
"disc": "1",
"size": "1000"
}
]
}
}
}"""
RESPONSE_ERROR = u"""{
"status": "error",
"error message": "the error message"
}"""
RESPONSE_MALFORMED = u"bla blup"
def setUp(self):
super(FanartTVTest, self).setUp()
self.source = fetchart.FanartTV(logger, self.plugin.config)
@responses.activate
def run(self, *args, **kwargs):
super(FanartTVTest, self).run(*args, **kwargs)
def mock_response(self, url, json):
responses.add(responses.GET, url, body=json,
content_type='application/json')
def test_fanarttv_finds_image(self):
album = _common.Bag(mb_releasegroupid=u'thereleasegroupid')
self.mock_response(fetchart.FanartTV.API_ALBUMS + u'thereleasegroupid',
self.RESPONSE_MULTIPLE)
result_url = self.source.get(album)
self.assertEqual(list(result_url)[0], 'http://example.com/1.jpg')
def test_fanarttv_returns_no_result_when_error_received(self):
album = _common.Bag(mb_releasegroupid=u'thereleasegroupid')
self.mock_response(fetchart.FanartTV.API_ALBUMS + u'thereleasegroupid',
self.RESPONSE_ERROR)
result_url = self.source.get(album)
self.assertEqual(list(result_url), [])
def test_fanarttv_returns_no_result_with_malformed_response(self):
album = _common.Bag(mb_releasegroupid=u'thereleasegroupid')
self.mock_response(fetchart.FanartTV.API_ALBUMS + u'thereleasegroupid',
self.RESPONSE_MALFORMED)
result_url = self.source.get(album)
self.assertEqual(list(result_url), [])
@_common.slow_test()
class ArtImporterTest(UseThePlugin):
def setUp(self):