From 112e0d885eec503da0344a63def3536d8d2bad68 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Mon, 24 Jul 2023 15:51:59 +0200 Subject: [PATCH] PICARD-2685: Preserve calculated AcoustID and ReplayGain tags Preserve calculated AcoustID fingerprint and ReplayGain tags set on a file object when that file object gets moved between tracks. --- picard/file.py | 14 ++++++++++---- picard/util/tags.py | 19 ++++++++++++++++++- test/test_file.py | 11 ++++++++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/picard/file.py b/picard/file.py index 003c3a6f6..8e33344a0 100644 --- a/picard/file.py +++ b/picard/file.py @@ -98,7 +98,10 @@ from picard.util.filenaming import ( ) from picard.util.preservedtags import PreservedTags from picard.util.scripttofilename import script_to_filename_with_metadata -from picard.util.tags import PRESERVED_TAGS +from picard.util.tags import ( + CALCULATED_TAGS, + PRESERVED_TAGS, +) from picard.ui.item import Item @@ -305,9 +308,14 @@ class File(QtCore.QObject, Item): to_metadata[info] = from_metadata[info] def copy_metadata(self, metadata, preserve_deleted=True): - acoustid = self.metadata["acoustid_id"] saved_metadata = {} + # Keep current value for special tags that got calculated from audio content + for tag in CALCULATED_TAGS: + if tag not in metadata.deleted_tags and self.metadata[tag]: + saved_metadata[tag] = self.metadata[tag] + + # Keep original values of preserved tags preserved_tags = PreservedTags() for tag, values in self.orig_metadata.rawitems(): if tag in preserved_tags or tag in PRESERVED_TAGS: @@ -321,8 +329,6 @@ class File(QtCore.QObject, Item): del self.metadata[tag] self.metadata.update(saved_metadata) - if acoustid and "acoustid_id" not in metadata.deleted_tags: - self.metadata["acoustid_id"] = acoustid if images_changed: self.metadata_images_changed.emit() diff --git a/picard/util/tags.py b/picard/util/tags.py index f3e8c0601..9ea5ec2b2 100644 --- a/picard/util/tags.py +++ b/picard/util/tags.py @@ -3,7 +3,7 @@ # Picard, the next-generation MusicBrainz tagger # # Copyright (C) 2007-2008, 2011 Lukáš Lalinský -# Copyright (C) 2008-2009, 2018-2021 Philipp Wolfer +# Copyright (C) 2008-2009, 2018-2021, 2023 Philipp Wolfer # Copyright (C) 2011 Johannes Weißl # Copyright (C) 2011-2013 Michael Wiencek # Copyright (C) 2012 Chad Wilson @@ -144,6 +144,23 @@ PRESERVED_TAGS = ( '~video', ) +# Tags that got generated in some way from the audio content. +# Those can be set by Picard but the new values usually should be kept +# when moving the file between tags. +CALCULATED_TAGS = { + 'acoustid_fingerprint', + 'acoustid_id', + 'replaygain_album_gain', + 'replaygain_album_peak', + 'replaygain_album_range', + 'replaygain_reference_loudness', + 'replaygain_track_gain', + 'replaygain_track_peak', + 'replaygain_track_range', + 'r128_album_gain', + 'r128_track_gain', +} + def display_tag_name(name): if ':' in name: diff --git a/test/test_file.py b/test/test_file.py index 56c3ef5ba..a75e1ef17 100644 --- a/test/test_file.py +++ b/test/test_file.py @@ -38,6 +38,7 @@ from picard.const.sys import ( ) from picard.file import File from picard.metadata import Metadata +from picard.util.tags import CALCULATED_TAGS class DataObjectTest(PicardTestCase): @@ -671,11 +672,15 @@ class FileCopyMetadataTest(PicardTestCase): self.assertEqual(self.file.metadata, new_metadata) self.assertEqual(self.file.metadata.deleted_tags, {'foo'}) - def test_copy_metadata_must_not_clear_acoustid_id(self): - self.file.metadata['acoustid_id'] = 'foo' + def test_copy_metadata_must_keep_file_content_specific_tags(self): + for tag in CALCULATED_TAGS: + self.file.metadata[tag] = 'foo' new_metadata = Metadata() self.file.copy_metadata(new_metadata) - self.assertEqual(self.file.metadata['acoustid_id'], 'foo') + for tag in CALCULATED_TAGS: + self.assertEqual( + self.file.metadata[tag], 'foo', + f'Tag {tag}: {self.file.metadata[tag]!r} != "foo"') def test_copy_metadata_must_remove_deleted_acoustid_id(self): self.file.metadata['acoustid_id'] = 'foo'