forked from Github/frigate
Compare commits
4 Commits
v0.11.0-be
...
v0.11.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef54cd6fb3 | ||
|
|
c2465a46a8 | ||
|
|
24d3a9cdd5 | ||
|
|
5e82eaed88 |
3
.github/workflows/pull_request.yml
vendored
3
.github/workflows/pull_request.yml
vendored
@@ -54,6 +54,7 @@ jobs:
|
||||
|
||||
python_tests:
|
||||
runs-on: ubuntu-latest
|
||||
name: Python Tests
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
@@ -69,6 +70,8 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Create Version Module
|
||||
run: make version
|
||||
- name: Build
|
||||
run: make
|
||||
- name: Run mypy
|
||||
|
||||
@@ -46,7 +46,7 @@ RUN pip3 wheel --wheel-dir=/wheels -r requirements-wheels.txt
|
||||
FROM debian:11-slim
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG JELLYFIN_FFMPEG_VERSION=4.3.2-1
|
||||
ARG JELLYFIN_FFMPEG_VERSION=4.4.1-4
|
||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||
ARG DEBIAN_FRONTEND="noninteractive"
|
||||
# http://stackoverflow.com/questions/48162574/ddg#49462622
|
||||
|
||||
@@ -183,6 +183,10 @@ Permanently deletes the event along with any clips/snapshots.
|
||||
|
||||
Sets retain to true for the event id.
|
||||
|
||||
### `POST /api/events/<id>/plus`
|
||||
|
||||
Submits the snapshot of the event to Frigate+ for labeling.
|
||||
|
||||
### `DELETE /api/events/<id>/retain`
|
||||
|
||||
Sets retain to false for the event id (event may be deleted quickly after removing).
|
||||
|
||||
@@ -256,7 +256,10 @@ def get_sub_labels():
|
||||
)
|
||||
|
||||
sub_labels = [e.sub_label for e in events]
|
||||
sub_labels.remove(None)
|
||||
|
||||
if None in sub_labels:
|
||||
sub_labels.remove(None)
|
||||
|
||||
return jsonify(sub_labels)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from frigate.const import PLUS_ENV_VAR, PLUS_API_HOST
|
||||
from requests.models import Response
|
||||
@@ -28,10 +30,23 @@ def get_jpg_bytes(image: ndarray, max_dim: int, quality: int) -> bytes:
|
||||
class PlusApi:
|
||||
def __init__(self) -> None:
|
||||
self.host = PLUS_API_HOST
|
||||
self.key = None
|
||||
if PLUS_ENV_VAR in os.environ:
|
||||
self.key = os.environ.get(PLUS_ENV_VAR)
|
||||
else:
|
||||
# check for the addon options file
|
||||
elif os.path.isfile("/data/options.json"):
|
||||
with open("/data/options.json") as f:
|
||||
raw_options = f.read()
|
||||
options = json.loads(raw_options)
|
||||
self.key = options.get("plus_api_key")
|
||||
|
||||
if self.key is not None and not re.match(
|
||||
r"[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}:[a-z0-9]{40}",
|
||||
self.key,
|
||||
):
|
||||
logger.error("Plus API Key is not formatted correctly.")
|
||||
self.key = None
|
||||
|
||||
self._is_active: bool = self.key is not None
|
||||
self._token_data: dict = {}
|
||||
|
||||
|
||||
4
frigate/test/const.py
Normal file
4
frigate/test/const.py
Normal file
@@ -0,0 +1,4 @@
|
||||
"""Consts for testing."""
|
||||
|
||||
TEST_DB = "test.db"
|
||||
TEST_DB_CLEANUPS = ["test.db", "test.db-shm", "test.db-wal"]
|
||||
327
frigate/test/test_http.py
Normal file
327
frigate/test/test_http.py
Normal file
@@ -0,0 +1,327 @@
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from peewee_migrate import Router
|
||||
from playhouse.sqlite_ext import SqliteExtDatabase
|
||||
from playhouse.sqliteq import SqliteQueueDatabase
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.http import create_app
|
||||
from frigate.models import Event, Recordings
|
||||
|
||||
from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS
|
||||
|
||||
|
||||
class TestHttp(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# setup clean database for each test run
|
||||
migrate_db = SqliteExtDatabase("test.db")
|
||||
del logging.getLogger("peewee_migrate").handlers[:]
|
||||
router = Router(migrate_db)
|
||||
router.run()
|
||||
migrate_db.close()
|
||||
self.db = SqliteQueueDatabase(TEST_DB)
|
||||
models = [Event, Recordings]
|
||||
self.db.bind(models)
|
||||
|
||||
self.minimal_config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
"cameras": {
|
||||
"front_door": {
|
||||
"ffmpeg": {
|
||||
"inputs": [
|
||||
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
|
||||
]
|
||||
},
|
||||
"detect": {
|
||||
"height": 1080,
|
||||
"width": 1920,
|
||||
"fps": 5,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
self.test_stats = {
|
||||
"detection_fps": 13.7,
|
||||
"detectors": {
|
||||
"cpu1": {
|
||||
"detection_start": 0.0,
|
||||
"inference_speed": 91.43,
|
||||
"pid": 42,
|
||||
},
|
||||
"cpu2": {
|
||||
"detection_start": 0.0,
|
||||
"inference_speed": 84.99,
|
||||
"pid": 44,
|
||||
},
|
||||
},
|
||||
"front_door": {
|
||||
"camera_fps": 0.0,
|
||||
"capture_pid": 53,
|
||||
"detection_fps": 0.0,
|
||||
"pid": 52,
|
||||
"process_fps": 0.0,
|
||||
"skipped_fps": 0.0,
|
||||
},
|
||||
"service": {
|
||||
"storage": {
|
||||
"/dev/shm": {
|
||||
"free": 50.5,
|
||||
"mount_type": "tmpfs",
|
||||
"total": 67.1,
|
||||
"used": 16.6,
|
||||
},
|
||||
"/media/frigate/clips": {
|
||||
"free": 42429.9,
|
||||
"mount_type": "ext4",
|
||||
"total": 244529.7,
|
||||
"used": 189607.0,
|
||||
},
|
||||
"/media/frigate/recordings": {
|
||||
"free": 0.2,
|
||||
"mount_type": "ext4",
|
||||
"total": 8.0,
|
||||
"used": 7.8,
|
||||
},
|
||||
"/tmp/cache": {
|
||||
"free": 976.8,
|
||||
"mount_type": "tmpfs",
|
||||
"total": 1000.0,
|
||||
"used": 23.2,
|
||||
},
|
||||
},
|
||||
"uptime": 101113,
|
||||
"version": "0.10.1",
|
||||
"latest_version": "0.11",
|
||||
},
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
if not self.db.is_closed():
|
||||
self.db.close()
|
||||
|
||||
try:
|
||||
for file in TEST_DB_CLEANUPS:
|
||||
os.remove(file)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_get_event_list(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
id2 = "7890.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
events = client.get(f"/events").json
|
||||
assert events
|
||||
assert len(events) == 1
|
||||
assert events[0]["id"] == id
|
||||
_insert_mock_event(id2)
|
||||
events = client.get(f"/events").json
|
||||
assert events
|
||||
assert len(events) == 2
|
||||
events = client.get(
|
||||
f"/events",
|
||||
query_string={"limit": 1},
|
||||
).json
|
||||
assert events
|
||||
assert len(events) == 1
|
||||
events = client.get(
|
||||
f"/events",
|
||||
query_string={"has_clip": 0},
|
||||
).json
|
||||
assert not events
|
||||
|
||||
def test_get_good_event(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
event = client.get(f"/events/{id}").json
|
||||
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
assert event == model_to_dict(Event.get(Event.id == id))
|
||||
|
||||
def test_get_bad_event(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
bad_id = "654321.other"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
event = client.get(f"/events/{bad_id}").json
|
||||
|
||||
assert not event
|
||||
|
||||
def test_delete_event(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
client.delete(f"/events/{id}")
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert not event
|
||||
|
||||
def test_event_retention(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
client.post(f"/events/{id}/retain")
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
assert event["retain_indefinitely"] == True
|
||||
client.delete(f"/events/{id}/retain")
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
assert event["retain_indefinitely"] == False
|
||||
|
||||
def test_set_delete_sub_label(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
sub_label = "sub"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
client.post(
|
||||
f"/events/{id}/sub_label",
|
||||
data=json.dumps({"subLabel": sub_label}),
|
||||
content_type="application/json",
|
||||
)
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
assert event["sub_label"] == sub_label
|
||||
client.post(
|
||||
f"/events/{id}/sub_label",
|
||||
data=json.dumps({"subLabel": ""}),
|
||||
content_type="application/json",
|
||||
)
|
||||
event = client.get(f"/events/{id}").json
|
||||
assert event
|
||||
assert event["id"] == id
|
||||
assert event["sub_label"] == ""
|
||||
|
||||
def test_sub_label_list(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config), self.db, None, None, None
|
||||
)
|
||||
id = "123456.random"
|
||||
sub_label = "sub"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_event(id)
|
||||
client.post(
|
||||
f"/events/{id}/sub_label",
|
||||
data=json.dumps({"subLabel": sub_label}),
|
||||
content_type="application/json",
|
||||
)
|
||||
sub_labels = client.get("/sub_labels").json
|
||||
assert sub_labels
|
||||
assert sub_labels == [sub_label]
|
||||
|
||||
def test_config(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config).runtime_config,
|
||||
self.db,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
with app.test_client() as client:
|
||||
config = client.get("/config").json
|
||||
assert config
|
||||
assert config["cameras"]["front_door"]
|
||||
|
||||
def test_recordings(self):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config).runtime_config,
|
||||
self.db,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
id = "123456.random"
|
||||
|
||||
with app.test_client() as client:
|
||||
_insert_mock_recording(id)
|
||||
recording = client.get("/front_door/recordings").json
|
||||
assert recording
|
||||
assert recording[0]["id"] == id
|
||||
|
||||
@patch("frigate.http.stats_snapshot")
|
||||
def test_stats(self, mock_stats):
|
||||
app = create_app(
|
||||
FrigateConfig(**self.minimal_config).runtime_config,
|
||||
self.db,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
mock_stats.return_value = self.test_stats
|
||||
|
||||
with app.test_client() as client:
|
||||
stats = client.get("/stats").json
|
||||
assert stats == self.test_stats
|
||||
|
||||
|
||||
def _insert_mock_event(id: str) -> Event:
|
||||
"""Inserts a basic event model with a given id."""
|
||||
return Event.insert(
|
||||
id=id,
|
||||
label="Mock",
|
||||
camera="front_door",
|
||||
start_time=datetime.datetime.now().timestamp(),
|
||||
end_time=datetime.datetime.now().timestamp() + 20,
|
||||
top_score=100,
|
||||
false_positive=False,
|
||||
zones=list(),
|
||||
thumbnail="",
|
||||
region=[],
|
||||
box=[],
|
||||
area=0,
|
||||
has_clip=True,
|
||||
has_snapshot=True,
|
||||
).execute()
|
||||
|
||||
|
||||
def _insert_mock_recording(id: str) -> Event:
|
||||
"""Inserts a basic recording model with a given id."""
|
||||
return Recordings.insert(
|
||||
id=id,
|
||||
camera="front_door",
|
||||
path=f"/recordings/{id}",
|
||||
start_time=datetime.datetime.now().timestamp() - 50,
|
||||
end_time=datetime.datetime.now().timestamp() - 60,
|
||||
duration=10,
|
||||
motion=True,
|
||||
objects=True,
|
||||
).execute()
|
||||
Reference in New Issue
Block a user