Debug replay fixes (#23276)

* filter replay camera from camera selectors

* add face rec and lpr to replay configuration sheet

* add missing config topic subscriptions in embeddings maintainer

* pop replay camera from config object when stopping
This commit is contained in:
Josh Hawkins
2026-05-21 08:12:53 -06:00
committed by GitHub
parent 01c82d6921
commit 555ef89800
18 changed files with 119 additions and 32 deletions
@@ -14,6 +14,7 @@ import Konva from "konva";
import { useResizeObserver } from "@/hooks/resize-observer";
import { useApiHost } from "@/api";
import { resolveCameraName } from "@/hooks/use-camera-friendly-name";
import { isReplayCamera } from "@/utils/cameraUtil";
import Heading from "@/components/ui/heading";
import { isMobile } from "react-device-detect";
import { cn } from "@/lib/utils";
@@ -67,6 +68,7 @@ export default function Step2StateArea({
([name, cam]) =>
cam.enabled &&
cam.enabled_in_config &&
!isReplayCamera(name) &&
!selectedCameraNames.includes(name),
)
.map(([name]) => ({
@@ -57,6 +57,7 @@ import isEqual from "lodash/isEqual";
import set from "lodash/set";
import type { ConfigSectionData, JsonObject } from "@/types/configForm";
import { sanitizeSectionData } from "@/utils/configUtil";
import { isReplayCamera } from "@/utils/cameraUtil";
import type { SectionRendererProps } from "./registry";
const NOTIFICATION_SERVICE_WORKER = "/notifications-worker.js";
@@ -94,7 +95,7 @@ export default function NotificationsSettingsExtras({
return Object.values(config.cameras)
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order)
.filter((c) => c.enabled_in_config);
.filter((c) => c.enabled_in_config && !isReplayCamera(c.name));
}, [config]);
const notificationCameras = useMemo(() => {
@@ -106,6 +107,7 @@ export default function NotificationsSettingsExtras({
.filter(
(conf) =>
conf.enabled_in_config &&
!isReplayCamera(conf.name) &&
conf.notifications &&
conf.notifications.enabled_in_config,
)
@@ -359,6 +361,7 @@ export default function NotificationsSettingsExtras({
Object.values(config.cameras).some(
(c) =>
c.enabled_in_config &&
!isReplayCamera(c.name) &&
c.notifications &&
c.notifications.enabled_in_config,
),
@@ -26,6 +26,7 @@ import {
import { useTranslation } from "react-i18next";
import { FrigateConfig } from "@/types/frigateConfig";
import { CameraNameLabel } from "../camera/FriendlyNameLabel";
import { isReplayCamera } from "@/utils/cameraUtil";
import { isDesktop, isMobile } from "react-device-detect";
import { cn } from "@/lib/utils";
import {
@@ -52,7 +53,9 @@ export default function CreateRoleDialog({
const { t } = useTranslation(["views/settings"]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const cameras = Object.keys(config.cameras || {});
const cameras = Object.keys(config.cameras || {}).filter(
(name) => !isReplayCamera(name),
);
const existingRoles = Object.keys(config.auth?.roles || {});
@@ -25,6 +25,7 @@ import {
import { Trans, useTranslation } from "react-i18next";
import { FrigateConfig } from "@/types/frigateConfig";
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
import { isReplayCamera } from "@/utils/cameraUtil";
type EditRoleCamerasOverlayProps = {
show: boolean;
@@ -46,7 +47,9 @@ export default function EditRoleCamerasDialog({
const { t } = useTranslation(["views/settings"]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const cameras = Object.keys(config.cameras || {});
const cameras = Object.keys(config.cameras || {}).filter(
(name) => !isReplayCamera(name),
);
const formSchema = z.object({
cameras: z
+4 -1
View File
@@ -54,6 +54,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import { Textarea } from "../ui/textarea";
import { useNavigate } from "react-router-dom";
import { useIsAdmin } from "@/hooks/use-is-admin";
import { isReplayCamera } from "@/utils/cameraUtil";
const EXPORT_OPTIONS = [
"1",
@@ -448,7 +449,9 @@ export function ExportContent({
);
const cameraActivities = useMemo<CameraActivity[]>(() => {
const allCameraIds = Object.keys(config?.cameras ?? {});
const allCameraIds = Object.keys(config?.cameras ?? {}).filter(
(name) => !isReplayCamera(name),
);
const byCamera = new Map<string, Event[]>();
events?.forEach((event) => {
@@ -13,6 +13,7 @@ import {
} from "@/components/ui/select";
import { Card, CardContent } from "@/components/ui/card";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { isReplayCamera } from "@/utils/cameraUtil";
import { useTimezone } from "@/hooks/use-date-utils";
import { Button } from "@/components/ui/button";
import { LuX } from "react-icons/lu";
@@ -36,11 +37,16 @@ export default function ObjectPathPlotter() {
const [currentPage, setCurrentPage] = useState(1);
const eventsPerPage = 20;
const cameraNames = useMemo(() => {
if (!config) return [];
return Object.keys(config.cameras).filter((name) => !isReplayCamera(name));
}, [config]);
useEffect(() => {
if (config && !selectedCamera) {
setSelectedCamera(Object.keys(config.cameras)[0]);
if (cameraNames.length > 0 && !selectedCamera) {
setSelectedCamera(cameraNames[0]);
}
}, [config, selectedCamera]);
}, [cameraNames, selectedCamera]);
const searchQuery = useMemo(() => {
if (!selectedCamera) return null;
@@ -143,12 +149,11 @@ export default function ObjectPathPlotter() {
<SelectValue placeholder="Select camera" />
</SelectTrigger>
<SelectContent>
{config &&
Object.keys(config.cameras).map((cameraName) => (
<SelectItem key={cameraName} value={cameraName}>
{cameraName}
</SelectItem>
))}
{cameraNames.map((cameraName) => (
<SelectItem key={cameraName} value={cameraName}>
{cameraName}
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={timeRange} onValueChange={setTimeRange}>