Add field to control if cameras show in review

This commit is contained in:
Nicolas Mowen 2026-06-02 13:44:38 -06:00
parent db9e64c598
commit 6805e3c995
5 changed files with 35 additions and 16 deletions

View File

@ -16,3 +16,8 @@ class CameraUiConfig(FrigateBaseModel):
title="Show in UI", title="Show in UI",
description="Toggle whether this camera is visible everywhere in the Frigate UI. Disabling this will require manually editing the config to view this camera in the UI again.", description="Toggle whether this camera is visible everywhere in the Frigate UI. Disabling this will require manually editing the config to view this camera in the UI again.",
) )
review: bool = Field(
default=True,
title="Show in review",
description="Toggle whether this camera is visible in review (the review page and its camera filter, motion review, and the history view).",
)

View File

@ -144,11 +144,13 @@ export default function ReviewFilterGroup({
const filterValues = useMemo( const filterValues = useMemo(
() => ({ () => ({
cameras: allowedCameras.sort( cameras: allowedCameras
(a, b) => .filter((cam) => config?.cameras[cam]?.ui?.review !== false)
(config?.cameras[a]?.ui?.order ?? 0) - .sort(
(config?.cameras[b]?.ui?.order ?? 0), (a, b) =>
), (config?.cameras[a]?.ui?.order ?? 0) -
(config?.cameras[b]?.ui?.order ?? 0),
),
labels: Object.values(allLabels || {}), labels: Object.values(allLabels || {}),
zones: Object.values(allZones || {}), zones: Object.values(allZones || {}),
}), }),

View File

@ -70,13 +70,15 @@ export default function Events() {
undefined, undefined,
); );
const motionSearchCameras = useMemo(() => { const reviewCameras = useMemo(() => {
if (!config?.cameras) { if (!config?.cameras) {
return [] as string[]; return [] as string[];
} }
return Object.keys(config.cameras).filter((cam) => return Object.keys(config.cameras).filter(
allowedCameras.includes(cam), (cam) =>
allowedCameras.includes(cam) &&
config.cameras[cam]?.ui?.review !== false,
); );
}, [allowedCameras, config?.cameras]); }, [allowedCameras, config?.cameras]);
@ -85,12 +87,12 @@ export default function Events() {
return null; return null;
} }
if (motionSearchCameras.includes(motionSearchCamera)) { if (reviewCameras.includes(motionSearchCamera)) {
return motionSearchCamera; return motionSearchCamera;
} }
return motionSearchCameras[0] ?? null; return reviewCameras[0] ?? null;
}, [motionSearchCamera, motionSearchCameras]); }, [motionSearchCamera, reviewCameras]);
const motionSearchTimeRange = useMemo(() => { const motionSearchTimeRange = useMemo(() => {
if (motionSearchDay) { if (motionSearchDay) {
@ -357,6 +359,10 @@ export default function Events() {
const motion: ReviewSegment[] = []; const motion: ReviewSegment[] = [];
reviews?.forEach((segment) => { reviews?.forEach((segment) => {
if (config?.cameras[segment.camera]?.ui?.review === false) {
return;
}
all.push(segment); all.push(segment);
switch (segment.severity) { switch (segment.severity) {
@ -378,7 +384,7 @@ export default function Events() {
detection: detections, detection: detections,
significant_motion: motion, significant_motion: motion,
}; };
}, [reviews]); }, [reviews, config?.cameras]);
// update review items in place when a review segment ends // update review items in place when a review segment ends
const reviewUpdate = useFrigateReviews(); const reviewUpdate = useFrigateReviews();
@ -635,7 +641,7 @@ export default function Events() {
} }
setStartTime(recording.startTime); setStartTime(recording.startTime);
const allCameras = reviewFilter?.cameras ?? allowedCameras; const allCameras = reviewFilter?.cameras ?? reviewCameras;
return { return {
camera: recording.camera, camera: recording.camera,
@ -680,7 +686,7 @@ export default function Events() {
) : ( ) : (
<MotionSearchView <MotionSearchView
config={config} config={config}
cameras={motionSearchCameras} cameras={reviewCameras}
selectedCamera={selectedMotionSearchCamera} selectedCamera={selectedMotionSearchCamera}
onCameraSelect={handleMotionSearchCameraSelect} onCameraSelect={handleMotionSearchCameraSelect}
cameraLocked={true} cameraLocked={true}

View File

@ -5,6 +5,7 @@ export interface UiConfig {
timezone?: string; timezone?: string;
time_format?: "browser" | "12hour" | "24hour"; time_format?: "browser" | "12hour" | "24hour";
dashboard: boolean; dashboard: boolean;
review: boolean;
order: number; order: number;
unit_system?: "metric" | "imperial"; unit_system?: "metric" | "imperial";
} }

View File

@ -123,8 +123,13 @@ export function RecordingView({
const allowedCameras = useAllowedCameras(); const allowedCameras = useAllowedCameras();
const effectiveCameras = useMemo( const effectiveCameras = useMemo(
() => allCameras.filter((camera) => allowedCameras.includes(camera)), () =>
[allCameras, allowedCameras], allCameras.filter(
(camera) =>
allowedCameras.includes(camera) &&
config?.cameras[camera]?.ui?.review !== false,
),
[allCameras, allowedCameras, config?.cameras],
); );
const [mainCamera, setMainCamera] = useState(startCamera); const [mainCamera, setMainCamera] = useState(startCamera);