Added tests and did improved class and file structure.

This commit is contained in:
The Wobbler 2025-03-15 17:52:41 +01:00
parent c6df2441f8
commit dfa9ab6529
6 changed files with 87 additions and 39 deletions

View file

@ -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)

View file

@ -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

BIN
tests/formats/test.mp3 Normal file

Binary file not shown.

30
tests/formats/test_id3.py Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/python3
from smalltag import SmallTag
# chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ "
# no "b" because there is a bug in TinyTag 2.1.0 that removes "b"s if they are at the beginning of a string
chars = "acdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789- _"
def test_writing_text_frames():
for char1 in chars:
for char2 in chars:
field_content = f"{char1}{char2}Test{char2}{char1}"
tag = SmallTag.get("formats/test.mp3")
tag.title = field_content
tag.artist = field_content
tag.album = field_content
tag.write()
tag = SmallTag.get("formats/test.mp3")
assert tag.title == field_content
assert tag.artist == field_content
assert tag.album == field_content

View file

@ -1,17 +0,0 @@
#!/usr/bin/python3
from json import dumps
from smalltag import SmallTag
test_tag = SmallTag.get("test.mp3")
print(test_tag)
print(test_tag.title)
test_tag.title = "bTest"
test_tag.write()
test_tag = SmallTag.get("test.mp3")
print(dumps(test_tag.as_dict(), indent=" "))

Binary file not shown.