Commit graph

39 commits

Author SHA1 Message Date
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
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
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
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
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
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
44759391e5 Fix jobs.js: send camelCase fields to conform worker (projectId/outputFormat) 2026-05-16 00:46:45 -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
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
4ba898f6a3 fix: remove premature thumbnail dispatch from upload route (proxy worker now handles it) 2026-05-15 21:26:57 -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
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
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
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