Debug replay (#22212)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions

* debug replay implementation

* fix masks after dev rebase

* fix squash merge issues

* fix

* fix

* fix

* no need to write debug replay camera to config

* camera and filter button and dropdown

* add filters

* add ability to edit motion and object config for debug replay

* add debug draw overlay to debug replay

* add guard to prevent crash when camera is no longer in camera_states

* fix overflow due to radix absolutely positioned elements

* increase number of messages

* ensure deep_merge replaces existing list values when override is true

* add back button

* add debug replay to explore and review menus

* clean up

* clean up

* update instructions to prevent exposing exception info

* fix typing

* refactor output logic

* refactor with helper function

* move init to function for consistency
This commit is contained in:
Josh Hawkins
2026-03-04 10:07:34 -06:00
committed by GitHub
parent 5e7d426768
commit 95956a690b
68 changed files with 4572 additions and 519 deletions
+49 -4
View File
@@ -1,5 +1,5 @@
import { baseUrl } from "./baseUrl";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import {
EmbeddingsReindexProgressType,
@@ -17,6 +17,13 @@ import { FrigateStats } from "@/types/stats";
import { createContainer } from "react-tracked";
import useDeepMemo from "@/hooks/use-deep-memo";
export type WsFeedMessage = {
topic: string;
payload: unknown;
timestamp: number;
id: string;
};
type Update = {
topic: string;
payload: unknown;
@@ -29,6 +36,9 @@ type WsState = {
type useValueReturn = [WsState, (update: Update) => void];
const wsMessageSubscribers = new Set<(msg: WsFeedMessage) => void>();
let wsMessageIdCounter = 0;
function useValue(): useValueReturn {
const wsUrl = `${baseUrl.replace(/^http/, "ws")}ws`;
@@ -43,8 +53,13 @@ function useValue(): useValueReturn {
return;
}
const cameraActivity: { [key: string]: FrigateCameraState } =
JSON.parse(activityValue);
let cameraActivity: { [key: string]: Partial<FrigateCameraState> };
try {
cameraActivity = JSON.parse(activityValue);
} catch {
return;
}
if (Object.keys(cameraActivity).length === 0) {
return;
@@ -53,6 +68,12 @@ function useValue(): useValueReturn {
const cameraStates: WsState = {};
Object.entries(cameraActivity).forEach(([name, state]) => {
const cameraConfig = state?.config;
if (!cameraConfig) {
return;
}
const {
record,
detect,
@@ -67,7 +88,7 @@ function useValue(): useValueReturn {
detections,
object_descriptions,
review_descriptions,
} = state["config"];
} = cameraConfig;
cameraStates[`${name}/recordings/state`] = record ? "ON" : "OFF";
cameraStates[`${name}/enabled/state`] = enabled ? "ON" : "OFF";
cameraStates[`${name}/detect/state`] = detect ? "ON" : "OFF";
@@ -115,6 +136,17 @@ function useValue(): useValueReturn {
...prevState,
[data.topic]: data.payload,
}));
// Notify feed subscribers
if (wsMessageSubscribers.size > 0) {
const feedMsg: WsFeedMessage = {
topic: data.topic,
payload: data.payload,
timestamp: Date.now(),
id: String(wsMessageIdCounter++),
};
wsMessageSubscribers.forEach((cb) => cb(feedMsg));
}
}
},
onOpen: () => {
@@ -740,3 +772,16 @@ export function useJobStatus(
return { payload: currentJob as Job | null };
}
export function useWsMessageSubscribe(callback: (msg: WsFeedMessage) => void) {
const callbackRef = useRef(callback);
callbackRef.current = callback;
useEffect(() => {
const handler = (msg: WsFeedMessage) => callbackRef.current(msg);
wsMessageSubscribers.add(handler);
return () => {
wsMessageSubscribers.delete(handler);
};
}, []);
}