From eac080571db3bc1dff596dd0b3829e875a2400db Mon Sep 17 00:00:00 2001 From: thebigmunch Date: Sun, 22 Mar 2020 11:58:08 -0400 Subject: [PATCH] Add support for ID3v2 unique file identifier frames [#22] --- CHANGELOG.md | 7 ++-- src/audio_metadata/formats/id3v2_frames.py | 39 ++++++++++++++++++++-- src/audio_metadata/formats/tables.py | 3 ++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d33b88..086ebf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org). * ``RIFFTag``. * ``ID3v1Field``. * ``ID3v1AlbumField``. -* ``ID3v2ArtistField``. +* ``ID3v1ArtistField``. * ``ID3v1CommentField``. * ``ID3v1GenreField``. * ``ID3v1TitleField``. @@ -25,6 +25,9 @@ This project adheres to [Semantic Versioning](https://semver.org). * ``FormatError``. * ``TagError``. * ``FLACVorbisComments``. +* Support for ID3v2 unique file identifier frames. + * ``ID3v2UniqueFileIdentifier``. + * ``ID3v2UniqueFileIdentifierFrame``. ### Changed @@ -33,7 +36,7 @@ This project adheres to [Semantic Versioning](https://semver.org). * Refactor ID3v1 to use tag classes. * ``ID3v1Field``. * ``ID3v1AlbumField``. - * ``ID3v2ArtistField``. + * ``ID3v1ArtistField``. * ``ID3v1CommentField``. * ``ID3v1GenreField``. * ``ID3v1TitleField``. diff --git a/src/audio_metadata/formats/id3v2_frames.py b/src/audio_metadata/formats/id3v2_frames.py index 219f928..addba6c 100644 --- a/src/audio_metadata/formats/id3v2_frames.py +++ b/src/audio_metadata/formats/id3v2_frames.py @@ -28,6 +28,8 @@ 'ID3v2TMCLFrame', 'ID3v2TextFrame', 'ID3v2TimestampFrame', + 'ID3v2UniqueFileIdentifier', + 'ID3v2UniqueFileIdentifierFrame', 'ID3v2UnsynchronizedLyrics', 'ID3v2UnsynchronizedLyricsFrame', 'ID3v2URLLinkFrame', @@ -128,6 +130,15 @@ class ID3v2PrivateInfo(AttrMapping): data = attrib() +@attrs( + repr=False, + kw_only=True, +) +class ID3v2UniqueFileIdentifier(AttrMapping): + owner = attrib() + identifier = attrib() + + @attrs( repr=False, kw_only=True, @@ -798,14 +809,37 @@ def validate_value(self, attribute, value): ) +@attrs( + repr=False, + kw_only=True, +) +class ID3v2UniqueFileIdentifierFrame(ID3v2Frame): + @datareader + @classmethod + def _parse_frame_data(cls, data, frame_size): + frame_data = data.read(frame_size) + owner, identifier = frame_data.split(b'\x00') + + if len(identifier) > 64: + raise TagError("ID3v2 unique file identifier must be no more than 64 bytes.") + + return ( + ID3v2UFID( + owner=owner.decode('iso-8859-1'), + identifier=identifier, + ), + None, + ) + + # TODO:ID3v2.2 # TODO: BUF, CNT, CRA, CRM, ETC, EQU, LNK, MCI, MLL, PCS, -# TODO: POP, REV, RVA, STC, UFI +# TODO: POP, REV, RVA, STC # TODO: ID3v2.3 # TODO: AENC, COMR, ENCR, EQUA, ETCO, GRID, LINK, MLLT, OWNE # TODO: PCNT, PCST, POPM, POSS, RBUF, RGAD, RVAD, RVRB, SYTC, -# TODO: UFID, USER, XRVA +# TODO: USER, XRVA # TODO: ID3v2.4 # TODO: ASPI, EQU2, PCST, RGAD, RVA2, SEEK, SIGN, @@ -828,6 +862,7 @@ def validate_value(self, attribute, value): 'TIPL': ID3v2InvolvedPeopleListFrame, 'TMCL': ID3v2TMCLFrame, 'TXXX': ID3v2UserTextFrame, + 'UFID': ID3v2UFIDFrame, # Genre Frame 'TCO': ID3v2GenreFrame, diff --git a/src/audio_metadata/formats/tables.py b/src/audio_metadata/formats/tables.py index c7f07c3..6171fd1 100644 --- a/src/audio_metadata/formats/tables.py +++ b/src/audio_metadata/formats/tables.py @@ -149,6 +149,7 @@ class ID3Version(_BaseEnum): 'title': 'TT2', 'titlesort': 'TST', 'tracknumber': 'TRK', + 'ufid': 'UFI', 'usertext': 'TXX', 'userurl': 'WXX', }, @@ -200,6 +201,7 @@ class ID3Version(_BaseEnum): 'title': 'TIT2', 'titlesort': 'TSOT', 'tracknumber': 'TRCK', + 'ufid': 'UFID', 'usertext': 'TXXX', 'userurl': 'WXXX', }, @@ -255,6 +257,7 @@ class ID3Version(_BaseEnum): 'title': 'TIT2', 'titlesort': 'TSOT', 'tracknumber': 'TRCK', + 'ufid': 'UFID', 'usertext': 'TXXX', 'userurl': 'WXXX', },