Allow deleting events and associated clips and snapshots from the event list view

This commit is contained in:
Scott Roach 2021-02-27 17:34:35 -08:00
parent 5043040530
commit 1ef0d99790
No known key found for this signature in database
GPG Key ID: 641478CF54A92761
3 changed files with 56 additions and 12 deletions

View File

@ -5,7 +5,7 @@ title: HTTP API
A web server is available on port 5000 with the following endpoints.
### `/api/<camera_name>`
### `GET /api/<camera_name>`
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/<camera_name>/<object_name>/best.jpg[?h=300&crop=1]`
### `GET /api/<camera_name>/<object_name>/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/<camera_name>/latest.jpg[?h=300]`
### `GET /api/<camera_name>/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/<id>`
### `GET /api/events/<id>`
Returns data for a single event.
### `/api/events/<id>/thumbnail.jpg`
### `DELETE /api/events/<id>`
Permantly deletes the event along with any clips/snapshots.
### `GET /api/events/<id>/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/<id>/snapshot.jpg`
### `GET /api/events/<id>/snapshot.jpg`
Returns the snapshot image for the event id. Works while the event is in progress and after completion.

View File

@ -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/<id>')
@bp.route('/events/<id>', 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/<id>', 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/<id>/thumbnail.jpg')
def event_thumbnail(id):
format = request.args.get('format', 'ios')

View File

@ -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 } = {}) {
<Th>Date</Th>
<Th>Start</Th>
<Th>End</Th>
<Th />
</Tr>
</Thead>
<Tbody>
@ -179,6 +194,11 @@ export default function Events({ path: pathname, limit = API_LIMIT } = {}) {
<Td>{start.toLocaleDateString()}</Td>
<Td>{start.toLocaleTimeString()}</Td>
<Td>{end.toLocaleTimeString()}</Td>
<Td>
<Button color="red" name="Delete" onClick={() => handleDelete(id)}>
<Delete className="w-6" />
</Button>
</Td>
</Tr>
);
}