Tweaks and fixes (#11372)

* Ensure camera activity is up to date

* Persist playback rate between cameras

* Add setting for default playback rate

* Fix audio events saving image

* Formatting

* Use select component
This commit is contained in:
Nicolas Mowen
2024-05-14 07:38:03 -06:00
committed by GitHub
parent b451d0a4f1
commit b10ae68c1f
5 changed files with 111 additions and 39 deletions

View File

@@ -204,13 +204,26 @@ export function useFrigateStats(): { payload: FrigateStats } {
return { payload: JSON.parse(payload as string) };
}
export function useInitialCameraState(camera: string): {
export function useInitialCameraState(
camera: string,
refreshOnStart: boolean,
): {
payload: FrigateCameraState;
} {
const {
value: { payload },
} = useWs("camera_activity", "");
send: sendCommand,
} = useWs("camera_activity", "onConnect");
const data = JSON.parse(payload as string);
useEffect(() => {
if (refreshOnStart) {
sendCommand("onConnect");
}
// only refresh when onRefresh value changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refreshOnStart]);
return { payload: data ? data[camera] : undefined };
}

View File

@@ -15,6 +15,7 @@ import { FrigateConfig } from "@/types/frigateConfig";
import { AxiosResponse } from "axios";
import { toast } from "sonner";
import { useOverlayState } from "@/hooks/use-overlay-state";
import { usePersistence } from "@/hooks/use-persistence";
// Android native hls does not seek correctly
const USE_NATIVE_HLS = !isAndroid;
@@ -111,6 +112,11 @@ export default function HlsVideoPlayer({
const [isPlaying, setIsPlaying] = useState(true);
const [muted, setMuted] = useOverlayState("playerMuted", true);
const [volume, setVolume] = useOverlayState("playerVolume", 1.0);
const [defaultPlaybackRate] = usePersistence("playbackRate", 1);
const [playbackRate, setPlaybackRate] = useOverlayState(
"playbackRate",
defaultPlaybackRate ?? 1,
);
const [mobileCtrlTimeout, setMobileCtrlTimeout] = useState<NodeJS.Timeout>();
const [controls, setControls] = useState(isMobile);
const [controlsOpen, setControlsOpen] = useState(false);
@@ -162,7 +168,7 @@ export default function HlsVideoPlayer({
}}
setControlsOpen={setControlsOpen}
setMuted={(muted) => setMuted(muted, true)}
playbackRate={videoRef.current?.playbackRate ?? 1}
playbackRate={playbackRate ?? 1}
hotKeys={hotKeys}
onPlayPause={(play) => {
if (!videoRef.current) {
@@ -184,9 +190,13 @@ export default function HlsVideoPlayer({
videoRef.current.currentTime = Math.max(0, currentTime + diff);
}}
onSetPlaybackRate={(rate) =>
videoRef.current ? (videoRef.current.playbackRate = rate) : null
}
onSetPlaybackRate={(rate) => {
setPlaybackRate(rate);
if (videoRef.current) {
videoRef.current.playbackRate = rate;
}
}}
onUploadFrame={async () => {
if (videoRef.current && onUploadFrame) {
const resp = await onUploadFrame(videoRef.current.currentTime);
@@ -255,8 +265,14 @@ export default function HlsVideoPlayer({
onLoadedMetadata={() => {
handleLoadedMetadata();
if (videoRef.current && volume) {
videoRef.current.volume = volume;
if (videoRef.current) {
if (playbackRate) {
videoRef.current.playbackRate = playbackRate;
}
if (volume) {
videoRef.current.volume = volume;
}
}
}}
onEnded={onClipEnded}

View File

@@ -9,6 +9,17 @@ import { Button } from "../ui/button";
import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig";
import { del as delData } from "idb-keyval";
import { usePersistence } from "@/hooks/use-persistence";
import { isSafari } from "react-device-detect";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
} from "../ui/select";
const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16];
export default function General() {
const { data: config } = useSWR<FrigateConfig>("config");
@@ -38,6 +49,8 @@ export default function General() {
document.title = "General Settings - Frigate";
}, []);
const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1);
return (
<>
<div className="flex flex-col md:flex-row size-full">
@@ -64,6 +77,36 @@ export default function General() {
</div>
</div>
<Separator className="flex my-2 bg-secondary" />
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<div className="text-md">Default Playback Rate</div>
<div className="text-sm text-muted-foreground my-2">
<p>Default playback rate for recordings playback.</p>
</div>
</div>
</div>
<Select
value={playbackRate?.toString()}
onValueChange={(value) => setPlaybackRate(parseFloat(value))}
>
<SelectTrigger className="w-20">
{`${playbackRate}x`}
</SelectTrigger>
<SelectContent>
<SelectGroup>
{PLAYBACK_RATE_DEFAULT.map((rate) => (
<SelectItem
key={rate}
className="cursor-pointer"
value={rate.toString()}
>
{rate}x
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
<Separator className="flex my-2 bg-secondary" />
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<div className="text-md">Low Data Mode</div>

View File

@@ -19,12 +19,16 @@ type useCameraActivityReturn = {
export function useCameraActivity(
camera: CameraConfig,
refreshOnStart: boolean = true,
): useCameraActivityReturn {
const [objects, setObjects] = useState<ObjectType[]>([]);
// init camera activity
const { payload: initialCameraState } = useInitialCameraState(camera.name);
const { payload: initialCameraState } = useInitialCameraState(
camera.name,
refreshOnStart,
);
const updatedCameraState = useDeepMemo(initialCameraState);