kaas
This commit is contained in:
parent
873677ea04
commit
d683b051b6
17 changed files with 244 additions and 273 deletions
|
@ -1,12 +1,10 @@
|
|||
"use server";
|
||||
|
||||
import type { paths } from "./jellyfin.generated"; // generated by openapi-typescript
|
||||
import createClient from "openapi-fetch";
|
||||
import type { paths, components } from "./jellyfin.generated"; // generated by openapi-typescript
|
||||
import { query } from "@solidjs/router";
|
||||
import { Entry } from "../types";
|
||||
|
||||
// ===============================
|
||||
'use server';
|
||||
// ===============================
|
||||
|
||||
type ItemImageType = "Primary" | "Art" | "Backdrop" | "Banner" | "Logo" | "Thumb" | "Disc" | "Box" | "Screenshot" | "Menu" | "Chapter" | "BoxRear" | "Profile";
|
||||
|
||||
const baseUrl = process.env.JELLYFIN_BASE_URL;
|
||||
|
@ -58,7 +56,71 @@ export const listUsers = query(async () => {
|
|||
return data ?? [];
|
||||
}, "jellyfin.listUsers");
|
||||
|
||||
export const listItems = query(async (userId: string): Promise<Entry[] | undefined> => {
|
||||
const { data, error } = await client.GET("/Items", {
|
||||
params: {
|
||||
query: {
|
||||
userId,
|
||||
hasTmdbInfo: true,
|
||||
recursive: true,
|
||||
includeItemTypes: ["Movie", "Series"],
|
||||
fields: [
|
||||
"ProviderIds",
|
||||
"Genres",
|
||||
"DateLastMediaAdded",
|
||||
"DateCreated",
|
||||
"MediaSources",
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (data === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return data.Items?.map(item => ({
|
||||
id: item.Id!,
|
||||
title: item.Name!,
|
||||
thumbnail: new URL(`/Items/${item.Id!}/Images/Primary`, baseUrl), //await getItemImage(data.Id!, 'Primary'),
|
||||
})) ?? [];
|
||||
}, "jellyfin.listItems");
|
||||
|
||||
export const getRandomItem = query(async (userId: string): Promise<Entry | undefined> => getRandomItems(userId, 1).then(items => items?.at(0)), "jellyfin.listRandomItem");
|
||||
|
||||
export const getRandomItems = query(async (userId: string, limit: number = 10): Promise<Entry[]> => {
|
||||
const { data, error } = await client.GET("/Items", {
|
||||
params: {
|
||||
query: {
|
||||
userId,
|
||||
hasTmdbInfo: true,
|
||||
recursive: true,
|
||||
limit,
|
||||
sortBy: ["Random"],
|
||||
includeItemTypes: ["Movie", "Series"],
|
||||
imageTypes: ["Primary", "Backdrop", "Thumb"],
|
||||
fields: [
|
||||
"ProviderIds",
|
||||
"Genres",
|
||||
"DateLastMediaAdded",
|
||||
"DateCreated",
|
||||
"MediaSources",
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return data?.Items?.map(item => ({
|
||||
id: item.Id!,
|
||||
title: item.Name!,
|
||||
thumbnail: new URL(`/Items/${item.Id!}/Images/Primary`, baseUrl), //await getItemImage(data.Id!, 'Primary'),
|
||||
image: new URL(`/Items/${item.Id!}/Images/Backdrop`, baseUrl), //await getItemImage(data.Id!, 'Primary'),
|
||||
})) ?? [];
|
||||
}, "jellyfin.listRandomItems");
|
||||
|
||||
export const getItem = query(async (userId: string, itemId: string): Promise<Entry | undefined> => {
|
||||
console.log('baseUrl', baseUrl);
|
||||
|
||||
const { data, error } = await client.GET("/Items/{itemId}", {
|
||||
params: {
|
||||
path: {
|
||||
|
@ -87,7 +149,10 @@ export const getItem = query(async (userId: string, itemId: string): Promise<Ent
|
|||
return {
|
||||
id: data.Id!,
|
||||
title: data.Name!,
|
||||
synopsis: data.Overview!,
|
||||
thumbnail: new URL(`/Items/${itemId}/Images/Primary`, baseUrl), //await getItemImage(data.Id!, 'Primary'),
|
||||
image: new URL(`/Items/${itemId}/Images/Backdrop`, baseUrl),
|
||||
// ...data,
|
||||
};
|
||||
}, "jellyfin.getItem");
|
||||
|
||||
|
@ -142,32 +207,29 @@ export const queryItems = query(async () => {
|
|||
|
||||
}, 'jellyfin.queryItems');
|
||||
|
||||
export const getContinueWatching = query(
|
||||
async (userId: string): Promise<Entry[]> => {
|
||||
const { data, error } = await client.GET("/UserItems/Resume", {
|
||||
params: {
|
||||
query: {
|
||||
userId,
|
||||
mediaTypes: ["Video"],
|
||||
// fields: ["ProviderIds", "Genres"],
|
||||
// includeItemTypes: ["Series", "Movie"]
|
||||
},
|
||||
export const getContinueWatching = query(async (userId: string): Promise<Entry[]> => {
|
||||
const { data, error } = await client.GET("/UserItems/Resume", {
|
||||
params: {
|
||||
query: {
|
||||
userId,
|
||||
mediaTypes: ["Video"],
|
||||
// fields: ["ProviderIds", "Genres"],
|
||||
// includeItemTypes: ["Series", "Movie"]
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (Array.isArray(data?.Items) !== true) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(data?.Items) !== true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uniqueIds = new Set<string>(data.Items.map(item => item.Type === 'Episode' ? item.SeriesId! : item.Id));
|
||||
const results = await Promise.allSettled(uniqueIds.values().map(id => getItem(userId, id)).toArray());
|
||||
const uniqueIds = new Set<string>(data.Items.map(item => item.Type === 'Episode' ? item.SeriesId! : item.Id!));
|
||||
const results = await Promise.allSettled(uniqueIds.values().map(id => getItem(userId, id)).toArray());
|
||||
|
||||
assertNoErrors(results);
|
||||
assertNoErrors(results);
|
||||
|
||||
return results.filter((result): result is PromiseFulfilledResult<Entry> => result.value !== undefined).map(({ value }) => value);
|
||||
},
|
||||
"jellyfin.continueWatching",
|
||||
);
|
||||
return results.filter((result): result is PromiseFulfilledResult<Entry> => result.value !== undefined).map(({ value }) => value);
|
||||
}, "jellyfin.continueWatching");
|
||||
|
||||
function assertNoErrors<T>(results: PromiseSettledResult<T>[]): asserts results is PromiseFulfilledResult<T>[] {
|
||||
if (results.some(({ status }) => status !== 'fulfilled')) {
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
"use server";
|
||||
|
||||
import type { Category, Entry } from "./types";
|
||||
import { query } from "@solidjs/router";
|
||||
import { entries } from "./data";
|
||||
import { getContinueWatching, getItem, TEST } from "./apis/jellyfin";
|
||||
import { getContinueWatching, getItem, getRandomItems } from "./apis/jellyfin";
|
||||
|
||||
const jellyfinUserId = "a9c51af8-4bf5-4578-a99a-b4dd0ebf0763";
|
||||
const jellyfinUserId = "a9c51af84bf54578a99ab4dd0ebf0763";
|
||||
|
||||
// export const getHighlights = () => getRandomItems(jellyfinUserId);
|
||||
export const getHighlights = () => getContinueWatching(jellyfinUserId);
|
||||
|
||||
export const listCategories = query(async (): Promise<Category[]> => {
|
||||
"use server";
|
||||
|
||||
// await TEST()
|
||||
// console.log(await getItemPlaybackInfo(jellyfinUserId, 'a69c0c0ab66177a7adb671f126335d16'));
|
||||
|
||||
return [
|
||||
{ label: "Continue", entries: await getContinueWatching(jellyfinUserId) },
|
||||
// { label: "Continue", entries: await getContinueWatching(jellyfinUserId) },
|
||||
{ label: "Random", entries: await getRandomItems(jellyfinUserId) },
|
||||
{
|
||||
label: "Popular",
|
||||
entries: [
|
||||
|
@ -70,11 +71,9 @@ export const listCategories = query(async (): Promise<Category[]> => {
|
|||
|
||||
export const getEntry = query(
|
||||
async (id: Entry["id"]): Promise<Entry | undefined> => {
|
||||
"use server";
|
||||
|
||||
return getItem(jellyfinUserId, id);
|
||||
},
|
||||
"series.get",
|
||||
);
|
||||
|
||||
export { listUsers, getContinueWatching } from "./apis/jellyfin";
|
||||
export { listUsers, getContinueWatching, listItems } from "./apis/jellyfin";
|
||||
|
|
|
@ -7,11 +7,13 @@ export interface Category {
|
|||
export interface Entry {
|
||||
id: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
synopsis?: string;
|
||||
releaseDate?: string;
|
||||
sources?: Entry.Source[];
|
||||
thumbnail?: URL | string;
|
||||
image?: string;
|
||||
image?: URL | string;
|
||||
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
export namespace Entry {
|
||||
|
|
|
@ -22,21 +22,16 @@
|
|||
|
||||
box-shadow: var(--shadow-2);
|
||||
background:
|
||||
/* Dot */
|
||||
radial-gradient(circle at 25% 30% #7772 #7774 1em transparent 1em),
|
||||
/* Dot */
|
||||
radial-gradient(circle at 85% 15% #7772 #7774 1em transparent 1em),
|
||||
/* Bottom fade */ linear-gradient(165deg transparent 60% #555 60% #333),
|
||||
/* wave dark part */
|
||||
radial-gradient(circle at 25% 30%, #7772, #7774 1em, transparent 1em),
|
||||
radial-gradient(circle at 85% 15%, #7772, #7774 1em, transparent 1em),
|
||||
linear-gradient(165deg, transparent 60%, #555 60%, #333),
|
||||
radial-gradient(
|
||||
ellipse 5em 2.25em at 0.5em calc(50% - 1em) #333 100% transparent 100%
|
||||
ellipse 5em 2.25em at 0.5em calc(50% - 1em), #333 100%, transparent 100%
|
||||
),
|
||||
/* wave light part */
|
||||
radial-gradient(
|
||||
ellipse 5em 2.25em at calc(100% - 0.5em) calc(50% + 1em) #555 100%
|
||||
transparent 100%
|
||||
ellipse 5em 2.25em at calc(100% - 0.5em) calc(50% + 1em), #555 100%, transparent 100%
|
||||
),
|
||||
/* Base */ linear-gradient(to bottom #333 50% #555 50%);
|
||||
linear-gradient(to bottom, #333 50%, #555 50%);
|
||||
|
||||
transform-origin: 50% 0;
|
||||
transform: scale(1.1) translateY(calc(-4 * var(--padding)));
|
||||
|
|
|
@ -10,6 +10,7 @@ export const ListItem: Component<{ entry: Entry }> = (props) => {
|
|||
<figure class={css.listItem} data-id={props.entry.id}>
|
||||
<img src={props.entry.thumbnail ?? ''} alt={props.entry.title} />
|
||||
|
||||
|
||||
<figcaption>
|
||||
<strong>{props.entry.title}</strong>
|
||||
|
||||
|
|
|
@ -12,16 +12,14 @@ import { Hero } from "~/components/hero";
|
|||
import css from "./overview.module.css";
|
||||
|
||||
type OverviewProps = {
|
||||
highlight: Entry;
|
||||
highlights: Entry[];
|
||||
categories: Category[];
|
||||
};
|
||||
|
||||
export const Overview: Component<OverviewProps> = (props) => {
|
||||
const [container, setContainer] = createSignal<HTMLElement>();
|
||||
|
||||
return (
|
||||
<div ref={setContainer} class={css.container}>
|
||||
<Hero class={css.hero} entry={props.highlight}></Hero>
|
||||
<div class={css.container}>
|
||||
<Hero class={css.hero} entries={props.highlights}></Hero>
|
||||
|
||||
<Index each={props.categories}>
|
||||
{(category) => (
|
||||
|
|
|
@ -140,7 +140,7 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
console.log("pause", e);
|
||||
},
|
||||
suspend(e) {
|
||||
console.log("suspend", e);
|
||||
// console.log("suspend", e);
|
||||
},
|
||||
|
||||
volumechange(e) {
|
||||
|
@ -152,7 +152,7 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
},
|
||||
|
||||
progress(e) {
|
||||
console.log(e);
|
||||
// console.log(e);
|
||||
},
|
||||
|
||||
// timeupdate(e) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue