Commit graph

565 commits

Author SHA1 Message Date
4f8964e807 fix(tokens): add requireAuth middleware to token routes
Token CRUD endpoints had no authentication guard.  Without it,
unauthenticated requests could reach the handler — GET would return
empty results silently, and POST could attempt to insert a token with
user_id = NULL.  All other route files in this codebase apply
requireAuth explicitly; tokens.js was simply missing it.
2026-05-19 00:07:41 -04:00
0ea8d7ce33 fix(timeline): cap right-trim at source asset boundary
When duration_ms is known, dragging the right-trim handle past the end
of the source clip could push timeline_out_frames beyond what the source
material covers.  Cap the delta so neither timeline_out_frames nor
source_out_frames can extend past the available source frames.

Also changed assetFrames fallback from origSrcOut (prevents any extension
when duration is unknown) to null, so the guard is simply skipped when
we don't have duration metadata.
2026-05-19 00:02:34 -04:00
3c689ccddf fix(timecode): correct framesToTC for all frames beyond position 3
The previous algorithm used `if (rem >= DROP)` (i.e. rem >= 4) to decide
whether to advance to the next minute group.  This fired immediately at
frame 4, still inside minute 0 of the 10-minute non-drop group, producing
00:01:00;00 for what should be 00:00:00;04.  Every timecode display in
the editor was wrong for any position past the first four frames.

Each 10-minute block has one 3600-frame non-drop minute followed by nine
3596-frame drop minutes.  The fix checks `rem < FRAMES_FIRST_MIN` (3600)
to identify the non-drop minute, then subtracts it before dividing into
drop-minute slots.  Frame labels within drop minutes are shifted by DROP
(+4) so the first usable label is :00;04 as per SMPTE 12M.
2026-05-19 00:01:18 -04:00
b23700f30a fix(recorders): use already-imported uuidv4 instead of dynamic import
Dynamic `(await import('uuid')).v4()` inside the /start route handler
re-imports the module every call (though Node caches it). uuidv4 is
already imported at the top of the file.
2026-05-18 23:56:00 -04:00
0f37d01b2d fix(editor): keyboard tool shortcuts now actually switch the active tool
V/C/H key shortcuts called updateToolbarActive() which only updated button
visual state — Timeline.setTool() was never called so the cursor stayed on
the previous tool. Fix by calling Timeline.setTool() inside updateToolbarActive.

Also bump api.js reference to ?v=6 to match other pages.
2026-05-18 23:53:38 -04:00
fb3b998cfd fix(worker/thumbnail): mark asset ready even when thumbnail extraction fails
If the thumbnail job throws (network blip, ffmpeg error, short clip), the
asset was left stuck in status='processing' indefinitely. Since the proxy
already exists and the asset is playable, set status='ready' in the catch
block before re-throwing so BullMQ can still record the failure.
2026-05-18 23:51:04 -04:00
ff892a1ad5 fix(capture): use duration_ms field for recent captures duration display
The asset schema stores duration as duration_ms (milliseconds).
renderRecent() was checking c.duration (always undefined) so duration
always showed as '—'. Fix to use c.duration_ms / 1000.
2026-05-18 23:50:05 -04:00
08e5ba6298 fix(jobs): fetchJobs → loadJobs, add credentials to inline api helper
killJob() referenced fetchJobs() which is undefined — the correct name is
loadJobs(). Also the inline api() wrapper was missing credentials:'include'
so any API call on the jobs page would fail with a 401 in prod.
2026-05-18 23:48:56 -04:00
e472075087 fix(library): evict stale thumb URL on image load error, re-observe for retry
When a signed S3 URL expires the img fires onerror. Previously the stale URL
stayed in thumbCache so the broken image would persist. Now we delete the cache
entry, clear the loaded class, and re-add the element to the IntersectionObserver
so the next time it scrolls into view a fresh signed URL is fetched.
2026-05-18 23:46:12 -04:00
e6314be92d fix(assets): strip internal full_count column from list response
The window function COUNT(*) OVER() leaks `full_count` on every row.
Strip it before sending so callers only see actual asset fields.
2026-05-18 23:44:14 -04:00
660afb94bb feat(editor): show fps/codec/resolution/duration in media panel asset list
- Add two-line layout to media panel items: name on top, metadata below
- fmtMs() converts duration_ms to MM:SS or HH:MM:SS for display
- Meta line shows resolution · codec · fps · duration, skipping null fields
- Assets with no extracted metadata (no proxy yet) show name only
- Active item meta line inherits accent color at reduced opacity
2026-05-18 23:37:56 -04:00
508cf8d41b feat(recorders): add Edit Recorder panel with PATCH support
- Edit (pencil) button appears on idle recorder cards; hidden while recording
- openEditPanel() pre-populates all form fields from existing recorder state
- openPanel() resets editingId and restores "New recorder" defaults
- closePanel() clears editingId and removes any stale probe result
- handleSaveRecorder() dispatches PATCH /recorders/:id in edit mode, POST otherwise
- Fix field name bugs in create path: codec→recording_codec, resolution→recording_resolution,
  proxy_config object→proxy_enabled/proxy_codec/proxy_resolution flat fields
