1090 Commits
Author SHA1 Message Date
Scott Lamb 5944f2bba7 v0.7.31 with retina v0.4.19
Fixes #352
v0.7.31
2026-03-13 11:55:44 -07:00
Scott Lamb 00dc543ac8 live view: support changing video parameters 2026-02-20 12:33:10 -08:00
Scott Lamb ffc8bd6b98 v0.7.30: fix severe timestamp drift
* reset local start time at each recording boundary
* include the final frame's time when converting from wall to media time

Together at 30 fps these caused about 15 seconds of drift over 4 hours.
v0.7.30
2026-02-19 15:47:00 -08:00
Scott Lamb 10e48265cf v0.7.29: fix garbage collection
As noted in the changelog, this problem caused restarts to take a long
time, and the SQLite database to (slowly) grow without bound.

The problem was simple: the directory worker incorrectly aborted
`collect_garbage` if nothing was watching the result. For some
operations (e.g. reading from sample files) this is the correct
behavior, but in this case it contradicts the `Pool::collect_garbage`
contract.
v0.7.29
2026-02-19 10:11:17 -08:00
Scott Lamb 3c8fc6b63a v0.7.28 v0.7.28 2026-02-19 09:35:14 -08:00
Scott Lamb b74b643d3e gracefully degrade live stream
See #343
2026-02-19 09:32:24 -08:00
Scott Lamb 7985abfc38 update some Rust deps 2026-02-18 19:24:34 -08:00
Scott Lamb fa19757b7b npx update-browserslist-db@latest 2026-02-18 18:01:12 -08:00
Scott LambandClaude Opus 4.6 2164f83c84 ui: migrate LoadingButton from @mui/lab to @mui/material Button
LoadingButton was merged into the core Button component in MUI v7.
Replace all usages and remove the @mui/lab dependency entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:28:56 -08:00
Scott LambandClaude Opus 4.6 4f2e22776c ui: remove unused dev dependencies
Remove @babel/core, @babel/preset-env, @babel/preset-react,
@babel/preset-typescript (Vite uses SWC via @vitejs/plugin-react-swc),
http-proxy-middleware (Vite has built-in proxy), and ts-node (unused).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:28:56 -08:00
Scott LambandClaude Opus 4.6 ff20d66413 ui: upgrade MUI 5→7, x-date-pickers 6→8, date-fns 2→4, react-hook-form-mui 6→8
- @mui/material ^5 → ^7, @mui/icons-material ^5 → ^7, @mui/lab → 7 beta
- @mui/x-date-pickers ^6 → ^8 with AdapterDateFns path update
- date-fns ^2 → ^4, date-fns-tz ^2 → ^3 (API renames: zonedTimeToUtc →
  fromZonedTime, utcToZonedTime → toZonedTime)
- react-hook-form-mui ^6 → ^8 (validation → rules prop)
- CssVarsProvider/extendTheme graduated from experimental
- Removed StyledEngineProvider (no longer needed)
- Fixed barrel import in DeleteDialog.tsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:28:56 -08:00
Scott LambandClaude Opus 4.6 694449d1bb ui: upgrade React 18 → 19, testing-library, react-router v7
React 19:
- Replace JSX.Element with React.JSX.Element (namespace moved)
- Update RefObject types to include null
- Remove forwardRef from FullScreenVideo (ref is a regular prop now)
- Fix snackbar test to use await act() for fake timer advancement

testing-library:
- @testing-library/dom 8 → 10
- @testing-library/react 13 → 16
- Use ReactNode instead of ReactElement in testutil

react-router:
- Rename react-router-dom → react-router (package consolidated in v7)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:28:56 -08:00
Scott LambandClaude Opus 4.6 928bbab51c ui: migrate ESLint 8 → 9 with flat config
Replace legacy eslintConfig in package.json with eslint.config.js
using the new flat config format. Update packages:

- eslint 8 → 9
- @typescript-eslint/{parser,eslint-plugin} 6 → typescript-eslint 8
- eslint-plugin-react-hooks 4 → 5
- eslint-plugin-vitest → @vitest/eslint-plugin
- eslint-plugin-react-refresh 0.4 → 0.5
- add @eslint/js

Auto-fix prefer-const violations and remove a stale eslint-disable
directive. Disable no-explicit-any, no-empty-object-type, and
no-this-alias to match previous leniency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:28:56 -08:00
Scott LambandClaude Opus 4.6 321fb5ccbd ui: upgrade @fontsource/roboto 4 → 5
No code changes needed; the bare import still works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:28:41 -08:00
Scott LambandClaude Opus 4.6 edc59d24cb ui: upgrade prettier 2 → 3
The default trailingComma changed from "es5" to "all", so many files
are reformatted with trailing commas in more positions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:28:06 -08:00
Scott LambandClaude Opus 4.6 9d14eba5ec ui: bump minor versions of deps; fix msw test race
Bump @emotion/react, @emotion/styled, react-hook-form,
@testing-library/jest-dom, msw, eslint-plugin-react, and
@testing-library/user-event within their current major versions.

Fix App.test.tsx race condition exposed by msw 2.12: wait for the
fetch error to render before test cleanup aborts the request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:27:15 -08:00
Scott Lamb 3b75703652 add SameSite=Lax to logout Set-Cookie header
When clearing the session cookie on logout, the Set-Cookie header had
no SameSite attribute. Older Firefox would warn about automatically
applying SameSite=Lax. Newer Firefox no longer warns, but we might as
well be explicit.

https://bugzilla.mozilla.org/show_bug.cgi?id=1654847
https://bugzilla.mozilla.org/show_bug.cgi?id=1653518

Fixes #292
2026-02-18 13:12:25 -08:00
Scott Lamb 8e2106b042 log full HTTP error chains
For example, this newly logs if a login fails because the user is
disabled.
2026-02-18 12:49:34 -08:00
Scott Lamb 30e8d8a5c2 prep v0.7.27 v0.7.27 2026-02-18 12:19:34 -08:00
Scott Lamb b85a27bcd8 fix React ref warnings when opening a recording in list view
FullScreenVideo was a plain function component, so MUI's Modal/FocusTrap
could not attach a ref to it, producing three React warnings. Wrap it in
React.forwardRef and forward the ref to the outermost Box element.

Also fix an MUI warning about the modal content node not accepting focus,
by adding tabIndex={-1} to FullScreenVideo's root element.
2026-02-18 12:15:43 -08:00
Scott Lamb dbf4c79644 update retina 2026-02-18 11:49:18 -08:00
Scott Lamb 7fb56fb0b3 fix lock order violations in moonfire-nvr config
These caused debug mode panics. I don't think they lead to any real
deadlocks or problems in release mode.
2026-02-18 11:46:56 -08:00
Scott Lamb a384f2cb2a fix dir pool shutdown with multiple streams
`v0.7.26` introduced a busy loop error when the dir pool was shutting
down with multiple streams. This commit tests and fixes this scenario.

Also fixes a second problem in dropping a writer without an explicit
close would panic in debug mode about using (dropping) the blake3 hasher
while holding a lock. The check is meant to detect slow operations while
holding a lock. The panic wouldn't happen in release mode and probably
isn't a significant source of slowness, but fix it anyway.
2026-02-18 11:19:43 -08:00
Scott Lamb ef8cc2d872 fix intermittent live view failures in FF
The live view WebSocket handler was declared async and called
`await blob.arrayBuffer()` directly:

```js
onWsMessage = async (e: MessageEvent<any>) => {
  ...
  const raw = new Uint8Array(await e.data.arrayBuffer());
  ...
};
```

When several WebSocket messages arrive in quick succession—as commonly
happens at stream start—the browser dispatches multiple concurrent
invocations of this handler. Each invocation independently awaits
`Blob.arrayBuffer()`. `LiveCamera.tsx` implicitly assumed that message
n's buffer would resolve before message n+1's buffer, which seemed
logical because they're on the same TCP stream. However, there's no
guarantee of this, and it apparently is not true on Firefox. Perhaps
it's about how FF schedules async tasks.

`LiveCamera.tsx` assumes in its timestamp handling that media segments'
blobs will be strictly resolved in the order they should be played.
Playing segments before their dependencies (e.g. non-IDR frames before
the first IDR frame) doesn't go well.

