diff --git a/docs/docs/usage/api.md b/docs/docs/usage/api.md index b7fb4dbbc..bf0c5f071 100644 --- a/docs/docs/usage/api.md +++ b/docs/docs/usage/api.md @@ -5,7 +5,7 @@ title: HTTP API A web server is available on port 5000 with the following endpoints. -### `/api/` +### `GET /api/` An mjpeg stream for debugging. Keep in mind the mjpeg endpoint is for debugging only and will put additional load on the system when in use. @@ -24,7 +24,7 @@ Accepts the following query string parameters: You can access a higher resolution mjpeg stream by appending `h=height-in-pixels` to the endpoint. For example `http://localhost:5000/back?h=1080`. You can also increase the FPS by appending `fps=frame-rate` to the URL such as `http://localhost:5000/back?fps=10` or both with `?fps=10&h=1000`. -### `/api///best.jpg[?h=300&crop=1]` +### `GET /api///best.jpg[?h=300&crop=1]` The best snapshot for any object type. It is a full resolution image by default. @@ -33,7 +33,7 @@ Example parameters: - `h=300`: resizes the image to 300 pixes tall - `crop=1`: crops the image to the region of the detection rather than returning the entire image -### `/api//latest.jpg[?h=300]` +### `GET /api//latest.jpg[?h=300]` The most recent frame that frigate has finished processing. It is a full resolution image by default. @@ -53,7 +53,7 @@ Example parameters: - `h=300`: resizes the image to 300 pixes tall -### `/api/stats` +### `GET /api/stats` Contains some granular debug info that can be used for sensors in HomeAssistant. @@ -150,15 +150,15 @@ Sample response: } ``` -### `/api/config` +### `GET /api/config` A json representation of your configuration -### `/api/version` +### `GET /api/version` Version info -### `/api/events` +### `GET /api/events` Events from the database. Accepts the following query string parameters: @@ -174,19 +174,23 @@ Events from the database. Accepts the following query string parameters: | `has_clip` | int | Filter to events that have clips (0 or 1) | | `include_thumbnails` | int | Include thumbnails in the response (0 or 1) | -### `/api/events/summary` +### `GET /api/events/summary` Returns summary data for events in the database. Used by the HomeAssistant integration. -### `/api/events/` +### `GET /api/events/` Returns data for a single event. -### `/api/events//thumbnail.jpg` +### `DELETE /api/events/` + +Permantly deletes the event along with any clips/snapshots. + +### `GET /api/events//thumbnail.jpg` Returns a thumbnail for the event id optimized for notifications. Works while the event is in progress and after completion. Passing `?format=android` will convert the thumbnail to 2:1 aspect ratio. -### `/api/events//snapshot.jpg` +### `GET /api/events//snapshot.jpg` Returns the snapshot image for the event id. Works while the event is in progress and after completion. diff --git a/frigate/http.py b/frigate/http.py index 4d818d37c..3ac622b91 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -5,6 +5,7 @@ import logging import os import time from functools import reduce +from pathlib import Path import cv2 import gevent @@ -145,13 +146,32 @@ def events_summary(): return jsonify([e for e in groups.dicts()]) -@bp.route('/events/') +@bp.route('/events/', methods=('GET',)) def event(id): try: return model_to_dict(Event.get(Event.id == id)) except DoesNotExist: return "Event not found", 404 +@bp.route('/events/', methods=('DELETE',)) +def delete_event(id): + try: + event = Event.get(Event.id == id) + except DoesNotExist: + return "Event not found", 404 + + media_name = f"{event.camera}-{event.id}" + if event.has_snapshot: + media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.jpg") + media.unlink(missing_ok=True) + if event.has_clip: + media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4") + media.unlink(missing_ok=True) + + event.delete_instance() + + return '', 204 + @bp.route('/events//thumbnail.jpg') def event_thumbnail(id): format = request.args.get('format', 'ios') diff --git a/web/src/routes/Events.jsx b/web/src/routes/Events.jsx index 76bce3d8e..8f7b10727 100644 --- a/web/src/routes/Events.jsx +++ b/web/src/routes/Events.jsx @@ -7,6 +7,8 @@ import produce from 'immer'; import { route } from 'preact-router'; import { useIntersectionObserver } from '../hooks'; import { FetchStatus, useApiHost, useConfig, useEvents } from '../api'; +import Button from '../components/Button'; +import Delete from '../icons/Delete' import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from '../components/Table'; import { useCallback, useEffect, useMemo, useReducer, useState } from 'preact/hooks'; @@ -99,6 +101,18 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) { [limit, pathname, setSearchString] ); + const handleDelete = useCallback( + async (eventId) => { + // eslint-disable-next-line no-alert + if(confirm('Are you sure you want to delete this event and any related clips and snapshots?')) { + await fetch(`${apiHost}/api/events/${eventId}`); + const { searchParams } = new URL(window.location); + handleFilter(searchParams) + } + }, + [apiHost, handleFilter] + ); + const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]); return ( @@ -119,6 +133,7 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) { Date Start End + @@ -179,6 +194,11 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) { {start.toLocaleDateString()} {start.toLocaleTimeString()} {end.toLocaleTimeString()} + + + ); }