Various fixes (#14786)

* Catch openvino error

* Remove clip deletion

* Update deletion text

* Fix timeline not respecting timezone config

* Tweaks

* More timezone fixes

* Fix

* More timezone fixes

* Fix shm docs
This commit is contained in:
Nicolas Mowen 2024-11-04 07:07:57 -07:00 committed by GitHub
parent 156e7cc628
commit a13b9815f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 103 additions and 257 deletions

View File

@ -81,15 +81,15 @@ You can calculate the **minimum** shm size for each camera with the following fo
```console ```console
# Replace <width> and <height> # Replace <width> and <height>
$ python -c 'print("{:.2f}MB".format((<width> * <height> * 1.5 * 10 + 270480) / 1048576))' $ python -c 'print("{:.2f}MB".format((<width> * <height> * 1.5 * 20 + 270480) / 1048576))'
# Example for 1280x720 # Example for 1280x720, including logs
$ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 10 + 270480) / 1048576))' $ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 20 + 270480) / 1048576)) + 40'
13.44MB 46.63MB
# Example for eight cameras detecting at 1280x720, including logs # Example for eight cameras detecting at 1280x720, including logs
$ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 10 + 270480) / 1048576) * 8 + 40))' $ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 20 + 270480) / 1048576) * 8 + 40))'
136.99MB 253MB
``` ```
The shm size cannot be set per container for Home Assistant add-ons. However, this is probably not required since by default Home Assistant Supervisor allocates `/dev/shm` with half the size of your total memory. If your machine has 8GB of memory, chances are that Frigate will have access to up to 4GB without any additional configuration. The shm size cannot be set per container for Home Assistant add-ons. However, this is probably not required since by default Home Assistant Supervisor allocates `/dev/shm` with half the size of your total memory. If your machine has 8GB of memory, chances are that Frigate will have access to up to 4GB without any additional configuration.
@ -194,7 +194,7 @@ services:
privileged: true # this may not be necessary for all setups privileged: true # this may not be necessary for all setups
restart: unless-stopped restart: unless-stopped
image: ghcr.io/blakeblackshear/frigate:stable image: ghcr.io/blakeblackshear/frigate:stable
shm_size: "64mb" # update for your cameras based on calculation above shm_size: "512mb" # update for your cameras based on calculation above
devices: devices:
- /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
- /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux - /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux

View File

@ -1042,9 +1042,6 @@ def delete_event(request: Request, event_id: str):
media.unlink(missing_ok=True) media.unlink(missing_ok=True)
media = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png") media = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png")
media.unlink(missing_ok=True) media.unlink(missing_ok=True)
if event.has_clip:
media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4")
media.unlink(missing_ok=True)
event.delete_instance() event.delete_instance()
Timeline.delete().where(Timeline.source_id == event_id).execute() Timeline.delete().where(Timeline.source_id == event_id).execute()

View File

@ -521,9 +521,9 @@ class FrigateApp:
f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM" f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM"
) )
if shm_frame_count < 10: if shm_frame_count < 20:
logger.warning( logger.warning(
f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 10)}MB." f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 20)}MB."
) )
return shm_frame_count return shm_frame_count

View File

