Desktop app
The desktop app is Nuvio Web with its own server built in: it starts that server on the computer's loopback address, waits for it to answer, and opens it in a native window. It needs no Nuvio Web server anywhere else. It is built with Electrobun and uses the system's web view (WebKit on macOS and Linux, WebView2 on Windows), so video plays with the codecs the system has.
#Signing in
The sign-in page offers the official Nuvio server, selected by default, or
Custom backend for any other Nuvio backend: type its URL, and leave the key
empty for a Nuvio self-hosted server, which publishes it. Custom backends are on
from the start (NUVIO_ALLOW_CUSTOM_BACKENDS, see
Configuration).
#Sources the window can't play
The window plays what its web engine decodes, in hardware where the engine can. A source it can't decode at all (a codec or container the engine refuses, or one the addon marks as not playable in a browser) opens in mpv instead, in its own window, with hardware decoding on (VideoToolbox on macOS, D3D11 on Windows, VAAPI and friends on Linux). The app's player screen says so while mpv plays, saves the position mpv reports as it goes, and offers to reopen mpv once it closes.
The macOS and Windows apps ship mpv 0.41.0, checked against a pinned digest at
build time. The Intel Mac build of it needs macOS 15. On Linux the app uses the
system's mpv (apt install mpv, dnf install mpv, …); without one, such
sources show the usual "can't play" screen.
#In the window (macOS, experimental)
On macOS, mpv draws inside the app's own window instead, under the page, and the
player's controls (play and pause, seeking, ±10 s, mute, fullscreen, the
keyboard shortcuts) drive it. It is libmpv from
MPVKit, linked with a small native view into
one library the app loads at start; the build fetches MPVKit's archives and
checks each against its pinned checksum. Set NUVIO_MPV_ENGINE=always before
starting the app to play every source this way, not only those the web view
refuses.
#Updates
The app updates itself from the latest stable release on GitHub. Once the server is up it looks for a newer build and downloads it in the background; the next launch asks to install it, then restarts into it. "Later" asks again on the launch after. Dev builds never update. Every update is the full app archive (about 130 MB), not a patch.
#Logs
Every launch logs into one SQLite file in the system's temporary folder,
$TMPDIR/nuvio-web-desktop-<uid>/logs.sqlite on macOS (a folder of the user's
own, readable by them only), which can be queried while the app runs. Each row
has the launch it belongs to (run), a time, a source (shell for the app
itself, server for its built-in server at debug level, page for the window),
a level, a message and its fields as JSON. The page reports its console, failed
requests, byte-range requests, CSP refusals, video events and the codec
questions the player asks the browser. Rows older than a week are dropped.
sqlite3 "$TMPDIR/nuvio-web-desktop-$(id -u)/logs.sqlite" \
"SELECT at, source, level, message, data FROM logs
WHERE run = (SELECT max(run) FROM logs) ORDER BY id"#Where things are kept
Sessions, the library, watch progress and history live in the app's own SQLite database, in the user's app-data folder, and sync with Nuvio in the background as on any instance (Sync). The server keeps the same loopback port from one launch to the next, so the window stays signed in. When the server fails to start, the window shows where the logs are.
Only the app's own pages open in the window. Any other link (a provider, an
addon's page, vlc://) opens in the system's default browser or app.
#Building it
Electrobun is a dev dependency, and builds go through Hutch, its build tool,
which the electrobun command fetches on first use. Each build compiles the
server first.
bun run dev:desktop # build and open a dev build
bun run build:desktop # a stable release (app, DMG / installer) for this machineA build targets the machine it runs on, so the Desktop workflow builds each target on its own runner (macOS Apple Silicon and Intel, Linux x64 and ARM64, Windows x64) whenever the desktop app, the dependencies or the icon change, and keeps the installers as run artifacts for 14 days. A stable release runs the same builds and attaches the installers and the updater's files to the release. Release builds are unsigned until signing is configured (see Electrobun's code-signing guide), so macOS asks before opening one.
This guide lives in the project repo: edit it there, and this page follows within a day.