0ea5f10b31
Ampel / ampel (push) Failing after 12m31s
- Metadaten-Preview WIEDERHERGESTELLT (Stub ueberschattete echte prescan-Implementierung), tote Altmodule geloescht - Celery update_state statt Phantom-Task/erfundener API - abcde-Kommando korrigiert (CD-Ripping war nie funktionsfaehig) - JWT: fester Schluessel Pflicht, echtes Logout, Cleanup nur Abgelaufene - main.py: crashende Endpoints (Path/secrets/api_keys), year-Bug, Admin-Login aus .env - Ruff gruen (29 Funde), Tests: auth/cache_keys/ripping_helpers, Placebo-test_health raus - SAVEPOINT: offene MakeMKV-Entscheidung SICHTBAR gemacht (Regel B)
111 lines
3.9 KiB
Python
111 lines
3.9 KiB
Python
"""Jellyfin Image Downloader."""
|
|
|
|
import requests
|
|
from pathlib import Path
|
|
from typing import Dict, Optional
|
|
|
|
from clients.tmdb import TMDBClient
|
|
from clients.thetvdb import TheTVDBClient
|
|
|
|
|
|
class ImageDownloader:
|
|
def __init__(self):
|
|
self.tmdb = TMDBClient()
|
|
self.thetvdb = TheTVDBClient()
|
|
self.base_url = "https://image.tmdb.org/t/p"
|
|
|
|
def download_image(self, url: str, output_path: Path, width: int = 500) -> bool:
|
|
"""Lade Image herunter."""
|
|
try:
|
|
# TMDB URL anpassen
|
|
if url.startswith("https://image.tmdb.org"):
|
|
# Konvertiere zu gewünschter Größe
|
|
path = url.replace(f"{self.base_url}/", "")
|
|
url = f"{self.base_url}/w{width}/{path}"
|
|
|
|
response = requests.get(url, timeout=30)
|
|
response.raise_for_status()
|
|
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(output_path, 'wb') as f:
|
|
f.write(response.content)
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"Image download error: {e}")
|
|
return False
|
|
|
|
def download_poster(self, title: str, output_dir: Path, width: int = 500) -> Optional[Path]:
|
|
"""Lade Poster herunter."""
|
|
# TMDB Search
|
|
movies = self.tmdb.search_movie(title)
|
|
if movies:
|
|
movie = movies[0]
|
|
images = self.tmdb.get_movie_images(movie["id"])
|
|
|
|
if images.get("poster"):
|
|
output_path = output_dir / "poster.jpg"
|
|
if self.download_image(images["poster"], output_path, width):
|
|
return output_path
|
|
|
|
return None
|
|
|
|
def download_fanart(self, title: str, output_dir: Path, width: int = 1920) -> Optional[Path]:
|
|
"""Lade Fanart herunter."""
|
|
# TMDB Search
|
|
movies = self.tmdb.search_movie(title)
|
|
if movies:
|
|
movie = movies[0]
|
|
images = self.tmdb.get_movie_images(movie["id"])
|
|
|
|
if images.get("fanart"):
|
|
output_path = output_dir / "fanart.jpg"
|
|
if self.download_image(images["fanart"], output_path, width):
|
|
return output_path
|
|
|
|
return None
|
|
|
|
def download_series_images(self, title: str, output_dir: Path) -> Dict[str, Optional[Path]]:
|
|
"""Lade Serien-Poster und Fanart herunter."""
|
|
result = {
|
|
"poster": None,
|
|
"fanart": None
|
|
}
|
|
|
|
tv_shows = self.tmdb.search_tv(title)
|
|
if tv_shows:
|
|
tv = tv_shows[0]
|
|
images = self.tmdb.get_tv_images(tv["id"])
|
|
|
|
if images.get("poster"):
|
|
output_path = output_dir / "poster.jpg"
|
|
if self.download_image(images["poster"], output_path, 500):
|
|
result["poster"] = output_path
|
|
|
|
if images.get("fanart"):
|
|
output_path = output_dir / "fanart.jpg"
|
|
if self.download_image(images["fanart"], output_path, 1920):
|
|
result["fanart"] = output_path
|
|
|
|
return result
|
|
|
|
def download_music_images(self, artist: str, album: str, output_dir: Path) -> Dict[str, Optional[Path]]:
|
|
"""Lade Musik-Album-Cover herunter (via TMDB als Fallback)."""
|
|
result = {
|
|
"album": None
|
|
}
|
|
|
|
# TMDB Search für Soundtracks
|
|
query = f"{album} soundtrack"
|
|
movies = self.tmdb.search_movie(query)
|
|
if movies:
|
|
movie = movies[0]
|
|
images = self.tmdb.get_movie_images(movie["id"])
|
|
|
|
if images.get("poster"):
|
|
output_path = output_dir / "album.jpg"
|
|
if self.download_image(images["poster"], output_path, 500):
|
|
result["album"] = output_path
|
|
|
|
return result
|