Convert all media endpoints to FastAPI. Added /media prefix (/media/camera && media/events && /media/preview)

This commit is contained in:
Rui Alves 2024-09-08 15:33:56 +01:00
parent b83f9532ab
commit 012c953dfb
17 changed files with 566 additions and 473 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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}` : ""
}`;

View File

@ -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]);

View File

@ -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>

View File

@ -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);

View File

@ -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">

View File

@ -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}`}
/>
)}

View File

@ -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;

View File

@ -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 },
);

View File

@ -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 && (

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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(() => {