mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 10:45:21 +03:00
Add a live view feature to camera cards with snapshot hover in Cameras.jsx
This commit is contained in:
parent
3efa77f302
commit
1355b6a1d5
46
web/src/components/CameraLiveView.jsx
Normal file
46
web/src/components/CameraLiveView.jsx
Normal file
@ -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 (
|
||||||
|
<div className="relative w-full" ref={containerRef}>
|
||||||
|
{
|
||||||
|
(enabled) ?
|
||||||
|
<img
|
||||||
|
data-testid="cameraliveview-img"
|
||||||
|
height={scaledHeight}
|
||||||
|
width={scaledWidth}
|
||||||
|
src={liveStreamUrl}
|
||||||
|
/>
|
||||||
|
: <div class="text-center pt-6">Camera is disabled in config, no stream or snapshot available!</div>
|
||||||
|
}
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
import { h, Fragment } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
|
import { useState, useMemo } from 'preact/hooks';
|
||||||
import ActivityIndicator from '../components/ActivityIndicator';
|
import ActivityIndicator from '../components/ActivityIndicator';
|
||||||
import Card from '../components/Card';
|
import Card from '../components/Card';
|
||||||
import CameraImage from '../components/CameraImage';
|
import CameraImage from '../components/CameraImage';
|
||||||
|
import CameraLiveView from '../components/CameraLiveView';
|
||||||
import ClipIcon from '../icons/Clip';
|
import ClipIcon from '../icons/Clip';
|
||||||
import MotionIcon from '../icons/Motion';
|
import MotionIcon from '../icons/Motion';
|
||||||
import SnapshotIcon from '../icons/Snapshot';
|
import SnapshotIcon from '../icons/Snapshot';
|
||||||
import { useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws';
|
import { useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws';
|
||||||
import { useMemo } from 'preact/hooks';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
export default function Cameras() {
|
export default function Cameras() {
|
||||||
@ -86,7 +87,25 @@ function Camera({ name, config }) {
|
|||||||
[config, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots]
|
[config, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card buttons={buttons} href={href} header={cleanName} icons={icons} media={<CameraImage camera={name} stretch />} />
|
<Card
|
||||||
|
buttons={buttons}
|
||||||
|
href={href}
|
||||||
|
header={cleanName}
|
||||||
|
icons={icons}
|
||||||
|
media={
|
||||||
|
<div
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
>
|
||||||
|
{isHovered
|
||||||
|
? <CameraLiveView camera={name} stretch /> // Show the live view when the mouse is over the snapshot
|
||||||
|
: <CameraImage camera={name} stretch /> // Show the snapshot when the mouse is not over it
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user