Commit graph

220 commits

Author SHA1 Message Date
95fa1b83b6 feat: add admin sidebar section and user footer widget 2026-05-18 13:06:40 -04:00
4213c8a7b3 feat: auth system — CSS page transitions, API helpers, users/tokens pages 2026-05-18 13:00:31 -04:00
c7d8be9f28 feat: auth system — CSS page transitions, API helpers, users/tokens pages 2026-05-18 12:59:36 -04:00
2d21d4d44d feat: auth system — CSS page transitions, API helpers, users/tokens pages 2026-05-18 12:58:24 -04:00
d0f9848717 feat: auth system — CSS page transitions, API helpers, users/tokens pages 2026-05-18 12:57:50 -04:00
28a4b24911 feat(mam-api): wire users, groups, and tokens routes into main app 2026-05-18 12:51:14 -04:00
1e4c92c2df feat(mam-api): add personal API token routes 2026-05-18 12:50:58 -04:00
d23ca9be73 feat(mam-api): add groups admin CRUD routes with member management 2026-05-18 12:50:49 -04:00
5ed604136c feat(mam-api): add users admin CRUD routes 2026-05-18 12:50:33 -04:00
f93feb6e40 feat(mam-api): add auth middleware with session and Bearer token support 2026-05-18 12:50:21 -04:00
f57ed1b498 feat(mam-api): add auth middleware with session and Bearer token support 2026-05-18 12:45:15 -04:00
2d8c44c529 feat(mam-api): add groups and API tokens schema patch 2026-05-18 12:45:06 -04:00
6bd97a2a03 feat(meme): Token Pricing page with usage chart + AMPP-style Z-AMPP SVG wordmark on home + Tokens tile/nav everywhere 2026-05-18 11:05:30 -04:00
1f4750a1b4 feat(meme): Token Pricing page — gentle ribbing of metered-compute broadcast platforms 2026-05-18 10:56:55 -04:00
c781a469f3 feat(recorders): align with home/projects aesthetic — brand-blue gradient, refreshed cards, tile selectors, slide-panel polish 2026-05-18 10:49:46 -04:00
32bce2e263 feat(editor): splice tool (B/S key + Split button), thumbnail hydration via signed URL, enable Export (draft for now) 2026-05-18 10:25:53 -04:00
3ae150ad53 feat(editor-native): repoint Editor links from openreel (:47435) to in-house /edit.html 2026-05-18 10:18:14 -04:00
2e1bcd655f feat(editor-native): Phase A — single-track editor logic (asset library, preview, in/out markers, drafts) 2026-05-18 10:17:31 -04:00
beb8f31674 feat(editor-native): Phase A — single-track editor shell (HTML scaffold) 2026-05-18 10:16:12 -04:00
a3596265eb feat(brand+home): swap sidebar to Wild Dragon logo, add favicon everywhere, fix home counters (status= not state=) 2026-05-18 10:13:08 -04:00
0e48a8d70f feat(brand): add Wild Dragon logo + favicon 2026-05-18 14:11:29 +00:00
5b557418f8 feat(home): drop Settings tile (not workspace nav; access via topbar gear) 2026-05-18 10:07:53 -04:00
81257b5201 feat(nav): add Home + Projects to sidebar across all pages; redirect login to home.html; bump image cache to v=hardhat3 2026-05-18 10:03:32 -04:00
623e38ae27 feat(home): redesign in AMPP layout — wide preview cards on brand-blue gradient, hardhat avatar centerpiece 2026-05-18 10:01:37 -04:00
1c7329ef35 feat(brand): cleaned hardhat photo (stray sketch lines removed via blob isolation) 2026-05-18 09:59:53 -04:00
efebf38271 feat(projects): project + bin management page (CRUD on /api/v1/projects + /api/v1/bins) 2026-05-18 09:58:34 -04:00
b9879d76b7 feat(home): add Home landing page modeled on AMPP — hardhat hero + workspace tiles 2026-05-18 09:56:20 -04:00
230944fc4b fix(recorders): kill the timer/status flap by computing live values inline + skipping unchanged DOM rebuilds 2026-05-18 09:47:03 -04:00
57116dde42 feat(recorders): stable elapsed timer + live HLS preview on the card; optimistic signal default 2026-05-18 09:40:42 -04:00
57c3871cc1 feat(brand): hardhat photo + Z-AMPP name on every page (library, upload, capture, jobs, recorders, settings) 2026-05-18 09:28:49 -04:00
a9c16d9509 fix(capture): wire bootstrapAutoStart() + add missing captureManager/MAM_API_URL/server (regression from earlier conflict resolution) 2026-05-18 09:25:55 -04:00
d8229e6f3f feat(probe): pre-flight reachability + actionable SRT/RTMP error messages 2026-05-18 07:57:48 -04:00
f181eb6d34 fix(splash): bust image cache + correct aspect ratio so the hardhat photo loads after redeploy 2026-05-18 07:45:59 -04:00
7d76f9c549 feat(growing-files): Phase 1 - live HLS preview during recording
While a recorder is running, the capture container tees an HLS
stream into /live/<assetId>/ alongside the ProRes master upload.
The asset row is pre-created at recorder start with status='live'
so the clip appears in the library immediately. /api/v1/assets/:id/stream
returns the HLS playlist URL until recording stops, then proxy.

* docker-compose: shared wild-dragon-live mount on api/capture/web-ui
* migration 001-add-live-status: idempotent ALTER TYPE for asset_status
* mam-api: runMigrations() on boot; recorders.js pre-creates live asset
  + passes ASSET_ID; assets.js POST upserts on existing live row instead
  of inserting a duplicate, and stream route returns HLS for live assets
* capture: parallel HLS ffmpeg into /live/<assetId>/; ASSET_ID env
* web-ui: nginx serves /live/, preview.js loads hls.js, LIVE badge added
2026-05-18 07:29:50 -04:00
Zac
6a8e4ac250 fix(editor): show loading banner during auto-import so Edit feels responsive
Clicking Edit on the preview modal worked, but the user only saw an empty editor for ~25s while the recovery + format-chooser cycle ran and the bridge waited for a stable project. Looked broken. Now: a centered top banner appears the moment the bridge detects ?asset=, reads Loading clip from Z-AMPP MAM, switches to Clip ready in media bin on success, or surfaces the failure. Project-stability gate tightened from 1500ms to 600ms so the import lands sooner.
2026-05-17 22:44:08 -04:00
Zac
e390f0efab fix(editor): asset auto-import now lands cleanly into the media bin
Three problems were blocking the round-trip. Each fixed.

* MediaBridge.importFromURL went through the file-import service but not the Zustand store, so the media bin stayed empty. mam-bridge now calls window.__projectStore.getState().importMedia(file) which is what the actual UI uses. project-store.ts exposes useProjectStore on window for that hook.
* rustfs serves the proxy with content-type application/octet-stream; the editor rejects with DECODE_ERROR on that mime. Bridge now forces video/mp4 (or audio/wav, video/webm, etc.) based on the asset filename.
* The Recover Your Work modal and the Welcome tour blocked editor initialization. Bridge now auto-clicks Start Fresh and Skip Tour (alongside the format chooser), and waits 1.5s of project-id stability before calling importMedia so it does not get clobbered by the project-replacement cycle. One-shot guard prevents duplicate imports.
2026-05-17 22:20:49 -04:00
Zac
b68f0c6aba feat(editor): integrate openreel-video as services/editor with MAM hooks
Vendored Augani/openreel-video (MIT) into services/editor and wired it to the MAM. Editor runs as its own container on port 47435. Library assets pull in via ?asset=<uuid>; render exports route back via POST /api/v1/upload/simple. Sidebar Editor link on every page; Edit button on every preview modal. See services/editor/INTEGRATION.md for the patch map.
2026-05-17 21:44:37 -04:00
Zac
562881f0db fix(jobs): stall detection + manual kill button so 5h-stuck actives can't happen
A thumbnail job from earlier stayed 'active' for 6+ hours: worker was restarted at 70% progress, BullMQ left it in the active set, and there was no stall reaper because the worker was created with only the default options.

Worker now passes stalledInterval: 30000, lockDuration: 60000, lockRenewTime: 15000, maxStalledCount: 1 to the Worker constructor. If a run dies, BullMQ reclaims the job back to waiting within 30s and a 'stalled' event is logged. Otherwise the lock is renewed mid-job.

Jobs UI gains a 'Kill' button per row next to Details. Calls DELETE /api/v1/jobs/:id which already removes the job from Redis. Use it on any row that looks stuck.
2026-05-17 19:10:19 -04:00
Zac
e441176961 feat(design): broadcast ops console redesign sweep
Confirmed shape brief: ops-console direction, balanced density, blue committed accent, readability emphasized.

Design system: surface scale to 5 steps tinted toward hue 266; text contrast lifted (secondary 62->72%, tertiary 44->52, added text-disabled); borders gained faint variant; status tokens renamed semantically (signal-good/warn/bad/idle); typography upgraded (Inter 400/500/600, JetBrains Mono for numerics, new 2xl/3xl/tc scale steps).

New components: tc-display timecode classes with blue glow; tally-word with good/warn/bad/rec variants; signal-strip flutter bar with modifiers; chip dense monospaced pill; manifest table styles.

Topbar status strip: new js/topbar-strip.js auto-injects a 28px strip on every page with wall clock, page name, live API latency, and system-pulse dot.

Per-page: recorders gain live signal-strip above fps line; capture timecode bumped 38->64px with blue glow; ingest drop zone slimmed 200->88px side-by-side layout so manifest gets the real estate.
2026-05-17 19:05:22 -04:00
Zac
bab24e156a feat(recorders): probe sources + reflect real signal in main status
Two things that together stop bogus URLs from masquerading as a recording:

PROBE BUTTON in the New Recorder panel. Before you commit to record, hit Probe Source - the capture container runs ffprobe with a 10s timeout against the URL and returns the parsed streams. UI shows green Signal Detected with codec/resolution/fps/audio, or red No Signal Detected with the actual ffprobe error message. For SDI it lists DeckLink devices. Listener-mode sources cannot be probed standalone (would block waiting for a publisher) and the UI says so.

MAIN STATUS LABEL ON THE RECORDING CARD now mirrors the live signal instead of hardcoding Recording. So a recorder pointed at a dead URL goes Connecting... -> Connection error (red) instead of looking like everything is fine. When frames actually start arriving the label flips to Recording (blue) and the dot turns blue. If a previously-good stream drops the label switches to Signal lost (red).

API:
* capture: POST /capture/probe runs ffprobe and returns { ok, streams, format, error? }
* mam-api: POST /api/v1/recorders/probe proxies through to the capture sidecar with a 15s outer timeout
2026-05-17 18:39:21 -04:00
Zac
f2b8d5dc4b feat(splash): transparent PNG so the subject composites cleanly
The source image had a black border baked in. Knocked out the dark pixels into an alpha channel so the figure now floats on whatever surface is behind it — the dark gradient on the splash, the panel surface on the loading indicator, anywhere.

Pipeline: source -> resize 1200w -> python/PIL alpha-from-luminance with soft 22-55 luminance ramp -> 8-bit RGBA PNG (267KB).
2026-05-17 18:39:21 -04:00
Zac
349bc5a41d feat: multi-select + bulk move/copy/delete, brand blue, hardhat loader
* Library cards now show a checkbox on hover (and persistent when selected). Click checkbox = toggle, shift-click = range. Plain click on a card with an active selection extends/shrinks the selection instead of opening preview. Floating pill at the bottom shows count + Move / Copy / Delete / Clear. Move + Copy open a tiny bin picker (current project, default to current bin).

* mam-api/routes/assets.js: PATCH /:id now also accepts bin_id (null = move out of bin). New POST /:id/copy makes a reference-copy of the asset row (same S3 keys, new id) into the target bin/project.

* api.js: moveAsset(id, binId) and copyAsset(id, {binId, projectId}) helpers.

* All accent tokens swapped from the amber oklch(76% 0.178 52) to the Wild Dragon signature blue oklch(55% 0.20 266) = #1f3ad0 ish. Login splash + first-load splash + signal-receiving + button primary all picked it up automatically through common.css.

* Loading indicator across the app uses the AMPP Safe hardhat photo gently pulsing with a tiny blue dot underneath. .ampp-loading component lives in common.css with --sm / --xs / --inline variants. Replaces the plain "Loading assets…" empty state in index.html.
2026-05-17 14:48:34 -04:00
Zac
f99f07e0e7 feat: AMPP Safe splash on login + first-visit overlay
Adds the BMG-branded "AMPP Safe" hardhat photo as the visual identity for the auth + first-load surfaces.

* services/web-ui/public/img/ampp-safe.jpg (52 KB, 1200w optimized JPEG)
* services/web-ui/public/login.html: full redesign as a two-column hero + sign-in panel. Hero shows the hardhat photo full-bleed with a subtle AMPP Safe pill badge and broadcast-safe caption. Login + first-run admin setup forms unchanged functionally.
* services/web-ui/public/index.html: brief first-visit splash overlay (~1.4s) using the same image. Dismisses to the library and uses sessionStorage so it only shows once per session.
2026-05-17 13:10:47 -04:00
Zac
72545126c4 fix: delete asset actually deletes
Trash icon in the library was firing PATCH /assets/:id with {status:"deleted"}. The PATCH route only accepts display_name/tags/notes so it returned "No fields to update" and the asset stayed put.

