mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-21 04:26:43 +03:00
Merge 44077ebe43 into de066d0062
This commit is contained in:
commit
84a21546c3
@ -424,7 +424,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
return {
|
return {
|
||||||
"message": "No face was recognized.",
|
"message": "Model is still training, please try again in a few moments.",
|
||||||
"success": False,
|
"success": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -398,11 +398,7 @@ export function GroupedClassificationCard({
|
|||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
selected={false}
|
selected={false}
|
||||||
i18nLibrary={i18nLibrary}
|
i18nLibrary={i18nLibrary}
|
||||||
onClick={(data, meta) => {
|
onClick={() => {}}
|
||||||
if (meta || selectedItems.length > 0) {
|
|
||||||
onClick(data);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children?.(data)}
|
{children?.(data)}
|
||||||
</ClassificationCard>
|
</ClassificationCard>
|
||||||
|
|||||||
@ -683,6 +683,22 @@ function ObjectDetailsTab({
|
|||||||
|
|
||||||
const mutate = useGlobalMutation();
|
const mutate = useGlobalMutation();
|
||||||
|
|
||||||
|
// Helper to map over SWR cached search results while preserving
|
||||||
|
// either paginated format (SearchResult[][]) or flat format (SearchResult[])
|
||||||
|
const mapSearchResults = useCallback(
|
||||||
|
(
|
||||||
|
currentData: SearchResult[][] | SearchResult[] | undefined,
|
||||||
|
fn: (event: SearchResult) => SearchResult,
|
||||||
|
) => {
|
||||||
|
if (!currentData) return currentData;
|
||||||
|
if (Array.isArray(currentData[0])) {
|
||||||
|
return (currentData as SearchResult[][]).map((page) => page.map(fn));
|
||||||
|
}
|
||||||
|
return (currentData as SearchResult[]).map(fn);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
// users
|
// users
|
||||||
|
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
@ -810,17 +826,12 @@ function ObjectDetailsTab({
|
|||||||
(key.includes("events") ||
|
(key.includes("events") ||
|
||||||
key.includes("events/search") ||
|
key.includes("events/search") ||
|
||||||
key.includes("events/explore")),
|
key.includes("events/explore")),
|
||||||
(currentData: SearchResult[][] | SearchResult[] | undefined) => {
|
(currentData: SearchResult[][] | SearchResult[] | undefined) =>
|
||||||
if (!currentData) return currentData;
|
mapSearchResults(currentData, (event) =>
|
||||||
// optimistic update
|
|
||||||
return currentData
|
|
||||||
.flat()
|
|
||||||
.map((event) =>
|
|
||||||
event.id === search.id
|
event.id === search.id
|
||||||
? { ...event, data: { ...event.data, description: desc } }
|
? { ...event, data: { ...event.data, description: desc } }
|
||||||
: event,
|
: event,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
optimisticData: true,
|
optimisticData: true,
|
||||||
rollbackOnError: true,
|
rollbackOnError: true,
|
||||||
@ -843,7 +854,7 @@ function ObjectDetailsTab({
|
|||||||
);
|
);
|
||||||
setDesc(search.data.description);
|
setDesc(search.data.description);
|
||||||
});
|
});
|
||||||
}, [desc, search, mutate, t]);
|
}, [desc, search, mutate, t, mapSearchResults]);
|
||||||
|
|
||||||
const regenerateDescription = useCallback(
|
const regenerateDescription = useCallback(
|
||||||
(source: "snapshot" | "thumbnails") => {
|
(source: "snapshot" | "thumbnails") => {
|
||||||
@ -915,9 +926,8 @@ function ObjectDetailsTab({
|
|||||||
(key.includes("events") ||
|
(key.includes("events") ||
|
||||||
key.includes("events/search") ||
|
key.includes("events/search") ||
|
||||||
key.includes("events/explore")),
|
key.includes("events/explore")),
|
||||||
(currentData: SearchResult[][] | SearchResult[] | undefined) => {
|
(currentData: SearchResult[][] | SearchResult[] | undefined) =>
|
||||||
if (!currentData) return currentData;
|
mapSearchResults(currentData, (event) =>
|
||||||
return currentData.flat().map((event) =>
|
|
||||||
event.id === search.id
|
event.id === search.id
|
||||||
? {
|
? {
|
||||||
...event,
|
...event,
|
||||||
@ -928,8 +938,7 @@ function ObjectDetailsTab({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
: event,
|
: event,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
optimisticData: true,
|
optimisticData: true,
|
||||||
rollbackOnError: true,
|
rollbackOnError: true,
|
||||||
@ -963,7 +972,7 @@ function ObjectDetailsTab({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[search, apiHost, mutate, setSearch, t],
|
[search, apiHost, mutate, setSearch, t, mapSearchResults],
|
||||||
);
|
);
|
||||||
|
|
||||||
// recognized plate
|
// recognized plate
|
||||||
@ -992,9 +1001,8 @@ function ObjectDetailsTab({
|
|||||||
(key.includes("events") ||
|
(key.includes("events") ||
|
||||||
key.includes("events/search") ||
|
key.includes("events/search") ||
|
||||||
key.includes("events/explore")),
|
key.includes("events/explore")),
|
||||||
(currentData: SearchResult[][] | SearchResult[] | undefined) => {
|
(currentData: SearchResult[][] | SearchResult[] | undefined) =>
|
||||||
if (!currentData) return currentData;
|
mapSearchResults(currentData, (event) =>
|
||||||
return currentData.flat().map((event) =>
|
|
||||||
event.id === search.id
|
event.id === search.id
|
||||||
? {
|
? {
|
||||||
...event,
|
...event,
|
||||||
@ -1005,8 +1013,7 @@ function ObjectDetailsTab({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
: event,
|
: event,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
optimisticData: true,
|
optimisticData: true,
|
||||||
rollbackOnError: true,
|
rollbackOnError: true,
|
||||||
@ -1040,7 +1047,7 @@ function ObjectDetailsTab({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[search, apiHost, mutate, setSearch, t],
|
[search, apiHost, mutate, setSearch, t, mapSearchResults],
|
||||||
);
|
);
|
||||||
|
|
||||||
// speech transcription
|
// speech transcription
|
||||||
@ -1102,17 +1109,12 @@ function ObjectDetailsTab({
|
|||||||
(key.includes("events") ||
|
(key.includes("events") ||
|
||||||
key.includes("events/search") ||
|
key.includes("events/search") ||
|
||||||
key.includes("events/explore")),
|
key.includes("events/explore")),
|
||||||
(currentData: SearchResult[][] | SearchResult[] | undefined) => {
|
(currentData: SearchResult[][] | SearchResult[] | undefined) =>
|
||||||
if (!currentData) return currentData;
|
mapSearchResults(currentData, (event) =>
|
||||||
// optimistic update
|
|
||||||
return currentData
|
|
||||||
.flat()
|
|
||||||
.map((event) =>
|
|
||||||
event.id === search.id
|
event.id === search.id
|
||||||
? { ...event, plus_id: "new_upload" }
|
? { ...event, plus_id: "new_upload" }
|
||||||
: event,
|
: event,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
optimisticData: true,
|
optimisticData: true,
|
||||||
rollbackOnError: true,
|
rollbackOnError: true,
|
||||||
@ -1120,7 +1122,7 @@ function ObjectDetailsTab({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[search, mutate],
|
[search, mutate, mapSearchResults],
|
||||||
);
|
);
|
||||||
|
|
||||||
const popoverContainerRef = useRef<HTMLDivElement | null>(null);
|
const popoverContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
@ -1503,7 +1505,7 @@ function ObjectDetailsTab({
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md h-32"
|
className="text-md h-32 md:text-sm"
|
||||||
placeholder={t("details.description.placeholder")}
|
placeholder={t("details.description.placeholder")}
|
||||||
value={desc}
|
value={desc}
|
||||||
onChange={(e) => setDesc(e.target.value)}
|
onChange={(e) => setDesc(e.target.value)}
|
||||||
@ -1511,25 +1513,7 @@ function ObjectDetailsTab({
|
|||||||
onBlur={handleDescriptionBlur}
|
onBlur={handleDescriptionBlur}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-row justify-end gap-4">
|
<div className="mb-10 flex flex-row justify-end gap-5">
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
|
||||||
className="text-primary/40 hover:text-primary/80"
|
|
||||||
onClick={() => {
|
|
||||||
setIsEditingDesc(false);
|
|
||||||
updateDescription();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FaCheck className="size-4" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
{t("button.save", { ns: "common" })}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
@ -1540,13 +1524,31 @@ function ObjectDetailsTab({
|
|||||||
setDesc(originalDescRef.current ?? "");
|
setDesc(originalDescRef.current ?? "");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FaTimes className="size-4" />
|
<FaTimes className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
|
className="text-primary/40 hover:text-primary/80"
|
||||||
|
onClick={() => {
|
||||||
|
setIsEditingDesc(false);
|
||||||
|
updateDescription();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaCheck className="size-5" />
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{t("button.save", { ns: "common" })}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -72,8 +72,7 @@ export default function StorageMetrics({
|
|||||||
const earliestDate = useMemo(() => {
|
const earliestDate = useMemo(() => {
|
||||||
const keys = Object.keys(recordingsSummary || {});
|
const keys = Object.keys(recordingsSummary || {});
|
||||||
return keys.length
|
return keys.length
|
||||||
? new TZDate(keys[keys.length - 1] + "T00:00:00", timezone).getTime() /
|
? new TZDate(keys[0] + "T00:00:00", timezone).getTime() / 1000
|
||||||
1000
|
|
||||||
: null;
|
: null;
|
||||||
}, [recordingsSummary, timezone]);
|
}, [recordingsSummary, timezone]);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user