mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Improve recognized license plate filter (#19491)
* Fetch all license plates outside of filter component If the swr call took a long time, the entire select component may not display. This change moves the fetch to the parent component (like sub labels). * add loading indicator * improve query
This commit is contained in:
		
							parent
							
								
									d1be614a10
								
							
						
					
					
						commit
						2cde58037d
					
				@ -20,7 +20,7 @@ from fastapi.encoders import jsonable_encoder
 | 
			
		||||
from fastapi.params import Depends
 | 
			
		||||
from fastapi.responses import JSONResponse, PlainTextResponse, StreamingResponse
 | 
			
		||||
from markupsafe import escape
 | 
			
		||||
from peewee import operator
 | 
			
		||||
from peewee import SQL, operator
 | 
			
		||||
from pydantic import ValidationError
 | 
			
		||||
 | 
			
		||||
from frigate.api.auth import require_role
 | 
			
		||||
@ -685,7 +685,14 @@ def plusModels(request: Request, filterByCurrentModelDetector: bool = False):
 | 
			
		||||
@router.get("/recognized_license_plates")
 | 
			
		||||
def get_recognized_license_plates(split_joined: Optional[int] = None):
 | 
			
		||||
    try:
 | 
			
		||||
        events = Event.select(Event.data).distinct()
 | 
			
		||||
        query = (
 | 
			
		||||
            Event.select(
 | 
			
		||||
                SQL("json_extract(data, '$.recognized_license_plate') AS plate")
 | 
			
		||||
            )
 | 
			
		||||
            .where(SQL("json_extract(data, '$.recognized_license_plate') IS NOT NULL"))
 | 
			
		||||
            .distinct()
 | 
			
		||||
        )
 | 
			
		||||
        recognized_license_plates = [row[0] for row in query.tuples()]
 | 
			
		||||
    except Exception:
 | 
			
		||||
        return JSONResponse(
 | 
			
		||||
            content=(
 | 
			
		||||
@ -694,14 +701,6 @@ def get_recognized_license_plates(split_joined: Optional[int] = None):
 | 
			
		||||
            status_code=404,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    recognized_license_plates = []
 | 
			
		||||
    for e in events:
 | 
			
		||||
        if e.data is not None and "recognized_license_plate" in e.data:
 | 
			
		||||
            recognized_license_plates.append(e.data["recognized_license_plate"])
 | 
			
		||||
 | 
			
		||||
    while None in recognized_license_plates:
 | 
			
		||||
        recognized_license_plates.remove(None)
 | 
			
		||||
 | 
			
		||||
    if split_joined:
 | 
			
		||||
        original_recognized_license_plates = recognized_license_plates.copy()
 | 
			
		||||
        for recognized_license_plate in original_recognized_license_plates:
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,7 @@ import {
 | 
			
		||||
  CommandList,
 | 
			
		||||
} from "@/components/ui/command";
 | 
			
		||||
import { LuCheck } from "react-icons/lu";
 | 
			
		||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
 | 
			
		||||
 | 
			
		||||
type SearchFilterDialogProps = {
 | 
			
		||||
  config?: FrigateConfig;
 | 
			
		||||
@ -64,6 +65,9 @@ export default function SearchFilterDialog({
 | 
			
		||||
  const { t } = useTranslation(["components/filter"]);
 | 
			
		||||
  const [currentFilter, setCurrentFilter] = useState(filter ?? {});
 | 
			
		||||
  const { data: allSubLabels } = useSWR(["sub_labels", { split_joined: 1 }]);
 | 
			
		||||
  const { data: allRecognizedLicensePlates } = useSWR<string[]>(
 | 
			
		||||
    "recognized_license_plates",
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (filter) {
 | 
			
		||||
@ -130,6 +134,7 @@ export default function SearchFilterDialog({
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      <RecognizedLicensePlatesFilterContent
 | 
			
		||||
        allRecognizedLicensePlates={allRecognizedLicensePlates}
 | 
			
		||||
        recognizedLicensePlates={currentFilter.recognized_license_plate}
 | 
			
		||||
        setRecognizedLicensePlates={(plate) =>
 | 
			
		||||
          setCurrentFilter({
 | 
			
		||||
@ -875,6 +880,7 @@ export function SnapshotClipFilterContent({
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RecognizedLicensePlatesFilterContentProps = {
 | 
			
		||||
  allRecognizedLicensePlates: string[] | undefined;
 | 
			
		||||
  recognizedLicensePlates: string[] | undefined;
 | 
			
		||||
  setRecognizedLicensePlates: (
 | 
			
		||||
    recognizedLicensePlates: string[] | undefined,
 | 
			
		||||
@ -882,18 +888,12 @@ type RecognizedLicensePlatesFilterContentProps = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function RecognizedLicensePlatesFilterContent({
 | 
			
		||||
  allRecognizedLicensePlates,
 | 
			
		||||
  recognizedLicensePlates,
 | 
			
		||||
  setRecognizedLicensePlates,
 | 
			
		||||
}: RecognizedLicensePlatesFilterContentProps) {
 | 
			
		||||
  const { t } = useTranslation(["components/filter"]);
 | 
			
		||||
 | 
			
		||||
  const { data: allRecognizedLicensePlates, error } = useSWR<string[]>(
 | 
			
		||||
    "recognized_license_plates",
 | 
			
		||||
    {
 | 
			
		||||
      revalidateOnFocus: false,
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const [selectedRecognizedLicensePlates, setSelectedRecognizedLicensePlates] =
 | 
			
		||||
    useState<string[]>(recognizedLicensePlates || []);
 | 
			
		||||
  const [inputValue, setInputValue] = useState("");
 | 
			
		||||
@ -923,7 +923,7 @@ export function RecognizedLicensePlatesFilterContent({
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (!allRecognizedLicensePlates || allRecognizedLicensePlates.length === 0) {
 | 
			
		||||
  if (allRecognizedLicensePlates && allRecognizedLicensePlates.length === 0) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -947,15 +947,12 @@ export function RecognizedLicensePlatesFilterContent({
 | 
			
		||||
    <div className="overflow-x-hidden">
 | 
			
		||||
      <DropdownMenuSeparator className="mb-3" />
 | 
			
		||||
      <div className="mb-3 text-lg">{t("recognizedLicensePlates.title")}</div>
 | 
			
		||||
      {error ? (
 | 
			
		||||
        <p className="text-sm text-red-500">
 | 
			
		||||
          {t("recognizedLicensePlates.loadFailed")}
 | 
			
		||||
        </p>
 | 
			
		||||
      ) : !allRecognizedLicensePlates ? (
 | 
			
		||||
        <p className="text-sm text-muted-foreground">
 | 
			
		||||
          {t("recognizedLicensePlates.loading")}
 | 
			
		||||
        </p>
 | 
			
		||||
      ) : (
 | 
			
		||||
      {allRecognizedLicensePlates == undefined ? (
 | 
			
		||||
        <div className="flex flex-col items-center justify-center text-sm text-muted-foreground">
 | 
			
		||||
          <ActivityIndicator className="mb-3 mr-2 size-5" />
 | 
			
		||||
          <p>{t("recognizedLicensePlates.loading")}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
      ) : allRecognizedLicensePlates.length == 0 ? null : (
 | 
			
		||||
        <>
 | 
			
		||||
          <Command
 | 
			
		||||
            className="border border-input bg-background"
 | 
			
		||||
@ -1010,11 +1007,11 @@ export function RecognizedLicensePlatesFilterContent({
 | 
			
		||||
              ))}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
          <p className="mt-1 text-sm text-muted-foreground">
 | 
			
		||||
            {t("recognizedLicensePlates.selectPlatesFromList")}
 | 
			
		||||
          </p>
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user