A person's TMDB bio was fetched only when their page was first opened, off the request path, so the first open showed no bio and the page never asked again (2 of 18,202 linked people were ever enriched). - people_enrich scheduler job (every 10 min, 300 per run): linked people's bios from TMDB and their portraits into the image cache, the most-credited people first. A failed TMDB lookup ends the batch. - Portraits are resized through Plex's photo transcoder to a 320 box (~15 KB instead of ~170 KB; the person-image proxy uses the same cache key, and a full-size original is removed as its small twin lands). - The person response says `enriching` while a bio is on its way (a person the job hasn't reached yet). - "Known for" is ranked by vote count, without guest visits as themselves (talk shows had crowded out the films: Tom Cruise had 14 of 15), with directed works, from a 40-deep pool; the page shows up to 15. Migration 0093 un-stamps enriched people so the job refetches them. - A phased job result (backfill, people_enrich) now counts as a change for the audit trail; before, an automatic backfill run that added videos was never audited. - HU: the watch reconcile job reads "egyeztetés" (a leftover from the S5 sweep).
154 lines
5.2 KiB
Python
154 lines
5.2 KiB
Python
"""DB-backed regression (E5 S4): GET /api/plex/person/{name} returns a cast/crew member's
|
|
filmography — movies + shows in the ENABLED libraries whose cast OR directors include the name,
|
|
each carrying the caller's watch-state, plus the cached headshot. Also covers `content_libs`
|
|
scoping (disabled libraries excluded) and the kind filter (episodes are not movies).
|
|
|
|
Uses the DB lane (`db` fixture) — skipped when no Postgres is reachable.
|
|
"""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
import app.state as state
|
|
from app.auth import current_user
|
|
from app.db import get_db
|
|
from app.main import app
|
|
from app.models import (
|
|
PlexItem,
|
|
PlexLibrary,
|
|
PlexPerson,
|
|
PlexShow,
|
|
PlexState,
|
|
User,
|
|
)
|
|
from app.plex.query import content_libs
|
|
|
|
NAME = "Robert De Niro"
|
|
|
|
|
|
def _get(db, user, monkeypatch, override_dep, path):
|
|
monkeypatch.setattr(state, "_configured_cache", True) # pass the request gate
|
|
override_dep(get_db, db.info["request"])
|
|
override_dep(current_user, lambda: user)
|
|
return TestClient(app).get(path) # no lifespan (skip scheduler/HLS sweep)
|
|
|
|
|
|
def test_person_detail_returns_cast_and_director_credits(db, monkeypatch, override_dep):
|
|
movies_lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
|
|
shows_lib = PlexLibrary(plex_key="2", title="Shows", kind="show", enabled=True)
|
|
off_lib = PlexLibrary(plex_key="3", title="Off", kind="movie", enabled=False)
|
|
db.add_all([movies_lib, shows_lib, off_lib])
|
|
db.flush()
|
|
|
|
heat = PlexItem(
|
|
rating_key="m1",
|
|
library_id=movies_lib.id,
|
|
kind="movie",
|
|
title="Heat",
|
|
year=1995,
|
|
cast_names=[NAME, "Al Pacino"],
|
|
)
|
|
# Director-only credit: the name is NOT in cast_names, only directors → the OR must still catch it.
|
|
directed = PlexItem(
|
|
rating_key="m2",
|
|
library_id=movies_lib.id,
|
|
kind="movie",
|
|
title="A Bronx Tale",
|
|
year=1993,
|
|
cast_names=["Chazz Palminteri"],
|
|
directors=[NAME],
|
|
)
|
|
# Excluded: a movie in a DISABLED library.
|
|
hidden = PlexItem(
|
|
rating_key="m3",
|
|
library_id=off_lib.id,
|
|
kind="movie",
|
|
title="Hidden",
|
|
year=2000,
|
|
cast_names=[NAME],
|
|
)
|
|
# Excluded: an episode (kind != "movie") that happens to list the name.
|
|
episode = PlexItem(
|
|
rating_key="e1",
|
|
library_id=movies_lib.id,
|
|
kind="episode",
|
|
title="An Episode",
|
|
year=2001,
|
|
cast_names=[NAME],
|
|
)
|
|
show = PlexShow(rating_key="s1", library_id=shows_lib.id, title="A Show", cast_names=[NAME])
|
|
db.add_all([heat, directed, hidden, episode, show])
|
|
db.add(PlexPerson(name=NAME, photo_thumb="/img/deniro.jpg"))
|
|
db.flush()
|
|
|
|
user = User(email="person@example.com")
|
|
db.add(user)
|
|
db.flush()
|
|
db.add(PlexState(user_id=user.id, item_id=heat.id, status="watched"))
|
|
db.commit()
|
|
|
|
resp = _get(db, user, monkeypatch, override_dep, f"/api/plex/person/{NAME.replace(' ', '%20')}")
|
|
assert resp.status_code == 200, resp.text
|
|
body = resp.json()
|
|
|
|
assert body["name"] == NAME
|
|
assert body["photo"] == "/img/deniro.jpg"
|
|
# Both the cast credit (Heat) and the director-only credit (A Bronx Tale), year desc; NOT the
|
|
# disabled-library movie and NOT the episode.
|
|
assert [m["title"] for m in body["movies"]] == ["Heat", "A Bronx Tale"]
|
|
assert body["movies"][0]["status"] == "watched" # from the caller's PlexState
|
|
assert body["movies"][1]["status"] == "new"
|
|
assert all(m["type"] == "movie" for m in body["movies"])
|
|
# The show credit.
|
|
assert [s["title"] for s in body["shows"]] == ["A Show"]
|
|
assert body["shows"][0]["type"] == "show"
|
|
|
|
|
|
def test_person_detail_unknown_name_is_empty(db, monkeypatch, override_dep):
|
|
lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
|
|
db.add(lib)
|
|
db.flush()
|
|
db.add(
|
|
PlexItem(
|
|
rating_key="m1",
|
|
library_id=lib.id,
|
|
kind="movie",
|
|
title="Solo",
|
|
cast_names=["Nobody Else"],
|
|
)
|
|
)
|
|
user = User(email="empty@example.com")
|
|
db.add(user)
|
|
db.commit()
|
|
|
|
resp = _get(db, user, monkeypatch, override_dep, "/api/plex/person/Ghost%20Actor")
|
|
assert resp.status_code == 200, resp.text
|
|
body = resp.json()
|
|
# An unknown name has no plex_people row → no bio, no titles (E5 S6 added the bio fields, all null).
|
|
assert body == {
|
|
"name": "Ghost Actor",
|
|
"photo": None,
|
|
"biography": None,
|
|
"birthday": None,
|
|
"deathday": None,
|
|
"place_of_birth": None,
|
|
"known_for_department": None,
|
|
"also_known_as": [],
|
|
"enriching": False,
|
|
"known_for": [],
|
|
"movies": [],
|
|
"shows": [],
|
|
}
|
|
|
|
|
|
def test_content_libs_partitions_only_enabled(db):
|
|
db.add_all(
|
|
[
|
|
PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True),
|
|
PlexLibrary(plex_key="2", title="Shows", kind="show", enabled=True),
|
|
PlexLibrary(plex_key="3", title="OffMovie", kind="movie", enabled=False),
|
|
]
|
|
)
|
|
db.commit()
|
|
movie_ids, show_ids = content_libs(db)
|
|
assert len(movie_ids) == 1 and len(show_ids) == 1 # the disabled movie lib is excluded
|