Merge pull request #214428 from mweinelt/frigate-init

frigate: init at 0.12.0
This commit is contained in:
0x4A6F 2023-05-22 18:29:37 +02:00 committed by GitHub
commit afad0c152b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 645 additions and 5 deletions

View File

@ -54,6 +54,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [system-repart](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html), grow and add partitions to a partition table. Available as [systemd.repart](options.html#opt-systemd.repart) and [boot.initrd.systemd.repart](options.html#opt-boot.initrd.systemd.repart)
- [frigate](https://frigate.video), an open source NVR built around real-time AI object detection. Available as [services.frigate](#opt-services.frigate.enable).
- [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).
- [readarr](https://github.com/Readarr/Readarr), Book Manager and Automation (Sonarr for Ebooks). Available as [services.readarr](options.html#opt-services.readarr.enable).

View File

@ -1151,6 +1151,7 @@
./services/ttys/kmscon.nix
./services/video/epgstation/default.nix
./services/video/go2rtc/default.nix
./services/video/frigate.nix
./services/video/mirakurun.nix
./services/video/replay-sorcery.nix
./services/video/mediamtx.nix

View File

@ -0,0 +1,368 @@
{ config
, lib
, pkgs
, ...
}:
let
inherit (lib)
literalExpression
mkDefault
mdDoc
mkEnableOption
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 = mkOption {
type = package;
default = pkgs.frigate;
description = mdDoc ''
The frigate package to use.
'';
};
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;
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" = {};
};
};
# 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/";
};
"~* /api/.*\.(jpg|jpeg|png)$" = {
proxyPass = "http://frigate-api";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
rewrite ^/api/(.*)$ $1 break;
'';
};
"/vod/" = {
extraConfig = ''
aio threads;
vod hls;
secure_token $args;
secure_token_types application/vnd.apple.mpegurl;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
add_header Access-Control-Allow-Origin '*';
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;
};
"/live/webrtc/" = {
proxyPass = "http://frigate-go2rtc/";
proxyWebsockets = true;
};
"/cache/" = {
alias = "/var/cache/frigate/";
};
"/clips/" = {
root = "/var/lib/frigate";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
types {
video/mp4 mp4;
image/jpeg jpg;
}
autoindex on;
'';
};
"/recordings/" = {
root = "/var/lib/frigate";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
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;
}
}
}
'';
};
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";
DynamicUser = true;
User = "frigate";
StateDirectory = "frigate";
UMask = "0077";
# Caches
PrivateTmp = true;
CacheDirectory = "frigate";
BindPaths = [
"/migrations:${cfg.package}/share/frigate/migrations:ro"
];
};
};
};
}

View File

@ -273,6 +273,7 @@ in {
freeswitch = handleTest ./freeswitch.nix {};
freshrss-sqlite = handleTest ./freshrss-sqlite.nix {};
freshrss-pgsql = handleTest ./freshrss-pgsql.nix {};
frigate = handleTest ./frigate.nix {};
frr = handleTest ./frr.nix {};
fsck = handleTest ./fsck.nix {};
fsck-systemd-stage-1 = handleTest ./fsck.nix { systemdStage1 = true; };

60
nixos/tests/frigate.nix Normal file
View File

@ -0,0 +1,60 @@
import ./make-test-python.nix ({ pkgs, lib, ...} :
{
name = "frigate";
meta.maintainers = with lib.maintainers; [ hexa ];
nodes = {
machine = { config, ... }: {
services.frigate = {
enable = true;
hostname = "localhost";
settings = {
mqtt.enabled = false;
cameras.test = {
ffmpeg = {
input_args = "-fflags nobuffer -strict experimental -fflags +genpts+discardcorrupt -r 10 -use_wallclock_as_timestamps 1";
inputs = [ {
path = "http://127.0.0.1:8080";
roles = [
"record"
];
} ];
};
};
record.enabled = true;
};
};
systemd.services.video-stream = {
description = "Start a test stream that frigate can capture";
before = [
"frigate.service"
];
wantedBy = [
"multi-user.target"
];
serviceConfig = {
DynamicUser = true;
ExecStart = "${lib.getBin pkgs.ffmpeg-headless}/bin/ffmpeg -re -f lavfi -i smptebars=size=800x600:rate=10 -f mpegts -listen 1 http://0.0.0.0:8080";
};
};
};
};
testScript = ''
start_all()
machine.wait_for_unit("frigate.service")
machine.wait_for_open_port(5001)
machine.succeed("curl http://localhost:5001")
machine.wait_for_file("/var/cache/frigate/test-*.mp4")
'';
})

View File

@ -0,0 +1,179 @@
{ lib
, callPackage
, python3
, fetchFromGitHub
, fetchurl
, fetchpatch
, frigate
, opencv4
, nixosTests
}:
let
version = "0.12.0";
src = fetchFromGitHub {
#name = "frigate-${version}-source";
owner = "blakeblackshear";
repo = "frigate";
rev = "refs/tags/v${version}";
hash = "sha256-kJ0MnmWThiFbXvrN+zL5pZHq+Ig3DhCc8wPlWX2+nP8=";
};
frigate-web = callPackage ./web.nix {
inherit version src;
};
python = python3.override {
packageOverrides = self: super: {
# https://github.com/blakeblackshear/frigate/blob/v0.12.0/requirements-wheels.txt#L7
opencv = super.toPythonModule ((opencv4.override {
enablePython = true;
pythonPackages = self;
}).overrideAttrs (oldAttrs: rec {
version = "4.5.5";
src = fetchFromGitHub {
owner = "opencv";
repo = "opencv";
rev = "refs/tags/${version}";
hash = "sha256-TJfzEAMh4JSshZ7oEZPgB59+NBACsj6Z5TCzVOBaEP4=";
};
contribSrc = fetchFromGitHub {
owner = "opencv";
repo = "opencv_contrib";
rev = "refs/tags/${version}";
hash = "sha256-skuH9GYg0mivGaJjxbggXk4x/0bbQISrAawA3ZUGfCk=";
};
postUnpack = ''
cp --no-preserve=mode -r "${contribSrc}/modules" "$NIX_BUILD_TOP/source/opencv_contrib"
'';
}));
};
};
# Tensorflow Lite models
# https://github.com/blakeblackshear/frigate/blob/v0.12.0/Dockerfile#L88-L91
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.12.0/Dockerfile#L92-L95
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;
patches = [
(fetchpatch {
# numpy 1.24 compat
url = "https://github.com/blakeblackshear/frigate/commit/cb73d0cd392990448811c7212bc5f09be411fc69.patch";
hash = "sha256-Spt7eRosmTN8zyJ2uVme5HPVy2TKgBtvbQ6tp6PaNac=";
})
];
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"
substituteInPlace frigate/detectors/detector_config.py \
--replace "/labelmap.txt" "${placeholder "out"}/share/frigate/labelmap.txt"
substituteInPlace frigate/detectors/plugins/edgetpu_tfl.py \
--replace "/edgetpu_model.tflite" "${tflite_edgetpu_model}"
substituteInPlace frigate/detectors/plugins/cpu_tfl.py \
--replace "/cpu_model.tflite" "${tflite_cpu_model}"
substituteInPlace frigate/ffmpeg_presets.py --replace \
'"-timeout" if os.path.exists(BTBN_PATH) else "-stimeout"' \
'"-timeout"'
'';
dontBuild = true;
propagatedBuildInputs = with python.pkgs; [
# requirements.txt
scikit-build
# requirements-wheel.txt
click
flask
imutils
matplotlib
numpy
opencv
openvino
paho-mqtt
peewee
peewee-migrate
psutil
pydantic
pyyaml
requests
scipy
setproctitle
tensorflow
tzlocal
ws4py
zeroconf
];
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
'';
checkInputs = with python.pkgs; [
pytestCheckHook
];
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 ];
};
}

View File

@ -0,0 +1,26 @@
{ buildNpmPackage
, src
, version
}:
buildNpmPackage {
pname = "frigate-web";
inherit version src;
sourceRoot = "source/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-fvRxpQjSEzd2CnoEOVgQcB6MJJ4dcjN8bOaacHjCdwU=";
installPhase = ''
cp -rv dist/ $out
'';
}

View File

@ -662,14 +662,15 @@ let self = {
};
};
secure-token = {
secure-token = rec {
name = "secure-token";
version = "1.5";
src = fetchFromGitHub {
name = "secure-token";
owner = "kaltura";
repo = "nginx-secure-token-module";
rev = "95bdc0d1aca06ea7fe42555f71e65910bd74914d";
sha256 = "19wzck1xzq4kz7nyabcwzlank1k7wi7w2wn2c1mwz374c79g8ggp";
rev = "refs/tags/${version}";
hash = "sha256-qYTjGS9pykRqMFmNls52YKxEdXYhHw+18YC2zzdjEpU=";
};
inputs = [ openssl ];
@ -980,8 +981,8 @@ let self = {
name = "vod";
owner = "kaltura";
repo = "nginx-vod-module";
rev = "1.29";
sha256 = "1z0ka0cwqbgh3fv2d5yva395sf90626rdzx7lyfrgs89gy4h9nrr";
rev = "1.31";
hash = "sha256-ZpeO8QWQ+fGkz08u/zFOq7vj4aHcodzSHNrc1SgGUyc=";
};
inputs = [ ffmpeg fdk_aac openssl libxml2 libiconv ];

View File

@ -7720,6 +7720,8 @@ with pkgs;
inherit (darwin.apple_sdk.frameworks) AppKit CoreFoundation DiskArbitration Foundation IOKit;
};
frigate = callPackage ../applications/video/frigate { };
frostwire = callPackage ../applications/networking/p2p/frostwire { };
frostwire-bin = callPackage ../applications/networking/p2p/frostwire/frostwire-bin.nix { };