Compare commits

...
Author SHA1 Message Date
Josh Hawkins d35ada1d22 check the scan clock on every entry 2026-11-11 06:54:05 -06:00
Josh Hawkins 077c475d6b report ownership scan progress
The ownership scan printed nothing until it finished, which took 30+ minutes on large recordings volumes and looked stuck. It now logs a running count of checked and mismatched entries every 30 seconds.
2026-11-11 06:47:16 -06:00
Josh HawkinsandGitHub fba967c74b fix annotation offset seeking in tracking details (#24936)
CI / AMD64 Build (push) Waiting to run
CI / AMD64 Smoke Test (push) Blocked by required conditions
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
The VOD manifest used to find the keyframe-snapped clip start was built from the live annotation offset while the playing clip used the offset from when the event opened. Each offset step fetched a manifest for a different clip, so the seek mapping drifted: the video jumped forward about one GOP every second of offset and flickered while each new manifest loaded. The manifest now uses the same stable source offset as the playlist
2026-11-10 07:57:21 -06:00
3 changed files with 22 additions and 7 deletions
+14 -1
View File
@@ -105,6 +105,8 @@ if [[ -n "$sentinel" ]]; then
mismatch_expr+=(-a ! -path "$sentinel") mismatch_expr+=(-a ! -path "$sentinel")
fi fi
exec 3>&1
for path in "$@"; do for path in "$@"; do
# An absent root is an incomplete sweep, not a finished one: /media/frigate # An absent root is an incomplete sweep, not a finished one: /media/frigate
# is not in the image, so a boot before the volume is mounted would # is not in the image, so a boot before the volume is mounted would
@@ -121,7 +123,18 @@ for path in "$@"; do
# stale mount. Tolerate it rather than aborting under errexit, but never # stale mount. Tolerate it rather than aborting under errexit, but never
# read a failed scan as "nothing to do": that would record the sweep as # read a failed scan as "nothing to do": that would record the sweep as
# complete without having looked. # complete without having looked.
if ! count=$(find "$path" -name lost+found -prune -o "${mismatch_expr[@]}" -printf '.' 2>/dev/null | wc -c); then # The walk prints nothing until it ends, so report every 30s through fd 3
# (stdout) while the count itself comes back through the substitution.
if ! count=$(find "$path" -name lost+found -prune -o "(" "${mismatch_expr[@]}" ")" -printf 'm\n' -o -printf '.\n' 2>/dev/null \
| awk -v path="$path" '
BEGIN { next_report = systime() + 30 }
$0 == "m" { mismatched++ }
systime() >= next_report {
printf "[INFO] fix-ownership: still scanning %s, %d entries checked, %d need changes\n", path, NR, mismatched > "/dev/fd/3"
fflush("/dev/fd/3")
next_report = systime() + 30
}
END { print mismatched + 0 }'); then
swept_clean=0 swept_clean=0
echo "[WARN] fix-ownership: could not scan ${path}; will retry on next boot" echo "[WARN] fix-ownership: could not scan ${path}; will retry on next boot"
continue continue
+2 -1
View File
@@ -71,13 +71,14 @@ Both the script and the boot sweep report progress, so you can tell a slow sweep
``` ```
[INFO] fix-ownership: scanning /media/frigate for ownership mismatches; this may take a while on large filesystems [INFO] fix-ownership: scanning /media/frigate for ownership mismatches; this may take a while on large filesystems
[INFO] fix-ownership: still scanning /media/frigate, 2481000 entries checked, 2390112 need changes
[WARN] fix-ownership: adjusting ownership of 4823941 entries under /media/frigate [WARN] fix-ownership: adjusting ownership of 4823941 entries under /media/frigate
[INFO] fix-ownership: /media/frigate 5% (241197/4823941 entries) [INFO] fix-ownership: /media/frigate 5% (241197/4823941 entries)
[INFO] fix-ownership: /media/frigate 10% (482394/4823941 entries) [INFO] fix-ownership: /media/frigate 10% (482394/4823941 entries)
[INFO] fix-ownership: finished /media/frigate in 12m 4s [INFO] fix-ownership: finished /media/frigate in 12m 4s
``` ```
The scan has no percentage because the total isn't known until it finishes. Watch the boot sweep with `docker logs -f frigate`. The scan has no percentage because the total isn't known until it finishes, so it reports a running count every 30 seconds instead. Watch the boot sweep with `docker logs -f frigate`.
Once the volumes are aligned, start Frigate normally. A file at `/config/.permissions_version` records what was done, so later boots skip the sweep unless you change `PUID`/`PGID`. Once the volumes are aligned, start Frigate normally. A file at `/config/.permissions_version` records what was done, so later boots skip the sweep unless you change `PUID`/`PGID`.
@@ -150,17 +150,18 @@ export function TrackingDetails({
// Fetch the VOD manifest JSON to get the actual clipFrom after keyframe // Fetch the VOD manifest JSON to get the actual clipFrom after keyframe
// snapping. The backend may snap clipFrom backwards to a keyframe, making // snapping. The backend may snap clipFrom backwards to a keyframe, making
// the video start earlier than the requested time. // the video start earlier than the requested time. Uses sourceOffsetRef so
// the manifest describes the same clip as videoSource.
const vodManifestUrl = useMemo(() => { const vodManifestUrl = useMemo(() => {
if (!event.camera) return null; if (!event.camera) return null;
const startTime = const sourceOffset = sourceOffsetRef.current;
event.start_time + annotationOffset / 1000 - REVIEW_PADDING; const startTime = event.start_time + sourceOffset / 1000 - REVIEW_PADDING;
const endTime = const endTime =
(event.end_time ?? Date.now() / 1000) + (event.end_time ?? Date.now() / 1000) +
annotationOffset / 1000 + sourceOffset / 1000 +
REVIEW_PADDING; REVIEW_PADDING;
return `vod/clip/${event.camera}/start/${startTime}/end/${endTime}`; return `vod/clip/${event.camera}/start/${startTime}/end/${endTime}`;
}, [event, annotationOffset]); }, [event]);
const { data: vodManifest } = useSWR<VodManifest>(vodManifestUrl, null, { const { data: vodManifest } = useSWR<VodManifest>(vodManifestUrl, null, {
revalidateOnFocus: false, revalidateOnFocus: false,