mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-10 00:57:38 +03:00
Updated understanding: events support multiple classification models
Co-authored-by: Teagan42 <2989925+Teagan42@users.noreply.github.com>
This commit is contained in:
parent
8f8e9badd3
commit
220f3e1e7e
@ -35,6 +35,7 @@ type ClassificationSelectionDialogProps = {
|
||||
modelName: string;
|
||||
image: string;
|
||||
onRefresh: () => void;
|
||||
onCategorize?: (category: string) => void; // Optional custom categorize handler
|
||||
children: ReactNode;
|
||||
};
|
||||
export default function ClassificationSelectionDialog({
|
||||
@ -43,12 +44,20 @@ export default function ClassificationSelectionDialog({
|
||||
modelName,
|
||||
image,
|
||||
onRefresh,
|
||||
onCategorize,
|
||||
children,
|
||||
}: ClassificationSelectionDialogProps) {
|
||||
const { t } = useTranslation(["views/classificationModel"]);
|
||||
|
||||
const onCategorizeImage = useCallback(
|
||||
(category: string) => {
|
||||
// If custom categorize handler is provided, use it instead
|
||||
if (onCategorize) {
|
||||
onCategorize(category);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default behavior: categorize single image
|
||||
axios
|
||||
.post(`/classification/${modelName}/dataset/categorize`, {
|
||||
category,
|
||||
@ -72,7 +81,7 @@ export default function ClassificationSelectionDialog({
|
||||
});
|
||||
});
|
||||
},
|
||||
[modelName, image, onRefresh, t],
|
||||
[modelName, image, onRefresh, onCategorize, t],
|
||||
);
|
||||
|
||||
const isChildButton = useMemo(
|
||||
|
||||
@ -458,6 +458,89 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{pageToggle === "train" && (
|
||||
<ClassificationSelectionDialog
|
||||
classes={classes}
|
||||
modelName={model.name}
|
||||
image={selectedImages[0]}
|
||||
onRefresh={(category: string) => {
|
||||
// Batch categorize all selected images
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
const totalCount = selectedImages.length;
|
||||
|
||||
selectedImages.forEach((filename, index) => {
|
||||
axios
|
||||
.post(`/classification/${model.name}/dataset/categorize`, {
|
||||
category,
|
||||
training_file: filename,
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.status == 200) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
|
||||
// Show final toast after all requests complete
|
||||
if (index === totalCount - 1) {
|
||||
if (successCount === totalCount) {
|
||||
toast.success(
|
||||
t("toast.success.batchCategorized", {
|
||||
count: successCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else if (successCount > 0) {
|
||||
toast.warning(
|
||||
t("toast.warning.partialBatchCategorized", {
|
||||
success: successCount,
|
||||
total: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.error(
|
||||
t("toast.error.batchCategorizeFailed", {
|
||||
count: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
}
|
||||
setSelectedImages([]);
|
||||
refreshAll();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
failCount++;
|
||||
if (index === totalCount - 1) {
|
||||
toast.error(
|
||||
t("toast.error.batchCategorizeFailed", {
|
||||
count: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
setSelectedImages([]);
|
||||
refreshAll();
|
||||
}
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button className="flex gap-2">
|
||||
<TbCategoryPlus className="size-7 rounded-md p-1 text-secondary-foreground" />
|
||||
{isDesktop && t("button.categorizeImages")}
|
||||
</Button>
|
||||
</ClassificationSelectionDialog>
|
||||
)}
|
||||
<Button
|
||||
className="flex gap-2"
|
||||
onClick={() => setDeleteDialogOpen(selectedImages)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user