Example #1
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)
        self.update_examples()

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.windows_compatible_filenames,
                     QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.use_va_format, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)

        self.connect(self.ui.file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.va_file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_file_naming_format_default)
        self.connect(self.ui.va_file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_va_file_naming_format_default)
        self.connect(self.ui.va_copy_from_above, QtCore.SIGNAL("clicked()"),
                     self.copy_format_to_va)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.highlighter_va = TaggerScriptSyntaxHighlighter(
            self.ui.va_file_naming_format.document())
Example #2
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_moving
            )
        )
        self.ui.rename_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_renaming
            )
        )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        script_edit = self.ui.file_naming_format
        font = QFont('Monospace')
        font.setStyleHint(QFont.TypeWriter)
        script_edit.setFont(font)
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(QPalette.Inactive, QPalette.Window)
        self.script_palette_readonly.setColor(QPalette.Disabled, QPalette.Base, disabled_color)
Example #3
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(
            self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        textEdit = self.ui.file_naming_format
        self.textEditPaletteNormal = textEdit.palette()
        self.textEditPaletteReadOnly = QPalette(self.textEditPaletteNormal)
        disabled_color = self.textEditPaletteNormal.color(
            QPalette.Inactive, QPalette.Window)
        self.textEditPaletteReadOnly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)
Example #4
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.script_text = ""
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(
            self.update_examples_from_local)
        self.ui.windows_compatibility.clicked.connect(
            self.update_examples_from_local)
        self.ui.rename_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files_to.editingFinished.connect(
            self.update_examples_from_local)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.open_script_editor.clicked.connect(
            self.show_script_editing_page)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        self.ui.example_filename_after.itemSelectionChanged.connect(
            self.match_before_to_after)
        self.ui.example_filename_before.itemSelectionChanged.connect(
            self.match_after_to_before)

        script_edit = self.ui.move_additional_files_pattern
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(
            QPalette.Inactive, QPalette.Window)
        self.script_palette_readonly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)

        self.ui.example_filename_sample_files_button.clicked.connect(
            self.update_example_files)

        self.examples = ScriptEditorExamples(tagger=self.tagger)

        self.ui.example_selection_note.setText(self.examples.notes_text)
        self.ui.example_filename_sample_files_button.setToolTip(
            self.examples.tooltip_text)

        self.script_editor_page = ScriptEditorPage(parent=self,
                                                   examples=self.examples)
        self.script_editor_page.signal_save.connect(self.save_from_editor)
        self.script_editor_page.signal_update.connect(self.update_from_editor)

        # Sync example lists vertical scrolling and selection colors
        self.script_editor_page.synchronize_vertical_scrollbars(
            (self.ui.example_filename_before, self.ui.example_filename_after))

        self.current_row = -1
Example #5
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.script_text = ""
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(
            self.update_examples_from_local)
        self.ui.windows_compatibility.clicked.connect(
            self.update_examples_from_local)
        self.ui.rename_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files_to.editingFinished.connect(
            self.update_examples_from_local)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.open_script_editor.clicked.connect(
            self.show_script_editing_page)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        self.ui.naming_script_selector.currentIndexChanged.connect(
            partial(self.update_selector_in_editor, skip_check=False))

        self.ui.example_filename_after.itemSelectionChanged.connect(
            self.match_before_to_after)
        self.ui.example_filename_before.itemSelectionChanged.connect(
            self.match_after_to_before)

        script_edit = self.ui.move_additional_files_pattern
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(
            QPalette.Inactive, QPalette.Window)
        self.script_palette_readonly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)

        self.ui.example_filename_sample_files_button.clicked.connect(
            self.update_example_files)

        self.examples = ScriptEditorExamples(tagger=self.tagger)
        # Script editor dialog object will not be created until it is specifically requested, in order to ensure proper window modality.
        self.script_editor_dialog = None

        self.ui.example_selection_note.setText(self.examples.get_notes_text())
        self.ui.example_filename_sample_files_button.setToolTip(
            self.examples.get_tooltip_text())

        # Sync example lists vertical scrolling and selection colors
        synchronize_vertical_scrollbars(
            (self.ui.example_filename_before, self.ui.example_filename_after))

        self.current_row = -1
Example #6
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatible_filenames.clicked.connect(
            self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        # The following code is there to fix
        # http://tickets.musicbrainz.org/browse/PICARD-417
        # In some older version of PyQt/sip it's impossible to connect a signal
        # emitting an `int` to a slot expecting a `bool`.
        # By using `enabledSlot` instead we can force python to do the
        # conversion from int (`state`) to bool.
        def enabledSlot(func, state):
            """Calls `func` with `state`."""
            func(state)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(
                partial(enabledSlot,
                        self.ui.windows_compatible_filenames.setEnabled))

        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.delete_empty_dirs.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_files_to.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_files_to_browse.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_additional_files.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot,
                    self.ui.move_additional_files_pattern.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot, self.ui.ascii_filenames.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot, self.ui.file_naming_format.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot,
                    self.ui.file_naming_format_default.setEnabled))
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(
            self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)
Example #7
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatible_filenames.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.rename_files.stateChanged.connect(self.ui.ascii_filenames.setEnabled)
        self.ui.rename_files.stateChanged.connect(self.ui.file_naming_format.setEnabled)
        self.ui.rename_files.stateChanged.connect(self.ui.file_naming_format_default.setEnabled)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(self.ui.windows_compatible_filenames.setEnabled)

        self.ui.move_files.stateChanged.connect(self.ui.delete_empty_dirs.setEnabled)
        self.ui.move_files.stateChanged.connect(self.ui.move_files_to.setEnabled)
        self.ui.move_files.stateChanged.connect(self.ui.move_files_to_browse.setEnabled)
        self.ui.move_files.stateChanged.connect(self.ui.move_additional_files.setEnabled)
        self.ui.move_files.stateChanged.connect(self.ui.move_additional_files_pattern.setEnabled)
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)
Example #8
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_moving
            )
        )
        self.ui.rename_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_renaming
            )
        )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        textEdit = self.ui.file_naming_format
        self.textEditPaletteNormal = textEdit.palette()
        self.textEditPaletteReadOnly = QPalette(self.textEditPaletteNormal)
        disabled_color = self.textEditPaletteNormal.color(QPalette.Inactive, QPalette.Window)
        self.textEditPaletteReadOnly.setColor(QPalette.Disabled, QPalette.Base, disabled_color)
Example #9
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.windows_compatible_filenames,
                     QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.move_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.move_files_to, QtCore.SIGNAL("editingFinished()"),
                     self.update_examples)

        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.ascii_filenames.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.file_naming_format.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.file_naming_format_default.setEnabled)

        if not sys.platform == "win32":
            self.connect(self.ui.rename_files,
                         QtCore.SIGNAL("stateChanged(int)"),
                         self.ui.windows_compatible_filenames.setEnabled)

        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.delete_empty_dirs.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_files_to.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_files_to_browse.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_additional_files.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_additional_files_pattern.setEnabled)
        self.connect(self.ui.file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.connect(self.ui.move_files_to_browse, QtCore.SIGNAL("clicked()"),
                     self.move_files_to_browse)
Example #10
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)
        self.update_examples()

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.windows_compatible_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.use_va_format, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"), self.update_examples)

        self.connect(self.ui.file_naming_format, QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.va_file_naming_format, QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_file_naming_format_default)
        self.connect(self.ui.va_file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_va_file_naming_format_default)
        self.connect(self.ui.va_copy_from_above, QtCore.SIGNAL("clicked()"), self.copy_format_to_va)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.highlighter_va = TaggerScriptSyntaxHighlighter(self.ui.va_file_naming_format.document())
