An open API service indexing awesome lists of open source software.

https://github.com/mayerwin/frigate-layout-sync

Save, sync and responsively restore Frigate camera-group dashboard layouts across devices. A tiny standalone companion service that transparently proxies Frigate and stores layouts server-side.
https://github.com/mayerwin/frigate-layout-sync

camera frigate home-automation layout nvr reverse-proxy

Last synced: 27 days ago
JSON representation

Save, sync and responsively restore Frigate camera-group dashboard layouts across devices. A tiny standalone companion service that transparently proxies Frigate and stores layouts server-side.

Awesome Lists containing this project

README

          

# frigate-layout-sync

Save, restore and **sync [Frigate](https://frigate.video) camera-group dashboard layouts across all your devices**, with **responsive, per-width breakpoints** so a phone, a tablet and a desktop can each load the arrangement that fits its screen.

Frigate stores each camera group's draggable tile layout (and per-camera stream settings like compatibility mode) **per-browser, in IndexedDB**. Every new device or browser starts from scratch, and there is no way to keep a different layout for a phone versus a wall display. This tool fixes both: one click saves the current layout to a small server, another click loads the right one back, on any device. It also gives Frigate's built-in **All Cameras** view a custom, draggable layout, which Frigate itself does not support.

> Community answer to Frigate issue [#23462](https://github.com/blakeblackshear/frigate/issues/23462) (*"Add import/export for camera group layouts"*), which is on Frigate's roadmap but planned as per-device file export only. This tool adds true **server-side, cross-device, responsive** sync today.


The injected Layouts button, sitting natively above Frigate's fullscreen control
  
Layout Sync panel
  
Save dialog


The Layouts button drops in with Frigate's own controls (above the fullscreen button) · the panel · the save dialog

---

## How it works — no reverse proxy

Frigate has no frontend plugin API, so this runs as a tiny companion container that makes **Frigate's own nginx** inject one button. **Frigate stays the front door on its normal port** — it serves all your assets, the live-view websockets and video exactly as before. The companion only does two small things:

```
your browser ───► Frigate (nginx, port 8971, unchanged) ──► cameras / recordings
(any device) │ injects via sub_filter
/__layoutsync/* ───► frigate-layout-sync companion ──► layouts.yaml
```

1. The companion adds two lines to Frigate's nginx config: a `sub_filter` that injects one `<script>` into the dashboard, and a `location /__layoutsync/` that routes just that path to the companion. It applies them by editing the config **inside the running Frigate container** (over the Docker socket), validating with `nginx -t`, then reloading.
2. It **re-applies automatically whenever Frigate (re)starts** — so a Frigate upgrade, which recreates the container from a fresh image and wipes the edit, *self-heals* with no manual step.
3. The companion serves the injected client (`/__layoutsync/inject.js`) and a tiny layout API (`/__layoutsync/api/*`), storing profiles as YAML.

Because the button runs **inside** the real Frigate page (same origin, same port) it reads and writes Frigate's own IndexedDB layout entries directly, and your existing dragged layouts always carry over. Because the layouts live on the **server**, every device that opens Frigate shares them — including phones — with nothing to install per-device. If the companion stops, you simply lose the extra button; if Frigate ever can't reach it, that one `location` just 502s while the rest of Frigate is unaffected.

---

## Install

### Docker Compose (recommended)

Add the companion next to your existing Frigate service and put both on a shared
network so Frigate's nginx can reach it. **You do not move Frigate's port or touch
its config** — the companion does the nginx wiring itself.

```yaml
services:
frigate:
image: ghcr.io/blakeblackshear/frigate:stable
container_name: frigate # must match FRIGATE_CONTAINER below
ports: ["8971:8971"] # Frigate keeps its normal port
networks: [layoutsync] # <-- add this line
# ...your usual frigate config...

frigate-layout-sync:
build: . # or image: ghcr.io/mayerwin/frigate-layout-sync:latest
container_name: frigate-layout-sync
restart: unless-stopped
environment:
FRIGATE_CONTAINER: frigate
LAYOUTSYNC_UPSTREAM: frigate-layout-sync:3000
volumes:
- /var/run/docker.sock:/var/run/docker.sock # to wire + reload Frigate's nginx
- ./frigate/config/layout-sync:/app/data # layouts.yaml (inside /config = backed up)
networks: [layoutsync]
depends_on: [frigate]

networks:
layoutsync:
```

```bash
docker compose up -d --build
```

Open Frigate at its **usual** `https://<frigate-host>:8971`. A small **Layouts** icon
appears bottom-right. (First run waits for Frigate to be up, then injects + reloads nginx.)

> **Why the Docker socket?** It's how the companion keeps the one-line nginx
> injection in place and reloads nginx after a Frigate upgrade, with zero manual
> steps. It only ever execs `nginx` in your Frigate container. If you'd rather not
> mount it, use the manual mode below.

### Manual mode (no Docker socket)

Set `NGINX_AUTOCONFIG=false` and wire Frigate's nginx yourself. Frigate
[supports bind-mounting a custom `nginx.conf`](https://docs.frigate.video/configuration/advanced/).
Add, **inside `location / { … }`**:

```nginx
sub_filter '</body>' '<script src="/__layoutsync/inject.js" defer>