feat(plex): person cards in the grid for the filtered actors (S3)

The actors currently in the filter now render as cards above the poster grid —
headshot + name + in-scope title count + a remove x — so you SEE who you're
exploring, not just chips in the rail (generalises the old search-only person
cards to the applied filter). New GET /plex/people/cards (photo from the
plex_people cache, count live against cast_names); PlexBrowse fetches + renders
them; i18n en/hu. Verified: John Goodman (11) / Douglas M. Griffin (1) / Bradley
Cooper (14) with photos + counts. (Plex cast-explore S3)
This commit is contained in:
2026-07-19 03:46:31 +02:00
parent 6f28ce36a7
commit 05105197e9
5 changed files with 114 additions and 2 deletions
+45
View File
@@ -1677,6 +1677,51 @@ def _enrich_people_from_rich(db: Session, row, rich: dict) -> bool:
return dirty
@router.get("/people/cards")
def people_cards(
names: str,
scope: str = "both",
user: User = Depends(current_user),
db: Session = Depends(get_db),
) -> dict:
"""Cards for the actor filter-chips currently applied: each name + its cached headshot + how many
movies/shows (in the given scope) feature them. Photos come from the plex_people cache (populated
as items are rich-fetched — info-page view + the rich re-sync); counts are live against cast_names.
A name with no cached photo yet still returns a card (the UI shows a placeholder)."""
wanted = _csv(names)[:16]
if not wanted:
return {"people": []}
libs = db.query(PlexLibrary).filter_by(enabled=True).all()
movie_lib_ids = [lb.id for lb in libs if lb.kind == "movie"]
show_lib_ids = [lb.id for lb in libs if lb.kind == "show"]
want_movies = scope in ("movie", "both") and bool(movie_lib_ids)
want_shows = scope in ("show", "both") and bool(show_lib_ids)
photos = {p.name: p.photo_thumb for p in db.query(PlexPerson).filter(PlexPerson.name.in_(wanted))}
out = []
for nm in wanted:
count = 0
if want_movies:
count += (
db.query(func.count(PlexItem.id))
.filter(
PlexItem.kind == "movie",
PlexItem.library_id.in_(movie_lib_ids),
PlexItem.cast_names.contains([nm]),
)
.scalar()
or 0
)
if want_shows:
count += (
db.query(func.count(PlexShow.id))
.filter(PlexShow.library_id.in_(show_lib_ids), PlexShow.cast_names.contains([nm]))
.scalar()
or 0
)
out.append({"name": nm, "photo": photos.get(nm), "count": count})
return {"people": out}
@router.get("/person-image")
def person_image(u: str, request: Request) -> Response:
"""Proxy a cast/crew photo from Plex's public metadata CDN (host-whitelisted; no token needed).