mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-15 07:35:27 +03:00
Revert media routers to old names. Order routes to make sure the dynamic ones from media.py are only used whenever there's no match on auth/etc
This commit is contained in:
parent
fcb54adb5f
commit
3a54b9251a
@ -12,7 +12,6 @@ class Extension(str, Enum):
|
|||||||
|
|
||||||
|
|
||||||
class MediaLatestFrameQueryParams(BaseModel):
|
class MediaLatestFrameQueryParams(BaseModel):
|
||||||
extension: Extension = Extension.webp
|
|
||||||
bbox: Optional[int] = None
|
bbox: Optional[int] = None
|
||||||
timestamp: Optional[int] = None
|
timestamp: Optional[int] = None
|
||||||
zones: Optional[int] = None
|
zones: Optional[int] = None
|
||||||
@ -22,14 +21,16 @@ class MediaLatestFrameQueryParams(BaseModel):
|
|||||||
quality: Optional[int] = 70
|
quality: Optional[int] = 70
|
||||||
height: Optional[int] = None
|
height: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
class MediaEventsSnapshotQueryParams(BaseModel):
|
class MediaEventsSnapshotQueryParams(BaseModel):
|
||||||
download: bool = False
|
download: Optional[bool] = False
|
||||||
timestamp: Optional[int] = None
|
timestamp: Optional[int] = None
|
||||||
bbox: Optional[int] = None
|
bbox: Optional[int] = None
|
||||||
crop: Optional[int] = None
|
crop: Optional[int] = None
|
||||||
height: Optional[int] = None
|
height: Optional[int] = None
|
||||||
quality: Optional[int] = 70
|
quality: Optional[int] = 70
|
||||||
|
|
||||||
|
|
||||||
class MediaMjpegFeedQueryParams(BaseModel):
|
class MediaMjpegFeedQueryParams(BaseModel):
|
||||||
fps: int = 3
|
fps: int = 3
|
||||||
height: int = 360
|
height: int = 360
|
||||||
@ -39,4 +40,3 @@ class MediaMjpegFeedQueryParams(BaseModel):
|
|||||||
mask: Optional[int] = None
|
mask: Optional[int] = None
|
||||||
motion: Optional[int] = None
|
motion: Optional[int] = None
|
||||||
regions: Optional[int] = None
|
regions: Optional[int] = None
|
||||||
|
|
||||||
|
|||||||
@ -79,19 +79,21 @@ def create_fastapi_app(
|
|||||||
database.close()
|
database.close()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
# Rate limiter (used for login endpoint)
|
||||||
app.state.limiter = limiter
|
app.state.limiter = limiter
|
||||||
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
||||||
app.add_middleware(SlowAPIMiddleware)
|
app.add_middleware(SlowAPIMiddleware)
|
||||||
|
|
||||||
# Routes
|
# Routes
|
||||||
|
# Order of include_router matters: https://fastapi.tiangolo.com/tutorial/path-params/#order-matters
|
||||||
|
app.include_router(auth.router)
|
||||||
|
app.include_router(review.router)
|
||||||
app.include_router(main_app.router)
|
app.include_router(main_app.router)
|
||||||
app.include_router(media.router)
|
|
||||||
app.include_router(preview.router)
|
app.include_router(preview.router)
|
||||||
app.include_router(notification.router)
|
app.include_router(notification.router)
|
||||||
app.include_router(review.router)
|
|
||||||
app.include_router(export.router)
|
app.include_router(export.router)
|
||||||
app.include_router(event.router)
|
app.include_router(event.router)
|
||||||
app.include_router(auth.router)
|
app.include_router(media.router)
|
||||||
# App Properties
|
# App Properties
|
||||||
app.frigate_config = frigate_config
|
app.frigate_config = frigate_config
|
||||||
app.embeddings = embeddings
|
app.embeddings = embeddings
|
||||||
|
|||||||
@ -20,6 +20,7 @@ from peewee import DoesNotExist, fn
|
|||||||
from tzlocal import get_localzone_name
|
from tzlocal import get_localzone_name
|
||||||
|
|
||||||
from frigate.api.defs.media_query_parameters import (
|
from frigate.api.defs.media_query_parameters import (
|
||||||
|
Extension,
|
||||||
MediaEventsSnapshotQueryParams,
|
MediaEventsSnapshotQueryParams,
|
||||||
MediaLatestFrameQueryParams,
|
MediaLatestFrameQueryParams,
|
||||||
MediaMjpegFeedQueryParams,
|
MediaMjpegFeedQueryParams,
|
||||||
@ -48,7 +49,7 @@ def secure_filename(file_name: str):
|
|||||||
return file_name
|
return file_name
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}")
|
@router.get("{camera_name}")
|
||||||
def mjpeg_feed(
|
def mjpeg_feed(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
@ -101,7 +102,7 @@ def imagestream(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/ptz/info")
|
@router.get("/{camera_name}/ptz/info")
|
||||||
def camera_ptz_info(request: Request, camera_name: str):
|
def camera_ptz_info(request: Request, camera_name: str):
|
||||||
if camera_name in request.app.frigate_config.cameras:
|
if camera_name in request.app.frigate_config.cameras:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
@ -114,10 +115,11 @@ def camera_ptz_info(request: Request, camera_name: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/frame/latest")
|
@router.get("/{camera_name}/latest.{extension}")
|
||||||
def latest_frame(
|
def latest_frame(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
|
extension: Extension,
|
||||||
params: MediaLatestFrameQueryParams = Depends(),
|
params: MediaLatestFrameQueryParams = Depends(),
|
||||||
):
|
):
|
||||||
draw_options = {
|
draw_options = {
|
||||||
@ -129,7 +131,6 @@ def latest_frame(
|
|||||||
"regions": params.regions,
|
"regions": params.regions,
|
||||||
}
|
}
|
||||||
quality = params.quality
|
quality = params.quality
|
||||||
extension = params.extension
|
|
||||||
|
|
||||||
if camera_name in request.app.frigate_config.cameras:
|
if camera_name in request.app.frigate_config.cameras:
|
||||||
frame = request.app.detected_frames_processor.get_current_frame(
|
frame = request.app.detected_frames_processor.get_current_frame(
|
||||||
@ -207,7 +208,7 @@ def latest_frame(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/recordings/{frame_time}/snapshot.{format}")
|
@router.get("/{camera_name}/recordings/{frame_time}/snapshot.{format}")
|
||||||
def get_snapshot_from_recording(
|
def get_snapshot_from_recording(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
@ -268,7 +269,7 @@ def get_snapshot_from_recording(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/media/camera/{camera_name}/plus/{frame_time}")
|
@router.post("/{camera_name}/plus/{frame_time}")
|
||||||
def submit_recording_snapshot_to_plus(
|
def submit_recording_snapshot_to_plus(
|
||||||
request: Request, camera_name: str, frame_time: str
|
request: Request, camera_name: str, frame_time: str
|
||||||
):
|
):
|
||||||
@ -332,7 +333,7 @@ def submit_recording_snapshot_to_plus(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/recordings/storage")
|
@router.get("/recordings/storage")
|
||||||
def get_recordings_storage_usage(request: Request):
|
def get_recordings_storage_usage(request: Request):
|
||||||
recording_stats = request.app.stats_emitter.get_latest_stats()["service"][
|
recording_stats = request.app.stats_emitter.get_latest_stats()["service"][
|
||||||
"storage"
|
"storage"
|
||||||
@ -356,7 +357,7 @@ def get_recordings_storage_usage(request: Request):
|
|||||||
return JSONResponse(content=camera_usages)
|
return JSONResponse(content=camera_usages)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/recordings/summary")
|
@router.get("/{camera_name}/recordings/summary")
|
||||||
def recordings_summary(camera_name: str, timezone: str = "utc"):
|
def recordings_summary(camera_name: str, timezone: str = "utc"):
|
||||||
"""Returns hourly summary for recordings of given camera"""
|
"""Returns hourly summary for recordings of given camera"""
|
||||||
hour_modifier, minute_modifier, seconds_offset = get_tz_modifiers(timezone)
|
hour_modifier, minute_modifier, seconds_offset = get_tz_modifiers(timezone)
|
||||||
@ -418,7 +419,7 @@ def recordings_summary(camera_name: str, timezone: str = "utc"):
|
|||||||
return JSONResponse(content=list(days.values()))
|
return JSONResponse(content=list(days.values()))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/recordings")
|
@router.get("/{camera_name}/recordings")
|
||||||
def recordings(
|
def recordings(
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
after: float = (datetime.now() - timedelta(hours=1)).timestamp(),
|
after: float = (datetime.now() - timedelta(hours=1)).timestamp(),
|
||||||
@ -448,7 +449,7 @@ def recordings(
|
|||||||
return JSONResponse(content=list(recordings))
|
return JSONResponse(content=list(recordings))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/start/{start_ts}/end/{end_ts}/clip.mp4")
|
@router.get("/{camera_name}/start/{start_ts}/end/{end_ts}/clip.mp4")
|
||||||
def recording_clip(
|
def recording_clip(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
@ -696,7 +697,73 @@ def vod_event(event_id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/label/{label}/snapshot.jpg")
|
@router.get("/events/{event_id}/snapshot.jpg")
|
||||||
|
def event_snapshot(
|
||||||
|
request: Request,
|
||||||
|
event_id: str,
|
||||||
|
params: MediaEventsSnapshotQueryParams = Depends(),
|
||||||
|
):
|
||||||
|
event_complete = False
|
||||||
|
jpg_bytes = None
|
||||||
|
try:
|
||||||
|
event = Event.get(Event.id == event_id, Event.end_time != None)
|
||||||
|
event_complete = True
|
||||||
|
if not event.has_snapshot:
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "message": "Snapshot not available"},
|
||||||
|
status_code=404,
|
||||||
|
)
|
||||||
|
# read snapshot from disk
|
||||||
|
with open(
|
||||||
|
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}.jpg"), "rb"
|
||||||
|
) as image_file:
|
||||||
|
jpg_bytes = image_file.read()
|
||||||
|
except DoesNotExist:
|
||||||
|
# see if the object is currently being tracked
|
||||||
|
try:
|
||||||
|
camera_states = request.app.detected_frames_processor.camera_states.values()
|
||||||
|
for camera_state in camera_states:
|
||||||
|
if event_id in camera_state.tracked_objects:
|
||||||
|
tracked_obj = camera_state.tracked_objects.get(event_id)
|
||||||
|
if tracked_obj is not None:
|
||||||
|
jpg_bytes = tracked_obj.get_jpg_bytes(
|
||||||
|
timestamp=params.timestamp,
|
||||||
|
bounding_box=params.bbox,
|
||||||
|
crop=params.crop,
|
||||||
|
height=params.height,
|
||||||
|
quality=params.quality,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "message": "Event not found"},
|
||||||
|
status_code=404,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "message": "Event not found"}, status_code=404
|
||||||
|
)
|
||||||
|
|
||||||
|
if jpg_bytes is None:
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "message": "Event not found"}, status_code=404
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "image/jpeg",
|
||||||
|
"Cache-Control": "private, max-age=31536000" if event_complete else "no-store",
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.download:
|
||||||
|
headers["Content-Disposition"] = f"attachment; filename=snapshot-{event_id}.jpg"
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(jpg_bytes),
|
||||||
|
media_type="image/jpeg",
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{camera_name}/{label}/snapshot.jpg")
|
||||||
def label_snapshot(request: Request, camera_name: str, label: str):
|
def label_snapshot(request: Request, camera_name: str, label: str):
|
||||||
"""Returns the snapshot image from the latest event for the given camera and label combo"""
|
"""Returns the snapshot image from the latest event for the given camera and label combo"""
|
||||||
label = unquote(label)
|
label = unquote(label)
|
||||||
@ -729,8 +796,8 @@ def label_snapshot(request: Request, camera_name: str, label: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/label/{label}/best.jpg")
|
@router.get("/{camera_name}/{label}/best.jpg")
|
||||||
@router.get("/media/camera/{camera_name}/label/{label}/thumbnail.jpg")
|
@router.get("/{camera_name}/{label}/thumbnail.jpg")
|
||||||
def label_thumbnail(request: Request, camera_name: str, label: str):
|
def label_thumbnail(request: Request, camera_name: str, label: str):
|
||||||
label = unquote(label)
|
label = unquote(label)
|
||||||
event_query = Event.select(fn.MAX(Event.id)).where(Event.camera == camera_name)
|
event_query = Event.select(fn.MAX(Event.id)).where(Event.camera == camera_name)
|
||||||
@ -752,7 +819,7 @@ def label_thumbnail(request: Request, camera_name: str, label: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/label/{label}/clip.mp4")
|
@router.get("/{camera_name}/{label}/clip.mp4")
|
||||||
def label_clip(request: Request, camera_name: str, label: str):
|
def label_clip(request: Request, camera_name: str, label: str):
|
||||||
label = unquote(label)
|
label = unquote(label)
|
||||||
event_query = Event.select(fn.MAX(Event.id)).where(
|
event_query = Event.select(fn.MAX(Event.id)).where(
|
||||||
@ -771,7 +838,7 @@ def label_clip(request: Request, camera_name: str, label: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/grid.jpg")
|
@router.get("/{camera_name}/grid.jpg")
|
||||||
def grid_snapshot(
|
def grid_snapshot(
|
||||||
request: Request, camera_name: str, color: str = "green", font_scale: float = 0.5
|
request: Request, camera_name: str, color: str = "green", font_scale: float = 0.5
|
||||||
):
|
):
|
||||||
@ -892,7 +959,7 @@ def grid_snapshot(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/events/{event_id}/snapshot-clean.png")
|
@router.get("/events/{event_id}/snapshot-clean.png")
|
||||||
def event_snapshot_clean(request: Request, event_id: str, download: bool = False):
|
def event_snapshot_clean(request: Request, event_id: str, download: bool = False):
|
||||||
png_bytes = None
|
png_bytes = None
|
||||||
try:
|
try:
|
||||||
@ -976,73 +1043,7 @@ def event_snapshot_clean(request: Request, event_id: str, download: bool = False
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/events/{event_id}/snapshot.jpg")
|
@router.get("/events/{event_id}/clip.mp4")
|
||||||
def event_snapshot(
|
|
||||||
request: Request,
|
|
||||||
event_id: str,
|
|
||||||
params: MediaEventsSnapshotQueryParams = Depends(),
|
|
||||||
):
|
|
||||||
event_complete = False
|
|
||||||
jpg_bytes = None
|
|
||||||
try:
|
|
||||||
event = Event.get(Event.id == event_id, Event.end_time != None)
|
|
||||||
event_complete = True
|
|
||||||
if not event.has_snapshot:
|
|
||||||
return JSONResponse(
|
|
||||||
content={"success": False, "message": "Snapshot not available"},
|
|
||||||
status_code=404,
|
|
||||||
)
|
|
||||||
# read snapshot from disk
|
|
||||||
with open(
|
|
||||||
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}.jpg"), "rb"
|
|
||||||
) as image_file:
|
|
||||||
jpg_bytes = image_file.read()
|
|
||||||
except DoesNotExist:
|
|
||||||
# see if the object is currently being tracked
|
|
||||||
try:
|
|
||||||
camera_states = request.app.detected_frames_processor.camera_states.values()
|
|
||||||
for camera_state in camera_states:
|
|
||||||
if event_id in camera_state.tracked_objects:
|
|
||||||
tracked_obj = camera_state.tracked_objects.get(event_id)
|
|
||||||
if tracked_obj is not None:
|
|
||||||
jpg_bytes = tracked_obj.get_jpg_bytes(
|
|
||||||
timestamp=params.timestamp,
|
|
||||||
bounding_box=params.bbox,
|
|
||||||
crop=params.crop,
|
|
||||||
height=params.height,
|
|
||||||
quality=params.quality,
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
return JSONResponse(
|
|
||||||
content={"success": False, "message": "Event not found"},
|
|
||||||
status_code=404,
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
return JSONResponse(
|
|
||||||
content={"success": False, "message": "Event not found"}, status_code=404
|
|
||||||
)
|
|
||||||
|
|
||||||
if jpg_bytes is None:
|
|
||||||
return JSONResponse(
|
|
||||||
content={"success": False, "message": "Event not found"}, status_code=404
|
|
||||||
)
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "image/jpeg",
|
|
||||||
"Cache-Control": "private, max-age=31536000" if event_complete else "no-store",
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.download:
|
|
||||||
headers["Content-Disposition"] = f"attachment; filename=snapshot-{event_id}.jpg"
|
|
||||||
|
|
||||||
return StreamingResponse(
|
|
||||||
io.BytesIO(jpg_bytes),
|
|
||||||
media_type="image/jpeg",
|
|
||||||
headers=headers,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/events/{event_id}/clip.mp4")
|
|
||||||
def event_clip(request: Request, event_id: str, download: bool = False):
|
def event_clip(request: Request, event_id: str, download: bool = False):
|
||||||
try:
|
try:
|
||||||
event: Event = Event.get(Event.id == event_id)
|
event: Event = Event.get(Event.id == event_id)
|
||||||
@ -1085,7 +1086,7 @@ def event_clip(request: Request, event_id: str, download: bool = False):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/events/{event_id}/thumbnail.jpg")
|
@router.get("/events/{event_id}/thumbnail.jpg")
|
||||||
def event_thumbnail(
|
def event_thumbnail(
|
||||||
request: Request,
|
request: Request,
|
||||||
event_id: str,
|
event_id: str,
|
||||||
@ -1150,7 +1151,7 @@ def event_thumbnail(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/events/{event_id}/preview.gif")
|
@router.get("/events/{event_id}/preview.gif")
|
||||||
def event_preview(request: Request, event_id: str):
|
def event_preview(request: Request, event_id: str):
|
||||||
try:
|
try:
|
||||||
event: Event = Event.get(Event.id == event_id)
|
event: Event = Event.get(Event.id == event_id)
|
||||||
@ -1166,7 +1167,7 @@ def event_preview(request: Request, event_id: str):
|
|||||||
return preview_gif(request, event.camera, start_ts, end_ts)
|
return preview_gif(request, event.camera, start_ts, end_ts)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/start/{start_ts}/end/{end_ts}/preview.gif")
|
@router.get("/{camera_name}/start/{start_ts}/end/{end_ts}/preview.gif")
|
||||||
def preview_gif(
|
def preview_gif(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
@ -1322,7 +1323,7 @@ def preview_gif(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/camera/{camera_name}/start/{start_ts}/end/{end_ts}/preview.mp4")
|
@router.get("/{camera_name}/start/{start_ts}/end/{end_ts}/preview.mp4")
|
||||||
def preview_mp4(
|
def preview_mp4(
|
||||||
request: Request,
|
request: Request,
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
@ -1498,7 +1499,7 @@ def preview_mp4(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/review/{event_id}/preview")
|
@router.get("/review/{event_id}/preview")
|
||||||
def review_preview(
|
def review_preview(
|
||||||
request: Request,
|
request: Request,
|
||||||
event_id: str,
|
event_id: str,
|
||||||
@ -1524,8 +1525,8 @@ def review_preview(
|
|||||||
return preview_mp4(request, review.camera, start_ts, end_ts)
|
return preview_mp4(request, review.camera, start_ts, end_ts)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/media/preview/{file_name}/thumbnail.jpg")
|
@router.get("/preview/{file_name}/thumbnail.jpg")
|
||||||
@router.get("/media/preview/{file_name}/thumbnail.webp")
|
@router.get("/preview/{file_name}/thumbnail.webp")
|
||||||
def preview_thumbnail(file_name: str):
|
def preview_thumbnail(file_name: str):
|
||||||
"""Get a thumbnail from the cached preview frames."""
|
"""Get a thumbnail from the cached preview frames."""
|
||||||
if len(file_name) > 1000:
|
if len(file_name) > 1000:
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export default function CameraImage({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSrc = `${apiHost}api/media/camera/${name}/frame/latest?extension=webp&?height=${requestHeight}${
|
const newSrc = `${apiHost}api/${name}/latest.webp?height=${requestHeight}${
|
||||||
searchParams ? `&${searchParams}` : ""
|
searchParams ? `&${searchParams}` : ""
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export default function CameraImage({
|
|||||||
if (!config || scaledHeight === 0 || !canvasRef.current) {
|
if (!config || scaledHeight === 0 || !canvasRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
img.src = `${apiHost}api/media/camera/${name}/frame/latest?extension=webp&height=${scaledHeight}${
|
img.src = `${apiHost}api/${name}/latest.webp?height=${scaledHeight}${
|
||||||
searchParams ? `&${searchParams}` : ""
|
searchParams ? `&${searchParams}` : ""
|
||||||
}`;
|
}`;
|
||||||
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight, config]);
|
}, [apiHost, canvasRef, name, img, searchParams, scaledHeight, config]);
|
||||||
|
|||||||
@ -184,7 +184,7 @@ export function AnimatedEventCard({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<source
|
<source
|
||||||
src={`${baseUrl}api/media/review/${event.id}/preview?format=mp4`}
|
src={`${baseUrl}api/review/${event.id}/preview?format=mp4`}
|
||||||
type="video/mp4"
|
type="video/mp4"
|
||||||
/>
|
/>
|
||||||
</video>
|
</video>
|
||||||
|
|||||||
@ -163,13 +163,13 @@ export default function ObjectLifecycle({
|
|||||||
// image
|
// image
|
||||||
|
|
||||||
const [src, setSrc] = useState(
|
const [src, setSrc] = useState(
|
||||||
`${apiHost}api/media/camera/${event.camera}/recordings/${event.start_time + annotationOffset / 1000}/snapshot.jpg?height=500`,
|
`${apiHost}api/${event.camera}/recordings/${event.start_time + annotationOffset / 1000}/snapshot.jpg?height=500`,
|
||||||
);
|
);
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (timeIndex) {
|
if (timeIndex) {
|
||||||
const newSrc = `${apiHost}api/media/camera/${event.camera}/recordings/${timeIndex + annotationOffset / 1000}/snapshot.jpg?height=500`;
|
const newSrc = `${apiHost}api/${event.camera}/recordings/${timeIndex + annotationOffset / 1000}/snapshot.jpg?height=500`;
|
||||||
setSrc(newSrc);
|
setSrc(newSrc);
|
||||||
}
|
}
|
||||||
setImgLoaded(false);
|
setImgLoaded(false);
|
||||||
|
|||||||
@ -302,8 +302,8 @@ function EventItem({
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
src={
|
src={
|
||||||
event.has_snapshot
|
event.has_snapshot
|
||||||
? `${apiHost}api/media/events/${event.id}/snapshot.jpg`
|
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||||
: `${apiHost}api/media/events/${event.id}/thumbnail.jpg`
|
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{hovered && (
|
{hovered && (
|
||||||
@ -317,8 +317,8 @@ function EventItem({
|
|||||||
download
|
download
|
||||||
href={
|
href={
|
||||||
event.has_snapshot
|
event.has_snapshot
|
||||||
? `${apiHost}api/media/events/${event.id}/snapshot.jpg`
|
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||||
: `${apiHost}api/media/events/${event.id}/thumbnail.jpg`
|
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Chip className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500">
|
<Chip className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500">
|
||||||
|
|||||||
@ -117,7 +117,7 @@ export function FrigatePlusDialog({
|
|||||||
{upload?.id && (
|
{upload?.id && (
|
||||||
<img
|
<img
|
||||||
className={`w-full ${grow} bg-black`}
|
className={`w-full ${grow} bg-black`}
|
||||||
src={`${baseUrl}api/media/events/${upload?.id}/snapshot.jpg`}
|
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`}
|
||||||
alt={`${upload?.label}`}
|
alt={`${upload?.label}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -149,7 +149,7 @@ export default function DynamicVideoPlayer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const time = controller.getProgress(playTime);
|
const time = controller.getProgress(playTime);
|
||||||
return axios.post(`/media/camera/${camera}/plus/${time}`);
|
return axios.post(`/${camera}/plus/${time}`);
|
||||||
},
|
},
|
||||||
[camera, controller],
|
[camera, controller],
|
||||||
);
|
);
|
||||||
@ -164,7 +164,7 @@ export default function DynamicVideoPlayer({
|
|||||||
[timeRange],
|
[timeRange],
|
||||||
);
|
);
|
||||||
const { data: recordings } = useSWR<Recording[]>(
|
const { data: recordings } = useSWR<Recording[]>(
|
||||||
[`media/camera/${camera}/recordings`, recordingParams],
|
[`${camera}/recordings`, recordingParams],
|
||||||
{ revalidateOnFocus: false },
|
{ revalidateOnFocus: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -432,7 +432,7 @@ export function InProgressPreview({
|
|||||||
<div className="relative flex size-full items-center bg-black">
|
<div className="relative flex size-full items-center bg-black">
|
||||||
<img
|
<img
|
||||||
className="pointer-events-none size-full object-contain"
|
className="pointer-events-none size-full object-contain"
|
||||||
src={`${apiHost}api/media/preview/${previewFrames[key]}/thumbnail.webp`}
|
src={`${apiHost}api/preview/${previewFrames[key]}/thumbnail.webp`}
|
||||||
onLoad={handleLoad}
|
onLoad={handleLoad}
|
||||||
/>
|
/>
|
||||||
{showProgress && (
|
{showProgress && (
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export function PolygonCanvas({
|
|||||||
const element = new window.Image();
|
const element = new window.Image();
|
||||||
element.width = width;
|
element.width = width;
|
||||||
element.height = height;
|
element.height = height;
|
||||||
element.src = `${apiHost}api/media/camera/${camera}/frame/latest?extension=webp&?cache=${Date.now()}`;
|
element.src = `${apiHost}api/${camera}/latest?extension=webp&?cache=${Date.now()}`;
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
// we know that these deps are correct
|
// we know that these deps are correct
|
||||||
|
|||||||
@ -500,7 +500,7 @@ function PtzControlPanel({
|
|||||||
setClickOverlay: React.Dispatch<React.SetStateAction<boolean>>;
|
setClickOverlay: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}) {
|
}) {
|
||||||
const { data: ptz } = useSWR<CameraPtzInfo>(
|
const { data: ptz } = useSWR<CameraPtzInfo>(
|
||||||
`/media/camera/${camera}/ptz/info`,
|
`${camera}/ptz/info`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { send: sendPtz } = usePtzCommand(camera);
|
const { send: sendPtz } = usePtzCommand(camera);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user