@ -1,5 +1,6 @@
"""Model Utils""" """Model Utils"""
import logging
import os import os
from typing import Any from typing import Any
@ -11,6 +12,8 @@ except ImportError:
# openvino is not included # openvino is not included
pass pass
logger = logging.getLogger(__name__)
def get_ort_providers( def get_ort_providers(
force_cpu: bool = False, device: str = "AUTO", requires_fp16: bool = False force_cpu: bool = False, device: str = "AUTO", requires_fp16: bool = False
@ -89,19 +92,27 @@ class ONNXModelRunner:
self.ort: ort.InferenceSession = None self.ort: ort.InferenceSession = None
self.ov: ov.Core = None self.ov: ov.Core = None
providers, options = get_ort_providers(device == "CPU", device, requires_fp16) providers, options = get_ort_providers(device == "CPU", device, requires_fp16)
self.interpreter = None
if "OpenVINOExecutionProvider" in providers: if "OpenVINOExecutionProvider" in providers:
# use OpenVINO directly try:
self.type = "ov" # use OpenVINO directly
self.ov = ov.Core() self.type = "ov"
self.ov.set_property( self.ov = ov.Core()
{ov.properties.cache_dir: "/config/model_cache/openvino"} self.ov.set_property(
) {ov.properties.cache_dir: "/config/model_cache/openvino"}
self.interpreter = self.ov.compile_model( )
model=model_path, device_name=device self.interpreter = self.ov.compile_model(
) model=model_path, device_name=device
else: )
# Use ONNXRuntime except Exception as e:
logger.warning(
f"OpenVINO failed to build model, using CPU instead: {e}"
)
self.interpreter = None
# Use ONNXRuntime
if self.interpreter is None:
self.type = "ort" self.type = "ort"
self.ort = ort.InferenceSession( self.ort = ort.InferenceSession(
model_path, model_path,

View File

@ -1,191 +0,0 @@
import { FrigateConfig } from "@/types/frigateConfig";
import { GraphDataPoint } from "@/types/graph";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import useSWR from "swr";
import ActivityIndicator from "../indicators/activity-indicator";
type TimelineBarProps = {
startTime: number;
graphData:
| {
objects: number[];
motion: GraphDataPoint[];
}
| undefined;
onClick?: () => void;
};
export default function TimelineBar({
startTime,
graphData,
onClick,
}: TimelineBarProps) {
const { data: config } = useSWR<FrigateConfig>("config");
if (!config) {
return <ActivityIndicator />;
}
return (
<div
className="h-18 my-1 w-full cursor-pointer rounded border p-1 hover:bg-secondary hover:bg-opacity-30"
onClick={onClick}
>
{graphData != undefined && (
<div className="relative flex h-8 w-full">
{getHourBlocks().map((idx) => {
return (
<div
key={idx}
className={`h-2 flex-auto ${
(graphData.motion.at(idx)?.y || 0) == 0
? ""
: graphData.objects.includes(idx)
? "bg-object"
: "bg-motion"
}`}
/>
);
})}
<div className="absolute bottom-0 left-0 top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:00" : "%I:00%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[8.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:05" : "%I:05%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[16.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:10" : "%I:10%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[25%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:15" : "%I:15%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[33.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:20" : "%I:20%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[41.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:25" : "%I:25%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[50%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:30" : "%I:30%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[58.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:35" : "%I:35%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[66.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:40" : "%I:40%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[75%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:45" : "%I:45%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[83.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:50" : "%I:50%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
<div className="absolute bottom-0 left-[91.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:55" : "%I:55%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
</div>
)}
<div className="text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config.ui.time_format == "24hour" ? "%m/%d %H:%M" : "%m/%d %I:%M%P",
time_style: "medium",
date_style: "medium",
})}
</div>
</div>
);
}
function getHourBlocks() {
const arr = [];
for (let x = 0; x <= 59; x++) {
arr.push(x);
}
return arr;
}

View File

@ -1,5 +1,6 @@
import { useTheme } from "@/context/theme-provider"; import { useTheme } from "@/context/theme-provider";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { useCallback, useEffect, useMemo } from "react"; import { useCallback, useEffect, useMemo } from "react";
import Chart from "react-apexcharts"; import Chart from "react-apexcharts";
import { isMobileOnly } from "react-device-detect"; import { isMobileOnly } from "react-device-detect";
@ -42,12 +43,14 @@ export function CameraLineGraph({
const formatTime = useCallback( const formatTime = useCallback(
(val: unknown) => { (val: unknown) => {
const date = new Date(updateTimes[Math.round(val as number)] * 1000); return formatUnixTimestampToDateTime(
return date.toLocaleTimeString([], { updateTimes[Math.round(val as number)],
hour12: config?.ui.time_format != "24hour", {
hour: "2-digit", timezone: config?.ui.timezone,
minute: "2-digit", strftime_fmt:
}); config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p",
},
);
}, },
[config, updateTimes], [config, updateTimes],
); );

View File

@ -1,6 +1,7 @@
import { useTheme } from "@/context/theme-provider"; import { useTheme } from "@/context/theme-provider";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { Threshold } from "@/types/graph"; import { Threshold } from "@/types/graph";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { useCallback, useEffect, useMemo } from "react"; import { useCallback, useEffect, useMemo } from "react";
import Chart from "react-apexcharts"; import Chart from "react-apexcharts";
import { isMobileOnly } from "react-device-detect"; import { isMobileOnly } from "react-device-detect";
@ -50,17 +51,17 @@ export function ThresholdBarGraph({
let timeOffset = 0; let timeOffset = 0;
if (dateIndex < 0) { if (dateIndex < 0) {
timeOffset = 5000 * Math.abs(dateIndex); timeOffset = 5 * Math.abs(dateIndex);
} }
const date = new Date( return formatUnixTimestampToDateTime(
updateTimes[Math.max(1, dateIndex) - 1] * 1000 - timeOffset, updateTimes[Math.max(1, dateIndex) - 1] - timeOffset,
{
timezone: config?.ui.timezone,
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p",
},
); );
return date.toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit",
minute: "2-digit",
});
}, },
[config, updateTimes], [config, updateTimes],
); );

