diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 93d3f2c57..64bad4442 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -67,10 +67,15 @@ class Candidate: self.match = match self.size = size - def _validate(self, plugin): + def _validate(self, plugin, skip_check_for=None): """Determine whether the candidate artwork is valid based on its dimensions (width and ratio). + `skip_check_for` is a check or list of checks to skip. This is used to + avoid redundant checks when the candidate has already been + validated for a particular operation without changing + plugin configuration. + Return `CANDIDATE_BAD` if the file is unusable. Return `CANDIDATE_EXACT` if the file is usable as-is. Return `CANDIDATE_DOWNSCALE` if the file must be rescaled. @@ -82,6 +87,11 @@ class Candidate: if not self.path: return self.CANDIDATE_BAD + if skip_check_for is None: + skip_check_for = [] + if isinstance(skip_check_for, int): + skip_check_for = [skip_check_for] + if not ( plugin.enforce_ratio or plugin.minwidth @@ -180,30 +190,51 @@ class Candidate: plugin.cover_format, ) - if downscale: + if downscale and (not self.CANDIDATE_DOWNSCALE in skip_check_for): return self.CANDIDATE_DOWNSCALE - elif downsize: - return self.CANDIDATE_DOWNSIZE - elif plugin.deinterlace: - return self.CANDIDATE_DEINTERLACE - elif reformat: + if reformat and (not self.CANDIDATE_REFORMAT in skip_check_for): return self.CANDIDATE_REFORMAT - else: - return self.CANDIDATE_EXACT + if plugin.deinterlace and ( + not self.CANDIDATE_DEINTERLACE in skip_check_for + ): + return self.CANDIDATE_DEINTERLACE + if downsize and (not self.CANDIDATE_DOWNSIZE in skip_check_for): + return self.CANDIDATE_DOWNSIZE + return self.CANDIDATE_EXACT - def validate(self, plugin): - self.check = self._validate(plugin) + def validate(self, plugin, skip_check_for=None): + self.check = self._validate(plugin, skip_check_for) return self.check def resize(self, plugin): - if self.check == self.CANDIDATE_DOWNSCALE: + """Resize the candidate artwork according to the plugin's + configuration until it is valid or no further resizing is + possible. + """ + # validate the candidate in case it hasn't been done yet + current_check = self.validate(plugin) + checks_performed = [] + + # we don't want to resize the image if it's valid or bad + while current_check not in [self.CANDIDATE_BAD, self.CANDIDATE_EXACT]: + self._resize(plugin, current_check) + checks_performed.append(current_check) + current_check = self.validate( + plugin, skip_check_for=checks_performed + ) + + def _resize(self, plugin, check=None): + """Resize the candidate artwork according to the plugin's + configuration and the specified check. + """ + if check == self.CANDIDATE_DOWNSCALE: self.path = ArtResizer.shared.resize( plugin.maxwidth, self.path, quality=plugin.quality, max_filesize=plugin.max_filesize, ) - elif self.check == self.CANDIDATE_DOWNSIZE: + elif check == self.CANDIDATE_DOWNSIZE: # dimensions are correct, so maxwidth is set to maximum dimension self.path = ArtResizer.shared.resize( max(self.size), @@ -211,9 +242,9 @@ class Candidate: quality=plugin.quality, max_filesize=plugin.max_filesize, ) - elif self.check == self.CANDIDATE_DEINTERLACE: + elif check == self.CANDIDATE_DEINTERLACE: self.path = ArtResizer.shared.deinterlace(self.path) - elif self.check == self.CANDIDATE_REFORMAT: + elif check == self.CANDIDATE_REFORMAT: self.path = ArtResizer.shared.reformat( self.path, plugin.cover_format,