diff --git a/plugins/deezerart/__init__.py b/plugins/deezerart/__init__.py index 64728dd3..22bb0fbf 100644 --- a/plugins/deezerart/__init__.py +++ b/plugins/deezerart/__init__.py @@ -1,12 +1,11 @@ PLUGIN_NAME = "Deezer cover art" PLUGIN_AUTHOR = "Fabio Forni " PLUGIN_DESCRIPTION = "Fetch cover arts from Deezer" -PLUGIN_VERSION = '1.1.1' +PLUGIN_VERSION = '1.2' PLUGIN_API_VERSIONS = ['2.5'] PLUGIN_LICENSE = "GPL-3.0-or-later" PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html" -from difflib import SequenceMatcher from typing import Any, List, Optional from urllib.parse import urlsplit @@ -14,6 +13,7 @@ from picard import config from picard.coverart import providers from picard.coverart.image import CoverArtImage +from picard.util.astrcmp import astrcmp from PyQt5 import QtNetwork as QtNet from .deezer import Client, SearchOptions, obj @@ -21,12 +21,13 @@ __version__ = PLUGIN_VERSION +DEFAULT_SIMILARITY_THRESHOLD = 0.6 -def is_similar(str1: str, str2: str) -> bool: + +def is_similar(str1: str, str2: str, min_similarity: float = DEFAULT_SIMILARITY_THRESHOLD) -> bool: if str1 in str2: return True - # Python doc considers a ratio equal to 0.6 a good match. - return SequenceMatcher(None, str1, str2).quick_ratio() >= 0.65 + return astrcmp(str1, str2) >= min_similarity def is_deezer_url(url: str) -> bool: @@ -36,16 +37,21 @@ def is_deezer_url(url: str) -> bool: class OptionsPage(providers.ProviderOptions): NAME = 'Deezer' TITLE = 'Deezer' - options = [config.TextOption('setting', 'deezerart_size', obj.CoverSize.BIG.value)] + options = [ + config.TextOption('setting', 'deezerart_size', obj.CoverSize.BIG.value), + config.FloatOption('setting', 'deezerart_min_similarity', DEFAULT_SIMILARITY_THRESHOLD), + ] _options_ui = Ui_Form def load(self): for s in obj.CoverSize: self.ui.size.addItem(str(s.name).title(), userData=s.value) self.ui.size.setCurrentIndex(self.ui.size.findData(config.setting['deezerart_size'])) + self.ui.min_similarity.setValue(int(config.setting["deezerart_min_similarity"] * 100)) def save(self): config.setting['deezerart_size'] = self.ui.size.currentData() + config.setting['deezerart_min_similarity'] = float(self.ui.min_similarity.value()) / 100.0 class Provider(providers.CoverArtProvider): @@ -81,8 +87,8 @@ def queue_images(self): def error(self, msg): super().error(self._log_prefix + msg) - def log_debug(self, msg: Any): - picard.log.debug('%s%s', self._log_prefix, msg) + def log_debug(self, msg: Any, *args): + picard.log.debug(self._log_prefix + msg, *args) def _url_callback(self, url: str): if is_deezer_url(url): @@ -119,10 +125,15 @@ def _queue_from_search(self, results: List[obj.APIObject], error: Optional[QtNet return artist = self._artist() album = self.metadata['album'] + min_similarity = config.setting['deezerart_min_similarity'] for result in results: if not isinstance(result, obj.Track): continue - if not is_similar(artist, result.artist.name) or not is_similar(album, result.album.title): + if not is_similar(artist, result.artist.name, min_similarity): + self.log_debug('artist similarity below threshold: %r ~ %r', artist, result.artist.title) + continue + if not is_similar(album, result.album.title, min_similarity): + self.log_debug('album similarity below threshold: %r ~ %r', album, result.album.title) continue cover_url = result.album.cover_url(obj.CoverSize(config.setting['deezerart_size'])) self.queue_put(CoverArtImage(cover_url)) diff --git a/plugins/deezerart/options.py b/plugins/deezerart/options.py index e610c396..4936bf06 100644 --- a/plugins/deezerart/options.py +++ b/plugins/deezerart/options.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'plugins/deezerart/options.ui' # -# Created by: PyQt5 UI code generator 5.15.4 +# Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -15,19 +15,33 @@ class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(420, 320) - self.verticalLayout = QtWidgets.QVBoxLayout(Form) - self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") + self.gridLayout = QtWidgets.QGridLayout(Form) + self.gridLayout.setObjectName("gridLayout") self.sizeLabel = QtWidgets.QLabel(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.sizeLabel.sizePolicy().hasHeightForWidth()) + self.sizeLabel.setSizePolicy(sizePolicy) self.sizeLabel.setObjectName("sizeLabel") - self.horizontalLayout.addWidget(self.sizeLabel) + self.gridLayout.addWidget(self.sizeLabel, 0, 0, 1, 1) self.size = QtWidgets.QComboBox(Form) self.size.setObjectName("size") - self.horizontalLayout.addWidget(self.size) - self.verticalLayout.addLayout(self.horizontalLayout) + self.gridLayout.addWidget(self.size, 0, 2, 1, 1) + self.min_similarity_label = QtWidgets.QLabel(Form) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.min_similarity_label.sizePolicy().hasHeightForWidth()) + self.min_similarity_label.setSizePolicy(sizePolicy) + self.min_similarity_label.setObjectName("min_similarity_label") + self.gridLayout.addWidget(self.min_similarity_label, 1, 0, 1, 2) + self.min_similarity = QtWidgets.QSpinBox(Form) + self.min_similarity.setMaximum(100) + self.min_similarity.setObjectName("min_similarity") + self.gridLayout.addWidget(self.min_similarity, 1, 2, 1, 1) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem) + self.gridLayout.addItem(spacerItem, 2, 1, 1, 1) self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) @@ -36,3 +50,6 @@ def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) self.sizeLabel.setText(_translate("Form", "Cover size:")) + self.min_similarity_label.setText(_translate("Form", "Minimal similarity for matches:")) + self.min_similarity.setSuffix(_translate("Form", " %")) + self.min_similarity.setPrefix(_translate("Form", " ")) diff --git a/plugins/deezerart/options.ui b/plugins/deezerart/options.ui index d889194f..9d4ce4de 100644 --- a/plugins/deezerart/options.ui +++ b/plugins/deezerart/options.ui @@ -13,22 +13,50 @@ Form - - - - - - - Cover size: - - - - - - - + + + + + + 0 + 0 + + + + Cover size: + + + + + + + + + + + 0 + 0 + + + + Minimal similarity for matches: + + + + + + + % + + + + + + 100 + + - + Qt::Vertical