From 333d5d1dd3a1e578427328a3ce5a09ee342fcd4c Mon Sep 17 00:00:00 2001 From: Jack Wilsdon Date: Mon, 30 Mar 2020 20:08:40 +0100 Subject: [PATCH] fetchart: Add Last.fm artwork source --- beetsplug/fetchart.py | 70 ++++++++++++++++++++++++++++++++++++++- docs/changelog.rst | 3 ++ docs/plugins/fetchart.rst | 17 ++++++++-- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 2fe8b0b2c..28067c310 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -21,6 +21,7 @@ from contextlib import closing import os import re from tempfile import NamedTemporaryFile +from collections import OrderedDict import requests @@ -742,11 +743,72 @@ class FileSystem(LocalArtSource): match=Candidate.MATCH_FALLBACK) +class LastFM(RemoteArtSource): + NAME = u"Last.fm" + + # Sizes in priority order. + SIZES = OrderedDict([ + ('mega', (300, 300)), + ('extralarge', (300, 300)), + ('large', (174, 174)), + ('medium', (64, 64)), + ('small', (34, 34)), + ]) + + if util.SNI_SUPPORTED: + API_URL = 'https://ws.audioscrobbler.com/2.0' + else: + API_URL = 'http://ws.audioscrobbler.com/2.0' + + def __init__(self, *args, **kwargs): + super(LastFM, self).__init__(*args, **kwargs) + self.key = self._config['lastfm_key'].get(), + + def get(self, album, plugin, paths): + if not album.mb_albumid: + return + + try: + response = self.request(self.API_URL, params={ + 'method': 'album.getinfo', + 'api_key': self.key, + 'mbid': album.mb_albumid, + 'format': 'json', + }) + except requests.RequestException: + self._log.debug(u'lastfm: error receiving response') + return + + try: + data = response.json() + + if 'error' in data: + if data['error'] == 6: + self._log.debug('lastfm: no results for {}', + album.mb_albumid) + else: + self._log.error( + 'lastfm: failed to get album info: {} ({})', + data['message'], data['error']) + else: + images = {image['size']: image['#text'] + for image in data['album']['image']} + + # Provide candidates in order of size. + for size in self.SIZES.keys(): + if size in images: + yield self._candidate(url=images[size], + size=self.SIZES[size]) + except ValueError: + self._log.debug(u'lastfm: error loading response: {}' + .format(response.text)) + return + # Try each source in turn. SOURCES_ALL = [u'filesystem', u'coverart', u'itunes', u'amazon', u'albumart', - u'wikipedia', u'google', u'fanarttv'] + u'wikipedia', u'google', u'fanarttv', u'lastfm'] ART_SOURCES = { u'filesystem': FileSystem, @@ -757,6 +819,7 @@ ART_SOURCES = { u'wikipedia': Wikipedia, u'google': GoogleImages, u'fanarttv': FanartTV, + u'lastfm': LastFM, } SOURCE_NAMES = {v: k for k, v in ART_SOURCES.items()} @@ -787,11 +850,13 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): 'google_key': None, 'google_engine': u'001442825323518660753:hrh5ch1gjzm', 'fanarttv_key': None, + 'lastfm_key': None, 'store_source': False, 'high_resolution': False, }) self.config['google_key'].redact = True self.config['fanarttv_key'].redact = True + self.config['lastfm_key'].redact = True self.minwidth = self.config['minwidth'].get(int) self.maxwidth = self.config['maxwidth'].get(int) @@ -831,6 +896,9 @@ 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['lastfm_key'].get() and \ + u'lastfm' in available_sources: + available_sources.remove(u'lastfm') available_sources = [(s, c) for s in available_sources for c in ART_SOURCES[s].VALID_MATCHING_CRITERIA] diff --git a/docs/changelog.rst b/docs/changelog.rst index 3769164be..33ad386ad 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -113,6 +113,8 @@ New features: titles. Thanks to :user:`cole-miller`. :bug:`3459` +* :doc:`/plugins/fetchart`: Album art can now be fetched from `last.fm`_. + :bug:`3530` Fixes: @@ -233,6 +235,7 @@ For packagers: .. _works: https://musicbrainz.org/doc/Work .. _Deezer: https://www.deezer.com .. _keyfinder-cli: https://github.com/EvanPurkhiser/keyfinder-cli +.. _last.fm: https://last.fm 1.4.9 (May 30, 2019) diff --git a/docs/plugins/fetchart.rst b/docs/plugins/fetchart.rst index 4441d4e30..e8f7b6d92 100644 --- a/docs/plugins/fetchart.rst +++ b/docs/plugins/fetchart.rst @@ -58,9 +58,9 @@ file. The available options are: - **sources**: List of sources to search for images. An asterisk `*` expands to all available sources. Default: ``filesystem coverart itunes amazon albumart``, i.e., everything but - ``wikipedia``, ``google`` and ``fanarttv``. Enable those sources for more - matches at the cost of some speed. They are searched in the given order, - thus in the default config, no remote (Web) art source are queried if + ``wikipedia``, ``google``, ``fanarttv`` and ``lastfm``. Enable those sources + for more matches at the cost of some speed. They are searched in the given + order, thus in the default config, no remote (Web) art source are queried if local art is found in the filesystem. To use a local image as fallback, move it to the end of the list. For even more fine-grained control over the search order, see the section on :ref:`album-art-sources` below. @@ -71,6 +71,8 @@ file. The available options are: 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. +- **lastfm_key**: The personal API key for requesting art from Last.fm. See + below. - **store_source**: If enabled, fetchart stores the artwork's source in a flexible tag named ``art_source``. See below for the rationale behind this. Default: ``no``. @@ -221,6 +223,15 @@ personal key will give you earlier access to new art. .. _on their blog: https://fanart.tv/2015/01/personal-api-keys/ +Last.fm +''''''' + +To use the Last.fm backend, you need to `register for a Last.fm API key`_. Set +the ``lastfm_key`` configuration option to your API key, then add ``lastfm`` to +the list of sources in your configutation. + +.. _register for a Last.fm API key: https://www.last.fm/api/account/create + Storing the Artwork's Source ----------------------------