]> git.draconx.ca Git - picard-plugins.git/blob - tweak-filename-filter.py
tweak-filename-filter: Update for picard 2.3.1
[picard-plugins.git] / tweak-filename-filter.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2018-2020 Nick Bowler
4 #
5 # License GPLv3+: GNU General Public License version 3 or any later version.
6 # This is free software: you are free to change and redistribute it.
7 # There is NO WARRANTY, to the extent permitted by law.
8
9 PLUGIN_NAME = u"Tweak Filename Filter"
10 PLUGIN_AUTHOR = u"Nick Bowler"
11 PLUGIN_DESCRIPTION = u'''<p>Adds additional options to tweak file naming.</p>
12 <p>Currently, this overrides the default methods to substitute forward and
13 back-slashes, allowing alternate behaviours.</p>
14
15 <dl>
16
17 <dt>tweak_file_replace_backslash (bool)</dt>
18 <dd>if true, backslashes will be replaced when renaming files. (default:
19 true)</dd>
20
21 <dt>tweak_file_replacement_char (string)</dt>
22 <dd>matching characters will be replaced by this string (default: _)</dd>
23
24 </dl>
25 '''
26 PLUGIN_VERSION = "2"
27 PLUGIN_API_VERSIONS = ["2.0"]
28 PLUGIN_LICENSE = "GPL-3.0-or-later"
29
30 from PyQt5 import QtWidgets
31
32 from picard import (config, log)
33 from picard.ui.options import (
34     OptionsPage, OptionsCheckError,
35     register_options_page
36 )
37 import picard.util
38
39 import sre_constants
40 import re
41
42 def modulename():
43     return modulename.__module__[len("picard.plugins."):]
44
45 class UI_TweakFilenameFilter(object):
46     def setupUi(self, TweakFilenameOptionsPage):
47         top = QtWidgets.QVBoxLayout(TweakFilenameOptionsPage)
48
49         group = QtWidgets.QGroupBox(TweakFilenameOptionsPage)
50         group.setTitle("Filename Sanitizer")
51         top.addWidget(group)
52
53         box = QtWidgets.QVBoxLayout(group)
54
55         msg = QtWidgets.QLabel(group)
56         msg.setText('''<em>Picard normally replaces slashes and backslashes
57         with underscores in all metadata tags prior to running the file-naming
58         script.  This substitution may be customized here.  Replacement text
59         may use <a href='https://docs.python.org/3/library/re.html#re.sub'
60         >backslash sequences supported by <code>re.sub</code></a>.</em>''')
61         msg.setOpenExternalLinks(True)
62         box.addWidget(msg)
63
64         self.replace_slashes = QtWidgets.QCheckBox(group)
65         self.replace_slashes.setText("Replace slashes in metadata")
66         self.replace_slashes.setChecked(True)
67         self.replace_slashes.setEnabled(False)
68         box.addWidget(self.replace_slashes)
69
70         self.replace_backslashes = QtWidgets.QCheckBox(group)
71         self.replace_backslashes.setText("Replace backslashes in metadata")
72         box.addWidget(self.replace_backslashes)
73
74         label = QtWidgets.QLabel()
75         label.setText("Replacement text:")
76         box.addWidget(label)
77
78         self.sanitize_replacement = QtWidgets.QLineEdit(group)
79         box.addWidget(self.sanitize_replacement)
80         label.setBuddy(self.sanitize_replacement)
81
82         top.addStretch()
83
84 class TweakFilenameOptionsPage(OptionsPage):
85     NAME = PLUGIN_NAME
86     TITLE = PLUGIN_NAME
87     PARENT = None
88     SORT_ORDER = 99
89     ACTIVE = True
90
91     options = [
92         config.TextOption("setting", "tweak_file_replacement_char", "_"),
93         config.BoolOption("setting", "tweak_file_replace_backslash", True)
94     ]
95
96     def __init__(self, parent=None):
97         super().__init__(parent)
98         self.ui = UI_TweakFilenameFilter()
99         self.ui.setupUi(self)
100
101     def load(self):
102         self.ui.replace_backslashes.setChecked(
103             config.setting["tweak_file_replace_backslash"]
104         )
105         self.ui.sanitize_replacement.setText(
106             config.setting["tweak_file_replacement_char"]
107         )
108
109     def check(self):
110         test = SanitizeHook(self.ui.replace_backslashes.isChecked())
111         test.replacement = self.ui.sanitize_replacement.text()
112         try:
113             test.sub("xyzzy", "a/b/c")
114         except sre_constants.error as err:
115             raise OptionsCheckError(_("Error"),
116                                     "Error in substitution: %s" % err);
117
118     def save(self):
119         config.setting["tweak_file_replace_backslash"] = \
120             self.ui.replace_backslashes.isChecked()
121         config.setting["tweak_file_replacement_char"] = \
122             self.ui.sanitize_replacement.text()
123         install_tweaker()
124
125 # Hook picard.util.sanitize_filename by replacing the underying re object.
126 class SanitizeHook(object):
127     def __init__(self, bs = config.setting["tweak_file_replace_backslash"]):
128         if bs:
129             self.re_match = re.compile(r'[\\/]', re.UNICODE)
130         else:
131             self.re_match = re.compile(r'[/]', re.UNICODE)
132         self.replacement = config.setting["tweak_file_replacement_char"]
133
134     def sub(self, repl, string):
135         ret = self.re_match.sub(self.replacement, string)
136         return ret
137
138 def install_tweaker():
139     re = SanitizeHook()
140     if not hasattr(picard.util, "_re_slashes"):
141         # Harder to globally monkey patch this picard.util function on newer
142         # Picard, so instead we patch it in the two modules which import it.
143         orig_sanitize_filename = picard.util.sanitize_filename
144         def sanitize_filename_hook(string, **kwargs):
145             return orig_sanitize_filename(re.sub(None, string), **kwargs)
146
147         picard.util.scripttofilename.sanitize_filename = sanitize_filename_hook
148         picard.util.textencoding.sanitize_filename = sanitize_filename_hook
149     else:
150         # On older picard we can monkey patch the underlying re object.
151         picard.util._re_slashes = re
152
153     log.info("%s activated" % (modulename()))
154
155 if modulename() in config.setting["enabled_plugins"]:
156     register_options_page(TweakFilenameOptionsPage)
157     install_tweaker()
158 else:
159     log.debug("%s disabled in configuration" % (modulename()))