diff --git a/frigate/http.py b/frigate/http.py index 194282425..e24d37b0b 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -2,18 +2,14 @@ import base64 from collections import OrderedDict from datetime import datetime, timedelta import copy -import json -import glob import logging import os -import re import subprocess as sp import time from functools import reduce from pathlib import Path import cv2 -from flask.helpers import send_file import numpy as np from flask import ( @@ -26,13 +22,12 @@ from flask import ( request, ) -from peewee import SqliteDatabase, operator, fn, DoesNotExist, Value +from peewee import SqliteDatabase, operator, fn, DoesNotExist from playhouse.shortcuts import model_to_dict from frigate.const import CLIPS_DIR, PLUS_ENV_VAR from frigate.models import Event, Recordings from frigate.stats import stats_snapshot -from frigate.util import calculate_region from frigate.version import VERSION logger = logging.getLogger(__name__) @@ -251,6 +246,20 @@ def set_sub_label(id): ) +@bp.route("/sub_labels") +def get_sub_labels(): + try: + events = Event.select(Event.sub_label).distinct() + except Exception as e: + return jsonify( + {"success": False, "message": f"Failed to get sub_labels: {e}"}, "404" + ) + + sub_labels = [e.sub_label for e in events] + sub_labels.remove(None) + return jsonify(sub_labels) + + @bp.route("/events/", methods=("DELETE",)) def delete_event(id): try: @@ -480,6 +489,7 @@ def events(): limit = request.args.get("limit", 100) camera = request.args.get("camera", "all") label = request.args.get("label", "all") + sub_label = request.args.get("sub_label", "all") zone = request.args.get("zone", "all") after = request.args.get("after", type=float) before = request.args.get("before", type=float) @@ -511,6 +521,9 @@ def events(): if label != "all": clauses.append((Event.label == label)) + if sub_label != "all": + clauses.append((Event.sub_label == sub_label)) + if zone != "all": clauses.append((Event.zones.cast("text") % f'*"{zone}"*')) diff --git a/web/config/handlers.js b/web/config/handlers.js index 706545165..59a1649d1 100644 --- a/web/config/handlers.js +++ b/web/config/handlers.js @@ -72,4 +72,13 @@ export const handlers = [ ) ); }), + rest.get(`${API_HOST}api/sub_labels`, (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([ + 'one', + 'two', + ]) + ); + }), ]; diff --git a/web/src/routes/Events.jsx b/web/src/routes/Events.jsx index 3be927f57..729087101 100644 --- a/web/src/routes/Events.jsx +++ b/web/src/routes/Events.jsx @@ -44,6 +44,7 @@ export default function Events({ path, ...props }) { camera: props.camera ?? 'all', label: props.label ?? 'all', zone: props.zone ?? 'all', + sub_label: props.sub_label ?? 'all', }); const [state, setState] = useState({ showDownloadMenu: false, @@ -86,6 +87,8 @@ export default function Events({ path, ...props }) { const { data: config } = useSWR('config'); + const { data: allSubLabels } = useSWR('sub_labels') + const filterValues = useMemo( () => ({ cameras: Object.keys(config?.cameras || {}), @@ -101,8 +104,9 @@ export default function Events({ path, ...props }) { return memo; }, config?.objects?.track || []) .filter((value, i, self) => self.indexOf(value) === i), + sub_labels: Object.values(allSubLabels || []), }), - [config] + [config, allSubLabels] ); const onSave = async (e, eventId, save) => { @@ -240,11 +244,11 @@ export default function Events({ path, ...props }) { Events
+ { + filterValues.sub_labels.length > 0 && ( + + )}