From ec25722eff05b31ba4fad633f665d4dcc6643b2f Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 14 Jun 2024 11:52:04 +0200 Subject: [PATCH] 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")