Fix: make `onWsMessage` synchronous and chain each blob onto a
sequential promise (`messageChain`) so that `processWsBlob` is always
called in arrival order, regardless of the implementation-defined
resolution order of individual `Blob.arrayBuffer()` calls.

Fixes #343.
2026-02-18 08:30:06 -08:00
Scott Lamb 483fb90a8d fix 9x16 videos being stretched in full screen 2026-02-17 21:08:50 -08:00
Scott Lamb cacb7eda76 fix TypeScript error: use Uint8Array<ArrayBuffer> for Part.body
TypeScript 5.7 made typed arrays generic (Uint8Array<TArrayBuffer extends
ArrayBufferLike>), which is no longer assignable to BufferSource (used by
SourceBuffer.appendBuffer), since ArrayBufferLike includes SharedArrayBuffer.
Since the data always comes from Blob.arrayBuffer(), it is always backed by a
plain ArrayBuffer, so use Uint8Array<ArrayBuffer> to reflect that precisely.
2026-02-17 20:58:25 -08:00
Scott Lamb 840dfb4679 use hyperlinks to issues in live view error alert
Fixes #326.
2026-02-17 20:58:25 -08:00
Scott Lamb a36457cd9f prep v0.7.26 v0.7.26 2026-02-17 11:55:55 -08:00
Scott Lamb 316d3d8482 allow inserting VSEs without the db lock 2026-02-17 11:55:55 -08:00
Scott Lamb fd7ae68657 overhaul streams!
This puts a lot of state into a new `LockedStream` that was formerly
guarded more coarsely by the recording lock or more granularly by
`RecordingToInsert` locks. And it restructures `LockedDatabase::flush`
to avoid holding this lock while performing SQLite operations.

Streamers write only into a recent frames buffer in RAM, which the
directory pool workers asynchronously write to disk approximately once
per GOP, where formerly there was a write call per frame.

Before, streamers could fall behind due to slow sample file directory
disk I/O calls or if writing got stuck in a retry loop, for example if
out of disk space. If Moonfire falls too far behind in receiving,
cameras tend to disconnect or (sadly) mis-frame messages because they do
not handle this case well. This removes a confusing potential cause of
such errors.

In the new model, streamers never wait for disk I/O. Frames are buffered
up to a limit. Recordings are aborted if the I/O pool falls too far
behind. There's no retry loop.

This roughly halves CPU usage as thread hand-offs and syscalls are less
common. It also enables several desirable future changes:

* audio recording (which radically increases the number of frames and
  thus would otherwise significantly increase CPU usage).
* "monitor mode" (allowing live view without recording to disk),
* further CPU reductions for live view (by skipping all SQLite ops and
  disk I/O).

In the new model, streamers also rarely wait for SQLite database
operations either—now only on video parameter changes. That might go
away in an upcoming change too.

The "syncer" (which synced files/dirs and kept track of what has to be
flushed into the SQLite database when) has mostly gone away too. The
directory pool workers handle the syncing now, and then they notify
a new "flusher" for the latter portion.

I'd like to go further still and have the database as a whole be owned
by a dedicated thread, the only one that uses the SQLite connection.
This prevents the tokio threads from blocking on SQLite operations.
I tried out doing so before making the streamers less dependent on the
db, but the extra thread handoffs were awful for performance.
2026-02-17 11:55:55 -08:00
Scott Lamb 0dc285b159 move all sample file dir I/O to pool
Now, all IO happens on each directory's two-thread pool with threads
named `dir-<PATH>`. Formerly, IO could happen on the per-stream writer
threads, per-directory reader threads, and occasional on even the tokio
worker threads.