- Badge in card now reads rec.recording_codec (correct DB field) instead of rec.codec
- Bump api.js cache-buster to v=6
2026-05-18 23:35:16 -04:00
79d44826fe feat(api.js): add patchRecorder() helper for PATCH /recorders/:id 2026-05-18 23:32:33 -04:00
7260b188c5 fix: remove dead DB UPDATE calls in conform worker
The jobs table row no longer exists for conform jobs (POST /jobs/conform
now goes directly to BullMQ). The UPDATE queries were no-ops (WHERE id = NULL)
so they're safe to remove. BullMQ tracks completed/failed status itself.
2026-05-18 23:28:13 -04:00
e895a2f2df fix: show duration overlay on asset cards using duration_ms
asset.duration is not a DB field — it's duration_ms (milliseconds).
Divide by 1000 before passing to formatDuration() which expects seconds.
2026-05-18 23:27:03 -04:00
a9ca7be1d5 feat: add PATCH /recorders/:id endpoint to edit recorder settings
Allows updating name, source_type, source_config, recording_codec,
recording_resolution, proxy_enabled, proxy_codec, proxy_resolution,
and project_id. Blocked while the recorder is actively recording.
2026-05-18 23:24:27 -04:00
29b5910fff feat: migrate editor sequences schema into auto-run migrations directory
Moved from schema_patch_editor.sql. All statements are idempotent
(IF NOT EXISTS / DO $$ BEGIN blocks) so safe to re-apply.
2026-05-18 23:23:33 -04:00
ffad0051f9 feat: migrate groups/tokens schema into auto-run migrations directory
Moved from schema_patch_groups_tokens.sql. All statements are idempotent
(IF NOT EXISTS / CREATE INDEX IF NOT EXISTS) so safe to re-apply.
2026-05-18 23:23:23 -04:00
717fdcd611 feat: extract and store fps/codec/resolution/duration_ms from source file
Uses getMediaInfo (ffprobe) on the downloaded original before transcoding.
Populates the asset record so the library can display accurate metadata.
2026-05-18 23:22:56 -04:00
817eaff8b1 feat: add getMediaInfo to executor.js using ffprobe JSON output
Exposes video stream fps/codec/resolution and container duration/size
so the proxy worker can populate asset metadata after transcoding.
2026-05-18 23:22:26 -04:00
48b69879cb fix: conform route broken SQL — remove dead DB insert, use BullMQ directly
The POST /conform route was inserting into the jobs table with non-existent
columns (project_id, metadata) and an invalid enum value ('pending'). Since
GET /jobs reads entirely from BullMQ, the DB insert was both incorrect and
redundant. Now we just enqueue the BullMQ job and return its ID.
2026-05-18 23:22:14 -04:00
596f755a6c fix: remove stray Wild Dragon brand remnant from editor.html 2026-05-18 23:14:14 -04:00
656c820638 feat: wire editor.html as primary editor, fix its sidebar/branding
- All pages: Editor nav link now points to editor.html (in-house NLE)
- Removes the :47435 OpenReel resolver script from all pages
- editor.html: canonical Z-AMPP sidebar (all 10 nav items, correct icons)
- editor.html: Z-AMPP brand logo, removes Wild Dragon SVG mark
- editor.html: removes Google Fonts import
- editor.html: adds auth-guard.js
2026-05-18 23:11:53 -04:00
910bbf8d3f merge: bring NLE editor pages (editor.html, timeline.js, timecode.js) from main 2026-05-18 23:02:51 -04:00
e8e26dd4d8 fix: remove Google Fonts, fix editor link to :47435, fix page titles
- Remove @import Google Fonts from common.css (was blocking CSS on LAN)
- Update Editor nav link on all pages to dynamically resolve to :47435
  (OpenReel SPA) using inline script so it works on any hostname
