From 5d13925d2bb8e2f2c69f77ab4f271024672c4c6e Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 17 May 2025 17:11:19 -0500 Subject: [PATCH] Fixes (#18275) --- .cspell/frigate-dictionary.txt | 1 + docker/main/requirements-wheels.txt | 1 + frigate/camera/state.py | 3 ++- frigate/comms/webpush.py | 5 +++-- frigate/embeddings/__init__.py | 8 +++---- frigate/track/tracked_object.py | 3 ++- web/public/locales/en/views/faceLibrary.json | 3 ++- .../components/indicators/StepIndicator.tsx | 2 +- web/src/components/input/TextEntry.tsx | 21 ++++++++++++++++--- .../overlay/detail/FaceCreateWizardDialog.tsx | 2 ++ .../overlay/dialog/TextEntryDialog.tsx | 6 ++++++ web/src/pages/FaceLibrary.tsx | 4 +++- 12 files changed, 45 insertions(+), 14 deletions(-) diff --git a/.cspell/frigate-dictionary.txt b/.cspell/frigate-dictionary.txt index 77e4ede62..e9405c9f7 100644 --- a/.cspell/frigate-dictionary.txt +++ b/.cspell/frigate-dictionary.txt @@ -264,6 +264,7 @@ tensorrt tflite thresholded timelapse +titlecase tmpfs tobytes toggleable diff --git a/docker/main/requirements-wheels.txt b/docker/main/requirements-wheels.txt index 4ab7e03e6..2764eca43 100644 --- a/docker/main/requirements-wheels.txt +++ b/docker/main/requirements-wheels.txt @@ -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.* diff --git a/frigate/camera/state.py b/frigate/camera/state.py index e5a9ada9d..d863d1040 100644 --- a/frigate/camera/state.py +++ b/frigate/camera/state.py @@ -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, ) diff --git a/frigate/comms/webpush.py b/frigate/comms/webpush.py index 47b0bafc7..c825c0617 100644 --- a/frigate/comms/webpush.py +++ b/frigate/comms/webpush.py @@ -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 diff --git a/frigate/embeddings/__init__.py b/frigate/embeddings/__init__.py index 169548577..0c118879c 100644 --- a/frigate/embeddings/__init__.py +++ b/frigate/embeddings/__init__.py @@ -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}") diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index 141a7a3d5..49bc3dfac 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -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, ) diff --git a/web/public/locales/en/views/faceLibrary.json b/web/public/locales/en/views/faceLibrary.json index feffa796c..e734ca974 100644 --- a/web/public/locales/en/views/faceLibrary.json +++ b/web/public/locales/en/views/faceLibrary.json @@ -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", diff --git a/web/src/components/indicators/StepIndicator.tsx b/web/src/components/indicators/StepIndicator.tsx index cf183f90b..a6255fd0f 100644 --- a/web/src/components/indicators/StepIndicator.tsx +++ b/web/src/components/indicators/StepIndicator.tsx @@ -16,7 +16,7 @@ export default function StepIndicator({ return (
{steps.map((name, idx) => ( -
+
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>({ diff --git a/web/src/components/overlay/detail/FaceCreateWizardDialog.tsx b/web/src/components/overlay/detail/FaceCreateWizardDialog.tsx index 9fb3c9fad..d1ebf21a8 100644 --- a/web/src/components/overlay/detail/FaceCreateWizardDialog.tsx +++ b/web/src/components/overlay/detail/FaceCreateWizardDialog.tsx @@ -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")} >