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.
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.
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.
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.
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.
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.
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.
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.
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.
- 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