Normalize all cover art to folder.jpg (Jellyfin standard)
- Add normalize_cover_to_folder_jpg(): renames/converts any local cover (Front.jpg, front.webp, cover.jpg, …) to folder.jpg in-place; WebP/PNG are converted to JPEG via PIL - resolve_cover() now calls normalize_cover_to_folder_jpg() automatically after finding a local cover, so future enrichment runs always produce folder.jpg - One-time batch: 38 existing library covers renamed/converted to folder.jpg Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7516de439f
commit
d06c0bbcc9
1 changed files with 36 additions and 1 deletions
|
|
@ -48,7 +48,6 @@ def _image_ok(path: Path) -> bool:
|
||||||
|
|
||||||
def find_local_cover(image_files: List[Path]) -> Optional[Path]:
|
def find_local_cover(image_files: List[Path]) -> Optional[Path]:
|
||||||
priority = ("folder", "front", "cover", "album")
|
priority = ("folder", "front", "cover", "album")
|
||||||
# Sort by priority keyword, then size descending
|
|
||||||
def key(p: Path):
|
def key(p: Path):
|
||||||
name = p.name.lower()
|
name = p.name.lower()
|
||||||
score = next((i for i, kw in enumerate(priority) if kw in name), len(priority))
|
score = next((i for i, kw in enumerate(priority) if kw in name), len(priority))
|
||||||
|
|
@ -61,6 +60,41 @@ def find_local_cover(image_files: List[Path]) -> Optional[Path]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_cover_to_folder_jpg(cover_path: Path) -> Path:
|
||||||
|
"""
|
||||||
|
Stellt sicher dass das Cover als folder.jpg (JPEG) im Album-Verzeichnis liegt.
|
||||||
|
- Ist es bereits folder.jpg → unverändert zurückgeben.
|
||||||
|
- Ist es eine andere JPEG → umbenennen.
|
||||||
|
- Ist es WebP oder PNG → zu JPEG konvertieren, Original löschen.
|
||||||
|
Gibt den Pfad zur folder.jpg zurück.
|
||||||
|
"""
|
||||||
|
dest = cover_path.parent / "folder.jpg"
|
||||||
|
if cover_path.resolve() == dest.resolve():
|
||||||
|
return dest
|
||||||
|
|
||||||
|
suffix = cover_path.suffix.lower()
|
||||||
|
try:
|
||||||
|
if suffix in (".jpg", ".jpeg"):
|
||||||
|
cover_path.rename(dest)
|
||||||
|
elif HAS_PIL:
|
||||||
|
import io
|
||||||
|
with cover_path.open("rb") as f:
|
||||||
|
raw = f.read()
|
||||||
|
with Image.open(io.BytesIO(raw)) as img:
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.convert("RGB").save(buf, format="JPEG", quality=92)
|
||||||
|
dest.write_bytes(buf.getvalue())
|
||||||
|
cover_path.unlink()
|
||||||
|
else:
|
||||||
|
# PIL nicht verfügbar: einfach umbenennen, auch wenn es kein JPEG ist
|
||||||
|
cover_path.rename(dest)
|
||||||
|
print(f" 🖼️ Cover normalisiert → folder.jpg ({cover_path.name})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Cover-Normalisierung fehlgeschlagen: {e}", file=sys.stderr)
|
||||||
|
return cover_path
|
||||||
|
return dest
|
||||||
|
|
||||||
|
|
||||||
def _mb_cover_url(release_mbid: str) -> Optional[str]:
|
def _mb_cover_url(release_mbid: str) -> Optional[str]:
|
||||||
url = f"https://coverartarchive.org/release/{release_mbid}/front"
|
url = f"https://coverartarchive.org/release/{release_mbid}/front"
|
||||||
if not HAS_REQUESTS:
|
if not HAS_REQUESTS:
|
||||||
|
|
@ -195,6 +229,7 @@ def resolve_cover(
|
||||||
"""Returns (cover_path, source_label)."""
|
"""Returns (cover_path, source_label)."""
|
||||||
local = find_local_cover(image_files)
|
local = find_local_cover(image_files)
|
||||||
if local:
|
if local:
|
||||||
|
local = normalize_cover_to_folder_jpg(local)
|
||||||
return local, "local"
|
return local, "local"
|
||||||
|
|
||||||
if release_mbid:
|
if release_mbid:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue