mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-19 01:17:06 +03:00
add frontend speed filter
This commit is contained in:
parent
adaacd20a1
commit
fe231e2391
@ -25,6 +25,8 @@ class EventsQueryParams(BaseModel):
|
||||
favorites: Optional[int] = None
|
||||
min_score: Optional[float] = None
|
||||
max_score: Optional[float] = None
|
||||
min_speed: Optional[float] = None
|
||||
max_speed: Optional[float] = None
|
||||
is_submitted: Optional[int] = None
|
||||
min_length: Optional[float] = None
|
||||
max_length: Optional[float] = None
|
||||
@ -51,6 +53,8 @@ class EventsSearchQueryParams(BaseModel):
|
||||
timezone: Optional[str] = "utc"
|
||||
min_score: Optional[float] = None
|
||||
max_score: Optional[float] = None
|
||||
min_speed: Optional[float] = None
|
||||
max_speed: Optional[float] = None
|
||||
sort: Optional[str] = None
|
||||
|
||||
|
||||
|
||||
@ -92,6 +92,8 @@ def events(params: EventsQueryParams = Depends()):
|
||||
favorites = params.favorites
|
||||
min_score = params.min_score
|
||||
max_score = params.max_score
|
||||
min_speed = params.min_speed
|
||||
max_speed = params.max_speed
|
||||
is_submitted = params.is_submitted
|
||||
min_length = params.min_length
|
||||
max_length = params.max_length
|
||||
@ -226,6 +228,12 @@ def events(params: EventsQueryParams = Depends()):
|
||||
if min_score is not None:
|
||||
clauses.append((Event.data["score"] >= min_score))
|
||||
|
||||
if max_speed is not None:
|
||||
clauses.append((Event.data["average_estimated_speed"] <= max_speed))
|
||||
|
||||
if min_speed is not None:
|
||||
clauses.append((Event.data["average_estimated_speed"] >= min_speed))
|
||||
|
||||
if min_length is not None:
|
||||
clauses.append(((Event.end_time - Event.start_time) >= min_length))
|
||||
|
||||
@ -249,6 +257,10 @@ def events(params: EventsQueryParams = Depends()):
|
||||
order_by = Event.data["score"].asc()
|
||||
elif sort == "score_desc":
|
||||
order_by = Event.data["score"].desc()
|
||||
elif sort == "speed_asc":
|
||||
order_by = Event.data["average_estimated_speed"].asc()
|
||||
elif sort == "speed_desc":
|
||||
order_by = Event.data["average_estimated_speed"].desc()
|
||||
elif sort == "date_asc":
|
||||
order_by = Event.start_time.asc()
|
||||
elif sort == "date_desc":
|
||||
@ -375,6 +387,8 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends())
|
||||
before = params.before
|
||||
min_score = params.min_score
|
||||
max_score = params.max_score
|
||||
min_speed = params.min_speed
|
||||
max_speed = params.max_speed
|
||||
time_range = params.time_range
|
||||
has_clip = params.has_clip
|
||||
has_snapshot = params.has_snapshot
|
||||
@ -474,6 +488,16 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends())
|
||||
if max_score is not None:
|
||||
event_filters.append((Event.data["score"] <= max_score))
|
||||
|
||||
if min_speed is not None and max_speed is not None:
|
||||
event_filters.append(
|
||||
(Event.data["average_estimated_speed"].between(min_speed, max_speed))
|
||||
)
|
||||
else:
|
||||
if min_speed is not None:
|
||||
event_filters.append((Event.data["average_estimated_speed"] >= min_speed))
|
||||
if max_speed is not None:
|
||||
event_filters.append((Event.data["average_estimated_speed"] <= max_speed))
|
||||
|
||||
if time_range != DEFAULT_TIME_RANGE:
|
||||
tz_name = params.timezone
|
||||
hour_modifier, minute_modifier, _ = get_tz_modifiers(tz_name)
|
||||
@ -613,6 +637,10 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends())
|
||||
processed_events.sort(key=lambda x: x["score"])
|
||||
elif min_score is not None and max_score is not None and sort == "score_desc":
|
||||
processed_events.sort(key=lambda x: x["score"], reverse=True)
|
||||
elif min_speed is not None and max_speed is not None and sort == "speed_asc":
|
||||
processed_events.sort(key=lambda x: x["average_estimated_speed"])
|
||||
elif min_speed is not None and max_speed is not None and sort == "speed_desc":
|
||||
processed_events.sort(key=lambda x: x["average_estimated_speed"], reverse=True)
|
||||
elif sort == "date_asc":
|
||||
processed_events.sort(key=lambda x: x["start_time"])
|
||||
else:
|
||||
|
||||
@ -114,6 +114,9 @@ export default function SearchFilterGroup({
|
||||
if (filter?.min_score || filter?.max_score) {
|
||||
sortTypes.push("score_desc", "score_asc");
|
||||
}
|
||||
if (filter?.min_speed || filter?.max_speed) {
|
||||
sortTypes.push("speed_desc", "speed_asc");
|
||||
}
|
||||
if (filter?.event_id || filter?.query) {
|
||||
sortTypes.push("relevance");
|
||||
}
|
||||
@ -496,6 +499,8 @@ export function SortTypeContent({
|
||||
date_desc: "Date (Descending)",
|
||||
score_asc: "Object Score (Ascending)",
|
||||
score_desc: "Object Score (Descending)",
|
||||
speed_asc: "Estimated Speed (Ascending)",
|
||||
speed_desc: "Estimated Speed (Descending)",
|
||||
relevance: "Relevance",
|
||||
};
|
||||
|
||||
|
||||
@ -216,11 +216,14 @@ export default function InputWithTags({
|
||||
type == "after" ||
|
||||
type == "time_range" ||
|
||||
type == "min_score" ||
|
||||
type == "max_score"
|
||||
type == "max_score" ||
|
||||
type == "min_speed" ||
|
||||
type == "max_speed"
|
||||
) {
|
||||
const newFilters = { ...filters };
|
||||
let timestamp = 0;
|
||||
let score = 0;
|
||||
let speed = 0;
|
||||
|
||||
switch (type) {
|
||||
case "before":
|
||||
@ -294,6 +297,40 @@ export default function InputWithTags({
|
||||
newFilters[type] = score / 100;
|
||||
}
|
||||
break;
|
||||
case "min_speed":
|
||||
case "max_speed":
|
||||
speed = parseFloat(value);
|
||||
if (score >= 0) {
|
||||
// Check for conflicts between min_speed and max_speed
|
||||
if (
|
||||
type === "min_speed" &&
|
||||
filters.max_speed !== undefined &&
|
||||
speed > filters.max_speed
|
||||
) {
|
||||
toast.error(
|
||||
"The 'min_speed' must be less than or equal to the 'max_speed'.",
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
type === "max_speed" &&
|
||||
filters.min_speed !== undefined &&
|
||||
speed < filters.min_speed
|
||||
) {
|
||||
toast.error(
|
||||
"The 'max_speed' must be greater than or equal to the 'min_speed'.",
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
newFilters[type] = speed;
|
||||
}
|
||||
break;
|
||||
case "time_range":
|
||||
newFilters[type] = value;
|
||||
break;
|
||||
@ -369,6 +406,10 @@ export default function InputWithTags({
|
||||
}`;
|
||||
} else if (filterType === "min_score" || filterType === "max_score") {
|
||||
return Math.round(Number(filterValues) * 100).toString() + "%";
|
||||
} else if (filterType === "min_speed" || filterType === "max_speed") {
|
||||
return (
|
||||
filterValues + (config?.ui.unit_system == "metric" ? " kph" : " mph")
|
||||
);
|
||||
} else if (
|
||||
filterType === "has_clip" ||
|
||||
filterType === "has_snapshot" ||
|
||||
@ -397,7 +438,11 @@ export default function InputWithTags({
|
||||
((filterType === "min_score" || filterType === "max_score") &&
|
||||
!isNaN(Number(trimmedValue)) &&
|
||||
Number(trimmedValue) >= 50 &&
|
||||
Number(trimmedValue) <= 100)
|
||||
Number(trimmedValue) <= 100) ||
|
||||
((filterType === "min_speed" || filterType === "max_speed") &&
|
||||
!isNaN(Number(trimmedValue)) &&
|
||||
Number(trimmedValue) >= 1 &&
|
||||
Number(trimmedValue) <= 150)
|
||||
) {
|
||||
createFilter(
|
||||
filterType,
|
||||
|
||||
@ -71,9 +71,11 @@ export default function SearchFilterDialog({
|
||||
currentFilter &&
|
||||
(currentFilter.time_range ||
|
||||
(currentFilter.min_score ?? 0) > 0.5 ||
|
||||
(currentFilter.min_speed ?? 1) > 1 ||
|
||||
(currentFilter.has_snapshot ?? 0) === 1 ||
|
||||
(currentFilter.has_clip ?? 0) === 1 ||
|
||||
(currentFilter.max_score ?? 1) < 1 ||
|
||||
(currentFilter.max_speed ?? 150) < 150 ||
|
||||
(currentFilter.zones?.length ?? 0) > 0 ||
|
||||
(currentFilter.sub_labels?.length ?? 0) > 0),
|
||||
[currentFilter],
|
||||
@ -124,6 +126,14 @@ export default function SearchFilterDialog({
|
||||
setCurrentFilter({ ...currentFilter, min_score: min, max_score: max })
|
||||
}
|
||||
/>
|
||||
<SpeedFilterContent
|
||||
config={config}
|
||||
minSpeed={currentFilter.min_speed}
|
||||
maxSpeed={currentFilter.max_speed}
|
||||
setSpeedRange={(min, max) =>
|
||||
setCurrentFilter({ ...currentFilter, min_speed: min, max_speed: max })
|
||||
}
|
||||
/>
|
||||
<SnapshotClipFilterContent
|
||||
config={config}
|
||||
hasSnapshot={
|
||||
@ -178,6 +188,8 @@ export default function SearchFilterDialog({
|
||||
search_type: undefined,
|
||||
min_score: undefined,
|
||||
max_score: undefined,
|
||||
min_speed: undefined,
|
||||
max_speed: undefined,
|
||||
has_snapshot: undefined,
|
||||
has_clip: undefined,
|
||||
}));
|
||||
@ -521,6 +533,62 @@ export function ScoreFilterContent({
|
||||
);
|
||||
}
|
||||
|
||||
type SpeedFilterContentProps = {
|
||||
config?: FrigateConfig;
|
||||
minSpeed: number | undefined;
|
||||
maxSpeed: number | undefined;
|
||||
setSpeedRange: (min: number | undefined, max: number | undefined) => void;
|
||||
};
|
||||
export function SpeedFilterContent({
|
||||
config,
|
||||
minSpeed,
|
||||
maxSpeed,
|
||||
setSpeedRange,
|
||||
}: SpeedFilterContentProps) {
|
||||
return (
|
||||
<div className="overflow-x-hidden">
|
||||
<DropdownMenuSeparator className="mb-3" />
|
||||
<div className="mb-3 text-lg">
|
||||
Estimated Speed ({config?.ui.unit_system == "metric" ? "kph" : "mph"})
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Input
|
||||
className="w-14 text-center"
|
||||
inputMode="numeric"
|
||||
value={minSpeed ?? 1}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
if (value) {
|
||||
setSpeedRange(parseInt(value), maxSpeed ?? 1.0);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DualThumbSlider
|
||||
className="mx-2 w-full"
|
||||
min={1}
|
||||
max={150}
|
||||
step={1}
|
||||
value={[minSpeed ?? 1, maxSpeed ?? 150]}
|
||||
onValueChange={([min, max]) => setSpeedRange(min, max)}
|
||||
/>
|
||||
<Input
|
||||
className="w-14 text-center"
|
||||
inputMode="numeric"
|
||||
value={maxSpeed ?? 150}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
if (value) {
|
||||
setSpeedRange(minSpeed ?? 1, parseInt(value));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type SnapshotClipContentProps = {
|
||||
config?: FrigateConfig;
|
||||
hasSnapshot: boolean | undefined;
|
||||
|
||||
@ -112,6 +112,8 @@ export default function Explore() {
|
||||
search_type: searchSearchParams["search_type"],
|
||||
min_score: searchSearchParams["min_score"],
|
||||
max_score: searchSearchParams["max_score"],
|
||||
min_speed: searchSearchParams["min_speed"],
|
||||
max_speed: searchSearchParams["max_speed"],
|
||||
has_snapshot: searchSearchParams["has_snapshot"],
|
||||
is_submitted: searchSearchParams["is_submitted"],
|
||||
has_clip: searchSearchParams["has_clip"],
|
||||
@ -145,6 +147,8 @@ export default function Explore() {
|
||||
search_type: searchSearchParams["search_type"],
|
||||
min_score: searchSearchParams["min_score"],
|
||||
max_score: searchSearchParams["max_score"],
|
||||
min_speed: searchSearchParams["min_speed"],
|
||||
max_speed: searchSearchParams["max_speed"],
|
||||
has_snapshot: searchSearchParams["has_snapshot"],
|
||||
is_submitted: searchSearchParams["is_submitted"],
|
||||
has_clip: searchSearchParams["has_clip"],
|
||||
|
||||
@ -70,6 +70,8 @@ export type SearchFilter = {
|
||||
after?: number;
|
||||
min_score?: number;
|
||||
max_score?: number;
|
||||
min_speed?: number;
|
||||
max_speed?: number;
|
||||
has_snapshot?: number;
|
||||
has_clip?: number;
|
||||
is_submitted?: number;
|
||||
@ -91,6 +93,8 @@ export type SearchQueryParams = {
|
||||
after?: string;
|
||||
min_score?: number;
|
||||
max_score?: number;
|
||||
min_speed?: number;
|
||||
max_speed?: number;
|
||||
search_type?: string;
|
||||
limit?: number;
|
||||
in_progress?: number;
|
||||
|
||||
@ -158,6 +158,8 @@ export default function SearchView({
|
||||
after: [formatDateToLocaleString(-5)],
|
||||
min_score: ["50"],
|
||||
max_score: ["100"],
|
||||
min_speed: ["1"],
|
||||
max_speed: ["150"],
|
||||
has_clip: ["yes", "no"],
|
||||
has_snapshot: ["yes", "no"],
|
||||
...(config?.plus?.enabled &&
|
||||
|
||||
Loading…
Reference in New Issue
Block a user