diff --git a/bun.lock b/bun.lock
index fba0103..93e2cbe 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,6 +5,7 @@
"name": "streamarr",
"dependencies": {
"@solid-primitives/context": "^0.3.0",
+ "@solid-primitives/event-listener": "^2.4.0",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3",
"@solidjs/start": "^1.1.3",
diff --git a/package.json b/package.json
index f2690ef..2c9590d 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
},
"dependencies": {
"@solid-primitives/context": "^0.3.0",
+ "@solid-primitives/event-listener": "^2.4.0",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3",
"@solidjs/start": "^1.1.3",
diff --git a/src/api/stream/video.ts b/src/api/stream/video.ts
new file mode 100644
index 0000000..c9b98ae
--- /dev/null
+++ b/src/api/stream/video.ts
@@ -0,0 +1,20 @@
+import { json } from "@solidjs/router";
+import vid from "../../../public/videos/bbb_sunflower_2160p_60fps_normal.mp4";
+import { APIEvent } from "@solidjs/start/server";
+
+export const GET = async (event: APIEvent) => {
+ "use server";
+
+ console.log(event);
+
+ // async function* packetGenerator() {
+ // for (let i = 0; i < 10; i++) {
+ // yield `packet ${i}`;
+ // await new Promise((res) => setTimeout(res, 1000));
+ // }
+ // }
+
+ // console.log(vid);
+
+ return "OK";
+};
diff --git a/src/features/overview/list-item.tsx b/src/features/overview/list-item.tsx
index 218558e..8c196b8 100644
--- a/src/features/overview/list-item.tsx
+++ b/src/features/overview/list-item.tsx
@@ -10,7 +10,7 @@ export const ListItem: Component<{ entry: Entry }> = (props) => {
{props.entry.title}
- Watch now
+ Watch now
);
diff --git a/src/features/overview/overview.tsx b/src/features/overview/overview.tsx
index efc8919..dd05c24 100644
--- a/src/features/overview/overview.tsx
+++ b/src/features/overview/overview.tsx
@@ -1,4 +1,10 @@
-import { Component, createEffect, createSignal, Index, onMount } from "solid-js";
+import {
+ Component,
+ createEffect,
+ createSignal,
+ Index,
+ onMount,
+} from "solid-js";
import type { Entry, Category } from "../content";
import { ListItem } from "./list-item";
import { List } from "~/components/list";
@@ -15,7 +21,11 @@ export const Overview: Component = (props) => {
onMount(() => {
new MutationObserver(() => {
- container()?.querySelector(`.${css.list} > ul > div:nth-child(4) > main > a`)?.focus({ preventScroll: true });
+ container()
+ ?.querySelector(
+ `.${css.list} > ul > div:nth-child(4) > main > a`,
+ )
+ ?.focus({ preventScroll: true });
}).observe(document.body, { subtree: true, childList: true });
});
@@ -25,11 +35,15 @@ export const Overview: Component = (props) => {
{(category) => (
-
+
{(entry) => }
)}
);
-}
+};
diff --git a/src/features/player/index.ts b/src/features/player/index.ts
new file mode 100644
index 0000000..1372f50
--- /dev/null
+++ b/src/features/player/index.ts
@@ -0,0 +1 @@
+export { Player } from "./player";
diff --git a/src/features/player/player.module.css b/src/features/player/player.module.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/features/player/player.tsx b/src/features/player/player.tsx
new file mode 100644
index 0000000..23c3d14
--- /dev/null
+++ b/src/features/player/player.tsx
@@ -0,0 +1,161 @@
+import {
+ createEventListenerMap,
+ makeEventListener,
+ makeEventListenerStack,
+} from "@solid-primitives/event-listener";
+import { createAsync, json, query } from "@solidjs/router";
+import {
+ Component,
+ createEffect,
+ createMemo,
+ createResource,
+ createSignal,
+ onMount,
+} 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 = (props) => {
+ const [video, setVideo] = createSignal();
+ const stream = createAsync(async () => {
+ const res = await streamKaas();
+
+ console.log(res);
+
+ return "";
+ });
+ // const [kaas, { refetch }] = createResource(async () => {
+ // if (isServer) {
+ // return "";
+ // }
+ // const response = await fetch("http://localhost:3000/api/stream/video", {
+ // method: "GET",
+ // });
+
+ // console.log(response.body);
+
+ // for await (const packet of response.body) {
+ // console.log(new TextDecoder().decode(packet));
+ // }
+
+ // return "";
+ // });
+
+ // onMount(() => refetch());
+
+ // createEffect(() => console.log(stream()));
+
+ // const progress = createMemo(() => {
+ // const
+ // });
+
+ createEventListenerMap(() => video()!, {
+ durationchange(e) {
+ console.log("durationchange", e);
+ },
+ loadeddata(e) {
+ console.log("loadeddata", e);
+ },
+ loadedmetadata(e) {
+ console.log("loadedmetadata", e);
+ },
+ ratechange(e) {
+ console.log("ratechange", e);
+ },
+ seeked(e) {
+ console.log("seeked", e);
+ },
+ seeking(e) {
+ console.log("seeking", e);
+ },
+ stalled(e) {
+ console.log("stalled", e);
+ },
+
+ play(e) {
+ console.log("play", e);
+ },
+ playing(e) {
+ console.log("playing", e);
+ },
+ pause(e) {
+ console.log("pause", e);
+ },
+ suspend(e) {
+ console.log("suspend", e);
+ },
+
+ volumechange(e) {
+ console.log("volumechange", e);
+ },
+
+ waiting(e) {
+ console.log("waiting", e);
+ },
+
+ progress(e) {
+ console.log(e);
+ },
+
+ timeupdate(e) {
+ console.log("timeupdate", e);
+ },
+ });
+
+ const toggle = () => {
+ const el = video();
+
+ if (!el) {
+ return;
+ }
+
+ el[el.paused ? "play" : "pause"]();
+ };
+
+ return (
+ <>
+ {props.id}
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/features/shell/nav.module.css b/src/features/shell/nav.module.css
new file mode 100644
index 0000000..cbcdb85
--- /dev/null
+++ b/src/features/shell/nav.module.css
@@ -0,0 +1,171 @@
+.nav {
+ grid-area: 2 / 1 / 3 / 2;
+ display: block grid;
+ grid-auto-flow: row;
+ justify-content: space-between;
+ inline-size: 5em;
+ block-size: 100%;
+ padding: 1em;
+ background: inherit;
+ z-index: 0;
+ transition: z-index 0.3s step-end;
+
+ & > ul {
+ position: relative;
+ display: block grid;
+ grid-template-columns: 2.5rem auto;
+ align-content: center;
+ inline-size: 4rem;
+ gap: 1rem;
+ transform-origin: left center;
+ padding: 0;
+ padding-inline-start: 0.5rem;
+ margin: 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ inset-inline-start: 100%;
+ inset-block: 0;
+ inline-size: 20vw;
+ /* background:
+ radial-gradient(ellipse at left center 100% 100%, #f00, transparent),
+ linear-gradient(to right, #0003, transparent); */
+ background-image: linear-gradient(to right, #0003, transparent);
+ mask: radial-gradient(
+ ellipse 20vw 100% at left center,
+ black,
+ transparent
+ );
+ backdrop-filter: blur(5px);
+ opacity: 0;
+ transition: opacity 0.3s var(--ease-3);
+ }
+
+ & > a {
+ position: relative;
+ grid-column: span 2;
+ display: block grid;
+ grid-template-columns: subgrid;
+ align-items: center;
+ text-decoration: none;
+ transform-origin: center left;
+ transition:
+ transform 2s var(--ease-spring-5),
+ opacity 0.3s var(--ease-3);
+ color: var(--stone-4);
+ font-size: 2rem;
+ line-height: 1.5;
+
+ & > span {
+ opacity: 0;
+ transition: opacity 0.3s var(--ease-3);
+ text-shadow: 0 0 1em #000;
+ }
+
+ & > svg {
+ fill: var(--stone-4);
+ inline-size: 2.5rem;
+ block-size: 2.5rem;
+ }
+
+ &.active {
+ color: var(--yellow-4);
+ list-style: disc;
+
+ &::before {
+ content: "•";
+ position: absolute;
+ inset-inline-start: -1rem;
+ }
+
+ & > svg {
+ fill: var(--yellow-4);
+ }
+ }
+ }
+
+ &:has(a:is(:hover, :focus))::before {
+ opacity: 1;
+ }
+
+ &:has(a:is(:hover, :focus)) > a:not(:is(:hover, :focus)) {
+ opacity: 0.25;
+ }
+
+ &:has(a:is(:hover, :focus)) > a {
+ transform: scale(
+ max(1, calc(1.5 - (0.2 * abs(var(--target) - var(--sibling-index)))))
+ );
+
+ & > span {
+ opacity: 1;
+ }
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(1)) {
+ --target: 1;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(2)) {
+ --target: 2;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(3)) {
+ --target: 3;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(4)) {
+ --target: 4;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(5)) {
+ --target: 5;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(6)) {
+ --target: 6;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(7)) {
+ --target: 7;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(8)) {
+ --target: 8;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(9)) {
+ --target: 9;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(10)) {
+ --target: 10;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(11)) {
+ --target: 11;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(12)) {
+ --target: 12;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(13)) {
+ --target: 13;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(14)) {
+ --target: 14;
+ }
+
+ &:has(a:is(:hover, :focus):nth-child(15)) {
+ --target: 15;
+ }
+ }
+
+ &:has(a:hover, :focus-within) {
+ z-index: 1;
+ transition: z-index 0.3s step-start;
+ }
+}
diff --git a/src/features/shell/nav.tsx b/src/features/shell/nav.tsx
new file mode 100644
index 0000000..1b48c46
--- /dev/null
+++ b/src/features/shell/nav.tsx
@@ -0,0 +1,25 @@
+import { A } from "@solidjs/router";
+import { AiOutlineHome, AiOutlineStar, AiOutlineSearch } from "solid-icons/ai";
+import { Component } from "solid-js";
+import css from "./nav.module.css";
+
+export const Nav: Component = (props) => {
+ return (
+
+ );
+};
diff --git a/src/features/shell/shell.module.css b/src/features/shell/shell.module.css
index 0641077..e85cbdd 100644
--- a/src/features/shell/shell.module.css
+++ b/src/features/shell/shell.module.css
@@ -3,27 +3,27 @@
display: grid;
grid: auto 1fr / 5em 1fr;
grid-template-areas:
- 'top top'
- 'nav content'
- ;
+ "top top"
+ "nav content";
inline-size: 100%;
block-size: 100%;
z-index: 0;
overflow: clip;
container-type: inline-size;
background-color: var(--surface-1);
-
- /* &:has(.nav a:hover) > .body {
- filter: blur(3px);
- } */
}
-
.body {
grid-area: 2 / 1 / 3 / 3;
inline-size: 100%;
block-size: 100%;
- background: linear-gradient(180deg, transparent, transparent 90vh, var(--surface-500) 90vh, var(--surface-500));
+ background: linear-gradient(
+ 180deg,
+ transparent,
+ transparent 90vh,
+ var(--surface-500) 90vh,
+ var(--surface-500)
+ );
overflow: clip auto;
padding-inline-start: 5em;
transition: filter var(--duration-moderate-1) var(--ease-3);
@@ -37,177 +37,3 @@
min-block-size: 100%;
}
}
-
-.top {
- grid-area: top;
- display: block grid;
- grid-auto-flow: column;
- justify-content: end;
- z-index: 1;
- background-color: inherit;
- padding: .5em;
-}
-
-.nav {
- grid-area: nav;
- display: block grid;
- grid-auto-flow: row;
- justify-content: space-between;
- inline-size: 5em;
- block-size: 100%;
- padding: 1em;
- background: inherit;
- z-index: 0;
- transition: z-index .3s step-end;
-
- & > ul {
- position: relative;
- display: block grid;
- grid-template-columns: 2.5rem auto;
- align-content: center;
- inline-size: 4rem;
- gap: 1rem;
- transform-origin: left center;
- padding: 0;
- padding-inline-start: .5rem;
- margin: 0;
-
- &::before {
- content: '';
- position: absolute;
- inset-inline-start: 100%;
- inset-block: 0;
- inline-size: 20vw;
- /* background:
- radial-gradient(ellipse at left center 100% 100%, #f00, transparent),
- linear-gradient(to right, #0003, transparent); */
- background-image: linear-gradient(to right, #0003, transparent);
- mask: radial-gradient(ellipse 20vw 100% at left center, black, transparent);
- backdrop-filter: blur(5px);
- opacity: 0;
- transition: opacity .3s var(--ease-3);
- }
-
- & > a {
- position: relative;
- grid-column: span 2;
- display: block grid;
- grid-template-columns: subgrid;
- align-items: center;
- text-decoration: none;
- transform-origin: center left;
- transition: transform 2s var(--ease-spring-5), opacity 0.3s var(--ease-3);
- color: var(--stone-4);
- font-size: 2rem;
- line-height: 1.5;
-
- & > span {
- opacity: 0;
- transition: opacity .3s var(--ease-3);
- text-shadow: 0 0 1em #000;
- }
-
- & > svg {
- fill: var(--stone-4);
- inline-size: 2.5rem;
- block-size: 2.5rem;
- }
-
- &.active {
- color: var(--yellow-4);
- list-style: disc;
-
- &::before {
- content: '•';
- position: absolute;
- inset-inline-start: -1rem;
- }
-
- & > svg {
- fill: var(--yellow-4);
- }
- }
- }
-
- &:has(a:is(:hover, :focus))::before {
- opacity: 1;
- }
-
- &:has(a:is(:hover, :focus)) > a:not(:is(:hover, :focus)) {
- opacity: .25;
- }
-
- &:has(a:is(:hover, :focus)) > a {
- transform: scale(max(1, calc(1.5 - (.2 * abs(var(--target) - var(--sibling-index))))));
-
- & > span {
- opacity: 1;
- }
- }
-
- &:has(a:is(:hover, :focus):nth-child(1)) {
- --target: 1;
- }
-
- &:has(a:is(:hover, :focus):nth-child(2)) {
- --target: 2;
- }
-
- &:has(a:is(:hover, :focus):nth-child(3)) {
- --target: 3;
- }
-
- &:has(a:is(:hover, :focus):nth-child(4)) {
- --target: 4;
- }
-
- &:has(a:is(:hover, :focus):nth-child(5)) {
- --target: 5;
- }
-
- &:has(a:is(:hover, :focus):nth-child(6)) {
- --target: 6;
- }
-
- &:has(a:is(:hover, :focus):nth-child(7)) {
- --target: 7;
- }
-
- &:has(a:is(:hover, :focus):nth-child(8)) {
- --target: 8;
- }
-
- &:has(a:is(:hover, :focus):nth-child(9)) {
- --target: 9;
- }
-
- &:has(a:is(:hover, :focus):nth-child(10)) {
- --target: 10;
- }
-
- &:has(a:is(:hover, :focus):nth-child(11)) {
- --target: 11;
- }
-
- &:has(a:is(:hover, :focus):nth-child(12)) {
- --target: 12;
- }
-
- &:has(a:is(:hover, :focus):nth-child(13)) {
- --target: 13;
- }
-
- &:has(a:is(:hover, :focus):nth-child(14)) {
- --target: 14;
- }
-
- &:has(a:is(:hover, :focus):nth-child(15)) {
- --target: 15;
- }
- }
-
- &:has(a:hover, :focus-within) {
- z-index: 1;
- transition: z-index .3s step-start;
- }
-}
\ No newline at end of file
diff --git a/src/features/shell/shell.tsx b/src/features/shell/shell.tsx
index 0209a4c..ea2b48b 100644
--- a/src/features/shell/shell.tsx
+++ b/src/features/shell/shell.tsx
@@ -1,11 +1,6 @@
-import { A } from "@solidjs/router";
-import {
- AiOutlineHome,
- AiOutlineStar,
- AiOutlineSearch,
-} from "solid-icons/ai";
-import { ParentComponent, Component } from "solid-js";
-import { ColorSchemePicker } from "../theme";
+import { ParentComponent } from "solid-js";
+import { Top } from "./top";
+import { Nav } from "./nav";
import css from "./shell.module.css";
export const Shell: ParentComponent = (props) => {
@@ -20,32 +15,3 @@ export const Shell: ParentComponent = (props) => {
);
};
-
-const Top: Component = (props) => {
- return (
-
- );
-};
-
-const Nav: Component = (props) => {
- return (
-
- );
-};
diff --git a/src/features/shell/top.module.css b/src/features/shell/top.module.css
new file mode 100644
index 0000000..cc33f93
--- /dev/null
+++ b/src/features/shell/top.module.css
@@ -0,0 +1,9 @@
+.top {
+ grid-area: 1 / 1 / 2 / 3;
+ display: block grid;
+ grid-auto-flow: column;
+ justify-content: end;
+ z-index: 1;
+ background-color: inherit;
+ padding: 0.5em;
+}
diff --git a/src/features/shell/top.tsx b/src/features/shell/top.tsx
new file mode 100644
index 0000000..2b9a335
--- /dev/null
+++ b/src/features/shell/top.tsx
@@ -0,0 +1,11 @@
+import { Component } from "solid-js";
+import { ColorSchemePicker } from "../theme";
+import css from "./top.module.css";
+
+export const Top: Component = (props) => {
+ return (
+
+ );
+};
diff --git a/src/routes/(shell)/watch/:item.tsx b/src/routes/(shell)/watch/:item.tsx
new file mode 100644
index 0000000..83d93ce
--- /dev/null
+++ b/src/routes/(shell)/watch/:item.tsx
@@ -0,0 +1,17 @@
+import { Params, useParams } from "@solidjs/router";
+import { createEffect } from "solid-js";
+import { Player } from "~/features/player";
+
+interface ItemParams extends Params {
+ item: string;
+}
+
+export default function Item() {
+ const params = useParams();
+
+ return (
+ <>
+
+ >
+ );
+}