Support WebP cover images: convert to JPEG via PIL, correct MIME type fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b2dd0df052
commit
f86db982a5
1 changed files with 29 additions and 2 deletions
31
cover_handler.py
Normal file → Executable file
31
cover_handler.py
Normal file → Executable file
|
|
@ -97,12 +97,38 @@ def download_cover(release_mbid: Optional[str], dest_dir: Path) -> Optional[Path
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _load_cover_data(cover_path: Path) -> tuple[bytes, str]:
|
||||||
|
"""
|
||||||
|
Liest Cover-Bilddaten und gibt (bytes, mime_type) zurück.
|
||||||
|
WebP wird zu JPEG konvertiert wenn PIL verfügbar (bessere Player-Kompatibilität).
|
||||||
|
"""
|
||||||
|
suffix = cover_path.suffix.lower()
|
||||||
|
if suffix in (".jpg", ".jpeg"):
|
||||||
|
return cover_path.read_bytes(), "image/jpeg"
|
||||||
|
if suffix == ".webp" and HAS_PIL:
|
||||||
|
try:
|
||||||
|
with Image.open(cover_path) as img:
|
||||||
|
img = img.convert("RGB")
|
||||||
|
buf = tempfile.SpooledTemporaryFile(max_size=10 * 1024 * 1024)
|
||||||
|
img.save(buf, format="JPEG", quality=90)
|
||||||
|
buf.seek(0)
|
||||||
|
return buf.read(), "image/jpeg"
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ WebP→JPEG-Konvertierung fehlgeschlagen ({cover_path.name}): {e}",
|
||||||
|
file=sys.stderr)
|
||||||
|
if suffix == ".webp":
|
||||||
|
return cover_path.read_bytes(), "image/webp"
|
||||||
|
if suffix == ".png":
|
||||||
|
return cover_path.read_bytes(), "image/png"
|
||||||
|
# Fallback: raw bytes, JPEG assumed
|
||||||
|
return cover_path.read_bytes(), "image/jpeg"
|
||||||
|
|
||||||
|
|
||||||
def embed_cover(audio_path: Path, cover_path: Path) -> bool:
|
def embed_cover(audio_path: Path, cover_path: Path) -> bool:
|
||||||
if not HAS_MUTAGEN:
|
if not HAS_MUTAGEN:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
img_data = cover_path.read_bytes()
|
img_data, mime = _load_cover_data(cover_path)
|
||||||
mime = "image/jpeg" if cover_path.suffix.lower() in (".jpg", ".jpeg") else "image/png"
|
|
||||||
ext = audio_path.suffix.lower()
|
ext = audio_path.suffix.lower()
|
||||||
|
|
||||||
if ext == ".mp3":
|
if ext == ".mp3":
|
||||||
|
|
@ -130,6 +156,7 @@ def embed_cover(audio_path: Path, cover_path: Path) -> bool:
|
||||||
elif ext == ".m4a":
|
elif ext == ".m4a":
|
||||||
audio = MP4(str(audio_path))
|
audio = MP4(str(audio_path))
|
||||||
fmt = MP4Cover.FORMAT_JPEG if mime == "image/jpeg" else MP4Cover.FORMAT_PNG
|
fmt = MP4Cover.FORMAT_JPEG if mime == "image/jpeg" else MP4Cover.FORMAT_PNG
|
||||||
|
# WebP wurde bereits zu JPEG konvertiert, mime ist dann "image/jpeg"
|
||||||
audio.tags["covr"] = [MP4Cover(img_data, imageformat=fmt)]
|
audio.tags["covr"] = [MP4Cover(img_data, imageformat=fmt)]
|
||||||
audio.save()
|
audio.save()
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue