Commit graph

623 commits

Author SHA1 Message Date
claude
9877ed351f fix(recorders): queue proxy on finalize + custom clip names
- POST /api/v1/assets: when transitioning from 'live' to 'processing'
  with a hi-res key but no proxy, queue a proxy job instead of just
  flipping status='ready'. Recorder-captured clips now get a proxy
  + thumbnail like upload-path assets do
- POST /api/v1/recorders/:id/start now accepts { clipName } in the body;
  operator-supplied name (sanitized to [A-Za-z0-9 ._-], capped at 80)
  overrides the auto-generated <recorder>_<timestamp> fallback
- RecorderRow gets a 'Clip name (optional)' input visible when stopped;
  Enter triggers Record, value sent on POST start, cleared on stop
- New POST /api/v1/assets/:id/generate-proxy and
  POST /api/v1/assets/backfill-proxies for one-shot cleanup of pre-fix
  clips that have a hi-res master but no proxy
2026-05-23 03:41:03 +00:00
claude
b128c9f5a9 fix(metrics): use real job_status enum values (queued/processing/complete) 2026-05-23 03:31:14 +00:00
claude
ef4c301149 feat(home,users): real metrics, working Users row actions + Groups CRUD
- Home: new /api/v1/metrics/home endpoint buckets last 24h of assets,
  jobs done/failed into hourly counts; sparklines now render real
  time-series instead of decorative sine waves
- Home stat cards are now clickable (route to relevant page) and the
  delta lines show real activity ("+N added in last 24h", "N completed")
- Home live-feed tiles use HlsPreview for recorders with a live_asset_id
- Users: row 3-dot menu is now a real popover with Rename / Reset
  password / Delete actions wired to PATCH /users/:id and DELETE
- Users: role is now an inline <select> that PATCHes immediately
- Users: Created column replaces fake 'last active' (no last_login
  tracking yet); group count is real
- Groups tab is now functional — list groups, create, expand to
  show + manage members (add/remove), delete; backed by existing
  /api/v1/groups CRUD
- Policies tab is now an honest 'coming soon' stub
- New icons: key, lock, edit; new .row-menu popover styles
2026-05-23 03:30:10 +00:00
claude
53196d38ce feat(scheduler): recorder scheduling — UI, CRUD, tick loop, recurrence
- New Ingest → Schedule page: upcoming/past/all tabs, status badges
  (pending / recording / completed / cancelled / failed), 10s
  auto-refresh, cancel/delete actions
- New Schedule modal: name, recorder dropdown, datetime-local start/end,
  recurrence (one-shot / daily / weekly), sensible defaults (+5min / +35min)
- Backend: migration 009 (recorder_schedules), routes/schedules.js
  (list/create/edit/cancel/delete), scheduler.js tick loop polling every
  15s; transitions trigger /recorders/:id/start and /stop via in-process
  HTTP so we reuse the full container orchestration path
- Recurring schedules: tick loop auto-queues the next occurrence on
  completion (daily = +24h, weekly = +7d)
- Sidebar + app.jsx route wired in, schedule-row table style added
2026-05-23 03:19:24 +00:00
claude
6398879b56 feat: SDK deployment UI, proxy encoding global settings, S3 env fallback
- Settings: drop AMPP tab, rename GPU/Transcoding → Proxy encoding
  with explicit 'applied to every ingested file' wording, expose
  CPU codec/preset options when GPU is off
- New Capture SDKs tab (Settings): upload Blackmagic / AJA / Deltacast
  SDK archives (.zip / .tar.gz) staged to /sdk/<vendor>/ inside mam-api;
  BMD is fully wired into the FFmpeg build pipeline, AJA + Deltacast
  staging-only pending FFmpeg patches
- mam-api: new /api/v1/sdk routes (multer upload, extract, list, delete);
  Dockerfile gets unzip+tar; docker-compose mounts /mnt/NVME/MAM/sdk:/sdk
- proxy worker now reads proxy-encoding settings from DB on every job,
  builds args for libx264 / NVENC / VAAPI, falls back to libx264 on
  hardware-encode failure
- settings GET /s3 falls back to S3_* env vars when DB is empty so the
  UI reflects what's actually wired (fixes 'not configured' false alarm)
2026-05-23 02:58:32 +00:00
328f7b4f31 feat: live HLS preview, proxy worker fixes, Settings tabs, growing-files + Premier panel
- worker/proxy: scale-to-even filter, analyzeduration 100M, skip images, hasAudio
- worker/promotion: SMB landing zone -> S3 on idle, queues proxy job, status='ready'
- web-ui screens-ingest: HlsPreview component replaces fake LiveStrip/FauxFrame
- web-ui screens-admin: functional Settings tabs (S3, GPU, Growing, SDI, AMPP)
- mam-api /settings/growing: GET/PUT growing-files config
- mam-api /assets/:id/live-path: SMB UNC/POSIX path for live growing assets
- capture-manager: GROWING_ENABLED -> write hires to /growing instead of S3 stream
- recorders.js: pass GROWING_ENABLED to capture container, bind /growing mount
- docker-compose: mount /mnt/NVME/MAM/wild-dragon-growing on mam-api + worker
- premiere-plugin: Mount Live button, Relink-to-HiRes, live->ready status poll
2026-05-22 19:12:53 -04:00
3fc8fbc230 add per-seat/per-stream/per-month strikethrough hero to Tokens page 2026-05-22 17:29:23 -04:00
ceceedf201 probe fallback: basic TCP/UDP connectivity when capture service is offline 2026-05-22 17:26:26 -04:00
4864db03f3 probe: fallback to basic TCP/UDP connectivity check when capture service is offline 2026-05-22 17:22:30 -04:00
8b57a9a35a expand codec list, add MXF container, remove proxy settings (fixed profile) 2026-05-22 17:20:01 -04:00
fa787bbe1e fix hover-to-play: remove status filter so any asset triggers stream fetch 2026-05-22 17:18:56 -04:00
0aa0922fd3 remove hardcoded recorders badge 2026-05-22 17:18:20 -04:00
1abf22623d feat: hover-to-play video preview on library asset cards
Fetches stream URL on hover after 350ms delay; renders muted autoplay
video overlay over the thumbnail. Supports both mp4 and HLS streams.
Only triggers for ready/live assets to avoid pointless API calls.
2026-05-22 16:58:11 -04:00
4afd0c7b21 feat: add /cluster/containers endpoint via Docker socket
Lists all containers on the local host; supports POST /containers/:id/restart.
Falls back to [] gracefully if Docker socket is unavailable.
2026-05-22 16:57:33 -04:00
6f2de45819 feat: wire real video playback via GET /assets/:id/stream
- Fetch stream URL on asset open; show <video> element for mp4/hls
- Use hls.js for live HLS streams (loaded via CDN in index.html)
- Sync video play/pause/seek/timeupdate to React state
- Show loading state while fetching stream, status message when unavailable
- Add Retry processing button for error-status assets
- totalMs derived from video metadata when available, falls back to parseDuration
2026-05-22 13:37:55 -04:00
e3c3d60103 index: add hls.js for live stream HLS playback 2026-05-22 13:31:57 -04:00
81324c8e52 shell: add Field component (used by modal-new-recorder, was missing from global scope) 2026-05-22 12:56:33 -04:00
bec58ab138 screens-asset: fix thumbGrad crash, parseDuration NaN, guard missing ACTIVITY 2026-05-22 12:49:33 -04:00
451bed834f screens-admin: wire all buttons — invite user, export CSV, cluster refresh, container logs/restart, node drain/remove 2026-05-22 12:27:02 -04:00
d00e1c666e screens-ingest: wire delete button on RecorderRow 2026-05-22 12:24:10 -04:00
ddb4cf0c51 feat: add POST /jobs/:id/retry endpoint for re-queuing failed BullMQ jobs 2026-05-22 12:18:53 -04:00
fea0f2962b fix: wire Jobs Retry (POST /jobs/:id/retry) and Delete (DELETE /jobs/:id) buttons 2026-05-22 12:18:23 -04:00
506ee2d695 fix: wire New Project button — modal + POST /projects + state refresh 2026-05-22 12:17:54 -04:00
88689a4eb2 fix: wire Library Upload button to navigate to Upload screen 2026-05-22 12:17:29 -04:00
dc269bec00 fix: make Settings S3 form functional — load from API, save & test 2026-05-22 12:08:10 -04:00
665ab5238d feat: live status polling in RecorderRow, immediate refresh on mount 2026-05-22 11:35:13 -04:00
bb508d3256 feat: add probe button to SRT/RTMP sources, fix node labels 2026-05-22 11:33:45 -04:00
994fd799d0 fix: stop endpoint handles missing/dead containers gracefully 2026-05-22 11:32:44 -04:00
6510871448 fix: implement real upload (XHR + S3 multipart) and fix SDI recorder device_index + manual fallback: modal-new-recorder.jsx 2026-05-22 11:10:01 -04:00
26399f8d0a fix: implement real upload (XHR + S3 multipart) and fix SDI recorder device_index + manual fallback: screens-ingest.jsx 2026-05-22 11:10:00 -04:00
529d14cb6b fix: SDI crash, monitors polling, home RAM fields, editor IN DEV splash, timecode, create recorder API: modal-new-recorder.jsx 2026-05-22 10:55:22 -04:00
fb44bd8aff fix: SDI crash, monitors polling, home RAM fields, editor IN DEV splash, timecode, create recorder API: screens-editor.jsx 2026-05-22 10:55:20 -04:00
24a1d57165 fix: SDI crash, monitors polling, home RAM fields, editor IN DEV splash, timecode, create recorder API: screens-ingest.jsx 2026-05-22 10:55:19 -04:00
48ee66e744 fix: SDI crash, monitors polling, home RAM fields, editor IN DEV splash, timecode, create recorder API: screens-home.jsx 2026-05-22 10:55:18 -04:00
0342aa0a5a fix admin screen: move data destructuring inside components, normalize field names: screens-admin.jsx 2026-05-22 10:15:42 -04:00
406f28c663 feat(ui): wire ingest screens to real API (recorders, capture devices): screens-ingest.jsx 2026-05-22 10:07:13 -04:00
835545e061 feat(ui): wire library, jobs, ingest, editor screens to live API data: screens-editor.jsx 2026-05-22 10:05:57 -04:00
1392e28a88 feat(ui): wire library, jobs, ingest, editor screens to live API data: screens-jobs.jsx 2026-05-22 10:05:56 -04:00
bc03ee866b feat(ui): wire library, jobs, ingest, editor screens to live API data: screens-library.jsx 2026-05-22 10:05:54 -04:00
69f0d130ee feat(ui): wire screens to live API data; add thumbnail lazy-loading: screens-projects.jsx 2026-05-22 10:04:25 -04:00
07af51b05c feat(ui): wire screens to live API data; add thumbnail lazy-loading: screens-home.jsx 2026-05-22 10:04:24 -04:00
3574ae8a43 feat(ui): wire screens to live API data; add thumbnail lazy-loading: visuals.jsx 2026-05-22 10:04:23 -04:00
7dda7cc89c feat(ui): wire data.jsx to real API; add loading gate in app.jsx: app.jsx 2026-05-22 10:02:55 -04:00
98025001e8 feat(ui): wire data.jsx to real API; add loading gate in app.jsx: data.jsx 2026-05-22 10:02:54 -04:00
068e3a0828 fix(ui): replace FauxFrame SVG scenes with clean dark placeholder; strip fake LiveStrip animation: screens-editor.jsx 2026-05-22 09:31:58 -04:00
6ad277275b fix(ui): replace FauxFrame SVG scenes with clean dark placeholder; strip fake LiveStrip animation: visuals.jsx 2026-05-22 09:31:57 -04:00
f58fe95f0d fix(ui): remove placeholder elements — no scanlines, no DEV BUILD, no tweaks panel: screens-projects.jsx 2026-05-22 09:30:50 -04:00
6e763e8270 fix(ui): remove placeholder elements — no scanlines, no DEV BUILD, no tweaks panel: screens-home.jsx 2026-05-22 09:30:49 -04:00
6ac3050a05 fix(ui): remove placeholder elements — no scanlines, no DEV BUILD, no tweaks panel: index.html 2026-05-22 09:30:47 -04:00
e13d111b9f feat(ui): Dragonflight redesign — admin screens (users, tokens, containers, cluster, settings): screens-admin.jsx 2026-05-22 08:24:08 -04:00
1eaf9dff5c Add Z-AMPP UI: screens-ingest + screens-admin: screens-admin.jsx 2026-05-22 08:22:38 -04:00
20dfa504e5 Add Z-AMPP UI: screens-ingest + screens-admin: screens-ingest.jsx 2026-05-22 08:22:37 -04:00
0945f488f6 feat(ui): Dragonflight redesign — ingest, jobs, editor, admin screens: screens-ingest.jsx 2026-05-22 08:20:15 -04:00
bd9dfd2cce Add Z-AMPP UI: screens-jobs + screens-editor + modal-new-recorder: modal-new-recorder.jsx 2026-05-22 08:19:03 -04:00
b8e1796c33 Add Z-AMPP UI: screens-jobs + screens-editor + modal-new-recorder: screens-editor.jsx 2026-05-22 08:19:02 -04:00
f8bd80e38e Add Z-AMPP UI: screens-jobs + screens-editor + modal-new-recorder: screens-jobs.jsx 2026-05-22 08:19:01 -04:00
7007d2df93 Add Z-AMPP UI: screens-asset + screens-projects: screens-asset.jsx 2026-05-22 08:17:17 -04:00
ed3084e60f feat(ui): Dragonflight redesign — screen components batch 1: screens-projects.jsx 2026-05-22 08:17:06 -04:00
4a77c1bed8 Add Z-AMPP UI: screens-home + screens-library: screens-library.jsx 2026-05-22 08:15:36 -04:00
100fc054cc Add Z-AMPP UI: screens-home + screens-library: screens-home.jsx 2026-05-22 08:15:35 -04:00
c0345e47c9 feat(ui): Dragonflight redesign — visuals + tweaks panel: visuals.jsx 2026-05-22 08:15:34 -04:00
a9e0313fe4 Add Z-AMPP UI: visuals + tweaks-panel: tweaks-panel.jsx 2026-05-22 08:13:37 -04:00
d54d960b8f Add Z-AMPP UI: visuals + tweaks-panel: visuals.jsx 2026-05-22 08:13:36 -04:00
2706903353 feat(ui): Dragonflight redesign — foundation JSX files: app.jsx 2026-05-22 08:13:03 -04:00
b6dcecb672 feat(ui): Dragonflight redesign — foundation JSX files: shell.jsx 2026-05-22 08:13:02 -04:00
14bfcabcaf feat(ui): Dragonflight redesign — foundation JSX files: icons.jsx 2026-05-22 08:13:01 -04:00
3b1610a167 feat(ui): Dragonflight redesign — foundation JSX files: data.jsx 2026-05-22 08:13:00 -04:00
d5fd705d66 feat(web-ui): Z-AMPP core JSX files (data, icons, visuals, tweaks, shell, app): icons.jsx 2026-05-22 08:09:04 -04:00
a700124f50 feat(web-ui): Z-AMPP core JSX files (data, icons, visuals, tweaks, shell, app): data.jsx 2026-05-22 08:09:03 -04:00
10952df591 feat(web-ui): add styles-asset and styles-rest CSS: styles-rest.css 2026-05-22 08:07:16 -04:00
352d21496f feat(web-ui): asset detail + rest CSS: styles-rest.css 2026-05-22 08:06:40 -04:00
016adff949 feat(web-ui): asset detail + rest CSS: styles-asset.css 2026-05-22 08:06:39 -04:00
6befb0f46a feat(web-ui): Z-AMPP screen + component CSS: styles-modal.css 2026-05-22 08:03:57 -04:00
e655ccdf64 feat(web-ui): Z-AMPP screen + component CSS: styles-screens.css 2026-05-22 08:03:55 -04:00
2c88fb0a03 feat(web-ui): Z-AMPP design system CSS: styles-fixes.css 2026-05-22 08:02:36 -04:00
7b13d8bd0f feat(web-ui): Z-AMPP design system CSS: styles.css 2026-05-22 08:02:35 -04:00
68df3797f1 feat(web-ui): new design system CSS from Claude Design: styles.css 2026-05-22 08:02:07 -04:00
dccca554e0 Add Dragonflight React SPA design - index.html and CSS: styles-fixes.css 2026-05-21 23:53:21 -04:00
1b63429def Add Dragonflight React SPA design - index.html and CSS: index.html 2026-05-21 23:53:19 -04:00
87da3c0b58 feat: migrate cluster.html to wd-* design system 2026-05-21 23:17:48 -04:00
06551c66a6 feat: migrate editor.html to wd-* design system 2026-05-21 23:16:46 -04:00
136820c8f9 feat: migrate capture.html to wd-* design system 2026-05-21 23:16:29 -04:00
7c88692c1c feat: migrate recorders.html to wd-* design system 2026-05-22 03:16:27 +00:00
1e0015322c feat: migrate projects.html to wd-* design system 2026-05-21 23:15:57 -04:00
6176791174 feat: migrate player.html to wd-* design system 2026-05-21 23:15:18 -04:00
9ff80f8cc1 feat: migrate upload.html to wd-* design system 2026-05-21 23:14:51 -04:00
738d6298d2 feat: migrate edit.html to wd-* design system 2026-05-21 23:14:19 -04:00
a84bc3ecfe feat: migrate api-tokens.html to wd-* design system 2026-05-21 23:14:09 -04:00
daa203a43e feat: migrate index.html to wd-* design system 2026-05-21 23:13:13 -04:00
33d2a4004d feat: migrate jobs.html to wd-* design system 2026-05-21 23:12:58 -04:00
6e43ab30c2 rebrand: Recorders — Dragonflight, ember orange hue-32 2026-05-22 02:54:10 +00:00
cc45cc6347 rebrand: _primitives-smoke — Dragonflight brand 2026-05-21 22:52:12 -04:00
c31933a53c rebrand: Projects — Dragonflight, ember orange hue-32 2026-05-21 22:51:20 -04:00
efe005378a rebrand: Editor (in development) — Dragonflight, ember orange hue-32 2026-05-21 22:49:43 -04:00
5874c93956 rebrand: Editor — Dragonflight, ember orange hue-32 2026-05-21 22:49:00 -04:00
cd5fc3a05c rebrand: upload.html — Dragonflight title + sidebar 2026-05-21 22:44:25 -04:00
e0a2d0c95c rebrand: jobs.html — Dragonflight title + sidebar 2026-05-21 22:42:56 -04:00
4572c88f58 rebrand: capture.html — Dragonflight title + sidebar + hue-32 2026-05-21 22:40:48 -04:00
c752227e20 rebrand: api-tokens.html — Dragonflight title + sidebar 2026-05-21 22:39:20 -04:00
d4e5af459e rebrand: settings.html — Z-AMPP → Dragonflight 2026-05-21 22:35:33 -04:00
29360e38e8 rebrand: users.html — Z-AMPP → Dragonflight 2026-05-21 22:33:21 -04:00
e5f4c00729 rebrand: containers.html — Z-AMPP → Dragonflight 2026-05-21 22:31:41 -04:00
c6aeedb5fc rebrand: Dragonflight — cluster.html brand names 2026-05-21 22:27:36 -04:00
32cf6bf63e rebrand: Dragonflight — tokens.html brand names and footer text 2026-05-21 22:26:24 -04:00
024833cc95 rebrand: Dragonflight — login.html brand names, description text 2026-05-21 22:22:58 -04:00
b4642b3c78 rebrand: Dragonflight — index.html brand names, splash screen, accent colors 2026-05-21 22:22:12 -04:00
b82cc73cf1 rebrand: Dragonflight — home.html wordmark, accent gradients, brand names 2026-05-21 22:19:00 -04:00
873920d27f rebrand: Dragonflight — ember orange accent (hue 266→32) 2026-05-21 22:16:32 -04:00
37767f9939 fix(cluster): pickIp() only treats 172.17.x as docker bridge, not all of RFC1918 172.16/12 2026-05-21 21:27:15 -04:00
30b4deffc6 fix(capture): proper SDK 16 patch via upstream FFmpeg master diff
The previous patch_decklink.py mixed v14_2_1 versioned types (Fix 1 renamed the allocator class) with no-ops for SetVideoInputFrameMemoryAllocator + QueryInterface-around-GetBytes (Fixes 2 & 3). That inconsistency compiled but silently dropped every video frame: VideoInputFrameArrived saw _v14_2_1 allocator output but tried to read it via the SDK 16 unversioned IDeckLinkVideoBuffer path, and the SDK released the buffer before FFmpeg could consume it.

