lovely. got a couple of partial implementations....

This commit is contained in:
Chris Kruining 2025-04-03 17:27:35 +02:00
parent 89ca4013fd
commit 89f526e9d9
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
14 changed files with 180 additions and 154 deletions

View file

@ -1,20 +1,21 @@
import { toSlug } from "~/utilities";
import type { Entry } from "./types";
export const entries = new Map<number, Entry>([
{ id: 1, title: 'Realtime with Bill Maher', thumbnail: 'https://www.themoviedb.org/t/p/w342/pbpoLLp4kvnYVfnEGiEhagpJuVZ.jpg' },
{ id: 2, title: 'Binnelanders', thumbnail: 'https://www.themoviedb.org/t/p/w342/v9nGSRx5lFz6KEgfmgHJMSgaARC.jpg' },
{ id: 3, title: 'Family guy', thumbnail: 'https://www.themoviedb.org/t/p/w342/y0HUz4eUNUe3TeEd8fQWYazPaC7.jpg' },
{ id: 4, title: 'The Simpsons', thumbnail: 'https://www.themoviedb.org/t/p/w342/vHqeLzYl3dEAutojCO26g0LIkom.jpg' },
{ id: 5, title: 'Breaking Bad', thumbnail: 'https://www.themoviedb.org/t/p/w342/ztkUQFLlC19CCMYHW9o1zWhJRNq.jpg' },
{ id: 6, title: 'Loki', thumbnail: 'https://www.themoviedb.org/t/p/w342/oJdVHUYrjdS2IqiNztVIP4GPB1p.jpg' },
{ id: 7, title: 'Dark desire', thumbnail: 'https://www.themoviedb.org/t/p/w342/uxFNAo2A6ZRcgNASLk02hJUbybn.jpg' },
{ id: 8, title: 'Bridgerton', thumbnail: 'https://www.themoviedb.org/t/p/w342/luoKpgVwi1E5nQsi7W0UuKHu2Rq.jpg' },
{ id: 9, title: 'Naruto', thumbnail: 'https://www.themoviedb.org/t/p/w342/xppeysfvDKVx775MFuH8Z9BlpMk.jpg' },
{ id: 11, title: 'Teenwolf', thumbnail: 'https://www.themoviedb.org/t/p/w342/fmlMmxSBgPEunHS5gjokIej048g.jpg' },
{ id: 12, title: 'Record of Ragnarok', thumbnail: 'https://www.themoviedb.org/t/p/w342/kTs2WNZOukpWdNhoRlH94pSJ3xf.jpg' },
{ id: 13, title: 'The Mandalorian', thumbnail: 'https://www.themoviedb.org/t/p/w342/eU1i6eHXlzMOlEq0ku1Rzq7Y4wA.jpg' },
export const entries = new Map<string, Entry>([
{ id: '1', title: 'Realtime with Bill Maher', thumbnail: 'https://www.themoviedb.org/t/p/w342/pbpoLLp4kvnYVfnEGiEhagpJuVZ.jpg' },
{ id: '2', title: 'Binnelanders', thumbnail: 'https://www.themoviedb.org/t/p/w342/v9nGSRx5lFz6KEgfmgHJMSgaARC.jpg' },
{ id: '3', title: 'Family guy', thumbnail: 'https://www.themoviedb.org/t/p/w342/y0HUz4eUNUe3TeEd8fQWYazPaC7.jpg' },
{ id: '4', title: 'The Simpsons', thumbnail: 'https://www.themoviedb.org/t/p/w342/vHqeLzYl3dEAutojCO26g0LIkom.jpg' },
{ id: '5', title: 'Breaking Bad', thumbnail: 'https://www.themoviedb.org/t/p/w342/ztkUQFLlC19CCMYHW9o1zWhJRNq.jpg' },
{ id: '6', title: 'Loki', thumbnail: 'https://www.themoviedb.org/t/p/w342/oJdVHUYrjdS2IqiNztVIP4GPB1p.jpg' },
{ id: '7', title: 'Dark desire', thumbnail: 'https://www.themoviedb.org/t/p/w342/uxFNAo2A6ZRcgNASLk02hJUbybn.jpg' },
{ id: '8', title: 'Bridgerton', thumbnail: 'https://www.themoviedb.org/t/p/w342/luoKpgVwi1E5nQsi7W0UuKHu2Rq.jpg' },
{ id: '9', title: 'Naruto', thumbnail: 'https://www.themoviedb.org/t/p/w342/xppeysfvDKVx775MFuH8Z9BlpMk.jpg' },
{ id: '11', title: 'Teenwolf', thumbnail: 'https://www.themoviedb.org/t/p/w342/fmlMmxSBgPEunHS5gjokIej048g.jpg' },
{ id: '12', title: 'Record of Ragnarok', thumbnail: 'https://www.themoviedb.org/t/p/w342/kTs2WNZOukpWdNhoRlH94pSJ3xf.jpg' },
{ id: '13', title: 'The Mandalorian', thumbnail: 'https://www.themoviedb.org/t/p/w342/eU1i6eHXlzMOlEq0ku1Rzq7Y4wA.jpg' },
{
id: 14,
id: '14',
title: 'Wednesday',
thumbnail: 'https://www.themoviedb.org/t/p/w342/9PFonBhy4cQy7Jz20NpMygczOkv.jpg',
image: 'https://www.themoviedb.org/t/p/original/iHSwvRVsRyxpX7FE7GbviaDvgGZ.jpg',
@ -27,4 +28,6 @@ export const entries = new Map<number, Entry>([
].map((entry) => [entry.id, entry]));
export const emptyEntry = Object.freeze<Entry>({ id: 0, title: '' });
export const emptyEntry = Object.freeze<Entry>({ id: '0', title: '' });
export const createSlug = (entry: Entry) => toSlug(`${entry.title}-${entry.id}`);

View file

@ -1,4 +1,4 @@
export type * from './types';
export { emptyEntry } from './data';
export { emptyEntry, createSlug } from './data';
export * from './service';

View file

@ -1,8 +1,8 @@
import type { Category, Entry } from './types';
import { cache } from "@solidjs/router";
import { query } from "@solidjs/router";
import { entries } from './data';
export const listCategories = cache(async (): Promise<Category[]> => {
export const listCategories = query(async (): Promise<Category[]> => {
"use server";
return [
@ -25,7 +25,7 @@ export const listCategories = cache(async (): Promise<Category[]> => {
];
}, 'series.categories.list');
export const getEntry = cache(async (id: Entry['id']): Promise<Entry | undefined> => {
export const getEntry = query(async (id: Entry['id']): Promise<Entry | undefined> => {
"use server";
return entries.get(id);

View file

@ -5,7 +5,7 @@ export interface Category {
}
export interface Entry {
id: number;
id: string;
title: string;
summary?: string;
releaseDate?: string;

View file

@ -1,8 +1,11 @@
import type { Entry } from "../content";
import { Component } from "solid-js";
import { createSlug } from "../content";
import { Component, createMemo } from "solid-js";
import css from "./list-item.module.css";
export const ListItem: Component<{ entry: Entry }> = (props) => {
const slug = createMemo(() => createSlug(props.entry));
return (
<div class={css.listItem}>
<img src={props.entry.thumbnail} />
@ -10,7 +13,7 @@ export const ListItem: Component<{ entry: Entry }> = (props) => {
<main>
<strong>{props.entry.title}</strong>
<a href={`/watch/${props.entry.id}`}>Watch now</a>
<a href={`/watch/${slug()}`}>Watch now</a>
</main>
</div>
);

View file

@ -19,16 +19,6 @@ type OverviewProps = {
export const Overview: Component<OverviewProps> = (props) => {
const [container, setContainer] = createSignal<HTMLElement>();
onMount(() => {
new MutationObserver(() => {
container()
?.querySelector<HTMLElement>(
`.${css.list} > ul > div:nth-child(4) > main > a`,
)
?.focus({ preventScroll: true });
}).observe(document.body, { subtree: true, childList: true });
});
return (
<div ref={setContainer} class={css.container}>
<Hero class={css.hero} entry={props.highlight}></Hero>

View file

@ -1,86 +1,44 @@
import {
createEventListenerMap,
makeEventListener,
makeEventListenerStack,
createEventSignal,
} from "@solid-primitives/event-listener";
import { createAsync, json, query } from "@solidjs/router";
import { query } from "@solidjs/router";
import {
Component,
createEffect,
createMemo,
createResource,
createSignal,
For,
onMount,
untrack,
} from "solid-js";
import { isServer } from "solid-js/web";
const streamKaas = query(async () => {
"use server";
const stream = new WritableStream();
async function* packetGenerator() {
for (let i = 0; i < 10; i++) {
yield `packet ${i}`;
await new Promise((res) => setTimeout(res, 1000));
}
}
(async () => {
const writer = stream.getWriter();
try {
await writer.ready;
for await (const packet of packetGenerator()) {
writer.write(packet);
}
} finally {
writer.releaseLock();
}
// response.body.wr
})();
return new Response(packetGenerator());
}, "kaas");
interface PlayerProps {
id: string;
}
export const Player: Component<PlayerProps> = (props) => {
const [video, setVideo] = createSignal<HTMLVideoElement>();
const stream = createAsync(async () => {
const res = await streamKaas();
const [video, setVideo] = createSignal<HTMLVideoElement>(undefined as unknown as HTMLVideoElement);
console.log(res);
const onDurationChange = createEventSignal(video, 'durationchange');
const onTimeUpdate = createEventSignal(video, 'timeupdate');
return "";
const duration = createMemo(() => {
onDurationChange();
onTimeUpdate();
return video()?.duration ?? 100;
});
// const [kaas, { refetch }] = createResource(async () => {
// if (isServer) {
// return "";
// }
// const response = await fetch("http://localhost:3000/api/stream/video", {
// method: "GET",
// });
// console.log(response.body);
const currentTime = createMemo(() => {
onTimeUpdate();
// for await (const packet of response.body) {
// console.log(new TextDecoder().decode(packet));
// }
return video()?.currentTime ?? 0;
});
// return "";
// });
// onMount(() => refetch());
// createEffect(() => console.log(stream()));
// const progress = createMemo(() => {
// const
// });
createEffect(() => {
console.log(duration(), currentTime());
});
createEventListenerMap(() => video()!, {
durationchange(e) {
@ -102,12 +60,15 @@ export const Player: Component<PlayerProps> = (props) => {
console.log("seeking", e);
},
stalled(e) {
console.log("stalled", e);
console.log("stalled (meaning downloading data failed)", e, video()!.error);
},
play(e) {
console.log("play", e);
},
canplay(e) {
console.log("canplay", e);
},
playing(e) {
console.log("playing", e);
},
@ -130,9 +91,9 @@ export const Player: Component<PlayerProps> = (props) => {
console.log(e);
},
timeupdate(e) {
console.log("timeupdate", e);
},
// timeupdate(e) {
// console.log("timeupdate", e);
// },
});
const toggle = () => {
@ -149,13 +110,14 @@ export const Player: Component<PlayerProps> = (props) => {
<>
<h1>{props.id}</h1>
<video ref={setVideo} muted preload="metadata">
<source src="/videos/bbb_sunflower_2160p_60fps_normal.mp4" />
</video>
<video ref={setVideo} width="1280px" height="720px" muted src="/api/stream/video" />
<button onclick={toggle}>play/pause</button>
<progress />
<span style={{ '--duration': duration(), '--current-time': currentTime() }} />
<span data-duration={duration()} data-current-time={currentTime()} />
<progress max={duration()} value={currentTime()} />
</>
);
};