Example #11
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True
    HELP_URL = '/config/options_filerenaming.html'

    options = [
        BoolOption("setting", "windows_compatibility", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption(
            "setting",
            "file_naming_format",
            DEFAULT_FILE_NAMING_FORMAT,
        ),
        BoolOption("setting", "move_files", False),
        TextOption("setting", "move_files_to", _default_music_dir),
        BoolOption("setting", "move_additional_files", False),
        TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(
            self.set_file_naming_format_default)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        script_edit = self.ui.file_naming_format
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(
            QPalette.Inactive, QPalette.Window)
        self.script_palette_readonly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)

    def toggle_file_moving(self, state):
        self.toggle_file_naming_format()
        self.ui.delete_empty_dirs.setEnabled(state)
        self.ui.move_files_to.setEnabled(state)
        self.ui.move_files_to_browse.setEnabled(state)
        self.ui.move_additional_files.setEnabled(state)
        self.ui.move_additional_files_pattern.setEnabled(state)

    def toggle_file_renaming(self, state):
        self.toggle_file_naming_format()

    def toggle_file_naming_format(self):
        active = self.ui.move_files.isChecked(
        ) or self.ui.rename_files.isChecked()
        self.ui.file_naming_format.setEnabled(active)
        self.ui.file_naming_format_default.setEnabled(active)
        palette = self.script_palette_normal if active else self.script_palette_readonly
        self.ui.file_naming_format.setPalette(palette)

        self.ui.ascii_filenames.setEnabled(active)
        if not IS_WIN:
            self.ui.windows_compatibility.setEnabled(active)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        config = get_config()
        settings = SettingsOverride(
            config.setting, {
                'ascii_filenames': self.ui.ascii_filenames.isChecked(),
                'file_naming_format': self.ui.file_naming_format.toPlainText(),
                'move_files': self.ui.move_files.isChecked(),
                'move_files_to': os.path.normpath(
                    self.ui.move_files_to.text()),
                'rename_files': self.ui.rename_files.isChecked(),
                'windows_compatibility':
                self.ui.windows_compatibility.isChecked(),
            })

        try:
            if config.setting["enable_tagger_scripts"]:
                for s_pos, s_name, s_enabled, s_text in config.setting[
                        "list_of_scripts"]:
                    if s_enabled and s_text:
                        parser = ScriptParser()
                        parser.eval(s_text, file.metadata)
            filename = file.make_filename(file.filename, file.metadata,
                                          settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except ScriptError:
            return ""
        except TypeError:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        config = get_config()
        if IS_WIN:
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(
                config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(
            config.setting["file_naming_format"])
        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.file_naming_format_documentation.setText(text)
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(
            config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(
            config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(
            config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked(
        ) and not self.ui.move_files_to.text().strip():
            raise OptionsCheckError(
                _("Error"),
                _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(self.ui.file_naming_format.toPlainText())
        except Exception as e:
            raise ScriptCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not self.ui.file_naming_format.toPlainText().strip():
                raise ScriptCheckError(
                    "", _("The file naming format must not be empty."))

    def save(self):
        config = get_config()
        config.setting[
            "windows_compatibility"] = self.ui.windows_compatibility.isChecked(
            )
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting[
            "file_naming_format"] = self.ui.file_naming_format.toPlainText()
        self.tagger.window.enable_renaming_action.setChecked(
            config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(
            self.ui.move_files_to.text())
        config.setting[
            "move_additional_files"] = self.ui.move_additional_files.isChecked(
            )
        config.setting[
            "move_additional_files_pattern"] = self.ui.move_additional_files_pattern.text(
            )
        config.setting[
            "delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(
            config.setting["move_files"])

    def display_error(self, error):
        # Ignore scripting errors, those are handled inline
        if not isinstance(error, ScriptCheckError):
            super().display_error(error)

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)


#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['~releasecomment'] = '2014 mono remaster'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['originaldate'] = '1965-08-06'
        file.metadata['originalyear'] = '1965'
        file.metadata['date'] = '2014-09-08'
        file.metadata['releasetype'] = ['album', 'soundtrack']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['soundtrack']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = 'd7fbbb0a-1348-40ad-8eef-cd438d4cd203'
        file.metadata[
            'musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_recordingid'] = 'ed052ae1-c950-47f2-8d2b-46e1b58ab76c'
        file.metadata[
            'musicbrainz_releasetrackid'] = '392639f5-5629-477e-b04b-93bffa703405'
        return file

    def example_2(self):
        config = get_config()
        file = File("track05.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = "Coup d'État, Volume 1: Ku De Ta / Prologue"
        file.metadata['title'] = "I've Got to Learn the Mambo"
        file.metadata['artist'] = "Snowboy feat. James Hunter"
        file.metadata['artistsort'] = "Snowboy feat. Hunter, James"
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '13'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['discsubtitle'] = "Beat Up"
        file.metadata['originaldate'] = '2005-07-04'
        file.metadata['originalyear'] = '2005'
        file.metadata['date'] = '2005-07-04'
        file.metadata['releasetype'] = ['album', 'compilation']
        file.metadata['~primaryreleasetype'] = 'album'
        file.metadata['~secondaryreleasetype'] = 'compilation'
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'AU'
        file.metadata['compilation'] = '1'
        file.metadata['~multiartist'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = '4b50c71e-0a07-46ac-82e4-cb85dc0c9bdd'
        file.metadata[
            'musicbrainz_recordingid'] = 'b3c487cb-0e55-477d-8df3-01ec6590f099'
        file.metadata[
            'musicbrainz_releasetrackid'] = 'f8649a05-da39-39ba-957c-7abf8f9012be'
        file.metadata[
            'musicbrainz_albumartistid'] = '89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata['musicbrainz_artistid'] = [
            '7b593455-d207-482c-8c6f-19ce22c94679',
            '9e082466-2390-40d1-891e-4803531f43fd'
        ]
        return file

    def move_files_to_browse(self):
        path = QtWidgets.QFileDialog.getExistingDirectory(
            self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(path)
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except ScriptCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #12
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True
    HELP_URL = '/config/options_filerenaming.html'

    options = [
        BoolOption("setting", "windows_compatibility", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        BoolOption("setting", "move_files", False),
        TextOption("setting", "move_files_to", _default_music_dir),
        BoolOption("setting", "move_additional_files", False),
        TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super().__init__(parent)
        self.script_text = ""
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples_from_local)
        self.ui.windows_compatibility.clicked.connect(self.update_examples_from_local)
        self.ui.rename_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files_to.editingFinished.connect(self.update_examples_from_local)

        self.ui.move_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_moving
            )
        )
        self.ui.rename_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_renaming
            )
        )
        self.ui.open_script_editor.clicked.connect(self.show_script_editing_page)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        self.ui.naming_script_selector.currentIndexChanged.connect(self.update_selector_in_editor)

        self.ui.example_filename_after.itemSelectionChanged.connect(self.match_before_to_after)
        self.ui.example_filename_before.itemSelectionChanged.connect(self.match_after_to_before)

        script_edit = self.ui.move_additional_files_pattern
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(QPalette.ColorGroup.Inactive, QPalette.ColorRole.Window)
        self.script_palette_readonly.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, disabled_color)

        self.ui.example_filename_sample_files_button.clicked.connect(self.update_example_files)

        self.examples = ScriptEditorExamples(tagger=self.tagger)
        # Script editor dialog object will not be created until it is specifically requested, in order to ensure proper window modality.
        self.script_editor_dialog = None

        self.ui.example_selection_note.setText(self.examples.get_notes_text())
        self.ui.example_filename_sample_files_button.setToolTip(self.examples.get_tooltip_text())

        # Sync example lists vertical scrolling and selection colors
        synchronize_vertical_scrollbars((self.ui.example_filename_before, self.ui.example_filename_after))

        self.current_row = -1

    def update_selector_from_editor(self):
        """Update the script selector combo box from the script editor page.
        """
        self.naming_scripts = self.script_editor_dialog.naming_scripts
        self.selected_naming_script_id = self.script_editor_dialog.selected_script_id
        populate_script_selection_combo_box(self.naming_scripts, self.selected_naming_script_id, self.ui.naming_script_selector)
        self.display_examples()

    def update_selector_from_settings(self):
        """Update the script selector combo box from the settings.
        """
        populate_script_selection_combo_box(self.naming_scripts, self.selected_naming_script_id, self.ui.naming_script_selector)
        self.update_selector_in_editor()

    def update_selector_in_editor(self):
        """Update the selection in the script editor page to match local selection.
        """
        idx = self.ui.naming_script_selector.currentIndex()
        if self.script_editor_dialog:
            self.script_editor_dialog.set_selected_script_index(idx)
        else:
            script_item = self.ui.naming_script_selector.itemData(idx)
            self.script_text = script_item["script"]
            self.selected_naming_script_id = script_item["id"]
            self.examples.update_examples(script_text=self.script_text)
            self.update_examples_from_local()

    def match_after_to_before(self):
        """Sets the selected item in the 'after' list to the corresponding item in the 'before' list.
        """
        self.examples.synchronize_selected_example_lines(self.current_row, self.ui.example_filename_before, self.ui.example_filename_after)

    def match_before_to_after(self):
        """Sets the selected item in the 'before' list to the corresponding item in the 'after' list.
        """
        self.examples.synchronize_selected_example_lines(self.current_row, self.ui.example_filename_after, self.ui.example_filename_before)

    def show_script_editing_page(self):
        self.script_editor_dialog = ScriptEditorDialog.show_instance(parent=self, examples=self.examples)

        self.script_editor_dialog.signal_save.connect(self.save_from_editor)
        self.script_editor_dialog.signal_update.connect(self.display_examples)
        self.script_editor_dialog.signal_selection_changed.connect(self.update_selector_from_editor)
        self.script_editor_dialog.finished.connect(self.script_editor_dialog_close)

        if self.tagger.window.script_editor_dialog is not None:
            self.update_selector_from_editor()
        else:
            self.script_editor_dialog.loading = True
            self.script_editor_dialog.naming_scripts = self.naming_scripts
            self.script_editor_dialog.populate_script_selector()
            self.update_selector_in_editor()
            self.script_editor_dialog.loading = False
            self.update_examples_from_local()
            self.tagger.window.script_editor_dialog = True

    def script_editor_dialog_close(self):
        self.tagger.window.script_editor_dialog = None

    def show_scripting_documentation(self):
        ScriptingDocumentationDialog.show_instance(parent=self)

    def toggle_file_moving(self, state):
        self.toggle_file_naming_format()
        self.ui.delete_empty_dirs.setEnabled(state)
        self.ui.move_files_to.setEnabled(state)
        self.ui.move_files_to_browse.setEnabled(state)
        self.ui.move_additional_files.setEnabled(state)
        self.ui.move_additional_files_pattern.setEnabled(state)

    def toggle_file_renaming(self, state):
        self.toggle_file_naming_format()

    def toggle_file_naming_format(self):
        active = self.ui.move_files.isChecked() or self.ui.rename_files.isChecked()
        self.ui.open_script_editor.setEnabled(active)
        self.ui.ascii_filenames.setEnabled(active)
        if not IS_WIN:
            self.ui.windows_compatibility.setEnabled(active)

    def save_from_editor(self):
        self.script_text = self.script_editor_dialog.get_script()
        self.update_selector_from_editor()

    def check_formats(self):
        self.test()
        self.update_examples_from_local()

    def update_example_files(self):
        self.examples.update_sample_example_files()
        self.update_displayed_examples()

    def update_examples_from_local(self):
        override = {
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'move_files_to': os.path.normpath(self.ui.move_files_to.text()),
            'rename_files': self.ui.rename_files.isChecked(),
            'windows_compatibility': self.ui.windows_compatibility.isChecked(),
        }
        self.examples.update_examples(override=override)
        self.update_displayed_examples()

    def update_displayed_examples(self):
        if self.script_editor_dialog is not None:
            # Update examples in script editor which will trigger update locally
            self.script_editor_dialog.display_examples()
        else:
            self.display_examples()

    def display_examples(self):
        self.current_row = -1
        self.examples.update_example_listboxes(self.ui.example_filename_before, self.ui.example_filename_after)

    def load(self):
        config = get_config()
        if IS_WIN:
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(config.setting["delete_empty_dirs"])
        self.naming_scripts = config.setting["file_renaming_scripts"]
        self.selected_naming_script_id = config.setting["selected_file_naming_script_id"]
        if self.script_editor_dialog:
            self.script_editor_dialog.load()
        else:
            self.update_selector_from_settings()
        self.update_examples_from_local()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not self.ui.move_files_to.text().strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(self.script_text)
        except Exception as e:
            raise ScriptCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not self.script_text.strip():
                raise ScriptCheckError("", _("The file naming format must not be empty."))

    def save(self):
        config = get_config()
        config.setting["windows_compatibility"] = self.ui.windows_compatibility.isChecked()
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(self.ui.move_files_to.text())
        config.setting["move_additional_files"] = self.ui.move_additional_files.isChecked()
        config.setting["move_additional_files_pattern"] = self.ui.move_additional_files_pattern.text()
        config.setting["delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        config.setting["selected_file_naming_script_id"] = self.selected_naming_script_id
        self.tagger.window.enable_renaming_action.setChecked(config.setting["rename_files"])
        self.tagger.window.enable_moving_action.setChecked(config.setting["move_files"])
        self.tagger.window.make_script_selector_menu()

    def display_error(self, error):
        # Ignore scripting errors, those are handled inline
        if not isinstance(error, ScriptCheckError):
            super().display_error(error)

    def move_files_to_browse(self):
        path = QtWidgets.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(path)
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except ScriptCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #13
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatibility", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption(
            "setting",
            "file_naming_format",
            _DEFAULT_FILE_NAMING_FORMAT,
        ),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", ""),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern",
                          "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(
            self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        textEdit = self.ui.file_naming_format
        self.textEditPaletteNormal = textEdit.palette()
        self.textEditPaletteReadOnly = QPalette(self.textEditPaletteNormal)
        disabled_color = self.textEditPaletteNormal.color(
            QPalette.Inactive, QPalette.Window)
        self.textEditPaletteReadOnly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)

    def toggle_file_moving(self, state):
        self.ui.delete_empty_dirs.setEnabled(state)
        self.ui.move_files_to.setEnabled(state)
        self.ui.move_files_to_browse.setEnabled(state)
        self.ui.move_additional_files.setEnabled(state)
        self.ui.move_additional_files_pattern.setEnabled(state)

    def toggle_file_renaming(self, state):

        self.ui.file_naming_format.setEnabled(state)
        self.ui.file_naming_format_default.setEnabled(state)
        self.ui.ascii_filenames.setEnabled(state)
        self.ui.file_naming_format_group.setEnabled(state)
        if not sys.platform == "win32":
            self.ui.windows_compatibility.setEnabled(state)

        if self.ui.file_naming_format.isEnabled():
            self.ui.file_naming_format.setPalette(self.textEditPaletteNormal)
        else:
            self.ui.file_naming_format.setPalette(self.textEditPaletteReadOnly)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatibility':
            self.ui.windows_compatibility.isChecked(),
            'ascii_filenames':
            self.ui.ascii_filenames.isChecked(),
            'rename_files':
            self.ui.rename_files.isChecked(),
            'move_files':
            self.ui.move_files.isChecked(),
            'use_va_format':
            False,  # TODO remove
            'file_naming_format':
            unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to':
            os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if config.setting["enable_tagger_script"]:
                script = config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata,
                                           settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError:
            return ""
        except ScriptError:
            return ""
        except TypeError:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(
                config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(
            config.setting["file_naming_format"])
        args = {
            "picard-doc-scripting-url": PICARD_URLS['doc_scripting'],
        }
        text = _(u'<a href="%(picard-doc-scripting-url)s">Open Scripting'
                 ' Documentation in your browser</a>') % args
        self.ui.file_naming_format_documentation.setText(text)
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(
            config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(
            config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(
            config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(
                self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(
                _("Error"),
                _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception as e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError(
                    "", _("The file naming format must not be empty."))

    def save(self):
        config.setting[
            "windows_compatibility"] = self.ui.windows_compatibility.isChecked(
            )
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = unicode(
            self.ui.file_naming_format.toPlainText())
        self.tagger.window.enable_renaming_action.setChecked(
            config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(
            unicode(self.ui.move_files_to.text()))
        config.setting[
            "move_additional_files"] = self.ui.move_additional_files.isChecked(
            )
        config.setting["move_additional_files_pattern"] = unicode(
            self.ui.move_additional_files_pattern.text())
        config.setting[
            "delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(
            config.setting["move_files"])

    def display_error(self, error):
        pass

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)


#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['date'] = '1965-08-06'
        file.metadata['releasetype'] = ['album', 'soundtrack']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['soundtrack']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = '2c053984-4645-4699-9474-d2c35c227028'
        file.metadata[
            'musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_recordingid'] = 'ed052ae1-c950-47f2-8d2b-46e1b58ab76c'
        file.metadata[
            'musicbrainz_releasetrackid'] = '7668a62a-2fac-3151-a744-5707ac8c883c'
        return file

    def example_2(self):
        file = File("track05.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = u"Coup d'État, Volume 1: Ku De Ta / Prologue"
        file.metadata['title'] = u"I've Got to Learn the Mambo"
        file.metadata['artist'] = u"Snowboy feat. James Hunter"
        file.metadata['artistsort'] = u"Snowboy feat. Hunter, James"
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '13'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['discsubtitle'] = u"Beat Up"
        file.metadata['date'] = u'2005-07-04'
        file.metadata['releasetype'] = [u'album', u'compilation']
        file.metadata['~primaryreleasetype'] = u'album'
        file.metadata['~secondaryreleasetype'] = u'compilation'
        file.metadata['releasestatus'] = u'official'
        file.metadata['releasecountry'] = u'AU'
        file.metadata['compilation'] = '1'
        file.metadata['~multiartist'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = u'4b50c71e-0a07-46ac-82e4-cb85dc0c9bdd'
        file.metadata[
            'musicbrainz_recordingid'] = u'b3c487cb-0e55-477d-8df3-01ec6590f099'
        file.metadata[
            'musicbrainz_releasetrackid'] = u'f8649a05-da39-39ba-957c-7abf8f9012be'
        file.metadata[
            'musicbrainz_albumartistid'] = u'89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata['musicbrainz_artistid'] = [
            u'7b593455-d207-482c-8c6f-19ce22c94679',
            u'9e082466-2390-40d1-891e-4803531f43fd'
        ]
        return file

    def move_files_to_browse(self):
        path = QtGui.QFileDialog.getExistingDirectory(
            self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(unicode(path))
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except OptionsCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #14
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        BoolOption("setting", "windows_compatible_filenames", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption(
            "setting", "file_naming_format",
            "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2)$if(%compilation%, %artist% -,) %title%"
        ),
        BoolOption("setting", "move_files", False),
        TextOption("setting", "move_files_to", ""),
        BoolOption("setting", "move_additional_files", False),
        TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.windows_compatible_filenames,
                     QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.move_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.move_files_to, QtCore.SIGNAL("editingFinished()"),
                     self.update_examples)

        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.ascii_filenames.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.file_naming_format.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.file_naming_format_default.setEnabled)

        if not sys.platform == "win32":
            self.connect(self.ui.rename_files,
                         QtCore.SIGNAL("stateChanged(int)"),
                         self.ui.windows_compatible_filenames.setEnabled)

        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.delete_empty_dirs.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_files_to.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_files_to_browse.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_additional_files.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                     self.ui.move_additional_files_pattern.setEnabled)
        self.connect(self.ui.file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.connect(self.ui.move_files_to_browse, QtCore.SIGNAL("clicked()"),
                     self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames':
            self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames':
            self.ui.ascii_filenames.isChecked(),
            'rename_files':
            self.ui.rename_files.isChecked(),
            'move_files':
            self.ui.move_files.isChecked(),
            'use_va_format':
            False,  # TODO remove
            'file_naming_format':
            unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to':
            os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if self.config.setting["enable_tagger_script"]:
                script = self.config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata,
                                           settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError, e:
            return ""
        except TypeError, e:
            return ""
Example #15
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatibility", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption(
            "setting",
            "file_naming_format",
            _DEFAULT_FILE_NAMING_FORMAT,
        ),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", ""),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(
                partial(
                    enabledSlot,
                    self.ui.windows_compatibility.setEnabled
                )
            )

        self.ui.move_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.delete_empty_dirs.setEnabled
            )
        )
        self.ui.move_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.move_files_to.setEnabled
            )
        )
        self.ui.move_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.move_files_to_browse.setEnabled
            )
        )
        self.ui.move_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.move_additional_files.setEnabled
            )
        )
        self.ui.move_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.move_additional_files_pattern.setEnabled
            )
        )
        self.ui.rename_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.ascii_filenames.setEnabled
            )
        )
        self.ui.rename_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.file_naming_format.setEnabled
            )
        )
        self.ui.rename_files.stateChanged.connect(
            partial(
                enabledSlot,
                self.ui.file_naming_format_default.setEnabled
            )
        )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatibility': self.ui.windows_compatibility.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'use_va_format': False,  # TODO remove
            'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if config.setting["enable_tagger_script"]:
                script = config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError:
            return ""
        except ScriptError:
            return ""
        except TypeError:
            return ""
        except UnknownFunction:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(config.setting["file_naming_format"])
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception as e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))

    def save(self):
        config.setting["windows_compatibility"] = self.ui.windows_compatibility.isChecked()
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = unicode(self.ui.file_naming_format.toPlainText())
        self.tagger.window.enable_renaming_action.setChecked(config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(unicode(self.ui.move_files_to.text()))
        config.setting["move_additional_files"] = self.ui.move_additional_files.isChecked()
        config.setting["move_additional_files_pattern"] = unicode(self.ui.move_additional_files_pattern.text())
        config.setting["delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(config.setting["move_files"])

    def display_error(self, error):
        pass

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)
#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['date'] = '1965-08-06'
        file.metadata['releasetype'] = ['album', 'soundtrack']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['soundtrack']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = '2c053984-4645-4699-9474-d2c35c227028'
        file.metadata['musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_recordingid'] = 'ed052ae1-c950-47f2-8d2b-46e1b58ab76c'
        file.metadata['musicbrainz_releasetrackid'] = '7668a62a-2fac-3151-a744-5707ac8c883c'
        return file

    def example_2(self):
        file = File("track05.mp3")
        file.state = File.NORMAL
        # The data for this example does not match the release on MusicBrainz,
        # but still works well enough as an example.
        file.metadata['album'] = 'Explosive Doowops, Volume 4'
        file.metadata['title'] = 'Why? Oh Why?'
        file.metadata['artist'] = 'The Fantasys'
        file.metadata['artistsort'] = 'Fantasys, The'
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '26'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['date'] = '1999-02-03'
        file.metadata['releasetype'] = ['album', 'compilation']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['compilation']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['compilation'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = 'bcc97e8a-2055-400b-a6ed-83288285c6fc'
        file.metadata['musicbrainz_albumartistid'] = '89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata['musicbrainz_artistid'] = '06704773-aafe-4aca-8833-b449e0a6467f'
        file.metadata['musicbrainz_recordingid'] = 'd92837ee-b1e4-4649-935f-e433c3e5e429'
        file.metadata['musicbrainz_releasetrackid'] = 'eac99807-93d4-3668-9714-fa0c1b487ccf'
        return file

    def move_files_to_browse(self):
        path = QtGui.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(unicode(path))
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except OptionsCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #16
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatibility", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption(
            "setting",
            "file_naming_format",
            _DEFAULT_FILE_NAMING_FORMAT,
        ),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", _default_music_dir),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        self.ui.move_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_moving
            )
        )
        self.ui.rename_files.toggled.connect(
            partial(
                enabledSlot,
                self.toggle_file_renaming
            )
        )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        textEdit = self.ui.file_naming_format
        self.textEditPaletteNormal = textEdit.palette()
        self.textEditPaletteReadOnly = QPalette(self.textEditPaletteNormal)
        disabled_color = self.textEditPaletteNormal.color(QPalette.Inactive, QPalette.Window)
        self.textEditPaletteReadOnly.setColor(QPalette.Disabled, QPalette.Base, disabled_color)

    def toggle_file_moving(self, state):
        self.ui.delete_empty_dirs.setEnabled(state)
        self.ui.move_files_to.setEnabled(state)
        self.ui.move_files_to_browse.setEnabled(state)
        self.ui.move_additional_files.setEnabled(state)
        self.ui.move_additional_files_pattern.setEnabled(state)

    def toggle_file_renaming(self, state):

        self.ui.file_naming_format.setEnabled(state)
        self.ui.file_naming_format_default.setEnabled(state)
        self.ui.ascii_filenames.setEnabled(state)
        self.ui.file_naming_format_group.setEnabled(state)
        if not sys.platform == "win32":
            self.ui.windows_compatibility.setEnabled(state)

        if self.ui.file_naming_format.isEnabled():
            self.ui.file_naming_format.setPalette(self.textEditPaletteNormal)
        else:
            self.ui.file_naming_format.setPalette(self.textEditPaletteReadOnly)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatibility': self.ui.windows_compatibility.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'use_va_format': False,  # TODO remove
            'file_naming_format': self.ui.file_naming_format.toPlainText(),
            'move_files_to': os.path.normpath(self.ui.move_files_to.text())
        }
        try:
            if config.setting["enable_tagger_scripts"]:
                for s_pos, s_name, s_enabled, s_text in config.setting["list_of_scripts"]:
                    if s_enabled and s_text:
                        parser = ScriptParser()
                        parser.eval(s_text, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except ScriptError:
            return ""
        except TypeError:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(config.setting["file_naming_format"])
        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.file_naming_format_documentation.setText(text)
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not self.ui.move_files_to.text().strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(self.ui.file_naming_format.toPlainText())
        except Exception as e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not self.ui.file_naming_format.toPlainText().strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))

    def save(self):
        config.setting["windows_compatibility"] = self.ui.windows_compatibility.isChecked()
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = self.ui.file_naming_format.toPlainText()
        self.tagger.window.enable_renaming_action.setChecked(config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(self.ui.move_files_to.text())
        config.setting["move_additional_files"] = self.ui.move_additional_files.isChecked()
        config.setting["move_additional_files_pattern"] = self.ui.move_additional_files_pattern.text()
        config.setting["delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(config.setting["move_files"])

    def display_error(self, error):
        pass

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)
#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['date'] = '1965-08-06'
        file.metadata['releasetype'] = ['album', 'soundtrack']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['soundtrack']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = '2c053984-4645-4699-9474-d2c35c227028'
        file.metadata['musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_recordingid'] = 'ed052ae1-c950-47f2-8d2b-46e1b58ab76c'
        file.metadata['musicbrainz_releasetrackid'] = '7668a62a-2fac-3151-a744-5707ac8c883c'
        return file

    def example_2(self):
        file = File("track05.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = "Coup d'État, Volume 1: Ku De Ta / Prologue"
        file.metadata['title'] = "I've Got to Learn the Mambo"
        file.metadata['artist'] = "Snowboy feat. James Hunter"
        file.metadata['artistsort'] = "Snowboy feat. Hunter, James"
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '13'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['discsubtitle'] = "Beat Up"
        file.metadata['date'] = '2005-07-04'
        file.metadata['releasetype'] = ['album', 'compilation']
        file.metadata['~primaryreleasetype'] = 'album'
        file.metadata['~secondaryreleasetype'] = 'compilation'
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'AU'
        file.metadata['compilation'] = '1'
        file.metadata['~multiartist'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = '4b50c71e-0a07-46ac-82e4-cb85dc0c9bdd'
        file.metadata['musicbrainz_recordingid'] = 'b3c487cb-0e55-477d-8df3-01ec6590f099'
        file.metadata['musicbrainz_releasetrackid'] = 'f8649a05-da39-39ba-957c-7abf8f9012be'
        file.metadata['musicbrainz_albumartistid'] = '89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata['musicbrainz_artistid'] = ['7b593455-d207-482c-8c6f-19ce22c94679',
                                                 '9e082466-2390-40d1-891e-4803531f43fd']
        return file

    def move_files_to_browse(self):
        path = QtWidgets.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(path)
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except OptionsCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #17
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True
    HELP_URL = '/config/options_filerenaming.html'

    options = [
        BoolOption("setting", "windows_compatibility", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption(
            "setting",
            "file_naming_format",
            DEFAULT_FILE_NAMING_FORMAT,
        ),
        BoolOption("setting", "move_files", False),
        TextOption("setting", "move_files_to", _default_music_dir),
        BoolOption("setting", "move_additional_files", False),
        TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super().__init__(parent)
        self.script_text = ""
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(
            self.update_examples_from_local)
        self.ui.windows_compatibility.clicked.connect(
            self.update_examples_from_local)
        self.ui.rename_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files.clicked.connect(self.update_examples_from_local)
        self.ui.move_files_to.editingFinished.connect(
            self.update_examples_from_local)

        self.ui.move_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_moving))
        self.ui.rename_files.toggled.connect(
            partial(enabledSlot, self.toggle_file_renaming))
        self.ui.open_script_editor.clicked.connect(
            self.show_script_editing_page)
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

        self.ui.example_filename_after.itemSelectionChanged.connect(
            self.match_before_to_after)
        self.ui.example_filename_before.itemSelectionChanged.connect(
            self.match_after_to_before)

        script_edit = self.ui.move_additional_files_pattern
        self.script_palette_normal = script_edit.palette()
        self.script_palette_readonly = QPalette(self.script_palette_normal)
        disabled_color = self.script_palette_normal.color(
            QPalette.Inactive, QPalette.Window)
        self.script_palette_readonly.setColor(QPalette.Disabled, QPalette.Base,
                                              disabled_color)

        self.ui.example_filename_sample_files_button.clicked.connect(
            self.update_example_files)

        self.examples = ScriptEditorExamples(tagger=self.tagger)

        self.ui.example_selection_note.setText(self.examples.notes_text)
        self.ui.example_filename_sample_files_button.setToolTip(
            self.examples.tooltip_text)

        self.script_editor_page = ScriptEditorPage(parent=self,
                                                   examples=self.examples)
        self.script_editor_page.signal_save.connect(self.save_from_editor)
        self.script_editor_page.signal_update.connect(self.update_from_editor)

        # Sync example lists vertical scrolling and selection colors
        self.script_editor_page.synchronize_vertical_scrollbars(
            (self.ui.example_filename_before, self.ui.example_filename_after))

        self.current_row = -1

    def match_after_to_before(self):
        """Sets the selected item in the 'after' list to the corresponding item in the 'before' list.
        """
        self.script_editor_page.synchronize_selected_example_lines(
            self.current_row, self.ui.example_filename_before,
            self.ui.example_filename_after)

    def match_before_to_after(self):
        """Sets the selected item in the 'before' list to the corresponding item in the 'after' list.
        """
        self.script_editor_page.synchronize_selected_example_lines(
            self.current_row, self.ui.example_filename_after,
            self.ui.example_filename_before)

    def show_script_editing_page(self):
        self.script_editor_page.show()
        self.script_editor_page.raise_()
        self.script_editor_page.activateWindow()
        self.update_examples_from_local()

    def show_scripting_documentation(self):
        ScriptingDocumentationDialog.show_instance(parent=self)

    def toggle_file_moving(self, state):
        self.toggle_file_naming_format()
        self.ui.delete_empty_dirs.setEnabled(state)
        self.ui.move_files_to.setEnabled(state)
        self.ui.move_files_to_browse.setEnabled(state)
        self.ui.move_additional_files.setEnabled(state)
        self.ui.move_additional_files_pattern.setEnabled(state)

    def toggle_file_renaming(self, state):
        self.toggle_file_naming_format()

    def toggle_file_naming_format(self):
        active = self.ui.move_files.isChecked(
        ) or self.ui.rename_files.isChecked()
        self.ui.open_script_editor.setEnabled(active)
        self.ui.ascii_filenames.setEnabled(active)
        if not IS_WIN:
            self.ui.windows_compatibility.setEnabled(active)

    def save_from_editor(self):
        self.script_text = self.script_editor_page.get_script()

    def update_from_editor(self):
        self.display_examples()

    def check_formats(self):
        self.test()
        self.update_examples_from_local()

    def update_example_files(self):
        self.examples.update_sample_example_files()
        self.script_editor_page.display_examples()

    def update_examples_from_local(self):
        override = {
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'move_files_to': os.path.normpath(self.ui.move_files_to.text()),
            'rename_files': self.ui.rename_files.isChecked(),
            'windows_compatibility': self.ui.windows_compatibility.isChecked(),
        }
        self.examples.update_examples(override=override)
        self.script_editor_page.display_examples()

    def display_examples(self):
        self.current_row = -1
        examples = self.examples.get_examples()
        self.script_editor_page.update_example_listboxes(
            self.ui.example_filename_before, self.ui.example_filename_after,
            examples)

    def load(self):
        config = get_config()
        if IS_WIN:
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(
                config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.script_text = config.setting["file_naming_format"]
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(
            config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(
            config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(
            config.setting["delete_empty_dirs"])
        self.update_examples_from_local()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked(
        ) and not self.ui.move_files_to.text().strip():
            raise OptionsCheckError(
                _("Error"),
                _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(self.script_text)
        except Exception as e:
            raise ScriptCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not self.script_text.strip():
                raise ScriptCheckError(
                    "", _("The file naming format must not be empty."))

    def save(self):
        config = get_config()
        config.setting[
            "windows_compatibility"] = self.ui.windows_compatibility.isChecked(
            )
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = self.script_text.strip()
        self.tagger.window.enable_renaming_action.setChecked(
            config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(
            self.ui.move_files_to.text())
        config.setting[
            "move_additional_files"] = self.ui.move_additional_files.isChecked(
            )
        config.setting[
            "move_additional_files_pattern"] = self.ui.move_additional_files_pattern.text(
            )
        config.setting[
            "delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(
            config.setting["move_files"])

    def display_error(self, error):
        # Ignore scripting errors, those are handled inline
        if not isinstance(error, ScriptCheckError):
            super().display_error(error)

    def move_files_to_browse(self):
        path = QtWidgets.QFileDialog.getExistingDirectory(
            self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(path)
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except ScriptCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #18
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatible_filenames", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption("setting", "file_naming_format", "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2)$if(%compilation%, %artist% -,) %title%"),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", ""),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatible_filenames.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        # The following code is there to fix
        # http://tickets.musicbrainz.org/browse/PICARD-417
        # In some older version of PyQt/sip it's impossible to connect a signal
        # emitting an `int` to a slot expecting a `bool`.
        # By using `enabledSlot` instead we can force python to do the
        # conversion from int (`state`) to bool.
        def enabledSlot(func, state):
            """Calls `func` with `state`."""
            func(state)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(partial(
                                                       enabledSlot,
                                                       self.ui.windows_compatible_filenames.setEnabled)
                                                      )

        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.delete_empty_dirs.setEnabled)
                                               )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to_browse.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files_pattern.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.ascii_filenames.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format_default.setEnabled)
                                                )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'use_va_format': False,  # TODO remove
            'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if config.setting["enable_tagger_script"]:
                script = config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError:
            return ""
        except TypeError:
            return ""
        except UnknownFunction:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatible_filenames.setChecked(True)
            self.ui.windows_compatible_filenames.setEnabled(False)
        else:
            self.ui.windows_compatible_filenames.setChecked(config.setting["windows_compatible_filenames"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(config.setting["file_naming_format"])
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception as e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))

    def save(self):
        config.setting["windows_compatible_filenames"] = self.ui.windows_compatible_filenames.isChecked()
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = unicode(self.ui.file_naming_format.toPlainText())
        self.tagger.window.enable_renaming_action.setChecked(config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(unicode(self.ui.move_files_to.text()))
        config.setting["move_additional_files"] = self.ui.move_additional_files.isChecked()
        config.setting["move_additional_files_pattern"] = unicode(self.ui.move_additional_files_pattern.text())
        config.setting["delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(config.setting["move_files"])

    def display_error(self, error):
        pass

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)
#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['date'] = '1965-08-06'
        file.metadata['releasetype'] = 'album'
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = '2c053984-4645-4699-9474-d2c35c227028'
        file.metadata['musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata['musicbrainz_trackid'] = '898a2916-f64d-48d3-ab1a-3446fb450448'
        return file

    def example_2(self):
        file = File("track05.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Explosive Doowops, Volume 4'
        file.metadata['title'] = 'Why? Oh Why?'
        file.metadata['artist'] = 'The Fantasys'
        file.metadata['artistsort'] = 'Fantasys, The'
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '26'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['date'] = '1999-02-03'
        file.metadata['releasetype'] = 'compilation'
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['compilation'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata['musicbrainz_albumid'] = 'bcc97e8a-2055-400b-a6ed-83288285c6fc'
        file.metadata['musicbrainz_albumartistid'] = '89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata['musicbrainz_artistid'] = '06704773-aafe-4aca-8833-b449e0a6467f'
        file.metadata['musicbrainz_trackid'] = 'd92837ee-b1e4-4649-935f-e433c3e5e429'
        return file

    STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }"

    def move_files_to_browse(self):
        path = QtGui.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(unicode(path))
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except OptionsCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #19
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        BoolOption("setting", "windows_compatible_filenames", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption("setting", "file_naming_format", "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2)$if(%compilation%, %artist% -,) %title%"),
        BoolOption("setting", "move_files", False),
        TextOption("setting", "move_files_to", ""),
        BoolOption("setting", "move_additional_files", False),
        TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.windows_compatible_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.move_files, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.move_files_to, QtCore.SIGNAL("editingFinished()"), self.update_examples)

        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.ascii_filenames.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.file_naming_format.setEnabled)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.file_naming_format_default.setEnabled)

        if not sys.platform == "win32":
            self.connect(self.ui.rename_files, QtCore.SIGNAL("stateChanged(int)"),
                    self.ui.windows_compatible_filenames.setEnabled)

        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.delete_empty_dirs.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.move_files_to.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.move_files_to_browse.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.move_additional_files.setEnabled)
        self.connect(self.ui.move_files, QtCore.SIGNAL("stateChanged(int)"),
                self.ui.move_additional_files_pattern.setEnabled)
        self.connect(self.ui.file_naming_format, QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.connect(self.ui.move_files_to_browse, QtCore.SIGNAL("clicked()"), self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'use_va_format': False, # TODO remove
            'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if self.config.setting["enable_tagger_script"]:
                script = self.config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError, e: return ""
        except TypeError, e: return ""
        except UnknownFunction, e: return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatible_filenames.setChecked(True)
            self.ui.windows_compatible_filenames.setEnabled(False)
        else:
            self.ui.windows_compatible_filenames.setChecked(self.config.setting["windows_compatible_filenames"])
        self.ui.rename_files.setChecked(self.config.setting["rename_files"])
        self.ui.move_files.setChecked(self.config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(self.config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(self.config.setting["file_naming_format"])
        self.ui.move_files_to.setText(self.config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(self.config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(self.config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(self.config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception, e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))
Example #20
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        BoolOption("setting", "windows_compatible_filenames", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption(
            "setting", "file_naming_format",
            "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2) %title%"
        ),
        TextOption(
            "setting", "va_file_naming_format",
            "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - %title%"
        ),
        BoolOption("setting", "use_va_format", False)
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)
        self.update_examples()

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.windows_compatible_filenames,
                     QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.use_va_format, QtCore.SIGNAL("clicked()"),
                     self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"),
                     self.update_examples)

        self.connect(self.ui.file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.va_file_naming_format,
                     QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_file_naming_format_default)
        self.connect(self.ui.va_file_naming_format_default,
                     QtCore.SIGNAL("clicked()"),
                     self.set_va_file_naming_format_default)
        self.connect(self.ui.va_copy_from_above, QtCore.SIGNAL("clicked()"),
                     self.copy_format_to_va)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.highlighter_va = TaggerScriptSyntaxHighlighter(
            self.ui.va_file_naming_format.document())

    def check_formats(self):
        self.test()
        self.va_test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames':
            self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames':
            self.ui.ascii_filenames.isChecked(),
            'rename_files':
            self.ui.rename_files.isChecked(),
            'move_files':
            self.config.setting["move_files"],
            'use_va_format':
            self.ui.use_va_format.isChecked(),
            'file_naming_format':
            unicode(self.ui.file_naming_format.toPlainText()),
            'va_file_naming_format':
            unicode(self.ui.va_file_naming_format.toPlainText()),
            'move_files_to':
            os.path.normpath(unicode(self.config.setting["move_files_to"])),
        }
        try:
            if self.config.setting["enable_tagger_script"]:
                script = self.config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata,
                                           settings)
            return filename
        except SyntaxError, e:
            return ""
        except TypeError, e:
            return ""
Example #21
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File Naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatibility", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption(
            "setting",
            "file_naming_format",
            _DEFAULT_FILE_NAMING_FORMAT,
        ),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", ""),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern",
                          "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatibility.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(
                partial(enabledSlot, self.ui.windows_compatibility.setEnabled))

        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.delete_empty_dirs.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_files_to.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_files_to_browse.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot, self.ui.move_additional_files.setEnabled))
        self.ui.move_files.stateChanged.connect(
            partial(enabledSlot,
                    self.ui.move_additional_files_pattern.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot, self.ui.ascii_filenames.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot, self.ui.file_naming_format.setEnabled))
        self.ui.rename_files.stateChanged.connect(
            partial(enabledSlot,
                    self.ui.file_naming_format_default.setEnabled))
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(
            self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(
            self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatibility':
            self.ui.windows_compatibility.isChecked(),
            'ascii_filenames':
            self.ui.ascii_filenames.isChecked(),
            'rename_files':
            self.ui.rename_files.isChecked(),
            'move_files':
            self.ui.move_files.isChecked(),
            'use_va_format':
            False,  # TODO remove
            'file_naming_format':
            unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to':
            os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if config.setting["enable_tagger_script"]:
                script = config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata,
                                           settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError:
            return ""
        except TypeError:
            return ""
        except UnknownFunction:
            return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatibility.setChecked(True)
            self.ui.windows_compatibility.setEnabled(False)
        else:
            self.ui.windows_compatibility.setChecked(
                config.setting["windows_compatibility"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(
            config.setting["file_naming_format"])
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(
            config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(
            config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(
            config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(
                self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(
                _("Error"),
                _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception as e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError(
                    "", _("The file naming format must not be empty."))

    def save(self):
        config.setting[
            "windows_compatibility"] = self.ui.windows_compatibility.isChecked(
            )
        config.setting["ascii_filenames"] = self.ui.ascii_filenames.isChecked()
        config.setting["rename_files"] = self.ui.rename_files.isChecked()
        config.setting["file_naming_format"] = unicode(
            self.ui.file_naming_format.toPlainText())
        self.tagger.window.enable_renaming_action.setChecked(
            config.setting["rename_files"])
        config.setting["move_files"] = self.ui.move_files.isChecked()
        config.setting["move_files_to"] = os.path.normpath(
            unicode(self.ui.move_files_to.text()))
        config.setting[
            "move_additional_files"] = self.ui.move_additional_files.isChecked(
            )
        config.setting["move_additional_files_pattern"] = unicode(
            self.ui.move_additional_files_pattern.text())
        config.setting[
            "delete_empty_dirs"] = self.ui.delete_empty_dirs.isChecked()
        self.tagger.window.enable_moving_action.setChecked(
            config.setting["move_files"])

    def display_error(self, error):
        pass

    def set_file_naming_format_default(self):
        self.ui.file_naming_format.setText(self.options[3].default)


#        self.ui.file_naming_format.setCursorPosition(0)

    def example_1(self):
        file = File("ticket_to_ride.mp3")
        file.state = File.NORMAL
        file.metadata['album'] = 'Help!'
        file.metadata['title'] = 'Ticket to Ride'
        file.metadata['artist'] = 'The Beatles'
        file.metadata['artistsort'] = 'Beatles, The'
        file.metadata['albumartist'] = 'The Beatles'
        file.metadata['albumartistsort'] = 'Beatles, The'
        file.metadata['tracknumber'] = '7'
        file.metadata['totaltracks'] = '14'
        file.metadata['discnumber'] = '1'
        file.metadata['totaldiscs'] = '1'
        file.metadata['date'] = '1965-08-06'
        file.metadata['releasetype'] = ['album', 'soundtrack']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['soundtrack']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = '2c053984-4645-4699-9474-d2c35c227028'
        file.metadata[
            'musicbrainz_albumartistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_artistid'] = 'b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d'
        file.metadata[
            'musicbrainz_recordingid'] = 'ed052ae1-c950-47f2-8d2b-46e1b58ab76c'
        file.metadata[
            'musicbrainz_releasetrackid'] = '7668a62a-2fac-3151-a744-5707ac8c883c'
        return file

    def example_2(self):
        file = File("track05.mp3")
        file.state = File.NORMAL
        # The data for this example does not match the release on MusicBrainz,
        # but still works well enough as an example.
        file.metadata['album'] = 'Explosive Doowops, Volume 4'
        file.metadata['title'] = 'Why? Oh Why?'
        file.metadata['artist'] = 'The Fantasys'
        file.metadata['artistsort'] = 'Fantasys, The'
        file.metadata['albumartist'] = config.setting['va_name']
        file.metadata['albumartistsort'] = config.setting['va_name']
        file.metadata['tracknumber'] = '5'
        file.metadata['totaltracks'] = '26'
        file.metadata['discnumber'] = '2'
        file.metadata['totaldiscs'] = '2'
        file.metadata['date'] = '1999-02-03'
        file.metadata['releasetype'] = ['album', 'compilation']
        file.metadata['~primaryreleasetype'] = ['album']
        file.metadata['~secondaryreleasetype'] = ['compilation']
        file.metadata['releasestatus'] = 'official'
        file.metadata['releasecountry'] = 'US'
        file.metadata['compilation'] = '1'
        file.metadata['~extension'] = 'mp3'
        file.metadata[
            'musicbrainz_albumid'] = 'bcc97e8a-2055-400b-a6ed-83288285c6fc'
        file.metadata[
            'musicbrainz_albumartistid'] = '89ad4ac3-39f7-470e-963a-56509c546377'
        file.metadata[
            'musicbrainz_artistid'] = '06704773-aafe-4aca-8833-b449e0a6467f'
        file.metadata[
            'musicbrainz_recordingid'] = 'd92837ee-b1e4-4649-935f-e433c3e5e429'
        file.metadata[
            'musicbrainz_releasetrackid'] = 'eac99807-93d4-3668-9714-fa0c1b487ccf'
        return file

    def move_files_to_browse(self):
        path = QtGui.QFileDialog.getExistingDirectory(
            self, "", self.ui.move_files_to.text())
        if path:
            path = os.path.normpath(unicode(path))
            self.ui.move_files_to.setText(path)

    def test(self):
        self.ui.renaming_error.setStyleSheet("")
        self.ui.renaming_error.setText("")
        try:
            self.check_format()
        except OptionsCheckError as e:
            self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
            self.ui.renaming_error.setText(e.info)
            return
Example #22
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        config.BoolOption("setting", "windows_compatible_filenames", True),
        config.BoolOption("setting", "ascii_filenames", False),
        config.BoolOption("setting", "rename_files", False),
        config.TextOption("setting", "file_naming_format", "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2)$if(%compilation%, %artist% -,) %title%"),
        config.BoolOption("setting", "move_files", False),
        config.TextOption("setting", "move_files_to", ""),
        config.BoolOption("setting", "move_additional_files", False),
        config.TextOption("setting", "move_additional_files_pattern", "*.jpg *.png"),
        config.BoolOption("setting", "delete_empty_dirs", True),
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatible_filenames.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        # The following code is there to fix
        # http://tickets.musicbrainz.org/browse/PICARD-417
        # In some older version of PyQt/sip it's impossible to connect a signal
        # emitting an `int` to a slot expecting a `bool`.
        # By using `enabledSlot` instead we can force python to do the
        # conversion from int (`state`) to bool.
        def enabledSlot(func, state):
            """Calls `func` with `state`."""
            func(state)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(partial(
                                                       enabledSlot,
                                                       self.ui.windows_compatible_filenames.setEnabled)
                                                      )

        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.delete_empty_dirs.setEnabled)
                                               )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to_browse.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files_pattern.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.ascii_filenames.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format_default.setEnabled)
                                                )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)

    def check_formats(self):
        self.test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.ui.move_files.isChecked(),
            'use_va_format': False, # TODO remove
            'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
            'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text()))
        }
        try:
            if config.setting["enable_tagger_script"]:
                script = config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            if not settings["move_files"]:
                return os.path.basename(filename)
            return filename
        except SyntaxError, e: return ""
        except TypeError, e: return ""
        except UnknownFunction, e: return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1)
        self.ui.example_filename_va.setText(example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatible_filenames.setChecked(True)
            self.ui.windows_compatible_filenames.setEnabled(False)
        else:
            self.ui.windows_compatible_filenames.setChecked(config.setting["windows_compatible_filenames"])
        self.ui.rename_files.setChecked(config.setting["rename_files"])
        self.ui.move_files.setChecked(config.setting["move_files"])
        self.ui.ascii_filenames.setChecked(config.setting["ascii_filenames"])
        self.ui.file_naming_format.setPlainText(config.setting["file_naming_format"])
        self.ui.move_files_to.setText(config.setting["move_files_to"])
        self.ui.move_files_to.setCursorPosition(0)
        self.ui.move_additional_files.setChecked(config.setting["move_additional_files"])
        self.ui.move_additional_files_pattern.setText(config.setting["move_additional_files_pattern"])
        self.ui.delete_empty_dirs.setChecked(config.setting["delete_empty_dirs"])
        self.update_examples()

    def check(self):
        self.check_format()
        if self.ui.move_files.isChecked() and not unicode(self.ui.move_files_to.text()).strip():
            raise OptionsCheckError(_("Error"), _("The location to move files to must not be empty."))

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception, e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))
Example #23
0
class RenamingOptionsPage(OptionsPage):

    NAME = "filerenaming"
    TITLE = N_("File naming")
    PARENT = None
    SORT_ORDER = 40
    ACTIVE = True

    options = [
        BoolOption("setting", "windows_compatible_filenames", True),
        BoolOption("setting", "ascii_filenames", False),
        BoolOption("setting", "rename_files", False),
        TextOption("setting", "file_naming_format", "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2) %title%"),
        TextOption("setting", "va_file_naming_format", "$if2(%albumartist%,%artist%)/%album%/$if($gt(%totaldiscs%,1),%discnumber%-,)$num(%tracknumber%,2) %artist% - %title%"),
        BoolOption("setting", "use_va_format", False)
    ]

    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)
        self.update_examples()

        self.connect(self.ui.ascii_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.windows_compatible_filenames, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.use_va_format, QtCore.SIGNAL("clicked()"), self.update_examples)
        self.connect(self.ui.rename_files, QtCore.SIGNAL("clicked()"), self.update_examples)

        self.connect(self.ui.file_naming_format, QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.va_file_naming_format, QtCore.SIGNAL("textChanged()"), self.check_formats)
        self.connect(self.ui.file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_file_naming_format_default)
        self.connect(self.ui.va_file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_va_file_naming_format_default)
        self.connect(self.ui.va_copy_from_above, QtCore.SIGNAL("clicked()"), self.copy_format_to_va)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.highlighter_va = TaggerScriptSyntaxHighlighter(self.ui.va_file_naming_format.document())

    def check_formats(self):
        self.test()
        self.va_test()
        self.update_examples()

    def _example_to_filename(self, file):
        settings = {
            'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(),
            'ascii_filenames': self.ui.ascii_filenames.isChecked(),
            'rename_files': self.ui.rename_files.isChecked(),
            'move_files': self.config.setting["move_files"],
            'use_va_format': self.ui.use_va_format.isChecked(),
            'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
            'va_file_naming_format': unicode(self.ui.va_file_naming_format.toPlainText()),
            'move_files_to': os.path.normpath(unicode(self.config.setting["move_files_to"])),
        }
        try:
            if self.config.setting["enable_tagger_script"]:
                script = self.config.setting["tagger_script"]
                parser = ScriptParser()
                parser.eval(script, file.metadata)
            filename = file._make_filename(file.filename, file.metadata, settings)
            return filename
        except SyntaxError, e: return ""
        except TypeError, e: return ""
        except UnknownFunction, e: return ""

    def update_examples(self):
        # TODO: Here should be more examples etc.
        # TODO: Would be nice to show diffs too....
        example1 = self._example_to_filename(self.example_1())
        example2 = self._example_to_filename(self.example_2())
        self.ui.example_filename.setText(example1 + "<br/>" + example2)

    def load(self):
        if sys.platform == "win32":
            self.ui.windows_compatible_filenames.setChecked(True)
            self.ui.windows_compatible_filenames.setEnabled(False)
        else:
            self.ui.windows_compatible_filenames.setChecked(self.config.setting["windows_compatible_filenames"])
        self.ui.use_va_format.setChecked(self.config.setting["use_va_format"])
        self.ui.ascii_filenames.setChecked(self.config.setting["ascii_filenames"])
        self.ui.rename_files.setChecked(self.config.setting["rename_files"])
        self.ui.file_naming_format.setPlainText(self.config.setting["file_naming_format"])
        self.ui.va_file_naming_format.setPlainText(self.config.setting["va_file_naming_format"])

    def check(self):
        self.check_format()
        self.check_va_format()

    def check_format(self):
        parser = ScriptParser()
        try:
            parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
        except Exception, e:
            raise OptionsCheckError("", str(e))
        if self.ui.rename_files.isChecked():
            if not unicode(self.ui.file_naming_format.toPlainText()).strip():
                raise OptionsCheckError("", _("The file naming format must not be empty."))
Example #24
0
    def __init__(self, parent=None):
        super(RenamingOptionsPage, self).__init__(parent)
        self.ui = Ui_RenamingOptionsPage()
        self.ui.setupUi(self)

        self.ui.ascii_filenames.clicked.connect(self.update_examples)
        self.ui.windows_compatible_filenames.clicked.connect(self.update_examples)
        self.ui.rename_files.clicked.connect(self.update_examples)
        self.ui.move_files.clicked.connect(self.update_examples)
        self.ui.move_files_to.editingFinished.connect(self.update_examples)

        # The following code is there to fix
        # http://tickets.musicbrainz.org/browse/PICARD-417
        # In some older version of PyQt/sip it's impossible to connect a signal
        # emitting an `int` to a slot expecting a `bool`.
        # By using `enabledSlot` instead we can force python to do the
        # conversion from int (`state`) to bool.
        def enabledSlot(func, state):
            """Calls `func` with `state`."""
            func(state)

        if not sys.platform == "win32":
            self.ui.rename_files.stateChanged.connect(partial(
                                                       enabledSlot,
                                                       self.ui.windows_compatible_filenames.setEnabled)
                                                      )

        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.delete_empty_dirs.setEnabled)
                                               )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_files_to_browse.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files.setEnabled)
                                                )
        self.ui.move_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.move_additional_files_pattern.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.ascii_filenames.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format.setEnabled)
                                                )
        self.ui.rename_files.stateChanged.connect(partial(
                                                        enabledSlot,
                                                        self.ui.file_naming_format_default.setEnabled)
                                                )
        self.ui.file_naming_format.textChanged.connect(self.check_formats)
        self.ui.file_naming_format_default.clicked.connect(self.set_file_naming_format_default)
        self.highlighter = TaggerScriptSyntaxHighlighter(self.ui.file_naming_format.document())
        self.ui.move_files_to_browse.clicked.connect(self.move_files_to_browse)