From e724fe3da6795f71d549ee5df347889e2c330b8d Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sun, 3 Apr 2022 15:00:11 -0500 Subject: [PATCH] add endpoint to submit to plus --- docker/Dockerfile | 3 +- frigate/app.py | 9 ++- frigate/const.py | 2 + frigate/http.py | 62 ++++++++++++++++--- frigate/models.py | 1 + frigate/plus.py | 96 +++++++++++++++++++++++++++++ migrations/010_add_plus_image_id.py | 46 ++++++++++++++ 7 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 frigate/plus.py create mode 100644 migrations/010_add_plus_image_id.py diff --git a/docker/Dockerfile b/docker/Dockerfile index d758a8b5a..622da1432 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -57,7 +57,8 @@ RUN pip3 wheel --wheel-dir=/wheels \ peewee_migrate \ pydantic \ zeroconf \ - ws4py + ws4py \ + requests # Frigate Container FROM debian:11-slim diff --git a/frigate/app.py b/frigate/app.py index bf593d6ac..8f0aa36da 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -16,7 +16,7 @@ from playhouse.sqliteq import SqliteQueueDatabase from pydantic import ValidationError from frigate.config import DetectorTypeEnum, FrigateConfig -from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR +from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR, PLUS_ENV_VAR, PLUS_API_HOST from frigate.edgetpu import EdgeTPUProcess from frigate.events import EventCleanup, EventProcessor from frigate.http import create_app @@ -25,6 +25,7 @@ from frigate.models import Event, Recordings from frigate.mqtt import MqttSocketRelay, create_mqtt_client from frigate.object_processing import TrackedObjectProcessor from frigate.output import output_frames +from frigate.plus import PlusApi from frigate.record import RecordingCleanup, RecordingMaintainer from frigate.stats import StatsEmitter, stats_init from frigate.version import VERSION @@ -44,6 +45,11 @@ class FrigateApp: self.detection_out_events: Dict[str, mp.Event] = {} self.detection_shms: List[mp.shared_memory.SharedMemory] = [] self.log_queue = mp.Queue() + self.plus_api = ( + PlusApi(PLUS_API_HOST, os.environ.get(PLUS_ENV_VAR)) + if PLUS_ENV_VAR in os.environ + else None + ) self.camera_metrics = {} def set_environment_vars(self): @@ -146,6 +152,7 @@ class FrigateApp: self.db, self.stats_tracking, self.detected_frames_processor, + self.plus_api, ) def init_mqtt(self): diff --git a/frigate/const.py b/frigate/const.py index afb6075a7..004e3c28e 100644 --- a/frigate/const.py +++ b/frigate/const.py @@ -3,3 +3,5 @@ CLIPS_DIR = f"{BASE_DIR}/clips" RECORD_DIR = f"{BASE_DIR}/recordings" CACHE_DIR = "/tmp/cache" YAML_EXT = (".yaml", ".yml") +PLUS_ENV_VAR = "PLUS_API_KEY" +PLUS_API_HOST = "https://api.frigate.video" diff --git a/frigate/http.py b/frigate/http.py index f21d1f8e6..ea7054e3c 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -29,7 +29,7 @@ from flask import ( from peewee import SqliteDatabase, operator, fn, DoesNotExist, Value from playhouse.shortcuts import model_to_dict -from frigate.const import CLIPS_DIR, RECORD_DIR +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 @@ -45,6 +45,7 @@ def create_app( database: SqliteDatabase, stats_tracking, detected_frames_processor, + plus_api, ): app = Flask(__name__) @@ -61,6 +62,7 @@ def create_app( app.frigate_config = frigate_config app.stats_tracking = stats_tracking app.detected_frames_processor = detected_frames_processor + app.plus_api = plus_api app.register_blueprint(bp) @@ -137,6 +139,52 @@ def set_retain(id): ) +@bp.route("/events//plus", methods=("POST",)) +def send_to_plus(id): + if current_app.plus_api is None: + return make_response( + jsonify({"success": False, "message": "Plus token not set"}), 400 + ) + + try: + event = Event.get(Event.id == id) + except DoesNotExist: + return make_response( + jsonify({"success": False, "message": "Event" + id + " not found"}), 404 + ) + + if event.plus_id: + return make_response( + jsonify({"success": False, "message": "Already submitted to plus"}), 400 + ) + + # load clean.png + try: + filename = f"{event.camera}-{event.id}-clean.png" + image = cv2.imread(os.path.join(CLIPS_DIR, filename)) + except Exception: + return make_response( + jsonify( + {"success": False, "message": "Unable to load clean png for event"} + ), + 400, + ) + + try: + plus_id = current_app.plus_api.upload_image(image, event.camera) + except Exception as ex: + return make_response( + jsonify({"success": False, "message": str(ex)}), + 400, + ) + + # store image id in the database + event.plus_id = plus_id + event.save() + + return "success" + + @bp.route("/events//retain", methods=("DELETE",)) def delete_retain(id): try: @@ -252,6 +300,7 @@ def event_thumbnail(id): response.headers["Cache-Control"] = "private, max-age=31536000" return response + @bp.route("//