Support multiple videos.
This commit is contained in:
parent
24281cd491
commit
11e799207e
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue