From e90ec46550a2cffdd2ed4cedfefd071903944bc4 Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 14 Jun 2024 09:56:23 +0200 Subject: [PATCH 1/3] Fix duplicated albumartistsort key albumartistsort was already added as a registered Text key some lines above so adding it again as a TXXX key replaces the previous TSO2 tag registration. Since picard uses TSO2 for "album artist sort order" and even if it's not an official tag it seems to be generally supported, I think it's better to leave that one. --- mutagen/easyid3.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mutagen/easyid3.py b/mutagen/easyid3.py index 510b9222..a9f530f1 100644 --- a/mutagen/easyid3.py +++ b/mutagen/easyid3.py @@ -530,7 +530,6 @@ def peakgain_list(id3, key): u"MusicBrainz Album Release Country": "releasecountry", u"MusicBrainz Disc Id": "musicbrainz_discid", u"ASIN": "asin", - u"ALBUMARTISTSORT": "albumartistsort", u"PERFORMER": "performer", u"BARCODE": "barcode", u"CATALOGNUMBER": "catalognumber", From 11629ef5d490b3f2721cad1b4ebf316483f11d5a Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 14 Jun 2024 10:07:11 +0200 Subject: [PATCH 2/3] Add test for correct handling of albumartistsort as TSO2 This tests that albumartistsort is handled by the TSO2 frameid. --- tests/test_easyid3.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_easyid3.py b/tests/test_easyid3.py index 6c899dac..857dbaa3 100644 --- a/tests/test_easyid3.py +++ b/tests/test_easyid3.py @@ -3,7 +3,7 @@ import pickle from mutagen import MutagenError -from mutagen.id3 import ID3FileType, ID3, RVA2, CHAP, TDRC, CTOC +from mutagen.id3 import ID3FileType, ID3, RVA2, CHAP, TDRC, CTOC, TSO2 from mutagen.easyid3 import EasyID3, error as ID3Error from tests import TestCase, DATA_DIR, get_temp_copy @@ -428,3 +428,9 @@ def test_text_tags(self): self.id3.save(self.filename) id3 = EasyID3(self.filename) self.failUnlessEqual(id3[tag], [u"foo"]) + + def test_albumartistsort(self): + self.realid3.add(TSO2(text=u"someartist")) + self.id3.save(self.filename) + id3 = EasyID3(self.filename) + self.failUnlessEqual(id3["albumartistsort"], [u"someartist"]) From ec25722eff05b31ba4fad633f665d4dcc6643b2f Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 14 Jun 2024 11:52:04 +0200 Subject: [PATCH 3/3] Allow TXXX:ALBUMARTISTSORT to be a fallback for albumartistsort I noticed that my previous fix for duplicate key registration of albumartistsort would break reading files already having TXXX:ALBUMARTISTSORT so this commit fixes it by making TXXX:ALBUMARTISTSORT a fallback for albumartistsort if the first getter can't get a value and also by making EasyID3 delete TXXX:ALBUMARTISTSORT frames when deleting albumartistsort. Still, when setting an albumartistsort value only TSO2 frames are used. This also adds tests for all those cases. --- mutagen/easyid3.py | 46 +++++++++++++++++++++++++++++++++++++++++++ tests/test_easyid3.py | 38 ++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/mutagen/easyid3.py b/mutagen/easyid3.py index a9f530f1..db84e016 100644 --- a/mutagen/easyid3.py +++ b/mutagen/easyid3.py @@ -12,6 +12,7 @@ """ from typing import Callable, Dict +from functools import partial import mutagen.id3 @@ -542,6 +543,51 @@ def peakgain_list(id3, key): EasyID3.RegisterTXXXKey(key, desc) +def fallback_getter_helper(id3, key, original, fallback): + try: + return original(id3, key) + except KeyError as e: + try: + return fallback(id3, key) + except KeyError: + raise e + + +def fallback_deleter_helper(id3, key, original, fallback): + try: + original(id3, key) + except KeyError as e: + exception1 = e + else: + exception1 = None + + try: + return fallback(id3, key) + except KeyError: + if exception1: + raise exception1 + + +def txxxkey_getter(id3, key, frameid): + return list(id3[frameid]) + + +def txxxkey_deleter(id3, key, frameid): + del id3[frameid] + +for key, extra_source in { + "albumartistsort": "TXXX:ALBUMARTISTSORT", +}.items(): + EasyID3.Get[key] = partial(fallback_getter_helper, + original=EasyID3.Get[key], + fallback=partial(txxxkey_getter, + frameid=extra_source)) + EasyID3.Delete[key] = partial(fallback_deleter_helper, + original=EasyID3.Delete[key], + fallback=partial(txxxkey_deleter, + frameid=extra_source)) + + class EasyID3FileType(ID3FileType): """EasyID3FileType(filething=None) diff --git a/tests/test_easyid3.py b/tests/test_easyid3.py index 857dbaa3..9d3c40e0 100644 --- a/tests/test_easyid3.py +++ b/tests/test_easyid3.py @@ -3,7 +3,7 @@ import pickle from mutagen import MutagenError -from mutagen.id3 import ID3FileType, ID3, RVA2, CHAP, TDRC, CTOC, TSO2 +from mutagen.id3 import ID3FileType, ID3, RVA2, CHAP, TDRC, CTOC, TSO2, TXXX from mutagen.easyid3 import EasyID3, error as ID3Error from tests import TestCase, DATA_DIR, get_temp_copy @@ -434,3 +434,39 @@ def test_albumartistsort(self): self.id3.save(self.filename) id3 = EasyID3(self.filename) self.failUnlessEqual(id3["albumartistsort"], [u"someartist"]) + + self.id3["albumartistsort"] = [u"otherartist"] + self.assertEqual(self.realid3["TSO2"], [u"otherartist"]) + + del self.id3["albumartistsort"] + self.assertEqual(len(self.id3), 0) + + def test_albumartistsort_from_TXXX(self): + self.realid3.add(TXXX(desc="ALBUMARTISTSORT", text=u"someartist")) + self.id3.save(self.filename) + id3 = EasyID3(self.filename) + self.failUnlessEqual(id3["albumartistsort"], [u"someartist"]) + + self.id3["albumartistsort"] = [u"otherartist"] + self.assertEqual(self.realid3["TSO2"], [u"otherartist"]) + + del self.id3["albumartistsort"] + self.assertEqual(len(self.id3), 0) + + def test_albumartistsort_with_both_frames(self): + self.realid3.add(TSO2(text=u"someartist")) + self.realid3.add(TXXX(desc="ALBUMARTISTSORT", text=u"someotherartist")) + self.id3.save(self.filename) + id3 = EasyID3(self.filename) + self.failUnlessEqual(id3["albumartistsort"], [u"someartist"]) + self.assertEqual(len(self.id3), 1) + self.assertEqual(len(self.realid3), 2) + + self.id3["albumartistsort"] = [u"otherartist"] + self.assertEqual(self.realid3["TSO2"], [u"otherartist"]) + + del self.id3["albumartistsort"] + self.assertEqual(len(self.id3), 0) + self.assertEqual(len(self.realid3), 0) + + self.failUnlessRaises(KeyError, self.id3.__delitem__, "albumartistsort")