mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-05 16:13:59 +00:00
PICARD-257: Add option to exclude embedded images from clear_existing_tags
This commit is contained in:
@@ -191,12 +191,15 @@ class APEv2File(File):
|
||||
tags = mutagen.apev2.APEv2()
|
||||
images_to_save = list(metadata.images.to_be_saved_to_tags())
|
||||
if config.setting["clear_existing_tags"]:
|
||||
preserved = []
|
||||
if config.setting['preserve_images']:
|
||||
preserved = list(self._iter_cover_art_tags(tags))
|
||||
tags.clear()
|
||||
for name, value in preserved:
|
||||
tags[name] = value
|
||||
elif images_to_save:
|
||||
for name, value in tags.items():
|
||||
if (value.kind == mutagen.apev2.BINARY
|
||||
and name.lower().startswith('cover art')):
|
||||
del tags[name]
|
||||
for name, value in self._iter_cover_art_tags(tags):
|
||||
del tags[name]
|
||||
temp = {}
|
||||
for name, value in metadata.items():
|
||||
if name.startswith("~") or not self.supports_tag(name):
|
||||
@@ -273,6 +276,12 @@ class APEv2File(File):
|
||||
else:
|
||||
return name.title()
|
||||
|
||||
@staticmethod
|
||||
def _iter_cover_art_tags(tags):
|
||||
for name, value in tags.items():
|
||||
if value.kind == mutagen.apev2.BINARY and name.lower().startswith('cover art'):
|
||||
yield (name, value)
|
||||
|
||||
@classmethod
|
||||
def supports_tag(cls, name):
|
||||
return (bool(name) and name not in UNSUPPORTED_TAGS
|
||||
|
||||
@@ -267,7 +267,10 @@ class ASFFile(File):
|
||||
tags = file.tags
|
||||
|
||||
if config.setting['clear_existing_tags']:
|
||||
cover = tags['WM/Picture'] if config.setting['preserve_images'] else None
|
||||
tags.clear()
|
||||
if cover:
|
||||
tags['WM/Picture'] = cover
|
||||
cover = []
|
||||
for image in metadata.images.to_be_saved_to_tags():
|
||||
tag_data = pack_image(image.mimetype, image.data,
|
||||
|
||||
@@ -374,7 +374,10 @@ class ID3File(File):
|
||||
tags = self._get_tags(filename)
|
||||
config = get_config()
|
||||
if config.setting['clear_existing_tags']:
|
||||
cover = tags.getall('APIC') if config.setting["preserve_images"] else None
|
||||
tags.clear()
|
||||
if cover:
|
||||
tags.setall('APIC', cover)
|
||||
images_to_save = list(metadata.images.to_be_saved_to_tags())
|
||||
if images_to_save:
|
||||
tags.delall('APIC')
|
||||
|
||||
@@ -253,7 +253,10 @@ class MP4File(File):
|
||||
tags = file.tags
|
||||
|
||||
if config.setting["clear_existing_tags"]:
|
||||
cover = tags['covr'] if config.setting['preserve_images'] else None
|
||||
tags.clear()
|
||||
if cover:
|
||||
tags['covr'] = cover
|
||||
|
||||
for name, values in metadata.rawitems():
|
||||
if name.startswith('lyrics:'):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (C) 2006-2008, 2012 Lukáš Lalinský
|
||||
# Copyright (C) 2008 Hendrik van Antwerpen
|
||||
# Copyright (C) 2008-2010, 2014-2015, 2018-2020 Philipp Wolfer
|
||||
# Copyright (C) 2008-2010, 2014-2015, 2018-2021 Philipp Wolfer
|
||||
# Copyright (C) 2012-2013 Michael Wiencek
|
||||
# Copyright (C) 2012-2014 Wieland Hoffmann
|
||||
# Copyright (C) 2013 Calvin Walton
|
||||
@@ -231,12 +231,22 @@ class VCommentFile(File):
|
||||
if file.tags is None:
|
||||
file.add_tags()
|
||||
if config.setting["clear_existing_tags"]:
|
||||
channel_mask = file.tags.get('waveformatextensible_channel_mask', None)
|
||||
preserve_tags = ['waveformatextensible_channel_mask']
|
||||
if not is_flac and config.setting["preserve_images"]:
|
||||
preserve_tags.append('METADATA_BLOCK_PICTURE')
|
||||
preserve_tags.append('COVERART')
|
||||
preserved_values = {}
|
||||
for name in preserve_tags:
|
||||
if name in file.tags:
|
||||
preserved_values[name] = file.tags[name]
|
||||
file.tags.clear()
|
||||
if channel_mask:
|
||||
file.tags['waveformatextensible_channel_mask'] = channel_mask
|
||||
for name, value in preserved_values.items():
|
||||
if value:
|
||||
file.tags[name] = value
|
||||
images_to_save = list(metadata.images.to_be_saved_to_tags())
|
||||
if is_flac and (config.setting["clear_existing_tags"] or images_to_save):
|
||||
if is_flac and (
|
||||
(config.setting["clear_existing_tags"] and not config.setting["preserve_images"])
|
||||
or images_to_save):
|
||||
file.clear_pictures()
|
||||
tags = {}
|
||||
for name, value in metadata.items():
|
||||
|
||||
@@ -53,6 +53,7 @@ class TagsOptionsPage(OptionsPage):
|
||||
BoolOption("setting", "dont_write_tags", False),
|
||||
BoolOption("setting", "preserve_timestamps", False),
|
||||
BoolOption("setting", "clear_existing_tags", False),
|
||||
BoolOption("setting", "preserve_images", False),
|
||||
BoolOption("setting", "remove_id3_from_flac", False),
|
||||
BoolOption("setting", "remove_ape_from_mp3", False),
|
||||
ListOption("setting", "preserved_tags", []),
|
||||
@@ -68,6 +69,7 @@ class TagsOptionsPage(OptionsPage):
|
||||
self.ui.write_tags.setChecked(not config.setting["dont_write_tags"])
|
||||
self.ui.preserve_timestamps.setChecked(config.setting["preserve_timestamps"])
|
||||
self.ui.clear_existing_tags.setChecked(config.setting["clear_existing_tags"])
|
||||
self.ui.preserve_images.setChecked(config.setting["preserve_images"])
|
||||
self.ui.remove_ape_from_mp3.setChecked(config.setting["remove_ape_from_mp3"])
|
||||
self.ui.remove_id3_from_flac.setChecked(config.setting["remove_id3_from_flac"])
|
||||
self.ui.preserved_tags.update(config.setting["preserved_tags"])
|
||||
@@ -81,6 +83,7 @@ class TagsOptionsPage(OptionsPage):
|
||||
if clear_existing_tags != config.setting["clear_existing_tags"]:
|
||||
config.setting["clear_existing_tags"] = clear_existing_tags
|
||||
self.tagger.window.metadata_box.update()
|
||||
config.setting["preserve_images"] = self.ui.preserve_images.isChecked()
|
||||
config.setting["remove_ape_from_mp3"] = self.ui.remove_ape_from_mp3.isChecked()
|
||||
config.setting["remove_id3_from_flac"] = self.ui.remove_id3_from_flac.isChecked()
|
||||
config.setting["preserved_tags"] = list(self.ui.preserved_tags.tags)
|
||||
|
||||
@@ -10,7 +10,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_TagsOptionsPage(object):
|
||||
def setupUi(self, TagsOptionsPage):
|
||||
TagsOptionsPage.setObjectName("TagsOptionsPage")
|
||||
TagsOptionsPage.resize(539, 525)
|
||||
TagsOptionsPage.resize(567, 525)
|
||||
self.vboxlayout = QtWidgets.QVBoxLayout(TagsOptionsPage)
|
||||
self.vboxlayout.setObjectName("vboxlayout")
|
||||
self.write_tags = QtWidgets.QCheckBox(TagsOptionsPage)
|
||||
@@ -28,6 +28,10 @@ class Ui_TagsOptionsPage(object):
|
||||
self.clear_existing_tags = QtWidgets.QCheckBox(self.before_tagging)
|
||||
self.clear_existing_tags.setObjectName("clear_existing_tags")
|
||||
self.vboxlayout1.addWidget(self.clear_existing_tags)
|
||||
self.preserve_images = QtWidgets.QCheckBox(self.before_tagging)
|
||||
self.preserve_images.setEnabled(False)
|
||||
self.preserve_images.setObjectName("preserve_images")
|
||||
self.vboxlayout1.addWidget(self.preserve_images)
|
||||
self.remove_id3_from_flac = QtWidgets.QCheckBox(self.before_tagging)
|
||||
self.remove_id3_from_flac.setObjectName("remove_id3_from_flac")
|
||||
self.vboxlayout1.addWidget(self.remove_id3_from_flac)
|
||||
@@ -50,12 +54,13 @@ class Ui_TagsOptionsPage(object):
|
||||
self.vboxlayout.addWidget(self.before_tagging)
|
||||
|
||||
self.retranslateUi(TagsOptionsPage)
|
||||
self.clear_existing_tags.toggled['bool'].connect(self.preserve_images.setEnabled)
|
||||
QtCore.QMetaObject.connectSlotsByName(TagsOptionsPage)
|
||||
TagsOptionsPage.setTabOrder(self.write_tags, self.preserve_timestamps)
|
||||
TagsOptionsPage.setTabOrder(self.preserve_timestamps, self.clear_existing_tags)
|
||||
TagsOptionsPage.setTabOrder(self.clear_existing_tags, self.remove_id3_from_flac)
|
||||
TagsOptionsPage.setTabOrder(self.clear_existing_tags, self.preserve_images)
|
||||
TagsOptionsPage.setTabOrder(self.preserve_images, self.remove_id3_from_flac)
|
||||
TagsOptionsPage.setTabOrder(self.remove_id3_from_flac, self.remove_ape_from_mp3)
|
||||
TagsOptionsPage.setTabOrder(self.remove_ape_from_mp3, self.preserved_tags)
|
||||
|
||||
def retranslateUi(self, TagsOptionsPage):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
@@ -63,6 +68,7 @@ class Ui_TagsOptionsPage(object):
|
||||
self.preserve_timestamps.setText(_("Preserve timestamps of tagged files"))
|
||||
self.before_tagging.setTitle(_("Before Tagging"))
|
||||
self.clear_existing_tags.setText(_("Clear existing tags"))
|
||||
self.preserve_images.setText(_("Keep embedded images when clearing tags"))
|
||||
self.remove_id3_from_flac.setText(_("Remove ID3 tags from FLAC files"))
|
||||
self.remove_ape_from_mp3.setText(_("Remove APEv2 tags from MP3 files"))
|
||||
self.preserved_tags_label.setText(_("Preserve these tags from being cleared or overwritten with MusicBrainz data:"))
|
||||
|
||||
@@ -40,6 +40,7 @@ from picard.metadata import Metadata
|
||||
|
||||
settings = {
|
||||
'clear_existing_tags': False,
|
||||
'preserve_images': False,
|
||||
'embed_only_one_front_image': False,
|
||||
'enabled_plugins': '',
|
||||
'id3v23_join_with': '/',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
#
|
||||
# Copyright (C) 2019-2020 Philipp Wolfer
|
||||
# Copyright (C) 2019-2021 Philipp Wolfer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -75,6 +75,10 @@ class CommonCoverArtTests:
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.set_config_values({
|
||||
'clear_existing_tags': False,
|
||||
'preserve_images': False,
|
||||
})
|
||||
self.jpegdata = load_coverart_file('mb.jpg')
|
||||
self.pngdata = load_coverart_file('mb.png')
|
||||
|
||||
@@ -118,6 +122,20 @@ class CommonCoverArtTests:
|
||||
loaded_metadata = save_and_load_metadata(self.filename, metadata)
|
||||
self.assertEqual(0, len(loaded_metadata.images))
|
||||
|
||||
@skipUnlessTestfile
|
||||
def test_cover_art_clear_tags(self):
|
||||
image = CoverArtImage(data=self.pngdata, types=['front'])
|
||||
file_save_image(self.filename, image)
|
||||
metadata = load_metadata(self.filename)
|
||||
self.assertEqual(image, metadata.images[0])
|
||||
config.setting['clear_existing_tags'] = True
|
||||
config.setting['preserve_images'] = True
|
||||
metadata = save_and_load_metadata(self.filename, Metadata())
|
||||
self.assertEqual(image, metadata.images[0])
|
||||
config.setting['preserve_images'] = False
|
||||
metadata = save_and_load_metadata(self.filename, Metadata())
|
||||
self.assertEqual(0, len(metadata.images))
|
||||
|
||||
def _cover_metadata(self):
|
||||
imgdata = self.jpegdata
|
||||
metadata = Metadata()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>539</width>
|
||||
<width>567</width>
|
||||
<height>525</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -47,6 +47,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="preserve_images">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep embedded images when clearing tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="remove_id3_from_flac">
|
||||
<property name="text">
|
||||
@@ -111,10 +121,27 @@
|
||||
<tabstop>write_tags</tabstop>
|
||||
<tabstop>preserve_timestamps</tabstop>
|
||||
<tabstop>clear_existing_tags</tabstop>
|
||||
<tabstop>preserve_images</tabstop>
|
||||
<tabstop>remove_id3_from_flac</tabstop>
|
||||
<tabstop>remove_ape_from_mp3</tabstop>
|
||||
<tabstop>preserved_tags</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>clear_existing_tags</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>preserve_images</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>283</x>
|
||||
<y>107</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>283</x>
|
||||
<y>132</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
Reference in New Issue
Block a user