Cover-Embedding: Auflösung auf 500px reduzieren vor dem Einbetten

Neue Hilfsfunktion _scale_cover_for_embed() skaliert das Coverbild
mit Pillow auf max. 500px (EMBED_COVER_MAX_SIZE) und kodiert es
als JPEG quality=85 in-memory. embed_cover() liest nicht mehr die
rohen Bytes der Originaldatei, sondern nutzt das skalierte Bild.

Ergebnis: eingebettete Cover ~40–100 KB statt 200–500 KB des
1200px-Originals, auf HiDPI-Displays noch scharf erkennbar.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-18 00:40:11 +01:00
commit 256be0ae33

View file

@ -2,12 +2,14 @@
from __future__ import annotations from __future__ import annotations
import io
import logging import logging
from pathlib import Path from pathlib import Path
from mutagen import File as MutagenFile from mutagen import File as MutagenFile
from mutagen.flac import FLAC, Picture from mutagen.flac import FLAC, Picture
from mutagen.id3 import APIC, ID3 from mutagen.id3 import APIC, ID3
from PIL import Image
from musiksammlung.models import Album, Disc, Track from musiksammlung.models import Album, Disc, Track
@ -75,11 +77,31 @@ def embed_album_cover(album: Album, album_dir: Path, cover_path: Path) -> None:
embed_cover(audio_file, cover_path) embed_cover(audio_file, cover_path)
def embed_cover(audio_path: Path, cover_path: Path) -> None: # Maximale Auflösung für eingebettete Cover (längste Seite in Pixeln).
"""Bettet ein Cover-Bild in eine Audiodatei ein.""" # Kleiner als das gespeicherte Originalbild (1200 px), aber auf HiDPI-Displays
cover_data = cover_path.read_bytes() # noch scharf genug. Typische Dateigröße: 40100 KB statt 200500 KB.
mime = "image/jpeg" if cover_path.suffix.lower() in (".jpg", ".jpeg") else "image/png" EMBED_COVER_MAX_SIZE = 500
def _scale_cover_for_embed(cover_path: Path, max_size: int) -> tuple[bytes, str]:
"""Skaliert ein Coverbild für das Einbetten und gibt (bytes, mime) zurück."""
img = Image.open(cover_path)
if max(img.size) > max_size:
img.thumbnail((max_size, max_size), Image.LANCZOS)
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
buf = io.BytesIO()
img.save(buf, "JPEG", quality=85)
return buf.getvalue(), "image/jpeg"
def embed_cover(audio_path: Path, cover_path: Path) -> None:
"""Bettet ein Cover-Bild in eine Audiodatei ein.
Das Bild wird auf EMBED_COVER_MAX_SIZE herunterskaliert, damit die
eingebetteten Cover die Audiodateigröße nicht wesentlich vergrößern.
"""
cover_data, mime = _scale_cover_for_embed(cover_path, EMBED_COVER_MAX_SIZE)
suffix = audio_path.suffix.lower() suffix = audio_path.suffix.lower()
if suffix == ".flac": if suffix == ".flac":