From 649992a10aeac0a622b18b11fb7c713bad76f13f Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sat, 30 May 2026 02:40:36 +0000 Subject: [PATCH] Align frontend with new cast and people wire format --- frontend/src/composables/useMediaWebSocket.ts | 129 ++++++++++++------ frontend/src/types.ts | 9 +- 2 files changed, 94 insertions(+), 44 deletions(-) diff --git a/frontend/src/composables/useMediaWebSocket.ts b/frontend/src/composables/useMediaWebSocket.ts index 8d36788..2729b8b 100644 --- a/frontend/src/composables/useMediaWebSocket.ts +++ b/frontend/src/composables/useMediaWebSocket.ts @@ -2,6 +2,7 @@ import { shallowRef, readonly, onUnmounted } from "vue" import type { Movie, MovieUi, + Person, Series, SeriesUi, Episode, @@ -17,6 +18,7 @@ interface RootState { ws: WebSocket | null movieMap: Map seriesMap: Map + peopleMap: Map connected: boolean initialized: boolean pendingMessages: WsMessage[] @@ -139,65 +141,86 @@ export function useMediaWebSocket() { return Object.fromEntries(sorted) } - function withMovieIdentity(id: string, movie: Movie, rootId: string): MovieUi { - return { ...normalizeMovie(movie, rootId), id, root_id: rootId } + function withMovieIdentity( + id: string, + movie: Movie, + rootId: string, + people: Map, + ): MovieUi { + return { ...normalizeMovie(movie, rootId, people), id, root_id: rootId } } - function withSeriesIdentity(id: string, series: Series, rootId: string): SeriesUi { - return { ...normalizeSeries(series, rootId), id, root_id: rootId } + function withSeriesIdentity( + id: string, + series: Series, + rootId: string, + people: Map, + ): SeriesUi { + return { ...normalizeSeries(series, rootId, people), id, root_id: rootId } } - function normalizeCastMember(member: unknown): { + function normalizeCastMember( + member: unknown, + people: Map, + ): { name: string character: string | null profile_path: string | null gender: string | null id: number | null } { - if (Array.isArray(member)) { + if (!Array.isArray(member)) { return { - name: typeof member[0] === "string" ? member[0] : "", - character: typeof member[1] === "string" ? member[1] : null, - profile_path: typeof member[2] === "string" ? member[2] : null, - gender: typeof member[3] === "string" ? member[3] : null, - id: typeof member[4] === "number" ? member[4] : null, + name: "", + character: null, + profile_path: null, + gender: null, + id: null, } } - const obj = member as { - name?: unknown - character?: unknown - profile_path?: unknown - gender?: unknown - id?: unknown - } | null + + // Current wire format: CastCredit(array_like=True) => [character, id] + const character = typeof member[0] === "string" ? member[0] : null + const id = typeof member[1] === "number" ? member[1] : null + const person = id !== null ? people.get(id) || null : null return { - name: typeof obj?.name === "string" ? obj.name : "", - character: typeof obj?.character === "string" ? obj.character : null, - profile_path: typeof obj?.profile_path === "string" ? obj.profile_path : null, - gender: typeof obj?.gender === "string" ? obj.gender : null, - id: typeof obj?.id === "number" ? obj.id : null, + name: person?.name || "", + character, + profile_path: person?.profile_path || null, + gender: person?.gender || null, + id, } } function normalizeSimilarMember(member: unknown): { id: number; title: string } { - if (Array.isArray(member)) { - return { - id: typeof member[0] === "number" ? member[0] : 0, - title: typeof member[1] === "string" ? member[1] : "", - } + if (!Array.isArray(member)) { + return { id: 0, title: "" } } - const obj = member as { id?: unknown; title?: unknown } | null return { - id: typeof obj?.id === "number" ? obj.id : 0, - title: typeof obj?.title === "string" ? obj.title : "", + id: typeof member[0] === "number" ? member[0] : 0, + title: typeof member[1] === "string" ? member[1] : "", } } - function normalizeInfo(info: T | null): T | null { + function normalizePerson(member: unknown): Person | null { + if (!Array.isArray(member)) return null + return { + name: typeof member[0] === "string" ? member[0] : "", + profile_path: typeof member[1] === "string" ? member[1] : null, + gender: typeof member[2] === "string" ? member[2] : null, + } + } + + function normalizeInfo( + info: T | null, + people: Map, + ): T | null { if (!info) return info let next: T = info if (Array.isArray((info as { cast?: unknown }).cast)) { - const cast = ((info as { cast?: unknown[] }).cast || []).map(normalizeCastMember) + const cast = ((info as { cast?: unknown[] }).cast || []) + .map((member) => normalizeCastMember(member, people)) + .filter((member) => member.name.length > 0) next = { ...next, cast } as T } if (Array.isArray((info as { similar?: unknown }).similar)) { @@ -207,15 +230,19 @@ export function useMediaWebSocket() { return next } - function normalizeMovie(movie: Movie, rootId: string | null): Movie { + function normalizeMovie(movie: Movie, rootId: string | null, people: Map): Movie { return { ...movie, files: annotateFiles(movie.files, rootId), - info: normalizeInfo(movie.info), + info: normalizeInfo(movie.info, people), } } - function normalizeSeries(series: Series, rootId: string | null): Series { + function normalizeSeries( + series: Series, + rootId: string | null, + people: Map, + ): Series { return { ...series, seasons: (series.seasons || []).map((season) => ({ @@ -225,7 +252,7 @@ export function useMediaWebSocket() { files: annotateFiles(episode.files, rootId), })), })), - info: normalizeInfo(series.info), + info: normalizeInfo(series.info, people), } } @@ -394,13 +421,22 @@ export function useMediaWebSocket() { switch (msg.type) { case "init": { + state.peopleMap.clear() + for (const [id, person] of Object.entries(msg.data.people || {})) { + const parsed = Number(id) + const normalized = normalizePerson(person) + if (Number.isFinite(parsed)) { + state.peopleMap.set(parsed, normalized || { name: "", profile_path: null, gender: null }) + } + } + state.movieMap.clear() state.seriesMap.clear() for (const [id, m] of Object.entries(msg.data.movies || {})) { - state.movieMap.set(id, withMovieIdentity(id, m, state.rootId)) + state.movieMap.set(id, withMovieIdentity(id, m, state.rootId, state.peopleMap)) } for (const [id, s] of Object.entries(msg.data.series || {})) { - state.seriesMap.set(id, withSeriesIdentity(id, s, state.rootId)) + state.seriesMap.set(id, withSeriesIdentity(id, s, state.rootId, state.peopleMap)) } state.initialized = true @@ -420,15 +456,25 @@ export function useMediaWebSocket() { break } case "upsert": { + if (msg.people) { + for (const [id, person] of Object.entries(msg.people)) { + const parsed = Number(id) + const normalized = normalizePerson(person) + if (Number.isFinite(parsed)) { + state.peopleMap.set(parsed, normalized || { name: "", profile_path: null, gender: null }) + } + } + } + if (msg.kind === "movie") { state.movieMap.set( msg.id, - withMovieIdentity(msg.id, msg.item as Movie, state.rootId), + withMovieIdentity(msg.id, msg.item as Movie, state.rootId, state.peopleMap), ) } else { state.seriesMap.set( msg.id, - withSeriesIdentity(msg.id, msg.item as Series, state.rootId), + withSeriesIdentity(msg.id, msg.item as Series, state.rootId, state.peopleMap), ) } updateMergedState() @@ -489,6 +535,7 @@ export function useMediaWebSocket() { ws: null, movieMap: new Map(), seriesMap: new Map(), + peopleMap: new Map(), connected: false, initialized: false, pendingMessages: [], diff --git a/frontend/src/types.ts b/frontend/src/types.ts index ba045a7..0e09223 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -10,6 +10,9 @@ export interface CastMember { id?: number | null } +export type CastCreditWire = [character: string | null, id: number | null] +export type PersonWire = [name: string, profile_path: string | null, gender: CastGender | null] + export interface Person { name: string profile_path: string | null @@ -36,7 +39,7 @@ export interface Info { tagline: string | null similar: SimilarMedia[] | null keywords: string[] | null - cast: CastMember[] | null + cast: CastCreditWire[] | null director: string | null creators: string[] | null number_of_seasons: number | null @@ -195,7 +198,7 @@ export interface WsInitMessage { data: { movies: Record series: Record - people?: Record | Record + people?: Record } } @@ -204,7 +207,7 @@ export interface WsUpsertMessage { kind: "movie" | "series" id: string item: Movie | Series - people?: Record | Record + people?: Record } export interface WsRemoveMessage {