Unverified Commit 8863a9ab authored by Martin Weinelt's avatar Martin Weinelt Committed by GitHub
Browse files

frigate: 0.13.2 -> 0.14.1 (#337388)

parents 6acef7fd b51e706d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -201,6 +201,10 @@

- `pkgs.nextcloud27` has been removed since it's EOL.

- `frigate` was updated past 0.14.0. This release includes various breaking changes, so please go read the [release notes](https://github.com/blakeblackshear/frigate/releases/tag/v0.14.0).
   Most prominently access to the webinterface and API are now protected by authentication. Retrieve the auto-created
   admin account from the `frigate.service` journal after upgrading.

- `services.forgejo.mailerPasswordFile` has been deprecated by the drop-in replacement `services.forgejo.secrets.mailer.PASSWD`,
  which is part of the new free-form `services.forgejo.secrets` option.
  `services.forgejo.secrets` is a small wrapper over systemd's `LoadCredential=`. It has the same structure (sections/keys) as
+198 −84
Original line number Diff line number Diff line
@@ -39,10 +39,8 @@ let
                '';
              };
              roles = mkOption {
                type = listOf (enum [ "detect" "record" "rtmp" ]);
                example = literalExpression ''
                  [ "detect" "rtmp" ]
                '';
                type = listOf (enum [ "audio" "detect" "record" ]);
                example = [ "detect" "record" ];
                description = ''
                  List of roles for this stream
                '';
@@ -54,6 +52,48 @@ let
    };
  };

  # auth_request.conf
  nginxAuthRequest = ''
    # Send a subrequest to verify if the user is authenticated and has permission to access the resource.
    auth_request /auth;

    # Save the upstream metadata response headers from Authelia to variables.
    auth_request_set $user $upstream_http_remote_user;
    auth_request_set $groups $upstream_http_remote_groups;
    auth_request_set $name $upstream_http_remote_name;
    auth_request_set $email $upstream_http_remote_email;

    # Inject the metadata response headers from the variables into the request made to the backend.
    proxy_set_header Remote-User $user;
    proxy_set_header Remote-Groups $groups;
    proxy_set_header Remote-Email $email;
    proxy_set_header Remote-Name $name;

    # Refresh the cookie as needed
    auth_request_set $auth_cookie $upstream_http_set_cookie;
    add_header Set-Cookie $auth_cookie;

    # Pass the location header back up if it exists
    auth_request_set $redirection_url $upstream_http_location;
    add_header Location $redirection_url;
  '';

  nginxProxySettings = ''
    # Basic Proxy Configuration
    client_body_buffer_size 128k;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead.
    proxy_redirect  http://  $scheme://;
    proxy_cache_bypass $cookie_session;
    proxy_no_cache $cookie_session;
    proxy_buffers 64 256k;

    # Advanced Proxy Configuration
    send_timeout 5m;
    proxy_read_timeout 360;
    proxy_send_timeout 360;
    proxy_connect_timeout 360;
  '';

in

