diff --git a/CHANGELOG.md b/CHANGELOG.md index b91da37..4c3f296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ This project adheres to [Semantic Versioning](https://semver.org). * ``OggVorbisStreamInfo``. * ``OggOpusVorbisComments``. * ``ID3v2GeneralEncapsulatedObject``. +* ``ID3v2PrivateInfo``. ### Changed @@ -54,9 +55,15 @@ This project adheres to [Semantic Versioning](https://semver.org). * Rework ID3v2 general encapsulated object abstractions. * Add ``ID3v2GeneralEncapsulatedObject`` class. * Change ``ID3v2GEOBFrame`` to have only value attribute. - that contains a single comment. + that contains a single general encapsulated object. * Change ``ID3v2Frames`` to present a list of general encapsulated objects for ``GEOB`` key. +* Rework ID3v2 private information frame abstractions. + * Add ``ID3v2PrivateInfo`` class. + * Change ``ID3v2PrivateFrame`` to have only value attribute. + that contains a single private information object. + * Change ``ID3v2Frames`` to present a list of private info + objects for ``PRIV`` key. ### Removed diff --git a/src/audio_metadata/formats/id3v2.py b/src/audio_metadata/formats/id3v2.py index 55a9463..8098fc8 100644 --- a/src/audio_metadata/formats/id3v2.py +++ b/src/audio_metadata/formats/id3v2.py @@ -264,8 +264,6 @@ def parse(cls, data, id3_version): ), ): frames[f'{frame.id}:{frame.description}:{frame.language}'].append(frame.value) - elif isinstance(frame, ID3v2PrivateFrame): - frames[f'{frame.id}:{frame.owner}'].append(frame.value) elif isinstance( frame, ( diff --git a/src/audio_metadata/formats/id3v2_frames.py b/src/audio_metadata/formats/id3v2_frames.py index e58611a..2927be8 100644 --- a/src/audio_metadata/formats/id3v2_frames.py +++ b/src/audio_metadata/formats/id3v2_frames.py @@ -70,6 +70,17 @@ class ID3v2Comment(AttrMapping): text = attrib() +@attrs( + repr=False, + kw_only=True, +) +class ID3v2GeneralEncapsulatedObject(AttrMapping): + mime_type = attrib() + filename = attrib() + description = attrib() + value = attrib() + + @attrs( repr=False, kw_only=True, @@ -88,6 +99,15 @@ class ID3v2Performer(AttrMapping): name = attrib() +@attrs( + repr=False, + kw_only=True, +) +class ID3v2PrivateInfo(AttrMapping): + owner = attrib() + data = attrib() + + @attrs( repr=False, kw_only=True, @@ -229,7 +249,6 @@ class ID3v2PictureFrame(ID3v2BaseFrame): kw_only=True, ) class ID3v2PrivateFrame(ID3v2BaseFrame): - owner = attrib() value = attrib() @@ -625,6 +644,19 @@ def parse(cls, data, struct_pattern, size_len, per_byte): return None kwargs['value'] = mapping_list + elif frame_type is ID3v2GEOBFrame: + encoding = determine_encoding(frame_data[0:1]) + + mime_type, remainder = split_encoded(frame_data[1:], encoding) + filename, remainder = split_encoded(remainder, encoding) + description, value = split_encoded(remainder, encoding) + + kwargs['value'] = ID3v2GeneralEncapsulatedObject( + mime_type=mime_type, + filename=filename, + description=description, + value=value, + ) elif frame_type is ID3v2GenreFrame: encoding = determine_encoding(frame_data[0:1]) @@ -677,8 +709,11 @@ def parse(cls, data, struct_pattern, size_len, per_byte): kwargs['value'] = frame_data elif frame_type is ID3v2PrivateFrame: owner_end = frame_data.index(b'\x00') - kwargs['owner'] = frame_data[0:owner_end].decode('iso-8859-1') - kwargs['value'] = frame_data[owner_end + 1:] + + kwargs['value'] = ID3v2PrivateInfo( + owner=frame_data[0:owner_end].decode('iso-8859-1'), + data=frame_data[owner_end + 1:], + ) elif frame_type is ID3v2UnsynchronizedLyricsFrame: encoding = determine_encoding(frame_data[0:1])