diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 7320312271..efa7077b2f 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -399,10 +399,15 @@ def get_image_urls(url, preferred_width=None): if 'Front' not in item['types']: continue - if preferred_width: - yield item['thumbnails'][preferred_width] - else: - yield item['image'] + # If there is a pre-sized thumbnail of the desired size + # we select it. Otherwise, we return the raw image. + image_url: str = item["image"] + if preferred_width is not None: + if isinstance(item.get("thumbnails"), dict): + image_url = item["thumbnails"].get( + preferred_width, image_url + ) + yield image_url except KeyError: pass @@ -422,7 +427,7 @@ def get_image_urls(url, preferred_width=None): yield self._candidate(url=url, match=Candidate.MATCH_EXACT) if 'releasegroup' in self.match_by and album.mb_releasegroupid: - for url in get_image_urls(release_group_url): + for url in get_image_urls(release_group_url, preferred_width): yield self._candidate(url=url, match=Candidate.MATCH_FALLBACK) diff --git a/docs/changelog.rst b/docs/changelog.rst index afb6cc3de2..a17d1c886f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -125,6 +125,12 @@ New features: * :doc:`/plugins/autobpm`: Add the `autobpm` plugin which uses Librosa to calculate the BPM of the audio. :bug:`3856` +* :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where the + `maxwidth` option would not be used to download a pre-sized thumbnail for + release groups, as is already done with releases. +* :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where no cover + would be found when the `maxwidth` option matches a pre-sized thumbnail size, + but no thumbnail is provided by CAA. We now fallback to the raw image. Bug fixes: diff --git a/docs/plugins/fetchart.rst b/docs/plugins/fetchart.rst index 28bc5672ec..6828b93fea 100644 --- a/docs/plugins/fetchart.rst +++ b/docs/plugins/fetchart.rst @@ -41,7 +41,10 @@ file. The available options are: considered as valid album art candidates. Default: 0. - **maxwidth**: A maximum image width to downscale fetched images if they are too big. The resize operation reduces image width to at most ``maxwidth`` - pixels. The height is recomputed so that the aspect ratio is preserved. + pixels. The height is recomputed so that the aspect ratio is preserved. See + the section on :ref:`cover-art-archive-maxwidth` below for additional + information regarding the Cover Art Archive source. + Default: 0 (no maximum is enforced). - **quality**: The JPEG quality level to use when compressing images (when ``maxwidth`` is set). This should be either a number from 1 to 100 or 0 to use the default quality. 65–75 is usually a good starting point. The default @@ -269,7 +272,21 @@ Spotify backend is enabled by default and will update album art if a valid Spoti Cover Art URL ''''''''''''' -The `fetchart` plugin can also use a flexible attribute field ``cover_art_url`` where you can manually specify the image URL to be used as cover art. Any custom plugin can use this field to provide the cover art and ``fetchart`` will use it as a source. +The `fetchart` plugin can also use a flexible attribute field ``cover_art_url`` +where you can manually specify the image URL to be used as cover art. Any custom +plugin can use this field to provide the cover art and ``fetchart`` will use it +as a source. + +.. _cover-art-archive-maxwidth: + +Cover Art Archive Pre-sized Thumbnails +-------------------------------------- + +The CAA provides pre-sized thumbnails of width 250, 500, and 1200 pixels. If you +set the `maxwidth` option to one of these values, the corresponding image will +be downloaded, saving `beets` the need to scale down the image. It can also +speed up the downloading process, as some cover arts can sometimes be very +large. Storing the Artwork's Source ---------------------------- diff --git a/test/test_art.py b/test/test_art.py index b14ec0f596..62b7393a49 100644 --- a/test/test_art.py +++ b/test/test_art.py @@ -130,6 +130,39 @@ class CAAHelper(): } ], "release": "https://musicbrainz.org/release/releaseid" +}""" + RESPONSE_RELEASE_WITHOUT_THUMBNAILS = """{ + "images": [ + { + "approved": false, + "back": false, + "comment": "GIF", + "edit": 12345, + "front": true, + "id": 12345, + "image": "http://coverartarchive.org/release/rid/12345.gif", + "types": [ + "Front" + ] + }, + { + "approved": false, + "back": false, + "comment": "", + "edit": 12345, + "front": false, + "id": 12345, + "image": "http://coverartarchive.org/release/rid/12345.jpg", + "thumbnails": { + "large": "http://coverartarchive.org/release/rgid/12345-500.jpg", + "small": "http://coverartarchive.org/release/rgid/12345-250.jpg" + }, + "types": [ + "Front" + ] + } + ], + "release": "https://musicbrainz.org/release/releaseid" }""" RESPONSE_GROUP = """{ "images": [ @@ -155,6 +188,23 @@ class CAAHelper(): ], "release": "https://musicbrainz.org/release/release-id" }""" + RESPONSE_GROUP_WITHOUT_THUMBNAILS = """{ + "images": [ + { + "approved": false, + "back": false, + "comment": "", + "edit": 12345, + "front": true, + "id": 12345, + "image": "http://coverartarchive.org/release/releaseid/12345.jpg", + "types": [ + "Front" + ] + } + ], + "release": "https://musicbrainz.org/release/release-id" + }""" def mock_caa_response(self, url, json): responses.add(responses.GET, url, body=json, @@ -521,6 +571,42 @@ def test_caa_finds_image(self): self.assertEqual(len(responses.calls), 2) self.assertEqual(responses.calls[0].request.url, self.RELEASE_URL) + def test_fetchart_uses_caa_pre_sized_maxwidth_thumbs(self): + # CAA provides pre-sized thumbnails of width 250px, 500px, and 1200px + # We only test with one of them here + maxwidth = 1200 + self.settings = Settings(maxwidth=maxwidth) + + album = _common.Bag( + mb_albumid=self.MBID_RELASE, mb_releasegroupid=self.MBID_GROUP + ) + self.mock_caa_response(self.RELEASE_URL, self.RESPONSE_RELEASE) + self.mock_caa_response(self.GROUP_URL, self.RESPONSE_GROUP) + candidates = list(self.source.get(album, self.settings, [])) + self.assertEqual(len(candidates), 3) + for candidate in candidates: + self.assertTrue(f"-{maxwidth}.jpg" in candidate.url) + + def test_caa_finds_image_if_maxwidth_is_set_and_thumbnails_is_empty(self): + # CAA provides pre-sized thumbnails of width 250px, 500px, and 1200px + # We only test with one of them here + maxwidth = 1200 + self.settings = Settings(maxwidth=maxwidth) + + album = _common.Bag( + mb_albumid=self.MBID_RELASE, mb_releasegroupid=self.MBID_GROUP + ) + self.mock_caa_response( + self.RELEASE_URL, self.RESPONSE_RELEASE_WITHOUT_THUMBNAILS + ) + self.mock_caa_response( + self.GROUP_URL, self.RESPONSE_GROUP_WITHOUT_THUMBNAILS, + ) + candidates = list(self.source.get(album, self.settings, [])) + self.assertEqual(len(candidates), 3) + for candidate in candidates: + self.assertFalse(f"-{maxwidth}.jpg" in candidate.url) + class FanartTVTest(UseThePlugin): RESPONSE_MULTIPLE = """{