Added frigate 0.13.1

This commit is contained in:
fleaz 2024-02-04 16:41:14 +01:00
parent 8d47f6b09c
commit 4e7c86505f
No known key found for this signature in database
GPG key ID: AED15F8FDD04D083
8 changed files with 807 additions and 0 deletions

View file

@ -6,9 +6,14 @@ self: super: {
packageOverrides = python-self: python-super: {
pytapo = python-self.callPackage ./pytapo { };
brother-ql = python-self.callPackage ./brother-ql { };
onvif-zeep = python-self.callPackage ./onvif-zeep { };
motmetrics = python-self.callPackage ./motmetrics { };
norfair = python-self.callPackage ./norfair { };
pydantic = python-super.pydantic_1;
};
};
frigate = super.callPackage ./frigate { };
#gotosocial = super.callPackage ./gotosocial { };
pulse-secure = super.callPackage ./pulse-secure { };
studio-link = super.callPackage ./studio-link { };

170
frigate/default.nix Normal file
View file

@ -0,0 +1,170 @@
{ lib
, callPackage
, python3
, fetchFromGitHub
, fetchurl
, frigate
, nixosTests
}:
let
version = "0.13.1";
src = fetchFromGitHub {
#name = "frigate-${version}-source";
owner = "blakeblackshear";
repo = "frigate";
rev = "refs/tags/v${version}";
hash = "sha256-2J7DhnYDX9ubbsk0qhji/vIKDouy9IqQztzbdPj2kxo=";
};
frigate-web = callPackage ./web.nix {
inherit version src;
};
python = python3.override {
packageOverrides = self: super: {
versioningit = super.versioningit.overridePythonAttrs {
# checkPhase requires pydantic>=2
doCheck = false;
};
onvif-zeep = self.callPackage ../onvif-zeep { };
norfair = self.callPackage ../norfair { };
};
};
# Tensorflow Lite models
# https://github.com/blakeblackshear/frigate/blob/v0.13.0/docker/main/Dockerfile#L96-L97
tflite_cpu_model = fetchurl {
url = "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite";
hash = "sha256-kLszpjTgQZFMwYGapd+ZgY5sOWxNLblSwP16nP/Eck8=";
};
tflite_edgetpu_model = fetchurl {
url = "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite";
hash = "sha256-Siviu7YU5XbVbcuRT6UnUr8PE0EVEnENNV2X+qGzVkE=";
};
# OpenVino models
# https://github.com/blakeblackshear/frigate/blob/v0.13.0/docker/main/Dockerfile#L101
openvino_model = fetchurl {
url = "https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt";
hash = "sha256-5Cj2vEiWR8Z9d2xBmVoLZuNRv4UOuxHSGZQWTJorXUQ=";
};
in
python.pkgs.buildPythonApplication rec {
pname = "frigate";
inherit version;
format = "other";
inherit src;
postPatch = ''
echo 'VERSION = "${version}"' > frigate/version.py
substituteInPlace frigate/app.py \
--replace "Router(migrate_db)" 'Router(migrate_db, "${placeholder "out"}/share/frigate/migrations")'
substituteInPlace frigate/const.py \
--replace "/media/frigate" "/var/lib/frigate" \
--replace "/tmp/cache" "/var/cache/frigate" \
--replace "/config" "/var/lib/frigate" \
--replace "{CONFIG_DIR}/model_cache" "/var/cache/frigate/model_cache"
substituteInPlace frigate/http.py \
--replace "/opt/frigate" "${placeholder "out"}/${python.sitePackages}"
substituteInPlace frigate/output.py \
--replace "/opt/frigate" "${placeholder "out"}/${python.sitePackages}"
substituteInPlace frigate/detectors/detector_config.py \
--replace "/labelmap.txt" "${placeholder "out"}/share/frigate/labelmap.txt"
substituteInPlace frigate/config.py \
--replace "/cpu_model.tflite" "${tflite_cpu_model}" \
--replace "/edgetpu_model.tflite" "${tflite_edgetpu_model}"
substituteInPlace frigate/test/test_config.py \
--replace "(MODEL_CACHE_DIR" "('/build/model_cache'" \
--replace "/config/model_cache" "/build/model_cache"
'';
dontBuild = true;
propagatedBuildInputs = with python.pkgs; [
# docker/main/requirements.txt
scikit-build
# docker/main/requirements-wheel.txt
click
flask
imutils
matplotlib
norfair
numpy
onvif-zeep
opencv4
openvino
paho-mqtt
peewee
peewee-migrate
psutil
py3nvml
pydantic
pytz
pyyaml
requests
ruamel-yaml
scipy
setproctitle
tensorflow
tzlocal
unidecode
ws4py
];
installPhase = ''
runHook preInstall
mkdir -p $out/${python.sitePackages}/frigate
cp -R frigate/* $out/${python.sitePackages}/frigate/
mkdir -p $out/share/frigate
cp -R {migrations,labelmap.txt} $out/share/frigate/
cp --no-preserve=mode ${openvino_model} $out/share/frigate/coco_91cl_bkgr.txt
sed -i 's/truck/car/g' $out/share/frigate/coco_91cl_bkgr.txt
runHook postInstall
'';
nativeCheckInputs = with python.pkgs; [
pytestCheckHook
];
disabledTests = [
# Test needs network access
"test_plus_labelmap"
];
passthru = {
web = frigate-web;
inherit python;
pythonPath =(python.pkgs.makePythonPath propagatedBuildInputs) + ":${frigate}/${python.sitePackages}";
tests = {
inherit (nixosTests) frigate;
};
};
meta = with lib; {
changelog = "https://github.com/blakeblackshear/frigate/releases/tag/v${version}";
description = "NVR with realtime local object detection for IP cameras";
longDescription = ''
A complete and local NVR designed for Home Assistant with AI
object detection. Uses OpenCV and Tensorflow to perform realtime
object detection locally for IP cameras.
'';
homepage = "https://github.com/blakeblackshear/frigate";
license = licenses.mit;
maintainers = with maintainers; [ hexa ];
};
}

26
frigate/web.nix Normal file
View file

@ -0,0 +1,26 @@
{ buildNpmPackage
, src
, version
}:
buildNpmPackage {
pname = "frigate-web";
inherit version src;
sourceRoot = "${src.name}/web";
postPatch = ''
substituteInPlace package.json \
--replace "--base=/BASE_PATH/" ""
substituteInPlace src/routes/Storage.jsx \
--replace "/media/frigate" "/var/lib/frigate" \
--replace "/tmp/cache" "/var/cache/frigate"
'';
npmDepsHash = "sha256-+36quezGArqIM9dM+UihwcIgmE3EVmJQThuicLgDW4A=";
installPhase = ''
cp -rv dist/ $out
'';
}

View file

@ -2,5 +2,6 @@
imports = [
#./gotosocial.nix
./mealie.nix
#./frigate.nix
];
}

438
modules/frigate.nix Normal file
View file

@ -0,0 +1,438 @@
{ config
, lib
, pkgs
, ...
}:
let
inherit (lib)
literalExpression
mkDefault
mdDoc
mkEnableOption
mkPackageOption
mkIf
mkOption
types;
cfg = config.services.frigate;
format = pkgs.formats.yaml { };
filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! lib.elem v [ null ])) cfg.settings;
cameraFormat = with types; submodule {
freeformType = format.type;
options = {
ffmpeg = {
inputs = mkOption {
description = mdDoc ''
List of inputs for this camera.
'';
type = listOf (submodule {
freeformType = format.type;
options = {
path = mkOption {
type = str;
example = "rtsp://192.0.2.1:554/rtsp";
description = mdDoc ''
Stream URL
'';
};
roles = mkOption {
type = listOf (enum [ "detect" "record" "rtmp" ]);
example = literalExpression ''
[ "detect" "rtmp" ]
'';
description = mdDoc ''
List of roles for this stream
'';
};
};
});
};
};
};
};
in
{
meta.buildDocsInSandbox = false;
options.services.frigate = with types; {
enable = mkEnableOption (mdDoc "Frigate NVR");
package = mkPackageOption pkgs "frigate" { };
hostname = mkOption {
type = str;
example = "frigate.exampe.com";
description = mdDoc ''
Hostname of the nginx vhost to configure.
Only nginx is supported by upstream for direct reverse proxying.
'';
};
settings = mkOption {
type = submodule {
freeformType = format.type;
options = {
cameras = mkOption {
type = attrsOf cameraFormat;
description = mdDoc ''
Attribute set of cameras configurations.
https://docs.frigate.video/configuration/cameras
'';
};
database = {
path = mkOption {
type = path;
default = "/var/lib/frigate/frigate.db";
description = mdDoc ''
Path to the SQLite database used
'';
};
};
mqtt = {
enabled = mkEnableOption (mdDoc "MQTT support");
host = mkOption {
type = nullOr str;
default = null;
example = "mqtt.example.com";
description = mdDoc ''
MQTT server hostname
'';
};
};
};
};
default = { };
description = mdDoc ''
Frigate configuration as a nix attribute set.
See the project documentation for how to configure frigate.
- [Creating a config file](https://docs.frigate.video/guides/getting_started)
- [Configuration reference](https://docs.frigate.video/configuration/index)
'';
};
};
config = mkIf cfg.enable {
services.nginx = {
enable = true;
additionalModules = with pkgs.nginxModules; [
secure-token
rtmp
vod
];
recommendedProxySettings = mkDefault true;
recommendedGzipSettings = mkDefault true;
mapHashBucketSize = mkDefault 128;
upstreams = {
frigate-api.servers = {
"127.0.0.1:5001" = { };
};
frigate-mqtt-ws.servers = {
"127.0.0.1:5002" = { };
};
frigate-jsmpeg.servers = {
"127.0.0.1:8082" = { };
};
frigate-go2rtc.servers = {
"127.0.0.1:1984" = { };
};
};
proxyCachePath."frigate" = {
enable = true;
keysZoneSize = "10m";
keysZoneName = "api_cache";
maxSize = "10m";
inactive = "1m";
levels = "1:2";
};
# Based on https://github.com/blakeblackshear/frigate/blob/v0.12.0/docker/rootfs/usr/local/nginx/conf/nginx.conf
virtualHosts."${cfg.hostname}" = {
locations = {
"/api/" = {
proxyPass = "http://frigate-api/";
extraConfig = ''
proxy_cache api_cache;
proxy_cache_lock on;
proxy_cache_use_stale updating;
proxy_cache_valid 200 5s;
proxy_cache_bypass $http_x_cache_bypass;
proxy_no_cache $should_not_cache;
add_header X-Cache-Status $upstream_cache_status;
location /api/vod/ {
proxy_pass http://frigate-api/vod/;
proxy_cache off;
}
location /api/stats {
access_log off;
rewrite ^/api/(.*)$ $1 break;
proxy_pass http://frigate-api;
}
location /api/version {
access_log off;
rewrite ^/api/(.*)$ $1 break;
proxy_pass http://frigate-api;
}
'';
};
"~* /api/.*\.(jpg|jpeg|png)$" = {
proxyPass = "http://frigate-api";
extraConfig = ''
rewrite ^/api/(.*)$ $1 break;
'';
};
"/vod/" = {
extraConfig = ''
aio threads;
vod hls;
secure_token $args;
secure_token_types application/vnd.apple.mpegurl;
add_header Cache-Control "no-store";
expires off;
'';
};
"/stream/" = {
# TODO
};
"/ws" = {
proxyPass = "http://frigate-mqtt-ws/";
proxyWebsockets = true;
};
"/live/jsmpeg" = {
proxyPass = "http://frigate-jsmpeg/";
proxyWebsockets = true;
};
"/live/mse/" = {
proxyPass = "http://frigate-go2rtc/";
proxyWebsockets = true;
};
# frigate lovelace card uses this path
"/live/mse/api/ws" = {
proxyPass = "http://frigate-go2rtc/api/ws";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/live/webrtc/" = {
proxyPass = "http://frigate-go2rtc/";
proxyWebsockets = true;
};
"/live/webrtc/api/ws" = {
proxyPass = "http://frigate-go2rtc/api/ws";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
# pass through go2rtc player
"/live/webrtc/webrtc.html" = {
proxyPass = "http://frigate-go2rtc/webrtc.html";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/api/go2rtc/api" = {
proxyPass = "http://frigate-go2rtc/api";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
# integrationn uses this to add webrtc candidate
"/api/go2rtc/webrtc" = {
proxyPass = "http://frigate-go2rtc/api/webrtc";
proxyWebsockets = true;
extraConfig = ''
limit_except GET {
deny all;
}
'';
};
"/cache/" = {
alias = "/var/cache/frigate/";
};
"/clips/" = {
root = "/var/lib/frigate";
extraConfig = ''
types {
video/mp4 mp4;
image/jpeg jpg;
}
autoindex on;
'';
};
"/recordings/" = {
root = "/var/lib/frigate";
extraConfig = ''
types {
video/mp4 mp4;
}
autoindex on;
autoindex_format json;
'';
};
"/assets/" = {
root = cfg.package.web;
extraConfig = ''
access_log off;
expires 1y;
add_header Cache-Control "public";
'';
};
"/" = {
root = cfg.package.web;
tryFiles = "$uri $uri/ /index.html";
extraConfig = ''
add_header Cache-Control "no-store";
expires off;
sub_filter 'href="/BASE_PATH/' 'href="$http_x_ingress_path/';
sub_filter 'url(/BASE_PATH/' 'url($http_x_ingress_path/';
sub_filter '"/BASE_PATH/dist/' '"$http_x_ingress_path/dist/';
sub_filter '"/BASE_PATH/js/' '"$http_x_ingress_path/js/';
sub_filter '"/BASE_PATH/assets/' '"$http_x_ingress_path/assets/';
sub_filter '"/BASE_PATH/monacoeditorwork/' '"$http_x_ingress_path/assets/';
sub_filter 'return"/BASE_PATH/"' 'return window.baseUrl';
sub_filter '<body>' '<body><script>window.baseUrl="$http_x_ingress_path/";</script>';
sub_filter_types text/css application/javascript;
sub_filter_once off;
'';
};
};
extraConfig = ''
# vod settings
vod_base_url "";
vod_segments_base_url "";
vod_mode mapped;
vod_max_mapping_response_size 1m;
vod_upstream_location /api;
vod_align_segments_to_key_frames on;
vod_manifest_segment_durations_mode accurate;
vod_ignore_edit_list on;
vod_segment_duration 10000;
vod_hls_mpegts_align_frames off;
vod_hls_mpegts_interleave_frames on;
# file handle caching / aio
open_file_cache max=1000 inactive=5m;
open_file_cache_valid 2m;
open_file_cache_min_uses 1;
open_file_cache_errors on;
aio on;
# https://github.com/kaltura/nginx-vod-module#vod_open_file_thread_pool
vod_open_file_thread_pool default;
# vod caches
vod_metadata_cache metadata_cache 512m;
vod_mapping_cache mapping_cache 5m 10m;
# gzip manifest
gzip_types application/vnd.apple.mpegurl;
'';
};
appendConfig = ''
rtmp {
server {
listen 1935;
chunk_size 4096;
allow publish 127.0.0.1;
deny publish all;
allow play all;
application live {
live on;
record off;
meta copy;
}
}
}
'';
appendHttpConfig = ''
map $sent_http_content_type $should_not_cache {
'application/json' 0;
default 1;
}
'';
};
systemd.services.nginx.serviceConfig.SupplementaryGroups = [
"frigate"
];
users.users.frigate = {
isSystemUser = true;
group = "frigate";
};
users.groups.frigate = { };
systemd.services.frigate = {
after = [
"go2rtc.service"
"network.target"
];
wantedBy = [
"multi-user.target"
];
environment = {
CONFIG_FILE = format.generate "frigate.yml" filteredConfig;
HOME = "/var/lib/frigate";
PYTHONPATH = cfg.package.pythonPath;
};
path = with pkgs; [
# unfree:
# config.boot.kernelPackages.nvidiaPackages.latest.bin
ffmpeg_5-headless
libva-utils
procps
radeontop
] ++ lib.optionals (!stdenv.isAarch64) [
# not available on aarch64-linux
intel-gpu-tools
];
serviceConfig = {
ExecStart = "${cfg.package.python.interpreter} -m frigate";
Restart = "on-failure";
User = "frigate";
Group = "frigate";
UMask = "0027";
StateDirectory = "frigate";
StateDirectoryMode = "0750";
# Caches
PrivateTmp = true;
CacheDirectory = "frigate";
CacheDirectoryMode = "0750";
BindPaths = [
"/migrations:${cfg.package}/share/frigate/migrations:ro"
];
};
};
};
}

58
motmetrics/default.nix Normal file
View file

@ -0,0 +1,58 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
# build-system
, setuptools
# dependencies
, numpy
, pandas
, scipy
, xmltodict
# tests
, pytestCheckHook
, pytest-benchmark
}:
buildPythonPackage rec {
pname = "motmetrics";
version = "1.4.0-unstable-20240130";
pyproject = true;
src = fetchFromGitHub {
owner = "cheind";
repo = "py-motmetrics";
# latest release is not compatible with pandas 2.0
rev = "7210fcce0be1b76c96a62f6fe4ddbc90d944eacb";
hash = "sha256-7LKLHXWgW4QpivAgzvWl6qEG0auVvpiZ6bfDViCKsFY=";
};
nativeBuildInputs = [
setuptools
];
propagatedBuildInputs = [
numpy
pandas
scipy
xmltodict
];
nativeCheckInputs = [
pytestCheckHook
pytest-benchmark
];
pythonImportsCheck = [
"motmetrics"
];
meta = with lib; {
description = "Bar_chart: Benchmark multiple object trackers (MOT) in Python";
homepage = "https://github.com/cheind/py-motmetrics";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
}

69
norfair/default.nix Normal file
View file

@ -0,0 +1,69 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
, poetry-core
, filterpy
, importlib-metadata
, numpy
, rich
, scipy
, opencv4
, pytestCheckHook
, pythonRelaxDepsHook
, python3
}:
buildPythonPackage rec {
pname = "norfair";
version = "2.2.0";
pyproject = true;
src = fetchFromGitHub {
owner = "tryolabs";
repo = "norfair";
rev = "v${version}";
hash = "sha256-aKB5TYSLW7FOXIy9u2hK7px6eEmIQdKPrhChKaU1uYs=";
};
nativeBuildInputs = [
poetry-core
pythonRelaxDepsHook
];
pythonRelaxDeps = [
"rich"
];
propagatedBuildInputs = [
filterpy
importlib-metadata
numpy
rich
scipy
];
passthru.optional-dependencies = {
metrics = [
python3.pkgs.motmetrics
];
video = [
opencv4
];
};
nativeCheckInputs = [
pytestCheckHook
];
pythonImportsCheck = [
"norfair"
];
meta = with lib; {
description = "Lightweight Python library for adding real-time multi-object tracking to any detector";
changelog = "https://github.com/tryolabs/norfair/releases/tag/v${version}";
homepage = "https://github.com/tryolabs/norfair";
license = licenses.bsd3;
maintainers = with maintainers; [ fleaz ];
};
}

40
onvif-zeep/default.nix Normal file
View file

@ -0,0 +1,40 @@
{ lib
, buildPythonPackage
, fetchPypi
, setuptools
, zeep
}:
buildPythonPackage rec {
pname = "onvif-zeep";
version = "0.2.12";
pyproject = true;
src = fetchPypi {
pname = "onvif_zeep";
inherit version;
hash = "sha256-qou8Aqc+qlCJSwwY45+o0xilg6ZkxlvzWzyAKdHEC0k=";
};
nativeBuildInputs = [
setuptools
];
propagatedBuildInputs = [
zeep
];
pythonImportsCheck = [
"onvif"
];
# Tests require hardware
doCheck = false;
meta = with lib; {
description = "Python Client for ONVIF Camera";
homepage = "https://github.com/quatanium/python-onvif";
license = licenses.mit;
maintainers = with maintainers; [ fleaz ];
};
}