Exemplo n.º 1
0
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()
Exemplo n.º 2
0
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()
Exemplo n.º 3
0
Arquivo: caa.py Projeto: cartr/picard
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
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
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()
Exemplo n.º 6
0
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()
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
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()
Exemplo n.º 12
0
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(
            )
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
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()
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
Arquivo: caa.py Projeto: skyme5/picard
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
Exemplo n.º 19
0
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)