worked on various things
This commit is contained in:
parent
f3cb35653e
commit
3c35b89250
18 changed files with 156 additions and 126 deletions
|
@ -1,14 +1,15 @@
|
|||
import { defineConfig } from "@solidjs/start/config";
|
||||
import solidSvg from "vite-plugin-solid-svg";
|
||||
import devtools from "solid-devtools/vite";
|
||||
import { build, fileURLToPath } from "bun";
|
||||
|
||||
export default defineConfig({
|
||||
vite: {
|
||||
plugins: [
|
||||
devtools({
|
||||
autoname: true,
|
||||
}),
|
||||
solidSvg(),
|
||||
// devtools({
|
||||
// autoname: true,
|
||||
// }),
|
||||
// solidSvg(),
|
||||
],
|
||||
},
|
||||
solid: {
|
||||
|
@ -19,7 +20,7 @@ export default defineConfig({
|
|||
server: {
|
||||
preset: "bun",
|
||||
prerender: {
|
||||
routes: ["/sitemaps.xml"],
|
||||
routes: ["/sitemap.xml"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
BIN
auth.sqlite
BIN
auth.sqlite
Binary file not shown.
7
better-auth_migrations/2025-06-17T07-59-07.464Z.sql
Normal file
7
better-auth_migrations/2025-06-17T07-59-07.464Z.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
create table "user" ("id" text not null primary key, "name" text not null, "email" text not null unique, "emailVerified" integer not null, "image" text, "createdAt" date not null, "updatedAt" date not null, "preferred_username" text not null, "username" text not null);
|
||||
|
||||
create table "session" ("id" text not null primary key, "expiresAt" date not null, "token" text not null unique, "createdAt" date not null, "updatedAt" date not null, "ipAddress" text, "userAgent" text, "userId" text not null references "user" ("id"));
|
||||
|
||||
create table "account" ("id" text not null primary key, "accountId" text not null, "providerId" text not null, "userId" text not null references "user" ("id"), "accessToken" text, "refreshToken" text, "idToken" text, "accessTokenExpiresAt" date, "refreshTokenExpiresAt" date, "scope" text, "password" text, "createdAt" date not null, "updatedAt" date not null);
|
||||
|
||||
create table "verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date, "updatedAt" date);
|
23
flake.nix
Normal file
23
flake.nix
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
description = "Streamarr";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgsnixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
bix = {
|
||||
url = "github:knarkzel/bix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
packages = bix.buildBunPackage {
|
||||
src = ./.;
|
||||
packages = ./package.json;
|
||||
};
|
||||
});
|
||||
}
|
6
src/auth.client.ts
Normal file
6
src/auth.client.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { createAuthClient } from "better-auth/solid";
|
||||
import { genericOAuthClient } from "better-auth/client/plugins";
|
||||
|
||||
export const { signIn, signOut, useSession, ...client } = createAuthClient({
|
||||
plugins: [genericOAuthClient()],
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import { betterAuth } from "better-auth";
|
||||
import { genericOAuth } from "better-auth/plugins";
|
||||
import { createAuthClient } from "better-auth/solid";
|
||||
import { genericOAuthClient } from "better-auth/client/plugins";
|
||||
import { profile } from "bun:jsc";
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
export const auth = betterAuth({
|
||||
|
@ -20,18 +19,10 @@ export const auth = betterAuth({
|
|||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
preferred_username: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
username: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
profile: {
|
||||
type: "string",
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
@ -54,12 +45,10 @@ export const auth = betterAuth({
|
|||
],
|
||||
accessType: "offline",
|
||||
pkce: true,
|
||||
mapProfileToUser: ({ id, name, email, image, preferred_username, emailVerified }) =>
|
||||
({ id, name, email, emailVerified, image, username: preferred_username }),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const { signIn, signOut, useSession, ...client } = createAuthClient({
|
||||
plugins: [genericOAuthClient()],
|
||||
});
|
|
@ -219,7 +219,7 @@ export const getItemStream = query(
|
|||
itemId,
|
||||
},
|
||||
query: {
|
||||
startTimeTicks: userData?.playbackPositionTicks,
|
||||
// startTimeTicks: userData?.playbackPositionTicks,
|
||||
},
|
||||
},
|
||||
parseAs: 'stream',
|
||||
|
@ -392,7 +392,7 @@ const toEntry = (item: components['schemas']['BaseItemDto']): Entry => {
|
|||
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'),
|
||||
thumbnail: new URL(`/Items/${item.Id}/Images/Primary`, getBaseUrl()),
|
||||
image: new URL(`/Items/${item.Id}/Images/Backdrop`, getBaseUrl()),
|
||||
providers: {
|
||||
jellyfin: item.Id
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
"use server";
|
||||
|
||||
import type { Category, Entry } from "./types";
|
||||
import { query, revalidate } from "@solidjs/router";
|
||||
import { listItemIds, getContinueWatching, getItemStream, getRandomItems, getItemUserData } from "./apis/jellyfin";
|
||||
|
@ -26,6 +24,8 @@ const lookupTable = query(async () => {
|
|||
|
||||
export const getHighlights = () => getContinueWatching(jellyfinUserId);
|
||||
export const getStream = query(async (id: string, range: string) => {
|
||||
'use server';
|
||||
|
||||
const table = await lookupTable();
|
||||
const ids = table[id];
|
||||
const manager = id[0] === 'm' ? 'radarr' : 'sonarr'
|
||||
|
@ -59,11 +59,12 @@ export const getStream = query(async (id: string, range: string) => {
|
|||
|
||||
const res = await ((manager === 'radarr' ? addMovie : addSeries)(id.slice(1)));
|
||||
|
||||
revalidate(lookupTable.keyFor())
|
||||
|
||||
revalidate(lookupTable.keyFor());
|
||||
}, 'content.stream');
|
||||
|
||||
export const listCategories = query(async (): Promise<Category[]> => {
|
||||
'use server';
|
||||
|
||||
return [
|
||||
// { label: "Continue", entries: await getContinueWatching(jellyfinUserId) },
|
||||
{
|
||||
|
@ -76,18 +77,24 @@ export const listCategories = query(async (): Promise<Category[]> => {
|
|||
}, "content.categories.list");
|
||||
|
||||
export const getEntryFromSlug = query(
|
||||
async (slug: string): Promise<Entry | undefined> => getEntry(slug.match(/\w+$/)![0]),
|
||||
async (slug: string): Promise<Entry | undefined> => {
|
||||
'use server';
|
||||
|
||||
return getEntry(slug.match(/\w+$/)![0]);
|
||||
},
|
||||
"content.getFromSlug",
|
||||
);
|
||||
|
||||
export const getEntry = query(
|
||||
async (id: Entry["id"]): Promise<Entry | undefined> => {
|
||||
'use server';
|
||||
|
||||
const [entry, userData] = await Promise.all([
|
||||
getTmdbEntry(id),
|
||||
getEntryUserData(id)
|
||||
] as const);
|
||||
|
||||
if (entry && userData) {
|
||||
if (entry !== undefined && userData !== undefined) {
|
||||
entry['offset'] = ticksToSeconds(userData.playbackPositionTicks ?? 0);
|
||||
}
|
||||
|
||||
|
@ -98,6 +105,8 @@ export const getEntry = query(
|
|||
|
||||
export const getEntryUserData = query(
|
||||
async (id: string): ReturnType<typeof getItemUserData> => {
|
||||
'use server';
|
||||
|
||||
const table = await lookupTable();
|
||||
const { jellyfin } = table[id] ?? {};
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@ import {
|
|||
} from "@solid-primitives/context";
|
||||
import { Accessor, createEffect, on, onMount, Setter } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { createEventListenerMap } from "@solid-primitives/event-listener";
|
||||
import {
|
||||
createEventListener,
|
||||
createEventListenerMap,
|
||||
} from "@solid-primitives/event-listener";
|
||||
import { createFullscreen } from "@solid-primitives/fullscreen";
|
||||
|
||||
type State = "playing" | "paused";
|
||||
|
@ -93,6 +96,7 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
currentTime: () => store.currentTime,
|
||||
|
||||
setTime(time) {
|
||||
console.log("time is set via api!", time);
|
||||
video!.currentTime = time;
|
||||
},
|
||||
|
||||
|
@ -124,8 +128,6 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
},
|
||||
};
|
||||
|
||||
console.log(props.root, props.video);
|
||||
|
||||
if (isServer || video === undefined) {
|
||||
return api;
|
||||
}
|
||||
|
@ -147,15 +149,6 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
video.volume = store.volume.value;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
console.log(store.currentTime, props.offset);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
setStore("duration", video.duration);
|
||||
setStore("currentTime", video.currentTime);
|
||||
});
|
||||
|
||||
createEventListenerMap(video, {
|
||||
play(e) {
|
||||
setStore("state", "playing");
|
||||
|
@ -167,6 +160,8 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
setStore("duration", video.duration);
|
||||
},
|
||||
timeupdate(e) {
|
||||
console.log("time update", video.currentTime, e);
|
||||
|
||||
setStore("currentTime", video.currentTime);
|
||||
},
|
||||
volumeChange() {
|
||||
|
@ -175,14 +170,15 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
progress(e) {
|
||||
const timeRanges = video.buffered;
|
||||
|
||||
setStore(
|
||||
"buffered",
|
||||
timeRanges.length > 0 ? timeRanges.end(timeRanges.length - 1) : 0
|
||||
);
|
||||
},
|
||||
canplay() {
|
||||
console.log("can play!");
|
||||
// setStore("loading", false);
|
||||
if (timeRanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const range = timeRanges.end(timeRanges.length - 1);
|
||||
|
||||
console.log(range);
|
||||
|
||||
setStore("buffered", range);
|
||||
},
|
||||
canplaythrough() {
|
||||
console.log("can play through!");
|
||||
|
@ -191,6 +187,10 @@ export const [VideoProvider, useVideo] = createContextProvider<
|
|||
waiting() {
|
||||
setStore("loading", true);
|
||||
},
|
||||
loadedmetadata() {
|
||||
console.log("metadata loaded");
|
||||
video.currentTime = props.offset ?? 0;
|
||||
},
|
||||
});
|
||||
|
||||
return api;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component } from "solid-js";
|
||||
import { Component, createEffect, on } from "solid-js";
|
||||
import { useVideo } from "../context";
|
||||
import css from "./seekBar.module.css";
|
||||
|
||||
|
@ -7,6 +7,15 @@ interface SeekBarProps {}
|
|||
export const SeekBar: Component<SeekBarProps> = () => {
|
||||
const video = useVideo();
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => [video.duration(), video.buffered(), video.currentTime()] as const,
|
||||
([duration, buffered, currentTime]) => {
|
||||
console.log({ duration, buffered, currentTime });
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<div class={css.container}>
|
||||
<span class={css.time}>{formatTime(video.currentTime())}</span>
|
||||
|
|
|
@ -75,7 +75,14 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
// : "";
|
||||
// });
|
||||
|
||||
// createEffect(on(thumbnails, (thumbnails) => {}));
|
||||
// createEffect(
|
||||
// on(
|
||||
// () => props.entry,
|
||||
// (entry) => {
|
||||
// console.log(entry);
|
||||
// }
|
||||
// )
|
||||
// );
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -86,8 +93,6 @@ export const Player: Component<PlayerProps> = (props) => {
|
|||
props.entry["offset"] ? `#t=${props.entry["offset"]}` : ""
|
||||
}`}
|
||||
poster={props.entry.image}
|
||||
lang="en"
|
||||
autoplay
|
||||
>
|
||||
{/* <track
|
||||
default
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Show } from "solid-js";
|
||||
import { signIn, signOut, client } from "~/auth";
|
||||
import { signIn, signOut, client } from "~/auth.client";
|
||||
import { Avatar, Profile, User } from "../user";
|
||||
import { ColorSchemePicker } from "../theme";
|
||||
import css from "./top.module.css";
|
||||
|
|
|
@ -67,10 +67,10 @@ const [ThemeContextProvider, useTheme] = createContextProvider<
|
|||
},
|
||||
|
||||
setColorScheme(colorScheme) {
|
||||
updateState({ colorScheme, hue: state.latest!.hue });
|
||||
// updateState({ colorScheme, hue: state.latest!.hue });
|
||||
},
|
||||
setHue(hue) {
|
||||
updateState({ hue, colorScheme: state.latest!.colorScheme });
|
||||
// updateState({ hue, colorScheme: state.latest!.colorScheme });
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Meta } from "@solidjs/meta";
|
|||
import { query, createAsync } from "@solidjs/router";
|
||||
import { createEffect, on, ParentProps } from "solid-js";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import { auth } from "~/auth";
|
||||
import { auth } from "~/auth.server";
|
||||
import { Shell } from "~/features/shell";
|
||||
import { useTheme } from "~/features/theme";
|
||||
import { User } from "~/features/user";
|
||||
|
@ -10,36 +10,15 @@ import { User } from "~/features/user";
|
|||
const load = query(async (): Promise<User | undefined> => {
|
||||
"use server";
|
||||
|
||||
const cookies = Object.fromEntries(
|
||||
getRequestEvent()!
|
||||
.request.headers.get("cookie")!
|
||||
.split(";")
|
||||
.map((c) => c.trim())
|
||||
.map((cookie) => {
|
||||
const index = cookie.indexOf("=");
|
||||
|
||||
return [
|
||||
cookie.slice(0, index),
|
||||
decodeURIComponent(cookie.slice(index + 1)),
|
||||
];
|
||||
})
|
||||
);
|
||||
|
||||
const session = await auth.api.getSession(getRequestEvent()!.request);
|
||||
|
||||
if (session === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
preferred_username,
|
||||
name,
|
||||
email,
|
||||
image = null,
|
||||
...user
|
||||
} = session.user;
|
||||
const { username, name, email, image = null } = session.user;
|
||||
|
||||
return { username: preferred_username, name, email, image };
|
||||
return { username, name, email, image };
|
||||
}, "session");
|
||||
|
||||
export const route = {
|
||||
|
@ -52,18 +31,14 @@ export default function ShellPage(props: ParentProps) {
|
|||
const user = createAsync(() => load());
|
||||
const themeContext = useTheme();
|
||||
|
||||
createEffect(() => {
|
||||
console.log(user());
|
||||
});
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => themeContext.theme.colorScheme,
|
||||
(colorScheme) => {
|
||||
document.documentElement.dataset.theme = colorScheme;
|
||||
}
|
||||
)
|
||||
);
|
||||
// createEffect(
|
||||
// on(
|
||||
// () => themeContext.theme.colorScheme,
|
||||
// (colorScheme) => {
|
||||
// document.documentElement.dataset.theme = colorScheme;
|
||||
// }
|
||||
// )
|
||||
// );
|
||||
|
||||
return (
|
||||
<Shell user={user()}>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
RouteDefinition,
|
||||
useParams,
|
||||
} from "@solidjs/router";
|
||||
import { Show } from "solid-js";
|
||||
import { createEffect, Show } from "solid-js";
|
||||
import { createSlug, getEntryFromSlug } from "~/features/content";
|
||||
import { Player } from "~/features/player";
|
||||
import { Title } from "@solidjs/meta";
|
||||
|
@ -54,10 +54,10 @@ export default function Item() {
|
|||
|
||||
return (
|
||||
<div class={css.page}>
|
||||
<Title>{entry()?.title}</Title>
|
||||
<Show when={entry()} fallback="Some kind of pretty 404 page I guess">
|
||||
{(entry) => (
|
||||
<>
|
||||
<Title>{entry().title}</Title>
|
||||
<Player entry={entry()} />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { auth } from "~/auth";
|
||||
import { auth } from "~/auth.server";
|
||||
import { toSolidStartHandler } from "better-auth/solid-start";
|
||||
|
||||
export const { GET, POST } = toSolidStartHandler(auth);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { SitemapStream, streamToPromise } from 'sitemap'
|
||||
import { App } from 'vinxi';
|
||||
import { App, } from 'vinxi';
|
||||
|
||||
const BASE_URL = 'https://ca-euw-prd-calque-app.purplecoast-f5b7f657.westeurope.azurecontainerapps.io';
|
||||
const BASE_URL = 'http://localhost:3000';
|
||||
|
||||
export async function GET() {
|
||||
|
||||
|
@ -22,7 +22,13 @@ export async function GET() {
|
|||
}
|
||||
|
||||
const getRoutes = async () => {
|
||||
const router = ((globalThis as any).app as App).getRouter('client').internals.routes;
|
||||
const app = (globalThis as any).app as App;
|
||||
|
||||
const kaas = app.getRouter('client');
|
||||
|
||||
console.log(kaas.internals);
|
||||
|
||||
const router = app.getRouter('client').internals?.routes;
|
||||
|
||||
if (router === undefined) {
|
||||
return [];
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue