Support multiple videos.

This commit is contained in:
James Pace 2026-09-12 13:17:31 +00:00
parent 24281cd491
commit 11e799207e
2 changed files with 68 additions and 12 deletions

View File

@ -11,26 +11,67 @@
import { AppNav, Footer } from "./components/AppNav.tsx"; import { AppNav, Footer } from "./components/AppNav.tsx";
import { Container, Row, Col, Card } from "react-bootstrap"; import { Container, Row, Col, Card } from "react-bootstrap";
import { MediaMtxPlayer } from "./external/MediaMtxPlayer.tsx"; import { MediaMtxPlayer } from "./external/MediaMtxPlayer.tsx";
import { atom, useAtomValue } from "jotai"; import { atom, useAtomValue, useAtom } from "jotai";
import { createQueryAtom } from "./utils/query.ts"; import { createQueryAtom } from "./utils/query.ts";
const tokenQueryAtom = createQueryAtom("api/mediamtx/login", { retry: true }); const tokenQueryAtom = createQueryAtom("api/mediamtx/login", { retry: true });
const tokenAtom = atom((get) => { const tokenAtom = atom((get) => {
const response = get(tokenQueryAtom); const response = get(tokenQueryAtom);
if (response.isPending || response.isError) { if (response.isPending || response.isError) {
console.log(response);
return null; return null;
} }
return response.data.token; 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() { export function Video() {
const streamName = "image"; const streamName = "image";
const imageStreamURI =
"http://" + window.location.hostname + ":8889/" + streamName + "/whep"; const imageStreamURI = useAtomValue(imageStreamURIAtom);
const token = useAtomValue(tokenAtom); const token = useAtomValue(tokenAtom);
const availableStreams = useAtomValue(streamsAtom);
const [selectedStream, setSelectedStream] = useAtom(selectedStreamAtom);
const handleSelect = (event) => {
setSelectedStream(event.target.value);
};
return ( return (
<div> <div>
<AppNav /> <AppNav />
@ -40,7 +81,21 @@ export function Video() {
<Col lg={8}> <Col lg={8}>
<MediaMtxPlayer url={imageStreamURI} token={token} /> <MediaMtxPlayer url={imageStreamURI} token={token} />
</Col> </Col>
<Col lg={4}>Showing stream: "{streamName}".</Col> <Col lg={4}>
<p>Available Streams: </p>
{availableStreams.map((stream, index) => (
<div style={{ display: "flex", gap: "8px" }}>
<input
type="radio"
name="selected_stream"
value={stream}
checked={selectedStream === stream}
onChange={handleSelect}
/>
{stream}
</div>
))}
</Col>
</Row> </Row>
</Card> </Card>
</Container> </Container>

View File

@ -71,11 +71,15 @@ function LevelIndicator(props: Any) {
return <CircleFill color="grey" />; return <CircleFill color="grey" />;
} }
function MessageText(props: Any) { function TitleText(props: Any) {
if (props.message === "") { if (props.message === "") {
return <div></div>; return <p>{props.name}</p>;
} }
return <div>Message: {props.message}</div>; return (
<p>
{props.name}: {props.message}
</p>
);
} }
function ValuesTable(props: Any) { function ValuesTable(props: Any) {
@ -106,10 +110,7 @@ function StatusContent(props: Any) {
console.log(status); console.log(status);
return ( return (
<div> <div>
<p>{status.name}</p> <TitleText name={status.name} message={status.message} />
<p>
<MessageText message={status.message} />
</p>
<p> <p>
<ValuesTable values={status.values} /> <ValuesTable values={status.values} />
</p> </p>