518155051f
- SQLite-Cache für API-Rate-Limits (LRU, 10k Einträge) - TMDB/MusicBrainz/TheTVDB Clients - Pre-Scan-Modul für TOC-Lesung ohne Ripping - Metadaten-Preview UI - Jellyfin-Formatierung (NFO + Images) - API Endpoints für Lookup, Confirm, Format
113 lines
3.9 KiB
Python
113 lines
3.9 KiB
Python
"""Jellyfin Image Downloader."""
|
|
|
|
import requests
|
|
from pathlib import Path
|
|
from typing import Dict, Optional
|
|
from urllib.parse import urlparse
|
|
|
|
from .clients.tmdb import TMDBClient
|
|
from .clients.thetvdb import TheTVDBClient
|
|
from .config import settings
|
|
|
|
|
|
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
|