From dfa9ab6529d79078920067b0bd494fa19a2a85a5 Mon Sep 17 00:00:00 2001 From: The Wobbler Date: Sat, 15 Mar 2025 17:52:41 +0100 Subject: [PATCH] Added tests and did improved class and file structure. --- smalltag/_formats/_id3.py | 12 +++---- smalltag/smalltag.py | 67 +++++++++++++++++++++++++++++--------- tests/formats/test.mp3 | Bin 0 -> 7452 bytes tests/formats/test_id3.py | 30 +++++++++++++++++ tests/id3.py | 17 ---------- tests/test.mp3 | Bin 7401 -> 0 bytes 6 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 tests/formats/test.mp3 create mode 100644 tests/formats/test_id3.py delete mode 100644 tests/id3.py delete mode 100644 tests/test.mp3 diff --git a/smalltag/_formats/_id3.py b/smalltag/_formats/_id3.py index e38767b..831bf44 100644 --- a/smalltag/_formats/_id3.py +++ b/smalltag/_formats/_id3.py @@ -1,19 +1,18 @@ #!/usr/bin/python3 +from __future__ import annotations from struct import pack -from tinytag import TinyTag +from io import BytesIO +from tinytag import TinyTag, tinytag from .. import SmallTag -if True: +if False: from collections.abc import Callable, Iterator # noqa from typing import Any, BinaryIO, Dict, List -_tinytag_id3 = TinyTag._get_parser_for_filename(".mp3") # noqa - - -class _ID3(SmallTag, _tinytag_id3): +class _ID3(SmallTag, tinytag._ID3): @staticmethod def _synchsafe(unsynchsafe_int: int) -> tuple[int, ...]: ints = ( @@ -41,7 +40,6 @@ class _ID3(SmallTag, _tinytag_id3): new_header = self._compose_id3v2_header(size) new_tag = new_header + new_frames - print(new_tag) size, extended, major = self._parse_id3v2_header(self._filehandler) diff --git a/smalltag/smalltag.py b/smalltag/smalltag.py index 7ff0c04..0d9f846 100644 --- a/smalltag/smalltag.py +++ b/smalltag/smalltag.py @@ -1,15 +1,29 @@ #!/usr/bin/python3 from __future__ import annotations -from tinytag import TinyTag, UnsupportedFormatError +from tinytag import tinytag if False: # just stole this lazy import type hinting trick from tinytag from collections.abc import Callable, Iterator # noqa from typing import Any, BinaryIO, Dict, List +TinyTag = tinytag.TinyTag +UnsupportedFormatError = tinytag.UnsupportedFormatError + class SmallTag(TinyTag): + _file_extension_mapping: dict[tuple[str, ...], type[SmallTag]]= { + (".mp1", ".mp2", ".mp3"): None, + ('.oga', '.ogg', '.opus', '.spx'): tinytag._Ogg, + ('.wav',): tinytag._Wave, + ('.flac',): tinytag._Flac, + ('.wma',): tinytag._Wma, + ('.m4b', '.m4a', '.m4r', '.m4v', '.mp4', + '.aax', '.aaxc'): tinytag._MP4, + ('.aiff', '.aifc', '.aif', '.afc'): tinytag._Aiff, + } + @classmethod def _get_parser_class( cls, @@ -30,6 +44,37 @@ class SmallTag(TinyTag): raise UnsupportedFormatError( 'No tag reader found to support file type') + @classmethod + def _get_parser_for_file_handle( + cls, + filehandle: BinaryIO + ) -> type[SmallTag] | None: + # https://en.wikipedia.org/wiki/List_of_file_signatures + header = filehandle.read(35) + filehandle.seek(0) + if header.startswith(b'ID3') or header.startswith(b'\xff\xfb'): + return _ID3 + + super()._get_parser_for_file_handle(filehandle) + # if header.startswith(b'fLaC'): + # return _Flac + # if ((header[4:8] == b'ftyp' + # and header[8:11] in {b'M4A', b'M4B', b'aax'}) + # or b'\xff\xf1' in header): + # return _MP4 + # if (header.startswith(b'OggS') + # and (header[29:33] == b'FLAC' or header[29:35] == b'vorbis' + # or header[28:32] == b'Opus' or header[29:34] == b'Speex')): + # return _Ogg + # if header.startswith(b'RIFF') and header[8:12] == b'WAVE': + # return _Wave + # if header.startswith(b'\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00' + # b'\xAA\x00\x62\xCE\x6C'): + # return _Wma + # if header.startswith(b'FORM') and header[8:12] in {b'AIFF', b'AIFC'}: + # return _Aiff + # return None + def write(self, file_obj: BinaryIO = None): raise NotImplementedError @@ -42,14 +87,14 @@ class SmallTag(TinyTag): "(Has to be from the same file as specified while creating this TinyTag instance.)" ) - if file_obj is not None and not self._filehandler.closed(): + if file_obj is not None and not self._filehandler.closed: self._filehandler.close() - if file_obj is not None: - self._filehandler = file_obj + if file_obj is None: + self._filehandler = open(self.filename, "rb+") else: - self._filehandler = open(self.filename, "rb+") + self._filehandler = file_obj return should_close_file @@ -58,13 +103,5 @@ class SmallTag(TinyTag): # (this solution is stupid, but I don't know anything better.) from ._formats import _ID3 # noqa -SmallTag._file_extension_mapping = { - ('.mp1', '.mp2', '.mp3'): _ID3, - # ('.oga', '.ogg', '.opus', '.spx'): _Ogg, (Not yet implemented.) - # ('.wav',): _Wave, - # ('.flac',): _Flac, - # ('.wma',): _Wma, - # ('.m4b', '.m4a', '.m4r', '.m4v', '.mp4', - # '.aax', '.aaxc'): _MP4, - # ('.aiff', '.aifc', '.aif', '.afc'): _Aiff, -} +SmallTag._file_extension_mapping[(".mp1", ".mp2", ".mp3")] = _ID3 + diff --git a/tests/formats/test.mp3 b/tests/formats/test.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..41fb2a9b3b012aac5b9a61ce7a4229ae7cd8e31b GIT binary patch literal 7452 zcmeZtF=k-^0n3m8S3@Ap3B(EtA*sbBK+@6230=rD#0X93|L+MYKq0&!A~P=?sDK}c zof#NpZZa^lb93_x3yVof$tfwRX=&*i8k$;K+B!J6czE~(1cZc#N5{t}r>AG-dl*XA3l8g^5vTk zAHMwf@#jC#t^glLUsq#2LjwbroiO*ADyXo|@MU0p0CERg*rum2iD66s|1FTh!2f}P zArt6D4h9BZ1qKF2AX)Np_4s#`|+o1M8d+sA~L;=+JhE;qv3|N97PKyOzvp7jfNX2Rg9*C(R47H z4sfgpO>TRVGuCr+I@W6qof zOO~uyy?XuT&D(eH-hb%Ou~VncUAlDj=FPhgA3lBg^38`2Uw-`f^B-tefRCfEtFfM; zfdR`-n8QpJR9I*DGB7>>xq~fi(^Htlu%-Y17D!>>|G>bI3G^Zd0|T!D0|O%vEdg?Y zOp^u%1{Qcgh2j7N6@-R@>`@|M4-V52!bIBRLMMCD=wwd^o$OgfCwtD&$)3CY_|r8a zVc}pAnchb2K?}dpaKl=TqJBza0@p8+?TnH