diff --git a/web/src/components/CameraLiveView.jsx b/web/src/components/CameraLiveView.jsx new file mode 100644 index 000000000..39d069171 --- /dev/null +++ b/web/src/components/CameraLiveView.jsx @@ -0,0 +1,46 @@ +import { h } from 'preact'; +import { useApiHost } from '../api'; +import { useMemo, useRef } from 'preact/hooks'; +import { useResizeObserver } from '../hooks'; +import useSWR from 'swr'; + +export default function CameraLiveView({ camera, stretch = false }) { + const { data: config } = useSWR('config'); + const apiHost = useApiHost(); + const containerRef = useRef(null); + const [{ width: containerWidth }] = useResizeObserver(containerRef); + + const { name } = config ? config.cameras[camera] : ''; + const enabled = config ? config.cameras[camera].enabled : 'True'; + const { width, height } = config ? config.cameras[camera].detect : { width: 1, height: 1 }; + const aspectRatio = width / height; + + const scaledHeight = useMemo(() => { + const scaledHeight = Math.floor(containerWidth / aspectRatio); + return stretch ? scaledHeight : Math.min(scaledHeight, height); + }, [containerWidth, aspectRatio, height, stretch]); + + const scaledWidth = useMemo(() => Math.ceil(scaledHeight * aspectRatio), [ + scaledHeight, + aspectRatio, + ]); + + const liveStreamUrl = useMemo(() => { + return `${apiHost}/api/${name}`; + }, [apiHost, name]); + + return ( +
+ { + (enabled) ? + + :
Camera is disabled in config, no stream or snapshot available!
+ } +
+ ); +} diff --git a/web/src/routes/Cameras.jsx b/web/src/routes/Cameras.jsx index 1e2bbf903..7af2a9def 100644 --- a/web/src/routes/Cameras.jsx +++ b/web/src/routes/Cameras.jsx @@ -1,12 +1,13 @@ import { h, Fragment } from 'preact'; +import { useState, useMemo } from 'preact/hooks'; import ActivityIndicator from '../components/ActivityIndicator'; import Card from '../components/Card'; import CameraImage from '../components/CameraImage'; +import CameraLiveView from '../components/CameraLiveView'; import ClipIcon from '../icons/Clip'; import MotionIcon from '../icons/Motion'; import SnapshotIcon from '../icons/Snapshot'; import { useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws'; -import { useMemo } from 'preact/hooks'; import useSWR from 'swr'; export default function Cameras() { @@ -86,7 +87,25 @@ function Camera({ name, config }) { [config, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots] ); + const [isHovered, setIsHovered] = useState(false); + return ( - } /> + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {isHovered + ? // Show the live view when the mouse is over the snapshot + : // Show the snapshot when the mouse is not over it + } + + } + /> ); }