Bisected with the BMD-provided Capture sample at SDK 16 mode 5 (Hp29) which got frames cleanly, confirming the signal was fine and the bug was in FFmpegs decklink demuxer.

Fix: pull libavdevice/decklink_{enc,dec,common}{.cpp,.h} from upstream FFmpeg master (commits past 7.1 that fully rename every decklink interface to its _v14_2_1 versioned form) and apply that diff in reverse during build. Now build is internally consistent and frames flow.

Verified: SDI1 recorder on zampp2 hits 423 frames in 14s @ 29 fps, ProRes HQ at 91 Mbps.
2026-05-22 00:53:03 +00:00
96f0f58e68 capture: yadif deint=1 so progressive SDI passes through unchanged
Bug: yadif=mode=1 unconditionally doubled output framerate for SDI input. On 1080p29.97 progressive sources the encoder produced zero frames (time advanced, size stayed at 1KiB MOV header).

Fix: deint=1 makes yadif only process frames flagged as interlaced; progressive frames pass through at the source rate.
2026-05-22 00:14:02 +00:00
a8656fc1a8 capture: custom FFmpeg 7.1 build with DeckLink + D-Bus mounts + SDI deinterlace
Dockerfile is now a two-stage build that compiles FFmpeg from source with --enable-decklink against the Blackmagic SDK 16.x headers in services/capture/sdk/ (operator-supplied, gitignored). build-with-decklink.sh + patch_decklink.py drive the build.

