diff --git a/.postcssrc b/.postcssrc index 0985cb2..1e9edb2 100644 --- a/.postcssrc +++ b/.postcssrc @@ -1,5 +1,5 @@ { - "plugins": { - "tailwindcss": {} - } -} + "plugins": { + "tailwindcss": {} + } +} \ No newline at end of file diff --git a/.posthtmlrc b/.posthtmlrc new file mode 100644 index 0000000..9cd9705 --- /dev/null +++ b/.posthtmlrc @@ -0,0 +1,7 @@ +{ + "plugins": { + "posthtml-include": { + "root": "./src" + } + } +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c640123..737c1c4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,4 +7,4 @@ "HashiCorp.terraform", "bradlc.vscode-tailwindcss" ] -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index e856451..857d6b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "eslint-plugin-prettier": "^5.0.0", "parcel": "^2.9.2", "postcss": "^8.4.24", + "posthtml-include": "^1.7.4", "prettier": "^3.0.1", "prettier-plugin-tailwindcss": "^0.5.2", "tailwindcss": "^3.3.2", @@ -4486,6 +4487,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", + "dev": true + }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, @@ -5966,6 +5973,57 @@ "node": ">=12.0.0" } }, + "node_modules/posthtml-expressions": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/posthtml-expressions/-/posthtml-expressions-1.11.1.tgz", + "integrity": "sha512-2zA5SRM7quupTGa422xH72T0n3tF5quZeZ66czmMa/4QAj8HFzCTlN5l42wWVjLCxj7XOmMmQJEeK0+p3AgH+w==", + "dev": true, + "dependencies": { + "fclone": "^1.0.11", + "posthtml": "^0.16.5", + "posthtml-match-helper": "^1.0.1", + "posthtml-parser": "^0.10.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/posthtml-include": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/posthtml-include/-/posthtml-include-1.7.4.tgz", + "integrity": "sha512-GO5QzHiM6/fXq8DxLoLN+jEW4sH/6nuGF9z+NJmP1qi1A3J2zCC7WwXrEwaPL3T8LrH+FL4IedK+mIJHbn5ZEA==", + "dev": true, + "dependencies": { + "posthtml": "^0.16.6", + "posthtml-expressions": "^1.7.1", + "posthtml-parser": "^0.11.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-include/node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-match-helper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/posthtml-match-helper/-/posthtml-match-helper-1.0.3.tgz", + "integrity": "sha512-aeRAPvok2Fs6uzSm85665jdAk5UOd8US2QCkWtGU6yLPlKSwzWTSgZZuABc3UeNy3K1lVk/HV9bRkWJYN05Ymw==", + "dev": true, + "peerDependencies": { + "posthtml": ">=0.5.0" + } + }, "node_modules/posthtml-parser": { "version": "0.10.2", "dev": true, diff --git a/package.json b/package.json index 824eaf7..a2555f6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,12 @@ "version": "0.0.1", "description": "Website for quic.video", "license": "(MIT OR Apache-2.0)", - "source": "src/index.html", + "source": [ + "src/index.html", + "src/explained.html", + "src/publish.html", + "src/watch.html" + ], "scripts": { "serve": "parcel serve --https --cert cert/localhost.crt --key cert/localhost.key --port 4444 --open", "build": "parcel build", @@ -34,6 +39,7 @@ "eslint-plugin-prettier": "^5.0.0", "parcel": "^2.9.2", "postcss": "^8.4.24", + "posthtml-include": "^1.7.4", "prettier": "^3.0.1", "prettier-plugin-tailwindcss": "^0.5.2", "tailwindcss": "^3.3.2", diff --git a/src/app.tsx b/src/app.tsx deleted file mode 100644 index f1d1d76..0000000 --- a/src/app.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Client } from "@kixelated/moq/transport" -import { asError } from "@kixelated/moq/common" - -import { createSignal, createEffect, Show, createMemo } from "solid-js" - -import * as Watch from "./watch" -import * as Publish from "./publish" - -import { Player } from "@kixelated/moq/playback" -import { Broadcast } from "@kixelated/moq/contribute" -import { Connection } from "@kixelated/moq/transport" - -export function App(props: { url: string }) { - const [error, setError] = createSignal() - const [connection, setConnection] = createSignal() - - const fingerprint = process.env.NODE_ENV !== "production" ? props.url + "/fingerprint" : undefined - - createEffect(async () => { - try { - const client = new Client({ - url: props.url, - role: "both", - fingerprint, - }) - - const conn = await client.connect() - - setConnection(conn) - await conn.run() - } catch (e) { - setError(asError(e)) - } finally { - setConnection() - } - }) - - createEffect(() => { - const err = error() - if (err) console.error(err) - }) - - const [player, setPlayer] = createSignal() - const [broadcast, setBroadcast] = createSignal() - const setup = createMemo(() => !player() && !broadcast()) - - return ( -
- -
- {error()?.name}: {error()?.message} -
-
-
- - - -
- -
- - - -
- -
-
-

Watch

- - - -
-
-
-

Publish

- -
-
-
- ) -} diff --git a/src/common/base.css b/src/common/base.css new file mode 100644 index 0000000..afe9700 --- /dev/null +++ b/src/common/base.css @@ -0,0 +1,11 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +a { + @apply text-green-400 hover:cursor-pointer hover:text-green-500; +} + +html { + @apply bg-slate-900 text-slate-100; +} diff --git a/src/common/github.svg b/src/common/github.svg new file mode 100644 index 0000000..b85c9f7 --- /dev/null +++ b/src/common/github.svg @@ -0,0 +1 @@ + diff --git a/src/common/logo.svg b/src/common/logo.svg new file mode 100644 index 0000000..90f0f53 --- /dev/null +++ b/src/common/logo.svg @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/explained.html b/src/explained.html new file mode 100644 index 0000000..b4961b0 --- /dev/null +++ b/src/explained.html @@ -0,0 +1,17 @@ + + + + + quic.video + + + + + + + +
+

