class RatingsOptionsPage(OptionsPage): NAME = "ratings" TITLE = N_("Ratings") PARENT = "metadata" SORT_ORDER = 20 ACTIVE = True options = [ config.BoolOption("setting", "enable_ratings", False), config.TextOption("setting", "rating_user_email", "*****@*****.**"), config.BoolOption("setting", "submit_ratings", True), config.IntOption("setting", "rating_steps", 6), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_RatingsOptionsPage() self.ui.setupUi(self) def load(self): self.ui.enable_ratings.setChecked(config.setting["enable_ratings"]) self.ui.rating_user_email.setText(config.setting["rating_user_email"]) self.ui.submit_ratings.setChecked(config.setting["submit_ratings"]) def save(self): config.setting["enable_ratings"] = self.ui.enable_ratings.isChecked() config.setting["rating_user_email"] = self.ui.rating_user_email.text() config.setting["submit_ratings"] = self.ui.submit_ratings.isChecked()
class GenresOptionsPage(OptionsPage): NAME = "genres" TITLE = N_("Genres") PARENT = "metadata" SORT_ORDER = 20 ACTIVE = True options = [ 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", "join_genres", ""), config.BoolOption("setting", "only_my_genres", False), config.BoolOption("setting", "artists_genres", False), config.BoolOption("setting", "folksonomy_tags", False), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_GenresOptionsPage() self.ui.setupUi(self) def load(self): self.ui.use_genres.setChecked(config.setting["use_genres"]) 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_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()
class ProviderOptionsCaa(ProviderOptions): """ Options for Cover Art Archive cover art provider """ options = [ config.BoolOption("setting", "caa_save_single_front_image", False), config.BoolOption("setting", "caa_approved_only", False), config.BoolOption("setting", "caa_image_type_as_filename", False), config.IntOption("setting", "caa_image_size", 1), config.ListOption("setting", "caa_image_types", [u"front"]), config.BoolOption("setting", "caa_restrict_image_types", True), ] _options_ui = Ui_CaaOptions def __init__(self, parent=None): super(ProviderOptionsCaa, self).__init__(parent) self.ui.restrict_images_types.clicked.connect(self.update_caa_types) self.ui.select_caa_types.clicked.connect(self.select_caa_types) def load(self): self.ui.cb_image_size.setCurrentIndex(config.setting["caa_image_size"]) self.ui.cb_save_single_front_image.setChecked( config.setting["caa_save_single_front_image"]) self.ui.cb_approved_only.setChecked( config.setting["caa_approved_only"]) self.ui.cb_type_as_filename.setChecked( config.setting["caa_image_type_as_filename"]) self.ui.restrict_images_types.setChecked( config.setting["caa_restrict_image_types"]) self.update_caa_types() def save(self): config.setting["caa_image_size"] = \ self.ui.cb_image_size.currentIndex() config.setting["caa_save_single_front_image"] = \ self.ui.cb_save_single_front_image.isChecked() config.setting["caa_approved_only"] = \ self.ui.cb_approved_only.isChecked() config.setting["caa_image_type_as_filename"] = \ self.ui.cb_type_as_filename.isChecked() config.setting["caa_restrict_image_types"] = \ self.ui.restrict_images_types.isChecked() def update_caa_types(self): enabled = self.ui.restrict_images_types.isChecked() self.ui.select_caa_types.setEnabled(enabled) def select_caa_types(self): (types, ok) = CAATypesSelectorDialog.run(self, config.setting["caa_image_types"]) if ok: config.setting["caa_image_types"] = types
class AdvancedOptionsPage(OptionsPage): NAME = "advanced" TITLE = N_("Advanced") PARENT = None SORT_ORDER = 90 ACTIVE = True HELP_URL = '/config/options_advanced.html' options = [ config.TextOption("setting", "ignore_regex", ""), config.BoolOption("setting", "ignore_hidden_files", False), config.BoolOption("setting", "recursively_add_files", True), config.IntOption("setting", "ignore_track_duration_difference_under", 2), config.BoolOption("setting", "completeness_ignore_videos", False), config.BoolOption("setting", "completeness_ignore_pregap", False), config.BoolOption("setting", "completeness_ignore_data", False), config.BoolOption("setting", "completeness_ignore_silence", False), config.ListOption("setting", "compare_ignore_tags", []), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_AdvancedOptionsPage() self.ui.setupUi(self) self.init_regex_checker(self.ui.ignore_regex, self.ui.regex_error) def load(self): self.ui.ignore_regex.setText(config.setting["ignore_regex"]) self.ui.ignore_hidden_files.setChecked(config.setting["ignore_hidden_files"]) self.ui.recursively_add_files.setChecked(config.setting["recursively_add_files"]) self.ui.ignore_track_duration_difference_under.setValue(config.setting["ignore_track_duration_difference_under"]) self.ui.completeness_ignore_videos.setChecked(config.setting["completeness_ignore_videos"]) self.ui.completeness_ignore_pregap.setChecked(config.setting["completeness_ignore_pregap"]) self.ui.completeness_ignore_data.setChecked(config.setting["completeness_ignore_data"]) self.ui.completeness_ignore_silence.setChecked(config.setting["completeness_ignore_silence"]) self.ui.compare_ignore_tags.update(config.setting["compare_ignore_tags"]) self.ui.compare_ignore_tags.set_user_sortable(False) def save(self): config.setting["ignore_regex"] = self.ui.ignore_regex.text() config.setting["ignore_hidden_files"] = self.ui.ignore_hidden_files.isChecked() config.setting["recursively_add_files"] = self.ui.recursively_add_files.isChecked() config.setting["ignore_track_duration_difference_under"] = self.ui.ignore_track_duration_difference_under.value() config.setting["completeness_ignore_videos"] = self.ui.completeness_ignore_videos.isChecked() config.setting["completeness_ignore_pregap"] = self.ui.completeness_ignore_pregap.isChecked() config.setting["completeness_ignore_data"] = self.ui.completeness_ignore_data.isChecked() config.setting["completeness_ignore_silence"] = self.ui.completeness_ignore_silence.isChecked() tags = list(self.ui.compare_ignore_tags.tags) if tags != config.setting["compare_ignore_tags"]: config.setting["compare_ignore_tags"] = tags def restore_defaults(self): self.ui.compare_ignore_tags.clear() super().restore_defaults()
class GeneralOptionsPage(OptionsPage): NAME = "general" TITLE = N_("General") PARENT = None SORT_ORDER = 1 ACTIVE = True options = [ config.TextOption("setting", "server_host", MUSICBRAINZ_SERVERS[0]), config.IntOption("setting", "server_port", 80), config.TextOption("setting", "username", ""), config.PasswordOption("setting", "password", ""), config.BoolOption("setting", "analyze_new_files", False), config.BoolOption("setting", "ignore_file_mbids", False), ] def __init__(self, parent=None): super(GeneralOptionsPage, self).__init__(parent) self.ui = Ui_GeneralOptionsPage() self.ui.setupUi(self) self.ui.server_host.addItems(MUSICBRAINZ_SERVERS) def load(self): self.ui.server_host.setEditText(config.setting["server_host"]) self.ui.server_port.setValue(config.setting["server_port"]) self.ui.username.setText(config.setting["username"]) self.ui.password.setText(config.setting["password"]) self.ui.analyze_new_files.setChecked(config.setting["analyze_new_files"]) self.ui.ignore_file_mbids.setChecked(config.setting["ignore_file_mbids"]) def save(self): config.setting["server_host"] = unicode(self.ui.server_host.currentText()).strip() config.setting["server_port"] = self.ui.server_port.value() reload_collections = config.setting["username"] != unicode(self.ui.username.text()) \ or config.setting["password"] != unicode(self.ui.password.text()) config.setting["username"] = unicode(self.ui.username.text()) # trivially encode the password, just to not make it so apparent config.setting["password"] = rot13(unicode(self.ui.password.text())) if reload_collections: load_user_collections() config.setting["analyze_new_files"] = self.ui.analyze_new_files.isChecked() config.setting["ignore_file_mbids"] = self.ui.ignore_file_mbids.isChecked()
class GeneralOptionsPage(OptionsPage): NAME = "general" TITLE = N_("General") PARENT = None SORT_ORDER = 1 ACTIVE = True options = [ config.TextOption("setting", "server_host", MUSICBRAINZ_SERVERS[0]), config.IntOption("setting", "server_port", 443), config.TextOption("persist", "oauth_refresh_token", ""), config.BoolOption("setting", "analyze_new_files", False), config.BoolOption("setting", "ignore_file_mbids", False), config.TextOption("persist", "oauth_refresh_token", ""), config.TextOption("persist", "oauth_refresh_token_scopes", ""), config.TextOption("persist", "oauth_access_token", ""), config.IntOption("persist", "oauth_access_token_expires", 0), config.TextOption("persist", "oauth_username", ""), config.BoolOption("setting", "check_for_updates", True), config.IntOption("setting", "update_check_days", 7), config.IntOption("setting", "update_level", 0), config.IntOption("persist", "last_update_check", 0), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_GeneralOptionsPage() self.ui.setupUi(self) self.ui.server_host.addItems(MUSICBRAINZ_SERVERS) self.ui.login.clicked.connect(self.login) self.ui.logout.clicked.connect(self.logout) self.update_login_logout() def load(self): self.ui.server_host.setEditText(config.setting["server_host"]) self.ui.server_port.setValue(config.setting["server_port"]) self.ui.analyze_new_files.setChecked( config.setting["analyze_new_files"]) self.ui.ignore_file_mbids.setChecked( config.setting["ignore_file_mbids"]) if self.tagger.autoupdate_enabled: self.ui.check_for_updates.setChecked( config.setting["check_for_updates"]) self.ui.update_level.clear() for level, description in PROGRAM_UPDATE_LEVELS.items(): # TODO: Remove temporary workaround once https://github.com/python-babel/babel/issues/415 has been resolved. babel_415_workaround = description['title'] self.ui.update_level.addItem(_(babel_415_workaround), level) self.ui.update_level.setCurrentIndex( self.ui.update_level.findData(config.setting["update_level"])) self.ui.update_check_days.setValue( config.setting["update_check_days"]) else: self.ui.update_check_groupbox.hide() def save(self): config.setting["server_host"] = self.ui.server_host.currentText( ).strip() config.setting["server_port"] = self.ui.server_port.value() config.setting[ "analyze_new_files"] = self.ui.analyze_new_files.isChecked() config.setting[ "ignore_file_mbids"] = self.ui.ignore_file_mbids.isChecked() if self.tagger.autoupdate_enabled: config.setting[ "check_for_updates"] = self.ui.check_for_updates.isChecked() config.setting["update_level"] = self.ui.update_level.currentData( QtCore.Qt.UserRole) config.setting[ "update_check_days"] = self.ui.update_check_days.value() def update_login_logout(self): if self.tagger.webservice.oauth_manager.is_logged_in(): self.ui.logged_in.setText( _("Logged in as <b>%s</b>.") % config.persist["oauth_username"]) self.ui.logged_in.show() self.ui.login.hide() self.ui.logout.show() else: self.ui.logged_in.hide() self.ui.login.show() self.ui.logout.hide() def login(self): self.tagger.mb_login(self.on_login_finished, self) def restore_defaults(self): super().restore_defaults() self.logout() def on_login_finished(self, successful): self.update_login_logout() def logout(self): self.tagger.mb_logout() self.update_login_logout()
class LogView(LogViewCommon): options = [ config.IntOption("setting", "log_verbosity", log.VERBOSITY_DEFAULT), ] def __init__(self, parent=None): super().__init__(log.main_tail, _("Log"), parent=parent) self.verbosity = log.get_effective_level() self._setup_formats() self.hl_text = '' self.hl = None self.hbox = QtWidgets.QHBoxLayout() self.vbox.addLayout(self.hbox) self.verbosity_menu_button = QtWidgets.QPushButton(_("Verbosity")) self.hbox.addWidget(self.verbosity_menu_button) self.verbosity_menu = VerbosityMenu() self.verbosity_menu.set_verbosity(self.verbosity) self.verbosity_menu.verbosity_changed.connect(self._verbosity_changed) self.verbosity_menu_button.setMenu(self.verbosity_menu) # highlight input self.highlight_text = QtWidgets.QLineEdit() self.highlight_text.setPlaceholderText(_("String to highlight")) self.highlight_text.textEdited.connect(self._highlight_text_edited) self.hbox.addWidget(self.highlight_text) # highlight button self.highlight_button = QtWidgets.QPushButton(_("Highlight")) self.hbox.addWidget(self.highlight_button) self.highlight_button.setDefault(True) self.highlight_button.setEnabled(False) self.highlight_button.clicked.connect(self._highlight_do) self.highlight_text.returnPressed.connect(self.highlight_button.click) # clear highlight button self.clear_highlight_button = QtWidgets.QPushButton( _("Clear Highlight")) self.hbox.addWidget(self.clear_highlight_button) self.clear_highlight_button.setEnabled(False) self.clear_highlight_button.clicked.connect(self._clear_highlight_do) # clear log self.clear_log_button = QtWidgets.QPushButton(_("Clear Log")) self.hbox.addWidget(self.clear_log_button) self.clear_log_button.clicked.connect(self._clear_log_do) # save as self.save_log_as_button = QtWidgets.QPushButton(_("Save As...")) self.hbox.addWidget(self.save_log_as_button) self.save_log_as_button.clicked.connect(self._save_log_as_do) def _clear_highlight_do(self): self.highlight_text.setText('') self.highlight_button.setEnabled(False) self._highlight_do() def _highlight_text_edited(self, text): if text and self.hl_text != text: self.highlight_button.setEnabled(True) else: self.highlight_button.setEnabled(False) if not text: self.clear_highlight_button.setEnabled(bool(self.hl)) def _highlight_do(self): new_hl_text = self.highlight_text.text() if new_hl_text != self.hl_text: self.hl_text = new_hl_text if self.hl is not None: self.hl.setDocument(None) self.hl = None if self.hl_text: self.hl = Highlighter(self.hl_text, parent=self.doc) self.clear_highlight_button.setEnabled(bool(self.hl)) def _setup_formats(self): interface_colors.load_from_config() self.formats = {} font = QtGui.QFont() font.setFamily("Monospace") for level, feat in log.levels_features.items(): text_fmt = QtGui.QTextCharFormat() text_fmt.setFont(font) text_fmt.setForeground(interface_colors.get_qcolor(feat.color_key)) self.formats[level] = text_fmt def _format(self, level): return self.formats[level] def _save_log_as_do(self): path, ok = QtWidgets.QFileDialog.getSaveFileName( self, caption=_("Save Log View to File"), options=QtWidgets.QFileDialog.DontConfirmOverwrite) if ok and path: if os.path.isfile(path): reply = QtWidgets.QMessageBox.question( self, _("Save Log View to File"), _("File already exists, do you really want to save to this file?" ), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if reply != QtWidgets.QMessageBox.Yes: return writer = QtGui.QTextDocumentWriter(path) writer.setFormat(b"plaintext") success = writer.write(self.doc) if not success: QtWidgets.QMessageBox.critical( self, _("Failed to save Log View to file"), _("Something prevented data to be written to '%s'") % writer.fileName()) def show(self): self.highlight_text.setFocus(QtCore.Qt.OtherFocusReason) super().show() def _clear_log_do(self): reply = QtWidgets.QMessageBox.question( self, _("Clear Log"), _("Are you sure you want to clear the log?"), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if reply != QtWidgets.QMessageBox.Yes: return self.log_tail.clear() self.display(clear=True) def is_shown(self, logitem): return logitem.level >= self.verbosity def _add_entry(self, logitem): if not self.is_shown(logitem): return fmt = self.textCursor.blockCharFormat() self.textCursor.setBlockCharFormat(self._format(logitem.level)) super()._add_entry(logitem) self.textCursor.setBlockCharFormat(fmt) def _set_verbosity(self, level): self.verbosity = level self.verbosity_menu.set_verbosity(self.verbosity) def _verbosity_changed(self, level): if level != self.verbosity: config.setting['log_verbosity'] = level QtCore.QObject.tagger.set_log_level(level) self.verbosity = level self.display(clear=True)
class ScriptingOptionsPage(OptionsPage): NAME = "scripting" TITLE = N_("Scripting") PARENT = None SORT_ORDER = 85 ACTIVE = True options = [ config.BoolOption("setting", "enable_tagger_scripts", False), config.ListOption("setting", "list_of_scripts", []), config.IntOption("persist", "last_selected_script_pos", 0), config.Option("persist", "scripting_splitter", QtCore.QByteArray()), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_ScriptingOptionsPage() self.ui.setupUi(self) self.highlighter = TaggerScriptSyntaxHighlighter( self.ui.tagger_script.document()) self.ui.tagger_script.textChanged.connect(self.live_update_and_check) self.ui.script_name.textChanged.connect(self.script_name_changed) self.ui.add_script.clicked.connect(self.add_to_list_of_scripts) self.ui.script_list.itemSelectionChanged.connect(self.script_selected) self.ui.tagger_script.setEnabled(False) self.ui.script_name.setEnabled(False) self.listitem_to_scriptitem = {} self.list_of_scripts = [] self.last_selected_script_pos = 0 self.ui.splitter.setStretchFactor(0, 1) self.ui.splitter.setStretchFactor(1, 2) def script_name_changed(self): items = self.ui.script_list.selectedItems() if items: script = self.listitem_to_scriptitem[items[0]] script.name = self.ui.script_name.text() list_widget = self.ui.script_list.itemWidget(items[0]) list_widget.update_name(script.name) self.list_of_scripts[script.pos] = script.get_all() def script_selected(self): items = self.ui.script_list.selectedItems() if items: self.ui.tagger_script.setEnabled(True) self.ui.script_name.setEnabled(True) script = self.listitem_to_scriptitem[items[0]] self.ui.tagger_script.setText(script.text) self.ui.script_name.setText(script.name) self.last_selected_script_pos = script.pos def setSignals(self, list_widget, item): list_widget.set_up_connection( lambda: self.move_script(self.ui.script_list.row(item), 1)) list_widget.set_down_connection( lambda: self.move_script(self.ui.script_list.row(item), -1)) list_widget.set_remove_connection( lambda: self.remove_from_list_of_scripts( self.ui.script_list.row(item))) list_widget.set_checkbox_connection(lambda: self.update_check_state( item, list_widget.checkbox_state())) list_widget.set_rename_connection(lambda: self.rename_script(item)) def rename_script(self, item): item.setSelected(True) self.ui.script_name.setFocus() self.ui.script_name.selectAll() def update_check_state(self, item, checkbox_state): script = self.listitem_to_scriptitem[item] script.enabled = checkbox_state self.list_of_scripts[script.pos] = script.get_all() def add_to_list_of_scripts(self): count = self.ui.script_list.count() numbered_name = _(DEFAULT_NUMBERED_SCRIPT_NAME) % (count + 1) script = ScriptItem(pos=count, name=numbered_name) list_item = HashableListWidgetItem() list_widget = AdvancedScriptItem(numbered_name) self.setSignals(list_widget, list_item) self.ui.script_list.addItem(list_item) self.ui.script_list.setItemWidget(list_item, list_widget) self.listitem_to_scriptitem[list_item] = script self.list_of_scripts.append(script.get_all()) list_item.setSelected(True) def update_script_positions(self): for i, script in enumerate(self.list_of_scripts): self.list_of_scripts[i] = (i, script[1], script[2], script[3]) item = self.ui.script_list.item(i) self.listitem_to_scriptitem[item].pos = i def remove_from_list_of_scripts(self, row): item = self.ui.script_list.item(row) confirm_remove = QtWidgets.QMessageBox() msg = _("Are you sure you want to remove this script?") reply = confirm_remove.question(confirm_remove, _('Confirm Remove'), msg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if item and reply == QtWidgets.QMessageBox.Yes: item = self.ui.script_list.takeItem(row) script = self.listitem_to_scriptitem[item] del self.listitem_to_scriptitem[item] del self.list_of_scripts[script.pos] del script item = None # update positions of other items self.update_script_positions() if not self.ui.script_list: self.ui.tagger_script.setText("") self.ui.tagger_script.setEnabled(False) self.ui.script_name.setText("") self.ui.script_name.setEnabled(False) # update position of last_selected_script if row == self.last_selected_script_pos: self.last_selected_script_pos = 0 # workaround to remove residue on UI if not self.ui.script_list.selectedItems(): current_item = self.ui.script_list.currentItem() if current_item: current_item.setSelected(True) else: item = self.ui.script_list.item(0) item.setSelected(True) elif row < self.last_selected_script_pos: self.last_selected_script_pos -= 1 def move_script(self, row, step): item1 = self.ui.script_list.item(row) item2 = self.ui.script_list.item(row - step) if item1 and item2: # make changes in the ui list_item = self.ui.script_list.takeItem(row) script = self.listitem_to_scriptitem[list_item] # list_widget has to be set again list_widget = AdvancedScriptItem(name=script.name, state=script.enabled) self.setSignals(list_widget, list_item) self.ui.script_list.insertItem(row - step, list_item) self.ui.script_list.setItemWidget(list_item, list_widget) # make changes in the picklable list script1 = self.listitem_to_scriptitem[item1] script2 = self.listitem_to_scriptitem[item2] # workaround since tuples are immutable indices = script1.pos, script2.pos self.list_of_scripts = [ i for j, i in enumerate(self.list_of_scripts) if j not in indices ] new_script1 = (script1.pos - step, script1.name, script1.enabled, script1.text) new_script2 = (script2.pos + step, script2.name, script2.enabled, script2.text) self.list_of_scripts.append(new_script1) self.list_of_scripts.append(new_script2) self.list_of_scripts = sorted(self.list_of_scripts, key=lambda x: x[0]) # corresponding mapping support also has to be updated self.listitem_to_scriptitem[item1] = ScriptItem( script1.pos - step, script1.name, script1.enabled, script1.text) self.listitem_to_scriptitem[item2] = ScriptItem( script2.pos + step, script2.name, script2.enabled, script2.text) def live_update_and_check(self): items = self.ui.script_list.selectedItems() if items: script = self.listitem_to_scriptitem[items[0]] script.text = self.ui.tagger_script.toPlainText() self.list_of_scripts[script.pos] = script.get_all() self.ui.script_error.setStyleSheet("") self.ui.script_error.setText("") try: self.check() except OptionsCheckError as e: self.ui.script_error.setStyleSheet(self.STYLESHEET_ERROR) self.ui.script_error.setText(e.info) return def check(self): parser = ScriptParser() try: parser.eval(self.ui.tagger_script.toPlainText()) except Exception as e: raise OptionsCheckError(_("Script Error"), string_(e)) def restore_defaults(self): # Remove existing scripts self.ui.script_list.clear() self.ui.script_name.setText("") self.ui.tagger_script.setText("") super().restore_defaults() def load(self): self.ui.enable_tagger_scripts.setChecked( config.setting["enable_tagger_scripts"]) self.list_of_scripts = config.setting["list_of_scripts"] for s_pos, s_name, s_enabled, s_text in self.list_of_scripts: script = ScriptItem(s_pos, s_name, s_enabled, s_text) list_item = HashableListWidgetItem() list_widget = AdvancedScriptItem(name=s_name, state=s_enabled) self.setSignals(list_widget, list_item) self.ui.script_list.addItem(list_item) self.ui.script_list.setItemWidget(list_item, list_widget) self.listitem_to_scriptitem[list_item] = script # Select the last selected script item self.last_selected_script_pos = config.persist[ "last_selected_script_pos"] last_selected_script = self.ui.script_list.item( self.last_selected_script_pos) if last_selected_script: last_selected_script.setSelected(True) # Preserve previous splitter position self.ui.splitter.restoreState(config.persist["scripting_splitter"]) args = { "picard-doc-scripting-url": PICARD_URLS['doc_scripting'], } text = _('<a href="%(picard-doc-scripting-url)s">Open Scripting' ' Documentation in your browser</a>') % args self.ui.scripting_doc_link.setText(text) def save(self): config.setting[ "enable_tagger_scripts"] = self.ui.enable_tagger_scripts.isChecked( ) config.setting["list_of_scripts"] = self.list_of_scripts config.persist[ "last_selected_script_pos"] = self.last_selected_script_pos config.persist["scripting_splitter"] = self.ui.splitter.saveState() def display_error(self, error): pass
class NetworkOptionsPage(OptionsPage): NAME = "network" TITLE = N_("Network") PARENT = "advanced" SORT_ORDER = 10 ACTIVE = True HELP_URL = '/config/options_network.html' options = [ config.BoolOption("setting", "use_proxy", False), config.TextOption("setting", "proxy_type", "http"), config.TextOption("setting", "proxy_server_host", ""), config.IntOption("setting", "proxy_server_port", 80), config.TextOption("setting", "proxy_username", ""), config.TextOption("setting", "proxy_password", ""), config.BoolOption("setting", "browser_integration", True), config.IntOption("setting", "browser_integration_port", 8000), config.BoolOption("setting", "browser_integration_localhost_only", True), config.IntOption("setting", "network_transfer_timeout_seconds", 30), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_NetworkOptionsPage() self.ui.setupUi(self) def load(self): self.ui.web_proxy.setChecked(config.setting["use_proxy"]) if config.setting["proxy_type"] == 'socks': self.ui.proxy_type_socks.setChecked(True) else: self.ui.proxy_type_http.setChecked(True) self.ui.server_host.setText(config.setting["proxy_server_host"]) self.ui.server_port.setValue(config.setting["proxy_server_port"]) self.ui.username.setText(config.setting["proxy_username"]) self.ui.password.setText(config.setting["proxy_password"]) self.ui.transfer_timeout.setValue( config.setting["network_transfer_timeout_seconds"]) self.ui.browser_integration.setChecked( config.setting["browser_integration"]) self.ui.browser_integration_port.setValue( config.setting["browser_integration_port"]) self.ui.browser_integration_localhost_only.setChecked( config.setting["browser_integration_localhost_only"]) def save(self): config.setting["use_proxy"] = self.ui.web_proxy.isChecked() if self.ui.proxy_type_socks.isChecked(): config.setting["proxy_type"] = 'socks' else: config.setting["proxy_type"] = 'http' config.setting["proxy_server_host"] = self.ui.server_host.text() config.setting["proxy_server_port"] = self.ui.server_port.value() config.setting["proxy_username"] = self.ui.username.text() config.setting["proxy_password"] = self.ui.password.text() self.tagger.webservice.setup_proxy() transfer_timeout = self.ui.transfer_timeout.value() config.setting["network_transfer_timeout_seconds"] = transfer_timeout self.tagger.webservice.set_transfer_timeout(transfer_timeout) config.setting[ "browser_integration"] = self.ui.browser_integration.isChecked() config.setting[ "browser_integration_port"] = self.ui.browser_integration_port.value( ) config.setting["browser_integration_localhost_only"] = \ self.ui.browser_integration_localhost_only.isChecked() self.update_browser_integration() def update_browser_integration(self): if self.ui.browser_integration.isChecked(): self.tagger.browser_integration.start() else: self.tagger.browser_integration.stop()
class ScriptingOptionsPage(OptionsPage): NAME = "scripting" TITLE = N_("Scripting") PARENT = None SORT_ORDER = 85 ACTIVE = True HELP_URL = '/config/options_scripting.html' options = [ config.BoolOption("setting", "enable_tagger_scripts", False), config.ListOption("setting", "list_of_scripts", []), config.IntOption("persist", "last_selected_script_pos", 0), config.Option("persist", "scripting_splitter", QtCore.QByteArray()), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_ScriptingOptionsPage() self.ui.setupUi(self) self.ui.tagger_script.setEnabled(False) self.ui.splitter.setStretchFactor(0, 1) self.ui.splitter.setStretchFactor(1, 2) self.move_view = MoveableListView(self.ui.script_list, self.ui.move_up_button, self.ui.move_down_button) self.ui.scripting_documentation_button.clicked.connect( self.show_scripting_documentation) self.scripting_documentation_shown = None def show_scripting_documentation(self): if not self.scripting_documentation_shown: self.scriptdoc_dialog = ScriptingDocumentationDialog(parent=self) self.scriptdoc_dialog.show() else: self.scriptdoc_dialog.raise_() self.scriptdoc_dialog.activateWindow() def enable_tagger_scripts_toggled(self, on): if on and self.ui.script_list.count() == 0: self.ui.script_list.add_script() def script_selected(self): items = self.ui.script_list.selectedItems() if items: item = items[0] self.ui.tagger_script.setEnabled(True) self.ui.tagger_script.setText(item.script) self.ui.tagger_script.setFocus(QtCore.Qt.OtherFocusReason) else: self.ui.tagger_script.setEnabled(False) self.ui.tagger_script.setText("") def live_update_and_check(self): items = self.ui.script_list.selectedItems() if items: script = items[0] script.script = self.ui.tagger_script.toPlainText() self.ui.script_error.setStyleSheet("") self.ui.script_error.setText("") try: self.check() except OptionsCheckError as e: self.ui.script_error.setStyleSheet(self.STYLESHEET_ERROR) self.ui.script_error.setText(e.info) return def check(self): parser = ScriptParser() try: parser.eval(self.ui.tagger_script.toPlainText()) except Exception as e: raise ScriptCheckError(_("Script Error"), str(e)) def restore_defaults(self): # Remove existing scripts self.ui.script_list.clear() self.ui.tagger_script.setText("") super().restore_defaults() def load(self): self.ui.enable_tagger_scripts.setChecked( config.setting["enable_tagger_scripts"]) for pos, name, enabled, text in config.setting["list_of_scripts"]: list_item = ScriptListWidgetItem(name, enabled, text) self.ui.script_list.addItem(list_item) # Select the last selected script item last_selected_script_pos = config.persist["last_selected_script_pos"] last_selected_script = self.ui.script_list.item( last_selected_script_pos) if last_selected_script: self.ui.script_list.setCurrentItem(last_selected_script) last_selected_script.setSelected(True) self.restore_state() def _all_scripts(self): for row in range(0, self.ui.script_list.count()): item = self.ui.script_list.item(row) yield item.get_all() @restore_method def restore_state(self): # Preserve previous splitter position self.ui.splitter.restoreState(config.persist["scripting_splitter"]) def save(self): config.setting[ "enable_tagger_scripts"] = self.ui.enable_tagger_scripts.isChecked( ) config.setting["list_of_scripts"] = list(self._all_scripts()) config.persist[ "last_selected_script_pos"] = self.ui.script_list.currentRow() config.persist["scripting_splitter"] = self.ui.splitter.saveState() def display_error(self, error): # Ignore scripting errors, those are handled inline if not isinstance(error, ScriptCheckError): super().display_error(error)
class GeneralOptionsPage(OptionsPage): NAME = "general" TITLE = N_("General") PARENT = None SORT_ORDER = 1 ACTIVE = True options = [ config.TextOption("setting", "server_host", MUSICBRAINZ_SERVERS[0]), config.IntOption("setting", "server_port", 80), config.TextOption("persist", "oauth_refresh_token", ""), config.BoolOption("setting", "analyze_new_files", False), config.BoolOption("setting", "ignore_file_mbids", False), config.TextOption("persist", "oauth_refresh_token", ""), config.TextOption("persist", "oauth_refresh_token_scopes", ""), config.TextOption("persist", "oauth_access_token", ""), config.IntOption("persist", "oauth_access_token_expires", 0), config.TextOption("persist", "oauth_username", ""), ] def __init__(self, parent=None): super(GeneralOptionsPage, self).__init__(parent) self.ui = Ui_GeneralOptionsPage() self.ui.setupUi(self) self.ui.server_host.addItems(MUSICBRAINZ_SERVERS) self.ui.login.clicked.connect(self.login) self.ui.logout.clicked.connect(self.logout) self.update_login_logout() def load(self): self.ui.server_host.setEditText(config.setting["server_host"]) self.ui.server_port.setValue(config.setting["server_port"]) self.ui.analyze_new_files.setChecked( config.setting["analyze_new_files"]) self.ui.ignore_file_mbids.setChecked( config.setting["ignore_file_mbids"]) def save(self): config.setting["server_host"] = unicode( self.ui.server_host.currentText()).strip() config.setting["server_port"] = self.ui.server_port.value() config.setting[ "analyze_new_files"] = self.ui.analyze_new_files.isChecked() config.setting[ "ignore_file_mbids"] = self.ui.ignore_file_mbids.isChecked() def update_login_logout(self): if self.tagger.xmlws.oauth_manager.is_logged_in(): self.ui.logged_in.setText( _("Logged in as <b>%s</b>.") % config.persist["oauth_username"]) self.ui.logged_in.show() self.ui.login.hide() self.ui.logout.show() else: self.ui.logged_in.hide() self.ui.login.show() self.ui.logout.hide() def login(self): scopes = "profile tag rating collection submit_isrc submit_barcode" authorization_url = self.tagger.xmlws.oauth_manager.get_authorization_url( scopes) webbrowser2.open(authorization_url) authorization_code, ok = QInputDialog.getText(self, _("MusicBrainz Account"), _("Authorization code:")) if ok: self.tagger.xmlws.oauth_manager.exchange_authorization_code( authorization_code, scopes, self.on_authorization_finished) def on_authorization_finished(self, successful): if successful: self.tagger.xmlws.oauth_manager.fetch_username( self.on_login_finished) def on_login_finished(self, successful): self.update_login_logout() if successful: load_user_collections() def logout(self): self.tagger.xmlws.oauth_manager.revoke_tokens() self.update_login_logout() load_user_collections()
class AdvancedOptionsPage(OptionsPage): NAME = "advanced" TITLE = N_("Advanced") PARENT = None SORT_ORDER = 90 ACTIVE = True options = [ config.TextOption("setting", "ignore_regex", ""), config.BoolOption("setting", "ignore_hidden_files", False), config.BoolOption("setting", "recursively_add_files", True), config.IntOption("setting", "ignore_track_duration_difference_under", 2), config.BoolOption("setting", "completeness_ignore_videos", False), config.BoolOption("setting", "completeness_ignore_pregap", False), config.BoolOption("setting", "completeness_ignore_data", False), config.BoolOption("setting", "completeness_ignore_silence", False), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_AdvancedOptionsPage() self.ui.setupUi(self) self.init_regex_checker(self.ui.ignore_regex, self.ui.regex_error) def load(self): self.ui.ignore_regex.setText(config.setting["ignore_regex"]) self.ui.ignore_hidden_files.setChecked( config.setting["ignore_hidden_files"]) self.ui.recursively_add_files.setChecked( config.setting["recursively_add_files"]) self.ui.ignore_track_duration_difference_under.setValue( config.setting["ignore_track_duration_difference_under"]) self.ui.completeness_ignore_videos.setChecked( config.setting["completeness_ignore_videos"]) self.ui.completeness_ignore_pregap.setChecked( config.setting["completeness_ignore_pregap"]) self.ui.completeness_ignore_data.setChecked( config.setting["completeness_ignore_data"]) self.ui.completeness_ignore_silence.setChecked( config.setting["completeness_ignore_silence"]) def save(self): config.setting["ignore_regex"] = self.ui.ignore_regex.text() config.setting[ "ignore_hidden_files"] = self.ui.ignore_hidden_files.isChecked() config.setting[ "recursively_add_files"] = self.ui.recursively_add_files.isChecked( ) config.setting[ "ignore_track_duration_difference_under"] = self.ui.ignore_track_duration_difference_under.value( ) config.setting[ "completeness_ignore_videos"] = self.ui.completeness_ignore_videos.isChecked( ) config.setting[ "completeness_ignore_pregap"] = self.ui.completeness_ignore_pregap.isChecked( ) config.setting[ "completeness_ignore_data"] = self.ui.completeness_ignore_data.isChecked( ) config.setting[ "completeness_ignore_silence"] = self.ui.completeness_ignore_silence.isChecked( )
class NetworkOptionsPage(OptionsPage): NAME = "network" TITLE = N_("Network") PARENT = "advanced" SORT_ORDER = 10 ACTIVE = True options = [ config.BoolOption("setting", "use_proxy", False), config.TextOption("setting", "proxy_server_host", ""), config.IntOption("setting", "proxy_server_port", 80), config.TextOption("setting", "proxy_username", ""), config.TextOption("setting", "proxy_password", ""), config.BoolOption("setting", "browser_integration", True), config.IntOption("setting", "browser_integration_port", 8000), config.BoolOption("setting", "browser_integration_localhost_only", True) ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_NetworkOptionsPage() self.ui.setupUi(self) def load(self): self.ui.web_proxy.setChecked(config.setting["use_proxy"]) self.ui.server_host.setText(config.setting["proxy_server_host"]) self.ui.server_port.setValue(config.setting["proxy_server_port"]) self.ui.username.setText(config.setting["proxy_username"]) self.ui.password.setText(config.setting["proxy_password"]) self.ui.browser_integration.setChecked( config.setting["browser_integration"]) self.ui.browser_integration_port.setValue( config.setting["browser_integration_port"]) self.ui.browser_integration_localhost_only.setChecked( config.setting["browser_integration_localhost_only"]) self.ui.browser_integration_port.valueChanged.connect( self.change_browser_integration_port) def save(self): config.setting["use_proxy"] = self.ui.web_proxy.isChecked() config.setting["proxy_server_host"] = self.ui.server_host.text() config.setting["proxy_server_port"] = self.ui.server_port.value() config.setting["proxy_username"] = self.ui.username.text() config.setting["proxy_password"] = self.ui.password.text() self.tagger.webservice.setup_proxy() config.setting[ "browser_integration"] = self.ui.browser_integration.isChecked() config.setting[ "browser_integration_port"] = self.ui.browser_integration_port.value( ) config.setting["browser_integration_localhost_only"] = \ self.ui.browser_integration_localhost_only.isChecked() self.update_browser_integration() def update_browser_integration(self): if self.ui.browser_integration.isChecked(): self.tagger.browser_integration.start() else: self.tagger.browser_integration.stop() def change_browser_integration_port(self, port): config.setting["browser_integration_port"] = port
class ScriptingOptionsPage(OptionsPage): NAME = "scripting" TITLE = N_("Scripting") PARENT = None SORT_ORDER = 85 ACTIVE = True options = [ config.BoolOption("setting", "enable_tagger_scripts", False), config.ListOption("setting", "list_of_scripts", []), config.IntOption("persist", "last_selected_script_pos", 0), config.Option("persist", "scripting_splitter", QtCore.QByteArray()), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_ScriptingOptionsPage() self.ui.setupUi(self) self.highlighter = TaggerScriptSyntaxHighlighter( self.ui.tagger_script.document()) self.ui.tagger_script.setEnabled(False) self.ui.splitter.setStretchFactor(0, 1) self.ui.splitter.setStretchFactor(1, 2) font = QtGui.QFont('Monospace') font.setStyleHint(QtGui.QFont.TypeWriter) self.ui.tagger_script.setFont(font) self.move_view = MoveableListView(self.ui.script_list, self.ui.move_up_button, self.ui.move_down_button) def script_selected(self): items = self.ui.script_list.selectedItems() if items: item = items[0] self.ui.tagger_script.setEnabled(True) self.ui.tagger_script.setText(item.script) else: self.ui.tagger_script.setEnabled(False) self.ui.tagger_script.setText("") def live_update_and_check(self): items = self.ui.script_list.selectedItems() if items: script = items[0] script.script = self.ui.tagger_script.toPlainText() self.ui.script_error.setStyleSheet("") self.ui.script_error.setText("") try: self.check() except OptionsCheckError as e: self.ui.script_error.setStyleSheet(self.STYLESHEET_ERROR) self.ui.script_error.setText(e.info) return def check(self): parser = ScriptParser() try: parser.eval(self.ui.tagger_script.toPlainText()) except Exception as e: raise ScriptCheckError(_("Script Error"), str(e)) def restore_defaults(self): # Remove existing scripts self.ui.script_list.clear() self.ui.tagger_script.setText("") super().restore_defaults() def load(self): self.ui.enable_tagger_scripts.setChecked( config.setting["enable_tagger_scripts"]) for pos, name, enabled, text in config.setting["list_of_scripts"]: list_item = ScriptListWidgetItem(name, enabled, text) self.ui.script_list.addItem(list_item) # Select the last selected script item last_selected_script_pos = config.persist["last_selected_script_pos"] last_selected_script = self.ui.script_list.item( last_selected_script_pos) if last_selected_script: self.ui.script_list.setCurrentItem(last_selected_script) last_selected_script.setSelected(True) self.restore_state() args = { "picard-doc-scripting-url": PICARD_URLS['doc_scripting'], } text = _('<a href="%(picard-doc-scripting-url)s">Open Scripting' ' Documentation in your browser</a>') % args self.ui.scripting_doc_link.setText(text) def _all_scripts(self): for row in range(0, self.ui.script_list.count()): item = self.ui.script_list.item(row) yield item.get_all() @restore_method def restore_state(self): # Preserve previous splitter position self.ui.splitter.restoreState(config.persist["scripting_splitter"]) def save(self): config.setting[ "enable_tagger_scripts"] = self.ui.enable_tagger_scripts.isChecked( ) config.setting["list_of_scripts"] = list(self._all_scripts()) config.persist[ "last_selected_script_pos"] = self.ui.script_list.currentRow() config.persist["scripting_splitter"] = self.ui.splitter.saveState() def display_error(self, error): # Ignore scripting errors, those are handled inline if not isinstance(error, ScriptCheckError): super().display_error(error)
class GeneralOptionsPage(OptionsPage): NAME = "general" TITLE = N_("General") PARENT = None SORT_ORDER = 1 ACTIVE = True options = [ config.TextOption("setting", "server_host", MUSICBRAINZ_SERVERS[0]), config.IntOption("setting", "server_port", 443), config.TextOption("persist", "oauth_refresh_token", ""), config.BoolOption("setting", "analyze_new_files", False), config.BoolOption("setting", "ignore_file_mbids", False), config.TextOption("persist", "oauth_refresh_token", ""), config.TextOption("persist", "oauth_refresh_token_scopes", ""), config.TextOption("persist", "oauth_access_token", ""), config.IntOption("persist", "oauth_access_token_expires", 0), config.TextOption("persist", "oauth_username", ""), config.BoolOption("setting", "check_for_updates", True), config.IntOption("setting", "update_check_days", 7), config.IntOption("setting", "update_level", 0), config.IntOption("persist", "last_update_check", 0), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_GeneralOptionsPage() self.ui.setupUi(self) self.ui.server_host.addItems(MUSICBRAINZ_SERVERS) self.ui.login.clicked.connect(self.login) self.ui.logout.clicked.connect(self.logout) self.update_login_logout() def load(self): self.ui.server_host.setEditText(config.setting["server_host"]) self.ui.server_port.setValue(config.setting["server_port"]) self.ui.analyze_new_files.setChecked( config.setting["analyze_new_files"]) self.ui.ignore_file_mbids.setChecked( config.setting["ignore_file_mbids"]) self.ui.check_for_updates.setChecked( config.setting["check_for_updates"]) self.ui.update_level.clear() for level, description in PROGRAM_UPDATE_LEVELS.items(): self.ui.update_level.addItem(_(description['title']), level) self.ui.update_level.setCurrentIndex( self.ui.update_level.findData(config.setting["update_level"])) self.ui.update_check_days.setValue(config.setting["update_check_days"]) def save(self): config.setting["server_host"] = self.ui.server_host.currentText( ).strip() config.setting["server_port"] = self.ui.server_port.value() config.setting[ "analyze_new_files"] = self.ui.analyze_new_files.isChecked() config.setting[ "ignore_file_mbids"] = self.ui.ignore_file_mbids.isChecked() config.setting[ "check_for_updates"] = self.ui.check_for_updates.isChecked() config.setting["update_level"] = self.ui.update_level.currentData( QtCore.Qt.UserRole) config.setting["update_check_days"] = self.ui.update_check_days.value() def update_login_logout(self): if self.tagger.webservice.oauth_manager.is_logged_in(): self.ui.logged_in.setText( _("Logged in as <b>%s</b>.") % config.persist["oauth_username"]) self.ui.logged_in.show() self.ui.login.hide() self.ui.logout.show() else: self.ui.logged_in.hide() self.ui.login.show() self.ui.logout.hide() def login(self): scopes = "profile tag rating collection submit_isrc submit_barcode" authorization_url = self.tagger.webservice.oauth_manager.get_authorization_url( scopes) webbrowser2.open(authorization_url) authorization_code, ok = QInputDialog.getText(self, _("MusicBrainz Account"), _("Authorization code:")) if ok: self.tagger.webservice.oauth_manager.exchange_authorization_code( authorization_code, scopes, self.on_authorization_finished) def restore_defaults(self): super().restore_defaults() self.logout() def on_authorization_finished(self, successful): if successful: self.tagger.webservice.oauth_manager.fetch_username( self.on_login_finished) def on_login_finished(self, successful): self.update_login_logout() if successful: load_user_collections() def logout(self): self.tagger.webservice.oauth_manager.revoke_tokens() self.update_login_logout() load_user_collections()
class CoverOptionsPage(OptionsPage): NAME = "cover" TITLE = N_("Cover Art") PARENT = None SORT_ORDER = 35 ACTIVE = True options = [ config.BoolOption("setting", "save_images_to_tags", True), config.BoolOption("setting", "save_only_front_images_to_tags", True), config.BoolOption("setting", "save_images_to_files", False), config.TextOption("setting", "cover_image_filename", "cover"), config.BoolOption("setting", "save_images_overwrite", False), config.BoolOption("setting", "ca_provider_use_amazon", True), config.BoolOption("setting", "ca_provider_use_caa", True), config.BoolOption("setting", "ca_provider_use_whitelist", True), config.BoolOption("setting", "caa_approved_only", True), config.BoolOption("setting", "caa_image_type_as_filename", False), config.IntOption("setting", "caa_image_size", 1), config.ListOption("setting", "caa_image_types", [u"front"]), ] def __init__(self, parent=None): super(CoverOptionsPage, self).__init__(parent) self.ui = Ui_CoverOptionsPage() self.ui.setupUi(self) self.ui.save_images_to_files.clicked.connect(self.update_filename) def load(self): self.ui.save_images_to_tags.setChecked( config.setting["save_images_to_tags"]) self.ui.cb_embed_front_only.setChecked( config.setting["save_only_front_images_to_tags"]) self.ui.save_images_to_files.setChecked( config.setting["save_images_to_files"]) self.ui.cover_image_filename.setText( config.setting["cover_image_filename"]) self.ui.save_images_overwrite.setChecked( config.setting["save_images_overwrite"]) self.update_filename() self.ui.caprovider_amazon.setChecked( config.setting["ca_provider_use_amazon"]) self.ui.caprovider_caa.setChecked( config.setting["ca_provider_use_caa"]) self.ui.caprovider_whitelist.setChecked( config.setting["ca_provider_use_whitelist"]) self.ui.gb_caa.setEnabled(config.setting["ca_provider_use_caa"]) self.ui.cb_image_size.setCurrentIndex(config.setting["caa_image_size"]) widget = self.ui.caa_types_selector_1 self._selector = CAATypesSelector(widget, config.setting["caa_image_types"]) config.setting["caa_image_types"] = self._selector.get_selected_types() self.ui.cb_approved_only.setChecked( config.setting["caa_approved_only"]) self.ui.cb_type_as_filename.setChecked( config.setting["caa_image_type_as_filename"]) self.connect(self.ui.caprovider_caa, QtCore.SIGNAL("toggled(bool)"), self.ui.gb_caa.setEnabled) def save(self): config.setting[ "save_images_to_tags"] = self.ui.save_images_to_tags.isChecked() config.setting[ "save_only_front_images_to_tags"] = self.ui.cb_embed_front_only.isChecked( ) config.setting[ "save_images_to_files"] = self.ui.save_images_to_files.isChecked() config.setting["cover_image_filename"] = unicode( self.ui.cover_image_filename.text()) config.setting["ca_provider_use_amazon"] =\ self.ui.caprovider_amazon.isChecked() config.setting["ca_provider_use_caa"] =\ self.ui.caprovider_caa.isChecked() config.setting["ca_provider_use_whitelist"] =\ self.ui.caprovider_whitelist.isChecked() config.setting["caa_image_size"] =\ self.ui.cb_image_size.currentIndex() config.setting["caa_image_types"] = self._selector.get_selected_types() config.setting["caa_approved_only"] =\ self.ui.cb_approved_only.isChecked() config.setting["caa_image_type_as_filename"] = \ self.ui.cb_type_as_filename.isChecked() config.setting[ "save_images_overwrite"] = self.ui.save_images_overwrite.isChecked( ) def update_filename(self): enabled = self.ui.save_images_to_files.isChecked() self.ui.cover_image_filename.setEnabled(enabled) self.ui.save_images_overwrite.setEnabled(enabled)
class GenresOptionsPage(OptionsPage): NAME = "genres" TITLE = N_("Genres") PARENT = "metadata" SORT_ORDER = 20 ACTIVE = True options = [ config.BoolOption("setting", "use_genres", False), config.IntOption("setting", "max_genres", 5), config.IntOption("setting", "min_genre_usage", 90), config.TextOption("setting", "genres_filter", "-seen live\n-favorites\n-fixme\n-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), ] def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_GenresOptionsPage() self.ui.setupUi(self) self.ui.genres_filter.setToolTip(_(TOOLTIP_GENRES_FILTER)) self.ui.genres_filter.textChanged.connect( self.update_test_genres_filter) self.ui.test_genres_filter.setToolTip(_(TOOLTIP_TEST_GENRES_FILTER)) self.ui.test_genres_filter.textChanged.connect( self.update_test_genres_filter) # FIXME: colors aren't great from accessibility POV self.fmt_keep = QTextBlockFormat() self.fmt_keep.setBackground(Qt.green) self.fmt_skip = QTextBlockFormat() self.fmt_skip.setBackground(Qt.red) self.fmt_clear = QTextBlockFormat() self.fmt_clear.clearBackground() def load(self): self.ui.use_genres.setChecked(config.setting["use_genres"]) 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.genres_filter.setPlainText(config.setting["genres_filter"]) 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_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["genres_filter"] = self.ui.genres_filter.toPlainText() 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() def update_test_genres_filter(self): test_text = self.ui.test_genres_filter.toPlainText() filters = self.ui.genres_filter.toPlainText() tagfilter = TagGenreFilter(filters) # FIXME: very simple error reporting, improve self.ui.label_test_genres_filter_error.setText("\n".join([ _("Error line %d: %s") % (lineno + 1, error) for lineno, error in tagfilter.errors.items() ])) def set_line_fmt(lineno, textformat): obj = self.ui.test_genres_filter if lineno < 0: # use current cursor position cursor = obj.textCursor() else: cursor = QTextCursor(obj.document().findBlockByNumber(lineno)) obj.blockSignals(True) cursor.setBlockFormat(textformat) obj.blockSignals(False) set_line_fmt(-1, self.fmt_clear) for lineno, line in enumerate(test_text.splitlines()): line = line.strip() fmt = self.fmt_clear if line: if tagfilter.skip(line): fmt = self.fmt_skip else: fmt = self.fmt_keep set_line_fmt(lineno, fmt)
class ProviderOptionsCaa(ProviderOptions): """ Options for Cover Art Archive cover art provider """ options = [ config.BoolOption("setting", "caa_save_single_front_image", False), config.BoolOption("setting", "caa_approved_only", False), config.BoolOption("setting", "caa_image_type_as_filename", False), config.IntOption("setting", "caa_image_size", _CAA_IMAGE_SIZE_DEFAULT), config.ListOption("setting", "caa_image_types", _CAA_IMAGE_TYPE_DEFAULT_INCLUDE), config.BoolOption("setting", "caa_restrict_image_types", True), config.ListOption("setting", "caa_image_types_to_omit", _CAA_IMAGE_TYPE_DEFAULT_EXCLUDE), ] _options_ui = Ui_CaaOptions def __init__(self, parent=None): super().__init__(parent) self.ui.restrict_images_types.clicked.connect(self.update_caa_types) self.ui.select_caa_types.clicked.connect(self.select_caa_types) def restore_defaults(self): self.caa_image_types = _CAA_IMAGE_TYPE_DEFAULT_INCLUDE self.caa_image_types_to_omit = _CAA_IMAGE_TYPE_DEFAULT_EXCLUDE super().restore_defaults() def load(self): self.ui.cb_image_size.clear() for item_id, item in _CAA_THUMBNAIL_SIZE_MAP.items(): self.ui.cb_image_size.addItem(_(item.label), userData=item_id) size = config.setting["caa_image_size"] index = self.ui.cb_image_size.findData(size) if index < 0: index = self.ui.cb_image_size.findData(_CAA_IMAGE_SIZE_DEFAULT) self.ui.cb_image_size.setCurrentIndex(index) self.ui.cb_save_single_front_image.setChecked(config.setting["caa_save_single_front_image"]) self.ui.cb_approved_only.setChecked(config.setting["caa_approved_only"]) self.ui.cb_type_as_filename.setChecked(config.setting["caa_image_type_as_filename"]) self.ui.restrict_images_types.setChecked( config.setting["caa_restrict_image_types"]) self.caa_image_types = config.setting["caa_image_types"] self.caa_image_types_to_omit = config.setting["caa_image_types_to_omit"] self.update_caa_types() def save(self): size = self.ui.cb_image_size.currentData() config.setting["caa_image_size"] = size config.setting["caa_save_single_front_image"] = \ self.ui.cb_save_single_front_image.isChecked() config.setting["caa_approved_only"] = \ self.ui.cb_approved_only.isChecked() config.setting["caa_image_type_as_filename"] = \ self.ui.cb_type_as_filename.isChecked() config.setting["caa_restrict_image_types"] = \ self.ui.restrict_images_types.isChecked() config.setting["caa_image_types"] = self.caa_image_types config.setting["caa_image_types_to_omit"] = self.caa_image_types_to_omit def update_caa_types(self): enabled = self.ui.restrict_images_types.isChecked() self.ui.select_caa_types.setEnabled(enabled) def select_caa_types(self): (types, types_to_omit, ok) = CAATypesSelectorDialog.run( self, self.caa_image_types, self.caa_image_types_to_omit) if ok: self.caa_image_types = types self.caa_image_types_to_omit = types_to_omit
class FormatPerformerTagsOptionsPage(OptionsPage): NAME = "format_performer_tags" TITLE = "Format Performer Tags" PARENT = "plugins" HELP_URL = "https://github.com/metabrainz/picard-plugins/blob/2.0/plugins/format_performer_tags/docs/README.md" options = [ config.IntOption("setting", "format_group_additional", 3), config.IntOption("setting", "format_group_guest", 4), config.IntOption("setting", "format_group_solo", 3), config.IntOption("setting", "format_group_vocals", 2), config.TextOption("setting", "format_group_1_start_char", ''), config.TextOption("setting", "format_group_1_end_char", ' '), config.TextOption("setting", "format_group_1_sep_char", ''), config.TextOption("setting", "format_group_2_start_char", ', '), config.TextOption("setting", "format_group_2_end_char", ''), config.TextOption("setting", "format_group_2_sep_char", ''), config.TextOption("setting", "format_group_3_start_char", ' ('), config.TextOption("setting", "format_group_3_end_char", ')'), config.TextOption("setting", "format_group_3_sep_char", ''), config.TextOption("setting", "format_group_4_start_char", ' ('), config.TextOption("setting", "format_group_4_end_char", ')'), config.TextOption("setting", "format_group_4_sep_char", ''), ] def __init__(self, parent=None): super(FormatPerformerTagsOptionsPage, self).__init__(parent) self.ui = Ui_FormatPerformerTagsOptionsPage() self.ui.setupUi(self) self.ui.additional_rb_1.clicked.connect(self.update_examples) self.ui.additional_rb_2.clicked.connect(self.update_examples) self.ui.additional_rb_3.clicked.connect(self.update_examples) self.ui.additional_rb_4.clicked.connect(self.update_examples) self.ui.guest_rb_1.clicked.connect(self.update_examples) self.ui.guest_rb_2.clicked.connect(self.update_examples) self.ui.guest_rb_3.clicked.connect(self.update_examples) self.ui.guest_rb_4.clicked.connect(self.update_examples) self.ui.solo_rb_1.clicked.connect(self.update_examples) self.ui.solo_rb_2.clicked.connect(self.update_examples) self.ui.solo_rb_3.clicked.connect(self.update_examples) self.ui.solo_rb_4.clicked.connect(self.update_examples) self.ui.vocals_rb_1.clicked.connect(self.update_examples) self.ui.vocals_rb_2.clicked.connect(self.update_examples) self.ui.vocals_rb_3.clicked.connect(self.update_examples) self.ui.vocals_rb_4.clicked.connect(self.update_examples) self.ui.format_group_1_start_char.editingFinished.connect(self.update_examples) self.ui.format_group_2_start_char.editingFinished.connect(self.update_examples) self.ui.format_group_3_start_char.editingFinished.connect(self.update_examples) self.ui.format_group_4_start_char.editingFinished.connect(self.update_examples) self.ui.format_group_1_sep_char.editingFinished.connect(self.update_examples) self.ui.format_group_2_sep_char.editingFinished.connect(self.update_examples) self.ui.format_group_3_sep_char.editingFinished.connect(self.update_examples) self.ui.format_group_4_sep_char.editingFinished.connect(self.update_examples) self.ui.format_group_1_end_char.editingFinished.connect(self.update_examples) self.ui.format_group_2_end_char.editingFinished.connect(self.update_examples) self.ui.format_group_3_end_char.editingFinished.connect(self.update_examples) self.ui.format_group_4_end_char.editingFinished.connect(self.update_examples) def load(self): # Enable external link self.ui.format_description.setOpenExternalLinks(True) # Settings for Keyword: additional temp = config.setting["format_group_additional"] if temp > 3: self.ui.additional_rb_4.setChecked(True) elif temp > 2: self.ui.additional_rb_3.setChecked(True) elif temp > 1: self.ui.additional_rb_2.setChecked(True) else: self.ui.additional_rb_1.setChecked(True) # Settings for Keyword: guest temp = config.setting["format_group_guest"] if temp > 3: self.ui.guest_rb_4.setChecked(True) elif temp > 2: self.ui.guest_rb_3.setChecked(True) elif temp > 1: self.ui.guest_rb_2.setChecked(True) else: self.ui.guest_rb_1.setChecked(True) # Settings for Keyword: solo temp = config.setting["format_group_solo"] if temp > 3: self.ui.solo_rb_4.setChecked(True) elif temp > 2: self.ui.solo_rb_3.setChecked(True) elif temp > 1: self.ui.solo_rb_2.setChecked(True) else: self.ui.solo_rb_1.setChecked(True) # Settings for all vocal keywords temp = config.setting["format_group_vocals"] if temp > 3: self.ui.vocals_rb_4.setChecked(True) elif temp > 2: self.ui.vocals_rb_3.setChecked(True) elif temp > 1: self.ui.vocals_rb_2.setChecked(True) else: self.ui.vocals_rb_1.setChecked(True) # Settings for word group 1 self.ui.format_group_1_start_char.setText(config.setting["format_group_1_start_char"]) self.ui.format_group_1_end_char.setText(config.setting["format_group_1_end_char"]) self.ui.format_group_1_sep_char.setText(config.setting["format_group_1_sep_char"]) # Settings for word group 2 self.ui.format_group_2_start_char.setText(config.setting["format_group_2_start_char"]) self.ui.format_group_2_end_char.setText(config.setting["format_group_2_end_char"]) self.ui.format_group_2_sep_char.setText(config.setting["format_group_2_sep_char"]) # Settings for word group 3 self.ui.format_group_3_start_char.setText(config.setting["format_group_3_start_char"]) self.ui.format_group_3_end_char.setText(config.setting["format_group_3_end_char"]) self.ui.format_group_3_sep_char.setText(config.setting["format_group_3_sep_char"]) # Settings for word group 4 self.ui.format_group_4_start_char.setText(config.setting["format_group_4_start_char"]) self.ui.format_group_4_end_char.setText(config.setting["format_group_4_end_char"]) self.ui.format_group_4_sep_char.setText(config.setting["format_group_4_sep_char"]) self.update_examples() # TODO: Modify self.format_description in ui_options_format_performer_tags.py to include a placeholder # such as {user_guide_url} so that the translated string can be formatted to include the value # of PLUGIN_USER_GUIDE_URL to dynamically set the link while not requiring retranslation if the # link changes. Preliminary code something like: # # temp = (self.ui.format_description.text).format(user_guide_url=PLUGIN_USER_GUIDE_URL,) # self.ui.format_description.setText(temp) def save(self): self._set_settings(config.setting) def restore_defaults(self): super().restore_defaults() self.update_examples() def _set_settings(self, settings): # Process 'additional' keyword settings temp = 1 if self.ui.additional_rb_2.isChecked(): temp = 2 if self.ui.additional_rb_3.isChecked(): temp = 3 if self.ui.additional_rb_4.isChecked(): temp = 4 settings["format_group_additional"] = temp # Process 'guest' keyword settings temp = 1 if self.ui.guest_rb_2.isChecked(): temp = 2 if self.ui.guest_rb_3.isChecked(): temp = 3 if self.ui.guest_rb_4.isChecked(): temp = 4 settings["format_group_guest"] = temp # Process 'solo' keyword settings temp = 1 if self.ui.solo_rb_2.isChecked(): temp = 2 if self.ui.solo_rb_3.isChecked(): temp = 3 if self.ui.solo_rb_4.isChecked(): temp = 4 settings["format_group_solo"] = temp # Process all vocal keyword settings temp = 1 if self.ui.vocals_rb_2.isChecked(): temp = 2 if self.ui.vocals_rb_3.isChecked(): temp = 3 if self.ui.vocals_rb_4.isChecked(): temp = 4 settings["format_group_vocals"] = temp # Settings for word group 1 settings["format_group_1_start_char"] = self.ui.format_group_1_start_char.text() settings["format_group_1_end_char"] = self.ui.format_group_1_end_char.text() settings["format_group_1_sep_char"] = self.ui.format_group_1_sep_char.text() # Settings for word group 2 settings["format_group_2_start_char"] = self.ui.format_group_2_start_char.text() settings["format_group_2_end_char"] = self.ui.format_group_2_end_char.text() settings["format_group_2_sep_char"] = self.ui.format_group_2_sep_char.text() # Settings for word group 3 settings["format_group_3_start_char"] = self.ui.format_group_3_start_char.text() settings["format_group_3_end_char"] = self.ui.format_group_3_end_char.text() settings["format_group_3_sep_char"] = self.ui.format_group_3_sep_char.text() # Settings for word group 4 settings["format_group_4_start_char"] = self.ui.format_group_4_start_char.text() settings["format_group_4_end_char"] = self.ui.format_group_4_end_char.text() settings["format_group_4_sep_char"] = self.ui.format_group_4_sep_char.text() def update_examples(self): settings = {} self._set_settings(settings) word_dict = get_word_dict(settings) instruments_credits = { "guitar": ["Johnny Flux", "John Watson"], "guest guitar": ["Jimmy Page"], "additional guest solo guitar": ["Jimmy Page"], } instruments_example = self.build_example(instruments_credits, word_dict, settings) self.ui.example_instruments.setText(instruments_example) vocals_credits = { "additional solo lead vocals": ["Robert Plant"], "additional solo guest lead vocals": ["Sandy Denny"], } vocals_example = self.build_example(vocals_credits, word_dict, settings) self.ui.example_vocals.setText(vocals_example) @staticmethod def build_example(credits, word_dict, settings): prefix = "performer:" metadata = Metadata() for key, values in credits.items(): rewrite_tag(prefix + key, values, metadata, word_dict, settings) examples = [] for key, values in metadata.rawitems(): credit = "%s: %s" % (key, ", ".join(values)) if credit.startswith(prefix): credit = credit[len(prefix):] examples.append(credit) return "\n".join(examples)