View File

@ -159,7 +159,13 @@ export default function SearchResultActions({
<AlertDialogTitle>Confirm Delete</AlertDialogTitle> <AlertDialogTitle>Confirm Delete</AlertDialogTitle>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogDescription> <AlertDialogDescription>
Are you sure you want to delete this tracked object? Deleting this tracked object removes the snapshot, any saved
embeddings, and any associated object lifecycle entries. Recorded
footage of this tracked object in History view will <em>NOT</em> be
deleted.
<br />
<br />
Are you sure you want to proceed?
</AlertDialogDescription> </AlertDialogDescription>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>

View File

@ -427,6 +427,7 @@ export default function ObjectLifecycle({
</div> </div>
<div className="text-sm text-primary-variant"> <div className="text-sm text-primary-variant">
{formatUnixTimestampToDateTime(item.timestamp, { {formatUnixTimestampToDateTime(item.timestamp, {
timezone: config.ui.timezone,
strftime_fmt: strftime_fmt:
config.ui.time_format == "24hour" config.ui.time_format == "24hour"
? "%d %b %H:%M:%S" ? "%d %b %H:%M:%S"

View File

@ -1,4 +1,6 @@
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { useMemo } from "react";
import useSWR from "swr"; import useSWR from "swr";
type MinimapSegmentProps = { type MinimapSegmentProps = {
@ -40,22 +42,22 @@ export function MinimapBounds({
className="pointer-events-none absolute inset-0 -bottom-7 z-20 flex w-full select-none scroll-mt-8 items-center justify-center text-center text-[10px] font-medium text-primary" className="pointer-events-none absolute inset-0 -bottom-7 z-20 flex w-full select-none scroll-mt-8 items-center justify-center text-center text-[10px] font-medium text-primary"
ref={firstMinimapSegmentRef} ref={firstMinimapSegmentRef}
> >
{new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], { {formatUnixTimestampToDateTime(alignedMinimapStartTime, {
hour12: config?.ui.time_format != "24hour", timezone: config?.ui.timezone,
hour: "2-digit", strftime_fmt: !dense
minute: "2-digit", ? `%b %d, ${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`
...(!dense && { month: "short", day: "2-digit" }), : `${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`,
})} })}
</div> </div>
)} )}
{isLastSegmentInMinimap && ( {isLastSegmentInMinimap && (
<div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary"> <div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary">
{new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], { {formatUnixTimestampToDateTime(alignedMinimapEndTime, {
hour12: config?.ui.time_format != "24hour", timezone: config?.ui.timezone,
hour: "2-digit", strftime_fmt: !dense
minute: "2-digit", ? `%b %d, ${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`
...(!dense && { month: "short", day: "2-digit" }), : `${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`,
})} })}
</div> </div>
)} )}
@ -92,6 +94,22 @@ export function Timestamp({
}: TimestampSegmentProps) { }: TimestampSegmentProps) {
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
const formattedTimestamp = useMemo(() => {
if (
!(
timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0
)
) {
return undefined;
}
return formatUnixTimestampToDateTime(timestamp.getTime() / 1000, {
timezone: config?.ui.timezone,
strftime_fmt: config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p",
});
}, [config, timestamp, timestampSpread]);
return ( return (
<div className="absolute left-[15px] z-10 h-[8px]"> <div className="absolute left-[15px] z-10 h-[8px]">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && ( {!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
@ -99,13 +117,7 @@ export function Timestamp({
key={`${segmentKey}_timestamp`} key={`${segmentKey}_timestamp`}
className="pointer-events-none select-none text-[8px] text-neutral_variant dark:text-neutral" className="pointer-events-none select-none text-[8px] text-neutral_variant dark:text-neutral"
> >
{timestamp.getMinutes() % timestampSpread === 0 && {formattedTimestamp}
timestamp.getSeconds() === 0 &&
timestamp.toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit",
minute: "2-digit",
})}
</div> </div>
)} )}
</div> </div>

View File

@ -10,6 +10,7 @@ import scrollIntoView from "scroll-into-view-if-needed";
import { useTimelineUtils } from "./use-timeline-utils"; import { useTimelineUtils } from "./use-timeline-utils";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import useSWR from "swr"; import useSWR from "swr";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
type DraggableElementProps = { type DraggableElementProps = {
contentRef: React.RefObject<HTMLElement>; contentRef: React.RefObject<HTMLElement>;
@ -168,6 +169,19 @@ function useDraggableElement({
[segmentDuration, timelineStartAligned, segmentHeight], [segmentDuration, timelineStartAligned, segmentHeight],
); );
const getFormattedTimestamp = useCallback(
(segmentStartTime: number) => {
return formatUnixTimestampToDateTime(segmentStartTime, {
timezone: config?.ui.timezone,
strftime_fmt:
config?.ui.time_format == "24hour"
? `%H:%M${segmentDuration < 60 && !dense ? ":%S" : ""}`
: `%I:%M${segmentDuration < 60 && !dense ? ":%S" : ""} %p`,
});
},
[config, dense, segmentDuration],
);
const updateDraggableElementPosition = useCallback( const updateDraggableElementPosition = useCallback(
( (
newElementPosition: number, newElementPosition: number,
@ -184,14 +198,8 @@ function useDraggableElement({
} }
if (draggableElementTimeRef.current) { if (draggableElementTimeRef.current) {
draggableElementTimeRef.current.textContent = new Date( draggableElementTimeRef.current.textContent =
segmentStartTime * 1000, getFormattedTimestamp(segmentStartTime);
).toLocaleTimeString([], {
hour12: config?.ui.time_format != "24hour",
hour: "2-digit",
minute: "2-digit",
...(segmentDuration < 60 && !dense && { second: "2-digit" }),
});
if (scrollTimeline && !userInteracting) { if (scrollTimeline && !userInteracting) {
scrollIntoView(thumb, { scrollIntoView(thumb, {
block: "center", block: "center",
@ -208,13 +216,11 @@ function useDraggableElement({
} }
}, },
[ [
segmentDuration,
draggableElementTimeRef, draggableElementTimeRef,
draggableElementRef, draggableElementRef,
setDraggableElementTime, setDraggableElementTime,
setDraggableElementPosition, setDraggableElementPosition,
dense, getFormattedTimestamp,
config,
userInteracting, userInteracting,
], ],
); );