mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 17:12:16 +03:00
Miscellaneous fixes (0.18 beta) (#23873)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* improve display of gpu graphs in system metrics * docs tweaks * Only hide cameras with ui.dashboard disabled from the All Cameras dashboard The settings camera selector and zone editor also filtered on ui.dashboard, so hiding a camera from the dashboard made its zones and masks uneditable in the UI (GH 23870). Drop those filters and correct the field title, help text, and reference docs to describe what the option actually does * hide cameras with ui.review disabled from the Motion tab and the review summaries The Motion tab built its own camera list that never checked ui.review, so a hidden camera still got a preview tile, and its motion and overlap queries fell back to every allowed camera. The review and recordings summaries had the same gap: they are aggregate day counts that can't be filtered client side, so a hidden camera kept contributing to the severity tab counts and calendar indicators while its items were absent from the list. Filter the motion camera list on ui.review and query all four endpoints with the visible camera list instead of letting the backend default to all, and skip the summary queries until the config resolves so the counts don't briefly render as zero. * Scope every review page query to the cameras visible in review The segments and the summary counts were derived from different camera sets: the list was fetched for all cameras and filtered client side, while the summaries were fetched for the visible cameras only when no explicit camera filter was set. A ?cameras= link can name a camera hidden from review, which left the count above zero with an empty list, pinning the new items to review popover open and making the auto refresh effect loop. Intersect an explicit camera selection with the visible list rather than trusting it, pass that to the segment and summary queries alike, and drop the now redundant client side filter, which the raw segments handed to the history view were bypassing anyway.
This commit is contained in:
@@ -859,8 +859,8 @@
|
||||
"description": "Numeric order used to sort the camera in the UI (default dashboard and lists); larger numbers appear later."
|
||||
},
|
||||
"dashboard": {
|
||||
"label": "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."
|
||||
"label": "Show on Live dashboard",
|
||||
"description": "Toggle whether this camera is visible on the default All Cameras live dashboard. The camera remains available everywhere else in the UI, including camera groups and settings."
|
||||
},
|
||||
"review": {
|
||||
"label": "Show in review",
|
||||
|
||||
@@ -1543,8 +1543,8 @@
|
||||
"description": "Numeric order used to sort the camera in the UI (default dashboard and lists); larger numbers appear later."
|
||||
},
|
||||
"dashboard": {
|
||||
"label": "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."
|
||||
"label": "Show on Live dashboard",
|
||||
"description": "Toggle whether this camera is visible on the default All Cameras live dashboard. The camera remains available everywhere else in the UI, including camera groups and settings."
|
||||
},
|
||||
"review": {
|
||||
"label": "Show in review",
|
||||
|
||||
@@ -499,7 +499,7 @@
|
||||
"webuiUrlHelp": "URL to visit the camera's web UI directly from the Debug view. Leave blank to disable the link.",
|
||||
"webuiUrlInvalid": "Must be a valid URL (e.g., https://example.com).",
|
||||
"dashboardLabel": "Show on Live dashboard",
|
||||
"dashboardHelp": "Show this camera on the Live dashboard.",
|
||||
"dashboardHelp": "Show this camera on the default All Cameras live dashboard. It remains available everywhere else, including camera groups.",
|
||||
"reviewLabel": "Show in Review",
|
||||
"reviewHelp": "Show this camera in Review, including the camera filter, motion review, and the history view."
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function ZoneEditPane({
|
||||
}
|
||||
|
||||
return Object.values(config.cameras)
|
||||
.filter((conf) => conf.ui.dashboard && conf.enabled_in_config)
|
||||
.filter((conf) => conf.enabled_in_config)
|
||||
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||
}, [config]);
|
||||
|
||||
|
||||
+42
-23
@@ -198,6 +198,19 @@ export default function Events() {
|
||||
return true;
|
||||
});
|
||||
|
||||
const reviewCamerasParam = useMemo(() => {
|
||||
const selected: string | undefined = reviewSearchParams["cameras"];
|
||||
|
||||
if (!selected) {
|
||||
return reviewCameras.join(",");
|
||||
}
|
||||
|
||||
const selectedCameras = new Set(selected.split(","));
|
||||
return reviewCameras
|
||||
.filter((camera) => selectedCameras.has(camera))
|
||||
.join(",");
|
||||
}, [reviewCameras, reviewSearchParams]);
|
||||
|
||||
useSearchEffect("labels", (labels: string) => {
|
||||
setReviewFilter({
|
||||
...reviewFilter,
|
||||
@@ -330,8 +343,12 @@ export default function Events() {
|
||||
}, []);
|
||||
|
||||
const getKey = useCallback(() => {
|
||||
if (!timezone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const params = {
|
||||
cameras: reviewSearchParams["cameras"],
|
||||
cameras: reviewCamerasParam,
|
||||
labels: reviewSearchParams["labels"],
|
||||
zones: reviewSearchParams["zones"],
|
||||
reviewed: null, // We want both reviewed and unreviewed items as we filter in the UI
|
||||
@@ -339,7 +356,7 @@ export default function Events() {
|
||||
after: reviewSearchParams["after"] || last24Hours.after,
|
||||
};
|
||||
return ["review", params];
|
||||
}, [reviewSearchParams, last24Hours]);
|
||||
}, [reviewSearchParams, reviewCamerasParam, last24Hours, timezone]);
|
||||
|
||||
const { data: reviews, mutate: updateSegments } = useSWR<ReviewSegment[]>(
|
||||
getKey,
|
||||
@@ -361,10 +378,6 @@ export default function Events() {
|
||||
const motion: ReviewSegment[] = [];
|
||||
|
||||
reviews?.forEach((segment) => {
|
||||
if (config?.cameras[segment.camera]?.ui?.review === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
all.push(segment);
|
||||
|
||||
switch (segment.severity) {
|
||||
@@ -386,7 +399,7 @@ export default function Events() {
|
||||
detection: detections,
|
||||
significant_motion: motion,
|
||||
};
|
||||
}, [reviews, config?.cameras]);
|
||||
}, [reviews]);
|
||||
|
||||
// update review items in place when a review segment ends
|
||||
const reviewUpdate = useFrigateReviews();
|
||||
@@ -450,15 +463,17 @@ export default function Events() {
|
||||
// review summary
|
||||
|
||||
const { data: reviewSummary, mutate: updateSummary } = useSWR<ReviewSummary>(
|
||||
[
|
||||
"review/summary",
|
||||
{
|
||||
timezone: timezone,
|
||||
cameras: reviewSearchParams["cameras"] ?? null,
|
||||
labels: reviewSearchParams["labels"] ?? null,
|
||||
zones: reviewSearchParams["zones"] ?? null,
|
||||
},
|
||||
],
|
||||
timezone
|
||||
? [
|
||||
"review/summary",
|
||||
{
|
||||
timezone: timezone,
|
||||
cameras: reviewCamerasParam,
|
||||
labels: reviewSearchParams["labels"] ?? null,
|
||||
zones: reviewSearchParams["zones"] ?? null,
|
||||
},
|
||||
]
|
||||
: null,
|
||||
{
|
||||
revalidateOnFocus: true,
|
||||
refreshInterval: 30000,
|
||||
@@ -473,13 +488,17 @@ export default function Events() {
|
||||
|
||||
// recordings summary
|
||||
|
||||
const { data: recordingsSummary } = useSWR<RecordingsSummary>([
|
||||
"recordings/summary",
|
||||
{
|
||||
timezone: timezone,
|
||||
cameras: reviewSearchParams["cameras"] ?? null,
|
||||
},
|
||||
]);
|
||||
const { data: recordingsSummary } = useSWR<RecordingsSummary>(
|
||||
timezone
|
||||
? [
|
||||
"recordings/summary",
|
||||
{
|
||||
timezone: timezone,
|
||||
cameras: reviewCamerasParam,
|
||||
},
|
||||
]
|
||||
: null,
|
||||
);
|
||||
|
||||
// preview videos
|
||||
const previewTimes = useMemo(() => {
|
||||
|
||||
@@ -706,12 +706,7 @@ export default function Settings() {
|
||||
}
|
||||
|
||||
return Object.values(config.cameras)
|
||||
.filter(
|
||||
(conf) =>
|
||||
conf.ui.dashboard &&
|
||||
conf.enabled_in_config &&
|
||||
!isReplayCamera(conf.name),
|
||||
)
|
||||
.filter((conf) => conf.enabled_in_config && !isReplayCamera(conf.name))
|
||||
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||
}, [config]);
|
||||
|
||||
|
||||
@@ -1024,6 +1024,9 @@ function MotionReview({
|
||||
if (!allowedCameras.includes(cam.name)) {
|
||||
return false;
|
||||
}
|
||||
if (cam.ui?.review === false) {
|
||||
return false;
|
||||
}
|
||||
if (selectedCams && !selectedCams.includes(cam.name)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1033,6 +1036,11 @@ function MotionReview({
|
||||
return cameras.sort((a, b) => a.ui.order - b.ui.order);
|
||||
}, [config, filter, allowedCameras]);
|
||||
|
||||
const reviewCamerasParam = useMemo(
|
||||
() => reviewCameras.map((cam) => cam.name).join(","),
|
||||
[reviewCameras],
|
||||
);
|
||||
|
||||
const videoPlayersRef = useRef<{ [camera: string]: PreviewController }>({});
|
||||
|
||||
// motion data
|
||||
@@ -1052,7 +1060,7 @@ function MotionReview({
|
||||
before: alignedBefore,
|
||||
after: alignedAfter,
|
||||
scale: segmentDuration / 2,
|
||||
cameras: filter?.cameras?.join(",") ?? null,
|
||||
cameras: reviewCamerasParam,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -1061,7 +1069,7 @@ function MotionReview({
|
||||
{
|
||||
before: alignedBefore,
|
||||
after: alignedAfter,
|
||||
cameras: filter?.cameras?.join(",") ?? null,
|
||||
cameras: reviewCamerasParam,
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
@@ -540,6 +540,36 @@ export default function GeneralMetrics({
|
||||
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
|
||||
}, [statsHistory]);
|
||||
|
||||
// Number of cards the hardware grid renders. Which ones appear depends on
|
||||
// the vendor, so the column count follows the count rather than assuming a
|
||||
// fixed set is present.
|
||||
const hardwareCardCount = useMemo(() => {
|
||||
if (!statsHistory[0]?.gpu_usages) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const hasNpu = statsHistory[0].npu_usages != undefined;
|
||||
|
||||
return (
|
||||
1 + // gpu usage always renders alongside gpu_usages
|
||||
(gpuMemSeries ? 1 : 0) +
|
||||
(gpuEncSeries?.length ? 1 : 0) +
|
||||
(gpuComputeSeries?.length ? 1 : 0) +
|
||||
(gpuDecSeries?.length ? 1 : 0) +
|
||||
(gpuTempSeries?.length ? 1 : 0) +
|
||||
(hasNpu ? 1 : 0) +
|
||||
(hasNpu && npuTempSeries?.length ? 1 : 0)
|
||||
);
|
||||
}, [
|
||||
statsHistory,
|
||||
gpuMemSeries,
|
||||
gpuEncSeries,
|
||||
gpuComputeSeries,
|
||||
gpuDecSeries,
|
||||
gpuTempSeries,
|
||||
npuTempSeries,
|
||||
]);
|
||||
|
||||
// other processes stats
|
||||
|
||||
const hardwareType = useMemo(() => {
|
||||
@@ -763,12 +793,9 @@ export default function GeneralMetrics({
|
||||
<div
|
||||
className={cn(
|
||||
"mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2",
|
||||
gpuTempSeries?.length && "md:grid-cols-3",
|
||||
(gpuEncSeries?.length || gpuComputeSeries?.length) &&
|
||||
"xl:grid-cols-4",
|
||||
(gpuEncSeries?.length || gpuComputeSeries?.length) &&
|
||||
gpuTempSeries?.length &&
|
||||
"3xl:grid-cols-5",
|
||||
hardwareCardCount >= 3 && "lg:grid-cols-3",
|
||||
hardwareCardCount >= 4 && "xl:grid-cols-4",
|
||||
hardwareCardCount >= 5 && "3xl:grid-cols-5",
|
||||
)}
|
||||
>
|
||||
{statsHistory[0]?.gpu_usages && (
|
||||
|
||||
Reference in New Issue
Block a user