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
170 lines
6.8 KiB
Python
170 lines
6.8 KiB
Python
"""NFO-Generator für Jellyfin (Kodi/NFO-Schema)."""
|
|
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
from xml.dom.minidom import getDOMImplementation
|
|
|
|
|
|
class NFOGenerator:
|
|
def __init__(self):
|
|
self.dom_impl = getDOMImplementation()
|
|
|
|
def _create_element(self, doc, name: str, text: str = None) -> None:
|
|
"""Hilfsfunktion für Element-Erstellung."""
|
|
element = doc.createElement(name)
|
|
if text:
|
|
element.appendChild(doc.createTextNode(str(text)))
|
|
return element
|
|
|
|
def generate_movie_nfo(self, title: str, year: int,
|
|
overview: str = "", rating: float = 0.0,
|
|
runtime: int = 0, genres: List[str] = None,
|
|
director: str = "", writer: str = "",
|
|
actors: List[str] = None, studio: str = "",
|
|
premiered: str = "", mpaa: str = "") -> str:
|
|
"""Generiere movie.nfo für Filme."""
|
|
doc = self.dom_impl.createDocument(None, "movie", None)
|
|
root = doc.documentElement
|
|
|
|
root.appendChild(self._create_element(doc, "title", title))
|
|
root.appendChild(self._create_element(doc, "year", year))
|
|
root.appendChild(self._create_element(doc, "plot", overview))
|
|
root.appendChild(self._create_element(doc, "rating", rating))
|
|
root.appendChild(self._create_element(doc, "runtime", runtime))
|
|
|
|
if genres:
|
|
for genre in genres:
|
|
root.appendChild(self._create_element(doc, "genre", genre))
|
|
|
|
if director:
|
|
root.appendChild(self._create_element(doc, "director", director))
|
|
|
|
if writer:
|
|
root.appendChild(self._create_element(doc, "writer", writer))
|
|
|
|
if actors:
|
|
for actor in actors:
|
|
actor_node = doc.createElement("actor")
|
|
actor_node.appendChild(self._create_element(doc, "name", actor))
|
|
root.appendChild(actor_node)
|
|
|
|
if studio:
|
|
root.appendChild(self._create_element(doc, "studio", studio))
|
|
|
|
if premiered:
|
|
root.appendChild(self._create_element(doc, "premiered", premiered))
|
|
|
|
if mpaa:
|
|
root.appendChild(self._create_element(doc, "mpaa", mpaa))
|
|
|
|
# Attribution
|
|
root.appendChild(self._create_element(doc, "details", "Source: TMDB"))
|
|
|
|
return doc.toprettyxml(indent=" ")
|
|
|
|
def generate_series_nfo(self, title: str, year: int,
|
|
overview: str = "", rating: float = 0.0,
|
|
genres: List[str] = None, studio: str = "",
|
|
premiered: str = "") -> str:
|
|
"""Generiere series.nfo für Serien."""
|
|
doc = self.dom_impl.createDocument(None, "tvshow", None)
|
|
root = doc.documentElement
|
|
|
|
root.appendChild(self._create_element(doc, "title", title))
|
|
root.appendChild(self._create_element(doc, "year", year))
|
|
root.appendChild(self._create_element(doc, "plot", overview))
|
|
root.appendChild(self._create_element(doc, "rating", rating))
|
|
|
|
if genres:
|
|
for genre in genres:
|
|
root.appendChild(self._create_element(doc, "genre", genre))
|
|
|
|
if studio:
|
|
root.appendChild(self._create_element(doc, "studio", studio))
|
|
|
|
if premiered:
|
|
root.appendChild(self._create_element(doc, "premiered", premiered))
|
|
|
|
# Attribution
|
|
root.appendChild(self._create_element(doc, "details", "Source: TMDB"))
|
|
|
|
return doc.toprettyxml(indent=" ")
|
|
|
|
def generate_episode_nfo(self, title: str, season: int, episode: int,
|
|
overview: str = "", rating: float = 0.0,
|
|
director: str = "", premiered: str = "",
|
|
writers: List[str] = None) -> str:
|
|
"""Generiere episode.nfo für Episoden."""
|
|
doc = self.dom_impl.createDocument(None, "episodedetails", None)
|
|
root = doc.documentElement
|
|
|
|
root.appendChild(self._create_element(doc, "title", title))
|
|
root.appendChild(self._create_element(doc, "season", season))
|
|
root.appendChild(self._create_element(doc, "episode", episode))
|
|
root.appendChild(self._create_element(doc, "plot", overview))
|
|
root.appendChild(self._create_element(doc, "rating", rating))
|
|
|
|
if director:
|
|
root.appendChild(self._create_element(doc, "director", director))
|
|
|
|
if premiered:
|
|
root.appendChild(self._create_element(doc, "premiered", premiered))
|
|
|
|
if writers:
|
|
for writer in writers:
|
|
root.appendChild(self._create_element(doc, "credits", writer))
|
|
|
|
return doc.toprettyxml(indent=" ")
|
|
|
|
def generate_album_nfo(self, title: str, artist: str, year: int,
|
|
genres: List[str] = None, rating: float = 0.0,
|
|
review: str = "") -> str:
|
|
"""Generiere album.nfo für Musikalben."""
|
|
doc = self.dom_impl.createDocument(None, "musicalbum", None)
|
|
root = doc.documentElement
|
|
|
|
root.appendChild(self._create_element(doc, "title", title))
|
|
root.appendChild(self._create_element(doc, "artist", artist))
|
|
root.appendChild(self._create_element(doc, "year", year))
|
|
root.appendChild(self._create_element(doc, "rating", rating))
|
|
root.appendChild(self._create_element(doc, "review", review))
|
|
|
|
if genres:
|
|
for genre in genres:
|
|
root.appendChild(self._create_element(doc, "genre", genre))
|
|
|
|
# Attribution
|
|
root.appendChild(self._create_element(doc, "details", "Source: MusicBrainz"))
|
|
|
|
return doc.toprettyxml(indent=" ")
|
|
|
|
def save_nfo(self, content: str, output_path: Path) -> bool:
|
|
"""Speichere NFO-Datei."""
|
|
try:
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
f.write(content)
|
|
return True
|
|
except Exception as e:
|
|
print(f"NFO save error: {e}")
|
|
return False
|
|
|
|
|
|
# Beispieldaten
|
|
if __name__ == "__main__":
|
|
nfo_gen = NFOGenerator()
|
|
|
|
# movie.nfo
|
|
movie_nfo = nfo_gen.generate_movie_nfo(
|
|
title="Inception",
|
|
year=2010,
|
|
overview="A thief who steals corporate secrets through the use of dream-sharing technology is given the inverse task of planting an idea into the mind of a C.E.O.",
|
|
rating=8.8,
|
|
runtime=148,
|
|
genres=["Action", "Sci-Fi", "Thriller"],
|
|
director="Christopher Nolan",
|
|
actors=["Leonardo DiCaprio", "Joseph Gordon-Levitt", "Ellen Page"]
|
|
)
|
|
|
|
print(movie_nfo)
|