merge
This commit is contained in:
parent
0eb2e34e60
commit
a15809f4fd
11 changed files with 32770 additions and 82 deletions
39
src/auth.ts
39
src/auth.ts
|
@ -4,19 +4,33 @@ import { createAuthClient } from "better-auth/solid";
|
|||
import { genericOAuthClient } from "better-auth/client/plugins";
|
||||
|
||||
export const auth = betterAuth({
|
||||
appName: 'Streamarr',
|
||||
basePath: '/api/auth',
|
||||
appName: "Streamarr",
|
||||
basePath: "/api/auth",
|
||||
advanced: {
|
||||
useSecureCookies: true,
|
||||
crossSubDomainCookies: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
level: 'debug',
|
||||
level: "info",
|
||||
},
|
||||
onAPIError: {
|
||||
throw: true,
|
||||
user: {
|
||||
additionalFields: {
|
||||
name: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
preferred_username: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
username: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
profile: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
genericOAuth({
|
||||
|
@ -28,7 +42,14 @@ export const auth = betterAuth({
|
|||
"ZPuiW2gpVV6MGXIJFk5P3EeSW8V_ICgqduF.hJVCKkrnVmRqIQXRk0o~HSA8ZdCf8joA4m_F",
|
||||
discoveryUrl:
|
||||
"https://auth.kruining.eu/.well-known/openid-configuration",
|
||||
scopes: ["openid", "email", "picture", "profile", "groups"],
|
||||
scopes: [
|
||||
"offline_access",
|
||||
"openid",
|
||||
"email",
|
||||
"picture",
|
||||
"profile",
|
||||
"groups",
|
||||
],
|
||||
accessType: "offline",
|
||||
pkce: true,
|
||||
},
|
||||
|
|
32577
src/features/content/apis/jellyfin.generated.d.ts
vendored
Normal file
32577
src/features/content/apis/jellyfin.generated.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
62
src/features/content/apis/jellyfin.ts
Normal file
62
src/features/content/apis/jellyfin.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import createClient from "openapi-fetch";
|
||||
import type { paths } from "./jellyfin.generated"; // generated by openapi-typescript
|
||||
import { query } from "@solidjs/router";
|
||||
import { Entry } from "../types";
|
||||
|
||||
const baseUrl = "http://ulmo:8096/";
|
||||
const client = createClient<paths>({
|
||||
baseUrl,
|
||||
headers: {
|
||||
Authorization: `MediaBrowser DeviceId="Streamarr", Token="b3c44db1e31f4349b19d1ff0bc487da2"`,
|
||||
},
|
||||
});
|
||||
|
||||
export const getItem = query(async (userId: string, itemId: string) => {
|
||||
const { data, error } = await client.GET("/Items/{itemId}", {
|
||||
params: {
|
||||
path: {
|
||||
itemId,
|
||||
},
|
||||
query: {
|
||||
userId,
|
||||
hasTmdbInfo: true,
|
||||
recursive: true,
|
||||
includeItemTypes: ["Movie", "Series"],
|
||||
fields: [
|
||||
"ProviderIds",
|
||||
"Genres",
|
||||
"DateLastMediaAdded",
|
||||
"DateCreated",
|
||||
"MediaSources",
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return data?.Items ?? [];
|
||||
}, "jellyfin.getItem");
|
||||
|
||||
export const getContinueWatching = query(
|
||||
async (userId: string): Promise<Entry[]> => {
|
||||
const { data, error } = await client.GET("/Users/{userId}/Items/Resume", {
|
||||
params: {
|
||||
path: {
|
||||
userId,
|
||||
},
|
||||
query: {
|
||||
mediaTypes: ["Video"],
|
||||
fields: ["ProviderIds", "Genres"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const items = (data?.Items ?? []).map(({ Id, Name }) => ({
|
||||
id: Id,
|
||||
title: Name,
|
||||
thumbnail: `${baseUrl}Items/${Id}/Images/Primary`,
|
||||
}));
|
||||
|
||||
return items;
|
||||
},
|
||||
"jellyfin.continueWatching",
|
||||
);
|
|
@ -69,3 +69,5 @@ export const getEntry = query(
|
|||
},
|
||||
"series.get",
|
||||
);
|
||||
|
||||
export { getContinueWatching } from "./apis/jellyfin";
|
||||
|
|
|
@ -3,6 +3,7 @@ import { signIn, signOut } from "~/auth";
|
|||
import { hash } from "~/utilities";
|
||||
import { Avatar, Profile, User } from "../user";
|
||||
import css from "./top.module.css";
|
||||
import { ColorSchemePicker } from "../theme";
|
||||
|
||||
interface TopProps {
|
||||
user: User | undefined;
|
||||
|
@ -53,7 +54,7 @@ export const Top: Component<TopProps> = (props) => {
|
|||
</>
|
||||
)}
|
||||
</Show>
|
||||
{/* <ColorSchemePicker /> */}
|
||||
<ColorSchemePicker />
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,80 +1,88 @@
|
|||
import { ContextProviderProps, createContextProvider } from "@solid-primitives/context";
|
||||
import {
|
||||
ContextProviderProps,
|
||||
createContextProvider,
|
||||
} from "@solid-primitives/context";
|
||||
import { action, createAsyncStore, query, useAction } from "@solidjs/router";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { useSession } from "vinxi/http";
|
||||
|
||||
|
||||
export enum ColorScheme {
|
||||
Auto = 'light dark',
|
||||
Light = 'light',
|
||||
Dark = 'dark',
|
||||
Auto = "light dark",
|
||||
Light = "light",
|
||||
Dark = "dark",
|
||||
}
|
||||
|
||||
export interface State {
|
||||
colorScheme: ColorScheme;
|
||||
hue: number;
|
||||
colorScheme: ColorScheme;
|
||||
hue: number;
|
||||
}
|
||||
|
||||
const getSession = async () => {
|
||||
'use server';
|
||||
"use server";
|
||||
|
||||
return useSession<State>({
|
||||
password: process.env.SESSION_SECRET!,
|
||||
});
|
||||
return useSession<State>({
|
||||
password: process.env.SESSION_SECRET!,
|
||||
});
|
||||
};
|
||||
|
||||
export const getState = query(async () => {
|
||||
'use server';
|
||||
"use server";
|
||||
|
||||
const session = await getSession();
|
||||
const session = await getSession();
|
||||
|
||||
if (Object.getOwnPropertyNames(session.data).length === 0) {
|
||||
await session.update({
|
||||
colorScheme: ColorScheme.Auto,
|
||||
hue: 0,
|
||||
})
|
||||
}
|
||||
if (Object.getOwnPropertyNames(session.data).length === 0) {
|
||||
await session.update({
|
||||
colorScheme: ColorScheme.Auto,
|
||||
hue: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return session.data;
|
||||
}, 'color-scheme');
|
||||
return session.data;
|
||||
}, "color-scheme");
|
||||
|
||||
const setState = action(async (state: State) => {
|
||||
'use server';
|
||||
"use server";
|
||||
|
||||
const session = await getSession();
|
||||
await session.update(prev => ({ ...prev, ...state }));
|
||||
}, 'color-scheme');
|
||||
const session = await getSession();
|
||||
await session.update((prev) => ({ ...prev, ...state }));
|
||||
}, "color-scheme");
|
||||
|
||||
interface ThemeContextType {
|
||||
readonly theme: State;
|
||||
setColorScheme(colorScheme: ColorScheme): void;
|
||||
setHue(colorScheme: number): void;
|
||||
readonly theme: State;
|
||||
setColorScheme(colorScheme: ColorScheme): void;
|
||||
setHue(colorScheme: number): void;
|
||||
}
|
||||
|
||||
const [ThemeContextProvider, useTheme] = createContextProvider<ThemeContextType, ContextProviderProps>((props) => {
|
||||
const [ThemeContextProvider, useTheme] = createContextProvider<
|
||||
ThemeContextType,
|
||||
ContextProviderProps
|
||||
>(
|
||||
(props) => {
|
||||
const updateState = useAction(setState);
|
||||
const state = createAsyncStore(() => getState());
|
||||
|
||||
return {
|
||||
get theme() {
|
||||
return state.latest ?? { colorScheme: null };
|
||||
},
|
||||
get theme() {
|
||||
return state.latest ?? { colorScheme: null };
|
||||
},
|
||||
|
||||
setColorScheme(colorScheme) {
|
||||
updateState({ colorScheme, hue: state.latest!.hue });
|
||||
},
|
||||
setHue(hue) {
|
||||
updateState({ hue, colorScheme: state.latest!.colorScheme });
|
||||
},
|
||||
setColorScheme(colorScheme) {
|
||||
// updateState({ colorScheme, hue: state.latest!.hue });
|
||||
},
|
||||
setHue(hue) {
|
||||
// updateState({ hue, colorScheme: state.latest!.colorScheme });
|
||||
},
|
||||
};
|
||||
}, {
|
||||
},
|
||||
{
|
||||
theme: {
|
||||
colorScheme: ColorScheme.Auto,
|
||||
hue: 180,
|
||||
colorScheme: ColorScheme.Auto,
|
||||
hue: 180,
|
||||
},
|
||||
|
||||
setColorScheme(colorScheme) { },
|
||||
setHue(hue) { },
|
||||
});
|
||||
setColorScheme(colorScheme) {},
|
||||
setHue(hue) {},
|
||||
},
|
||||
);
|
||||
|
||||
export { ThemeContextProvider, useTheme };
|
||||
export { ThemeContextProvider, useTheme };
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export interface User {
|
||||
username: string;
|
||||
name: string;
|
||||
email: string;
|
||||
image: string | null;
|
||||
|
|
|
@ -18,9 +18,15 @@ const load = query(async (): Promise<User | undefined> => {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { name, email, image = null } = session.user;
|
||||
const {
|
||||
preferred_username,
|
||||
name,
|
||||
email,
|
||||
image = null,
|
||||
...user
|
||||
} = session.user;
|
||||
|
||||
return { name, email, image };
|
||||
return { username: preferred_username, name, email, image };
|
||||
}, "session");
|
||||
|
||||
export const route = {
|
||||
|
|
|
@ -1,34 +1,40 @@
|
|||
import { Title } from "@solidjs/meta";
|
||||
import { createAsync, query } from "@solidjs/router";
|
||||
import { createAsync } from "@solidjs/router";
|
||||
import { Overview } from "~/features/overview";
|
||||
import { listCategories, getEntry } from "~/features/content";
|
||||
import { createEffect, Show } from "solid-js";
|
||||
|
||||
const load = query(async () => {
|
||||
"use server";
|
||||
|
||||
// const response =
|
||||
}, "home.data");
|
||||
import {
|
||||
listCategories,
|
||||
getEntry,
|
||||
getContinueWatching,
|
||||
} from "~/features/content";
|
||||
import { Show } from "solid-js";
|
||||
import { List } from "~/components/list";
|
||||
import { ListItem } from "~/features/overview/list-item";
|
||||
|
||||
export const route = {
|
||||
preload: async () => ({
|
||||
highlight: await getEntry("14"),
|
||||
categories: await listCategories(),
|
||||
continue: await getContinueWatching("a9c51af84bf54578a99ab4dd0ebf0763"),
|
||||
}),
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
const highlight = createAsync(() => getEntry("14"));
|
||||
const categories = createAsync(() => listCategories());
|
||||
|
||||
createEffect(() => {
|
||||
console.log(highlight(), categories());
|
||||
});
|
||||
const continueWatching = createAsync(() =>
|
||||
getContinueWatching("a9c51af84bf54578a99ab4dd0ebf0763"),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Home</Title>
|
||||
|
||||
<Show when={continueWatching()}>
|
||||
<List label="Continue watching" items={continueWatching()}>
|
||||
{(item) => <ListItem entry={item()} />}
|
||||
</List>
|
||||
</Show>
|
||||
|
||||
<Show when={highlight() && categories()}>
|
||||
<Overview highlight={highlight()!} categories={categories()!} />
|
||||
</Show>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue