Skip to content

Commit e82dc4c

Browse files
committed
PICARD-2729: Allow disabling date sanitazation for APE and Vorbis tags
1 parent 05ddd5f commit e82dc4c

File tree

10 files changed

+190
-44
lines changed

10 files changed

+190
-44
lines changed

picard/formats/apev2.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class APEv2File(File):
125125
'replaygain_reference_loudness': 'REPLAYGAIN_REFERENCE_LOUDNESS',
126126
}
127127
__rtranslate = {v.lower(): k for k, v in __translate.items()}
128+
sanitize_date = sanitize_date
128129

129130
def __init__(self, filename):
130131
super().__init__(filename)
@@ -136,6 +137,8 @@ def _load(self, filename):
136137
file = self._File(encode_filename(filename))
137138
metadata = Metadata()
138139
if file.tags:
140+
config = get_config()
141+
date_sanitize = self.NAME not in config.setting['formats_to_disable_date_sanitize']
139142
for origname, values in file.tags.items():
140143
name_lower = origname.lower()
141144
if (values.kind == mutagen.apev2.BINARY
@@ -160,7 +163,8 @@ def _load(self, filename):
160163
name = name_lower
161164
if name == 'year':
162165
name = 'date'
163-
value = sanitize_date(value)
166+
if date_sanitize:
167+
value = sanitize_date(value)
164168
elif name == 'track':
165169
name = 'tracknumber'
166170
track = value.split('/')

picard/formats/id3.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ class ID3File(File):
248248
__lrc_line_re_parse = re.compile(r'(\[\d\d:\d\d\.\d\d\d\])')
249249
__lrc_syllable_re_parse = re.compile(r'(<\d\d:\d\d\.\d\d\d>)')
250250
__lrc_both_re_parse = re.compile(r'(\[\d\d:\d\d\.\d\d\d\]|<\d\d:\d\d\.\d\d\d>)')
251+
sanitize_date = sanitize_date
251252

252253
def __init__(self, filename):
253254
super().__init__(filename)
@@ -278,6 +279,7 @@ def _load(self, filename):
278279
f = tags.pop(old)
279280
tags.add(getattr(id3, new)(encoding=f.encoding, text=f.text))
280281
metadata = Metadata()
282+
date_sanitize = self.NAME not in config.setting['formats_to_disable_date_sanitize']
281283
for frame in tags.values():
282284
frameid = frame.FrameID
283285
if frameid in self.__translate:
@@ -395,9 +397,12 @@ def _load(self, filename):
395397
metadata.add('~rating', rating)
396398

397399
if 'date' in metadata:
398-
sanitized = sanitize_date(metadata.getall('date')[0])
399-
if sanitized:
400-
metadata['date'] = sanitized
400+
if date_sanitize:
401+
sanitized = sanitize_date(metadata.getall('date')[0])
402+
if sanitized:
403+
metadata['date'] = sanitized
404+
else:
405+
metadata['date'] = metadata.getall('date')[0]
401406

402407
self._info(metadata, file)
403408
return metadata

picard/formats/util.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ def supported_formats():
4545
return [(file_format.EXTENSIONS, file_format.NAME) for file_format in _formats]
4646

4747

48+
def formats_with_sanitize_date():
49+
for fmt in _formats:
50+
if hasattr(fmt, 'sanitize_date'):
51+
yield fmt
52+
53+
4854
def supported_extensions():
4955
"""Returns list of supported extensions."""
5056
return [ext for exts, name in supported_formats() for ext in exts]

picard/formats/vorbis.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,24 @@ class VCommentFile(File):
129129
'waveformatextensible_channel_mask': '~waveformatextensible_channel_mask',
130130
}
131131
__rtranslate = {v: k for k, v in __translate.items()}
132+
sanitize_date = sanitize_date
132133

133134
def _load(self, filename):
134135
log.debug("Loading file %r", filename)
135136
config = get_config()
136137
file = self._File(encode_filename(filename))
137138
file.tags = file.tags or {}
138139
metadata = Metadata()
140+
config = get_config()
141+
date_sanitize = self.NAME not in config.setting['formats_to_disable_date_sanitize']
139142
for origname, values in file.tags.items():
140143
for value in values:
141144
value = value.rstrip('\0')
142145
name = origname
143146
if name in {'date', 'originaldate', 'releasedate'}:
144147
# YYYY-00-00 => YYYY
145-
value = sanitize_date(value)
148+
if date_sanitize:
149+
value = sanitize_date(value)
146150
elif name == 'performer' or name == 'comment':
147151
# transform "performer=Joe Barr (Piano)" to "performer:Piano=Joe Barr"
148152
name += ':'
@@ -280,7 +284,8 @@ def _save(self, filename, metadata):
280284
name = 'lyrics'
281285
elif name in {'date', 'originaldate', 'releasedate'}:
282286
# YYYY-00-00 => YYYY
283-
value = sanitize_date(value)
287+
if self.NAME not in config.setting['formats_to_disable_date_sanitize']:
288+
value = sanitize_date(value)
284289
elif name.startswith('performer:') or name.startswith('comment:'):
285290
# transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
286291
name, desc = name.split(':', 1)