- Fix page titles from Wild Dragon -> Z-AMPP across all pages
- Resolver: <a href="#" id="editor-nav-link"> + IIFE sets href at load
2026-05-18 22:56:51 -04:00
1f31d1037d merge: bring sequences/auth/admin backend + auth-guard frontend into fix/library-and-signal-indicator 2026-05-18 21:25:36 -04:00
8ab71239e3 feat(ui): add Open in Editor action to library cards 2026-05-18 20:14:29 -04:00
78a887a3e0 feat(ui): add NLE editor page (editor.html) 2026-05-18 20:10:25 -04:00
2fabc73299 fix(ui): prevent keydown listener accumulation on re-init 2026-05-18 20:05:34 -04:00
10152b5ad7 feat(ui): add DOM-based timeline engine (select, razor, playhead) 2026-05-18 20:02:41 -04:00
ad6e836345 feat(ui): add sequence API helpers to api.js 2026-05-18 19:58:35 -04:00
7d8ccc95e9 feat(ui): add 59.94 DF timecode utility module 2026-05-18 19:58:34 -04:00
7b8d8d4818 feat(api): register sequences route 2026-05-18 19:54:41 -04:00
1816c7fa1e fix(api): guard DELETE 404, verify sequence before clip wipe, add clip validation 2026-05-18 19:53:14 -04:00
05d49b7199 feat(api): add sequences route with CRUD, clip sync, and EDL export 2026-05-18 19:50:29 -04:00
eb248c690f fix(db): use DO blocks for idempotent ALTER TABLE (ADD CONSTRAINT IF NOT EXISTS is not valid PG syntax) 2026-05-18 19:48:11 -04:00
c662df94c4 fix(db): add CHECK constraints, UNIQUE, and asset_id index to sequences schema 2026-05-18 19:46:42 -04:00
b12d8c619a feat(db): add sequences and sequence_clips tables 2026-05-18 19:41:21 -04:00
0b94153518 Replace inline auth script with shared auth-guard.js in tokens.html 2026-05-18 13:46:10 -04:00
3203832aa9 Replace inline auth script with shared auth-guard.js in users.html 2026-05-18 13:44:35 -04:00
88c0781767 Replace inline auth script with shared auth-guard.js in jobs.html 2026-05-18 13:43:22 -04:00
81b832dc70 Replace inline auth script with shared auth-guard.js in recorders.html 2026-05-18 13:41:52 -04:00
936867c0c3 Replace inline auth script with shared auth-guard.js on recorders, jobs, users, tokens pages: tokens.html 2026-05-18 13:38:27 -04:00
de3920dd4a Replace inline auth script with shared auth-guard.js on recorders, jobs, users, tokens pages: users.html 2026-05-18 13:38:26 -04:00
9dfefc5731 Replace inline auth script with shared auth-guard.js on recorders, jobs, users, tokens pages: jobs.html 2026-05-18 13:38:25 -04:00
f3fbb027f6 Replace inline auth script with shared auth-guard.js on recorders, jobs, users, tokens pages: recorders.html 2026-05-18 13:38:24 -04:00
e3128acb15 fix: use shared auth-guard.js on capture.html 2026-05-18 13:27:24 -04:00
d8766f18cc fix: use shared auth-guard.js on upload.html 2026-05-18 13:26:19 -04:00
a40232e2b5 fix: use shared auth-guard.js on settings.html 2026-05-18 13:25:03 -04:00
ebe8b3be59 fix: use shared auth-guard.js on index.html 2026-05-18 13:24:25 -04:00
cb63e4743d fix: /me returns guest user when AUTH_ENABLED is false so auth-guard never fires on dev 2026-05-18 13:21:37 -04:00
4f649b41a9 feat: add shared auth-guard.js with 401 → login redirect 2026-05-18 13:21:22 -04:00
725c3ed292 feat: rebuild settings.html with new shell layout 2026-05-18 13:08:19 -04:00
9ceb5db1e3 feat: add admin sidebar section and user footer widget 2026-05-18 13:08:04 -04:00
533250b1c3 feat: add admin sidebar section and user footer widget 2026-05-18 13:07:47 -04:00
ffffe8039e feat: add admin sidebar section and user footer widget 2026-05-18 13:07:40 -04:00
027e73467f feat: add admin sidebar section and user footer widget 2026-05-18 13:06:57 -04:00
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
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
7aae1d2738 feat: redesign capture.html with new design system 2026-05-16 16:48:25 -04:00
f7a96677ef feat: redesign recorders.html with new design system 2026-05-16 13:57:20 -04:00
cf93b2f378 feat: redesign upload.html and recorders.html: upload.html 2026-05-16 13:06:10 -04:00
c6cca63595 feat: redesign index.html, upload.html, recorders.html: index.html 2026-05-16 13:04:45 -04:00
c0d3d0590b feat: full GUI redesign — broadcast control register aesthetic: common.css 2026-05-16 13:02:33 -04:00
f5abf359fb fix(nginx): use Docker embedded DNS resolver to avoid startup DNS failure
nginx resolves upstream hostnames at config load time, which fails when
sibling containers haven't registered with the Docker DNS yet. Using
resolver 127.0.0.11 with set $upstream defers resolution to request
time, preventing the "host not found in upstream" startup crash.
2026-05-16 08:44:50 -04:00
af9c9dbae4 fix(db): parse DATABASE_URL in pool.js instead of individual DB_* vars
pool.js was using DB_HOST/DB_USER/etc which were never set.
The docker-compose.yml passes DATABASE_URL. Parse that if present,
fall back to individual vars for local dev.
2026-05-16 08:39:47 -04:00
0a5b4d6191 feat(ui): SRT/RTMP listener/caller mode UI in recorders
- SRT: mode selector (Listener / Caller)
  - Listener: listen_port field + live connection info banner
  - Caller: source URL field
