feat(plex): Plex-web-style 3-level series view (Phase 2 of series view)
Restructure TV browsing into show detail → seasons → season episodes → player, like
the Plex web app.
Backend: /show/{rk} now returns a rich show page — hero meta (rating/content_rating/
genres/studio + live IMDb), live Cast & Crew, Related shows (Plex 'related', mapped to
our mirrored shows so they're openable), and per-season cards with an aggregate
watch-state + on-deck episode, plus show-level resume (on-deck)/first(play-from-start)/
status rollup. New PlexClient.related(). New bulk-state endpoints POST /show/{rk}/state
and /season/{rk}/state mark every episode watched/unwatched for the user and mirror
each change to a linked Plex account in the background (best-effort, checked once).
Frontend: PlexShowView reworked into the show detail page (hero + Resume/Play-from-start/
Mark-show-watched/Add-to-playlist/[admin]Add-to-collection + season card grid + cast +
related strips); new PlexSeasonView season subpage (hero + Resume/Play/Mark-season/
Add-season-to-playlist + landscape episode grid). Both read the one cached ['plex-show']
payload (season page picks its season out of it — instant, no extra fetch). Player queue =
the whole show (from the show page) or the season (from the season page) so prev/next +
auto-advance follow order. New 'season' history subview; Backspace steps back one drill
level (grid←show←season, out of info/playlist) alongside browser/mouse Back. Season-level
'Add to collection' intentionally omitted (Plex collections hold whole shows, not seasons).
i18n plex.series.* (en/hu/de).
This commit is contained in:
parent
569d31235d
commit
11b7558c6c
7 changed files with 694 additions and 102 deletions
|
|
@ -664,10 +664,14 @@ export interface PlexBrowseResult {
|
|||
items: PlexCard[];
|
||||
}
|
||||
export interface PlexSeasonDetail {
|
||||
id: string;
|
||||
id: string; // season rating_key
|
||||
season_number: number | null;
|
||||
title: string;
|
||||
thumb: string;
|
||||
episode_count: number;
|
||||
status: string; // aggregate: new | in_progress | watched
|
||||
resume?: PlexCard | null; // on-deck episode (last in-progress, else first unwatched)
|
||||
first?: PlexCard | null; // first episode (Play from the start)
|
||||
episodes: PlexCard[];
|
||||
}
|
||||
export interface PlexShowDetail {
|
||||
|
|
@ -678,8 +682,23 @@ export interface PlexShowDetail {
|
|||
year?: number | null;
|
||||
thumb: string;
|
||||
art: string;
|
||||
content_rating?: string | null;
|
||||
rating?: number | null;
|
||||
genres: string[];
|
||||
studio?: string | null;
|
||||
season_count?: number | null;
|
||||
imdb_rating?: number | null;
|
||||
imdb_id?: string | null;
|
||||
imdb_url?: string | null;
|
||||
cast: PlexCastMember[];
|
||||
status: string; // aggregate across all episodes
|
||||
resume?: PlexCard | null;
|
||||
first?: PlexCard | null;
|
||||
episode_count: number;
|
||||
collection_keys: string[];
|
||||
};
|
||||
seasons: PlexSeasonDetail[];
|
||||
related: PlexCard[];
|
||||
}
|
||||
|
||||
export interface PlexMarker {
|
||||
|
|
@ -1230,6 +1249,11 @@ export const api = {
|
|||
req(`/api/plex/playlists/${id}/order`, { method: "PUT", body: JSON.stringify({ item_rating_keys: itemRks }) }),
|
||||
plexShow: (id: string): Promise<PlexShowDetail> =>
|
||||
req(`/api/plex/show/${encodeURIComponent(id)}`),
|
||||
// Mark a whole show / season watched or unwatched (all its episodes) for the current user.
|
||||
plexShowState: (rk: string, watched: boolean): Promise<{ changed: number; watched: boolean }> =>
|
||||
req(`/api/plex/show/${encodeURIComponent(rk)}/state`, { method: "POST", body: JSON.stringify({ watched }) }),
|
||||
plexSeasonState: (rk: string, watched: boolean): Promise<{ changed: number; watched: boolean }> =>
|
||||
req(`/api/plex/season/${encodeURIComponent(rk)}/state`, { method: "POST", body: JSON.stringify({ watched }) }),
|
||||
plexItem: (id: string): Promise<PlexItemDetail> =>
|
||||
req(`/api/plex/item/${encodeURIComponent(id)}`),
|
||||
plexSession: (id: string, start = 0, audio?: number | null, aoff = 0, multi = false): Promise<PlexPlaySession> => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue