Airwave

Changelog

The most recent Airwave releases and what changed in each.

The 20 most recent releases. For the complete history (743 releases), see CHANGELOG.md on GitHub.

0.14.18 (2026-09-18)

getairwave.tv: privacy-friendly analytics.

Added

  • Self-hosted Umami analytics on getairwave.tv, loaded via next/script (afterInteractive, non-blocking) in the root layout. Cookieless and no personal data, so no consent banner is needed. Configured entirely through NEXT_PUBLIC_UMAMI_SRC / NEXT_PUBLIC_UMAMI_WEBSITE_ID (nothing instance-specific in the repo), and rendered only on the production deploy so local dev and Vercel previews don't count against the stats. Umami auto-tracks App Router client-side navigation.

0.14.17 (2026-09-18)

getairwave.tv: the second Airwave Weekly post, and a self-hosting docs refresh.

Added

  • Airwave Weekly (18 September 2026) blog post covering the week: the three channel-building modes, the AI assistant working with all of them, the observable AI lineup builder, cross-platform scrubbing, the browser player mode, and one-command self-hosting. Branded featured image generated with gen-blog-image.py.
  • Reusable MDX components for the docs: PlatformTiles (a compact platform grid linking to the downloads page), DockerConfig (the full docker-compose.yml + .env.example viewer, sharing the home page's source), and Alert (a minimal tinted banner: info/warn/danger). Registered fumadocs Steps/Step too.

Changed

  • Self-hosting docs restructured. The landing leads with Docker (the quick start and the one-line installer nested under it) and keeps the one-click desktop app as a first-class option. The Docker and desktop quick starts are now numbered steppers; the Docker page shows the full compose + .env and documents a stable PLEX_CLIENT_IDENTIFIER; both point at /docs/downloads instead of GitHub Releases.
  • Desktop docs: corrected the macOS note (builds are signed and notarized, Apple Silicon and Intel), added tray-menu and settings screenshots, and moved the Windows database-runtime note into an Alert.
  • Prose styling hooks: prose-enhanced / prose-docs / prose-blog (renamed from blog-prose), so any typography override lives in one place; nested lists now indent clearly.

Fixed

  • Blog post dates showed a day early in behind-UTC timezones (the date was parsed as UTC midnight); dates now format in UTC so they render as written.

0.14.16 (2026-09-17)

Desktop CI: the v0.14.15 MSVC-runtime check now looks at the right artifact, so the Windows build passes (#42).

Fixed

  • The "Verify bundled MSVC runtime" step added in v0.14.15 scanned the build output for the loose vcruntime140.dll / vcruntime140_1.dll / msvcp140.dll, but the release build packs the whole app into a compressed Resources/*.tar.zst, so there was no loose pg/native/bin on disk at that point and the check always failed, taking the whole Windows desktop build down with it. It now runs after the Inno installer step (which extracts the bundle to build/inno-src/) and scans that, the exact files the installer ships. The runtime DLLs were bundled correctly the whole time; only the verification looked in the wrong place. No load-bearing build/sign/upload step was reordered.

0.14.15 (2026-09-17)

Airwave Desktop: fix the embedded database failing to start on a clean Windows box (#42).

Fixed

  • The bundled Postgres crashed at first-run database init on Windows machines without a recent Microsoft Visual C++ Runtime (initdb "post-bootstrap initialization" → 0xC0000005, or it would not start at all, code 53). PG18's binaries link vcruntime140_1.dll, which older or absent redistributables do not include. The Windows build now bundles the MSVC runtime (vcruntime140.dll, vcruntime140_1.dll, msvcp140.dll) next to postgres.exe, so a clean box works with no manual install. It was never an antivirus problem, despite how the crash reads.

Added

  • If the database still fails to start on Windows, the setup screen now shows a clear message with a one-click link to the VC++ runtime instead of a cryptic crash code.
  • CI verifies and logs that the runtime DLLs are bundled in the Windows package, and fails the build if any are missing, so a broken installer can never ship.

0.14.14 (2026-09-17)

Self-hosting: a one-line installer for the Docker stack (curl | sh / irm | iex).

Added

  • One-line installer, served from getairwave.tv/install.sh (POSIX) and /install.ps1 (Windows). It checks Docker, asks a short set of questions with sensible defaults (auto-detecting your LAN IP), generates the Postgres password, the auth secret, and a stable Plex client id, writes docker-compose.yml + a fully-documented .env, and brings the stack up. Latest image by default; pin with --version. Pretty prompts via gum when available (auto-downloaded and checksum-verified on Linux/macOS/WSL, plain fallback).
  • An action menu (Install/update, Uninstall, Quit) when run with no arguments, plus --dry-run (preview only), --advanced (bind host paths for Postgres / bumper music, toggle the AI engine or the browser TV player, set extra CORS origins), and --uninstall [--purge].
  • Batteries-included defaults: the AI lineup workflow engine and the browser TV player are on.
  • Re-running updates in place (keeps your secrets); a stable per-user install dir (~/airwave); and a shared airwave_meta record so a run from any shell (PowerShell / WSL / Git Bash) finds the real install and offers to update it (handing off into WSL when needed) instead of starting a duplicate on the same Docker engine.
  • Robust data handling: reuse an existing database (verifying the password, or offer to reset it), and clean up root-owned bind-mounted data on --purge. A build-commit stamp in each served file so you can verify what the CDN is serving.

Changed

  • docker-compose.yml: the Postgres data volume and PGDATA are now parameterizable (POSTGRES_DATA_VOLUME), backward-compatibly, so a host-path bind is configured entirely in .env.
  • scripts/bump-version.ts keeps the installer scripts' version in lockstep with the apps; the getairwave.tv build now also runs when the installer scripts or the compose file change.

0.14.13 (2026-09-17)

TV web player — browser-mode mouse scrubbing: click and drag the DVR scrubber to seek.

Added

  • tv-web browser mode: the full-screen scrubber now supports mouse click and drag to seek (matching tv-tauri). Click anywhere on the bar to jump there; press and drag the thumb to scrub with a live preview, playback paused, committing one seek on release. While dragging, the thumb, fill, and time label track the cursor 1:1 (the easing that would read as lag is turned off for the drag). TV/remote keeps OK for play/pause and the arrow keys for debounced seeking, unchanged.

0.14.12 (2026-09-17)

Added

  • tv-web browser mode: the guide grid now uses a two-step mouse click (matching tv-tauri) instead of tuning on any click. Clicking a program focuses it (its info shows in the featured panel); clicking the already-focused program tunes. Clicking a channel's rail focuses it (the circle becomes the favorite heart); clicking the already-focused rail toggles the favorite. TV/remote keeps the single-click tune and immediate favorite.

0.14.11 (2026-09-17)

Added

  • tv-web browser mode: every full-screen player control now highlights on mouse hover — the play/pause, restart, channel-surf, info, and live buttons, the audio/subtitle/quality circles, and the scrubber. Hover drives the same focus the D-pad uses, so the highlight matches the keyboard exactly (matches tv-tauri). TV is unchanged.

0.14.10 (2026-09-17)

TV web player — browser-mode sidebars: mouse hover on the guide rail, a pinned/collapsible settings rail.

Added

  • Browser mode: the guide's left sidebar expands and highlights on mouse hover (and collapses on leave), driving the same selection / scrim / activate paths as the D-pad — click an item to apply a lens. The sidebar's layout is unchanged (still the edge rail, not inset).
  • Browser mode: the settings sidebar is pinned open with a bottom Collapse/Expand toggle (folds to the icon sliver), hover-highlights its rows, and the content reserves the rail's width so it's never overlaid.

Notes

  • A real TV build is unchanged — both are gated on the browser flag, and D-pad navigation is untouched.

0.14.9 (2026-09-17)

Fixed

  • tv-web browser mode: Spacebar (play/pause) now works while the full-screen feature panel is open. The key layer that handled it is inactive when the panel is up, so the panel now claims playpause itself — previously Space did nothing (e.g. after a click-to-pause opened the panel) until the panel was closed.

0.14.8 (2026-09-17)

TV web player — a desktop "browser mode" so the Docker / desktop-server web player feels right with a mouse and keyboard instead of a 10-foot remote.

Added

  • tv-web now runs in a browser mode on the Docker web player and the packaged desktop server, separate from a real webOS/Samsung TV. It's driven by VITE_IS_BROWSER — baked in the Docker build, injected at serve time by the desktop supervisor (wins over the baked value), and defaulted off for the TV .ipk.
  • Keyboard shortcuts (safe on every platform — no TV remote emits these): Space play/pause, Escape back, [ / ] step channels down/up.
  • Mouse (browser mode only): click the video to play/pause and reveal the chrome; moving the mouse reveals the chrome; a top-left Back button on the full-screen player; the docked mini feed reveals its Full-screen / Close buttons on hover (highlight follows the cursor, while keyboard focus still shows the selection ring); and the program Info view gets a Back button.

Notes

  • A real TV build is unchanged — every browser affordance is gated behind the flag, and the keyboard maps only fire for keys a remote never sends.

0.14.7 (2026-09-16)

Admin — every destructive action uses a proper in-app confirm dialog, and the AI chat history reads cleaner.

Changed

  • Replaced the browser's window.confirm and two hand-rolled confirm modals with one shared in-app alert dialog (a new useConfirm hook on the base-lyra alert-dialog) across the admin app: deleting a channel, package, bumper, user, or AI connection, rebuilding a package from the preset, and refreshing package styling. Destructive actions get a red confirm button.
  • Removing a media source keeps its "type DELETE to confirm" gate, now built on the shared dialog (the confirm button stays disabled until you type DELETE).
  • Deleting an AI connection and deleting an AI chat conversation now ask first (both deleted silently before).
  • The assistant's chat history shows each conversation as a rounded tile with its last-active time.

0.14.6 (2026-09-16)

AI lineup — rebuild a single channel in place (#23), and real in-app confirm dialogs.

Added

  • Rebuild on any channel of a completed AI-lineup run. It re-runs the AI for just that one channel and replaces it in place — reusing its existing number and package, with no wipe, no re-plan, and no renumbering. Every other channel is left untouched (shown as "skipped over"). Refuses to touch a channel that isn't AI-generated. Works off any completed run — an originally-planned run, an applied dry run, or a prior rebuild — because the seed loader chains through seeded runs to recover the plan.
  • ai.rebuildChannels procedure and a rebuild seed mode on the lineup workflow.
  • A "skipped over" run-page status (a muted, dashed marker) for channels a rebuild deliberately left alone — distinct from "skipped" (the builder gave up) and "cancelled" (the run was stopped).

Changed

  • The run page's Stop, Build this plan, and Rebuild actions now use a proper in-app confirmation dialog (a new base-lyra alert-dialog component + a useConfirm hook) instead of the browser's window.confirm.

0.14.5 (2026-09-16)

AI lineup — build a real lineup from a completed dry run, without re-running the AI (#32).

Added

  • Build this plan on a completed dry run's page. A dry run already verifies every channel's filter; this commits that result as a real lineup with no AI and no re-planning — it reads the plan and each channel's committed filter back from the source run (via the workflow observability API), wipes and recreates the AI lineup, re-assigns channel numbers against the real packages, and materializes each channel directly from its verified filter. Same destructive confirm as a normal build; it navigates you to the new run so you can watch it.
  • ai.buildFromRun procedure and a seed mode on the lineup workflow (apply) that drives the above.
  • The dry-run build now records the filter it actually committed (which can differ from the planner's proposal after verification), so applying a dry run reproduces exactly what it previewed.
  • scripts/seed-probe.ts — a permanent inspector that assembles the seed a build-from-dry-run would consume and shows the SDK step layer (one per channel) vs. the observability trace rows (per execution).

Notes

  • Materializing skips the per-channel agent loop entirely, so applying a dry run costs no build tokens.
  • Rebuild-a-single-channel (#23) is a follow-up; the workflow guards that seed mode until it lands.
  • After deploying, bunx workflow build regenerates the handlers (pnpm dev does this on startup when a workflow or an inlined service changed).

0.14.4 (2026-09-16)

AI lineup run lifecycle — Stop now truly cancels a run, and a stale run can no longer resume and wipe the lineup with old settings.

Fixed

  • Stop actually cancels the work (#28). Cancelling a run now aborts the in-flight model calls, not just future steps: each long step (the planner, and every channel build) polls the run status and trips an AbortSignal on its generateText / generateObject call the moment the run is stopped. Previously a ~minutes-long planner or build call kept running (and billing) after Stop.
  • Zombie resume with stale args (#29). A plan that failed with a deterministic error (a malformed or token-truncated response) could stay re-deliverable and, after a container restart days later, resume and destructively wipe-and-rebuild the lineup using the run's old frozen concurrency / token settings. Two guards: a deterministic plan failure is now marked terminal immediately (it fails identically on retry, so the run ends as failed instead of lingering), and on engine startup any non-terminal lineup run whose frozen config no longer matches the current settings is cancelled before it can resume.

Changed

  • A build that is aborted by a cancel is reported as cancelled (gray), not failed (red), so stopping a run no longer paints the run page with false failures. A terminal run also no longer shows builds stuck on "running" if their final trace landed just after polling stopped.
  • The planner step is capped to one retry (it is the single most expensive call, and a deterministic failure is already terminal), so a flaky provider can't silently run it four times.
  • The run page's Replay-timeline gantt no longer has its own slider — the floating action bar's scrubber drives it, and the gantt's playhead follows.

Notes

  • After deploying, bunx workflow build regenerates the workflow handlers (pnpm dev does this automatically on startup when a workflow or a service it inlines has changed).

0.14.3 (2026-09-16)

AI lineup run page — the replay timeline follows the global scrubber and sits up top.

Changed

  • The gantt "Replay timeline" now shares the page's scrub time: the global scrubber in the floating bar moves its playhead (and it still has its own slider that drives the same shared time), while it keeps showing the full run timeline. Moved it above the plan and channel-build frames so it reads as the run overview that drives the detail below.

0.14.2 (2026-09-16)

AI lineup run page — a floating action bar with a global replay scrubber.

Added

  • A floating action bar pinned to the bottom of the run page (sticky, strong shadow) holding the run status, a global replay scrubber, a jump-to-live/end button, and Stop run.
  • The global scrubber replays the WHOLE page through observed time: dragging back re-renders every frame (plan, builds, other phases, step timeline) as of that instant — rows not yet started are hidden and rows still in flight read as running. Jump-to-live re-pins to the latest.

Changed

  • The Stop-run control moved from the header into the floating bar. The gantt "Replay timeline" frame stays as its own self-contained overview (its own slider, always the full timeline) and is not affected by the global scrubber.

0.14.1 (2026-09-16)

AI lineup run page — a Stop button and a replay scrubber.

Added

  • Stop run button in the run detail header (shown while a run is live). Wires the existing ai.cancelLineupRun (which had no caller), with a confirm — marks the run cancelled and stops further steps. (#28; cooperative abort of an in-flight model call is a follow-up.)
  • Replay scrubber: a Gantt of every trace row on a shared time axis with a slider that reconstructs the run's state at any instant (upcoming / running / final), so the order and concurrency of a run are legible. Pure view over the trace rows; pinned to live until you scrub back.

0.14.0 (2026-09-16)

AI lineup — richer, live observability tracing across the whole workflow.

Added

  • The planner step (planLineup) now opens a LIVE trace row up front (status "running") and finalizes it on success/failure, like the channel builds — so the observability page shows the planner in flight instead of a black box until the single (long) call returns.
  • Every workflow phase now records a trace row with a real duration: analyzeLibrary, buildSharedContext, listExistingPackages, createPackages, assignNumbers, and reportLineup previously left no trace at all (only console logs). The run's timeline is now complete end to end.

Notes

  • After deploying, bunx workflow build regenerates the workflow handlers (the apps/server/workflows edits are a build-time transform).

0.13.65 (2026-09-16)

AI assistant — clearer "media source not found" error so the model self-corrects.

Changed

  • When an assistant tool is called with a missing/placeholder mediaSourceId (a valid string slips past schema validation), the error now says to call list_media_sources to get a valid id and retry, instead of a bare "not found." Shared by every source-taking tool, so the model recovers in one step rather than guessing again.

On this page