# -*- coding: utf-8 -*- # # Copyright © 2018-2020 Nick Bowler # # License GPLv3+: GNU General Public License version 3 or any later version. # This is free software: you are free to change and redistribute it. # There is NO WARRANTY, to the extent permitted by law. PLUGIN_NAME = u"Tweak Filename Filter" PLUGIN_AUTHOR = u"Nick Bowler" PLUGIN_DESCRIPTION = u'''

Adds additional options to tweak file naming.

Currently, this overrides the default methods to substitute forward and back-slashes, allowing alternate behaviours.

tweak_file_replace_backslash (bool)
if true, backslashes will be replaced when renaming files. (default: true)
tweak_file_replacement_char (string)
matching characters will be replaced by this string (default: _)
''' PLUGIN_VERSION = "2" PLUGIN_API_VERSIONS = ["2.0"] PLUGIN_LICENSE = "GPL-3.0-or-later" from PyQt5 import QtWidgets from picard import (config, log) from picard.ui.options import ( OptionsPage, OptionsCheckError, register_options_page ) import picard.util import sre_constants import re def modulename(): return modulename.__module__[len("picard.plugins."):] class UI_TweakFilenameFilter(object): def setupUi(self, TweakFilenameOptionsPage): top = QtWidgets.QVBoxLayout(TweakFilenameOptionsPage) group = QtWidgets.QGroupBox(TweakFilenameOptionsPage) group.setTitle("Filename Sanitizer") top.addWidget(group) box = QtWidgets.QVBoxLayout(group) msg = QtWidgets.QLabel(group) msg.setText('''Picard normally replaces slashes and backslashes with underscores in all metadata tags prior to running the file-naming script. This substitution may be customized here. Replacement text may use backslash sequences supported by re.sub.''') msg.setOpenExternalLinks(True) box.addWidget(msg) self.replace_slashes = QtWidgets.QCheckBox(group) self.replace_slashes.setText("Replace slashes in metadata") self.replace_slashes.setChecked(True) self.replace_slashes.setEnabled(False) box.addWidget(self.replace_slashes) self.replace_backslashes = QtWidgets.QCheckBox(group) self.replace_backslashes.setText("Replace backslashes in metadata") box.addWidget(self.replace_backslashes) label = QtWidgets.QLabel() label.setText("Replacement text:") box.addWidget(label) self.sanitize_replacement = QtWidgets.QLineEdit(group) box.addWidget(self.sanitize_replacement) label.setBuddy(self.sanitize_replacement) top.addStretch() class TweakFilenameOptionsPage(OptionsPage): NAME = PLUGIN_NAME TITLE = PLUGIN_NAME PARENT = None SORT_ORDER = 99 ACTIVE = True options = [ config.TextOption("setting", "tweak_file_replacement_char", "_"), config.BoolOption("setting", "tweak_file_replace_backslash", True) ] def __init__(self, parent=None): super().__init__(parent) self.ui = UI_TweakFilenameFilter() self.ui.setupUi(self) def load(self): self.ui.replace_backslashes.setChecked( config.setting["tweak_file_replace_backslash"] ) self.ui.sanitize_replacement.setText( config.setting["tweak_file_replacement_char"] ) def check(self): test = SanitizeHook(self.ui.replace_backslashes.isChecked()) test.replacement = self.ui.sanitize_replacement.text() try: test.sub("xyzzy", "a/b/c") except sre_constants.error as err: raise OptionsCheckError(_("Error"), "Error in substitution: %s" % err); def save(self): config.setting["tweak_file_replace_backslash"] = \ self.ui.replace_backslashes.isChecked() config.setting["tweak_file_replacement_char"] = \ self.ui.sanitize_replacement.text() install_tweaker() # Hook picard.util.sanitize_filename by replacing the underying re object. class SanitizeHook(object): def __init__(self, bs = config.setting["tweak_file_replace_backslash"]): if bs: self.re_match = re.compile(r'[\\/]', re.UNICODE) else: self.re_match = re.compile(r'[/]', re.UNICODE) self.replacement = config.setting["tweak_file_replacement_char"] def sub(self, repl, string): ret = self.re_match.sub(self.replacement, string) return ret def install_tweaker(): re = SanitizeHook() if not hasattr(picard.util, "_re_slashes"): # Harder to globally monkey patch this picard.util function on newer # Picard, so instead we patch it in the two modules which import it. orig_sanitize_filename = picard.util.sanitize_filename def sanitize_filename_hook(string, **kwargs): return orig_sanitize_filename(re.sub(None, string), **kwargs) picard.util.scripttofilename.sanitize_filename = sanitize_filename_hook picard.util.textencoding.sanitize_filename = sanitize_filename_hook else: # On older picard we can monkey patch the underlying re object. picard.util._re_slashes = re log.info("%s activated" % (modulename())) if modulename() in config.setting["enabled_plugins"]: register_options_page(TweakFilenameOptionsPage) install_tweaker() else: log.debug("%s disabled in configuration" % (modulename()))