From 11e799207e34814a17bd202c883c54f8d554cb9d Mon Sep 17 00:00:00 2001 From: James Pace Date: Sat, 12 Sep 2026 13:17:31 +0000 Subject: [PATCH] Support multiple videos. --- src/Video.tsx | 65 +++++++++++++++++++++++++++--- src/components/DiagnosticsCard.tsx | 15 +++---- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/Video.tsx b/src/Video.tsx index 2df3953..c5d74da 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -11,26 +11,67 @@ import { AppNav, Footer } from "./components/AppNav.tsx"; import { Container, Row, Col, Card } from "react-bootstrap"; import { MediaMtxPlayer } from "./external/MediaMtxPlayer.tsx"; -import { atom, useAtomValue } from "jotai"; +import { atom, useAtomValue, useAtom } from "jotai"; import { createQueryAtom } from "./utils/query.ts"; const tokenQueryAtom = createQueryAtom("api/mediamtx/login", { retry: true }); const tokenAtom = atom((get) => { const response = get(tokenQueryAtom); if (response.isPending || response.isError) { - console.log(response); return null; } return response.data.token; }); +const streamsQueryAtom = createQueryAtom("api/available_streams"); +const streamsAtom = atom((get) => { + const response = get(streamsQueryAtom); + if (response.isPending || response.isError) { + return []; + } + return response.data.streams; +}); +const videoDisplayPortAtom = atom((get) => { + const response = get(streamsQueryAtom); + if (response.isPending || response.isError) { + return null; + } + return response.data.port; +}); + +const selectedStreamAtom = atom("image"); +const imageStreamURIAtom = atom((get) => { + const port = get(videoDisplayPortAtom); + const selectedStream = get(selectedStreamAtom); + if (port === null || selectedStream === null) { + return null; + } + const imageStreamURI = + "http://" + + window.location.hostname + + ":" + + port + + "/" + + selectedStream + + "/whep"; + return imageStreamURI; +}); + export function Video() { const streamName = "image"; - const imageStreamURI = - "http://" + window.location.hostname + ":8889/" + streamName + "/whep"; + + const imageStreamURI = useAtomValue(imageStreamURIAtom); const token = useAtomValue(tokenAtom); + const availableStreams = useAtomValue(streamsAtom); + + const [selectedStream, setSelectedStream] = useAtom(selectedStreamAtom); + + const handleSelect = (event) => { + setSelectedStream(event.target.value); + }; + return (
@@ -40,7 +81,21 @@ export function Video() { - Showing stream: "{streamName}". + +

Available Streams:

+ {availableStreams.map((stream, index) => ( +
+ + {stream} +
+ ))} + diff --git a/src/components/DiagnosticsCard.tsx b/src/components/DiagnosticsCard.tsx index e6dfdd9..9e847ed 100644 --- a/src/components/DiagnosticsCard.tsx +++ b/src/components/DiagnosticsCard.tsx @@ -71,11 +71,15 @@ function LevelIndicator(props: Any) { return ; } -function MessageText(props: Any) { +function TitleText(props: Any) { if (props.message === "") { - return
; + return

{props.name}

; } - return
Message: {props.message}
; + return ( +

+ {props.name}: {props.message} +

+ ); } function ValuesTable(props: Any) { @@ -106,10 +110,7 @@ function StatusContent(props: Any) { console.log(status); return (
-

{status.name}

-

- -

+