From c584376e68e7f64b9d3f39f6dd1cdf8393b99a04 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Fri, 23 Nov 2018 14:24:42 +0100 Subject: [PATCH 1/8] PICARD-1395: Renamed "Folksonomy Tags" options to "Genres" --- picard/ui/options/dialog.py | 2 +- .../ui/options/{folksonomy.py => genres.py} | 12 +++--- ...ons_folksonomy.py => ui_options_genres.py} | 18 ++++----- ...ptions_folksonomy.ui => options_genres.ui} | 37 ++++++++++++++++--- 4 files changed, 48 insertions(+), 21 deletions(-) rename picard/ui/options/{folksonomy.py => genres.py} (90%) rename picard/ui/{ui_options_folksonomy.py => ui_options_genres.py} (91%) rename ui/{options_folksonomy.ui => options_genres.ui} (83%) diff --git a/picard/ui/options/dialog.py b/picard/ui/options/dialog.py index a08958d27..c34582085 100644 --- a/picard/ui/options/dialog.py +++ b/picard/ui/options/dialog.py @@ -40,8 +40,8 @@ from picard.ui.options import ( cdlookup, cover, fingerprinting, - folksonomy, general, + genres, interface, matching, metadata, diff --git a/picard/ui/options/folksonomy.py b/picard/ui/options/genres.py similarity index 90% rename from picard/ui/options/folksonomy.py rename to picard/ui/options/genres.py index 2630f1700..0a78c7f10 100644 --- a/picard/ui/options/folksonomy.py +++ b/picard/ui/options/genres.py @@ -23,13 +23,13 @@ from picard.ui.options import ( OptionsPage, register_options_page, ) -from picard.ui.ui_options_folksonomy import Ui_FolksonomyOptionsPage +from picard.ui.ui_options_genres import Ui_GenresOptionsPage -class FolksonomyOptionsPage(OptionsPage): +class GenresOptionsPage(OptionsPage): - NAME = "folsonomy" - TITLE = N_("Folksonomy Tags") + NAME = "genres" + TITLE = N_("Genres") PARENT = "metadata" SORT_ORDER = 20 ACTIVE = True @@ -45,7 +45,7 @@ class FolksonomyOptionsPage(OptionsPage): def __init__(self, parent=None): super().__init__(parent) - self.ui = Ui_FolksonomyOptionsPage() + self.ui = Ui_GenresOptionsPage() self.ui.setupUi(self) def load(self): @@ -65,4 +65,4 @@ class FolksonomyOptionsPage(OptionsPage): config.setting["artists_tags"] = self.ui.artists_tags.isChecked() -register_options_page(FolksonomyOptionsPage) +register_options_page(GenresOptionsPage) diff --git a/picard/ui/ui_options_folksonomy.py b/picard/ui/ui_options_genres.py similarity index 91% rename from picard/ui/ui_options_folksonomy.py rename to picard/ui/ui_options_genres.py index d01b500ce..51bfaed33 100644 --- a/picard/ui/ui_options_folksonomy.py +++ b/picard/ui/ui_options_genres.py @@ -5,13 +5,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets -class Ui_FolksonomyOptionsPage(object): - def setupUi(self, FolksonomyOptionsPage): - FolksonomyOptionsPage.setObjectName("FolksonomyOptionsPage") - FolksonomyOptionsPage.resize(590, 304) - self.verticalLayout_2 = QtWidgets.QVBoxLayout(FolksonomyOptionsPage) +class Ui_GenresOptionsPage(object): + def setupUi(self, GenresOptionsPage): + GenresOptionsPage.setObjectName("GenresOptionsPage") + GenresOptionsPage.resize(590, 304) + self.verticalLayout_2 = QtWidgets.QVBoxLayout(GenresOptionsPage) self.verticalLayout_2.setObjectName("verticalLayout_2") - self.rename_files_3 = QtWidgets.QGroupBox(FolksonomyOptionsPage) + self.rename_files_3 = QtWidgets.QGroupBox(GenresOptionsPage) self.rename_files_3.setObjectName("rename_files_3") self.verticalLayout = QtWidgets.QVBoxLayout(self.rename_files_3) self.verticalLayout.setObjectName("verticalLayout") @@ -94,10 +94,10 @@ class Ui_FolksonomyOptionsPage(object): self.label_5.setBuddy(self.min_tag_usage) self.label_6.setBuddy(self.min_tag_usage) - self.retranslateUi(FolksonomyOptionsPage) - QtCore.QMetaObject.connectSlotsByName(FolksonomyOptionsPage) + self.retranslateUi(GenresOptionsPage) + QtCore.QMetaObject.connectSlotsByName(GenresOptionsPage) - def retranslateUi(self, FolksonomyOptionsPage): + def retranslateUi(self, GenresOptionsPage): _translate = QtCore.QCoreApplication.translate self.rename_files_3.setTitle(_("Folksonomy Tags")) self.ignore_tags_2.setText(_("Ignore tags:")) diff --git a/ui/options_folksonomy.ui b/ui/options_genres.ui similarity index 83% rename from ui/options_folksonomy.ui rename to ui/options_genres.ui index a6c29b68f..e93f4878b 100644 --- a/ui/options_folksonomy.ui +++ b/ui/options_genres.ui @@ -1,7 +1,7 @@ - FolksonomyOptionsPage - + GenresOptionsPage + 0 @@ -49,7 +49,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -85,7 +94,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -118,7 +136,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 From a5d33bc914fe490ecb2df02ab3a5c1daf1782f97 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Fri, 23 Nov 2018 14:30:39 +0100 Subject: [PATCH 2/8] PICARD-1395: Moved folksonomy_tags opton from metadata to genre dialog --- picard/ui/options/genres.py | 3 +++ picard/ui/options/metadata.py | 3 --- picard/ui/ui_options_genres.py | 4 ++++ picard/ui/ui_options_metadata.py | 8 ++------ ui/options_genres.ui | 7 +++++++ ui/options_metadata.ui | 8 -------- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/picard/ui/options/genres.py b/picard/ui/options/genres.py index 0a78c7f10..7da9a7e4f 100644 --- a/picard/ui/options/genres.py +++ b/picard/ui/options/genres.py @@ -41,6 +41,7 @@ class GenresOptionsPage(OptionsPage): config.TextOption("setting", "join_tags", ""), config.BoolOption("setting", "only_my_tags", False), config.BoolOption("setting", "artists_tags", False), + config.BoolOption("setting", "folksonomy_tags", False), ] def __init__(self, parent=None): @@ -55,6 +56,7 @@ class GenresOptionsPage(OptionsPage): self.ui.ignore_tags.setText(config.setting["ignore_tags"]) self.ui.only_my_tags.setChecked(config.setting["only_my_tags"]) self.ui.artists_tags.setChecked(config.setting["artists_tags"]) + self.ui.folksonomy_tags.setChecked(config.setting["folksonomy_tags"]) def save(self): config.setting["max_tags"] = self.ui.max_tags.value() @@ -63,6 +65,7 @@ class GenresOptionsPage(OptionsPage): config.setting["ignore_tags"] = self.ui.ignore_tags.text() config.setting["only_my_tags"] = self.ui.only_my_tags.isChecked() config.setting["artists_tags"] = self.ui.artists_tags.isChecked() + config.setting["folksonomy_tags"] = self.ui.folksonomy_tags.isChecked() register_options_page(GenresOptionsPage) diff --git a/picard/ui/options/metadata.py b/picard/ui/options/metadata.py index 37512a4fe..b8f8a0fb5 100644 --- a/picard/ui/options/metadata.py +++ b/picard/ui/options/metadata.py @@ -42,7 +42,6 @@ class MetadataOptionsPage(OptionsPage): config.BoolOption("setting", "translate_artist_names", False), config.BoolOption("setting", "release_ars", True), config.BoolOption("setting", "track_ars", False), - config.BoolOption("setting", "folksonomy_tags", False), config.BoolOption("setting", "convert_punctuation", True), config.BoolOption("setting", "standardize_artists", False), config.BoolOption("setting", "standardize_instruments", True), @@ -71,7 +70,6 @@ class MetadataOptionsPage(OptionsPage): self.ui.convert_punctuation.setChecked(config.setting["convert_punctuation"]) self.ui.release_ars.setChecked(config.setting["release_ars"]) self.ui.track_ars.setChecked(config.setting["track_ars"]) - self.ui.folksonomy_tags.setChecked(config.setting["folksonomy_tags"]) self.ui.va_name.setText(config.setting["va_name"]) self.ui.nat_name.setText(config.setting["nat_name"]) self.ui.standardize_artists.setChecked(config.setting["standardize_artists"]) @@ -83,7 +81,6 @@ class MetadataOptionsPage(OptionsPage): config.setting["convert_punctuation"] = self.ui.convert_punctuation.isChecked() config.setting["release_ars"] = self.ui.release_ars.isChecked() config.setting["track_ars"] = self.ui.track_ars.isChecked() - config.setting["folksonomy_tags"] = self.ui.folksonomy_tags.isChecked() config.setting["va_name"] = self.ui.va_name.text() nat_name = self.ui.nat_name.text() if nat_name != config.setting["nat_name"]: diff --git a/picard/ui/ui_options_genres.py b/picard/ui/ui_options_genres.py index 51bfaed33..d2d856f9a 100644 --- a/picard/ui/ui_options_genres.py +++ b/picard/ui/ui_options_genres.py @@ -28,6 +28,9 @@ class Ui_GenresOptionsPage(object): self.artists_tags.setEnabled(True) self.artists_tags.setObjectName("artists_tags") self.verticalLayout.addWidget(self.artists_tags) + self.folksonomy_tags = QtWidgets.QCheckBox(self.rename_files_3) + self.folksonomy_tags.setObjectName("folksonomy_tags") + self.verticalLayout.addWidget(self.folksonomy_tags) self.hboxlayout = QtWidgets.QHBoxLayout() self.hboxlayout.setContentsMargins(0, 0, 0, 0) self.hboxlayout.setSpacing(6) @@ -103,6 +106,7 @@ class Ui_GenresOptionsPage(object): self.ignore_tags_2.setText(_("Ignore tags:")) self.only_my_tags.setText(_("Only use my tags")) self.artists_tags.setText(_("Fall back on album\'s artists tags if no tags are found for the release or release group")) + self.folksonomy_tags.setText(_("Use folksonomy tags as genre")) self.label_5.setText(_("Minimal tag usage:")) self.min_tag_usage.setSuffix(_(" %")) self.label_6.setText(_("Maximum number of tags:")) diff --git a/picard/ui/ui_options_metadata.py b/picard/ui/ui_options_metadata.py index c3abce3dc..1b98d547b 100644 --- a/picard/ui/ui_options_metadata.py +++ b/picard/ui/ui_options_metadata.py @@ -43,9 +43,6 @@ class Ui_MetadataOptionsPage(object): self.track_ars = QtWidgets.QCheckBox(self.metadata_groupbox) self.track_ars.setObjectName("track_ars") self.verticalLayout_3.addWidget(self.track_ars) - self.folksonomy_tags = QtWidgets.QCheckBox(self.metadata_groupbox) - self.folksonomy_tags.setObjectName("folksonomy_tags") - self.verticalLayout_3.addWidget(self.folksonomy_tags) self.verticalLayout.addWidget(self.metadata_groupbox) self.custom_fields_groupbox = QtWidgets.QGroupBox(MetadataOptionsPage) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) @@ -89,8 +86,7 @@ class Ui_MetadataOptionsPage(object): MetadataOptionsPage.setTabOrder(self.standardize_artists, self.convert_punctuation) MetadataOptionsPage.setTabOrder(self.convert_punctuation, self.release_ars) MetadataOptionsPage.setTabOrder(self.release_ars, self.track_ars) - MetadataOptionsPage.setTabOrder(self.track_ars, self.folksonomy_tags) - MetadataOptionsPage.setTabOrder(self.folksonomy_tags, self.va_name) + MetadataOptionsPage.setTabOrder(self.track_ars, self.va_name) MetadataOptionsPage.setTabOrder(self.va_name, self.va_name_default) MetadataOptionsPage.setTabOrder(self.va_name_default, self.nat_name) MetadataOptionsPage.setTabOrder(self.nat_name, self.nat_name_default) @@ -104,9 +100,9 @@ class Ui_MetadataOptionsPage(object): self.convert_punctuation.setText(_("Convert Unicode punctuation characters to ASCII")) self.release_ars.setText(_("Use release relationships")) self.track_ars.setText(_("Use track relationships")) - self.folksonomy_tags.setText(_("Use folksonomy tags as genre")) self.custom_fields_groupbox.setTitle(_("Custom Fields")) self.label_6.setText(_("Various artists:")) self.label_7.setText(_("Non-album tracks:")) self.nat_name_default.setText(_("Default")) self.va_name_default.setText(_("Default")) + diff --git a/ui/options_genres.ui b/ui/options_genres.ui index e93f4878b..841bf5e61 100644 --- a/ui/options_genres.ui +++ b/ui/options_genres.ui @@ -44,6 +44,13 @@ + + + + Use folksonomy tags as genre + + + diff --git a/ui/options_metadata.ui b/ui/options_metadata.ui index 15cf61317..d78660740 100644 --- a/ui/options_metadata.ui +++ b/ui/options_metadata.ui @@ -77,13 +77,6 @@ - - - - Use folksonomy tags as genre - - - @@ -176,7 +169,6 @@ convert_punctuation release_ars track_ars - folksonomy_tags va_name va_name_default nat_name From 5988bd66ad0a9e80ad54663fd8c6e72d7345a93d Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Fri, 23 Nov 2018 15:55:37 +0100 Subject: [PATCH 3/8] PICARD-1395: Load MusicBrainz genres --- picard/album.py | 7 +---- picard/dataobj.py | 12 ++++++++ picard/mbjson.py | 28 ++++++++--------- picard/track.py | 9 ++---- picard/ui/options/genres.py | 3 ++ picard/ui/ui_options_genres.py | 56 ++++++++++++++++++---------------- test/data/ws_data/release.json | 43 +++++++++++++++----------- test/test_dataobj.py | 54 ++++++++++++++++++++++++++++++++ test/test_mbjson.py | 4 ++- ui/options_genres.ui | 46 ++++++++++++++++------------ 10 files changed, 169 insertions(+), 93 deletions(-) create mode 100644 test/test_dataobj.py diff --git a/picard/album.py b/picard/album.py index 9825de6c9..e5441935f 100644 --- a/picard/album.py +++ b/picard/album.py @@ -379,12 +379,7 @@ class Album(DataObject, Item): inc += ['artist-rels', 'release-rels', 'url-rels', 'recording-rels', 'work-rels'] if config.setting['track_ars']: inc += ['recording-level-rels', 'work-level-rels'] - if config.setting['folksonomy_tags']: - if config.setting['only_my_tags']: - require_authentication = True - inc += ['user-tags'] - else: - inc += ['tags'] + require_authentication = self.set_genre_inc_params(inc) if config.setting['enable_ratings']: require_authentication = True inc += ['user-ratings'] diff --git a/picard/dataobj.py b/picard/dataobj.py index 31d4e9602..64db4b4ee 100644 --- a/picard/dataobj.py +++ b/picard/dataobj.py @@ -17,6 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from picard import config from picard.util import LockableObject @@ -31,6 +32,17 @@ class DataObject(LockableObject): def add_folksonomy_tag(self, name, count): self.folksonomy_tags[name] = self.folksonomy_tags.get(name, 0) + count + def set_genre_inc_params(self, inc): + require_authentication = False + if config.setting['use_genres']: + use_folksonomy = config.setting['folksonomy_tags'] + if config.setting['only_my_tags']: + require_authentication = True + inc += ['user-tags'] if use_folksonomy else ['user-genres'] + else: + inc += ['tags'] if use_folksonomy else ['genres'] + return require_authentication + @staticmethod def merge_folksonomy_tags(this, that): for name, count in that.items(): diff --git a/picard/mbjson.py b/picard/mbjson.py index f8ee39f4b..581b31e4c 100644 --- a/picard/mbjson.py +++ b/picard/mbjson.py @@ -369,10 +369,10 @@ def recording_to_metadata(node, m, track=None): track.append_track_artist(artist['artist']['id']) elif key == 'relations': _relations_to_metadata(value, m) - elif key == 'tags' and track: - add_folksonomy_tags(value, track) - elif key == 'user-tags' and track: - add_user_folksonomy_tags(value, track) + elif key in ('genres', 'tags') and track: + add_genres(value, track) + elif key in ('user-genres', 'user-tags') and track: + add_user_genres(value, track) elif key == 'isrcs': add_isrcs_to_metadata(value, m) elif key == 'video' and value: @@ -458,10 +458,10 @@ def release_to_metadata(node, m, album=None): m['~releaselanguage'] = value['language'] if 'script' in value: m['script'] = value['script'] - elif key == 'tags': - add_folksonomy_tags(value, album) - elif key == 'user-tags': - add_user_folksonomy_tags(value, album) + elif key in ('genres', 'tags'): + add_genres(value, album) + elif key in ('user-genres', 'user-tags'): + add_user_genres(value, album) def release_group_to_metadata(node, m, release_group=None): @@ -472,10 +472,10 @@ def release_group_to_metadata(node, m, release_group=None): continue if key in _RELEASE_GROUP_TO_METADATA: m[_RELEASE_GROUP_TO_METADATA[key]] = value - elif key == 'tags': - add_folksonomy_tags(value, release_group) - elif key == 'user-tags': - add_user_folksonomy_tags(value, release_group) + elif key in ('genres', 'tags'): + add_genres(value, release_group) + elif key in ('user-genres', 'user-tags'): + add_user_genres(value, release_group) elif key == 'primary-type': m['~primaryreleasetype'] = value.lower() elif key == 'secondary-types': @@ -490,7 +490,7 @@ def add_secondary_release_types(node, m): m.add_unique('~secondaryreleasetype', secondary_type.lower()) -def add_folksonomy_tags(node, obj): +def add_genres(node, obj): if obj is not None: for tag in node: key = tag['name'] @@ -499,7 +499,7 @@ def add_folksonomy_tags(node, obj): obj.add_folksonomy_tag(key, count) -def add_user_folksonomy_tags(node, obj): +def add_user_genres(node, obj): if obj is not None: for tag in node: key = tag['name'] diff --git a/picard/track.py b/picard/track.py index 0868dd5e6..c930a69de 100644 --- a/picard/track.py +++ b/picard/track.py @@ -190,7 +190,7 @@ class Track(DataObject, Item): if tm['title'] == SILENCE_TRACK_TITLE: tm['~silence'] = '1' - if config.setting['folksonomy_tags']: + if config.setting['use_genres']: self._convert_folksonomy_tags_to_genre() # Convert Unicode punctuation @@ -284,12 +284,7 @@ class NonAlbumTrack(Track): if config.setting["track_ars"]: inc += ["artist-rels", "url-rels", "recording-rels", "work-rels", "work-level-rels"] - if config.setting["folksonomy_tags"]: - if config.setting["only_my_tags"]: - mblogin = True - inc += ["user-tags"] - else: - inc += ["tags"] + mblogin = self.set_genre_inc_params(inc) if config.setting["enable_ratings"]: mblogin = True inc += ["user-ratings"] diff --git a/picard/ui/options/genres.py b/picard/ui/options/genres.py index 7da9a7e4f..8585eb256 100644 --- a/picard/ui/options/genres.py +++ b/picard/ui/options/genres.py @@ -35,6 +35,7 @@ class GenresOptionsPage(OptionsPage): ACTIVE = True options = [ + config.BoolOption("setting", "use_genres", False), config.IntOption("setting", "max_tags", 5), config.IntOption("setting", "min_tag_usage", 90), config.TextOption("setting", "ignore_tags", "seen live,favorites,fixme,owned"), @@ -50,6 +51,7 @@ class GenresOptionsPage(OptionsPage): self.ui.setupUi(self) def load(self): + self.ui.use_genres.setChecked(config.setting["use_genres"]) self.ui.max_tags.setValue(config.setting["max_tags"]) self.ui.min_tag_usage.setValue(config.setting["min_tag_usage"]) self.ui.join_tags.setEditText(config.setting["join_tags"]) @@ -59,6 +61,7 @@ class GenresOptionsPage(OptionsPage): self.ui.folksonomy_tags.setChecked(config.setting["folksonomy_tags"]) def save(self): + config.setting["use_genres"] = self.ui.use_genres.isChecked() config.setting["max_tags"] = self.ui.max_tags.value() config.setting["min_tag_usage"] = self.ui.min_tag_usage.value() config.setting["join_tags"] = self.ui.join_tags.currentText() diff --git a/picard/ui/ui_options_genres.py b/picard/ui/ui_options_genres.py index d2d856f9a..7285d722f 100644 --- a/picard/ui/ui_options_genres.py +++ b/picard/ui/ui_options_genres.py @@ -11,31 +11,33 @@ class Ui_GenresOptionsPage(object): GenresOptionsPage.resize(590, 304) self.verticalLayout_2 = QtWidgets.QVBoxLayout(GenresOptionsPage) self.verticalLayout_2.setObjectName("verticalLayout_2") - self.rename_files_3 = QtWidgets.QGroupBox(GenresOptionsPage) - self.rename_files_3.setObjectName("rename_files_3") - self.verticalLayout = QtWidgets.QVBoxLayout(self.rename_files_3) + self.use_genres = QtWidgets.QGroupBox(GenresOptionsPage) + self.use_genres.setFlat(False) + self.use_genres.setCheckable(True) + self.use_genres.setChecked(False) + self.use_genres.setObjectName("use_genres") + self.verticalLayout = QtWidgets.QVBoxLayout(self.use_genres) self.verticalLayout.setObjectName("verticalLayout") - self.ignore_tags_2 = QtWidgets.QLabel(self.rename_files_3) - self.ignore_tags_2.setObjectName("ignore_tags_2") - self.verticalLayout.addWidget(self.ignore_tags_2) - self.ignore_tags = QtWidgets.QLineEdit(self.rename_files_3) - self.ignore_tags.setObjectName("ignore_tags") - self.verticalLayout.addWidget(self.ignore_tags) - self.only_my_tags = QtWidgets.QCheckBox(self.rename_files_3) + self.only_my_tags = QtWidgets.QCheckBox(self.use_genres) self.only_my_tags.setObjectName("only_my_tags") self.verticalLayout.addWidget(self.only_my_tags) - self.artists_tags = QtWidgets.QCheckBox(self.rename_files_3) - self.artists_tags.setEnabled(True) + self.artists_tags = QtWidgets.QCheckBox(self.use_genres) self.artists_tags.setObjectName("artists_tags") self.verticalLayout.addWidget(self.artists_tags) - self.folksonomy_tags = QtWidgets.QCheckBox(self.rename_files_3) + self.folksonomy_tags = QtWidgets.QCheckBox(self.use_genres) self.folksonomy_tags.setObjectName("folksonomy_tags") self.verticalLayout.addWidget(self.folksonomy_tags) + self.ignore_tags_2 = QtWidgets.QLabel(self.use_genres) + self.ignore_tags_2.setObjectName("ignore_tags_2") + self.verticalLayout.addWidget(self.ignore_tags_2) + self.ignore_tags = QtWidgets.QLineEdit(self.use_genres) + self.ignore_tags.setObjectName("ignore_tags") + self.verticalLayout.addWidget(self.ignore_tags) self.hboxlayout = QtWidgets.QHBoxLayout() self.hboxlayout.setContentsMargins(0, 0, 0, 0) self.hboxlayout.setSpacing(6) self.hboxlayout.setObjectName("hboxlayout") - self.label_5 = QtWidgets.QLabel(self.rename_files_3) + self.label_5 = QtWidgets.QLabel(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -43,7 +45,7 @@ class Ui_GenresOptionsPage(object): self.label_5.setSizePolicy(sizePolicy) self.label_5.setObjectName("label_5") self.hboxlayout.addWidget(self.label_5) - self.min_tag_usage = QtWidgets.QSpinBox(self.rename_files_3) + self.min_tag_usage = QtWidgets.QSpinBox(self.use_genres) self.min_tag_usage.setMaximum(100) self.min_tag_usage.setObjectName("min_tag_usage") self.hboxlayout.addWidget(self.min_tag_usage) @@ -52,7 +54,7 @@ class Ui_GenresOptionsPage(object): self.hboxlayout1.setContentsMargins(0, 0, 0, 0) self.hboxlayout1.setSpacing(6) self.hboxlayout1.setObjectName("hboxlayout1") - self.label_6 = QtWidgets.QLabel(self.rename_files_3) + self.label_6 = QtWidgets.QLabel(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -60,7 +62,7 @@ class Ui_GenresOptionsPage(object): self.label_6.setSizePolicy(sizePolicy) self.label_6.setObjectName("label_6") self.hboxlayout1.addWidget(self.label_6) - self.max_tags = QtWidgets.QSpinBox(self.rename_files_3) + self.max_tags = QtWidgets.QSpinBox(self.use_genres) self.max_tags.setMaximum(100) self.max_tags.setObjectName("max_tags") self.hboxlayout1.addWidget(self.max_tags) @@ -69,7 +71,7 @@ class Ui_GenresOptionsPage(object): self.hboxlayout2.setContentsMargins(0, 0, 0, 0) self.hboxlayout2.setSpacing(6) self.hboxlayout2.setObjectName("hboxlayout2") - self.ignore_tags_4 = QtWidgets.QLabel(self.rename_files_3) + self.ignore_tags_4 = QtWidgets.QLabel(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(0) @@ -77,7 +79,7 @@ class Ui_GenresOptionsPage(object): self.ignore_tags_4.setSizePolicy(sizePolicy) self.ignore_tags_4.setObjectName("ignore_tags_4") self.hboxlayout2.addWidget(self.ignore_tags_4) - self.join_tags = QtWidgets.QComboBox(self.rename_files_3) + self.join_tags = QtWidgets.QComboBox(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) @@ -91,7 +93,7 @@ class Ui_GenresOptionsPage(object): self.join_tags.addItem("") self.hboxlayout2.addWidget(self.join_tags) self.verticalLayout.addLayout(self.hboxlayout2) - self.verticalLayout_2.addWidget(self.rename_files_3) + self.verticalLayout_2.addWidget(self.use_genres) spacerItem = QtWidgets.QSpacerItem(181, 31, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem) self.label_5.setBuddy(self.min_tag_usage) @@ -102,15 +104,15 @@ class Ui_GenresOptionsPage(object): def retranslateUi(self, GenresOptionsPage): _translate = QtCore.QCoreApplication.translate - self.rename_files_3.setTitle(_("Folksonomy Tags")) - self.ignore_tags_2.setText(_("Ignore tags:")) - self.only_my_tags.setText(_("Only use my tags")) - self.artists_tags.setText(_("Fall back on album\'s artists tags if no tags are found for the release or release group")) + self.use_genres.setTitle(_("Use genres from MusicBrainz")) + self.only_my_tags.setText(_("Only use my genres")) + self.artists_tags.setText(_("Fall back on album\'s artists genres if no genres are found for the release or release group")) self.folksonomy_tags.setText(_("Use folksonomy tags as genre")) - self.label_5.setText(_("Minimal tag usage:")) + self.ignore_tags_2.setText(_("Ignore genres:")) + self.label_5.setText(_("Minimal genre usage:")) self.min_tag_usage.setSuffix(_(" %")) - self.label_6.setText(_("Maximum number of tags:")) - self.ignore_tags_4.setText(_("Join multiple tags with:")) + self.label_6.setText(_("Maximum number of genres:")) + self.ignore_tags_4.setText(_("Join multiple genres with:")) self.join_tags.setItemText(1, _(" / ")) self.join_tags.setItemText(2, _(", ")) diff --git a/test/data/ws_data/release.json b/test/data/ws_data/release.json index 1d88cf591..60a223c42 100644 --- a/test/data/ws_data/release.json +++ b/test/data/ws_data/release.json @@ -1,10 +1,17 @@ { + "genres": [ + {"name": "genre1", "count": 5}, + {"name": "genre2", "count": 3} + ], + "user-genres": [ + {"name": "genre1"} + ], "tags": [ - {"name": "test", "count": 5}, - {"name": "test2", "count": 3} + {"name": "tag1", "count": 5}, + {"name": "tag2", "count": 3} ], "user-tags": [ - {"name": "test"} + {"name": "tag1"} ], "release-group": { "first-release-date": "1973-03-24", @@ -25,18 +32,18 @@ "title": "The Dark Side of the Moon", "disambiguation": "", "secondary-type-ids": [ - + ], "primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc", "secondary-types": [ - + ], "aliases": [ - + ] }, "aliases": [ - + ], "cover-art-archive": { "front": true, @@ -163,14 +170,14 @@ "relations": [ { "attributes": [ - + ], "target-credit": "George Hardie N.T.A.", "source-credit": "", "target-type": "artist", "type": "design/illustration", "attribute-values": { - + }, "begin": null, "type-id": "307e95dd-88b5-419b-8223-b146d4a0d439", @@ -179,7 +186,7 @@ "artist": { "id": "89931942-3182-4448-8e63-0c2ce90f1f81", "aliases": [ - + ], "sort-name": "Hardie, George", "disambiguation": "", @@ -189,14 +196,14 @@ }, { "attributes": [ - + ], "target-credit": "", "source-credit": "", "type": "design/illustration", "target-type": "artist", "attribute-values": { - + }, "begin": null, "type-id": "307e95dd-88b5-419b-8223-b146d4a0d439", @@ -207,7 +214,7 @@ "name": "Hipgnosis", "id": "fd1a4572-59ca-40f2-8f55-b82be28bb0ff", "aliases": [ - + ], "sort-name": "Hipgnosis", "disambiguation": "UK art design group" @@ -227,16 +234,16 @@ "target-type": "url", "type": "discogs", "attributes": [ - + ], "attribute-values": { - + }, "begin": null }, { "attribute-values": { - + }, "begin": null, "type": "photography", @@ -244,14 +251,14 @@ "target-credit": "", "source-credit": "", "attributes": [ - + ], "ended": false, "artist": { "id": "fd1a4572-59ca-40f2-8f55-b82be28bb0ff", "sort-name": "Hipgnosis", "aliases": [ - + ], "disambiguation": "UK art design group", "name": "Hipgnosis" diff --git a/test/test_dataobj.py b/test/test_dataobj.py new file mode 100644 index 000000000..2e3dd5c99 --- /dev/null +++ b/test/test_dataobj.py @@ -0,0 +1,54 @@ +from test.picardtestcase import PicardTestCase + +from picard import config +from picard.dataobj import DataObject + + +class DataObjectTest(PicardTestCase): + + def setUp(self): + super().setUp() + self.obj = DataObject('id') + + def test_set_genre_inc_params_no_genres(self): + inc = [] + config.setting['use_genres'] = False + require_auth = self.obj.set_genre_inc_params(inc) + self.assertEqual([], inc) + self.assertFalse(require_auth) + + def test_set_genre_inc_params_with_genres(self): + inc = [] + config.setting['use_genres'] = True + config.setting['folksonomy_tags'] = False + config.setting['only_my_tags'] = False + require_auth = self.obj.set_genre_inc_params(inc) + self.assertIn('genres', inc) + self.assertFalse(require_auth) + + def test_set_genre_inc_params_with_user_genres(self): + inc = [] + config.setting['use_genres'] = True + config.setting['folksonomy_tags'] = False + config.setting['only_my_tags'] = True + require_auth = self.obj.set_genre_inc_params(inc) + self.assertIn('user-genres', inc) + self.assertTrue(require_auth) + + def test_set_genre_inc_params_with_tags(self): + inc = [] + config.setting['use_genres'] = True + config.setting['folksonomy_tags'] = True + config.setting['only_my_tags'] = False + require_auth = self.obj.set_genre_inc_params(inc) + self.assertIn('tags', inc) + self.assertFalse(require_auth) + + def test_set_genre_inc_params_with_user_tags(self): + inc = [] + config.setting['use_genres'] = True + config.setting['folksonomy_tags'] = True + config.setting['only_my_tags'] = True + require_auth = self.obj.set_genre_inc_params(inc) + self.assertIn('user-tags', inc) + self.assertTrue(require_auth) diff --git a/test/test_mbjson.py b/test/test_mbjson.py index eeafaf849..ddcb041dc 100644 --- a/test/test_mbjson.py +++ b/test/test_mbjson.py @@ -65,7 +65,9 @@ class ReleaseTest(MBJSONTest): self.assertEqual(m['~albumartists'], 'Pink Floyd') self.assertEqual(m['~albumartists_sort'], 'Pink Floyd') self.assertEqual(m['~releaselanguage'], 'eng') - self.assertEqual(a.folksonomy_tags, {'test2': 3, 'test': 6}) + self.assertEqual(a.folksonomy_tags, { + 'genre1': 6, 'genre2': 3, + 'tag1': 6, 'tag2': 3 }) def test_media_formats_from_node(self): formats = media_formats_from_node(self.json_doc['media']) diff --git a/ui/options_genres.ui b/ui/options_genres.ui index 841bf5e61..c3dd7cee5 100644 --- a/ui/options_genres.ui +++ b/ui/options_genres.ui @@ -12,35 +12,31 @@ - + - Folksonomy Tags + Use genres from MusicBrainz + + + false + + + true + + + false - - - - Ignore tags: - - - - - - - Only use my tags + Only use my genres - - true - - Fall back on album's artists tags if no tags are found for the release or release group + Fall back on album's artists genres if no genres are found for the release or release group @@ -51,6 +47,16 @@ + + + + Ignore genres: + + + + + + @@ -77,7 +83,7 @@ - Minimal tag usage: + Minimal genre usage: min_tag_usage @@ -122,7 +128,7 @@ - Maximum number of tags: + Maximum number of genres: min_tag_usage @@ -164,7 +170,7 @@ - Join multiple tags with: + Join multiple genres with: From 21c2a65c2cdfb64b4f6131d2d403d80786ef0e5b Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Fri, 23 Nov 2018 17:04:21 +0100 Subject: [PATCH 4/8] PICARD-1395: Rename genre related options and handle config upgrade --- picard/__init__.py | 2 +- picard/config_upgrade.py | 30 +++++++++--- picard/dataobj.py | 2 +- picard/track.py | 22 ++++----- picard/ui/options/genres.py | 36 +++++++------- picard/ui/ui_options_genres.py | 88 +++++++++++++++++----------------- test/test_dataobj.py | 8 ++-- ui/options_genres.ui | 20 ++++---- 8 files changed, 112 insertions(+), 96 deletions(-) diff --git a/picard/__init__.py b/picard/__init__.py index cebc496cf..679600a5d 100644 --- a/picard/__init__.py +++ b/picard/__init__.py @@ -21,7 +21,7 @@ import re PICARD_ORG_NAME = "MusicBrainz" PICARD_APP_NAME = "Picard" -PICARD_VERSION = (2, 0, 5, 'dev', 1) +PICARD_VERSION = (2, 1, 0, 'dev', 1) # optional build version diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py index 63b7f789a..b5ee09460 100644 --- a/picard/config_upgrade.py +++ b/picard/config_upgrade.py @@ -95,9 +95,7 @@ def upgrade_to_v1_3_0_dev_1(): _s = config.setting old_opt = "windows_compatible_filenames" new_opt = "windows_compatibility" - if old_opt in _s: - _s[new_opt] = _s.value(old_opt, config.BoolOption, True) - _s.remove(old_opt) + rename_option(old_opt, new_opt, config.BoolOption, True) def upgrade_to_v1_3_0_dev_2(): @@ -219,12 +217,9 @@ def upgrade_to_v1_4_0_dev_6(): def upgrade_to_v1_4_0_dev_7(): """Option "save_only_front_images_to_tags" was renamed to "embed_only_one_front_image".""" - _s = config.setting old_opt = "save_only_front_images_to_tags" new_opt = "embed_only_one_front_image" - if old_opt in _s: - _s[new_opt] = _s.value(old_opt, config.BoolOption, True) - _s.remove(old_opt) + rename_option(old_opt, new_opt, config.BoolOption, True) def upgrade_to_v2_0_0_dev_3(): @@ -246,6 +241,26 @@ def upgrade_to_v2_0_0_dev_3(): _s[opt] = _CAA_SIZE_COMPAT[value] +def upgrade_to_v2_1_0_dev_1(): + """Upgrade genre related options""" + _s = config.setting + if _s.get("folksonomy_tags"): + _s["use_genres"] = True + rename_option("max_tags", "max_genres", config.IntOption, 5) + rename_option("min_tag_usage", "min_genre_usage", config.IntOption, 90) + rename_option("ignore_tags", "ignore_genres", config.TextOption, "") + rename_option("join_tags", "join_genres", config.TextOption, "") + rename_option("only_my_tags", "only_my_genres", config.BoolOption, False) + rename_option("artists_tags", "artists_genres", config.BoolOption, False) + + +def rename_option(old_opt, new_opt, option_type, default): + _s = config.setting + if old_opt in _s: + _s[new_opt] = _s.value(old_opt, option_type, default) + _s.remove(old_opt) + + def upgrade_config(): cfg = config.config cfg.register_upgrade_hook(upgrade_to_v1_0_0_final_0) @@ -260,4 +275,5 @@ def upgrade_config(): cfg.register_upgrade_hook(upgrade_to_v1_4_0_dev_6) cfg.register_upgrade_hook(upgrade_to_v1_4_0_dev_7) cfg.register_upgrade_hook(upgrade_to_v2_0_0_dev_3) + cfg.register_upgrade_hook(upgrade_to_v2_1_0_dev_1) cfg.run_upgrade_hooks(log.debug) diff --git a/picard/dataobj.py b/picard/dataobj.py index 64db4b4ee..e2656867f 100644 --- a/picard/dataobj.py +++ b/picard/dataobj.py @@ -36,7 +36,7 @@ class DataObject(LockableObject): require_authentication = False if config.setting['use_genres']: use_folksonomy = config.setting['folksonomy_tags'] - if config.setting['only_my_tags']: + if config.setting['only_my_genres']: require_authentication = True inc += ['user-tags'] if use_folksonomy else ['user-genres'] else: diff --git a/picard/track.py b/picard/track.py index c930a69de..a99d80a35 100644 --- a/picard/track.py +++ b/picard/track.py @@ -203,7 +203,7 @@ class Track(DataObject, Item): self.merge_folksonomy_tags(tags, self.album.folksonomy_tags) if self.album.release_group: self.merge_folksonomy_tags(tags, self.album.release_group.folksonomy_tags) - if not tags and config.setting['artists_tags']: + if not tags and config.setting['artists_genres']: # For compilations use each track's artists to look up tags if self.metadata['musicbrainz_albumartistid'] == VARIOUS_ARTISTS_ID: for artist in self._track_artists: @@ -222,27 +222,27 @@ class Track(DataObject, Item): taglist.append((100 * count // maxcount, name)) taglist.sort(reverse=True) # And generate the genre metadata tag - maxtags = config.setting['max_tags'] - minusage = config.setting['min_tag_usage'] - ignore_tags = self._get_ignored_folksonomy_tags() + maxtags = config.setting['max_genres'] + minusage = config.setting['min_genre_usage'] + ignore_genres = self._get_ignored_folksonomy_tags() genre = [] for usage, name in taglist[:maxtags]: - if name.lower() in ignore_tags: + if name.lower() in ignore_genres: continue if usage < minusage: break name = _TRANSLATE_TAGS.get(name, name.title()) genre.append(name) - join_tags = config.setting['join_tags'] - if join_tags: - genre = [join_tags.join(genre)] + join_genres = config.setting['join_genres'] + if join_genres: + genre = [join_genres.join(genre)] self.metadata['genre'] = genre def _get_ignored_folksonomy_tags(self): tags = [] - ignore_tags = config.setting['ignore_tags'] - if ignore_tags: - tags = [s.strip().lower() for s in ignore_tags.split(',')] + ignore_genres = config.setting['ignore_genres'] + if ignore_genres: + tags = [s.strip().lower() for s in ignore_genres.split(',')] return tags def update_orig_metadata_images(self): diff --git a/picard/ui/options/genres.py b/picard/ui/options/genres.py index 8585eb256..c822e0874 100644 --- a/picard/ui/options/genres.py +++ b/picard/ui/options/genres.py @@ -36,12 +36,12 @@ class GenresOptionsPage(OptionsPage): options = [ config.BoolOption("setting", "use_genres", False), - config.IntOption("setting", "max_tags", 5), - config.IntOption("setting", "min_tag_usage", 90), - config.TextOption("setting", "ignore_tags", "seen live,favorites,fixme,owned"), - config.TextOption("setting", "join_tags", ""), - config.BoolOption("setting", "only_my_tags", False), - config.BoolOption("setting", "artists_tags", False), + config.IntOption("setting", "max_genres", 5), + config.IntOption("setting", "min_genre_usage", 90), + config.TextOption("setting", "ignore_genres", "seen live,favorites,fixme,owned"), + config.TextOption("setting", "join_genres", ""), + config.BoolOption("setting", "only_my_genres", False), + config.BoolOption("setting", "artists_genres", False), config.BoolOption("setting", "folksonomy_tags", False), ] @@ -52,22 +52,22 @@ class GenresOptionsPage(OptionsPage): def load(self): self.ui.use_genres.setChecked(config.setting["use_genres"]) - self.ui.max_tags.setValue(config.setting["max_tags"]) - self.ui.min_tag_usage.setValue(config.setting["min_tag_usage"]) - self.ui.join_tags.setEditText(config.setting["join_tags"]) - self.ui.ignore_tags.setText(config.setting["ignore_tags"]) - self.ui.only_my_tags.setChecked(config.setting["only_my_tags"]) - self.ui.artists_tags.setChecked(config.setting["artists_tags"]) + self.ui.max_genres.setValue(config.setting["max_genres"]) + self.ui.min_genre_usage.setValue(config.setting["min_genre_usage"]) + self.ui.join_genres.setEditText(config.setting["join_genres"]) + self.ui.ignore_genres.setText(config.setting["ignore_genres"]) + self.ui.only_my_genres.setChecked(config.setting["only_my_genres"]) + self.ui.artists_genres.setChecked(config.setting["artists_genres"]) self.ui.folksonomy_tags.setChecked(config.setting["folksonomy_tags"]) def save(self): config.setting["use_genres"] = self.ui.use_genres.isChecked() - config.setting["max_tags"] = self.ui.max_tags.value() - config.setting["min_tag_usage"] = self.ui.min_tag_usage.value() - config.setting["join_tags"] = self.ui.join_tags.currentText() - config.setting["ignore_tags"] = self.ui.ignore_tags.text() - config.setting["only_my_tags"] = self.ui.only_my_tags.isChecked() - config.setting["artists_tags"] = self.ui.artists_tags.isChecked() + config.setting["max_genres"] = self.ui.max_genres.value() + config.setting["min_genre_usage"] = self.ui.min_genre_usage.value() + config.setting["join_genres"] = self.ui.join_genres.currentText() + config.setting["ignore_genres"] = self.ui.ignore_genres.text() + config.setting["only_my_genres"] = self.ui.only_my_genres.isChecked() + config.setting["artists_genres"] = self.ui.artists_genres.isChecked() config.setting["folksonomy_tags"] = self.ui.folksonomy_tags.isChecked() diff --git a/picard/ui/ui_options_genres.py b/picard/ui/ui_options_genres.py index 7285d722f..9c208e36f 100644 --- a/picard/ui/ui_options_genres.py +++ b/picard/ui/ui_options_genres.py @@ -18,21 +18,21 @@ class Ui_GenresOptionsPage(object): self.use_genres.setObjectName("use_genres") self.verticalLayout = QtWidgets.QVBoxLayout(self.use_genres) self.verticalLayout.setObjectName("verticalLayout") - self.only_my_tags = QtWidgets.QCheckBox(self.use_genres) - self.only_my_tags.setObjectName("only_my_tags") - self.verticalLayout.addWidget(self.only_my_tags) - self.artists_tags = QtWidgets.QCheckBox(self.use_genres) - self.artists_tags.setObjectName("artists_tags") - self.verticalLayout.addWidget(self.artists_tags) + self.only_my_genres = QtWidgets.QCheckBox(self.use_genres) + self.only_my_genres.setObjectName("only_my_genres") + self.verticalLayout.addWidget(self.only_my_genres) + self.artists_genres = QtWidgets.QCheckBox(self.use_genres) + self.artists_genres.setObjectName("artists_genres") + self.verticalLayout.addWidget(self.artists_genres) self.folksonomy_tags = QtWidgets.QCheckBox(self.use_genres) self.folksonomy_tags.setObjectName("folksonomy_tags") self.verticalLayout.addWidget(self.folksonomy_tags) - self.ignore_tags_2 = QtWidgets.QLabel(self.use_genres) - self.ignore_tags_2.setObjectName("ignore_tags_2") - self.verticalLayout.addWidget(self.ignore_tags_2) - self.ignore_tags = QtWidgets.QLineEdit(self.use_genres) - self.ignore_tags.setObjectName("ignore_tags") - self.verticalLayout.addWidget(self.ignore_tags) + self.ignore_genres_2 = QtWidgets.QLabel(self.use_genres) + self.ignore_genres_2.setObjectName("ignore_genres_2") + self.verticalLayout.addWidget(self.ignore_genres_2) + self.ignore_genres = QtWidgets.QLineEdit(self.use_genres) + self.ignore_genres.setObjectName("ignore_genres") + self.verticalLayout.addWidget(self.ignore_genres) self.hboxlayout = QtWidgets.QHBoxLayout() self.hboxlayout.setContentsMargins(0, 0, 0, 0) self.hboxlayout.setSpacing(6) @@ -45,10 +45,10 @@ class Ui_GenresOptionsPage(object): self.label_5.setSizePolicy(sizePolicy) self.label_5.setObjectName("label_5") self.hboxlayout.addWidget(self.label_5) - self.min_tag_usage = QtWidgets.QSpinBox(self.use_genres) - self.min_tag_usage.setMaximum(100) - self.min_tag_usage.setObjectName("min_tag_usage") - self.hboxlayout.addWidget(self.min_tag_usage) + self.min_genre_usage = QtWidgets.QSpinBox(self.use_genres) + self.min_genre_usage.setMaximum(100) + self.min_genre_usage.setObjectName("min_genre_usage") + self.hboxlayout.addWidget(self.min_genre_usage) self.verticalLayout.addLayout(self.hboxlayout) self.hboxlayout1 = QtWidgets.QHBoxLayout() self.hboxlayout1.setContentsMargins(0, 0, 0, 0) @@ -62,42 +62,42 @@ class Ui_GenresOptionsPage(object): self.label_6.setSizePolicy(sizePolicy) self.label_6.setObjectName("label_6") self.hboxlayout1.addWidget(self.label_6) - self.max_tags = QtWidgets.QSpinBox(self.use_genres) - self.max_tags.setMaximum(100) - self.max_tags.setObjectName("max_tags") - self.hboxlayout1.addWidget(self.max_tags) + self.max_genres = QtWidgets.QSpinBox(self.use_genres) + self.max_genres.setMaximum(100) + self.max_genres.setObjectName("max_genres") + self.hboxlayout1.addWidget(self.max_genres) self.verticalLayout.addLayout(self.hboxlayout1) self.hboxlayout2 = QtWidgets.QHBoxLayout() self.hboxlayout2.setContentsMargins(0, 0, 0, 0) self.hboxlayout2.setSpacing(6) self.hboxlayout2.setObjectName("hboxlayout2") - self.ignore_tags_4 = QtWidgets.QLabel(self.use_genres) + self.ignore_genres_4 = QtWidgets.QLabel(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.ignore_tags_4.sizePolicy().hasHeightForWidth()) - self.ignore_tags_4.setSizePolicy(sizePolicy) - self.ignore_tags_4.setObjectName("ignore_tags_4") - self.hboxlayout2.addWidget(self.ignore_tags_4) - self.join_tags = QtWidgets.QComboBox(self.use_genres) + sizePolicy.setHeightForWidth(self.ignore_genres_4.sizePolicy().hasHeightForWidth()) + self.ignore_genres_4.setSizePolicy(sizePolicy) + self.ignore_genres_4.setObjectName("ignore_genres_4") + self.hboxlayout2.addWidget(self.ignore_genres_4) + self.join_genres = QtWidgets.QComboBox(self.use_genres) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.join_tags.sizePolicy().hasHeightForWidth()) - self.join_tags.setSizePolicy(sizePolicy) - self.join_tags.setEditable(True) - self.join_tags.setObjectName("join_tags") - self.join_tags.addItem("") - self.join_tags.setItemText(0, "") - self.join_tags.addItem("") - self.join_tags.addItem("") - self.hboxlayout2.addWidget(self.join_tags) + sizePolicy.setHeightForWidth(self.join_genres.sizePolicy().hasHeightForWidth()) + self.join_genres.setSizePolicy(sizePolicy) + self.join_genres.setEditable(True) + self.join_genres.setObjectName("join_genres") + self.join_genres.addItem("") + self.join_genres.setItemText(0, "") + self.join_genres.addItem("") + self.join_genres.addItem("") + self.hboxlayout2.addWidget(self.join_genres) self.verticalLayout.addLayout(self.hboxlayout2) self.verticalLayout_2.addWidget(self.use_genres) spacerItem = QtWidgets.QSpacerItem(181, 31, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem) - self.label_5.setBuddy(self.min_tag_usage) - self.label_6.setBuddy(self.min_tag_usage) + self.label_5.setBuddy(self.min_genre_usage) + self.label_6.setBuddy(self.min_genre_usage) self.retranslateUi(GenresOptionsPage) QtCore.QMetaObject.connectSlotsByName(GenresOptionsPage) @@ -105,14 +105,14 @@ class Ui_GenresOptionsPage(object): def retranslateUi(self, GenresOptionsPage): _translate = QtCore.QCoreApplication.translate self.use_genres.setTitle(_("Use genres from MusicBrainz")) - self.only_my_tags.setText(_("Only use my genres")) - self.artists_tags.setText(_("Fall back on album\'s artists genres if no genres are found for the release or release group")) + self.only_my_genres.setText(_("Only use my genres")) + self.artists_genres.setText(_("Fall back on album\'s artists genres if no genres are found for the release or release group")) self.folksonomy_tags.setText(_("Use folksonomy tags as genre")) - self.ignore_tags_2.setText(_("Ignore genres:")) + self.ignore_genres_2.setText(_("Ignore genres:")) self.label_5.setText(_("Minimal genre usage:")) - self.min_tag_usage.setSuffix(_(" %")) + self.min_genre_usage.setSuffix(_(" %")) self.label_6.setText(_("Maximum number of genres:")) - self.ignore_tags_4.setText(_("Join multiple genres with:")) - self.join_tags.setItemText(1, _(" / ")) - self.join_tags.setItemText(2, _(", ")) + self.ignore_genres_4.setText(_("Join multiple genres with:")) + self.join_genres.setItemText(1, _(" / ")) + self.join_genres.setItemText(2, _(", ")) diff --git a/test/test_dataobj.py b/test/test_dataobj.py index 2e3dd5c99..63c388c47 100644 --- a/test/test_dataobj.py +++ b/test/test_dataobj.py @@ -21,7 +21,7 @@ class DataObjectTest(PicardTestCase): inc = [] config.setting['use_genres'] = True config.setting['folksonomy_tags'] = False - config.setting['only_my_tags'] = False + config.setting['only_my_genres'] = False require_auth = self.obj.set_genre_inc_params(inc) self.assertIn('genres', inc) self.assertFalse(require_auth) @@ -30,7 +30,7 @@ class DataObjectTest(PicardTestCase): inc = [] config.setting['use_genres'] = True config.setting['folksonomy_tags'] = False - config.setting['only_my_tags'] = True + config.setting['only_my_genres'] = True require_auth = self.obj.set_genre_inc_params(inc) self.assertIn('user-genres', inc) self.assertTrue(require_auth) @@ -39,7 +39,7 @@ class DataObjectTest(PicardTestCase): inc = [] config.setting['use_genres'] = True config.setting['folksonomy_tags'] = True - config.setting['only_my_tags'] = False + config.setting['only_my_genres'] = False require_auth = self.obj.set_genre_inc_params(inc) self.assertIn('tags', inc) self.assertFalse(require_auth) @@ -48,7 +48,7 @@ class DataObjectTest(PicardTestCase): inc = [] config.setting['use_genres'] = True config.setting['folksonomy_tags'] = True - config.setting['only_my_tags'] = True + config.setting['only_my_genres'] = True require_auth = self.obj.set_genre_inc_params(inc) self.assertIn('user-tags', inc) self.assertTrue(require_auth) diff --git a/ui/options_genres.ui b/ui/options_genres.ui index c3dd7cee5..2ac44f1b6 100644 --- a/ui/options_genres.ui +++ b/ui/options_genres.ui @@ -27,14 +27,14 @@ - + Only use my genres - + Fall back on album's artists genres if no genres are found for the release or release group @@ -48,14 +48,14 @@ - + Ignore genres: - + @@ -86,12 +86,12 @@ Minimal genre usage: - min_tag_usage + min_genre_usage - + % @@ -131,12 +131,12 @@ Maximum number of genres: - min_tag_usage + min_genre_usage - + 100 @@ -162,7 +162,7 @@ 0 - + 4 @@ -175,7 +175,7 @@ - + 1 From 93f38db1b9df0b50dc55d9021c006261cf6ee24f Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Fri, 23 Nov 2018 17:29:59 +0100 Subject: [PATCH 5/8] PICARD-1395: Rename genre related methods in DataObj --- picard/album.py | 4 ++-- picard/dataobj.py | 8 ++++---- picard/mbjson.py | 4 ++-- picard/track.py | 10 +++++----- test/test_dataobj.py | 17 +++++++++++++++++ test/test_mbjson.py | 4 ++-- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/picard/album.py b/picard/album.py index e5441935f..e5e4dd6bb 100644 --- a/picard/album.py +++ b/picard/album.py @@ -364,9 +364,9 @@ class Album(DataObject, Item): self.status = _("[loading album information]") if self.release_group: self.release_group.loaded = False - self.release_group.folksonomy_tags.clear() + self.release_group.genres.clear() self.metadata.clear() - self.folksonomy_tags.clear() + self.genres.clear() self.update() self._new_metadata = Metadata() self._new_tracks = [] diff --git a/picard/dataobj.py b/picard/dataobj.py index e2656867f..7047449d2 100644 --- a/picard/dataobj.py +++ b/picard/dataobj.py @@ -26,11 +26,11 @@ class DataObject(LockableObject): def __init__(self, obj_id): super().__init__() self.id = obj_id - self.folksonomy_tags = {} + self.genres = {} self.item = None - def add_folksonomy_tag(self, name, count): - self.folksonomy_tags[name] = self.folksonomy_tags.get(name, 0) + count + def add_genre(self, name, count): + self.genres[name] = self.genres.get(name, 0) + count def set_genre_inc_params(self, inc): require_authentication = False @@ -44,6 +44,6 @@ class DataObject(LockableObject): return require_authentication @staticmethod - def merge_folksonomy_tags(this, that): + def merge_genres(this, that): for name, count in that.items(): this[name] = this.get(name, 0) + count diff --git a/picard/mbjson.py b/picard/mbjson.py index 581b31e4c..486d41a4a 100644 --- a/picard/mbjson.py +++ b/picard/mbjson.py @@ -496,7 +496,7 @@ def add_genres(node, obj): key = tag['name'] count = tag['count'] if key: - obj.add_folksonomy_tag(key, count) + obj.add_genre(key, count) def add_user_genres(node, obj): @@ -504,7 +504,7 @@ def add_user_genres(node, obj): for tag in node: key = tag['name'] if key: - obj.add_folksonomy_tag(key, 1) + obj.add_genre(key, 1) def add_isrcs_to_metadata(node, metadata): diff --git a/picard/track.py b/picard/track.py index a99d80a35..f81b996e9 100644 --- a/picard/track.py +++ b/picard/track.py @@ -199,18 +199,18 @@ class Track(DataObject, Item): def _convert_folksonomy_tags_to_genre(self): # Combine release and track tags - tags = dict(self.folksonomy_tags) - self.merge_folksonomy_tags(tags, self.album.folksonomy_tags) + tags = dict(self.genres) + self.merge_genres(tags, self.album.genres) if self.album.release_group: - self.merge_folksonomy_tags(tags, self.album.release_group.folksonomy_tags) + self.merge_genres(tags, self.album.release_group.genres) if not tags and config.setting['artists_genres']: # For compilations use each track's artists to look up tags if self.metadata['musicbrainz_albumartistid'] == VARIOUS_ARTISTS_ID: for artist in self._track_artists: - self.merge_folksonomy_tags(tags, artist.folksonomy_tags) + self.merge_genres(tags, artist.genres) else: for artist in self.album.get_album_artists(): - self.merge_folksonomy_tags(tags, artist.folksonomy_tags) + self.merge_genres(tags, artist.genres) # Ignore tags with zero or lower score tags = dict((name, count) for name, count in tags.items() if count > 0) if not tags: diff --git a/test/test_dataobj.py b/test/test_dataobj.py index 63c388c47..945b7738c 100644 --- a/test/test_dataobj.py +++ b/test/test_dataobj.py @@ -52,3 +52,20 @@ class DataObjectTest(PicardTestCase): require_auth = self.obj.set_genre_inc_params(inc) self.assertIn('user-tags', inc) self.assertTrue(require_auth) + + def test_add_genres(self): + self.obj.add_genre('genre1', 2) + self.assertEqual(self.obj.genres['genre1'], 2) + self.obj.add_genre('genre1', 5) + self.assertEqual(self.obj.genres['genre1'], 7) + + def test_merge_genres(self): + genres1 = {'a': 2, 'b': 7} + genres2 = {'b': 4, 'c': 3} + DataObject.merge_genres(genres1, genres2) + self.assertEqual(genres1['a'], 2) + self.assertEqual(genres1['b'], 11) + self.assertEqual(genres1['c'], 3) + self.assertNotIn('a', genres2) + self.assertEqual(genres2['b'], 4) + self.assertEqual(genres2['c'], 3) diff --git a/test/test_mbjson.py b/test/test_mbjson.py index ddcb041dc..c56f2a187 100644 --- a/test/test_mbjson.py +++ b/test/test_mbjson.py @@ -65,7 +65,7 @@ class ReleaseTest(MBJSONTest): self.assertEqual(m['~albumartists'], 'Pink Floyd') self.assertEqual(m['~albumartists_sort'], 'Pink Floyd') self.assertEqual(m['~releaselanguage'], 'eng') - self.assertEqual(a.folksonomy_tags, { + self.assertEqual(a.genres, { 'genre1': 6, 'genre2': 3, 'tag1': 6, 'tag2': 3 }) @@ -248,7 +248,7 @@ class ReleaseGroupTest(MBJSONTest): self.assertEqual(m['releasetype'], 'album') self.assertEqual(m['~primaryreleasetype'], 'album') self.assertEqual(m['~releasegroup'], 'The Dark Side of the Moon') - self.assertEqual(r.folksonomy_tags, {'test2': 3, 'test': 6}) + self.assertEqual(r.genres, {'test2': 3, 'test': 6}) class NullReleaseGroupTest(MBJSONTest): From 5eb7948b26ca54eebfb15c6cb91381ad190e59bc Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sat, 24 Nov 2018 10:21:29 +0100 Subject: [PATCH 6/8] PICARD-1395: Text changes for genre option dialog --- picard/ui/ui_options_genres.py | 2 +- ui/options_genres.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/picard/ui/ui_options_genres.py b/picard/ui/ui_options_genres.py index 9c208e36f..179e14c62 100644 --- a/picard/ui/ui_options_genres.py +++ b/picard/ui/ui_options_genres.py @@ -108,7 +108,7 @@ class Ui_GenresOptionsPage(object): self.only_my_genres.setText(_("Only use my genres")) self.artists_genres.setText(_("Fall back on album\'s artists genres if no genres are found for the release or release group")) self.folksonomy_tags.setText(_("Use folksonomy tags as genre")) - self.ignore_genres_2.setText(_("Ignore genres:")) + self.ignore_genres_2.setText(_("Genres or folksonomy tags to exclude (comma-separated list):")) self.label_5.setText(_("Minimal genre usage:")) self.min_genre_usage.setSuffix(_(" %")) self.label_6.setText(_("Maximum number of genres:")) diff --git a/ui/options_genres.ui b/ui/options_genres.ui index 2ac44f1b6..4c555dfbf 100644 --- a/ui/options_genres.ui +++ b/ui/options_genres.ui @@ -50,7 +50,7 @@ - Ignore genres: + Genres or folksonomy tags to exclude (comma-separated list): From a91712de57da5f237c5bac42449a3d04f440a95b Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sat, 24 Nov 2018 10:28:16 +0100 Subject: [PATCH 7/8] PICARD-1395: Make ignore genres list more readable by default --- picard/ui/options/genres.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picard/ui/options/genres.py b/picard/ui/options/genres.py index c822e0874..2eb601f78 100644 --- a/picard/ui/options/genres.py +++ b/picard/ui/options/genres.py @@ -38,7 +38,7 @@ class GenresOptionsPage(OptionsPage): config.BoolOption("setting", "use_genres", False), config.IntOption("setting", "max_genres", 5), config.IntOption("setting", "min_genre_usage", 90), - config.TextOption("setting", "ignore_genres", "seen live,favorites,fixme,owned"), + config.TextOption("setting", "ignore_genres", "seen live, favorites, fixme, owned"), config.TextOption("setting", "join_genres", ""), config.BoolOption("setting", "only_my_genres", False), config.BoolOption("setting", "artists_genres", False), From 64a3638c71fc7b86887fb7ed2369ecec989b7320 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sat, 24 Nov 2018 10:49:37 +0100 Subject: [PATCH 8/8] PICARD-1395: DataObj.set_genre_inc_params defined as @staticmethod --- picard/dataobj.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/picard/dataobj.py b/picard/dataobj.py index 7047449d2..35482b8e7 100644 --- a/picard/dataobj.py +++ b/picard/dataobj.py @@ -32,7 +32,8 @@ class DataObject(LockableObject): def add_genre(self, name, count): self.genres[name] = self.genres.get(name, 0) + count - def set_genre_inc_params(self, inc): + @staticmethod + def set_genre_inc_params(inc): require_authentication = False if config.setting['use_genres']: use_folksonomy = config.setting['folksonomy_tags']