mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-11 05:35:25 +03:00
Handle in progress review items
This commit is contained in:
parent
5ac79a7a4b
commit
94d64920c4
@ -21,11 +21,14 @@ import { useSwipeable } from "react-swipeable";
|
|||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||||
import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
|
import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
|
||||||
import useContextMenu from "@/hooks/use-contextmenu";
|
import useContextMenu from "@/hooks/use-contextmenu";
|
||||||
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
|
import { TimeRange } from "@/types/timeline";
|
||||||
|
|
||||||
type PreviewPlayerProps = {
|
type PreviewPlayerProps = {
|
||||||
review: ReviewSegment;
|
review: ReviewSegment;
|
||||||
allPreviews?: Preview[];
|
allPreviews?: Preview[];
|
||||||
scrollLock?: boolean;
|
scrollLock?: boolean;
|
||||||
|
timeRange: TimeRange;
|
||||||
onTimeUpdate?: (time: number | undefined) => void;
|
onTimeUpdate?: (time: number | undefined) => void;
|
||||||
setReviewed: (review: ReviewSegment) => void;
|
setReviewed: (review: ReviewSegment) => void;
|
||||||
onClick: (review: ReviewSegment, ctrl: boolean) => void;
|
onClick: (review: ReviewSegment, ctrl: boolean) => void;
|
||||||
@ -43,6 +46,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
review,
|
review,
|
||||||
allPreviews,
|
allPreviews,
|
||||||
scrollLock = false,
|
scrollLock = false,
|
||||||
|
timeRange,
|
||||||
setReviewed,
|
setReviewed,
|
||||||
onClick,
|
onClick,
|
||||||
onTimeUpdate,
|
onTimeUpdate,
|
||||||
@ -70,8 +74,10 @@ export default function PreviewThumbnailPlayer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleSetReviewed = useCallback(() => {
|
const handleSetReviewed = useCallback(() => {
|
||||||
|
if (review.end_time) {
|
||||||
review.has_been_reviewed = true;
|
review.has_been_reviewed = true;
|
||||||
setReviewed(review);
|
setReviewed(review);
|
||||||
|
}
|
||||||
}, [review, setReviewed]);
|
}, [review, setReviewed]);
|
||||||
|
|
||||||
useContextMenu(imgRef, () => {
|
useContextMenu(imgRef, () => {
|
||||||
@ -91,7 +97,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (review.end_time > preview.end) {
|
if ((review.end_time ?? timeRange.before) > preview.end) {
|
||||||
multiHour = true;
|
multiHour = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +114,8 @@ export default function PreviewThumbnailPlayer({
|
|||||||
|
|
||||||
const firstPrev = allPreviews[firstIndex];
|
const firstPrev = allPreviews[firstIndex];
|
||||||
const firstDuration = firstPrev.end - review.start_time;
|
const firstDuration = firstPrev.end - review.start_time;
|
||||||
const secondDuration = review.end_time - firstPrev.end;
|
const secondDuration =
|
||||||
|
(review.end_time ?? timeRange.before) - firstPrev.end;
|
||||||
|
|
||||||
if (firstDuration > secondDuration) {
|
if (firstDuration > secondDuration) {
|
||||||
// the first preview is longer than the second, return the first
|
// the first preview is longer than the second, return the first
|
||||||
@ -123,7 +130,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}, [allPreviews, review]);
|
}, [allPreviews, review, timeRange]);
|
||||||
|
|
||||||
// Hover Playback
|
// Hover Playback
|
||||||
|
|
||||||
@ -183,6 +190,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
<PreviewContent
|
<PreviewContent
|
||||||
review={review}
|
review={review}
|
||||||
relevantPreview={relevantPreview}
|
relevantPreview={relevantPreview}
|
||||||
|
timeRange={timeRange}
|
||||||
setReviewed={handleSetReviewed}
|
setReviewed={handleSetReviewed}
|
||||||
setIgnoreClick={setIgnoreClick}
|
setIgnoreClick={setIgnoreClick}
|
||||||
isPlayingBack={setPlayback}
|
isPlayingBack={setPlayback}
|
||||||
@ -256,7 +264,13 @@ export default function PreviewThumbnailPlayer({
|
|||||||
<div className="absolute top-0 inset-x-0 rounded-t-l z-10 w-full h-[30%] bg-gradient-to-b from-black/60 to-transparent pointer-events-none"></div>
|
<div className="absolute top-0 inset-x-0 rounded-t-l z-10 w-full h-[30%] bg-gradient-to-b from-black/60 to-transparent pointer-events-none"></div>
|
||||||
<div className="absolute bottom-0 inset-x-0 rounded-b-l z-10 w-full h-[20%] bg-gradient-to-t from-black/60 to-transparent pointer-events-none">
|
<div className="absolute bottom-0 inset-x-0 rounded-b-l z-10 w-full h-[20%] bg-gradient-to-t from-black/60 to-transparent pointer-events-none">
|
||||||
<div className="flex h-full justify-between items-end mx-3 pb-1 text-white text-sm">
|
<div className="flex h-full justify-between items-end mx-3 pb-1 text-white text-sm">
|
||||||
|
{review.end_time ? (
|
||||||
<TimeAgo time={review.start_time * 1000} dense />
|
<TimeAgo time={review.start_time * 1000} dense />
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<ActivityIndicator size={24} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{formattedDate}
|
{formattedDate}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -270,6 +284,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
type PreviewContentProps = {
|
type PreviewContentProps = {
|
||||||
review: ReviewSegment;
|
review: ReviewSegment;
|
||||||
relevantPreview: Preview | undefined;
|
relevantPreview: Preview | undefined;
|
||||||
|
timeRange: TimeRange;
|
||||||
setReviewed: () => void;
|
setReviewed: () => void;
|
||||||
setIgnoreClick: (ignore: boolean) => void;
|
setIgnoreClick: (ignore: boolean) => void;
|
||||||
isPlayingBack: (ended: boolean) => void;
|
isPlayingBack: (ended: boolean) => void;
|
||||||
@ -278,6 +293,7 @@ type PreviewContentProps = {
|
|||||||
function PreviewContent({
|
function PreviewContent({
|
||||||
review,
|
review,
|
||||||
relevantPreview,
|
relevantPreview,
|
||||||
|
timeRange,
|
||||||
setReviewed,
|
setReviewed,
|
||||||
setIgnoreClick,
|
setIgnoreClick,
|
||||||
isPlayingBack,
|
isPlayingBack,
|
||||||
@ -300,6 +316,7 @@ function PreviewContent({
|
|||||||
return (
|
return (
|
||||||
<InProgressPreview
|
<InProgressPreview
|
||||||
review={review}
|
review={review}
|
||||||
|
timeRange={timeRange}
|
||||||
setReviewed={setReviewed}
|
setReviewed={setReviewed}
|
||||||
setIgnoreClick={setIgnoreClick}
|
setIgnoreClick={setIgnoreClick}
|
||||||
isPlayingBack={isPlayingBack}
|
isPlayingBack={isPlayingBack}
|
||||||
@ -348,7 +365,10 @@ function VideoPreview({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
const playerDuration = useMemo(
|
const playerDuration = useMemo(
|
||||||
() => review.end_time - review.start_time + PREVIEW_PADDING,
|
() =>
|
||||||
|
(review.end_time ?? relevantPreview.end) -
|
||||||
|
review.start_time +
|
||||||
|
PREVIEW_PADDING,
|
||||||
// we know that these deps are correct
|
// we know that these deps are correct
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
@ -551,6 +571,7 @@ function VideoPreview({
|
|||||||
const MIN_LOAD_TIMEOUT_MS = 200;
|
const MIN_LOAD_TIMEOUT_MS = 200;
|
||||||
type InProgressPreviewProps = {
|
type InProgressPreviewProps = {
|
||||||
review: ReviewSegment;
|
review: ReviewSegment;
|
||||||
|
timeRange: TimeRange;
|
||||||
setReviewed: (reviewId: string) => void;
|
setReviewed: (reviewId: string) => void;
|
||||||
setIgnoreClick: (ignore: boolean) => void;
|
setIgnoreClick: (ignore: boolean) => void;
|
||||||
isPlayingBack: (ended: boolean) => void;
|
isPlayingBack: (ended: boolean) => void;
|
||||||
@ -558,6 +579,7 @@ type InProgressPreviewProps = {
|
|||||||
};
|
};
|
||||||
function InProgressPreview({
|
function InProgressPreview({
|
||||||
review,
|
review,
|
||||||
|
timeRange,
|
||||||
setReviewed,
|
setReviewed,
|
||||||
setIgnoreClick,
|
setIgnoreClick,
|
||||||
isPlayingBack,
|
isPlayingBack,
|
||||||
@ -567,7 +589,7 @@ function InProgressPreview({
|
|||||||
const sliderRef = useRef<HTMLDivElement | null>(null);
|
const sliderRef = useRef<HTMLDivElement | null>(null);
|
||||||
const { data: previewFrames } = useSWR<string[]>(
|
const { data: previewFrames } = useSWR<string[]>(
|
||||||
`preview/${review.camera}/start/${Math.floor(review.start_time) - PREVIEW_PADDING}/end/${
|
`preview/${review.camera}/start/${Math.floor(review.start_time) - PREVIEW_PADDING}/end/${
|
||||||
Math.ceil(review.end_time) + PREVIEW_PADDING
|
Math.ceil(review.end_time ?? timeRange.before) + PREVIEW_PADDING
|
||||||
}/frames`,
|
}/frames`,
|
||||||
{ revalidateOnFocus: false },
|
{ revalidateOnFocus: false },
|
||||||
);
|
);
|
||||||
|
|||||||
@ -204,7 +204,7 @@ export default function Events() {
|
|||||||
const newData = [...data];
|
const newData = [...data];
|
||||||
|
|
||||||
newData.forEach((seg) => {
|
newData.forEach((seg) => {
|
||||||
if (seg.severity == severity) {
|
if (seg.end_time && seg.severity == severity) {
|
||||||
seg.has_been_reviewed = true;
|
seg.has_been_reviewed = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -214,10 +214,16 @@ export default function Events() {
|
|||||||
{ revalidate: false, populateCache: true },
|
{ revalidate: false, populateCache: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const itemsToMarkReviewed = currentItems
|
||||||
|
?.filter((seg) => seg.end_time)
|
||||||
|
?.map((seg) => seg.id);
|
||||||
|
|
||||||
|
if (itemsToMarkReviewed.length > 0) {
|
||||||
await axios.post(`reviews/viewed`, {
|
await axios.post(`reviews/viewed`, {
|
||||||
ids: currentItems?.map((seg) => seg.id),
|
ids: itemsToMarkReviewed,
|
||||||
});
|
});
|
||||||
reloadData();
|
reloadData();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[reloadData, updateSegments],
|
[reloadData, updateSegments],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ export interface ReviewSegment {
|
|||||||
camera: string;
|
camera: string;
|
||||||
severity: ReviewSeverity;
|
severity: ReviewSeverity;
|
||||||
start_time: number;
|
start_time: number;
|
||||||
end_time: number;
|
end_time?: number;
|
||||||
thumb_path: string;
|
thumb_path: string;
|
||||||
has_been_reviewed: boolean;
|
has_been_reviewed: boolean;
|
||||||
data: ReviewData;
|
data: ReviewData;
|
||||||
|
|||||||
@ -546,6 +546,7 @@ function DetectionReview({
|
|||||||
<PreviewThumbnailPlayer
|
<PreviewThumbnailPlayer
|
||||||
review={value}
|
review={value}
|
||||||
allPreviews={relevantPreviews}
|
allPreviews={relevantPreviews}
|
||||||
|
timeRange={timeRange}
|
||||||
setReviewed={markItemAsReviewed}
|
setReviewed={markItemAsReviewed}
|
||||||
scrollLock={scrollLock}
|
scrollLock={scrollLock}
|
||||||
onTimeUpdate={onPreviewTimeUpdate}
|
onTimeUpdate={onPreviewTimeUpdate}
|
||||||
@ -787,16 +788,16 @@ function MotionReview({
|
|||||||
} else {
|
} else {
|
||||||
const segmentStartTime = alignStartDateToTimeline(currentTime);
|
const segmentStartTime = alignStartDateToTimeline(currentTime);
|
||||||
const segmentEndTime = segmentStartTime + segmentDuration;
|
const segmentEndTime = segmentStartTime + segmentDuration;
|
||||||
const matchingItem = reviewItems?.all.find(
|
const matchingItem = reviewItems?.all.find((item) => {
|
||||||
(item) =>
|
const endTime = item.end_time ?? timeRange.before;
|
||||||
|
|
||||||
((item.start_time >= segmentStartTime &&
|
((item.start_time >= segmentStartTime &&
|
||||||
item.start_time < segmentEndTime) ||
|
item.start_time < segmentEndTime) ||
|
||||||
(item.end_time > segmentStartTime &&
|
(endTime > segmentStartTime && endTime <= segmentEndTime) ||
|
||||||
item.end_time <= segmentEndTime) ||
|
|
||||||
(item.start_time <= segmentStartTime &&
|
(item.start_time <= segmentStartTime &&
|
||||||
item.end_time >= segmentEndTime)) &&
|
endTime >= segmentEndTime)) &&
|
||||||
item.camera === cameraName,
|
item.camera === cameraName;
|
||||||
);
|
});
|
||||||
|
|
||||||
return matchingItem ? matchingItem.severity : null;
|
return matchingItem ? matchingItem.severity : null;
|
||||||
}
|
}
|
||||||
@ -805,6 +806,7 @@ function MotionReview({
|
|||||||
reviewItems,
|
reviewItems,
|
||||||
motionData,
|
motionData,
|
||||||
currentTime,
|
currentTime,
|
||||||
|
timeRange,
|
||||||
motionOnly,
|
motionOnly,
|
||||||
alignStartDateToTimeline,
|
alignStartDateToTimeline,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -47,8 +47,8 @@ export default function LiveDashboardView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if event is ended and was saved, update events list
|
// if event is ended and was saved, update events list
|
||||||
if (eventUpdate.type == "end" && eventUpdate.review.severity == "alert") {
|
if (eventUpdate.review.severity == "alert") {
|
||||||
setTimeout(() => updateEvents(), 1000);
|
setTimeout(() => updateEvents(), eventUpdate.type == "end" ? 1000 : 6000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, [eventUpdate, updateEvents]);
|
}, [eventUpdate, updateEvents]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user