availableWidth starts at 0 (not null/undefined) before ResizeObserver fires.
The ?? operator passes 0 through instead of falling back to window.innerWidth,
making cellHeight negative and causing react-grid-layout to render a ~10px
container. The overflow-x-hidden div then becomes an implicit scroll container,
producing the 'cards squeezed in a small rectangle' symptom.
Changing ?? to || makes 0 trigger the window.innerWidth fallback, giving a
reasonable initial rowHeight until the real container width is measured.
https://claude.ai/code/session_01H1sqbcFmtwwsdNTJcJHJWd
VideoPreview's <video> had aspect-video + size-full, but size-full overrides
the aspect-ratio constraint, leaving object-fit at the default fill.
Adding object-contain preserves the video's natural aspect ratio in event cards.
https://claude.ai/code/session_01EwdaKGsrRLZ74smmCQ1MgW
PreviewVideoPlayer's <video> had no explicit object-fit, so browsers
applied the CSS default (fill), stretching the video when the container
aspect ratio (detect resolution) didn't match the actual preview video.
Adding object-contain preserves aspect ratio in the recording/history view.
https://claude.ai/code/session_01EwdaKGsrRLZ74smmCQ1MgW
Grid tiles explicitly set --frigate-mse-object-fit:fill so video stretches
to fill the card without preserving aspect ratio. The MsePlayer default
is contain, so History preview and all other contexts keep correct proportions.
https://claude.ai/code/session_01EwdaKGsrRLZ74smmCQ1MgW
The MSE player default was set to 'fill' which stretches video in all contexts.
Only the draggable grid should use 'cover' (via --frigate-mse-object-fit:cover).
Changing the fallback to 'contain' restores aspect-ratio-preserving behaviour
everywhere else (History preview, etc.) while keeping the grid fill intact.
https://claude.ai/code/session_01EwdaKGsrRLZ74smmCQ1MgW
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
When reduce_storage_consumption deletes old recording segments to free
disk space, it now also deletes preview files that overlap the same
time range. Without this, preview mp4 files on the same disk continued
to consume space, causing the storage maintainer to delete progressively
newer recordings while old previews accumulated — resulting in archives
where older periods had previews but no video.
This is particularly impactful for multi-path setups where each camera's
preview directory shares a disk with its recordings.
https://claude.ai/code/session_016bxjbVpx8DqpjysnGYmXdx
- write_frame_to_cache() now returns bool; callers only append the
timestamp to output_frames when cv2.imwrite() actually succeeded,
preventing dangling timestamps that cause ffmpeg "Impossible to open"
errors when the cache disk is full
- FFMpegConverter removes the partial output mp4 on ffmpeg failure so
stale partial files don't accumulate on the recording disk
https://claude.ai/code/session_016bxjbVpx8DqpjysnGYmXdx
- storage.py: refactor check_storage_needs_cleanup(root) to check a
specific path instead of returning the first needy one; run() now
iterates all configured recording roots per 5-minute cycle so a
stuck path can no longer starve the others
- storage.py: skip stale camera entries in _get_path_bandwidths to
avoid KeyError when a camera is removed from config at runtime
- maintainer.py: delete partial output file when ffmpeg fails
(ENOSPC), preventing orphaned files that consume disk space without
a DB entry and block future conversion attempts
https://claude.ai/code/session_016bxjbVpx8DqpjysnGYmXdx
* add shm frame lifetime calculation and update UI for shared memory metrics
* consistent sizing on activity indicator in save buttons
* fix offline overlay overflowing on mobile when in grid mode
asyncio.SubprocessError does not exist — Python's asyncio module has no
such class. The correct exception is subprocess.SubprocessError, which
is available via the existing `import subprocess as sp` alias already
present in this file.
The invalid exception reference causes the except clause to raise a
NameError rather than catching the intended exception.
* refactor websockets to remove react-tracked
react 19 removed useReducer eager bailout, which broke react-tracked.
react-tracked works by wrapping state in a JavaScript Proxy. When a component reads state.someField, the proxy records that access. On the next state update, it compares only the fields each component actually touched and skips re-renders if those fields are unchanged. Under the hood, this relies on useReducer — and in React 18, useReducer had an "eager bail-out" that short-circuited rendering when the new state was === to the old state. React 19 removed that optimization, so every dispatch now schedules a render regardless, and the proxy comparison runs too late to prevent it.
useSyncExternalStore is a React primitive (added in 18, stable in 19) designed for exactly this pattern: subscribing to an external store:
useSyncExternalStore(
subscribe, // (listener) => unsubscribe — called when the store changes
getSnapshot // () => value — returns the current value for this subscriber
)
React calls getSnapshot during render and compares the result with Object.is. If the value is the same reference, the component bails out — no re-render. The key difference from react-tracked is that this bail-out is built into React's reconciler, not bolted on via proxy tricks and useReducer.
The per-topic subscription model makes this efficient. Instead of one global store where every subscriber has to check if their fields changed, each useWs("some/topic", ...) call subscribes only to that topic's listener set. When a message arrives for front_door/detect/state, only components subscribed to that exact topic get their listener fired → React calls their getSnapshot → Object.is compares the value → bail-out if unchanged. Components watching back_yard/detect/state are never even notified.
* remove react-tracked and react-use-websocket
* refactor usePolygonStates to use ws topic subscription
* fix TimeAgo refresh interval always returning 1s due to unit mismatch (seconds vs milliseconds)
older events now correctly refresh every minute/hour instead of every second
* simplify
* clean up
* don't resend onconnect
* clean up
* remove patch
* Improve title to better capture activity
* Improve efficiency of prompt
* Use json format for llama.cpp
* Cleanup prompt
* Add output format for other LLMs
Currently translated at 24.7% (268 of 1084 strings)
Translated using Weblate (Cantonese (Traditional Han script))
Currently translated at 63.5% (297 of 467 strings)
Translated using Weblate (Cantonese (Traditional Han script))
Currently translated at 100.0% (58 of 58 strings)
Translated using Weblate (Cantonese (Traditional Han script))
Currently translated at 100.0% (230 of 230 strings)
Translated using Weblate (Cantonese (Traditional Han script))
Currently translated at 86.9% (147 of 169 strings)
Added translation using Weblate (Cantonese (Traditional Han script))
Update translation files
Updated by "Squash Git commits" add-on in Weblate.
Added translation using Weblate (Cantonese (Traditional Han script))
Added translation using Weblate (Cantonese (Traditional Han script))
Translated using Weblate (Cantonese (Traditional Han script))
Currently translated at 0.2% (1 of 464 strings)
Added translation using Weblate (Cantonese (Traditional Han script))
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: beginner2047 <leoywng44@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/yue_Hant/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-system
Update translation files
Updated by "Squash Git commits" add-on in Weblate.
Added translation using Weblate (Korean)
Added translation using Weblate (Korean)
Added translation using Weblate (Korean)
Translated using Weblate (Korean)
Currently translated at 86.2% (50 of 58 strings)
Translated using Weblate (Korean)
Currently translated at 99.5% (227 of 228 strings)
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: John <john@akfn.net>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/ko/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ko/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Update translation files
Updated by "Squash Git commits" add-on in Weblate.
Added translation using Weblate (Persian)
Added translation using Weblate (Persian)
Added translation using Weblate (Persian)
Translated using Weblate (Persian)
Currently translated at 99.0% (215 of 217 strings)
Co-authored-by: Amir reza Irani ali poor <amir1376irani@yahoo.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/fa/
Translation: Frigate NVR/common
Update translation files
Updated by "Squash Git commits" add-on in Weblate.
Added translation using Weblate (Swedish)
Added translation using Weblate (Swedish)
Added translation using Weblate (Swedish)
Translated using Weblate (Swedish)
Currently translated at 96.5% (56 of 58 strings)
Translated using Weblate (Swedish)
Currently translated at 92.5% (136 of 147 strings)
Translated using Weblate (Swedish)
Currently translated at 95.1% (217 of 228 strings)
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: ThomasW <thomas.wursig@remote24.se>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/sv/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-system