// TODO explain how MoQ works

+
+ + diff --git a/src/header.html b/src/header.html deleted file mode 100644 index f136864..0000000 --- a/src/header.html +++ /dev/null @@ -1,56 +0,0 @@ -
-
- Media over QUIC -
- -
- - - - - -
-
diff --git a/src/img/logo.svg b/src/img/logo.svg deleted file mode 100644 index 8298b8d..0000000 --- a/src/img/logo.svg +++ /dev/null @@ -1,700 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 369b575..0000000 --- a/src/index.css +++ /dev/null @@ -1,7 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -a { - @apply text-indigo-600 hover:cursor-pointer hover:text-indigo-800 hover:underline; -} diff --git a/src/index.html b/src/index.html index bfec422..c889f2b 100644 --- a/src/index.html +++ b/src/index.html @@ -4,32 +4,14 @@ quic.video - + - -
-
-

Media

-

over QUIC

-
+ + -
- - - - server - web - spec -
+
+

// TODO splash page

- -
- -
- - diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 355337d..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { App } from "./app" - -import { render } from "solid-js/web" - -const params = new URLSearchParams(window.location.search) - -let url = params.get("url") - -// Change the default URL based on the environment. -if (process.env.NODE_ENV === "production") { - url ??= "https://moq-demo.englishm.net:4443" -} else { - url ??= "https://localhost:4443" -} - -const app = document.getElementById("app") -if (!app) { - throw new Error("no container") -} - -render(() => , app) diff --git a/src/nav.html b/src/nav.html new file mode 100644 index 0000000..2be5d21 --- /dev/null +++ b/src/nav.html @@ -0,0 +1,13 @@ + diff --git a/src/publish.html b/src/publish.html new file mode 100644 index 0000000..9bc48a6 --- /dev/null +++ b/src/publish.html @@ -0,0 +1,25 @@ + + + + + quic.video + + + + + + + +
+ + + + diff --git a/src/publish/index.tsx b/src/publish/index.tsx new file mode 100644 index 0000000..80fb5d3 --- /dev/null +++ b/src/publish/index.tsx @@ -0,0 +1,72 @@ +import { Broadcast } from "@kixelated/moq/contribute" +import { Connection, Client } from "@kixelated/moq/transport" +import { asError } from "@kixelated/moq/common" + +import { createEffect, createSignal, Match, Show, Switch } from "solid-js" + +import { Preview } from "./preview" +import { Setup } from "./setup" + +export function Main() { + const [error, setError] = createSignal() + const [connection, setConnection] = createSignal() + + const params = new URLSearchParams(window.location.search) + + let url = params.get("url") ?? undefined + let fingerprint = params.get("fingerprint") ?? undefined + + // Change the default URL based on the environment. + if (process.env.NODE_ENV === "production") { + url ??= "https://moq-demo.englishm.net:4443" + } else { + url ??= "https://localhost:4443" + fingerprint ??= url + "/fingerprint" + } + + const client = new Client({ + url, + role: "both", + fingerprint, + }) + + createEffect(async () => { + try { + const conn = await client.connect() + setConnection(conn) + await conn.run() + } catch (e) { + setError(asError(e)) + } finally { + setConnection() + } + }) + + createEffect(() => { + const err = error() + if (err) console.error(err) + }) + + const [broadcast, setBroadcast] = createSignal() + + return ( +
+ +
+ {error()?.name}: {error()?.message} +
+
+ +
+ + + + + + + + +
+
+ ) +} diff --git a/src/publish/preview.tsx b/src/publish/preview.tsx new file mode 100644 index 0000000..823942a --- /dev/null +++ b/src/publish/preview.tsx @@ -0,0 +1,24 @@ +import { Broadcast } from "@kixelated/moq/contribute" +import { asError } from "@kixelated/moq/common" + +import { createEffect, onMount } from "solid-js" + +export function Preview(props: { broadcast: Broadcast; setBroadcast(): void; setError(e: Error): void }) { + let preview: HTMLVideoElement + + onMount(() => { + props.broadcast.preview(preview) + }) + + createEffect(async () => { + try { + await props.broadcast.run() + } catch (e) { + props.setError(asError(e)) + } finally { + props.setBroadcast() + } + }) + + return +} diff --git a/src/publish.tsx b/src/publish/setup.tsx similarity index 76% rename from src/publish.tsx rename to src/publish/setup.tsx index ed9af75..5f8bb8f 100644 --- a/src/publish.tsx +++ b/src/publish/setup.tsx @@ -2,18 +2,7 @@ import { Broadcast, VideoEncoder, AudioEncoderCodecs } from "@kixelated/moq/cont import { Connection } from "@kixelated/moq/transport" import { asError } from "@kixelated/moq/common" -import { - createEffect, - Switch, - Match, - createMemo, - createSignal, - For, - createResource, - onMount, - createSelector, - Show, -} from "solid-js" +import { createEffect, Switch, Match, createMemo, createSignal, For, createResource, createSelector } from "solid-js" import { SetStoreFunction, Store, createStore } from "solid-js/store" @@ -110,26 +99,6 @@ const VIDEO_CODECS: VideoCodec[] = [ { name: "h.264", profile: "baseline", value: "avc1.420034" }, ] -export function Main(props: { broadcast: Broadcast; setBroadcast(): void; setError(e: Error): void }) { - let preview: HTMLVideoElement - - onMount(() => { - props.broadcast.preview(preview) - }) - - createEffect(async () => { - try { - await props.broadcast.run() - } catch (e) { - props.setError(asError(e)) - } finally { - props.setBroadcast() - } - }) - - return -} - export function Setup(props: { connection: Connection | undefined setBroadcast(v: Broadcast | undefined): void @@ -196,27 +165,14 @@ export function Setup(props: { const isState = createSelector(state) - const [advanced, setAdvanced] = createSignal(false) - const toggleAdvanced = (e: MouseEvent) => { - e.preventDefault() - setAdvanced(!advanced()) - } - - // We pass advanced to each component instead of hiding them so they can compute the config. return ( -
- -