Opus/M4A-Cover-Embedding, cover.py-Tests und OCR-Tests

- tagger.py: embed_cover() unterstützt jetzt .opus (Vorbis-Comment
  METADATA_BLOCK_PICTURE) und .m4a (MP4Cover); imports ergänzt
- test_tagger.py: 2 neue Tests für Opus/M4A; minimale Audio-Fixtures
  als base64-Konstanten (176 B Opus, 856 B M4A)
- test_cover.py: TestPrepareCover (5 Tests) und TestCopyCovers (6 Tests)
  für prepare_cover() und copy_covers()
- test_ocr.py: 13 Tests für run_ocr(), _detect_and_fix_rotation()
  und ocr_images(); Tesseract via subprocess.run gemockt

144 Tests, 0 Fehler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-18 04:50:13 +01:00
commit 795be8609a
4 changed files with 343 additions and 1 deletions

View file

@ -2,6 +2,7 @@
from __future__ import annotations
import base64
import io
import logging
from pathlib import Path
@ -9,6 +10,8 @@ from pathlib import Path
from mutagen import File as MutagenFile
from mutagen.flac import FLAC, Picture
from mutagen.id3 import APIC, ID3, ID3NoHeaderError
from mutagen.mp4 import MP4, MP4Cover
from mutagen.oggopus import OggOpus
from PIL import Image
from musiksammlung.models import Album, Disc, Track
@ -127,6 +130,24 @@ def embed_cover(audio_path: Path, cover_path: Path) -> None:
))
audio.save(str(audio_path))
elif suffix == ".opus":
audio = OggOpus(str(audio_path))
pic = Picture()
pic.type = 3 # Front cover
pic.mime = mime
pic.data = cover_data
pic.width = 0
pic.height = 0
pic.depth = 0
pic.colors = 0
audio["metadata_block_picture"] = [base64.b64encode(pic.write()).decode("ascii")]
audio.save()
elif suffix == ".m4a":
audio = MP4(str(audio_path))
audio["covr"] = [MP4Cover(cover_data, imageformat=MP4Cover.FORMAT_JPEG)]
audio.save()
else:
logger.debug("Cover-Embedding für %s nicht unterstützt", suffix)
return