E5 S6: enriched person page (TMDB bio + birth facts + Known-for rail)
The person page now leads with a TMDB bio, lazily fetched the first time it's
opened (off the S2-linked `tmdb_person_id`) and then served from the row:
- Migration 0072: ADD-only nullable person columns on plex_people — biography,
birthday, deathday, place_of_birth, known_for_department, also_known_as,
known_for ([{tmdb_id, media_type, title}]), tmdb_enriched_at.
- tmdb.py: TmdbClient.fetch_person (/person/{id}+combined_credits), parse_person
(bio + a popularity-ranked, deduped known_for), apply_person, enrich_person
(lazy one-shot, key+id guarded, commits, 404 stamps the watermark).
- people.py person_detail: loads the full person row, lazily enriches (best-effort
— never fails the page), serves the bio fields + resolves known_for to library
rows by guid (in-library only, popularity order).
- FE PlexPersonView: bio/birth-facts/aka hero + a "Known for" Carousel rail above
the existing Movies/Shows filmography; PlexPersonDetail type + i18n (en+hu).
Tests: test_plex_person_bio.py (6: parse / lazy-enrich guards / serve) + extended
PlexPersonView.dom.test.tsx + the unknown-name contract in test_plex_person.py.
Browser-E2E'd on Jim Carrey (live TMDB enrich → bio 1937 chars, born 1962-01-17
Newmarket, Known-for resolved). Gate: vitest 337, backend lane 402.
This commit is contained in:
@@ -1259,6 +1259,18 @@ class PlexPerson(Base, TimestampMixin, UpdatedAtMixin):
|
||||
# TMDB person id, linked by name from a title's TMDB credits (E5 S2 lays the foundation; the /person
|
||||
# biography fetch + person-page render land in S6). NULL until a TMDB-enriched title names this person.
|
||||
tmdb_person_id: Mapped[int | None] = mapped_column(Integer)
|
||||
# --- TMDB person enrichment (E5 S6): fetched from /person/{tmdb_person_id} the first time the person
|
||||
# page is opened, then SERVED FROM THE ROW. `tmdb_enriched_at` is the watermark (set even on a
|
||||
# no-op fetch so it isn't retried every open). `known_for` = top combined-credits [{tmdb_id,
|
||||
# media_type, title}], resolved to library rows by guid at serve time (like recommendations). ---
|
||||
biography: Mapped[str | None] = mapped_column(Text)
|
||||
birthday: Mapped[str | None] = mapped_column(String(10)) # "YYYY-MM-DD", served as-is
|
||||
deathday: Mapped[str | None] = mapped_column(String(10))
|
||||
place_of_birth: Mapped[str | None] = mapped_column(Text)
|
||||
known_for_department: Mapped[str | None] = mapped_column(String(64))
|
||||
also_known_as: Mapped[list | None] = mapped_column(JSONB(none_as_null=True)) # [str]
|
||||
known_for: Mapped[list | None] = mapped_column(JSONB(none_as_null=True)) # [{tmdb_id, media_type, title}]
|
||||
tmdb_enriched_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
|
||||
class PlexCollection(Base, TimestampMixin, UpdatedAtMixin):
|
||||
|
||||
Reference in New Issue
Block a user