- RTMP: mode selector (Listener / Caller)
  - Listener: listen_port + stream_key fields + live connection info banner
  - Caller: source URL field
- Connection info banners update live as port/key fields change
- handleCreateRecorder builds correct source_config per mode
- Card meta display handles listener config (shows port, not url)
- updateSrtModeFields / updateRtmpModeFields helpers for dynamic show/hide
2026-05-16 08:23:24 -04:00
78b1f3482f feat(recorders): add PortBindings for SRT/RTMP listener mode containers
When source_config.mode === 'listener':
- SRT: bind UDP listen_port (default 9000) on container host
- RTMP: bind TCP listen_port (default 1935) on container host
Add ExposedPorts to container config alongside HostConfig.PortBindings.
Also pass LISTEN, LISTEN_PORT, STREAM_KEY env vars to container.
2026-05-16 08:21:03 -04:00
55fec605c6 feat(capture): accept SRT/RTMP source params in POST /start
- Accept source_type, source_url, listen, listen_port, stream_key
- Validate: SDI requires device; SRT/RTMP caller requires source_url
- Pass all params through to captureManager.start()
- On stop: if proxyKey is null (network source), include needsProxy flag
  in MAM API registration so worker can generate proxy asynchronously
2026-05-16 08:20:10 -04:00
ea48e98465 feat(capture): add SRT/RTMP source type support
- Add _buildInputArgs() to build FFmpeg input args per source type
- SRT caller: srt://host:port?mode=caller
- SRT listener: srt://0.0.0.0:PORT?mode=listener
- RTMP caller: -i rtmp://host/app/key
- RTMP listener: -listen 1 -i rtmp://0.0.0.0:PORT/live/key
- Network sources spawn hires-only FFmpeg process (can't open stream twice)
- proxyKey is null for network sources; proxy generated by worker post-stop
- SDI keeps existing dual-process behavior unchanged
2026-05-16 08:19:41 -04:00
ed52dfcafb Fix recorders.html: rename handlers to avoid api.js shadowing (infinite recursion), fix resolution→recording_resolution 2026-05-16 00:48:40 -04:00
cc174c4977 Fix worker/index.js: job.progress is a property not a function in BullMQ v3+ 2026-05-16 00:46:53 -04:00
44759391e5 Fix jobs.js: send camelCase fields to conform worker (projectId/outputFormat) 2026-05-16 00:46:45 -04:00
79dcfaffeb Fix capture.html: remove bin requirement, fix start/stop handler naming to avoid recursion, track sessionId 2026-05-16 00:42:36 -04:00
1862082ba7 Fix upload.html: camelCase multipart params, filename field, ETag/partNumber, s3Key/assetId tracking 2026-05-16 00:41:36 -04:00
31ca999075 fix(api.js): correct capture paths, bin routes, device normalisation, upload camelCase, session tracking 2026-05-16 00:31:58 -04:00
a9cc8caf42 fix(recorders): add S3_REGION to container env, accept 304/404 on stop/remove 2026-05-16 00:31:10 -04:00
e796a0d15f fix(routes+ui): capture route bin optional, jobs Redis URL, recorders S3_REGION+stop codes, api.js full rewrite, upload.html multipart fix, capture.html bin guard: jobs.js 2026-05-16 00:30:26 -04:00
f9c680cc22 fix(routes+ui): capture route bin optional, jobs Redis URL, recorders S3_REGION+stop codes, api.js full rewrite, upload.html multipart fix, capture.html bin guard: capture.js 2026-05-16 00:30:25 -04:00
0bdfbaf130 fix(infra+workers): S3 creds, ffprobe, BullMQ awaits, thumbnail seek, bin optional, docker-compose vars, jobs Redis, recorders stop codes: thumbnail.js 2026-05-16 00:29:51 -04:00
647cf55389 fix(infra+workers): S3 creds, ffprobe, BullMQ awaits, thumbnail seek, bin optional, docker-compose vars, jobs Redis, recorders stop codes: proxy.js 2026-05-16 00:29:50 -04:00
8be9c20124 fix(infra+workers): S3 creds, ffprobe, BullMQ awaits, thumbnail seek, bin optional, docker-compose vars, jobs Redis, recorders stop codes: executor.js 2026-05-16 00:29:49 -04:00
b2da06b4cc fix(infra+workers): S3 creds, ffprobe, BullMQ awaits, thumbnail seek, bin optional, docker-compose vars, jobs Redis, recorders stop codes: client.js 2026-05-16 00:29:48 -04:00
bb069760fe fix(infra+workers): S3 creds, ffprobe, BullMQ awaits, thumbnail seek, bin optional, docker-compose vars, jobs Redis, recorders stop codes: client.js 2026-05-16 00:29:47 -04:00
be8e0bda41 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: login.html 2026-05-15 23:40:15 -04:00
72c4a7f136 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: main.js 2026-05-15 23:40:14 -04:00
47c113e6c3 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: conform.js 2026-05-15 23:40:13 -04:00
f745122ef0 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: index.js 2026-05-15 23:40:12 -04:00
ada5597f79 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: auth.js 2026-05-15 23:40:11 -04:00
069c20ad43 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: auth.js 2026-05-15 23:40:10 -04:00
583b3f0ad6 fix(auth+bugs): optional auth bypass, login routes, conform column name, panel metadata fields, login page: package.json 2026-05-15 23:40:09 -04:00
668e7c6c24 fix(premiere-plugin): CSInterface init, correct API prefix, Node.js download, lazy thumbnails, proper ExtendScript export API: premiere.jsx 2026-05-15 21:36:15 -04:00
a239e30ef2 fix(premiere-plugin): CSInterface init, correct API prefix, Node.js download, lazy thumbnails, proper ExtendScript export API: main.js 2026-05-15 21:36:13 -04:00
c162104b7c fix(premiere-plugin): CSInterface init, correct API prefix, Node.js download, lazy thumbnails, proper ExtendScript export API: index.html 2026-05-15 21:36:12 -04:00
ea92cad310 fix(premiere-plugin): CSInterface init, correct API prefix, Node.js download, lazy thumbnails, proper ExtendScript export API: manifest.xml 2026-05-15 21:36:11 -04:00
4ba898f6a3 fix: remove premature thumbnail dispatch from upload route (proxy worker now handles it) 2026-05-15 21:26:57 -04:00
6aff3cabc0 fix: set asset status=ready after thumbnail completes 2026-05-15 21:26:22 -04:00
10949bc460 fix: dispatch thumbnail job after proxy completes instead of racing from upload route 2026-05-15 21:26:16 -04:00
b42199e597 fix: assets response shape, thumbnail lazy-load, bin sidebar wired up 2026-05-15 21:25:29 -04:00
7ef8476bd3 fix: add ampp_folder_id/ampp_synced_at to assets; fix recorders.current_session_id type to TEXT 2026-05-15 21:24:16 -04:00
db73235149 fix: add POST /assets handler for capture registration + thumbnail job dispatch 2026-05-15 21:24:02 -04:00
cd0c724bdd feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: settings.html 2026-04-18 13:42:09 -04:00
4630a18dde feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: index.js 2026-04-18 13:42:09 -04:00
0e36ca9972 feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: upload.js 2026-04-18 13:42:09 -04:00
e25e63b3f0 feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: ampp.js 2026-04-18 13:42:08 -04:00
56e2a97506 feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: settings.js 2026-04-18 13:42:08 -04:00
36a462dac4 feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: client.js 2026-04-18 13:42:07 -04:00
2b9499a606 feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: schema_patch_ampp.sql 2026-04-18 13:42:07 -04:00
0fa9e960d4 Phase 2: services/premiere-plugin/PLUGIN_FILES.txt 2026-04-07 22:05:49 -04:00
ea157f7835 Phase 2: services/premiere-plugin/QUICK_START.md 2026-04-07 22:05:48 -04:00
6239795300 Phase 2: services/premiere-plugin/README.md 2026-04-07 22:05:48 -04:00
227d951d6d Phase 2: services/premiere-plugin/jsx/premiere.jsx 2026-04-07 22:05:47 -04:00
c78433aa14 Phase 2: services/premiere-plugin/js/main.js 2026-04-07 22:05:47 -04:00
f0e3e90870 Phase 2: services/premiere-plugin/js/CSInterface.js 2026-04-07 22:05:46 -04:00
bb92381a47 Phase 2: services/premiere-plugin/index.html 2026-04-07 22:05:46 -04:00
33f8b1c26a Phase 2: services/premiere-plugin/css/styles.css 2026-04-07 22:05:45 -04:00
30a10805c3 Phase 2: services/premiere-plugin/CSXS/manifest.xml 2026-04-07 22:05:45 -04:00
ee7958cdd2 Phase 2: services/premiere-plugin/.debug 2026-04-07 22:05:44 -04:00
3f25ea1124 Phase 2: services/web-ui/public/js/api.js 2026-04-07 22:05:44 -04:00
3aee8c41f5 Phase 2: services/web-ui/public/recorders.html 2026-04-07 22:05:43 -04:00
1ed284eac3 Phase 2: services/web-ui/public/upload.html 2026-04-07 22:05:42 -04:00
0e86cbb1f3 Phase 2: services/web-ui/public/index.html 2026-04-07 22:05:42 -04:00
aa4baca091 Phase 2: services/web-ui/nginx.conf 2026-04-07 22:05:41 -04:00
8ab7ea4d8d Phase 2: services/mam-api/src/routes/recorders.js 2026-04-07 22:05:41 -04:00
6994e2d697 Phase 2: services/mam-api/src/routes/upload.js 2026-04-07 22:05:40 -04:00
a2c233aed3 Phase 2: services/mam-api/src/s3/client.js 2026-04-07 22:05:40 -04:00
cc06166e00 Phase 2: services/mam-api/src/db/schema.sql 2026-04-07 22:05:39 -04:00
53d4124514 Phase 2: services/mam-api/src/index.js 2026-04-07 22:05:39 -04:00
7c7fcd2b0d add services/capture/src/routes/capture.js 2026-04-07 21:58:30 -04:00
dfffca879a add services/capture/src/s3/client.js 2026-04-07 21:58:30 -04:00
75ef8a4ed8 add services/capture/src/capture-manager.js 2026-04-07 21:58:29 -04:00
febf394a81 add services/capture/src/index.js 2026-04-07 21:58:29 -04:00
e0902acbb6 add services/capture/package.json 2026-04-07 21:58:29 -04:00
63a8415cee add services/capture/Dockerfile 2026-04-07 21:58:28 -04:00
a463b67bce add services/mam-api/src/routes/bins.js 2026-04-07 21:58:28 -04:00
b1e9b4a9ca add services/mam-api/src/routes/projects.js 2026-04-07 21:58:28 -04:00
31927f9742 add services/mam-api/src/routes/capture.js 2026-04-07 21:58:27 -04:00
55b765698c add services/mam-api/src/routes/jobs.js 2026-04-07 21:58:27 -04:00
5cb9ccaefc add services/mam-api/src/routes/assets.js 2026-04-07 21:58:27 -04:00
8e247fe2ce add services/mam-api/src/s3/client.js 2026-04-07 21:58:26 -04:00
e0f7f7c264 add services/mam-api/src/middleware/errors.js 2026-04-07 21:58:26 -04:00
e863cf3d6e add services/mam-api/src/middleware/auth.js 2026-04-07 21:58:26 -04:00
d7573707ed add services/mam-api/src/db/pool.js 2026-04-07 21:58:26 -04:00
44a5781f99 add services/mam-api/src/db/schema.sql 2026-04-07 21:58:25 -04:00
9a9259628a add services/mam-api/src/index.js 2026-04-07 21:58:25 -04:00
2f81029112 add services/mam-api/Dockerfile 2026-04-07 21:58:25 -04:00
3694e65ab0 add services/mam-api/.env.example 2026-04-07 21:58:24 -04:00
43fe16831f add services/mam-api/.gitignore 2026-04-07 21:58:24 -04:00
2938183b12 add services/mam-api/package.json 2026-04-07 21:58:24 -04:00
58ca698eac add services/web-ui/public/css/common.css 2026-04-07 21:58:23 -04:00
ee9e6865ab add services/web-ui/public/js/api.js 2026-04-07 21:58:23 -04:00
481f8f43f0 add services/web-ui/public/index.html 2026-04-07 21:58:23 -04:00
e444162800 add services/web-ui/public/player.html 2026-04-07 21:58:22 -04:00
28be46403a add services/web-ui/public/capture.html 2026-04-07 21:58:22 -04:00
b716a6e6d7 add services/web-ui/.gitignore 2026-04-07 21:58:22 -04:00
403df6558b add services/web-ui/.dockerignore 2026-04-07 21:58:21 -04:00
a0994cfffe add services/web-ui/nginx.conf 2026-04-07 21:58:21 -04:00
9c4764cf78 add services/web-ui/Dockerfile 2026-04-07 21:58:21 -04:00
5bc6cf7c17 add services/worker/src/ffmpeg/executor.js 2026-04-07 21:58:21 -04:00
9e2833ba85 add services/worker/src/db/client.js 2026-04-07 21:58:20 -04:00
76e15b4b76 add services/worker/src/edl/parser.js 2026-04-07 21:58:20 -04:00
0537b7ab44 add services/worker/src/s3/client.js 2026-04-07 21:58:20 -04:00
7a677fe36e add services/worker/src/workers/proxy.js 2026-04-07 21:58:19 -04:00
a97804bcb3 add services/worker/src/workers/thumbnail.js 2026-04-07 21:58:19 -04:00
1ff7ff8d2b add services/worker/src/workers/conform.js 2026-04-07 21:58:19 -04:00
1a1f34a468 add services/worker/src/index.js 2026-04-07 21:58:18 -04:00
b2d0072104 add services/worker/package.json 2026-04-07 21:58:18 -04:00
167bf25443 add services/worker/Dockerfile 2026-04-07 21:58:18 -04:00