mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-06-04 01:16:52 +02:00
Fixes (#18275)
This commit is contained in:
parent
ebae6cb1ed
commit
5d13925d2b
@ -264,6 +264,7 @@ tensorrt
|
||||
tflite
|
||||
thresholded
|
||||
timelapse
|
||||
titlecase
|
||||
tmpfs
|
||||
tobytes
|
||||
toggleable
|
||||
|
@ -31,6 +31,7 @@ norfair == 2.2.*
|
||||
setproctitle == 1.3.*
|
||||
ws4py == 0.5.*
|
||||
unidecode == 1.3.*
|
||||
titlecase == 2.4.*
|
||||
# Image Manipulation
|
||||
numpy == 1.26.*
|
||||
opencv-python-headless == 4.11.0.*
|
||||
|
@ -172,6 +172,7 @@ class CameraState:
|
||||
# draw any attributes
|
||||
for attribute in obj["current_attributes"]:
|
||||
box = attribute["box"]
|
||||
box_area = int((box[2] - box[0]) * (box[3] - box[1]))
|
||||
draw_box_with_label(
|
||||
frame_copy,
|
||||
box[0],
|
||||
@ -179,7 +180,7 @@ class CameraState:
|
||||
box[2],
|
||||
box[3],
|
||||
attribute["label"],
|
||||
f"{attribute['score']:.0%}",
|
||||
f"{attribute['score']:.0%} {str(box_area)}",
|
||||
thickness=thickness,
|
||||
color=color,
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ from typing import Any, Callable
|
||||
|
||||
from py_vapid import Vapid01
|
||||
from pywebpush import WebPusher
|
||||
from titlecase import titlecase
|
||||
|
||||
from frigate.comms.base_communicator import Communicator
|
||||
from frigate.comms.config_updater import ConfigSubscriber
|
||||
@ -325,8 +326,8 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
||||
|
||||
sorted_objects.update(payload["after"]["data"]["sub_labels"])
|
||||
|
||||
title = f"{', '.join(sorted_objects).replace('_', ' ').title()}{' was' if state == 'end' else ''} detected in {', '.join(payload['after']['data']['zones']).replace('_', ' ').title()}"
|
||||
message = f"Detected on {camera.replace('_', ' ').title()}"
|
||||
title = f"{titlecase(', '.join(sorted_objects).replace('_', ' '))}{' was' if state == 'end' else ''} detected in {titlecase(', '.join(payload['after']['data']['zones']).replace('_', ' '))}"
|
||||
message = f"Detected on {titlecase(camera.replace('_', ' '))}"
|
||||
image = f"{payload['after']['thumb_path'].replace('/media/frigate', '')}"
|
||||
|
||||
# if event is ongoing open to live view otherwise open to recordings view
|
||||
|
@ -5,12 +5,12 @@ import json
|
||||
import logging
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import threading
|
||||
from types import FrameType
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
import regex
|
||||
from pathvalidate import ValidationError, sanitize_filename
|
||||
from setproctitle import setproctitle
|
||||
|
||||
@ -243,7 +243,7 @@ class EmbeddingsContext:
|
||||
)
|
||||
|
||||
def rename_face(self, old_name: str, new_name: str) -> None:
|
||||
valid_name_pattern = r"^[a-zA-Z0-9\s_-]{1,50}$"
|
||||
valid_name_pattern = r"^[\p{L}\p{N}\s'_-]{1,50}$"
|
||||
|
||||
try:
|
||||
sanitized_old_name = sanitize_filename(old_name, replacement_text="_")
|
||||
@ -251,9 +251,9 @@ class EmbeddingsContext:
|
||||
except ValidationError as e:
|
||||
raise ValueError(f"Invalid face name: {str(e)}")
|
||||
|
||||
if not re.match(valid_name_pattern, old_name):
|
||||
if not regex.match(valid_name_pattern, old_name):
|
||||
raise ValueError(f"Invalid old face name: {old_name}")
|
||||
if not re.match(valid_name_pattern, new_name):
|
||||
if not regex.match(valid_name_pattern, new_name):
|
||||
raise ValueError(f"Invalid new face name: {new_name}")
|
||||
if sanitized_old_name != old_name:
|
||||
raise ValueError(f"Old face name contains invalid characters: {old_name}")
|
||||
|
@ -474,6 +474,7 @@ class TrackedObject:
|
||||
# draw any attributes
|
||||
for attribute in self.thumbnail_data["attributes"]:
|
||||
box = attribute["box"]
|
||||
box_area = int((box[2] - box[0]) * (box[3] - box[1]))
|
||||
draw_box_with_label(
|
||||
best_frame,
|
||||
box[0],
|
||||
@ -481,7 +482,7 @@ class TrackedObject:
|
||||
box[2],
|
||||
box[3],
|
||||
attribute["label"],
|
||||
f"{attribute['score']:.0%}",
|
||||
f"{attribute['score']:.0%} {str(box_area)}",
|
||||
thickness=thickness,
|
||||
color=color,
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"description": {
|
||||
"addFace": "Walk through adding a new collection to the Face Library.",
|
||||
"placeholder": "Enter a name for this collection"
|
||||
"placeholder": "Enter a name for this collection",
|
||||
"invalidName": "Invalid name. Names can only include letters, numbers, spaces, apostrophes, underscores, and hyphens."
|
||||
},
|
||||
"details": {
|
||||
"person": "Person",
|
||||
|
@ -16,7 +16,7 @@ export default function StepIndicator({
|
||||
return (
|
||||
<div className="flex flex-row justify-evenly">
|
||||
{steps.map((name, idx) => (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div key={idx} className="flex flex-col items-center gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-16 items-center justify-center rounded-full",
|
||||
|
@ -18,18 +18,33 @@ type TextEntryProps = {
|
||||
allowEmpty?: boolean;
|
||||
onSave: (text: string) => void;
|
||||
children?: React.ReactNode;
|
||||
regexPattern?: RegExp;
|
||||
regexErrorMessage?: string;
|
||||
};
|
||||
|
||||
export default function TextEntry({
|
||||
defaultValue = "",
|
||||
placeholder,
|
||||
allowEmpty = false,
|
||||
onSave,
|
||||
children,
|
||||
regexPattern,
|
||||
regexErrorMessage = "Input does not match the required format",
|
||||
}: TextEntryProps) {
|
||||
const formSchema = z.object({
|
||||
text: allowEmpty
|
||||
? z.string().optional()
|
||||
: z.string().min(1, "Field is required"),
|
||||
text: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine(
|
||||
(val) => {
|
||||
if (!allowEmpty && !val) return false;
|
||||
if (val && regexPattern) return regexPattern.test(val);
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: regexPattern ? regexErrorMessage : "Field is required",
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
|
@ -119,6 +119,8 @@ export default function CreateFaceWizardDialog({
|
||||
setName(name);
|
||||
setStep(1);
|
||||
}}
|
||||
regexPattern={/^[\p{L}\p{N}\s'_-]{1,50}$/u}
|
||||
regexErrorMessage={t("description.invalidName")}
|
||||
>
|
||||
<div className="flex justify-end py-2">
|
||||
<Button variant="select" type="submit">
|
||||
|
@ -20,6 +20,8 @@ type TextEntryDialogProps = {
|
||||
onSave: (text: string) => void;
|
||||
defaultValue?: string;
|
||||
allowEmpty?: boolean;
|
||||
regexPattern?: RegExp;
|
||||
regexErrorMessage?: string;
|
||||
};
|
||||
|
||||
export default function TextEntryDialog({
|
||||
@ -30,6 +32,8 @@ export default function TextEntryDialog({
|
||||
onSave,
|
||||
defaultValue = "",
|
||||
allowEmpty = false,
|
||||
regexPattern,
|
||||
regexErrorMessage,
|
||||
}: TextEntryDialogProps) {
|
||||
const { t } = useTranslation("common");
|
||||
|
||||
@ -44,6 +48,8 @@ export default function TextEntryDialog({
|
||||
defaultValue={defaultValue}
|
||||
allowEmpty={allowEmpty}
|
||||
onSave={onSave}
|
||||
regexPattern={regexPattern}
|
||||
regexErrorMessage={regexErrorMessage}
|
||||
>
|
||||
<DialogFooter className={cn("pt-4", isMobile && "gap-2")}>
|
||||
<Button type="button" onClick={() => setOpen(false)}>
|
||||
|
@ -499,6 +499,8 @@ function LibrarySelector({
|
||||
setRenameFace(null);
|
||||
}}
|
||||
defaultValue={renameFace || ""}
|
||||
regexPattern={/^[\p{L}\p{N}\s'_-]{1,50}$/u}
|
||||
regexErrorMessage={t("description.invalidName")}
|
||||
/>
|
||||
|
||||
<DropdownMenu>
|
||||
@ -538,7 +540,7 @@ function LibrarySelector({
|
||||
className="group flex items-center justify-between"
|
||||
>
|
||||
<div
|
||||
className="flex-grow cursor-pointer smart-capitalize"
|
||||
className="flex-grow cursor-pointer"
|
||||
onClick={() => setPageToggle(face)}
|
||||
>
|
||||
{face}
|
||||
|
Loading…
Reference in New Issue
Block a user