From 4524d9440c7e671d0039a2833789b5f8e1630120 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 12 Dec 2023 19:48:52 -0700 Subject: [PATCH] Add initial implementation of history view in new webui framework (#8895) * Add support for review grid * Cleanup reloading on focus * Adjust timeline api to include metadata and before * Be more efficient about getting info * Adjust to new data format * Cleanup types * Cleanup text * Transition to history * Cleanup * remove old web implementations * Cleanup --- frigate/events/maintainer.py | 6 + frigate/http.py | 128 +++++++++- frigate/timeline.py | 56 +++-- web-new/package-lock.json | 32 +++ web-new/package.json | 1 + web-new/src/components/card/HistoryCard.tsx | 144 +++++++++++ .../player/PreviewThumbnailPlayer.tsx | 99 ++++++++ web-new/src/components/ui/scroll-area.tsx | 46 ++++ web-new/src/pages/History.tsx | 162 ++++++++++++- web-new/src/types/frigateConfig.ts | 20 +- web-new/src/types/history.ts | 39 +++ web-new/src/utils/dateUtil.ts | 229 ++++++++++++++++++ web-new/vite.config.ts | 3 + 13 files changed, 932 insertions(+), 33 deletions(-) create mode 100644 web-new/src/components/card/HistoryCard.tsx create mode 100644 web-new/src/components/player/PreviewThumbnailPlayer.tsx create mode 100644 web-new/src/components/ui/scroll-area.tsx create mode 100644 web-new/src/types/history.ts create mode 100644 web-new/src/utils/dateUtil.ts diff --git a/frigate/events/maintainer.py b/frigate/events/maintainer.py index db8341656..19bb44ef4 100644 --- a/frigate/events/maintainer.py +++ b/frigate/events/maintainer.py @@ -45,6 +45,12 @@ def should_update_state(prev_event: Event, current_event: Event) -> bool: if prev_event["attributes"] != current_event["attributes"]: return True + if prev_event["sub_label"] != current_event["sub_label"]: + return True + + if len(prev_event["current_zones"]) < len(current_event["current_zones"]): + return True + return False diff --git a/frigate/http.py b/frigate/http.py index c43feee9f..6b7ff8c3a 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -611,6 +611,78 @@ def timeline(): return jsonify([t for t in timeline]) +@bp.route("/timeline/hourly") +def hourly_timeline(): + """Get hourly summary for timeline.""" + camera = request.args.get("camera", "all") + before = request.args.get("before", type=float) + limit = request.args.get("limit", 200) + tz_name = request.args.get("timezone", default="utc", type=str) + _, minute_modifier, _ = get_tz_modifiers(tz_name) + + clauses = [] + + if camera != "all": + clauses.append((Timeline.camera == camera)) + + if before: + clauses.append((Timeline.timestamp < before)) + + if len(clauses) == 0: + clauses.append((True)) + + timeline = ( + Timeline.select( + Timeline.camera, + Timeline.timestamp, + Timeline.data, + Timeline.class_type, + Timeline.source_id, + Timeline.source, + ) + .where(reduce(operator.and_, clauses)) + .order_by(Timeline.timestamp.desc()) + .limit(limit) + .dicts() + .iterator() + ) + + count = 0 + start = 0 + end = 0 + hours: dict[str, list[dict[str, any]]] = {} + + for t in timeline: + if count == 0: + start = t["timestamp"] + else: + end = t["timestamp"] + + count += 1 + + hour = ( + datetime.fromtimestamp(t["timestamp"]).replace( + minute=0, second=0, microsecond=0 + ) + + timedelta( + minutes=int(minute_modifier.split(" ")[0]), + ) + ).timestamp() + if hour not in hours: + hours[hour] = [t] + else: + hours[hour].insert(0, t) + + return jsonify( + { + "start": start, + "end": end, + "count": count, + "hours": hours, + } + ) + + @bp.route("//