From 7b363964f75aab99bc0da7ee65bbc558aeb43501 Mon Sep 17 00:00:00 2001 From: Chris Kruining Date: Thu, 5 Jun 2025 10:24:37 +0200 Subject: [PATCH] finish simplification of ids into single value --- src/features/content/apis/jellyfin.ts | 39 ++++--- src/features/content/apis/tmdb.ts | 99 ++++++++---------- src/features/content/data.ts | 4 +- src/features/content/service.ts | 12 +-- src/features/content/types.ts | 1 - src/features/player/player.tsx | 3 +- .../[id]/SampleVideo_1280x720_10mb.mp4 | Bin .../api/content/{[type] => }/[id]/metadata.ts | 0 src/routes/api/content/[id]/stream.ts | 14 +++ src/routes/api/content/[type]/[id]/stream.ts | 46 -------- 10 files changed, 86 insertions(+), 132 deletions(-) rename src/routes/api/content/{[type] => }/[id]/SampleVideo_1280x720_10mb.mp4 (100%) rename src/routes/api/content/{[type] => }/[id]/metadata.ts (100%) create mode 100644 src/routes/api/content/[id]/stream.ts delete mode 100644 src/routes/api/content/[type]/[id]/stream.ts diff --git a/src/features/content/apis/jellyfin.ts b/src/features/content/apis/jellyfin.ts index 4adc3bc..a136867 100644 --- a/src/features/content/apis/jellyfin.ts +++ b/src/features/content/apis/jellyfin.ts @@ -84,12 +84,12 @@ export const listItemIds = query( return Object.fromEntries( data.Items?.map((item) => { const type = { - Movie: 'movie', - Series: 'tv', - }[item.Type as string] ?? 'unknown'; + Movie: 'm', + Series: 's', + }[item.Type as string] ?? ''; return [ - `${type}-${item.ProviderIds!["Tmdb"]!}`, + `${type}${item.ProviderIds!["Tmdb"]!}`, { jellyfin: item.Id! }, ]; }) ?? [] @@ -125,7 +125,7 @@ export const listItems = query( } return ( - data.Items?.map((item) => mapToEntry(item)) ?? [] + data.Items?.map((item) => toEntry(item)) ?? [] ); }, "jellyfin.listItems", @@ -166,7 +166,7 @@ export const getRandomItems = query( }); return ( - data?.Items?.map((item) => mapToEntry(item)) ?? [] + data?.Items?.map((item) => toEntry(item)) ?? [] ); }, "jellyfin.listRandomItems", @@ -201,7 +201,7 @@ export const getItem = query( return undefined; } - return mapToEntry(data); + return toEntry(data); }, "jellyfin.getItem", ); @@ -357,21 +357,20 @@ function assertNoErrors( } } -const mapToEntry = (item: components['schemas']['BaseItemDto']): Entry => { +const toEntry = (item: components['schemas']['BaseItemDto']): Entry => { const type = { - Movie: 'movie', - Series: 'tv', + Movie: 'm', + Series: 's', }[item.Type as string] as any; return { - type, - id: item.ProviderIds!["Tmdb"]!, - title: item.Name!, - overview: item.Overview!, - thumbnail: new URL(`/Items/${item.Id}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'), - image: new URL(`/Items/${item.Id}/Images/Backdrop`, getBaseUrl()), - providers: { - jellyfin: item.Id - } - }; + id: `${type}${item.ProviderIds!["Tmdb"]!}`, + title: item.Name!, + overview: item.Overview!, + thumbnail: new URL(`/Items/${item.Id}/Images/Primary`, getBaseUrl()), //await getItemImage(data.Id!, 'Primary'), + image: new URL(`/Items/${item.Id}/Images/Backdrop`, getBaseUrl()), + providers: { + jellyfin: item.Id + } + }; }; \ No newline at end of file diff --git a/src/features/content/apis/tmdb.ts b/src/features/content/apis/tmdb.ts index b7cab87..b3f3890 100644 --- a/src/features/content/apis/tmdb.ts +++ b/src/features/content/apis/tmdb.ts @@ -1,9 +1,19 @@ import createClient from "openapi-fetch"; import { query } from "@solidjs/router"; import { Entry, SearchResult } from "../types"; -import { paths as pathsV3 } from "./tmdb.generated"; +import { paths as pathsV3, operations } from "./tmdb.generated"; import { paths as pathsV4 } from "./tmdb.not.generated"; +interface TMDBItem { + id: number; + media_type: string; + name?: string; + title?: string; + overview?: string; + backdrop_path?: string; + poster_path?: string; +} + const getClients = () => { "use server"; @@ -28,20 +38,27 @@ const getClients = () => { }; export const getEntry = query( - async (type: Entry['type'], id: string): Promise => { + async (id: string): Promise => { "use server"; const [ clientV3 ] = getClients(); + const mediaType = ({ + m: 'movie', + s: 'tv', + } as const)[id[0]]!; + const endpoint = ({ movie: "/movie/{movie_id}", tv: '/tv/{series_id}', - } as const)[type]; + } as const)[mediaType]; const params = ({ - movie: { movie_id: Number.parseInt(id) }, - tv: { series_id: Number.parseInt(id) }, - } as const)[type]; + movie: { movie_id: Number.parseInt(id.slice(1)) }, + tv: { series_id: Number.parseInt(id.slice(1)) }, + } as const)[mediaType]; + + console.log(`going to fetch from '${endpoint}' with id '${id}'`) const { data } = await clientV3.GET(endpoint, { params: { @@ -53,14 +70,7 @@ export const getEntry = query( return undefined; } - return { - type, - id: String(data.id ?? -1), - title: data.title!, - overview: data.overview, - thumbnail: `http://image.tmdb.org/t/p/w342${data.poster_path}`, - image: `http://image.tmdb.org/t/p/original${data.backdrop_path}`, - }; + return toEntry(data as any, mediaType); }, "tmdb.getEntry", ); @@ -85,22 +95,13 @@ export const getRecommendations = query(async (): Promise => { return []; } - return data?.results.map( - ({ id, title, overview, poster_path, backdrop_path }) => ({ - type: 'movie', - id: String(id ?? -1), - title: title!, - overview, - thumbnail: `http://image.tmdb.org/t/p/w342${poster_path}`, - image: `http://image.tmdb.org/t/p/original${backdrop_path}`, - }), - ); + return data?.results.map((item) => toEntry(item)); }, "tmdb.getRecommendations"); export const getDiscovery = query(async (): Promise => { "use server"; - const [ clientV3 ] = getClients(); + const [ clientV3 ] = getClients(); const [{ data: movies }, { data: series }] = await Promise.all([ clientV3.GET("/discover/movie"), @@ -111,25 +112,8 @@ export const getDiscovery = query(async (): Promise => { return []; } - const movieEntries = movies?.results?.slice(0, 10) - .map(({ id, title, overview, poster_path, backdrop_path }): Entry => ({ - type: 'movie', - id: String(id ?? -1), - title: title!, - overview, - thumbnail: `http://image.tmdb.org/t/p/w342${poster_path}`, - image: `http://image.tmdb.org/t/p/original${backdrop_path}`, - })) ?? [] - - const seriesEntries = series?.results?.slice(0, 10) - .map(({ id, name, overview, poster_path, backdrop_path }): Entry => ({ - type: 'tv', - id: String(id ?? -1), - title: name!, - overview, - thumbnail: `http://image.tmdb.org/t/p/w342${poster_path}`, - image: `http://image.tmdb.org/t/p/original${backdrop_path}`, - })) ?? [] + const movieEntries = movies?.results?.slice(0, 10).map((item): Entry => toEntry(item)) ?? [] + const seriesEntries = series?.results?.slice(0, 10).map((item): Entry => toEntry(item)) ?? [] return movieEntries.concat(seriesEntries); }, "tmdb.getDiscovery"); @@ -158,18 +142,23 @@ export const searchMulti = query(async (query: string, page: number = 1): Promis return { count: 0, pages: 0, results: [] }; } - console.log(`loaded page ${page}, found ${data.results?.length} results`); - console.log(data.results[0]); + return { count: data.total_results!, pages: data.total_pages!, results: data.results?.filter(({ media_type }) => media_type === 'movie' || media_type === 'tv').map((item): Entry => toEntry(item)) ?? [] }; +}, "tmdb.search.multi"); - return { count: data.total_results!, pages: data.total_pages!, results: data.results?.filter(({ media_type }) => media_type === 'movie' || media_type === 'tv').map(({ id, name, title, media_type, overview, backdrop_path, poster_path }): Entry => ({ - type: ({ - movie: 'movie', - tv: 'tv', - }[media_type ?? '']) as any, - id: String(id), - title: `${name ?? title ?? ''} (${media_type})`, +function toEntry(item: TMDBItem): Entry; +function toEntry(item: Omit, mediaType: string): Entry; + +function toEntry({ id, name, title, overview, media_type = '', backdrop_path, poster_path }: TMDBItem | Omit, mediaType?: string): Entry { + const type = ({ + movie: 'm', + tv: 's', + }[(mediaType ?? media_type) as string]); + + return ({ + id: `${type}${String(id ?? -1)}`, + title: `${name ?? title ?? ''}`, overview, thumbnail: `http://image.tmdb.org/t/p/w342${poster_path}`, image: `http://image.tmdb.org/t/p/original${backdrop_path}`, - })) ?? [] }; -}, "tmdb.search.multi"); \ No newline at end of file + }); +}; \ No newline at end of file diff --git a/src/features/content/data.ts b/src/features/content/data.ts index 1250b37..55db6dd 100644 --- a/src/features/content/data.ts +++ b/src/features/content/data.ts @@ -1,6 +1,6 @@ import { toSlug } from "~/utilities"; import type { Entry } from "./types"; -export const emptyEntry = Object.freeze({ type: 'movie', id: '0', title: '' }); +export const emptyEntry = Object.freeze({ id: '0', title: '' }); -export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.type}-${entry.id}`); +export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.id}`); diff --git a/src/features/content/service.ts b/src/features/content/service.ts index 025358b..0b31f12 100644 --- a/src/features/content/service.ts +++ b/src/features/content/service.ts @@ -15,10 +15,10 @@ const jellyfinUserId = "a9c51af84bf54578a99ab4dd0ebf0763"; const lookupTable = query(async () => listItemIds(), 'content.lookupTable'); export const getHighlights = () => getContinueWatching(jellyfinUserId); -export const getStream = query(async (type: Entry['type'], id: string, range: string) => { +export const getStream = query(async (id: string, range: string) => { const table = await lookupTable(); - return getItemStream(table[`${type}-${id}`].jellyfin, range); + return getItemStream(table[id].jellyfin, range); }, 'content.stream'); export const listCategories = query(async (): Promise => { @@ -35,16 +35,16 @@ export const listCategories = query(async (): Promise => { export const getEntryFromSlug = query( async (slug: string): Promise => { - const { type, id } = slug.match(/^.+-(?\w+)-(?\w+)$/)?.groups ?? {}; + const { id } = slug.match(/^.+-(?\w+)$/)?.groups ?? {}; - return getTmdbEntry(type as any, id); + return getTmdbEntry(id); }, "content.getFromSlug", ); export const getEntry = query( - async (type: Entry['type'], id: Entry["id"]): Promise => { - return getTmdbEntry(type, id); + async (id: Entry["id"]): Promise => { + return getTmdbEntry(id); }, "content.get", ); diff --git a/src/features/content/types.ts b/src/features/content/types.ts index 6e5a3d7..8e5be69 100644 --- a/src/features/content/types.ts +++ b/src/features/content/types.ts @@ -4,7 +4,6 @@ export interface Category { } export interface Entry { - type: 'movie'|'tv'; id: string; title: string; overview?: string; diff --git a/src/features/player/player.tsx b/src/features/player/player.tsx index 037789d..a54dde3 100644 --- a/src/features/player/player.tsx +++ b/src/features/player/player.tsx @@ -90,8 +90,7 @@ export const Player: Component = (props) => {