E5 S2: TMDB enrichment engine (Plex catalog)
Matches each mirrored Plex movie/show to TMDB by its stored canonical guid, fetches once (append_to_response), and PERSISTS the enrichment onto the row — served from the DB, never a live TMDB call at serve time. Additive: the Plex-sourced imdb_rating/rating are untouched; tmdb_rating is the distinct TMDb community score (ratings scope = TMDb + Plex's IMDb/audience, no OMDb/RT). - config.py: tmdb_api_key + api_base/language/interval/batch defaults. - sysconfig SPECS: tmdb_api_key (secret, Fernet) + tmdb_language, auto-rendered in the admin Config page; read like the Plex token. - migration 0071 (ADD-only nullable, reversible): plex_items + plex_shows gain tmdb_id / tmdb_enriched_at (watermark) / tmdb_rating / tmdb_vote_count / budget / revenue / keywords / crew / studios / production_countries / recommendations / backdrops; plex_people gains tmdb_person_id (the S2 foundation for S6 person bios). No watermark reset needed. - app/plex/tmdb.py: TmdbClient (mirrors PlexClient; throttled; 404->None, transient->TmdbError), parse_tmdb / apply_tmdb_detail (one-parser rule), guid_to_tmdb_id (tmdb: direct, imdb:/tvdb: via /find), enrich_batch (movies then shows, transient error leaves the watermark NULL to retry, definitive no-match still stamps so it isn't re-fetched), _link_people (upserts tmdb_person_id by name), serve_block (recommendations resolved to library rows by guid), run_enrich (no-op without a key). - scheduler: tmdb_enrich nightly job (default 120 min, no YouTube quota), runtime-tunable + "run now" from the Scheduler dashboard. - detail endpoints emit `tmdb` (movies + shows) so it's curl-verifiable now; the FE render lands in S5. Note: _link_people inserts plex_people rows for TMDB cast/crew; unreferenced crew (composers etc., not in cast_names/directors) get pruned by the orphan sweep — acceptable, the person page is cast-driven and cast rows survive. Gate: ruff + py_compile green; backend test lane FULL suite 387 passed (incl. migration drift downgrade->re-upgrade + test_plex_tmdb 9 tests). Frontend unchanged since S1. Not merged (epic branch, stacks on S1).
This commit is contained in:
@@ -23,6 +23,7 @@ from app.models import (
|
||||
from app.plex import detail as plex_detail
|
||||
from app.plex import people as plex_people
|
||||
from app.plex import rollup, subtitles
|
||||
from app.plex import tmdb as plex_tmdb
|
||||
from app.plex.cards import episode_card, movie_card, show_card
|
||||
from app.plex.client import PlexClient, PlexError, PlexNotConfigured
|
||||
from app.plex.lookups import by_rating_key
|
||||
@@ -172,6 +173,8 @@ def show_detail(
|
||||
"first": show_roll["first"],
|
||||
"episode_count": show_roll["episode_count"],
|
||||
"collection_keys": sh.collection_keys or [],
|
||||
# TMDB enrichment (E5 S2), served from the row.
|
||||
"tmdb": plex_tmdb.serve_block(db, sh),
|
||||
},
|
||||
"seasons": [_season_block(se, by_season.get(se.id, [])) for se in seasons],
|
||||
"related": related,
|
||||
@@ -435,4 +438,6 @@ def item_detail(
|
||||
"prev_id": prev_id,
|
||||
"next_id": next_id,
|
||||
"collections": _collection_strips(db, it, user.id),
|
||||
# TMDB enrichment (E5 S2), served from the row — movies only (episodes aren't enriched).
|
||||
"tmdb": plex_tmdb.serve_block(db, it) if it.kind == "movie" else None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user