Skip to content

Commit

Permalink
PICARD-2729: Allow disabling date sanitazation for APE and Vorbis tags
Browse files Browse the repository at this point in the history
  • Loading branch information
ShubhamBhut committed Apr 21, 2024
1 parent 05ddd5f commit a00cb79
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 4 deletions.
6 changes: 5 additions & 1 deletion picard/formats/apev2.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class APEv2File(File):
'replaygain_reference_loudness': 'REPLAYGAIN_REFERENCE_LOUDNESS',
}
__rtranslate = {v.lower(): k for k, v in __translate.items()}
sanitize_date = sanitize_date

def __init__(self, filename):
super().__init__(filename)
Expand All @@ -136,6 +137,8 @@ def _load(self, filename):
file = self._File(encode_filename(filename))
metadata = Metadata()
if file.tags:
config = get_config()
date_sanitize = not config.setting['disable_date_sanitize'] and not any('.ape' in fmt[0] for fmt in config.setting['formats_to_disable_date_sanitize'])
for origname, values in file.tags.items():
name_lower = origname.lower()
if (values.kind == mutagen.apev2.BINARY
Expand All @@ -160,7 +163,8 @@ def _load(self, filename):
name = name_lower
if name == 'year':
name = 'date'
value = sanitize_date(value)
if date_sanitize:
value = sanitize_date(value)
elif name == 'track':
name = 'tracknumber'
track = value.split('/')
Expand Down
4 changes: 4 additions & 0 deletions picard/formats/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def supported_formats():
return [(file_format.EXTENSIONS, file_format.NAME) for file_format in _formats]


def formats_with_sanitize_date():
return [f"{fmt.EXTENSIONS, fmt.NAME}" for fmt in _formats if hasattr(fmt, 'sanitize_date')]


def supported_extensions():
"""Returns list of supported extensions."""
return [ext for exts, name in supported_formats() for ext in exts]
Expand Down
8 changes: 6 additions & 2 deletions picard/formats/vorbis.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,16 @@ def _load(self, filename):
file = self._File(encode_filename(filename))
file.tags = file.tags or {}
metadata = Metadata()
config = get_config()
date_sanitize = not config.setting['disable_date_sanitize']
for origname, values in file.tags.items():
for value in values:
value = value.rstrip('\0')
name = origname
if name in {'date', 'originaldate', 'releasedate'}:
# YYYY-00-00 => YYYY
value = sanitize_date(value)
if date_sanitize:
value = sanitize_date(value)
elif name == 'performer' or name == 'comment':
# transform "performer=Joe Barr (Piano)" to "performer:Piano=Joe Barr"
name += ':'
Expand Down Expand Up @@ -280,7 +283,8 @@ def _save(self, filename, metadata):
name = 'lyrics'
elif name in {'date', 'originaldate', 'releasedate'}:
# YYYY-00-00 => YYYY
value = sanitize_date(value)
if not config.setting['disable_date_sanitize']:
value = sanitize_date(value)
elif name.startswith('performer:') or name.startswith('comment:'):
# transform "performer:Piano=Joe Barr" to "performer=Joe Barr (Piano)"
name, desc = name.split(':', 1)
Expand Down
2 changes: 2 additions & 0 deletions picard/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class UserProfileGroups():
SettingDesc('convert_punctuation', ['convert_punctuation']),
SettingDesc('release_ars', ['release_ars']),
SettingDesc('track_ars', ['track_ars']),
SettingDesc('disable_date_sanitize', ['disable_date_sanitize']),
SettingDesc('formats_to_disable_date_sanitize', ['formats_to_disable_date_sanitize']),
SettingDesc('guess_tracknumber_and_title', ['guess_tracknumber_and_title']),
SettingDesc('va_name', ['va_name']),
SettingDesc('nat_name', ['nat_name']),
Expand Down
9 changes: 9 additions & 0 deletions picard/ui/options/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class MetadataOptionsPage(OptionsPage):
ListOption('setting', 'script_exceptions', [], title=N_("Translation script exceptions")),
BoolOption('setting', 'release_ars', True, title=N_("Use release relationships")),
BoolOption('setting', 'track_ars', False, title=N_("Use track and release relationships")),
BoolOption('setting', 'disable_date_sanitize', False, title=N_("Disable date sanitization for APE and Vorbis tags")),
ListOption('setting', 'formats_to_disable_date_sanitize', [], title=N_("Formats to disable date sanitize")),
BoolOption('setting', 'convert_punctuation', False, title=N_("Convert Unicode punctuation characters to ASCII")),
BoolOption('setting', 'standardize_artists', False, title=N_("Use standardized artist names")),
BoolOption('setting', 'standardize_instruments', True, title=N_("Use standardized instrument and vocal credits")),
Expand All @@ -108,6 +110,7 @@ def __init__(self, parent=None):
self.ui.select_scripts.clicked.connect(self.open_script_selector)
self.ui.translate_artist_names.stateChanged.connect(self.set_enabled_states)
self.ui.translate_artist_names_script_exception.stateChanged.connect(self.set_enabled_states)
self.ui.disable_date_sanitize.stateChanged.connect(self.set_enabled_states)

def load(self):
config = get_config()
Expand All @@ -117,6 +120,8 @@ def load(self):
self.current_scripts = config.setting['script_exceptions']
self.make_scripts_text()
self.ui.translate_artist_names_script_exception.setChecked(config.setting['translate_artist_names_script_exception'])
self.ui.disable_date_sanitize.setChecked(config.setting['disable_date_sanitize'])
self.current_formats = config.setting['formats_to_disable_date_sanitize']

self.ui.convert_punctuation.setChecked(config.setting['convert_punctuation'])
self.ui.release_ars.setChecked(config.setting['release_ars'])
Expand Down Expand Up @@ -152,6 +157,8 @@ def save(self):
config.setting['convert_punctuation'] = self.ui.convert_punctuation.isChecked()
config.setting['release_ars'] = self.ui.release_ars.isChecked()
config.setting['track_ars'] = self.ui.track_ars.isChecked()
config.setting['disable_date_sanitize'] = self.ui.disable_date_sanitize.isChecked()
config.setting['formats_to_disable_date_sanitize'] = self.current_formats
config.setting['va_name'] = self.ui.va_name.text()
nat_name = self.ui.nat_name.text()
if nat_name != config.setting['nat_name']:
Expand Down Expand Up @@ -179,6 +186,8 @@ def set_enabled_states(self):
select_scripts_enabled = translate_checked and translate_exception_checked
self.ui.selected_scripts.setEnabled(select_scripts_enabled)
self.ui.select_scripts.setEnabled(select_scripts_enabled)
disable_date_sanitize_checked = self.ui.disable_date_sanitize.isChecked()
self.ui.selected_formats.setEnabled(disable_date_sanitize_checked)

def open_locale_selector(self):
dialog = MultiLocaleSelector(self)
Expand Down
14 changes: 13 additions & 1 deletion picard/ui/ui_options_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@


from PyQt6 import QtCore, QtGui, QtWidgets
from picard.ui.widgets.multicombobox import MultiComboBox
from picard.formats.util import formats_with_sanitize_date


class Ui_MetadataOptionsPage(object):
Expand Down Expand Up @@ -72,6 +74,13 @@ def setupUi(self, MetadataOptionsPage):
self.guess_tracknumber_and_title = QtWidgets.QCheckBox(self.metadata_groupbox)
self.guess_tracknumber_and_title.setObjectName("guess_tracknumber_and_title")
self.verticalLayout_3.addWidget(self.guess_tracknumber_and_title)
self.disable_date_sanitize = QtWidgets.QCheckBox(self.metadata_groupbox)
self.disable_date_sanitize.setObjectName("disable_date_sanitize")
self.verticalLayout_3.addWidget(self.disable_date_sanitize)
self.selected_formats = MultiComboBox()
self.selected_formats.addItems(formats_with_sanitize_date())
self.selected_formats.setObjectName("selected_formats")
self.verticalLayout_3.addWidget(self.selected_formats)
self.verticalLayout.addWidget(self.metadata_groupbox)
self.custom_fields_groupbox = QtWidgets.QGroupBox(MetadataOptionsPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Maximum)
Expand Down Expand Up @@ -121,7 +130,9 @@ def setupUi(self, MetadataOptionsPage):
MetadataOptionsPage.setTabOrder(self.convert_punctuation, self.release_ars)
MetadataOptionsPage.setTabOrder(self.release_ars, self.track_ars)
MetadataOptionsPage.setTabOrder(self.track_ars, self.guess_tracknumber_and_title)
MetadataOptionsPage.setTabOrder(self.guess_tracknumber_and_title, self.va_name)
MetadataOptionsPage.setTabOrder(self.guess_tracknumber_and_title, self.disable_date_sanitize)
MetadataOptionsPage.setTabOrder(self.disable_date_sanitize, self.selected_formats)
MetadataOptionsPage.setTabOrder(self.selected_formats, self.va_name)
MetadataOptionsPage.setTabOrder(self.va_name, self.va_name_default)
MetadataOptionsPage.setTabOrder(self.va_name_default, self.nat_name)
MetadataOptionsPage.setTabOrder(self.nat_name, self.nat_name_default)
Expand All @@ -139,6 +150,7 @@ def retranslateUi(self, MetadataOptionsPage):
self.release_ars.setText(_("Use release relationships"))
self.track_ars.setText(_("Use track relationships"))
self.guess_tracknumber_and_title.setText(_("Guess track number and title from filename if empty"))
self.disable_date_sanitize.setText(_("Disable sanitizing date for APE and Vorbis tags"))
self.custom_fields_groupbox.setTitle(_("Custom Fields"))
self.label_6.setText(_("Various artists:"))
self.label_7.setText(_("Standalone recordings:"))
Expand Down
73 changes: 73 additions & 0 deletions picard/ui/widgets/multicombobox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
#
# Copyright (C) 2019-2020, 2022 Philipp Wolfer
# Copyright (C) 2020-2021, 2024 Laurent Monin
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.


from PyQt6.QtGui import QStandardItemModel, QStandardItem
from PyQt6.QtWidgets import QComboBox
from PyQt6.QtCore import Qt


class MultiComboBox(QComboBox):
def __init__(self, parent=None):
super().__init__(parent)
self.setEditable(True)
self.lineEdit().setReadOnly(True)
self.setModel(QStandardItemModel(self))

# Connect to the dataChanged signal to update the text
self.model().dataChanged.connect(self.updateText)

def addItem(self, text: str, data=None):
item = QStandardItem()
item.setText(text)
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
self.model().appendRow(item)

def addItems(self, items_list: list):
for text in items_list:
self.addItem(text)

def updateText(self):
selected_items = [self.model().item(i).text() for i in range(self.model().rowCount())
if self.model().item(i).checkState() == Qt.CheckState.Checked]
self.lineEdit().setText(", ".join(selected_items))

def showPopup(self):
super().showPopup()
# Set the state of each item in the dropdown
for i in range(self.model().rowCount()):
item = self.model().item(i)
combo_box_view = self.view()
combo_box_view.setRowHidden(i, False)
check_box = combo_box_view.indexWidget(item.index())
if check_box:
check_box.setChecked(item.checkState() == Qt.CheckState.Checked)

def hidePopup(self):
# Update the check state of each item based on the checkbox state
for i in range(self.model().rowCount()):
item = self.model().item(i)
combo_box_view = self.view()
check_box = combo_box_view.indexWidget(item.index())
if check_box:
item.setCheckState(Qt.CheckState.Checked if check_box.isChecked() else Qt.CheckState.Unchecked)
super().hidePopup()
2 changes: 2 additions & 0 deletions test/formats/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
'replace_spaces_with_underscores': False,
'replace_dir_separator': '_',
'win_compat_replacements': {},
'disable_date_sanitize': False,
'formats_to_disable_date_sanitize': [],
}


Expand Down

0 comments on commit a00cb79

Please sign in to comment.