diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 9c5ec5b6b..3abb3ee57 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -17,10 +17,8 @@ import os import re -import tempfile from collections import OrderedDict from contextlib import closing -from mimetypes import guess_extension from tempfile import NamedTemporaryFile import confuse @@ -31,6 +29,13 @@ from beets import config, importer, plugins, ui, util from beets.util import bytestring_path, py3_path, sorted_walk, syspath from beets.util.artresizer import ArtResizer +try: + from bs4 import BeautifulSoup + HAS_BEAUTIFUL_SOUP = True +except ImportError: + HAS_BEAUTIFUL_SOUP = False + + CONTENT_TYPES = { 'image/jpeg': [b'jpg', b'jpeg'], 'image/png': [b'png'] @@ -907,10 +912,35 @@ class LastFM(RemoteArtSource): .format(response.text)) return + +class Spotify(RemoteArtSource): + NAME = "Spotify" + + SPOTIFY_ALBUM_URL = 'https://open.spotify.com/album/' + + def get(self, album, plugin, paths): + url = self.SPOTIFY_ALBUM_URL + album.mb_albumid + try: + response = requests.get(url) + response.raise_for_status() + except requests.RequestException as e: + self._log.debug("Error: " + str(e)) + return + try: + html = response.text + soup = BeautifulSoup(html, 'html.parser') + image_url = soup.find('meta', + attrs={'property': 'og:image'})['content'] + yield self._candidate(url=image_url, + match=Candidate.MATCH_EXACT) + except ValueError: + self._log.debug('Spotify: error loading response: {}' + .format(response.text)) + return # Try each source in turn. SOURCES_ALL = ['filesystem', 'coverart', 'itunes', 'amazon', 'albumart', - 'wikipedia', 'google', 'fanarttv', 'lastfm', 'cover_art_url'] + 'spotify', 'wikipedia', 'google', 'fanarttv', 'lastfm', 'cover_art_url'] ART_SOURCES = { 'filesystem': FileSystem, @@ -923,6 +953,7 @@ ART_SOURCES = { 'fanarttv': FanartTV, 'lastfm': LastFM, 'cover_art_url': CoverArtUrl, + 'spotify': Spotify, } SOURCE_NAMES = {v: k for k, v in ART_SOURCES.items()} @@ -1011,6 +1042,12 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): if not self.config['lastfm_key'].get() and \ 'lastfm' in available_sources: available_sources.remove('lastfm') + if not HAS_BEAUTIFUL_SOUP and \ + 'spotify' in available_sources: + self._log.debug('To use Spotify as an album art source, ' + 'you must install the beautifulsoup4 module. See ' + 'the documentation for further details.') + available_sources.remove('spotify') 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 b7aaa7dff..d7ddf6d03 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,7 @@ for Python 3.6). New features: +* :doc:`/plugins/fetchart`: The plugin can now get album art from `spotify`. * Added option to specify a URL in the `embedart` plugin. :bug:`83` * :ref:`list-cmd` `singleton:true` queries have been made faster diff --git a/docs/plugins/fetchart.rst b/docs/plugins/fetchart.rst index 2b884c5ad..2dc8cbf21 100644 --- a/docs/plugins/fetchart.rst +++ b/docs/plugins/fetchart.rst @@ -50,7 +50,7 @@ file. The available options are: PIL defaults to 75. Default: 0 (disabled) - **max_filesize**: The maximum size of a target piece of cover art in bytes. - When using an ImageMagick backend this sets + When using an ImageMagick backend this sets ``-define jpeg:extent=max_filesize``. Using PIL this will reduce JPG quality by up to 50% to attempt to reach the target filesize. Neither method is *guaranteed* to reach the target size, however in most cases it should @@ -254,6 +254,18 @@ the list of sources in your configutation. .. _register for a Last.fm API key: https://www.last.fm/api/account/create +Spotify +''''''' + +Spotify backend requires `BeautifulSoup`_, which you can install using `pip`_ by typing:: + + pip install beautifulsoup4 + +Spotify backend is enabled by default and will update album art if a valid Spotify album id is found. + +.. _pip: https://pip.pypa.io +.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/bs4/doc/ + Storing the Artwork's Source ---------------------------- diff --git a/setup.py b/setup.py index a5c57cc7d..26cebaa87 100755 --- a/setup.py +++ b/setup.py @@ -135,7 +135,7 @@ setup( # Plugin (optional) dependencies: 'absubmit': ['requests'], - 'fetchart': ['requests', 'Pillow'], + 'fetchart': ['requests', 'Pillow', 'beautifulsoup4'], 'embedart': ['Pillow'], 'embyupdate': ['requests'], 'chroma': ['pyacoustid'],