mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-13 22:55:26 +03:00
FastAPI example POC
This commit is contained in:
parent
bf11ae34eb
commit
d99bfa1730
@ -222,12 +222,16 @@ http {
|
|||||||
include proxy.conf;
|
include proxy.conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
|
# FIXME: Needed to disabled this rule, otherwise it fails for endpoints that end with one of those file extensions
|
||||||
include auth_request.conf;
|
# 1. with httptools it passes the auth.conf but then throws a 400 error "WARN "Invalid HTTP request received." -> https://github.com/encode/uvicorn/blob/47304d9ae76321f0f5f649ff4f73e09b17085933/uvicorn/protocols/http/httptools_impl.py#L165
|
||||||
rewrite ^/api/(.*)$ $1 break;
|
# 2. With h11 it goes through the auth.conf but returns a 404 error
|
||||||
proxy_pass http://frigate_api;
|
# We might need to add extra rules that will allow endpoint that end with an extension OR find a fix without creating other rules
|
||||||
include proxy.conf;
|
# location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
|
||||||
}
|
# include auth_request.conf;
|
||||||
|
# rewrite ^/api/(.*)$ $1 break;
|
||||||
|
# proxy_pass http://frigate_api;
|
||||||
|
# include proxy.conf;
|
||||||
|
# }
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
include auth_request.conf;
|
include auth_request.conf;
|
||||||
|
|||||||
@ -10,6 +10,9 @@ from functools import reduce
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from flask import Blueprint, Flask, current_app, jsonify, make_response, request
|
from flask import Blueprint, Flask, current_app, jsonify, make_response, request
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
from peewee import operator
|
from peewee import operator
|
||||||
@ -21,7 +24,6 @@ from frigate.api.event import EventBp
|
|||||||
from frigate.api.export import ExportBp
|
from frigate.api.export import ExportBp
|
||||||
from frigate.api.media import MediaBp
|
from frigate.api.media import MediaBp
|
||||||
from frigate.api.notification import NotificationBp
|
from frigate.api.notification import NotificationBp
|
||||||
from frigate.api.preview import PreviewBp
|
|
||||||
from frigate.api.review import ReviewBp
|
from frigate.api.review import ReviewBp
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.const import CONFIG_DIR
|
from frigate.const import CONFIG_DIR
|
||||||
@ -47,22 +49,23 @@ bp = Blueprint("frigate", __name__)
|
|||||||
bp.register_blueprint(EventBp)
|
bp.register_blueprint(EventBp)
|
||||||
bp.register_blueprint(ExportBp)
|
bp.register_blueprint(ExportBp)
|
||||||
bp.register_blueprint(MediaBp)
|
bp.register_blueprint(MediaBp)
|
||||||
bp.register_blueprint(PreviewBp)
|
|
||||||
bp.register_blueprint(ReviewBp)
|
bp.register_blueprint(ReviewBp)
|
||||||
bp.register_blueprint(AuthBp)
|
bp.register_blueprint(AuthBp)
|
||||||
bp.register_blueprint(NotificationBp)
|
bp.register_blueprint(NotificationBp)
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
def create_app(
|
def create_app(
|
||||||
frigate_config,
|
frigate_config,
|
||||||
database: SqliteQueueDatabase,
|
database: SqliteQueueDatabase,
|
||||||
embeddings: Optional[EmbeddingsContext],
|
embeddings: Optional[EmbeddingsContext],
|
||||||
detected_frames_processor,
|
detected_frames_processor,
|
||||||
storage_maintainer: StorageMaintainer,
|
storage_maintainer: StorageMaintainer,
|
||||||
onvif: OnvifController,
|
onvif: OnvifController,
|
||||||
external_processor: ExternalEventProcessor,
|
external_processor: ExternalEventProcessor,
|
||||||
plus_api: PlusApi,
|
plus_api: PlusApi,
|
||||||
stats_emitter: StatsEmitter,
|
stats_emitter: StatsEmitter,
|
||||||
):
|
):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@ -454,19 +457,26 @@ def vainfo():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/logs/<service>", methods=["GET"])
|
@router.get("/logs/{service}", tags=["Logs"])
|
||||||
def logs(service: str):
|
def logs(
|
||||||
|
service: str,
|
||||||
|
download: Optional[str] = None,
|
||||||
|
start: Optional[int] = 0,
|
||||||
|
end: Optional[int] = None,
|
||||||
|
):
|
||||||
|
"""Get logs for the requested service (frigate/nginx/go2rtc/chroma)"""
|
||||||
|
|
||||||
def download_logs(service_location: str):
|
def download_logs(service_location: str):
|
||||||
try:
|
try:
|
||||||
file = open(service_location, "r")
|
file = open(service_location, "r")
|
||||||
contents = file.read()
|
contents = file.read()
|
||||||
file.close()
|
file.close()
|
||||||
return jsonify(contents)
|
return JSONResponse(jsonable_encoder(contents))
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"success": False, "message": "Could not find log file"}),
|
content={"success": False, "message": "Could not find log file"},
|
||||||
500,
|
status_code=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
log_locations = {
|
log_locations = {
|
||||||
@ -478,17 +488,14 @@ def logs(service: str):
|
|||||||
service_location = log_locations.get(service)
|
service_location = log_locations.get(service)
|
||||||
|
|
||||||
if not service_location:
|
if not service_location:
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"success": False, "message": "Not a valid service"}),
|
content={"success": False, "message": "Not a valid service"},
|
||||||
404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
if request.args.get("download", type=bool, default=False):
|
if download:
|
||||||
return download_logs(service_location)
|
return download_logs(service_location)
|
||||||
|
|
||||||
start = request.args.get("start", type=int, default=0)
|
|
||||||
end = request.args.get("end", type=int)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file = open(service_location, "r")
|
file = open(service_location, "r")
|
||||||
contents = file.read()
|
contents = file.read()
|
||||||
@ -529,15 +536,15 @@ def logs(service: str):
|
|||||||
|
|
||||||
logLines.append(currentLine)
|
logLines.append(currentLine)
|
||||||
|
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"totalLines": len(logLines), "lines": logLines[start:end]}),
|
content={"totalLines": len(logLines), "lines": logLines[start:end]},
|
||||||
200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"success": False, "message": "Could not find log file"}),
|
content={"success": False, "message": "Could not find log file"},
|
||||||
500,
|
status_code=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,20 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import glob
|
import glob
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Optional
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pytz
|
import pytz
|
||||||
|
from fastapi import APIRouter, Request
|
||||||
|
from fastapi.responses import JSONResponse, StreamingResponse
|
||||||
from flask import Blueprint, Response, current_app, jsonify, make_response, request
|
from flask import Blueprint, Response, current_app, jsonify, make_response, request
|
||||||
from peewee import DoesNotExist, fn
|
from peewee import DoesNotExist, fn
|
||||||
from tzlocal import get_localzone_name
|
from tzlocal import get_localzone_name
|
||||||
@ -32,6 +36,8 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
MediaBp = Blueprint("media", __name__)
|
MediaBp = Blueprint("media", __name__)
|
||||||
|
|
||||||
|
router = APIRouter(tags=["Media"])
|
||||||
|
|
||||||
|
|
||||||
@MediaBp.route("/<camera_name>")
|
@MediaBp.route("/<camera_name>")
|
||||||
def mjpeg_feed(camera_name):
|
def mjpeg_feed(camera_name):
|
||||||
@ -92,90 +98,102 @@ def camera_ptz_info(camera_name):
|
|||||||
404,
|
404,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.get("/{camera_name}/latest.{extension}")
|
||||||
@MediaBp.route("/<camera_name>/latest.jpg")
|
def latest_frame(
|
||||||
@MediaBp.route("/<camera_name>/latest.webp")
|
request: Request,
|
||||||
def latest_frame(camera_name):
|
camera_name: str,
|
||||||
|
extension: str, # jpg/jpeg/png/webp
|
||||||
|
bbox: Optional[int] = None,
|
||||||
|
timestamp: Optional[int] = None,
|
||||||
|
zones: Optional[int] = None,
|
||||||
|
mask: Optional[int] = None,
|
||||||
|
motion: Optional[int] = None,
|
||||||
|
regions: Optional[int] = None,
|
||||||
|
quality: Optional[int] = 70,
|
||||||
|
h: Optional[int] = None,
|
||||||
|
):
|
||||||
draw_options = {
|
draw_options = {
|
||||||
"bounding_boxes": request.args.get("bbox", type=int),
|
"bounding_boxes": bbox,
|
||||||
"timestamp": request.args.get("timestamp", type=int),
|
"timestamp": timestamp,
|
||||||
"zones": request.args.get("zones", type=int),
|
"zones": zones,
|
||||||
"mask": request.args.get("mask", type=int),
|
"mask": mask,
|
||||||
"motion_boxes": request.args.get("motion", type=int),
|
"motion_boxes": motion,
|
||||||
"regions": request.args.get("regions", type=int),
|
"regions": regions,
|
||||||
}
|
}
|
||||||
resize_quality = request.args.get("quality", default=70, type=int)
|
|
||||||
extension = os.path.splitext(request.path)[1][1:]
|
|
||||||
|
|
||||||
if camera_name in current_app.frigate_config.cameras:
|
if camera_name in request.app.frigate_config.cameras:
|
||||||
frame = current_app.detected_frames_processor.get_current_frame(
|
frame = request.app.detected_frames_processor.get_current_frame(
|
||||||
camera_name, draw_options
|
camera_name, draw_options
|
||||||
)
|
)
|
||||||
retry_interval = float(
|
retry_interval = float(
|
||||||
current_app.frigate_config.cameras.get(camera_name).ffmpeg.retry_interval
|
request.app.frigate_config.cameras.get(camera_name).ffmpeg.retry_interval
|
||||||
or 10
|
or 10
|
||||||
)
|
)
|
||||||
|
|
||||||
if frame is None or datetime.now().timestamp() > (
|
if frame is None or datetime.now().timestamp() > (
|
||||||
current_app.detected_frames_processor.get_current_frame_time(camera_name)
|
request.app.detected_frames_processor.get_current_frame_time(camera_name)
|
||||||
+ retry_interval
|
+ retry_interval
|
||||||
):
|
):
|
||||||
if current_app.camera_error_image is None:
|
if request.app.camera_error_image is None:
|
||||||
error_image = glob.glob("/opt/frigate/frigate/images/camera-error.jpg")
|
error_image = glob.glob("/opt/frigate/frigate/images/camera-error.jpg")
|
||||||
|
|
||||||
if len(error_image) > 0:
|
if len(error_image) > 0:
|
||||||
current_app.camera_error_image = cv2.imread(
|
request.app.camera_error_image = cv2.imread(
|
||||||
error_image[0], cv2.IMREAD_UNCHANGED
|
error_image[0], cv2.IMREAD_UNCHANGED
|
||||||
)
|
)
|
||||||
|
|
||||||
frame = current_app.camera_error_image
|
frame = request.app.camera_error_image
|
||||||
|
|
||||||
height = int(request.args.get("h", str(frame.shape[0])))
|
height = int(h or str(frame.shape[0]))
|
||||||
width = int(height * frame.shape[1] / frame.shape[0])
|
width = int(height * frame.shape[1] / frame.shape[0])
|
||||||
|
|
||||||
if frame is None:
|
if frame is None:
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"success": False, "message": "Unable to get valid frame"}),
|
content={"success": False, "message": "Unable to get valid frame"},
|
||||||
500,
|
status_code=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
if height < 1 or width < 1:
|
if height < 1 or width < 1:
|
||||||
return (
|
return JSONResponse(
|
||||||
"Invalid height / width requested :: {} / {}".format(height, width),
|
content="Invalid height / width requested :: {} / {}".format(
|
||||||
400,
|
height, width
|
||||||
|
),
|
||||||
|
status_code=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
ret, img = cv2.imencode(
|
ret, img = cv2.imencode(
|
||||||
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), resize_quality]
|
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), quality]
|
||||||
)
|
)
|
||||||
response = make_response(img.tobytes())
|
return StreamingResponse(
|
||||||
response.headers["Content-Type"] = f"image/{extension}"
|
io.BytesIO(img.tobytes()),
|
||||||
response.headers["Cache-Control"] = "no-store"
|
media_type=f"image/{extension}",
|
||||||
return response
|
headers={"Content-Type": f"image/{extension}", "Cache-Control": "no-store"},
|
||||||
elif camera_name == "birdseye" and current_app.frigate_config.birdseye.restream:
|
)
|
||||||
|
elif camera_name == "birdseye" and request.app.frigate_config.birdseye.restream:
|
||||||
frame = cv2.cvtColor(
|
frame = cv2.cvtColor(
|
||||||
current_app.detected_frames_processor.get_current_frame(camera_name),
|
request.app.detected_frames_processor.get_current_frame(camera_name),
|
||||||
cv2.COLOR_YUV2BGR_I420,
|
cv2.COLOR_YUV2BGR_I420,
|
||||||
)
|
)
|
||||||
|
|
||||||
height = int(request.args.get("h", str(frame.shape[0])))
|
height = int(h or str(frame.shape[0]))
|
||||||
width = int(height * frame.shape[1] / frame.shape[0])
|
width = int(height * frame.shape[1] / frame.shape[0])
|
||||||
|
|
||||||
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
frame = cv2.resize(frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
ret, img = cv2.imencode(
|
ret, img = cv2.imencode(
|
||||||
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), resize_quality]
|
f".{extension}", frame, [int(cv2.IMWRITE_WEBP_QUALITY), quality]
|
||||||
|
)
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(img.tobytes()),
|
||||||
|
media_type=f"image/{extension}",
|
||||||
|
headers={"Content-Type": f"image/{extension}", "Cache-Control": "no-store"},
|
||||||
)
|
)
|
||||||
response = make_response(img.tobytes())
|
|
||||||
response.headers["Content-Type"] = f"image/{extension}"
|
|
||||||
response.headers["Cache-Control"] = "no-store"
|
|
||||||
return response
|
|
||||||
else:
|
else:
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify({"success": False, "message": "Camera not found"}),
|
content={"success": False, "message": "Camera not found"},
|
||||||
404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
37
frigate/api/new_app.py
Normal file
37
frigate/api/new_app.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from frigate.api import app as main_app
|
||||||
|
from frigate.api import media, preview
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# https://fastapi.tiangolo.com/tutorial/metadata/#use-your-tags
|
||||||
|
tags_metadata = [
|
||||||
|
{
|
||||||
|
"name": "Preview",
|
||||||
|
"description": "Preview routes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Logs",
|
||||||
|
"description": "Logs routes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Media",
|
||||||
|
"description": "Media routes",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def create_fastapi_app(frigate_config, detected_frames_processor):
|
||||||
|
logger.info("Starting FastAPI app")
|
||||||
|
app = FastAPI(debug=False, tags_metadata=tags_metadata)
|
||||||
|
app.include_router(preview.router)
|
||||||
|
app.include_router(media.router)
|
||||||
|
app.include_router(main_app.router)
|
||||||
|
app.frigate_config = frigate_config
|
||||||
|
app.detected_frames_processor = detected_frames_processor
|
||||||
|
app.camera_error_image = None
|
||||||
|
|
||||||
|
return app
|
||||||
@ -5,23 +5,20 @@ import os
|
|||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from flask import (
|
from fastapi import APIRouter
|
||||||
Blueprint,
|
from fastapi.responses import JSONResponse
|
||||||
jsonify,
|
|
||||||
make_response,
|
|
||||||
)
|
|
||||||
|
|
||||||
from frigate.const import CACHE_DIR, PREVIEW_FRAME_TYPE
|
from frigate.const import CACHE_DIR, PREVIEW_FRAME_TYPE
|
||||||
from frigate.models import Previews
|
from frigate.models import Previews
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
PreviewBp = Blueprint("previews", __name__)
|
|
||||||
|
router = APIRouter(tags=["Preview"])
|
||||||
|
|
||||||
|
|
||||||
@PreviewBp.route("/preview/<camera_name>/start/<int:start_ts>/end/<int:end_ts>")
|
@router.get("/preview/{camera_name}/start/{start_ts}/end/{end_ts}")
|
||||||
@PreviewBp.route("/preview/<camera_name>/start/<float:start_ts>/end/<float:end_ts>")
|
def preview_ts(camera_name: str, start_ts: float, end_ts: float):
|
||||||
def preview_ts(camera_name, start_ts, end_ts):
|
|
||||||
"""Get all mp4 previews relevant for time period."""
|
"""Get all mp4 previews relevant for time period."""
|
||||||
if camera_name != "all":
|
if camera_name != "all":
|
||||||
camera_clause = Previews.camera == camera_name
|
camera_clause = Previews.camera == camera_name
|
||||||
@ -62,21 +59,20 @@ def preview_ts(camera_name, start_ts, end_ts):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not clips:
|
if not clips:
|
||||||
return make_response(
|
return JSONResponse(
|
||||||
jsonify(
|
content={
|
||||||
{
|
"success": False,
|
||||||
"success": False,
|
"message": "No previews found.",
|
||||||
"message": "No previews found.",
|
},
|
||||||
}
|
status_code=404,
|
||||||
),
|
|
||||||
404,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return make_response(jsonify(clips), 200)
|
return JSONResponse(content=clips, status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@PreviewBp.route("/preview/<year_month>/<day>/<hour>/<camera_name>/<tz_name>")
|
@router.get("/preview/{year_month}/{day}/{hour}/{camera_name}/{tz_name}")
|
||||||
def preview_hour(year_month, day, hour, camera_name, tz_name):
|
def preview_hour(year_month: str, day: int, hour: int, camera_name: str, tz_name: str):
|
||||||
|
"""Get all mp4 previews relevant for time period given the timezone"""
|
||||||
parts = year_month.split("-")
|
parts = year_month.split("-")
|
||||||
start_date = (
|
start_date = (
|
||||||
datetime(int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc)
|
datetime(int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc)
|
||||||
@ -89,11 +85,8 @@ def preview_hour(year_month, day, hour, camera_name, tz_name):
|
|||||||
return preview_ts(camera_name, start_ts, end_ts)
|
return preview_ts(camera_name, start_ts, end_ts)
|
||||||
|
|
||||||
|
|
||||||
@PreviewBp.route("/preview/<camera_name>/start/<int:start_ts>/end/<int:end_ts>/frames")
|
@router.get("/preview/{camera_name}/start/{start_ts}/end/{end_ts}/frames")
|
||||||
@PreviewBp.route(
|
def get_preview_frames_from_cache(camera_name: str, start_ts: float, end_ts: float):
|
||||||
"/preview/<camera_name>/start/<float:start_ts>/end/<float:end_ts>/frames"
|
|
||||||
)
|
|
||||||
def get_preview_frames_from_cache(camera_name: str, start_ts, end_ts):
|
|
||||||
"""Get list of cached preview frames"""
|
"""Get list of cached preview frames"""
|
||||||
preview_dir = os.path.join(CACHE_DIR, "preview_frames")
|
preview_dir = os.path.join(CACHE_DIR, "preview_frames")
|
||||||
file_start = f"preview_{camera_name}"
|
file_start = f"preview_{camera_name}"
|
||||||
@ -113,4 +106,7 @@ def get_preview_frames_from_cache(camera_name: str, start_ts, end_ts):
|
|||||||
|
|
||||||
selected_previews.append(file)
|
selected_previews.append(file)
|
||||||
|
|
||||||
return jsonify(selected_previews)
|
return JSONResponse(
|
||||||
|
content=selected_previews,
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
|||||||
@ -14,6 +14,8 @@ from types import FrameType
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
|
import uvicorn
|
||||||
|
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||||
from peewee_migrate import Router
|
from peewee_migrate import Router
|
||||||
from playhouse.sqlite_ext import SqliteExtDatabase
|
from playhouse.sqlite_ext import SqliteExtDatabase
|
||||||
from playhouse.sqliteq import SqliteQueueDatabase
|
from playhouse.sqliteq import SqliteQueueDatabase
|
||||||
@ -21,6 +23,7 @@ from pydantic import ValidationError
|
|||||||
|
|
||||||
from frigate.api.app import create_app
|
from frigate.api.app import create_app
|
||||||
from frigate.api.auth import hash_password
|
from frigate.api.auth import hash_password
|
||||||
|
from frigate.api.new_app import create_fastapi_app
|
||||||
from frigate.comms.config_updater import ConfigPublisher
|
from frigate.comms.config_updater import ConfigPublisher
|
||||||
from frigate.comms.dispatcher import Communicator, Dispatcher
|
from frigate.comms.dispatcher import Communicator, Dispatcher
|
||||||
from frigate.comms.inter_process import InterProcessCommunicator
|
from frigate.comms.inter_process import InterProcessCommunicator
|
||||||
@ -397,6 +400,8 @@ class FrigateApp:
|
|||||||
self.stats_emitter,
|
self.stats_emitter,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.fastapi_app = create_fastapi_app(self.config, self.detected_frames_processor)
|
||||||
|
|
||||||
def init_onvif(self) -> None:
|
def init_onvif(self) -> None:
|
||||||
self.onvif_controller = OnvifController(self.config, self.ptz_metrics)
|
self.onvif_controller = OnvifController(self.config, self.ptz_metrics)
|
||||||
|
|
||||||
@ -739,11 +744,17 @@ class FrigateApp:
|
|||||||
signal.signal(signal.SIGTERM, receiveSignal)
|
signal.signal(signal.SIGTERM, receiveSignal)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.flask_app.run(host="127.0.0.1", port=5001, debug=False, threaded=True)
|
# Run the flask app inside fastapi: https://fastapi.tiangolo.com/advanced/sub-applications/
|
||||||
|
self.fastapi_app.mount("", WSGIMiddleware(self.flask_app))
|
||||||
|
uvicorn.run(
|
||||||
|
self.fastapi_app,
|
||||||
|
host="127.0.0.1",
|
||||||
|
port=5001,
|
||||||
|
)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger.info("Flask has exited...")
|
logger.info("FastAPI/Flask has exited...")
|
||||||
|
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user