{
@@ -126,8 +166,9 @@ in
    services.nginx = {
      enable = true;
      additionalModules = with pkgs.nginxModules; [
        develkit
        secure-token
        rtmp
        set-misc
        vod
      ];
      recommendedProxySettings = mkDefault true;
@@ -158,43 +199,46 @@ in
      # Based on https://github.com/blakeblackshear/frigate/blob/v0.13.1/docker/main/rootfs/usr/local/nginx/conf/nginx.conf
      virtualHosts."${cfg.hostname}" = {
        locations = {
          "/api/" = {
            proxyPass = "http://frigate-api/";
            extraConfig = ''
              proxy_cache frigate_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";
          # auth_location.conf
          "/auth" = {
            proxyPass = "http://frigate-api/auth";
            extraConfig = ''
              rewrite ^/api/(.*)$ $1 break;
              internal;

              # Strip all request headers
              proxy_pass_request_headers off;

              # Pass info about the request
              proxy_set_header X-Original-Method $request_method;
              proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
              proxy_set_header X-Server-Port $server_port;
              proxy_set_header Content-Length "";

              # Pass along auth related info
              proxy_set_header Authorization $http_authorization;
              proxy_set_header Cookie $http_cookie;
              proxy_set_header X-CSRF-TOKEN "1";

              # Pass headers for common auth proxies
              proxy_set_header Remote-User $http_remote_user;
              proxy_set_header Remote-Groups $http_remote_groups;
              proxy_set_header Remote-Email $http_remote_email;
              proxy_set_header Remote-Name $http_remote_name;
              proxy_set_header X-Forwarded-User $http_x_forwarded_user;
              proxy_set_header X-Forwarded-Groups $http_x_forwarded_groups;
              proxy_set_header X-Forwarded-Email $http_x_forwarded_email;
              proxy_set_header X-Forwarded-Preferred-Username $http_x_forwarded_preferred_username;
              proxy_set_header X-authentik-username $http_x_authentik_username;
              proxy_set_header X-authentik-groups $http_x_authentik_groups;
              proxy_set_header X-authentik-email $http_x_authentik_email;
              proxy_set_header X-authentik-name $http_x_authentik_name;
              proxy_set_header X-authentik-uid $http_x_authentik_uid;

              ${nginxProxySettings}
            '';
          };
          "/vod/" = {
            extraConfig = ''
            extraConfig = nginxAuthRequest + ''
              aio threads;
              vod hls;

@@ -206,38 +250,84 @@ in
            '';
          };
          "/stream/" = {
            # TODO
            alias = "/var/cache/frigate/stream/";
            extraConfig = nginxAuthRequest + ''
              add_header Cache-Control "no-store";
              expires off;

              types {
                  application/dash+xml mpd;
                  application/vnd.apple.mpegurl m3u8;
                  video/mp2t ts;
                  image/jpeg jpg;
              }
            '';
          };
          "/clips/" = {
            root = "/var/lib/frigate";
            extraConfig = nginxAuthRequest + ''
              types {
                  video/mp4 mp4;
                  image/jpeg jpg;
              }

              expires 7d;
              add_header Cache-Control "public";
              autoindex on;
            '';
          };
          "/cache/" = {
            alias = "/var/cache/frigate/";
            extraConfig = ''
              internal;
            '';
          };
          "/recordings/" = {
            root = "/var/lib/frigate";
            extraConfig = nginxAuthRequest + ''
              types {
                  video/mp4 mp4;
              }

              autoindex on;
              autoindex_format json;
            '';
          };
          "/exports/" = {
            root = "/var/lib/frigate";
            extraConfig = nginxAuthRequest + ''
              types {
                video/mp4 mp4;
              }

              autoindex on;
              autoindex_format json;
            '';
          };
          "/ws" = {
            proxyPass = "http://frigate-mqtt-ws/";
            proxyWebsockets = true;
            extraConfig = nginxAuthRequest + nginxProxySettings;
          };
          "/live/jsmpeg" = {
            proxyPass = "http://frigate-jsmpeg/";
            proxyWebsockets = true;
          };
          "/live/mse/" = {
            proxyPass = "http://frigate-go2rtc/";
            proxyWebsockets = true;
            extraConfig = nginxAuthRequest + nginxProxySettings;
          };
          # frigate lovelace card uses this path
          "/live/mse/api/ws" = {
            proxyPass = "http://frigate-go2rtc/api/ws";
            proxyWebsockets = true;
            extraConfig = ''
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              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 = ''
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              limit_except GET {
                  deny  all;
              }
@@ -246,17 +336,16 @@ in
          # pass through go2rtc player
          "/live/webrtc/webrtc.html" = {
            proxyPass = "http://frigate-go2rtc/webrtc.html";
            proxyWebsockets = true;
            extraConfig = ''
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              limit_except GET {
                  deny  all;
              }
            '';
          };
          # frontend uses this to fetch the version
          "/api/go2rtc/api" = {
            proxyPass = "http://frigate-go2rtc/api";
            proxyWebsockets = true;
            extraConfig = ''
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              limit_except GET {
                  deny  all;
              }
@@ -266,35 +355,64 @@ in
          "/api/go2rtc/webrtc" = {
            proxyPass = "http://frigate-go2rtc/api/webrtc";
            proxyWebsockets = true;
            extraConfig = ''
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              limit_except GET {
                  deny  all;
              }
            '';
          };
          "/cache/" = {
            alias = "/var/cache/frigate/";
          "~* /api/.*\.(jpg|jpeg|png|webp|gif)$" = {
            proxyPass = "http://frigate-api";
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              rewrite ^/api/(.*)$ $1 break;
            '';
          };
          "/clips/" = {
            root = "/var/lib/frigate";
            extraConfig = ''
              types {
                  video/mp4 mp4;
                  image/jpeg jpg;
          "/api/" = {
            proxyPass = "http://frigate-api/";
            extraConfig = nginxAuthRequest + nginxProxySettings + ''
              add_header Cache-Control "no-store";
              expires off;

              proxy_cache frigate_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/ {
                  ${nginxAuthRequest}
                  proxy_pass http://frigate-api/vod/;
                  proxy_cache off;
                  add_header Cache-Control "no-store";
                  ${nginxProxySettings}
              }

              autoindex on;
            '';
          };
          "/recordings/" = {
            root = "/var/lib/frigate";
            extraConfig = ''
              types {
                  video/mp4 mp4;
              location /api/login {
                  auth_request off;
                  rewrite ^/api(/.*)$ $1 break;
                  proxy_pass http://frigate-api;
                  ${nginxProxySettings}
              }

              autoindex on;
              autoindex_format json;
              location /api/stats {
                  ${nginxAuthRequest}
                  access_log off;
                  rewrite ^/api/(.*)$ $1 break;
                  add_header Cache-Control "no-store";
                  proxy_pass http://frigate-api;
                  ${nginxProxySettings}
              }

              location /api/version {
                  ${nginxAuthRequest}
                  access_log off;
                  rewrite ^/api/(.*)$ $1 break;
                  add_header Cache-Control "no-store";
                  proxy_pass http://frigate-api;
                  ${nginxProxySettings}
              }
            '';
          };
          "/assets/" = {
@@ -307,21 +425,10 @@ in
          };
          "/" = {
            root = cfg.package.web;
            tryFiles = "$uri $uri/ /index.html";
            tryFiles = "$uri $uri.html $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;
            '';
          };
        };
@@ -338,17 +445,21 @@ in
          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;
        '';
@@ -427,6 +538,9 @@ in
        PrivateTmp = true;
        CacheDirectory = "frigate";
        CacheDirectoryMode = "0750";

        # Sockets/IPC
        RuntimeDirectory = "frigate";
      };
    };
  };
+14 −7
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
  meta.maintainers = with lib.maintainers; [ hexa ];

  nodes = {
    machine = { config, ... }: {
    machine = {
      services.frigate = {
        enable = true;

@@ -40,26 +40,33 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
        ];
        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";
          ExecStart = "${lib.getExe pkgs.ffmpeg-headless} -re -f lavfi -i smptebars=size=1280x720:rate=5 -f mpegts -listen 1 http://0.0.0.0:8080";
          Restart = "always";
        };
      };

      environment.systemPackages = with pkgs; [ httpie ];
    };
  };

  testScript = ''
    start_all()

    # wait until frigate is up
    machine.wait_for_unit("frigate.service")

    # Frigate startup
    machine.wait_for_open_port(5001)

    # nginx startup
    machine.wait_for_open_port(80)
    # extract admin password from logs
    machine.wait_until_succeeds("journalctl -u frigate.service -o cat | grep -q 'Password: '")
    password = machine.execute("journalctl -u frigate.service -o cat | grep -oP '([a-f0-9]{32})'")[1]

    # login and store session
    machine.log(machine.succeed(f"http --check-status --session=frigate post http://localhost/api/login user=admin password={password}"))

    machine.succeed("curl http://localhost")
    # make authenticated api requested
    machine.log(machine.succeed("http --check-status --session=frigate get http://localhost/api/version"))

    # wait for a recording to appear
    machine.wait_for_file("/var/cache/frigate/test@*.mp4")
  '';
})
+28 −50
Original line number Diff line number Diff line
{ lib
, callPackage
, python311
, python312
, fetchFromGitHub
, fetchurl
, fetchpatch2
, frigate
, nixosTests
}:

let
  version = "0.13.2";
  version = "0.14.1";

  src = fetchFromGitHub {
    #name = "frigate-${version}-source";
    name = "frigate-${version}-source";
    owner = "blakeblackshear";
    repo = "frigate";
    rev = "refs/tags/v${version}";
    hash = "sha256-NVT7yaJkVA7b7GL0S0fHjNneBzhjCru56qY1Q4sTVcE=";
    hash = "sha256-PfUlo9ua4SVcQJTfmSVoEXHH1MUJ8A/w3kJHFpEzll8=";
  };

  frigate-web = callPackage ./web.nix {
    inherit version src;
  };

  python = python311.override {
  python = python312.override {
    self = python;
    packageOverrides = self: super: {
      pydantic = super.pydantic_1;

      versioningit = super.versioningit.overridePythonAttrs {
        # checkPhase requires pydantic>=2
        doCheck = false;
      };
      paho-mqtt = super.paho-mqtt_2;
    };
  };

@@ -60,56 +54,35 @@ python.pkgs.buildPythonApplication rec {

  inherit src;

  patches = [
    (fetchpatch2 {
      name = "frigate-flask3.0-compat.patch";
      url = "https://github.com/blakeblackshear/frigate/commit/56bdacc1c661eff8a323e033520e75e2ba0a3842.patch";
      hash = "sha256-s/goUJxIbjq/woCEOEZECdcZoJDoWc1eM63sd60cxeY=";
    })
    (fetchpatch2 {
      # https://github.com/blakeblackshear/frigate/pull/10967
      name = "frigate-wsdl-path.patch";
      url = "https://github.com/blakeblackshear/frigate/commit/b65656fa8733c1c2f3d944f716d2e9493ae7c99f.patch";
      hash = "sha256-taPWFV4PldBGUKAwFMKag4W/3TLMSGdKLYG8bj1Y5mU=";
    })
    (fetchpatch2 {
      # https://github.com/blakeblackshear/frigate/pull/10097
      name = "frigate-secrets-permissionerror.patch";
      url = "https://github.com/blakeblackshear/frigate/commit/a1424bad6c0163e790129ade7a9784514d0bf89d.patch";
      hash = "sha256-/kIy4aW9o5AKHJQfCDVY46si+DKaUb+CsZsCGIbXvUQ=";
    })
    # https://github.com/blakeblackshear/frigate/pull/12324
    ./mpl-3.9.0.patch
  ];

  postPatch = ''
    echo 'VERSION = "${version}"' > frigate/version.py

    substituteInPlace frigate/app.py \
      --replace "Router(migrate_db)" 'Router(migrate_db, "${placeholder "out"}/share/frigate/migrations")'
      --replace-fail "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}"
      --replace-fail "/media/frigate" "/var/lib/frigate" \
      --replace-fail "/tmp/cache" "/var/cache/frigate" \
      --replace-fail "/config" "/var/lib/frigate" \
      --replace-fail "{CONFIG_DIR}/model_cache" "/var/cache/frigate/model_cache"

    substituteInPlace frigate/output.py \
      --replace "/opt/frigate" "${placeholder "out"}/${python.sitePackages}"
    substituteInPlace frigate/comms/{config,detections,events}_updater.py frigate/comms/inter_process.py \
      --replace-fail "ipc:///tmp/cache" "ipc:///run/frigate"

    substituteInPlace frigate/detectors/detector_config.py \
      --replace "/labelmap.txt" "${placeholder "out"}/share/frigate/labelmap.txt"
      --replace-fail "/labelmap.txt" "${placeholder "out"}/share/frigate/labelmap.txt"

    # work around onvif-zeep idiosyncrasy
    substituteInPlace frigate/ptz/onvif.py \
      --replace-fail dist-packages site-packages

    substituteInPlace frigate/config.py \
      --replace "/cpu_model.tflite" "${tflite_cpu_model}" \
      --replace "/edgetpu_model.tflite" "${tflite_edgetpu_model}"
      --replace-fail "/cpu_model.tflite" "${tflite_cpu_model}" \
      --replace-fail "/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"
      --replace-fail "(MODEL_CACHE_DIR" "('/build/model_cache'" \
      --replace-fail "/config/model_cache" "/build/model_cache"
  '';

  dontBuild = true;
@@ -119,8 +92,11 @@ python.pkgs.buildPythonApplication rec {
    scikit-build
    # docker/main/requirements-wheel.txt
    click
    distutils
    flask
    flask-limiter
    imutils
    joserfc
    markupsafe
    matplotlib
    norfair
@@ -128,6 +104,7 @@ python.pkgs.buildPythonApplication rec {
    onvif-zeep
    opencv4
    openvino
    pandas
    paho-mqtt
    peewee
    peewee-migrate
@@ -136,11 +113,12 @@ python.pkgs.buildPythonApplication rec {
    pydantic
    pytz
    pyyaml
    pyzmq
    requests
    ruamel-yaml
    scipy
    setproctitle
    tensorflow
    tensorflow-bin
    tzlocal
    unidecode
    ws4py
+0 −42
Original line number Diff line number Diff line
From fba8cff13186bd80ceaa06806392957598139deb Mon Sep 17 00:00:00 2001
From: Martin Weinelt <hexa@darmstadt.ccc.de>
Date: Sun, 7 Jul 2024 14:23:29 +0200
Subject: [PATCH] Fix colormap usage with matplotlib 3.9.0

The mpl.cm toplevel registration has been removed.

https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.9.0.html#top-level-cmap-registration-and-access-functions-in-mpl-cm
---
 frigate/config.py                    | 2 +-
 frigate/detectors/detector_config.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/frigate/config.py b/frigate/config.py
index 2e8b2570..af4f3263 100644
--- a/frigate/config.py
+++ b/frigate/config.py
@@ -807,7 +807,7 @@ class CameraConfig(FrigateBaseModel):
     def __init__(self, **config):
         # Set zone colors
         if "zones" in config:
-            colors = plt.cm.get_cmap("tab10", len(config["zones"]))
+            colors = plt.colormaps["tab10"].resampled(len(config["zones"]))
             config["zones"] = {
                 name: {**z, "color": tuple(round(255 * c) for c in colors(idx)[:3])}
                 for idx, (name, z) in enumerate(config["zones"].items())
diff --git a/frigate/detectors/detector_config.py b/frigate/detectors/detector_config.py
index 7fc958a3..b65631eb 100644
--- a/frigate/detectors/detector_config.py
+++ b/frigate/detectors/detector_config.py
@@ -125,7 +125,7 @@ class ModelConfig(BaseModel):
 
     def create_colormap(self, enabled_labels: set[str]) -> None:
         """Get a list of colors for enabled labels."""
-        cmap = plt.cm.get_cmap("tab10", len(enabled_labels))
+        cmap = plt.colormaps["tab10"].resampled(len(enabled_labels))
 
         for key, val in enumerate(enabled_labels):
             self._colormap[val] = tuple(int(round(255 * c)) for c in cmap(key)[:3])
-- 
2.45.1
Loading