* api.js: add deleteAsset(id, {hard}) helper hitting the real DELETE route.
* index.html: deleteAssetPrompt now calls deleteAsset (soft archive). Confirm dialog reworded to match.
* mam-api/routes/assets.js: list endpoint hides status=archived by default. Pass ?include_archived=true to see them in a future restore-from-trash view. Filtering by ?status=archived still works for power users.
* All HTML: bump api.js cache-buster v=4 -> v=5 so the new helper is fetched.
2026-05-17 12:55:55 -04:00
Zac
ea28c5189d feat: in-library asset preview + Premiere plugin installer
Click any asset card to open a modal with the H.264 proxy playing inline (or audio/image, per media_type). Esc or click outside closes. Sidebar shows status/codec/resolution/fps/duration/size/created plus tags and notes.

Plugin install side: added install-windows.ps1 that copies the CEP panel to %APPDATA%\Adobe\CEP\extensions, flips PlayerDebugMode=1 across the CSXS.8-13 hives, and prints the next steps. Plugin already wired against the current API.

* services/web-ui/public/js/preview.js: standalone IIFE that lazy-injects the modal markup + CSS on first use. Renders <video controls> (or <audio>, <img>) sourced from /api/v1/assets/:id/stream, with sidebar from /api/v1/assets/:id. Falls back to a clear empty state when proxy is still processing.
* services/web-ui/public/index.html: loads preview.js, wires asset-card click to window.openAssetPreview(asset.id), guards against delete-button clicks bubbling.
* services/premiere-plugin/install-windows.ps1: one-shot Windows installer for the CEP extension.
2026-05-17 08:55:14 -04:00
Zac
3ea896c368 fix(web-ui): bust JS cache so api.js fix actually reaches the browser
The api.js library-list fix from the previous commit never reached the browser because nginx served all .js with `Cache-Control: public, immutable; max-age=31536000`. The HTML referenced api.js with no version query, so the browser kept its year-cached buggy copy.

* nginx.conf: drop .js from the immutable long-cache block, add a no-cache must-revalidate block so future redeploys are picked up immediately.
* All HTML files: tag api.js refs with ?v=4 so already-running browsers fetch the new version on next page load.
2026-05-17 08:31:00 -04:00
Zac
ac1878452f fix: library + caller-only recorders + live signal indicator
Three problems blocked the end-to-end flow:

1) Library always rendered empty because /assets returns {assets,total} but
   index.html (and capture.html) assumed r.data was an array. Fixed in
   api.js by unwrapping r.data.assets centrally; total is kept on r.total.

2) SRT/RTMP caller mode pulled audio only. ffmpeg opened the network input
   before the H264 SPS arrived, marked the video stream as pix_fmt=none,
   and silently dropped it from the stream map. Added -probesize 32M
   -analyzeduration 10M -fflags +genpts and explicit -map 0✌️0?/0🅰️0? so
   each track survives independently of when it appears.

3) Hitting Record gave no feedback about whether a stream was actually
   arriving. capture-manager now parses ffmpeg progress lines (frame=...
   fps=...) and tracks framesReceived, currentFps, lastFrameAt, lastError.
   getStatus() returns a derived signal enum (connecting | receiving |
   lost | error | stopped). The recorder controller gives each spawned
   container a stable network alias `recorder-<id>` and the GET
   /recorders/:id/status endpoint proxies the live capture status through.
   recorders.html polls that every 2s and renders the badge under each
   active card with the running frame/fps counter or the ffmpeg error.

Also:
* recorders.html: dropped the listener-mode UI entirely. All new recorders
  are caller-mode (pull). The MAM is no longer offered as an RTMP/SRT
  server. Legacy listener records still render but read-only.
2026-05-17 07:39:58 -04:00
3154cce37c fix: ETag case mismatch in multipart upload complete route
api.js sends parts as { partNumber, ETag } (uppercase) but upload.js
was reading p.etag (lowercase), resulting in undefined ETag passed to
S3 CompleteMultipartUpload → InvalidPart error on all large file uploads.

Also handle both casings defensively.
2026-05-16 18:56:38 -04:00
17646c1155 fix(jobs): read from BullMQ queues instead of empty DB table
GET /api/v1/jobs now queries the proxy, thumbnail, and conform BullMQ
queues directly and returns normalized job objects with id, type,
status, progress, asset_id, timestamps, and error fields.

Also adds DELETE /:id to remove completed/failed jobs from the queue,
supporting the clearCompleted action in jobs.html.

The PostgreSQL jobs table is still used only for conform job creation
(POST /conform) to preserve that workflow.
2026-05-16 17:38:53 -04:00
44b59742b8 redesign: jobs.html — filter tabs, type chips, inline progress, detail panel 2026-05-16 17:02:39 -04:00