Unify movies and series list, add TMDb-sourced tags and detail panel

This commit is contained in:
2026-09-09 07:13:36 +02:00
parent 79c9e34a5d
commit c0ad1c1553
5 changed files with 376 additions and 108 deletions
+41
View File
@@ -826,6 +826,47 @@ class FileManager:
self.on_files_changed(self.filelist)
return file_obj
def refresh_tags_from_tmdb(
self, files: list[File] | None = None,
on_progress: Callable[[int, int, File], None] | None = None,
should_cancel: Callable[[], bool] | None = None,
) -> dict:
"""(Re)derive each movie's tags from **TMDb** (by the tmdbid in its name).
Fetches genres / year / country / rating from TMDb per the tag schema and
regenerates the auto tags (`File.apply_nfo`, which is source-agnostic);
user-added tags are kept. Movies with no tmdbid in the filename are skipped.
Returns ``{ok, updated, without_id, errors}``.
"""
from . import tmdb
if not tmdb.api_available():
return {"ok": False, "error": "TMDB_API_KEY není nastaven (.env).",
"updated": 0, "without_id": 0, "errors": []}
targets = files if files is not None else self.filelist
schema = self.tag_schema
updated, without_id = 0, 0
errors: list[str] = []
total = len(targets)
for i, f in enumerate(targets, 1):
if should_cancel is not None and should_cancel():
break
tmdb_id = parse_tmdb_id(f.file_path.stem)
if tmdb_id is None:
without_id += 1
else:
try:
f.apply_nfo(tmdb.movie_details(tmdb_id), schema)
updated += 1
except tmdb.TmdbError as exc:
errors.append(f"{f.filename}: {exc}")
if on_progress is not None:
on_progress(i, total, f)
if updated and self.on_files_changed:
self.on_files_changed(self.filelist)
return {"ok": True, "updated": updated, "without_id": without_id,
"errors": errors}
def match_pool_to_tmdb(
self, files: list[File] | None = None,
on_progress: Callable[[int, int, File], None] | None = None,
+41
View File
@@ -35,6 +35,33 @@ class TmdbResult:
kind: str # "movie" | "tv"
@dataclass
class TmdbDetails:
"""Full TMDb data used to (re)generate a movie's tags (genres etc.).
Exposes :meth:`field_values` so it plugs into the tag schema the same way the
NFO reader does (fields: genres / countries / year / rating).
"""
tmdb_id: int
title: str
year: Optional[str]
genres: list[str]
countries: list[str]
rating: Optional[int] # 0–100 (TMDb vote_average 0–10 scaled up)
def field_values(self, name: str) -> list[str]:
if name == "genres":
return list(self.genres)
if name == "countries":
return list(self.countries)
if name == "year":
return [self.year] if self.year else []
if name == "rating":
return [str(self.rating)] if self.rating is not None else []
return []
class TmdbError(RuntimeError):
"""A TMDb request could not be completed (no key, network, bad status)."""
@@ -83,6 +110,20 @@ def lookup_movie(tmdb_id: int) -> Optional[TmdbResult]:
return TmdbResult(int(data["id"]), title, _year_of(data.get("release_date")), "movie")
def movie_details(tmdb_id: int) -> TmdbDetails:
"""Full movie data from TMDb (genres/countries/rating/year), localized to cs."""
data = _get(f"/movie/{int(tmdb_id)}", {})
title = data.get("title") or data.get("original_title") or ""
genres = [g["name"] for g in (data.get("genres") or []) if g.get("name")]
countries = [c["name"] for c in (data.get("production_countries") or [])
if c.get("name")]
vote = data.get("vote_average")
rating = round(float(vote) * 10) if vote else None
return TmdbDetails(
tmdb_id=int(data["id"]), title=title, year=_year_of(data.get("release_date")),
genres=genres, countries=countries, rating=rating)
def lookup_tv(tmdb_id: int) -> Optional[TmdbResult]:
"""Resolve a series by its TMDb id → name + first-air year, or None."""
data = _get(f"/tv/{int(tmdb_id)}", {})