From 18bcadcfdcb46494685f2ad8fc11062275f10364 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Thu, 27 Apr 2023 18:10:09 -0400 Subject: [PATCH 1/8] Add spotify art source --- beetsplug/fetchart.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 4a9693e64..1b1e96560 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -20,6 +20,7 @@ import os import re from tempfile import NamedTemporaryFile from collections import OrderedDict +from bs4 import BeautifulSoup import requests @@ -893,11 +894,37 @@ 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): + if not len(album.mb_albumid) == 22 and \ + not album.data_source == 'Spotify': + return + URL = self.SPOTIFY_ALBUM_URL + album.mb_albumid + try: + response = requests.get(URL) + except requests.RequestException: + self._log.debug('Spotify: error receiving response') + 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'] +SOURCES_ALL = ['filesystem', 'coverart', 'itunes', 'amazon', 'albumart', + 'wikipedia', 'google', 'fanarttv', 'lastfm', 'spotify'] ART_SOURCES = { 'filesystem': FileSystem, @@ -909,6 +936,7 @@ ART_SOURCES = { 'google': GoogleImages, 'fanarttv': FanartTV, 'lastfm': LastFM, + 'spotify': Spotify, } SOURCE_NAMES = {v: k for k, v in ART_SOURCES.items()} From 036667c51e233516d07c191c7ad43f458d83e6b2 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Thu, 27 Apr 2023 18:13:39 -0400 Subject: [PATCH 2/8] change variable to lower case --- beetsplug/fetchart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 1b1e96560..68e2fd536 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -904,9 +904,9 @@ class Spotify(RemoteArtSource): if not len(album.mb_albumid) == 22 and \ not album.data_source == 'Spotify': return - URL = self.SPOTIFY_ALBUM_URL + album.mb_albumid + url = self.SPOTIFY_ALBUM_URL + album.mb_albumid try: - response = requests.get(URL) + response = requests.get(url) except requests.RequestException: self._log.debug('Spotify: error receiving response') return From 654b57f50abc40d5b234659eb9668283985ec5d8 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 08:47:32 -0400 Subject: [PATCH 3/8] Make beautifulsoup optional --- beetsplug/fetchart.py | 35 +++++++++++++++++++++-------------- setup.py | 2 +- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 68e2fd536..a825eed3b 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -15,25 +15,26 @@ """Fetches album art. """ -from contextlib import closing import os import re -from tempfile import NamedTemporaryFile from collections import OrderedDict -from bs4 import BeautifulSoup +from contextlib import closing +from tempfile import NamedTemporaryFile -import requests - -from beets import plugins -from beets import importer -from beets import ui -from beets import util -from beets import config -from mediafile import image_mime_type -from beets.util.artresizer import ArtResizer -from beets.util import sorted_walk -from beets.util import syspath, bytestring_path, py3_path import confuse +import requests +from mediafile import image_mime_type + +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'], @@ -1025,6 +1026,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 a 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/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'], From 34fe9344ae5ad3a910a769553cda2ff88f97b9d5 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 08:50:01 -0400 Subject: [PATCH 4/8] change preposition --- beetsplug/fetchart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index a825eed3b..857d36c5c 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -1028,7 +1028,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): available_sources.remove('lastfm') if not HAS_BEAUTIFUL_SOUP and \ 'spotify' in available_sources: - self._log.debug('To use Spotify as a album art source, ' + 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') From 718f682d76efab4c1489cbbb2cb86d38d7b62bb5 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 08:57:29 -0400 Subject: [PATCH 5/8] Update changelog.rst --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) 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 From 731015b6d57ce45675e21fedf309da3f38d2b3af Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 11:05:26 -0400 Subject: [PATCH 6/8] Update docs --- docs/plugins/fetchart.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 ---------------------------- From 067a3633d3c8d174dbd0eddbc474f06da671eb13 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 18:02:17 -0400 Subject: [PATCH 7/8] Change error handling --- beetsplug/fetchart.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 857d36c5c..bb4d1c7d5 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -902,14 +902,12 @@ class Spotify(RemoteArtSource): SPOTIFY_ALBUM_URL = 'https://open.spotify.com/album/' def get(self, album, plugin, paths): - if not len(album.mb_albumid) == 22 and \ - not album.data_source == 'Spotify': - return url = self.SPOTIFY_ALBUM_URL + album.mb_albumid try: response = requests.get(url) - except requests.RequestException: - self._log.debug('Spotify: error receiving response') + response.raise_for_status() + except requests.exceptions.HTTPError as e: + self._log.debug("Error: " + str(e)) return try: html = response.text From d4eb3a6b82410b59442cc102376d3d3e6db19228 Mon Sep 17 00:00:00 2001 From: Alok Saboo Date: Fri, 28 Apr 2023 18:05:27 -0400 Subject: [PATCH 8/8] Update fetchart.py --- beetsplug/fetchart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index bb4d1c7d5..a50f98f01 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -906,7 +906,7 @@ class Spotify(RemoteArtSource): try: response = requests.get(url) response.raise_for_status() - except requests.exceptions.HTTPError as e: + except requests.RequestException as e: self._log.debug("Error: " + str(e)) return try: