110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Cover-Optimierungs-Tool
|
||
Repariert alle Cover-Bilder im _audio-Verzeichnis für Apple Podcasts Kompatibilität.
|
||
"""
|
||
|
||
from pathlib import Path
|
||
import urllib.parse
|
||
|
||
def fix_all_covers(audio_dir="../httpdocs/_audio"):
|
||
"""Optimiert alle Cover-Dateien im Audio-Verzeichnis."""
|
||
try:
|
||
from PIL import Image
|
||
except ImportError:
|
||
print("❌ PIL/Pillow nicht installiert!")
|
||
print("💡 Installiere mit: uv add pillow")
|
||
return False
|
||
|
||
audio_path = Path(audio_dir)
|
||
if not audio_path.exists():
|
||
print(f"❌ Audio-Verzeichnis '{audio_dir}' existiert nicht!")
|
||
return False
|
||
|
||
# Finde alle Cover-Dateien
|
||
cover_files = list(audio_path.glob("cover_*.jpg"))
|
||
print(f"🖼️ {len(cover_files)} Cover-Dateien gefunden")
|
||
|
||
if not cover_files:
|
||
print("ℹ️ Keine Cover-Dateien zum Optimieren gefunden")
|
||
return True
|
||
|
||
fixed_count = 0
|
||
|
||
for cover_file in cover_files:
|
||
try:
|
||
print(f"\n📋 Prüfe: {cover_file.name}")
|
||
|
||
# Lade das Bild
|
||
image = Image.open(cover_file)
|
||
original_width, original_height = image.size
|
||
print(f" 📐 Aktuelle Größe: {original_width}x{original_height}px")
|
||
|
||
# Prüfe ob Optimierung nötig ist
|
||
needs_fix = False
|
||
|
||
if original_width < 1400 or original_height < 1400:
|
||
print(f" ⚠️ Zu klein (< 1400px)")
|
||
needs_fix = True
|
||
elif original_width > 3000 or original_height > 3000:
|
||
print(f" ⚠️ Zu groß (> 3000px)")
|
||
needs_fix = True
|
||
elif original_width != original_height:
|
||
print(f" ⚠️ Nicht quadratisch")
|
||
needs_fix = True
|
||
|
||
if not needs_fix:
|
||
print(f" ✅ Cover ist bereits optimal")
|
||
continue
|
||
|
||
# Bestimme Zielgröße
|
||
if original_width < 1400 or original_height < 1400:
|
||
target_size = 1400
|
||
elif original_width > 3000 or original_height > 3000:
|
||
target_size = 3000
|
||
else:
|
||
target_size = min(original_width, original_height)
|
||
if target_size < 1400:
|
||
target_size = 1400
|
||
elif target_size > 3000:
|
||
target_size = 3000
|
||
|
||
print(f" 🎯 Zielgröße: {target_size}x{target_size}px")
|
||
|
||
# Mache quadratisch falls nötig
|
||
if original_width != original_height:
|
||
size = min(original_width, original_height)
|
||
left = (original_width - size) // 2
|
||
top = (original_height - size) // 2
|
||
right = left + size
|
||
bottom = top + size
|
||
image = image.crop((left, top, right, bottom))
|
||
print(f" ✂️ Zugeschnitten auf quadratisch")
|
||
|
||
# Skaliere auf Zielgröße
|
||
image = image.resize((target_size, target_size), Image.Resampling.LANCZOS)
|
||
|
||
# Konvertiere zu RGB falls nötig
|
||
if image.mode in ('RGBA', 'LA', 'P'):
|
||
background = Image.new('RGB', image.size, (255, 255, 255))
|
||
if image.mode == 'P':
|
||
image = image.convert('RGBA')
|
||
background.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
|
||
image = background
|
||
|
||
# Speichere optimiertes Cover
|
||
image.save(cover_file, 'JPEG', quality=95, optimize=True)
|
||
final_width, final_height = image.size
|
||
print(f" ✅ Optimiert: {final_width}x{final_height}px")
|
||
|
||
fixed_count += 1
|
||
|
||
except Exception as e:
|
||
print(f" ❌ Fehler bei {cover_file.name}: {e}")
|
||
|
||
print(f"\n🎉 {fixed_count} von {len(cover_files)} Cover-Dateien optimiert!")
|
||
return True
|
||
|
||
if __name__ == "__main__":
|
||
fix_all_covers()
|