mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-14 15:15:22 +03:00
Convert all media endpoints to FastAPI. Added /media prefix (/media/camera && media/events && /media/preview)
This commit is contained in:
parent
b83f9532ab
commit
012c953dfb
@ -23,7 +23,6 @@ from frigate.api.auth import AuthBp, get_jwt_secret, limiter
|
||||
from frigate.api.defs.tags import Tags
|
||||
from frigate.api.event import EventBp
|
||||
from frigate.api.export import ExportBp
|
||||
from frigate.api.media import MediaBp
|
||||
from frigate.api.notification import NotificationBp
|
||||
from frigate.api.review import ReviewBp
|
||||
from frigate.config import FrigateConfig
|
||||
@ -49,7 +48,6 @@ logger = logging.getLogger(__name__)
|
||||
bp = Blueprint("frigate", __name__)
|
||||
bp.register_blueprint(EventBp)
|
||||
bp.register_blueprint(ExportBp)
|
||||
bp.register_blueprint(MediaBp)
|
||||
bp.register_blueprint(ReviewBp)
|
||||
bp.register_blueprint(AuthBp)
|
||||
bp.register_blueprint(NotificationBp)
|
||||
|
||||
@ -3,7 +3,7 @@ import logging
|
||||
from fastapi import FastAPI
|
||||
|
||||
from frigate.api import app as main_app
|
||||
from frigate.api import preview
|
||||
from frigate.api import media, preview
|
||||
from frigate.plus import PlusApi
|
||||
from frigate.ptz.onvif import OnvifController
|
||||
from frigate.stats.emitter import StatsEmitter
|
||||
@ -21,9 +21,13 @@ def create_fastapi_app(
|
||||
stats_emitter: StatsEmitter,
|
||||
):
|
||||
logger.info("Starting FastAPI app")
|
||||
app = FastAPI(debug=False)
|
||||
app = FastAPI(
|
||||
debug=False,
|
||||
swagger_ui_parameters={"apisSorter": "alpha", "operationsSorter": "alpha"},
|
||||
)
|
||||
# Routes
|
||||
app.include_router(main_app.router)
|
||||
app.include_router(media.router)
|
||||
app.include_router(preview.router)
|
||||
# App Properties
|
||||
app.frigate_config = frigate_config
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,12 +5,14 @@ import os
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from peewee_migrate import Router
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
from playhouse.sqlite_ext import SqliteExtDatabase
|
||||
from playhouse.sqliteq import SqliteQueueDatabase
|
||||
|
||||
from frigate.api.app import create_app
|
||||
from frigate.api.fastapi_app import create_fastapi_app
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.models import Event, Recordings
|
||||
from frigate.plus import PlusApi
|
||||
@ -362,22 +364,21 @@ class TestHttp(unittest.TestCase):
|
||||
assert config["cameras"]["front_door"]
|
||||
|
||||
def test_recordings(self):
|
||||
app = create_app(
|
||||
app_fastapi = create_fastapi_app(
|
||||
FrigateConfig(**self.minimal_config).runtime_config(),
|
||||
self.db,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
PlusApi(),
|
||||
None,
|
||||
)
|
||||
client = TestClient(app_fastapi)
|
||||
id = "123456.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_recording(id)
|
||||
recording = client.get("/front_door/recordings").json
|
||||
response = client.get("/media/camera/front_door/recordings")
|
||||
assert response.status_code == 200
|
||||
recording = response.json()
|
||||
assert recording
|
||||
assert recording[0]["id"] == id
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ export default function CameraImage({
|
||||
return;
|
||||
}
|
||||
|
||||
const newSrc = `${apiHost}api/${name}/latest.webp?h=${requestHeight}${
|
||||
const newSrc = `${apiHost}api/media/camera/${name}/frame/latest?extension=webp&?height=${requestHeight}${
|
||||
searchParams ? `&${searchParams}` : ""
|
||||
}`;
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ export default function CameraImage({
|
||||
if (!config || scaledHeight === 0 || !canvasRef.current) {
|
||||
return;
|
||||
}
|
||||
img.src = `${apiHost}api/${name}/latest.webp?h=${scaledHeight}${
|
||||
img.src = `${apiHost}api/media/camera/${name}/frame/latest?extension=webp&height=${scaledHeight}${
|
||||
searchParams ? `&${searchParams}` : ""
|
||||
}`;
|
||||
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight, config]);
|
||||
|
||||
@ -173,7 +173,7 @@ export function AnimatedEventCard({
|
||||
}}
|
||||
>
|
||||
<source
|
||||
src={`${baseUrl}api/review/${event.id}/preview?format=mp4`}
|
||||
src={`${baseUrl}api/media/review/${event.id}/preview?format=mp4`}
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
|
||||
@ -160,13 +160,13 @@ export default function ObjectLifecycle({
|
||||
// image
|
||||
|
||||
const [src, setSrc] = useState(
|
||||
`${apiHost}api/${event.camera}/recordings/${event.start_time + annotationOffset / 1000}/snapshot.jpg?height=500`,
|
||||
`${apiHost}api/media/camera/${event.camera}/recordings/${event.start_time + annotationOffset / 1000}/snapshot.jpg?height=500`,
|
||||
);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (timeIndex) {
|
||||
const newSrc = `${apiHost}api/${event.camera}/recordings/${timeIndex + annotationOffset / 1000}/snapshot.jpg?height=500`;
|
||||
const newSrc = `${apiHost}api/media/camera/${event.camera}/recordings/${timeIndex + annotationOffset / 1000}/snapshot.jpg?height=500`;
|
||||
setSrc(newSrc);
|
||||
}
|
||||
setImgLoaded(false);
|
||||
|
||||
@ -248,8 +248,8 @@ function EventItem({
|
||||
draggable={false}
|
||||
src={
|
||||
event.has_snapshot
|
||||
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||
? `${apiHost}api/media/events/${event.id}/snapshot.jpg`
|
||||
: `${apiHost}api/media/events/${event.id}/thumbnail.jpg`
|
||||
}
|
||||
/>
|
||||
{hovered && (
|
||||
@ -263,8 +263,8 @@ function EventItem({
|
||||
download
|
||||
href={
|
||||
event.has_snapshot
|
||||
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||
? `${apiHost}api/media/events/${event.id}/snapshot.jpg`
|
||||
: `${apiHost}api/media/events/${event.id}/thumbnail.jpg`
|
||||
}
|
||||
>
|
||||
<Chip className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500">
|
||||
|
||||
@ -95,7 +95,7 @@ export function FrigatePlusDialog({
|
||||
{upload?.id && (
|
||||
<img
|
||||
className={`w-full ${grow} bg-black`}
|
||||
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`}
|
||||
src={`${baseUrl}api/media/events/${upload?.id}/snapshot.jpg`}
|
||||
alt={`${upload?.label}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -586,7 +586,7 @@ class PreviewFramesController extends PreviewController {
|
||||
if (this.seeking) {
|
||||
this.timeToSeek = frame;
|
||||
} else {
|
||||
const newSrc = `${baseUrl}api/preview/preview_${this.camera}-${frame}.webp/thumbnail.webp`;
|
||||
const newSrc = `${baseUrl}api/media/preview/preview_${this.camera}-${frame}.webp/thumbnail.webp`;
|
||||
|
||||
if (this.imgController.current.src != newSrc) {
|
||||
this.imgController.current.src = newSrc;
|
||||
@ -603,7 +603,7 @@ class PreviewFramesController extends PreviewController {
|
||||
}
|
||||
|
||||
if (this.timeToSeek) {
|
||||
const newSrc = `${baseUrl}api/preview/preview_${this.camera}-${this.timeToSeek}.webp/thumbnail.webp`;
|
||||
const newSrc = `${baseUrl}api/media/preview/preview_${this.camera}-${this.timeToSeek}.webp/thumbnail.webp`;
|
||||
|
||||
if (this.imgController.current.src != newSrc) {
|
||||
this.imgController.current.src = newSrc;
|
||||
|
||||
@ -149,7 +149,7 @@ export default function DynamicVideoPlayer({
|
||||
}
|
||||
|
||||
const time = controller.getProgress(playTime);
|
||||
return axios.post(`/${camera}/plus/${time}`);
|
||||
return axios.post(`/media/camera/${camera}/plus/${time}`);
|
||||
},
|
||||
[camera, controller],
|
||||
);
|
||||
@ -164,7 +164,7 @@ export default function DynamicVideoPlayer({
|
||||
[timeRange],
|
||||
);
|
||||
const { data: recordings } = useSWR<Recording[]>(
|
||||
[`${camera}/recordings`, recordingParams],
|
||||
[`media/camera/${camera}/recordings`, recordingParams],
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
|
||||
@ -433,7 +433,7 @@ export function InProgressPreview({
|
||||
<div className="relative flex size-full items-center bg-black">
|
||||
<img
|
||||
className="pointer-events-none size-full object-contain"
|
||||
src={`${apiHost}api/preview/${previewFrames[key]}/thumbnail.webp`}
|
||||
src={`${apiHost}api/media/preview/${previewFrames[key]}/thumbnail.webp`}
|
||||
onLoad={handleLoad}
|
||||
/>
|
||||
{showProgress && (
|
||||
|
||||
@ -42,7 +42,7 @@ export function PolygonCanvas({
|
||||
const element = new window.Image();
|
||||
element.width = width;
|
||||
element.height = height;
|
||||
element.src = `${apiHost}api/${camera}/latest.webp?cache=${Date.now()}`;
|
||||
element.src = `${apiHost}api/media/camera/${camera}/frame/latest?extension=webp&?cache=${Date.now()}`;
|
||||
return element;
|
||||
}
|
||||
// we know that these deps are correct
|
||||
|
||||
@ -259,7 +259,7 @@ export default function SubmitPlus() {
|
||||
</div>
|
||||
<img
|
||||
className="aspect-video h-full rounded-lg object-contain md:rounded-2xl"
|
||||
src={`${baseUrl}api/events/${event.id}/snapshot.jpg`}
|
||||
src={`${baseUrl}api/media/events/${event.id}/snapshot.jpg`}
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -499,7 +499,9 @@ function PtzControlPanel({
|
||||
clickOverlay: boolean;
|
||||
setClickOverlay: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}) {
|
||||
const { data: ptz } = useSWR<CameraPtzInfo>(`${camera}/ptz/info`);
|
||||
const { data: ptz } = useSWR<CameraPtzInfo>(
|
||||
`/media/camera/${camera}/ptz/info`,
|
||||
);
|
||||
|
||||
const { send: sendPtz } = usePtzCommand(camera);
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ type StorageMetricsProps = {
|
||||
export default function StorageMetrics({
|
||||
setLastUpdated,
|
||||
}: StorageMetricsProps) {
|
||||
const { data: cameraStorage } = useSWR<CameraStorage>("recordings/storage");
|
||||
const { data: cameraStorage } = useSWR<CameraStorage>("media/recordings/storage");
|
||||
const { data: stats } = useSWR<FrigateStats>("stats");
|
||||
|
||||
const totalStorage = useMemo(() => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user