docker-compose.yml mounts /dev/shm, /run/dbus, /run/systemd into mam-api, capture, web-ui so the BMD runtime can talk to the host.

capture-manager.js wraps SDI sources with -vf yadif=mode=1 (deinterlace).

recorders.html defaults to SDI source type now that we have a working DeckLink path.
2026-05-22 00:01:43 +00:00
1074104d34 fix(capture): FFmpeg 7.x DeckLink compatibility 2026-05-22 00:00:02 +00:00
bbed2a7059 fix(decklink): mount /dev/blackmagic in sidecar + remote node routing via node-agent
Two bugs fixed:
1. SDI capture sidecar never had /dev/blackmagic bound — ffmpeg opened the
   decklink input inside a container with no device nodes, so frame=0.
   Fix: local spawns now push '/dev/blackmagic:/dev/blackmagic' onto Binds
   when source_type='sdi'.

2. recorders.js always spawned sidecars against the local Docker socket
   (zampp1), even when a recorder's node_id pointed at zampp2 (where the
   card is). Fix: resolveNodeTarget() looks up the recorder's cluster node;
   if it's a different hostname the sidecar is spawned via a new
   POST /sidecar/start endpoint on the remote node-agent.

node-agent gains three new routes (all talk to the local Docker socket):
  POST   /sidecar/start         — create + start container (host network,
                                   privileged, /dev/blackmagic bind for sdi)
  DELETE /sidecar/:id           — stop + remove
  GET    /sidecar/:id/status    — inspect + poll capture service

docker-compose.worker.yml: add /var/run/docker.sock and LIVE_DIR to
node-agent so it can spawn sidecars, and document build-capture prerequisite.: recorders.js
2026-05-21 18:51:10 -04:00
8186b181cc fix(decklink): mount /dev/blackmagic in sidecar + remote node routing via node-agent
Two bugs fixed:
1. SDI capture sidecar never had /dev/blackmagic bound — ffmpeg opened the
   decklink input inside a container with no device nodes, so frame=0.
   Fix: local spawns now push '/dev/blackmagic:/dev/blackmagic' onto Binds
   when source_type='sdi'.

2. recorders.js always spawned sidecars against the local Docker socket
   (zampp1), even when a recorder's node_id pointed at zampp2 (where the
   card is). Fix: resolveNodeTarget() looks up the recorder's cluster node;
   if it's a different hostname the sidecar is spawned via a new
   POST /sidecar/start endpoint on the remote node-agent.

node-agent gains three new routes (all talk to the local Docker socket):
  POST   /sidecar/start         — create + start container (host network,
                                   privileged, /dev/blackmagic bind for sdi)
  DELETE /sidecar/:id           — stop + remove
  GET    /sidecar/:id/status    — inspect + poll capture service

docker-compose.worker.yml: add /var/run/docker.sock and LIVE_DIR to
node-agent so it can spawn sidecars, and document build-capture prerequisite.: index.js
2026-05-21 18:51:09 -04:00
539429c058 tokens.html: remove orphan sidebar-footer + duplicate /nav; use main flex wrapper 2026-05-21 16:40:28 -04:00
01a9d6c3db settings.html: remove orphan sidebar-footer + duplicate /nav; use main flex wrapper 2026-05-21 16:40:28 -04:00
Zac
ddd3b3eca1 Revert shell.css primitive rework (5 commits eea1ed6..a8f5bce) 2026-05-21 20:34:08 +00:00
a8f5bce9ee home.html: drop per-page body+shell rules (now in shell primitive) 2026-05-21 16:22:49 -04:00
683f0ff101 containers.html: drop inline shell hack (now in shell primitive) 2026-05-21 16:22:49 -04:00
47c0e1f933 users.html: drop inline shell hack (now in shell primitive) 2026-05-21 16:22:48 -04:00
6cad11f687 app.css: import new shell primitive; drop redundant html base rule (now in shell.css) 2026-05-21 16:22:47 -04:00
eea1ed6bcb shell.css: codify body + .wd-shell + .wd-main as a primitive (fix dead-space layout bug) 2026-05-21 16:22:46 -04:00
c6bcbbd214 web-ui(wave 2): migrate settings.html to new primitives
Surgical migration: stylesheet swap to /dist/app.css + sidebar markup
updated to wd-sidebar primitives. Page-specific content + JS unchanged.
2026-05-21 13:35:04 -04:00
e7495dfe29 web-ui(wave 2): migrate tokens.html to new primitives
Surgical migration: stylesheet swap to /dist/app.css + sidebar markup
updated to wd-sidebar primitives. Page-specific content + JS unchanged.
2026-05-21 13:35:03 -04:00
5650b279c3 web-ui(wave 2): migrate users.html to new primitives 2026-05-21 13:33:22 -04:00
596fe228ed web-ui(wave 2): migrate containers.html to new primitives 2026-05-21 13:33:22 -04:00
e0cfe80a9e web-ui(wave 2): migrate home.html to new primitives
Swap stylesheet to /dist/app.css. Sidebar markup ported to wd-sidebar /
wd-nav-item / wd-sidebar-* primitives (active state = leading accent dot).
Logout button promoted to wd-btn--ghost--sm--icon.

Hero (portrait, wordmark, tagline) and the 10 illustrated cards keep
their bespoke design — they're a brand moment. Hardcoded oklch values
in the inline style replaced with var(--accent-bright), var(--signal-bad),
var(--bg-base), var(--accent-border), var(--overlay), etc. wherever the
brand palette already provides them.

All JS ids preserved (assetCount, projectCount, recorderCount,
containerCount, nodeCount, jobCount, ingestCount, captureStatus,
tokenBurn, userAvatar, userName, userRole, logoutBtn, systemBuild).
loadStats() poll cycle unchanged.
2026-05-21 13:19:16 -04:00
16a34a2fad web-ui(wave 2): migrate login.html to new primitives
Keeps the hero + AMPP Safe stamp + brand panel. Right column now uses
wd-form-group / wd-input / wd-label / wd-btn primitives loaded from
/dist/app.css. All JS ids and handlers preserved verbatim (login-form,
setup-form, flash, show-setup, show-login).

Visual changes: tighter form spacing, focus rings use accent-subtle
ring, flash messages use the top-strip toast pattern.
2026-05-21 13:09:39 -04:00
75b94a5025 web-ui(wave 2): token cleanups from wave-1 code review
Promoted 14 new tokens (--accent-hover, --signal-{good,bad,warn}-hover,
--accent-bright, --thumb-black, --overlay, --shadow, --ease-out-{quart,expo},
--dur-{fast,normal,slide}, --z-topbar) and substituted every raw oklch /
cubic-bezier / hardcoded z-index occurrence in the 12 primitive files.

cubic-bezier appearances dropped from 8 files to 0 (only in tokens.css).
Bundle byte count: 138 KB -> 139 KB. Visual regression: zero (smoke page
still renders identically).
2026-05-21 17:08:02 +00:00
447b2b2b81 web-ui: add _primitives-smoke.html for wave-1 visual QA
Delete at end of wave 4 once every shell page has migrated to the new primitives.
2026-05-21 12:43:00 -04:00
3b89cf2d5f web-ui: fix wave-1 build pipeline (primitives missing from bundle)
Three bugs found during task 20 verify, all fixed:

1. Tailwind CLI does NOT read postcss.config.js. Switched Dockerfile to
   npx postcss + postcss-cli so the postcss plugin chain actually runs.

2. postcss-import was not installed but app.css uses @import for the
   primitive component files. Added postcss-import + cssnano (for prod
   minification under --env production).

3. @import statements must come BEFORE any other rules per CSS spec.
   app.css had @tailwind base/components ABOVE @import, so postcss-import
   silently skipped every component @import. Moved all @imports to the
   top, @tailwind directives below. Bundle went from 121KB with 0 wd-*
   classes to 138KB with 116 wd-* classes.

Also added tailwind safelist for wd-/is-/nav-dev-badge so the wave-2
migration of HTML files cannot accidentally tree-shake primitives.
2026-05-21 16:41:55 +00:00
f9236101b9 web-ui: wave-1 finish — self-host fonts + multi-stage Dockerfile
Fonts: Inter 400/500/600 + JetBrains Mono 400/600 (woff2 from rsms/inter and JetBrains/JetBrainsMono github).

Dockerfile: two-stage build. Stage 1 (node:20-alpine) runs tailwindcss --minify to emit public/dist/app.css. Stage 2 (nginx:alpine) ships the static result.

NOTE on task 19 (nginx caching for /dist /fonts): SKIPPED. The existing nginx.conf already caches *.css and *.woff2 for 1y immutable via the generic location ~* \\.(css|...|woff2|...)$ regex. Adding explicit /dist/ and /fonts/ blocks would be redundant.
2026-05-21 16:32:55 +00:00
6561cecf33 web-ui: fix corrupted .gitignore from earlier patch 2026-05-21 12:31:18 -04:00
a4bb6e7b0c web-ui: add node_modules + public/dist to .gitignore for wave-1 build 2026-05-21 12:30:56 -04:00
1f995c9029 web-ui: wave-1 foundation — services/web-ui/tailwind.config.js 2026-05-21 12:30:41 -04:00
891a8f82b7 web-ui: wave-1 foundation — services/web-ui/src/css/components/topbar.css 2026-05-21 12:30:40 -04:00
23ae848f5b web-ui: wave-1 foundation — services/web-ui/src/css/components/tokens.css 2026-05-21 12:30:40 -04:00
a16c235f71 web-ui: wave-1 foundation — services/web-ui/src/css/components/toast.css 2026-05-21 12:30:39 -04:00
e56704b69f web-ui: wave-1 foundation — services/web-ui/src/css/components/slide-panel.css 2026-05-21 12:30:39 -04:00
1c0ed05ac9 web-ui: wave-1 foundation — services/web-ui/src/css/components/sidebar.css 2026-05-21 12:30:38 -04:00
a6c9f88068 web-ui: wave-1 foundation — services/web-ui/src/css/components/motion.css 2026-05-21 12:30:38 -04:00
310eca0810 web-ui: wave-1 foundation — services/web-ui/src/css/components/list-row.css 2026-05-21 12:30:38 -04:00
a76e6b9a81 web-ui: wave-1 foundation — services/web-ui/src/css/components/form-controls.css 2026-05-21 12:30:37 -04:00
836a163cc8 web-ui: wave-1 foundation — services/web-ui/src/css/components/field-group.css 2026-05-21 12:30:37 -04:00
052a880b0f web-ui: wave-1 foundation — services/web-ui/src/css/components/empty-state.css 2026-05-21 12:30:36 -04:00
2f3e04cfc3 web-ui: wave-1 foundation — services/web-ui/src/css/components/card-operational.css 2026-05-21 12:30:36 -04:00
080f82e198 web-ui: wave-1 foundation — services/web-ui/src/css/components/card-asset.css 2026-05-21 12:30:36 -04:00
c08025eeb2 web-ui: wave-1 foundation — services/web-ui/src/css/components/button.css 2026-05-21 12:30:35 -04:00
30cb6663dd web-ui: wave-1 foundation — services/web-ui/src/css/components/badge.css 2026-05-21 12:30:35 -04:00
e256a771d5 web-ui: wave-1 foundation — services/web-ui/src/css/app.css 2026-05-21 12:30:34 -04:00
3df6a4434e web-ui: wave-1 foundation — services/web-ui/postcss.config.js 2026-05-21 12:30:34 -04:00
9d99811272 web-ui: wave-1 foundation — services/web-ui/package.json 2026-05-21 12:30:33 -04:00
fd955076dd web-ui: fix codec/settings panel clipping in recorders.html
Flex-child overflow footgun: .slide-panel-body had flex:1 and overflow-y:auto
but without min-height:0 it never shrank below content height, so the new
Master/Proxy codec blocks overflowed past the panel bottom and the footer
(Cancel / Probe / Save buttons) was unreachable. Lock the panel to 100vh,
add min-height:0 to the body. Also drop redundant margin-bottom on
.codec-block since the body already has gap spacing.
2026-05-21 14:10:24 +00:00
89ceef444e web-ui: include auth-guard.js on home.html and projects.html so the IN DEV badge renders on those pages too 2026-05-21 14:01:52 +00:00
00bf112b5a web-ui: replace editor.html with under-construction screen
The timeline editor isn't ready yet. Replace the 49 KB prototype page
with a clean construction screen (still rendering the standard sidebar
so users can navigate away). The 'IN DEV' badge on the sidebar nav item
is injected by auth-guard.js across all pages.
2026-05-21 09:59:29 -04:00
16a1fe604f web-ui: tag IN DEV pages in sidebar from auth-guard
Adds a tiny CSS rule + DOM patch that walks .nav-item links on every
page and appends an 'IN DEV' badge to those matching a known in-dev
page (currently just editor.html). Avoids touching all 13 HTML files
for the same single-line nav change.
2026-05-21 09:59:29 -04:00
f6c0594088 web-ui: rewrite recorders.html with tabbed codec settings + BMD card picker
- Replace flat codec dropdowns with Master/Proxy blocks, each with
  Video/Audio/Container tabs.
- Replace BM1/BM2 device dropdown with cluster-node picker plus
  inline BMDCards.render(...) SVG -- click a port to set device_index.
- Wire full codec field set (video bitrate, framerate, audio codec/
  bitrate/channels, container) end-to-end to /api/v1/recorders.
- Auto-hide bitrate input for profile-driven codecs (ProRes, DNxHR,
  PCM, FLAC); show for H.264/265/NVENC, AAC, AC-3, Opus, DNxHD.
- Resolve SDI source display in cards via /cluster/devices/blackmagic
  (hostname + model + port) instead of raw device index.

Finishes the pending item from
docs/superpowers/plans/2026-05-21-cluster-codec-revamp.md.
2026-05-21 09:47:32 -04:00
4a3a672cbe cluster: stable hostname for mam-api, jq-based smoke test
mam-api self-heartbeat now reads NODE_HOSTNAME so primary rows survive container restarts instead of resurrecting with the random container ID. test-cluster.sh rewritten to use jq (the python f-strings had a parse bug that silently passed the IP check) and limited the docker-bridge alarm to 172.17.x since the user LAN occupies 172.18.0.0/16.
2026-05-21 11:50:52 +00:00
d39f86d9c5 ui: add bmd-card.js — visual DeckLink port picker
Renders an inline SVG of the detected card (Duo 2 / Quad 2 / Mini
Recorder / Mini Monitor / UltraStudio 4K Mini, with a generic fallback)
showing each connector in its real physical position. Click to select.
Used by recorders.html for SDI source selection.
2026-05-21 00:19:51 -04:00
f4a83eedc4 capture-manager: dynamic ffmpeg args from per-recorder codec config
Adds a VIDEO_CODECS / AUDIO_CODECS / CONTAINER catalogue and a
buildEncodeArgs() that composes -c:v / -c:a / -b:v / -b:a / -r / -ac / -f
from the recorder's saved settings. Master and proxy each get their own
codec stack, both honour the container format chosen in the UI, and the
S3 keys now use the actual container extension instead of hardcoded mov/mp4.
2026-05-21 00:19:00 -04:00
4c65753358 recorders route: accept full codec field set + node/port pinning
POST/PATCH now persist all new codec columns via a whitelist. /start
forwards every codec setting to the capture container as an env var. The
live-asset created during /start now uses the recorder's container ext
(.mov vs .mp4 etc.) instead of always assuming .mov.
2026-05-21 00:17:45 -04:00
0efef0d81b cluster route: fallback IP from request + /devices/blackmagic endpoint
Heartbeat handler now overrides 172.x docker bridge IPs with the
request's source address when the request itself came from a real LAN.
Adds GET /devices/blackmagic that flattens every node's capabilities so
the recorder UI can show a card picker spanning the whole cluster.
2026-05-21 00:16:36 -04:00
485af25d4a capture bootstrap: forward every codec env var to captureManager.start 2026-05-21 00:16:03 -04:00
3b4af6ef11 node-agent: prefer NODE_IP and skip docker bridge interfaces
In bridge mode the agent was reporting the container's 172.x address
because the first non-internal interface in os.networkInterfaces() was
docker0. Now honours NODE_IP, skips lo/docker*/br-*/veth*/etc, and
down-ranks the 172.16-31 range so real LAN IPs win. Also exposes the
detected IP on /health for the onboarding script to print.
2026-05-21 00:15:03 -04:00
049beb8818 recorders: add granular codec / container / port columns
Expands recorders with video bitrate, framerate, audio codec / bitrate
/ channels, container format, and a node_id/device_index pair so the UI
can pin SDI recorders to a specific node + DeckLink port instead of
relying on a flat "BM1/BM2" index. capture-manager.js consumes these via
env vars and builds ffmpeg args from them.
2026-05-21 00:14:11 -04:00
a39c9831c5 cluster: dedupe rows + enforce unique hostname index
Migration 004 wrapped table creation in IF NOT EXISTS, so deploys with
a pre-existing cluster_nodes table never picked up the inline UNIQUE
constraint and accumulated duplicate hostnames on every container
restart. This migration purges older duplicates and adds the unique
index idempotently so the ON CONFLICT (hostname) upsert finally works.
2026-05-21 00:14:01 -04:00
066b9b17d3 feat: expand GPU transcoding settings (extension, framerate, rc mode, audio) 2026-05-20 23:41:42 -04:00
629022ab5f fix(worker): use npm install instead of npm ci — no package-lock.json present 2026-05-20 23:29:15 -04:00
cc8ee63639 fix(node-agent): replace express with built-in http — no external deps needed 2026-05-20 22:59:03 -04:00
21d31f1678 mam-api: switch base image to node:22-slim (glibc) so host nvidia-smi binary runs 2026-05-20 19:19:33 -04:00
74299629e6 feat: detect GPUs via nvidia-smi and populate cluster_nodes capabilities 2026-05-20 17:25:11 -04:00
a4b9b5be82 fix: prefer NODE_IP env var in getLocalIp() for Docker deployments 2026-05-20 16:16:09 -04:00
a926da1c30 feat: add settings key-value table migration 2026-05-20 15:57:23 -04:00
11e1de1cf8 feat: add S3 / Object Storage settings section 2026-05-20 15:55:34 -04:00
7032cee6b3 feat: call loadS3ConfigFromDb() on startup after migrations 2026-05-20 15:53:26 -04:00
02cfa68b92 fix(assets): replace static S3_BUCKET with getS3Bucket() for dynamic config 2026-05-20 15:49:40 -04:00
737e69d72f fix(upload): replace static S3_BUCKET with getS3Bucket() for dynamic config 2026-05-20 15:48:48 -04:00
ab504841c3 feat(settings): add S3 / object-storage settings routes (GET, PUT, test) 2026-05-20 15:48:14 -04:00
b1457f0aad feat(s3): dynamic DB-driven config with rebuildS3Client + Proxy export 2026-05-20 15:47:40 -04:00
beb58d3cd6 Add Settings nav link to sidebar 2026-05-20 15:07:36 -04:00
2f48d0243b Add Settings nav link to sidebar 2026-05-20 15:06:41 -04:00
cfdd0d1a55 Add Settings nav link to sidebar 2026-05-20 15:05:16 -04:00
0433fc8805 fix(home): prevent bottom cutoff — safe center + remove min-height: 100% 2026-05-20 15:01:50 -04:00
777fa7fc2b Add Settings nav link to sidebar 2026-05-20 14:56:04 -04:00
53392608e5 Add Settings nav link to sidebar 2026-05-20 14:51:37 -04:00
b7c7bb1662 Add Settings nav link to sidebar 2026-05-20 14:50:02 -04:00
dd1c40c9c8 Add Settings nav link to sidebar 2026-05-20 14:45:49 -04:00
7c37eababd Add Settings nav link to sidebar 2026-05-20 14:42:46 -04:00
53805f2c59 Add Settings nav link to sidebar 2026-05-20 14:37:38 -04:00
74e87359e2 Add Settings nav link to sidebar 2026-05-20 14:36:03 -04:00
5e2683aba7 Add Settings nav link to sidebar 2026-05-20 14:32:34 -04:00
fe921d0444 Add Settings nav link to sidebar 2026-05-20 14:29:41 -04:00
12a52c40c9 feat: settings.html — GPU transcoding, SDI capture routing, AMPP integration 2026-05-20 14:21:18 -04:00
76281b7564 feat: GPU worker Dockerfile using CUDA base with ffmpeg NVENC support 2026-05-20 14:18:55 -04:00
1725ec1de9 feat: settings routes for hardware inventory, GPU transcoding, capture service URL 2026-05-20 14:18:43 -04:00
dd3c2894f6 feat: cluster heartbeat stores capabilities (GPU/BMD hardware detection) 2026-05-20 14:18:22 -04:00
a941f609f0 feat: node-agent detects NVIDIA GPUs and Blackmagic DeckLink cards, reports in heartbeat 2026-05-20 14:18:07 -04:00
86d2960b60 feat: add capabilities column to cluster_nodes (migration 005) 2026-05-20 14:17:44 -04:00
5161644205 fix(capture): handle non-JSON responses from capture service gracefully 2026-05-20 13:55:06 -04:00