The recording path uses more CPU than before due to extra thread
hand-offs. That will change in the next commit, which will radically
change the approach to writing.
2026-02-17 11:17:28 -08:00
Scott Lamb d127b80c87 make even debug builds run decently fast
...by optimizing certain crates that are responsible for most of the CPU
time. Most are self-explanatory.
2026-02-17 11:17:28 -08:00
Scott Lamb ee84b7bdf2 no per-layer filter in test setup
I stopped using it in the production path in 7968700a because per-layer
filters are buggy. Do the same in the test path.
2026-02-16 17:20:35 -08:00
Scott Lamb 4f45a7f34d clarify db ops used by sample file dir code 2026-02-16 17:20:35 -08:00
Scott Lamb f85ce0d66a don't capture backtrace on Unknown
Currently all errors from retina are `Unknown`, and these massive
backtraces are obnoxious when a camera fails on every attempt, with
only a second between.
2026-02-16 17:20:35 -08:00
Scott Lamb c1bf79a01c cloneable Error
This is useful in `mp4::Segment::index`. It will also be useful in
an upcoming change that can fan out errors from directory pool
transitions.
2026-02-16 17:20:35 -08:00
Scott Lamb 424d61f2b5 small tweaks to Docker experience
* add `/var/tmp` to the image so it doesn't have to be mounted
* remove obsolete `version: 3` from compose snippet
* mention `/usr/share/zoneinfo` in error msg and troubleshooting guide
2026-02-16 08:25:19 -08:00
Scott Lamb bdffe6323d rm profile arg to setup-rust-toolchain
...it was incorrect, but apparently harmless aside from a warning.
Looks like this this action defaults to no extra components anyway.
2026-02-14 20:01:19 -08:00
Scott Lamb eff099ea58 update various github actions to current versions 2026-02-14 18:57:48 -08:00
Scott Lamb 10bc1c6fc1 fix release workflow error v0.7.25 2026-02-14 13:24:00 -08:00
Scott Lamb e9ba47e9ab fix release workflow: pass --provenance=false to docker build
Since buildx v0.10.0, provenance attestations are enabled by default,
causing even single-platform `docker build --push` to produce a
manifest list (OCI image index) rather than a plain image manifest.
This breaks the subsequent `docker manifest create` step, which
expects plain image manifests as inputs:

    ghcr.io/scottlamb/moonfire-nvr:v0.7.24-x86_64 is a manifest list

Fix by passing `--provenance=false` to suppress the attestation,
ensuring each per-arch tag is a plain image manifest.

Ref: https://github.com/docker/buildx/issues/1507
2026-02-14 13:21:41 -08:00
Scott Lamb 26e8448e46 prep v0.7.24 v0.7.24 2026-02-14 13:09:59 -08:00
Scott Lamb 20299cab89 update retina 2026-02-14 12:51:48 -08:00
Scott Lamb f1f1a04394 adjust panic handling, debug info in release binaries 2026-02-14 12:48:46 -08:00
Scott Lamb 0cc1aa96d6 quick fix for #346 2026-02-14 12:45:19 -08:00
Scott Lamb 1cc422bf0d update node versions
...to:

* 20 (maintenance LTS)
* 22 (maintenance LTS)
* 24 (active LTS release)
* 25 (current non-LTS release)

And address some "realm mismatch" problems in tests:

* It was using `AbortSignal` from `jsdom`'s realm, `fetch` from the Node
  native `fetch`. Node 24 upgraded to undici v7 with stricter Fetch spec
  compliance, which complained about this.
* It was doing `instanceof` checks from `myfetch` which failed for
  similar reasons.

by updating vitest (see <https://github.com/vitest-dev/vitest/issues/8374>) and simplifying the `api.ts` checking.

Along the way, give error messages like `POST /api/login failed; see browser console` rather than the unhelpful `TypeError: Failed to fetch`.
2026-02-14 12:29:25 -08:00
Scott Lamb ff383147e4 prep v0.7.23
I'd actually intended the things here to be in v0.7.22, but I tagged
the wrong revision. Embarrassing, but version numbers are free.
v0.7.23
2025-10-03 15:42:34 -07:00
Scott Lamb 5cc93349fc prep v0.7.22
...with dependency updates, notably including Retina.
2025-10-03 14:48:12 -07:00
Scott Lamb dc1909d073 fix lifetime elision warnings 2025-10-03 14:19:52 -07:00
Jared Wolff bc93712314 Addressing FreeBSD compile issue v0.7.22 2025-06-18 20:46:01 -07:00