Recorder model was creating capture containers but ffmpeg never spawned
inside them, so SRT/RTMP listeners bound the host port without ingesting
anything. Thumbnail extraction was also crashing on yuv444p sources,
leaving uploaded assets stuck at status=processing forever.
* capture/src/index.js: read RECORDER_ID/SOURCE_TYPE/LISTEN/LISTEN_PORT/
STREAM_KEY/SOURCE_URL from env on startup and call captureManager.start()
immediately. SIGTERM handler now flushes ffmpeg + S3 upload and POSTs the
asset to mam-api before exiting.
* worker/ffmpeg/executor.js: force -pix_fmt yuv420p on proxy transcode and
-pix_fmt yuvj420p on thumbnail extraction so mjpeg encoder accepts the
input regardless of source pixel format.
* mam-api/routes/assets.js: when capture posts proxyKey=null but hiresKey
is set (SRT/RTMP case), enqueue a proxy job from the hires so the asset
ends up with a browser-playable proxy + thumbnail instead of stuck-ready.
* mam-api/routes/recorders.js: accept UI field aliases (codec/resolution/
proxy_config), clean up unstarted containers on port collision, bump the
docker stop timeout to 5min so long uploads can flush.
* web-ui/recorders.html: change default ports from 1935/9000 to 41936/49001
to avoid common collisions with other RTMP/SRT services.
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.
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.