mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 10:45:21 +03:00
Move to events package
This commit is contained in:
parent
6d0c2ec5c8
commit
27f01d1dca
@ -295,3 +295,12 @@ Get ffprobe output for camera feed paths.
|
||||
### `GET /api/<camera_name>/ptz/info`
|
||||
|
||||
Get PTZ info for the camera.
|
||||
|
||||
### `POST /api/events/manual/<camera_name>/<label>/create`
|
||||
|
||||
Create a manual API with a given `label` (ex: doorbell press) to capture a specific event besides an object being detected.
|
||||
NOTE: This call will return the unique ID for the event which will be required to `end` the event.
|
||||
|
||||
### `POST /api/events/manual/<event_id>/end`
|
||||
|
||||
End a specific manual event.
|
||||
|
||||
@ -28,7 +28,8 @@ from frigate.const import (
|
||||
RECORD_DIR,
|
||||
)
|
||||
from frigate.object_detection import ObjectDetectProcess
|
||||
from frigate.events import EventCleanup, EventProcessor
|
||||
from frigate.events.cleanup import EventCleanup
|
||||
from frigate.events.maintainer import EventProcessor
|
||||
from frigate.http import create_app
|
||||
from frigate.log import log_process, root_configurer
|
||||
from frigate.models import Event, Recordings, Timeline
|
||||
|
||||
175
frigate/events/cleanup.py
Normal file
175
frigate/events/cleanup.py
Normal file
@ -0,0 +1,175 @@
|
||||
"""Cleanup events based on configured retention."""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from peewee import fn
|
||||
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import CLIPS_DIR
|
||||
from frigate.models import Event
|
||||
|
||||
from multiprocessing.synchronize import Event as MpEvent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class EventCleanup(threading.Thread):
|
||||
def __init__(self, config: FrigateConfig, stop_event: MpEvent):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = "event_cleanup"
|
||||
self.config = config
|
||||
self.stop_event = stop_event
|
||||
self.camera_keys = list(self.config.cameras.keys())
|
||||
|
||||
def expire(self, media_type: str) -> None:
|
||||
# TODO: Refactor media_type to enum
|
||||
## Expire events from unlisted cameras based on the global config
|
||||
if media_type == "clips":
|
||||
retain_config = self.config.record.events.retain
|
||||
file_extension = "mp4"
|
||||
update_params = {"has_clip": False}
|
||||
else:
|
||||
retain_config = self.config.snapshots.retain
|
||||
file_extension = "jpg"
|
||||
update_params = {"has_snapshot": False}
|
||||
|
||||
distinct_labels = (
|
||||
Event.select(Event.label)
|
||||
.where(Event.camera.not_in(self.camera_keys))
|
||||
.distinct()
|
||||
)
|
||||
|
||||
# loop over object types in db
|
||||
for l in distinct_labels:
|
||||
# get expiration time for this label
|
||||
expire_days = retain_config.objects.get(l.label, retain_config.default)
|
||||
expire_after = (
|
||||
datetime.datetime.now() - datetime.timedelta(days=expire_days)
|
||||
).timestamp()
|
||||
# grab all events after specific time
|
||||
expired_events = Event.select().where(
|
||||
Event.camera.not_in(self.camera_keys),
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
# delete the media from disk
|
||||
for event in expired_events:
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
if file_extension == "jpg":
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
|
||||
# update the clips attribute for the db entry
|
||||
update_query = Event.update(update_params).where(
|
||||
Event.camera.not_in(self.camera_keys),
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
update_query.execute()
|
||||
|
||||
## Expire events from cameras based on the camera config
|
||||
for name, camera in self.config.cameras.items():
|
||||
if media_type == "clips":
|
||||
retain_config = camera.record.events.retain
|
||||
else:
|
||||
retain_config = camera.snapshots.retain
|
||||
# get distinct objects in database for this camera
|
||||
distinct_labels = (
|
||||
Event.select(Event.label).where(Event.camera == name).distinct()
|
||||
)
|
||||
|
||||
# loop over object types in db
|
||||
for l in distinct_labels:
|
||||
# get expiration time for this label
|
||||
expire_days = retain_config.objects.get(l.label, retain_config.default)
|
||||
expire_after = (
|
||||
datetime.datetime.now() - datetime.timedelta(days=expire_days)
|
||||
).timestamp()
|
||||
# grab all events after specific time
|
||||
expired_events = Event.select().where(
|
||||
Event.camera == name,
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
# delete the grabbed clips from disk
|
||||
for event in expired_events:
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
if file_extension == "jpg":
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
# update the clips attribute for the db entry
|
||||
update_query = Event.update(update_params).where(
|
||||
Event.camera == name,
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
update_query.execute()
|
||||
|
||||
def purge_duplicates(self) -> None:
|
||||
duplicate_query = """with grouped_events as (
|
||||
select id,
|
||||
label,
|
||||
camera,
|
||||
has_snapshot,
|
||||
has_clip,
|
||||
row_number() over (
|
||||
partition by label, camera, round(start_time/5,0)*5
|
||||
order by end_time-start_time desc
|
||||
) as copy_number
|
||||
from event
|
||||
)
|
||||
|
||||
select distinct id, camera, has_snapshot, has_clip from grouped_events
|
||||
where copy_number > 1;"""
|
||||
|
||||
duplicate_events = Event.raw(duplicate_query)
|
||||
for event in duplicate_events:
|
||||
logger.debug(f"Removing duplicate: {event.id}")
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.jpg")
|
||||
media_path.unlink(missing_ok=True)
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png")
|
||||
media_path.unlink(missing_ok=True)
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4")
|
||||
media_path.unlink(missing_ok=True)
|
||||
|
||||
(
|
||||
Event.delete()
|
||||
.where(Event.id << [event.id for event in duplicate_events])
|
||||
.execute()
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
# only expire events every 5 minutes
|
||||
while not self.stop_event.wait(300):
|
||||
self.expire("clips")
|
||||
self.expire("snapshots")
|
||||
self.purge_duplicates()
|
||||
|
||||
# drop events from db where has_clip and has_snapshot are false
|
||||
delete_query = Event.delete().where(
|
||||
Event.has_clip == False, Event.has_snapshot == False
|
||||
)
|
||||
delete_query.execute()
|
||||
|
||||
logger.info(f"Exiting event cleanup...")
|
||||
@ -10,7 +10,6 @@ from pathlib import Path
|
||||
from peewee import fn
|
||||
|
||||
from frigate.config import EventsConfig, FrigateConfig
|
||||
from frigate.const import CLIPS_DIR
|
||||
from frigate.models import Event
|
||||
from frigate.types import CameraMetricsTypes
|
||||
from frigate.util import to_relative_box
|
||||
@ -196,161 +195,3 @@ class EventProcessor(threading.Thread):
|
||||
if event_type == "end":
|
||||
del self.events_in_process[event_data["id"]]
|
||||
self.event_processed_queue.put((event_data["id"], camera))
|
||||
|
||||
|
||||
class EventCleanup(threading.Thread):
|
||||
def __init__(self, config: FrigateConfig, stop_event: MpEvent):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = "event_cleanup"
|
||||
self.config = config
|
||||
self.stop_event = stop_event
|
||||
self.camera_keys = list(self.config.cameras.keys())
|
||||
|
||||
def expire(self, media_type: str) -> None:
|
||||
# TODO: Refactor media_type to enum
|
||||
## Expire events from unlisted cameras based on the global config
|
||||
if media_type == "clips":
|
||||
retain_config = self.config.record.events.retain
|
||||
file_extension = "mp4"
|
||||
update_params = {"has_clip": False}
|
||||
else:
|
||||
retain_config = self.config.snapshots.retain
|
||||
file_extension = "jpg"
|
||||
update_params = {"has_snapshot": False}
|
||||
|
||||
distinct_labels = (
|
||||
Event.select(Event.label)
|
||||
.where(Event.camera.not_in(self.camera_keys))
|
||||
.distinct()
|
||||
)
|
||||
|
||||
# loop over object types in db
|
||||
for l in distinct_labels:
|
||||
# get expiration time for this label
|
||||
expire_days = retain_config.objects.get(l.label, retain_config.default)
|
||||
expire_after = (
|
||||
datetime.datetime.now() - datetime.timedelta(days=expire_days)
|
||||
).timestamp()
|
||||
# grab all events after specific time
|
||||
expired_events = Event.select().where(
|
||||
Event.camera.not_in(self.camera_keys),
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
# delete the media from disk
|
||||
for event in expired_events:
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
if file_extension == "jpg":
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
|
||||
# update the clips attribute for the db entry
|
||||
update_query = Event.update(update_params).where(
|
||||
Event.camera.not_in(self.camera_keys),
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
update_query.execute()
|
||||
|
||||
## Expire events from cameras based on the camera config
|
||||
for name, camera in self.config.cameras.items():
|
||||
if media_type == "clips":
|
||||
retain_config = camera.record.events.retain
|
||||
else:
|
||||
retain_config = camera.snapshots.retain
|
||||
# get distinct objects in database for this camera
|
||||
distinct_labels = (
|
||||
Event.select(Event.label).where(Event.camera == name).distinct()
|
||||
)
|
||||
|
||||
# loop over object types in db
|
||||
for l in distinct_labels:
|
||||
# get expiration time for this label
|
||||
expire_days = retain_config.objects.get(l.label, retain_config.default)
|
||||
expire_after = (
|
||||
datetime.datetime.now() - datetime.timedelta(days=expire_days)
|
||||
).timestamp()
|
||||
# grab all events after specific time
|
||||
expired_events = Event.select().where(
|
||||
Event.camera == name,
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
# delete the grabbed clips from disk
|
||||
for event in expired_events:
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
if file_extension == "jpg":
|
||||
media_path = Path(
|
||||
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
|
||||
)
|
||||
media_path.unlink(missing_ok=True)
|
||||
# update the clips attribute for the db entry
|
||||
update_query = Event.update(update_params).where(
|
||||
Event.camera == name,
|
||||
Event.start_time < expire_after,
|
||||
Event.label == l.label,
|
||||
Event.retain_indefinitely == False,
|
||||
)
|
||||
update_query.execute()
|
||||
|
||||
def purge_duplicates(self) -> None:
|
||||
duplicate_query = """with grouped_events as (
|
||||
select id,
|
||||
label,
|
||||
camera,
|
||||
has_snapshot,
|
||||
has_clip,
|
||||
row_number() over (
|
||||
partition by label, camera, round(start_time/5,0)*5
|
||||
order by end_time-start_time desc
|
||||
) as copy_number
|
||||
from event
|
||||
)
|
||||
|
||||
select distinct id, camera, has_snapshot, has_clip from grouped_events
|
||||
where copy_number > 1;"""
|
||||
|
||||
duplicate_events = Event.raw(duplicate_query)
|
||||
for event in duplicate_events:
|
||||
logger.debug(f"Removing duplicate: {event.id}")
|
||||
media_name = f"{event.camera}-{event.id}"
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.jpg")
|
||||
media_path.unlink(missing_ok=True)
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png")
|
||||
media_path.unlink(missing_ok=True)
|
||||
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4")
|
||||
media_path.unlink(missing_ok=True)
|
||||
|
||||
(
|
||||
Event.delete()
|
||||
.where(Event.id << [event.id for event in duplicate_events])
|
||||
.execute()
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
# only expire events every 5 minutes
|
||||
while not self.stop_event.wait(300):
|
||||
self.expire("clips")
|
||||
self.expire("snapshots")
|
||||
self.purge_duplicates()
|
||||
|
||||
# drop events from db where has_clip and has_snapshot are false
|
||||
delete_query = Event.delete().where(
|
||||
Event.has_clip == False, Event.has_snapshot == False
|
||||
)
|
||||
delete_query.execute()
|
||||
|
||||
logger.info(f"Exiting event cleanup...")
|
||||
144
frigate/events_manual.py
Normal file
144
frigate/events_manual.py
Normal file
@ -0,0 +1,144 @@
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
import cv2
|
||||
from frigate.config import CameraConfig
|
||||
|
||||
from frigate.const import CLIPS_DIR
|
||||
from frigate.models import Event
|
||||
from frigate.object_processing import TrackedObjectProcessor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_manual_event(
|
||||
tracked_object_processor: TrackedObjectProcessor,
|
||||
camera_config: CameraConfig,
|
||||
camera_name: str,
|
||||
label: str,
|
||||
) -> str:
|
||||
# get a valid frame time for camera
|
||||
frame_time = tracked_object_processor.get_current_frame_time(camera_name)
|
||||
|
||||
# create event id and start frame time
|
||||
rand_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
||||
event_id = f"{frame_time}-{rand_id}"
|
||||
|
||||
# fabricate obj_data
|
||||
obj_data = {
|
||||
"id": event_id,
|
||||
"camera": camera_name,
|
||||
"frame_time": frame_time,
|
||||
"snapshot_time": 0.0,
|
||||
"label": label,
|
||||
"top_score": 1,
|
||||
"false_positive": False,
|
||||
"start_time": frame_time,
|
||||
"end_time": None,
|
||||
"score": 1,
|
||||
"box": [],
|
||||
"area": 0,
|
||||
"ratio": 0,
|
||||
"region": [],
|
||||
"stationary": False,
|
||||
"motionless_count": 0,
|
||||
"position_changes": [],
|
||||
"current_zones": "",
|
||||
"entered_zones": "",
|
||||
"has_clip": False,
|
||||
"has_snapshot": False,
|
||||
"thumbnail": None,
|
||||
}
|
||||
|
||||
# insert object into the queue
|
||||
current_obj_data = obj_data.copy()
|
||||
tracked_object_processor.event_queue.put(("start", camera_name, current_obj_data))
|
||||
|
||||
# update object data an send another event
|
||||
obj_data["has_clip"] = camera_config.record.enabled
|
||||
obj_data["has_snapshot"] = True
|
||||
|
||||
# Get current frame for thumb & snapshot
|
||||
(
|
||||
current_frame,
|
||||
updated_frame_time,
|
||||
) = tracked_object_processor.get_current_frame_and_time(camera_name)
|
||||
|
||||
# write jpg snapshot
|
||||
height = int(current_frame.shape[0])
|
||||
width = int(height * current_frame.shape[1] / current_frame.shape[0])
|
||||
|
||||
current_frame = cv2.resize(
|
||||
current_frame, dsize=(width, height), interpolation=cv2.INTER_AREA
|
||||
)
|
||||
|
||||
ret, jpg = cv2.imencode(".jpg", current_frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70])
|
||||
|
||||
with open(
|
||||
os.path.join(CLIPS_DIR, f"{camera_name}-{event_id}.jpg"),
|
||||
"wb",
|
||||
) as j:
|
||||
j.write(jpg.tobytes())
|
||||
|
||||
# write clean snapshot if enabled
|
||||
if camera_config.snapshots.clean_copy:
|
||||
ret, png = cv2.imencode(".png", current_frame)
|
||||
png_bytes = png.tobytes()
|
||||
|
||||
if png_bytes is None:
|
||||
logger.warning(f"Unable to save clean snapshot for {event_id}.")
|
||||
else:
|
||||
with open(
|
||||
os.path.join(
|
||||
CLIPS_DIR,
|
||||
f"{camera_name}-{event_id}-clean.png",
|
||||
),
|
||||
"wb",
|
||||
) as p:
|
||||
p.write(png_bytes)
|
||||
|
||||
# get thumbnail
|
||||
height = 175
|
||||
width = int(height * current_frame.shape[1] / current_frame.shape[0])
|
||||
|
||||
current_frame = cv2.resize(
|
||||
current_frame, dsize=(width, height), interpolation=cv2.INTER_AREA
|
||||
)
|
||||
|
||||
ret, thumb = cv2.imencode(
|
||||
".jpg", current_frame, [int(cv2.IMWRITE_JPEG_QUALITY), 30]
|
||||
)
|
||||
|
||||
obj_data["thumbnail"] = base64.b64encode(thumb).decode("utf-8")
|
||||
obj_data["snapshot_time"] = updated_frame_time
|
||||
|
||||
# update event in queue
|
||||
tracked_object_processor.event_queue.put(("update", camera_name, obj_data))
|
||||
|
||||
return event_id
|
||||
|
||||
|
||||
def finish_manual_event(
|
||||
tracked_object_processor: TrackedObjectProcessor,
|
||||
event_id: str,
|
||||
):
|
||||
# get associated event and data
|
||||
manual_event: Event = Event.get(Event.id == event_id)
|
||||
camera_name = manual_event.camera
|
||||
|
||||
# get frame time
|
||||
frame_time = tracked_object_processor.get_current_frame_time(camera_name)
|
||||
|
||||
# create obj_data
|
||||
obj_data = {
|
||||
"id": event_id,
|
||||
"end_time": frame_time,
|
||||
"has_snapshot": False,
|
||||
"has_clip": False,
|
||||
}
|
||||
|
||||
# end event in queue
|
||||
tracked_object_processor.event_queue.put(("end", camera_name, obj_data))
|
||||
@ -1,8 +1,8 @@
|
||||
import base64
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import copy
|
||||
import glob
|
||||
import logging
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import subprocess as sp
|
||||
@ -34,6 +34,7 @@ from playhouse.shortcuts import model_to_dict
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import CLIPS_DIR, MAX_SEGMENT_DURATION, RECORD_DIR
|
||||
from frigate.models import Event, Recordings, Timeline
|
||||
from frigate.events_manual import create_manual_event, finish_manual_event
|
||||
from frigate.object_processing import TrackedObject
|
||||
from frigate.plus import PlusApi
|
||||
from frigate.ptz import OnvifController
|
||||
@ -44,6 +45,7 @@ from frigate.util import (
|
||||
restart_frigate,
|
||||
vainfo_hwaccel,
|
||||
get_tz_modifiers,
|
||||
to_relative_box,
|
||||
)
|
||||
from frigate.storage import StorageMaintainer
|
||||
from frigate.version import VERSION
|
||||
@ -195,7 +197,7 @@ def send_to_plus(id):
|
||||
return make_response(jsonify({"success": False, "message": message}), 404)
|
||||
|
||||
# events from before the conversion to relative dimensions cant include annotations
|
||||
if any(d > 1 for d in event.data["box"]):
|
||||
if any(d > 1 for d in event.box):
|
||||
include_annotation = None
|
||||
|
||||
if event.end_time is None:
|
||||
@ -251,8 +253,8 @@ def send_to_plus(id):
|
||||
event.save()
|
||||
|
||||
if not include_annotation is None:
|
||||
region = event.data["region"]
|
||||
box = event.data["box"]
|
||||
region = event.region
|
||||
box = event.box
|
||||
|
||||
try:
|
||||
current_app.plus_api.add_annotation(
|
||||
@ -293,7 +295,7 @@ def false_positive(id):
|
||||
return make_response(jsonify({"success": False, "message": message}), 404)
|
||||
|
||||
# events from before the conversion to relative dimensions cant include annotations
|
||||
if any(d > 1 for d in event.data["box"]):
|
||||
if any(d > 1 for d in event.box):
|
||||
message = f"Events prior to 0.13 cannot be submitted as false positives"
|
||||
logger.error(message)
|
||||
return make_response(jsonify({"success": False, "message": message}), 400)
|
||||
@ -310,15 +312,11 @@ def false_positive(id):
|
||||
# need to refetch the event now that it has a plus_id
|
||||
event = Event.get(Event.id == id)
|
||||
|
||||
region = event.data["region"]
|
||||
box = event.data["box"]
|
||||
region = event.region
|
||||
box = event.box
|
||||
|
||||
# provide top score if score is unavailable
|
||||
score = (
|
||||
(event.data["top_score"] if event.data["top_score"] else event.top_score)
|
||||
if event.data["score"] is None
|
||||
else event.data["score"]
|
||||
)
|
||||
score = event.top_score if event.score is None else event.score
|
||||
|
||||
try:
|
||||
current_app.plus_api.add_false_positive(
|
||||
@ -759,7 +757,6 @@ def events():
|
||||
Event.top_score,
|
||||
Event.false_positive,
|
||||
Event.box,
|
||||
Event.data,
|
||||
]
|
||||
|
||||
if camera != "all":
|
||||
@ -848,6 +845,47 @@ def events():
|
||||
return jsonify([model_to_dict(e, exclude=excluded_fields) for e in events])
|
||||
|
||||
|
||||
@bp.route("/events/manual/<camera_name>/<label>/create", methods=("POST",))
|
||||
def create_event(camera_name, label):
|
||||
if not camera_name or not current_app.frigate_config.cameras.get(camera_name):
|
||||
return jsonify(
|
||||
{"success": False, "message": f"{camera_name} is not a valid camera."}, 404
|
||||
)
|
||||
|
||||
camera_config = current_app.frigate_config.cameras.get(camera_name)
|
||||
|
||||
if not label:
|
||||
return jsonify({"success": False, "message": f"{label} must be set."}, 404)
|
||||
|
||||
event_id = create_manual_event(
|
||||
current_app.detected_frames_processor,
|
||||
camera_config,
|
||||
camera_name,
|
||||
label,
|
||||
)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"message": f"Event successfully created.",
|
||||
"event_id": event_id,
|
||||
},
|
||||
200,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/events/manual/<event_id>/end", methods=("POST",))
|
||||
def end_event(event_id):
|
||||
try:
|
||||
finish_manual_event(current_app.detected_frames_processor, event_id)
|
||||
except:
|
||||
return jsonify(
|
||||
{"success": False, "message": f"{event_id} must be set and valid."}, 404
|
||||
)
|
||||
|
||||
return jsonify({"success": True, "message": f"Event successfully ended."}, 200)
|
||||
|
||||
|
||||
@bp.route("/config")
|
||||
def config():
|
||||
config = current_app.frigate_config.dict()
|
||||
@ -866,11 +904,6 @@ def config():
|
||||
|
||||
config["plus"] = {"enabled": current_app.plus_api.is_active()}
|
||||
|
||||
for detector, detector_config in config["detectors"].items():
|
||||
detector_config["model"][
|
||||
"labelmap"
|
||||
] = current_app.frigate_config.model.merged_labelmap
|
||||
|
||||
return jsonify(config)
|
||||
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ from frigate.config import (
|
||||
FrigateConfig,
|
||||
)
|
||||
from frigate.const import CLIPS_DIR
|
||||
from frigate.events import EventTypeEnum
|
||||
from frigate.events.maintainer import EventTypeEnum
|
||||
from frigate.util import (
|
||||
SharedMemoryFrameManager,
|
||||
calculate_region,
|
||||
|
||||
@ -5,7 +5,7 @@ import threading
|
||||
import queue
|
||||
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.events import EventTypeEnum
|
||||
from frigate.events.maintainer import EventTypeEnum
|
||||
from frigate.models import Timeline
|
||||
|
||||
from multiprocessing.queues import Queue
|
||||
|
||||
Loading…
Reference in New Issue
Block a user