picard/profile.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class UserProfileGroups():
7070
SettingDesc('convert_punctuation', ['convert_punctuation']),
7171
SettingDesc('release_ars', ['release_ars']),
7272
SettingDesc('track_ars', ['track_ars']),
73+
SettingDesc('disable_date_sanitize', ['disable_date_sanitize']),
74+
SettingDesc('formats_to_disable_date_sanitize', ['selected_formats']),
7375
SettingDesc('guess_tracknumber_and_title', ['guess_tracknumber_and_title']),
7476
SettingDesc('va_name', ['va_name']),
7577
SettingDesc('nat_name', ['nat_name']),

picard/ui/options/metadata.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from picard.ui.ui_multi_locale_selector import Ui_MultiLocaleSelector
5656
from picard.ui.ui_options_metadata import Ui_MetadataOptionsPage
5757
from picard.ui.util import qlistwidget_items
58+
from picard.formats.util import formats_with_sanitize_date
5859

5960

6061
def iter_sorted_locales(locales):
@@ -92,6 +93,8 @@ class MetadataOptionsPage(OptionsPage):
9293
ListOption('setting', 'script_exceptions', [], title=N_("Translation script exceptions")),
9394
BoolOption('setting', 'release_ars', True, title=N_("Use release relationships")),
9495
BoolOption('setting', 'track_ars', False, title=N_("Use track and release relationships")),
96+
BoolOption('setting', 'disable_date_sanitize', False, title=N_("Disable date sanitization for APE and Vorbis tags")),
97+
Option('setting', 'formats_to_disable_date_sanitize', set(), title=N_("Formats to disable date sanitize")),
9598
BoolOption('setting', 'convert_punctuation', False, title=N_("Convert Unicode punctuation characters to ASCII")),
9699
BoolOption('setting', 'standardize_artists', False, title=N_("Use standardized artist names")),
97100
BoolOption('setting', 'standardize_instruments', True, title=N_("Use standardized instrument and vocal credits")),
@@ -108,6 +111,7 @@ def __init__(self, parent=None):
108111
self.ui.select_scripts.clicked.connect(self.open_script_selector)
109112
self.ui.translate_artist_names.stateChanged.connect(self.set_enabled_states)
110113
self.ui.translate_artist_names_script_exception.stateChanged.connect(self.set_enabled_states)
114+
self.ui.disable_date_sanitize.stateChanged.connect(self.set_enabled_states)
111115

112116
def load(self):
113117
config = get_config()
@@ -117,6 +121,10 @@ def load(self):
117121
self.current_scripts = config.setting['script_exceptions']
118122
self.make_scripts_text()
119123
self.ui.translate_artist_names_script_exception.setChecked(config.setting['translate_artist_names_script_exception'])
124+
self.ui.disable_date_sanitize.setChecked(config.setting['disable_date_sanitize'])
125+
self.current_formats = config.setting['formats_to_disable_date_sanitize']
126+
fmt_names = sorted(fmt.NAME for fmt in formats_with_sanitize_date())
127+
self.ui.selected_formats.addItems(fmt_names)
120128

121129
self.ui.convert_punctuation.setChecked(config.setting['convert_punctuation'])
122130
self.ui.release_ars.setChecked(config.setting['release_ars'])
@@ -152,6 +160,8 @@ def save(self):
152160
config.setting['convert_punctuation'] = self.ui.convert_punctuation.isChecked()
153161
config.setting['release_ars'] = self.ui.release_ars.isChecked()
154162
config.setting['track_ars'] = self.ui.track_ars.isChecked()
163+
config.setting['disable_date_sanitize'] = self.ui.disable_date_sanitize.isChecked()
164+
config.setting['formats_to_disable_date_sanitize'] = self.current_formats
155165
config.setting['va_name'] = self.ui.va_name.text()
156166
nat_name = self.ui.nat_name.text()
157167
if nat_name != config.setting['nat_name']:
@@ -179,6 +189,8 @@ def set_enabled_states(self):
179189
select_scripts_enabled = translate_checked and translate_exception_checked
180190
self.ui.selected_scripts.setEnabled(select_scripts_enabled)
181191
self.ui.select_scripts.setEnabled(select_scripts_enabled)
192+
disable_date_sanitize_checked = self.ui.disable_date_sanitize.isChecked()
193+
self.ui.selected_formats.setEnabled(disable_date_sanitize_checked)
182194

183195
def open_locale_selector(self):
184196
dialog = MultiLocaleSelector(self)

0 commit comments

Comments
 (0)