From b5ec26c9495080082e8a64010fe684fe2bf66ff0 Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Sat, 4 Apr 2015 20:52:08 +0200 Subject: [PATCH 1/2] Fetchart: minwidth & enforce_ratio options - Minimum image width can be specified via minwidth (default `0`) - The image ratio can be enforced to 1:1 using `enforce_ratio` (default `no`) See #1058 --- beetsplug/fetchart.py | 24 ++++++++++++++++++++---- docs/plugins/fetchart.rst | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 7db05583b..3eaa3d3e8 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -300,7 +300,9 @@ class FetchArtPlugin(plugins.BeetsPlugin): self.config.add({ 'auto': True, + 'minwidth': 0, 'maxwidth': 0, + 'enforce_ratio': False, 'remote_priority': False, 'cautious': False, 'google_search': False, @@ -312,7 +314,10 @@ class FetchArtPlugin(plugins.BeetsPlugin): # placing them in the filesystem. self.art_paths = {} + self.minwidth = self.config['minwidth'].get(int) self.maxwidth = self.config['maxwidth'].get(int) + self.enforce_ratio = self.config['enforce_ratio'].get(bool) + if self.config['auto']: # Enable two import hooks when fetching is enabled. self.import_stages = [self.fetch_art] @@ -397,6 +402,17 @@ class FetchArtPlugin(plugins.BeetsPlugin): except (IOError, requests.RequestException): self._log.debug(u'error fetching art') + def _is_valid_image_candidate(self, candidate): + if not candidate: + return False + + if not (self.enforce_ratio or self.minwidth): + return True + + size = ArtResizer.shared.get_size(candidate) + return size and size[0] >= self.minwidth and \ + (not self.enforce_ratio or size[0] == size[1]) + def art_for_album(self, album, paths, local_only=False): """Given an Album object, returns a path to downloaded art for the album (or None if no art is found). If `maxwidth`, then images are @@ -412,9 +428,9 @@ class FetchArtPlugin(plugins.BeetsPlugin): cautious = self.config['cautious'].get(bool) if paths: for path in paths: - # FIXME - out = self.fs_source.get(path, cover_names, cautious) - if out: + candidate = self.fs_source.get(path, cover_names, cautious) + if self._is_valid_image_candidate(candidate): + out = candidate break # Web art sources. @@ -424,7 +440,7 @@ class FetchArtPlugin(plugins.BeetsPlugin): if self.maxwidth: url = ArtResizer.shared.proxy_url(self.maxwidth, url) candidate = self._fetch_image(url) - if candidate: + if self._is_valid_image_candidate(candidate): out = candidate break diff --git a/docs/plugins/fetchart.rst b/docs/plugins/fetchart.rst index 36266871b..81f4be6aa 100644 --- a/docs/plugins/fetchart.rst +++ b/docs/plugins/fetchart.rst @@ -38,9 +38,13 @@ file. The available options are: Default: ``cover front art album folder``. - **google_search**: Gather images from Google Image Search. Default: ``no``. +- **minwidth**: Only images with a width bigger or equal to ``minwidth`` 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. +- **enforce_ratio**: Only images with a width:height ratio of 1:1 are + considered as valid album art candidates. Default: ``no``. - **remote_priority**: Query remote sources every time and use local image only as fallback. Default: ``no``; remote (Web) art sources are only queried if no local art is From 2c6f29bfb6f19eba1333f050bc87482eb3bcee14 Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Mon, 6 Apr 2015 11:12:16 +0200 Subject: [PATCH 2/2] Add tests for minwidth and enforce_ratio --- test/test_art.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/test_art.py b/test/test_art.py index 25b254568..ab403ec5d 100644 --- a/test/test_art.py +++ b/test/test_art.py @@ -357,6 +357,72 @@ class ArtImporterTest(UseThePlugin): self._fetch_art(True) +class ArtForAlbumTest(UseThePlugin): + """ Tests that fetchart.art_for_album respects the size + configuration (e.g., minwidth, enforce_ratio) + """ + + IMG_225x225 = os.path.join(_common.RSRC, 'abbey.jpg') + IMG_348x348 = os.path.join(_common.RSRC, 'abbey-different.jpg') + IMG_500x490 = os.path.join(_common.RSRC, 'abbey-similar.jpg') + + def setUp(self): + super(ArtForAlbumTest, self).setUp() + + self.old_fs_source_get = self.plugin.fs_source.get + self.old_fetch_img = self.plugin._fetch_image + self.old_source_urls = self.plugin._source_urls + + def fs_source_get(*_): + return self.image_file + + def source_urls(_): + return [''] + + def fetch_img(_): + return self.image_file + + self.plugin.fs_source.get = fs_source_get + self.plugin._source_urls = source_urls + self.plugin._fetch_image = fetch_img + + def tearDown(self): + self.plugin.fs_source.get = self.old_fs_source_get + self.plugin._source_urls = self.old_source_urls + self.plugin._fetch_image = self.old_fetch_img + super(ArtForAlbumTest, self).tearDown() + + def _assertImageIsValidArt(self, image_file, should_exist): + self.assertExists(image_file) + self.image_file = image_file + + local_artpath = self.plugin.art_for_album(None, [''], True) + remote_artpath = self.plugin.art_for_album(None, [], False) + + self.assertEqual(local_artpath, remote_artpath) + + if should_exist: + self.assertEqual(local_artpath, self.image_file) + self.assertExists(local_artpath) + return local_artpath + else: + self.assertIsNone(local_artpath) + + def test_respect_minwidth(self): + self.plugin.minwidth = 300 + self._assertImageIsValidArt(self.IMG_225x225, False) + self._assertImageIsValidArt(self.IMG_348x348, True) + + def test_respect_enforce_ratio_yes(self): + self.plugin.enforce_ratio = True + self._assertImageIsValidArt(self.IMG_500x490, False) + self._assertImageIsValidArt(self.IMG_225x225, True) + + def test_respect_enforce_ratio_no(self): + self.plugin.enforce_ratio = False + self._assertImageIsValidArt(self.IMG_500x490, True) + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)