finish simplification of ids into single value
This commit is contained in:
parent
61795fdc5e
commit
7b363964f7
10 changed files with 86 additions and 132 deletions
|
@ -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<T>(
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
};
|
|
@ -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<Entry | undefined> => {
|
||||
async (id: string): Promise<Entry | undefined> => {
|
||||
"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<Entry[]> => {
|
|||
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<Entry[]> => {
|
||||
"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<Entry[]> => {
|
|||
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<TMDBItem, 'media_type'>, mediaType: string): Entry;
|
||||
|
||||
function toEntry({ id, name, title, overview, media_type = '', backdrop_path, poster_path }: TMDBItem | Omit<TMDBItem, 'media_type'>, 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");
|
||||
});
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import { toSlug } from "~/utilities";
|
||||
import type { Entry } from "./types";
|
||||
|
||||
export const emptyEntry = Object.freeze<Entry>({ type: 'movie', id: '0', title: '' });
|
||||
export const emptyEntry = Object.freeze<Entry>({ 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}`);
|
||||
|
|
|
@ -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<Category[]> => {
|
||||
|
@ -35,16 +35,16 @@ export const listCategories = query(async (): Promise<Category[]> => {
|
|||
|
||||
export const getEntryFromSlug = query(
|
||||
async (slug: string): Promise<Entry | undefined> => {
|
||||
const { type, id } = slug.match(/^.+-(?<type>\w+)-(?<id>\w+)$/)?.groups ?? {};
|
||||
const { id } = slug.match(/^.+-(?<id>\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<Entry | undefined> => {
|
||||
return getTmdbEntry(type, id);
|
||||
async (id: Entry["id"]): Promise<Entry | undefined> => {
|
||||
return getTmdbEntry(id);
|
||||
},
|
||||
"content.get",
|
||||
);
|
||||
|
|
|
@ -4,7 +4,6 @@ export interface Category {
|
|||
}
|
||||
|
||||
export interface Entry {
|
||||
type: 'movie'|'tv';
|
||||
id: string;
|
||||
title: string;
|
||||
overview?: string;
|
||||
|
|
|
@ -90,8 +90,7 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
|
||||
<video
|
||||
ref={setVideo}
|
||||
src={`/api/content/${props.entry.type}/${props.entry.id}/stream`}
|
||||
// src="https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4"
|
||||
src={`/api/content/${props.entry.id}/stream`}
|
||||
poster={props.entry.image}
|
||||
lang="en"
|
||||
>
|
||||
|
|
14
src/routes/api/content/[id]/stream.ts
Normal file
14
src/routes/api/content/[id]/stream.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { APIEvent } from "@solidjs/start/server";
|
||||
import { getStream } from "~/features/content";
|
||||
|
||||
export const GET = async ({ request, params: { id } }: APIEvent) => {
|
||||
"use server";
|
||||
|
||||
const range = request.headers.get("range");
|
||||
|
||||
if (range === null) {
|
||||
return new Response("Requires Range header", { status: 400 });
|
||||
}
|
||||
|
||||
return getStream(id, range);
|
||||
};
|
|
@ -1,46 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server";
|
||||
import { getStream } from "~/features/content";
|
||||
|
||||
// const CHUNK_SIZE = 1 * 1e6; // 1MB
|
||||
|
||||
export const GET = async ({ request, params: { type, id } }: APIEvent) => {
|
||||
"use server";
|
||||
|
||||
const range = request.headers.get("range");
|
||||
|
||||
if (range === null) {
|
||||
return new Response("Requires Range header", { status: 400 });
|
||||
}
|
||||
|
||||
return getStream(type, id, range);
|
||||
|
||||
// try {
|
||||
// const file = Bun.file(
|
||||
// import.meta.dirname + "/SampleVideo_1280x720_10mb.mp4",
|
||||
// );
|
||||
|
||||
// if ((await file.exists()) !== true) {
|
||||
// return new Response("File not found", { status: 404 });
|
||||
// }
|
||||
|
||||
// const videoSize = file.size;
|
||||
// const start = Number.parseInt(range.replace(/\D/g, ""));
|
||||
// const end = Math.min(start + CHUNK_SIZE - 1, videoSize - 1);
|
||||
|
||||
// console.log(`streaming slice(${start}, ${end})`);
|
||||
|
||||
// return new Response(file.slice(start, end), {
|
||||
// status: 206,
|
||||
// headers: {
|
||||
// 'Accept-Ranges': 'bytes',
|
||||
// 'Content-Range': `bytes ${start}-${end}/${videoSize}`,
|
||||
// 'Content-Length': `${end - start + 1}`,
|
||||
// 'Content-Type': 'video/mp4',
|
||||
// },
|
||||
// });
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
|
||||
// throw e;
|
||||
// }
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue