Publish finished reviews to mqtt / ws and use that for source of update banner (#10072)

* Add reviews to frontend

* Update ready when new review is saved

* fix

* Formatting
This commit is contained in:
Nicolas Mowen 2024-02-27 06:37:39 -07:00 committed by GitHub
parent 00c2caa1b7
commit f95ce913b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 53 additions and 15 deletions

View File

@ -1,5 +1,6 @@
"""Maintain review segments in db."""
import json
import logging
import os
import random
@ -138,7 +139,14 @@ class ReviewSegmentMaintainer(threading.Thread):
def end_segment(self, segment: PendingReviewSegment) -> None:
"""End segment."""
self.requestor.send_data(UPSERT_REVIEW_SEGMENT, segment.end())
seg_data = segment.end()
self.requestor.send_data(UPSERT_REVIEW_SEGMENT, seg_data)
self.requestor.send_data(
"reviews",
json.dumps(
{"type": "end", "review": {k.name: v for k, v in seg_data.items()}}
),
)
self.active_review_segments[segment.camera] = None
def update_existing_segment(

View File

@ -10,7 +10,7 @@ import {
import { produce, Draft } from "immer";
import useWebSocket, { ReadyState } from "react-use-websocket";
import { FrigateConfig } from "@/types/frigateConfig";
import { FrigateEvent, ToggleableSetting } from "@/types/ws";
import { FrigateEvent, FrigateReview, ToggleableSetting } from "@/types/ws";
import { FrigateStats } from "@/types/stats";
type ReducerState = {
@ -218,7 +218,14 @@ export function useRestart(): {
export function useFrigateEvents(): { payload: FrigateEvent } {
const {
value: { payload },
} = useWs(`events`, "");
} = useWs("events", "");
return { payload };
}
export function useFrigateReviews(): { payload: FrigateReview } {
const {
value: { payload },
} = useWs("reviews", "");
return { payload };
}

View File

@ -1,7 +1,7 @@
import { useFrigateEvents } from "@/api/ws";
import { useFrigateReviews } from "@/api/ws";
import useApiFilter from "@/hooks/use-api-filter";
import useOverlayState from "@/hooks/use-overlay-state";
import { ReviewFilter, ReviewSegment } from "@/types/review";
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
import DesktopEventView from "@/views/events/DesktopEventView";
import DesktopRecordingView from "@/views/events/DesktopRecordingView";
import MobileEventView from "@/views/events/MobileEventView";
@ -15,6 +15,8 @@ const API_LIMIT = 250;
export default function Events() {
// recordings viewer
const [severity, setSeverity] = useState<ReviewSeverity>("alert");
const [selectedReviewId, setSelectedReviewId] = useOverlayState("review");
// review filter
@ -97,6 +99,11 @@ export default function Events() {
setSize(size + 1);
}, [size]);
const reloadData = useCallback(() => {
setSize(1);
updateSegments();
}, [])
// preview videos
const previewTimes = useMemo(() => {
@ -196,22 +203,21 @@ export default function Events() {
// review updates
const { payload: eventUpdate } = useFrigateEvents();
const { payload: reviewUpdate } = useFrigateReviews();
const [hasUpdate, setHasUpdate] = useState(false);
useEffect(() => {
if (!eventUpdate) {
if (!reviewUpdate || hasUpdate) {
return;
}
// if event is ended and was saved, update events list
if (
eventUpdate.type == "end" &&
(eventUpdate.after.has_clip || eventUpdate.after.has_snapshot)
reviewUpdate.type == "end" &&
reviewUpdate.review.severity == severity
) {
setHasUpdate(true);
return;
}
}, [eventUpdate]);
}, [reviewUpdate]);
if (selectedData) {
return (
@ -229,11 +235,13 @@ export default function Events() {
relevantPreviews={allPreviews}
reachedEnd={isDone}
isValidating={isValidating}
severity={severity}
hasUpdate={hasUpdate}
setSeverity={setSeverity}
setHasUpdate={setHasUpdate}
loadNextPage={onLoadNextPage}
markItemAsReviewed={markItemAsReviewed}
pullLatestData={updateSegments}
pullLatestData={reloadData}
/>
);
}
@ -246,12 +254,14 @@ export default function Events() {
reachedEnd={isDone}
isValidating={isValidating}
filter={reviewFilter}
severity={severity}
hasUpdate={hasUpdate}
setSeverity={setSeverity}
setHasUpdate={setHasUpdate}
loadNextPage={onLoadNextPage}
markItemAsReviewed={markItemAsReviewed}
onSelectReview={setSelectedReviewId}
pullLatestData={updateSegments}
pullLatestData={reloadData}
updateFilter={onUpdateFilter}
/>
);

View File

@ -1,3 +1,5 @@
import { ReviewSegment } from "./review";
type FrigateObjectState = {
id: string;
camera: string;
@ -27,6 +29,11 @@ type FrigateObjectState = {
};
};
export interface FrigateReview {
type: "new" | "update" | "end";
review: ReviewSegment;
}
export interface FrigateEvent {
type: "new" | "update" | "end";
before: FrigateObjectState;

View File

@ -18,7 +18,9 @@ type DesktopEventViewProps = {
reachedEnd: boolean;
isValidating: boolean;
filter?: ReviewFilter;
severity: ReviewSeverity;
hasUpdate: boolean;
setSeverity: (severity: ReviewSeverity) => void;
setHasUpdate: (hasUpdated: boolean) => void;
loadNextPage: () => void;
markItemAsReviewed: (reviewId: string) => void;
@ -33,7 +35,9 @@ export default function DesktopEventView({
reachedEnd,
isValidating,
filter,
severity,
hasUpdate,
setSeverity,
setHasUpdate,
loadNextPage,
markItemAsReviewed,
@ -42,7 +46,6 @@ export default function DesktopEventView({
updateFilter,
}: DesktopEventViewProps) {
const { data: config } = useSWR<FrigateConfig>("config");
const [severity, setSeverity] = useState<ReviewSeverity>("alert");
const contentRef = useRef<HTMLDivElement | null>(null);
// review paging

View File

@ -14,7 +14,9 @@ type MobileEventViewProps = {
relevantPreviews?: Preview[];
reachedEnd: boolean;
isValidating: boolean;
severity: ReviewSeverity;
hasUpdate: boolean;
setSeverity: (severity: ReviewSeverity) => void;
setHasUpdate: (hasUpdated: boolean) => void;
loadNextPage: () => void;
markItemAsReviewed: (reviewId: string) => void;
@ -25,14 +27,15 @@ export default function MobileEventView({
relevantPreviews,
reachedEnd,
isValidating,
severity,
hasUpdate,
setSeverity,
setHasUpdate,
loadNextPage,
markItemAsReviewed,
pullLatestData,
}: MobileEventViewProps) {
const { data: config } = useSWR<FrigateConfig>("config");
const [severity, setSeverity] = useState<ReviewSeverity>("alert");
const contentRef = useRef<HTMLDivElement | null>(null);
// review paging