From 5ba780231672f824bd4fc87d57ce0e9be1aaf826 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Mar 2026 09:01:56 +0000 Subject: [PATCH] Serve preview files from non-default recording roots via nginx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cameras are configured with recording paths outside /media/frigate (e.g. /video1, /video2), preview mp4 files generated there had no corresponding nginx location block — requests returned 404. At nginx startup, get_nginx_settings.py now extracts unique recording roots outside /media/frigate from the Frigate config. The nginx run script uses a new extra_recordings.gotmpl template to generate location blocks (e.g. /video1/preview/) with alias directives for each such root, included via extra_recordings.conf. The API already returns correct src URLs for these paths (the existing replace(BASE_DIR, "") leaves non-media paths unchanged), so no API changes are needed. https://claude.ai/code/session_016bxjbVpx8DqpjysnGYmXdx --- docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run | 5 +++++ docker/main/rootfs/usr/local/nginx/conf/nginx.conf | 1 + .../rootfs/usr/local/nginx/get_nginx_settings.py | 11 +++++++++++ .../local/nginx/templates/extra_recordings.gotmpl | 12 ++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 docker/main/rootfs/usr/local/nginx/templates/extra_recordings.gotmpl diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run index a3c7b3248..a92ac0d73 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run @@ -89,6 +89,11 @@ python3 /usr/local/nginx/get_nginx_settings.py | \ tempio -template /usr/local/nginx/templates/listen.gotmpl \ -out /usr/local/nginx/conf/listen.conf +# build location blocks for recording roots outside /media/frigate +python3 /usr/local/nginx/get_nginx_settings.py | \ + tempio -template /usr/local/nginx/templates/extra_recordings.gotmpl \ + -out /usr/local/nginx/conf/extra_recordings.conf + # Replace the bash process with the NGINX process, redirecting stderr to stdout exec 2>&1 exec \ diff --git a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf index 46241c5ab..e96f0e26e 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf @@ -101,6 +101,7 @@ http { include auth_location.conf; include base_path.conf; + include extra_recordings.conf; location /vod/ { include auth_request.conf; diff --git a/docker/main/rootfs/usr/local/nginx/get_nginx_settings.py b/docker/main/rootfs/usr/local/nginx/get_nginx_settings.py index 79cda3686..e2e0e4746 100644 --- a/docker/main/rootfs/usr/local/nginx/get_nginx_settings.py +++ b/docker/main/rootfs/usr/local/nginx/get_nginx_settings.py @@ -52,11 +52,22 @@ listen_config["external_port"] = external_port base_path = os.environ.get("FRIGATE_BASE_PATH", "") +# Collect recording roots that are outside the default /media/frigate tree. +# Nginx needs an explicit location block for each such root to serve preview files. +_default_recordings = "/media/frigate" +_extra_roots: set[str] = set() +for _cam_cfg in config.get("cameras", {}).values(): + if isinstance(_cam_cfg, dict): + _path = _cam_cfg.get("path", "") + if _path and not _path.startswith(_default_recordings): + _extra_roots.add(_path.rstrip("/")) + result: dict[str, Any] = { "tls": tls_config, "ipv6": ipv6_config, "listen": listen_config, "base_path": base_path, + "extra_recording_roots": sorted(_extra_roots), } print(json.dumps(result)) diff --git a/docker/main/rootfs/usr/local/nginx/templates/extra_recordings.gotmpl b/docker/main/rootfs/usr/local/nginx/templates/extra_recordings.gotmpl new file mode 100644 index 000000000..7abe93126 --- /dev/null +++ b/docker/main/rootfs/usr/local/nginx/templates/extra_recordings.gotmpl @@ -0,0 +1,12 @@ +{{ range .extra_recording_roots }} +location {{ . }}/preview/ { + include auth_request.conf; + types { + video/mp4 mp4; + } + + expires 7d; + add_header Cache-Control "public"; + alias {{ . }}/preview/; +} +{{ end }}