mirror of
https://github.com/beetbox/beets.git
synced 2026-02-08 00:14:31 +01:00
Merge pull request #1941 from wordofglass/fetchart_fanarttv
Fetchart: add fanart.tv source
This commit is contained in:
commit
4691deb5a5
4 changed files with 161 additions and 3 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in a new issue