Commit graph

829 commits

Author SHA1 Message Date
7d408035ac fix(capture): fix VHD_CORE_SP_BUFFERQUEUE_DEPTH constant name for SDK 6.34 2026-06-01 08:59:06 -04:00
884c8829a0 merge: integrate remote changes + deltacast bridge capture
Merges remote main (raw2bmx growing-file, playout, auth) with local
Deltacast SDI capture implementation (bridge binary, _buildInputArgs,
start/stop lifecycle).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 08:41:05 -04:00
962c7c8f20 fix(capture): log SDK errors in video loop, warn on unsupported port index 2026-06-01 08:00:55 -04:00
b46abc9b1a fix(capture): shell injection, stale FIFO, stderr listener, bridge exit handler 2026-06-01 08:00:06 -04:00
adfbeac217 feat(capture): accept deltacast as valid source_type in /start handler 2026-06-01 07:56:43 -04:00
d4924b044f feat(capture): wire bridge process lifecycle into start/stop for deltacast 2026-06-01 07:55:34 -04:00
4da7bf8b41 feat(capture): replace deltacast _buildInputArgs stub with real bridge spawn 2026-06-01 07:54:17 -04:00
6fec41aaf9 feat(capture): add readFirstStderrLine helper for deltacast bridge handshake 2026-06-01 07:53:12 -04:00
de6e44b991 build(capture): add Deltacast SDK extraction and bridge build stages to Dockerfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 07:52:03 -04:00
c7e07df515 fix(capture): handle partial writes in audio_thread FIFO write loop 2026-06-01 07:51:10 -04:00
67c071a0ee feat(capture): add deltacast-capture bridge binary source 2026-06-01 07:50:07 -04:00
3529590160 build(capture): add CMakeLists for deltacast-capture bridge binary 2026-06-01 07:47:32 -04:00
bda33fedca fix(web-ui): sync route state with URL hash on mount + hashchange
Added useEffect to parse location.hash and update route state.
Fixes deep links like /#/library not rendering correct screen.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-06-01 11:10:19 +00:00
Zac Gaetano
66ff7065ec Constrain playout preview to 960px for 1080p screens
Cap monitor column at 960px width so full GUI fits 1920x1080 without scroll.
Preview now ~960×540px (16:9), leaves room for 300px rail + margins.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-06-01 11:09:54 +00:00
c1512e29c5 feat(capture): true growing MXF via bmx/raw2bmx (Premiere edit-while-record)
ffmpeg's mxf muxer cannot write a growing file — its header/index duration
stays N/A until the footer at close (proven: file grows on disk but readable
duration never advances), so Premiere never sees growth. Replace the growing
master muxer with bmx/raw2bmx --growing-file, the reference growing-OP1a writer.

Capture image builds bmx (bbc/bmx v1.6) from source (bmxlib-tools absent in
bookworm). Growing pipeline: one ffmpeg decodes SDI -> split into MPEG-2 422
essence + PCM (to named FIFOs) + the H.264 HLS preview; raw2bmx muxes the
growing OP1a MXF to the share, updating IndexDuration incrementally. FIFO
open-order deadlock fixed by parent-priming both FIFOs. Stop forwards SIGINT
so ffmpeg EOFs and raw2bmx finalizes the footer; stop() awaits raw2bmx exit
before the promotion worker uploads. Raster/fps -> raw2bmx essence flag via
deriveGrowingRaster (default 1080i59.94).

Proven live (zampp2): IndexDuration grows 43->223->403 frames at 3/8/15s
mid-write (ffmpeg stayed N/A); finalized file valid; HLS preview intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 23:23:46 -04:00
Zac
9bcbac558c Add multi-select to library page
- Selection mode toggle in toolbar
- Checkboxes on cards (grid) and rows (list)
- Bulk actions: move to bin, delete
- Select all / clear selection controls

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-06-01 03:01:19 +00:00
2cd20a0e72 fix(growing): don't promote a growing file while its recorder is still recording
The promotion worker promoted on mtime-idle (>=8s), but CIFS attribute caching
makes an actively-growing MXF look idle, so it grabbed the live file ~15s into
recording, uploaded it, flipped the asset live->ready, and unlinked it ("a
worker is stealing the file"). Gate promotion on the recorder's live status:
the growing asset's display_name is the recorder's current_session_id, so skip
promotion while a recorder with that session is status='recording'. Only
promote once recording has stopped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:26:07 -04:00
35165e28a8 fix(gui): modal reflects MXF XDCAM growing format (was stale MPEG-TS text)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:14:52 -04:00
32a2d0329e fix(growing+gui): growing file = MXF XDCAM HD422 (Premiere-growable) + GUI fixes
Growing root cause (4th attempt): Premiere doesn't import H.264-in-.ts
("unsupported compression type"); its growing-file support is MXF OP1a.
Prior MXF/DNxHR failed because DNxHR is VBR and never flushes the incremental
index — XDCAM HD422 (mpeg2video, CBR) DOES write index segments into body
partitions mid-record (proven live via SIGKILL: 5 index segments, readable,
no footer). Growing master is now MXF OP1a / XDCAM HD422 4:2:2 CBR + PCM s16le,
operator bitrate as CBR (default 50M). live-path returns .mxf to match.

GUI: bitrate input is now always editable in growing mode (was hidden for
ProRes-selected codecs); codec menu shown disabled-with-explanation under
growing (it had only looked "missing" due to a stale served bundle).

Requires Premiere prefs: Media > "Automatically refresh growing files" ON,
and disable the two XMP-write-on-import options.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:13:01 -04:00
b92a5bc7f7 fix(web-ui): playout fmtDuration no longer clobbers global asset duration formatter
screens-playout.jsx declared a top-level function fmtDuration(secs) that, in
the shared global script scope, overwrote data.jsx's fmtDuration(ms). After
the playout redesign loaded, normalizeAsset(duration_ms) hit the seconds-based
version, rendering every asset duration x1000 (15000ms shown as 4:10:00).
Rename the playout-local helpers to playoutFmtDur/playoutFmtTC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:02:46 -04:00
7b878d48c9 fix(capture): growing TS is 8-bit 4:2:0 (Premiere-importable) + honor bitrate
Growing master was H.264 High 4:2:2 Intra (high422/yuv422p) — ffprobe/VLC
open it but Adobe Premiere's H.264 importer only accepts 8-bit 4:2:0, so it
refused ("won't import"). Switch growing video to -profile:v high
-pix_fmt yuv420p (still -g 1 all-intra). Also the growing branch ignored the
operator's bitrate; now applies -b:v/-maxrate/-bufsize. Modal notes that
growing mode fixes codec/container (bitrate still applies).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 21:56:58 -04:00
64bbb221f7 fix(api): parse Postgres bigint (int8) as Number, not string
duration_ms/file_size are int8; node-postgres returned them as strings,
a footgun for any consumer doing arithmetic/sorting/comparison (already
hand-patched once in playout totals). Register a global int8 type parser
so the API emits real numbers. All such values are < 2^53 (no precision loss).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 21:47:45 -04:00
Zac
ef329399f1 feat: v2.2.3 plugin + duration fix
- Add v2.2.3 to downloads (streaming write fix for large imports)
- Fix duration bug: worker now overwrites with ffprobe result instead of preserving capture wall-clock estimate

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-06-01 01:10:55 +00:00
984a73e8ec feat(playout): redesigned MCR screen + SCTE-35 end-to-end
Drop in the redesigned timeline-centric Playout (PGM monitor, transport,
SCTE-35 card, as-run drawer) from the on-node redesign, fully wired to the
real playout API (channels/transport/HLS preview w/ error-recovery/as-run);
no mock data. In-page ConfirmModal for destructive actions.

SCTE-35: new playout_scte_breaks table (migration 033), endpoints to
schedule/trigger/list/cancel breaks (POST/GET/DELETE /channels/:id/scte[/trigger]),
scheduler due-break sweep, engine triggerScte + auto-return + as-run 'scte'
rows + on-air SCTE-BREAK state and timeline AD markers. In-stream SCTE-35
cue injection is a documented stub (CasparCG FFMPEG consumer exposes no
scte35 muxer) — scheduling/triggering/countdown/as-run are functional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 19:58:02 -04:00
8a958046ef fix(growing-files): MPEG-TS growing master + promotion-worker share mount
Root cause: MXF OP1a writes its index/duration only in the footer partition
on finalize, so a growing MXF has no footer and VLC/Premiere/ffmpeg-strict
refuse it ("Unable to open file on disk"). Separately the proxy job pointed
at a .mov S3 key that never existed (promotion worker watched a local empty
disk, not the SMB share), so stop -> instant proxy failure.

Fix: growing master is now MPEG-TS (H.264 high422 all-intra + AAC), which is
readable from the first PAT/PMT while still growing (verified mid-write decode).
hiresKey derives from the actual produced extension. Capture skips finalize for
growing recorders (leaves asset live for promotion). Promotion worker CIFS-
mounts the same growing_smb share before scanning; worker image gets cifs-utils
and worker-p4 runs privileged (local /growing bind removed). /live-path uses .ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 19:41:28 -04:00
08499b93b2 feat(gpu+capture): nvenc HLS preview, source-backend abstraction, GPU affinity+telemetry
#164 HLS preview uses h264_nvenc (forced-IDR, GOP=segment) when the sidecar
has the GPU, else keeps libx264 fallback.
#168 source-backend abstraction in capture-manager (blackmagic implemented as
a behavior-preserving refactor; deltacast/aja stubbed pending hardware).
#167 per-recorder gpu_uuid (migration 032) plumbed mam-api->agent->
NVIDIA_VISIBLE_DEVICES (defaults to 'all').
#166 node-agent reports encoder util + NVENC session count per GPU; Cluster
screen renders per-GPU GPU/ENC util, VRAM, sessions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:38:56 -04:00
ca1eec0600 fix/feat: recorder finalize-grace + codec validation, cluster mem/version, library download
#162 local-spawn stop now uses /stop?t=180 + waits for asset to leave 'live'
before removing the container (no more SIGKILL-corrupted masters / stuck-live).
#163 validateRecorderConfig guard (PCM!=MP4, HEVC!=MXF, NVENC needs GPU) on
create+PATCH; codec presets in new-recorder modal.
#159 container list reads Docker /stats memory (N/A when null) + UI render.
#160 primary node self-populates version + uptime on the Cluster screen.
#145 asset-detail Download original gated by dismissable size warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:34:36 -04:00
794b9d9929 fix(capture): growing file is MXF OP1a (DNxHR HQ) so Premiere can open it
The growing edit-while-record file was a fragmented MOV (empty moov), which
Premiere can't open ("Unable to open file on disk"). Write the growing master
as MXF OP1a / DNxHR HQ (Premiere-native, growable on disk); finalized master
keeps today's non-fragmented +faststart MOV.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:31:07 -04:00
1d642bd437 feat(web-ui): in-page delete confirm modal + WDL home footer
Replace 17 native window.confirm() destructive prompts with an in-page
ConfirmModal/useConfirm (added to visuals.jsx) across jobs/asset/editor/
ingest/projects/admin/playout/library. Add "Created by Wild Dragon LLC"
footer to the home launcher.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:31:07 -04:00
fffff1c016 feat(cluster): install capture-card drivers/SDKs from the admin screen
Per-node "Capture Drivers / SDKs" panel installs Blackmagic / AJA / Deltacast
/ NDI drivers without SSH. node-agent gains NODE_TOKEN-gated /driver/install
+ /driver/status (spawns a one-shot privileged ubuntu container that bind-
mounts host kernel paths + the repo and runs deploy/install-driver.sh);
mam-api adds admin-gated /cluster/:id/install-driver + /driver-status.
Driver files live in-repo under sdk/<vendor>/ (private repo); binaries are
admin-supplied per each sdk/<vendor>/README.md. Vendor allowlist throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:14:59 -04:00
549ca6c73f fix(capture): finalized MOV master is non-fragmented so Premiere can open it
The hi-res master was streamed to S3 over a non-seekable pipe, which forced
a fragmented MOV (+frag_keyframe+empty_moov) with empty stco/stsz sample
tables — Premiere reports "file cannot be opened". Now: fragmentation only
for the growing SMB file; finalized master writes to a seekable local temp
with +faststart, stop() awaits ffmpeg exit to flush the moov, then uploads
the finalized file and cleans up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:14:59 -04:00
ea615c8c76 chore(capture): commit Blackmagic DeckLink SDK 16.0 Linux headers
Repo is private/internal — vendor the DeckLink SDK headers (Linux/include)
under services/capture/sdk/ so the capture ffmpeg build is self-contained
instead of operator-supplied. Runtime libDeckLinkAPI.so (from DesktopVideo
driver) remains uncommitted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:07:12 -04:00
f2d5f5aa16 feat(cluster): auto-assign node profiles from detected hardware
onboard-node.sh auto-detects GPU (nvidia-smi/lspci) and SDI capture cards
(blackmagic/deltacast) and computes PROFILES (worker [+gpu] [+capture])
automatically; explicit NODE_ROLE/PROFILES still override. Add Node wizard
drops the role picker — node self-configures from hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:02:29 -04:00
d908c0c056 fix(playout+capture+monitors): preview recovery, SMB UNC, playout monitors
Playout preview: add hls.js ERROR handler (recover media/network errors,
resume on stall) + live-edge tuning — first transient error no longer halts
<video> to black. Purge stale HLS segments before re-mux (re)start so a
prior/duplicate sidecar session can't corrupt the live playlist.

Growing files: normalize growing_smb_mount (smb://, \host\share, host/share)
to CIFS UNC //host/share in capture-manager — mount no longer fails and
falls back to S3.

Monitors: surface playout channels as monitor tiles (live HLS on-air,
idle placeholder otherwise) in a labeled Playout group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:56:45 -04:00
43011bd794 Merge feat/playout-mcr into main
Playout/MCR, as-run log, redesigned dashboard, capture CIFS/growing-files,
SDI settings, cluster Add Node wizard, homepage refresh.

# Conflicts:
#	services/mam-api/src/routes/cluster.js
#	services/mam-api/src/routes/playout.js
#	services/mam-api/src/scheduler.js
#	services/playout/Dockerfile
#	services/playout/entrypoint.sh
#	services/web-ui/public/screens-home.jsx
2026-05-31 17:46:12 -04:00
19f0abeabe feat(cluster+home): Add Node wizard, homepage tagline/logo/settings tweaks
Cluster: AddNodeModal on Admin->Cluster mints a node token via /auth/tokens
and emits a ready-to-paste curl|bash onboarding command. New admin-only
GET /cluster/onboard-info returns apiUrl/scriptUrl/branch. Role->PROFILES
mapping (worker/capture/gpu); gate worker-l4 behind compose profile [gpu].

Home: restore "Let's Create" kicker + one-line "Media Asset Management &
Production Platform" tagline; animated accent pulse behind the dragon logo
(reduced-motion safe); move Settings tile to a centered bottom row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:37:37 -04:00
f21bc490e8 feat(web-ui): redesigned Dashboard + playout as-run log
Dashboard (screens-home.jsx): rebuild to new design, fully live-wired.
Dropped fabricated figures per "real data" rule (object-store %, uptime,
storage breakdown); repurposed ingest cell to real Assets-24h count.
Fixed undefined refs and double-rendered Resources section.

Playout: as-run writer in scheduler.js writeAsRun() off the health-tick
/status poll; AsRunPanel UI + missing CSS in styles-playout.css.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:15:32 -04:00
7451d7c703 fix(playout): lighter 360p/20fps preview encode so the CPU re-mux keeps up
The 720p30 libx264 preview re-encode couldn't sustain real time on the
CPU-only sidecar (running alongside CasparCG's mixer + STREAM consumer):
the UDP input overran and the HLS output stalled, freezing the playlist so
hls.js saw a static live edge (monitor black). Drop the confidence monitor
to 360p / 20fps / ultrafast (-g 40 = 2.0s GOP) — a fraction of the cost,
sustains real time comfortably. NVENC would be ideal but the image's static
ffmpeg has no nvenc encoder; 360p ultrafast is plenty for a preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 16:15:16 -04:00
d3ad2397fb fix(playout): serve preview m3u8 via /api to bypass the proxy's static cache
Root cause of the persistent black preview, fully isolated: ZAMPP1's nginx
serves the live .m3u8 fresh on every request (no-store works there), but
the PUBLIC reverse proxy (159.112.211.103 -> ZAMPP1) caches the static
.m3u8 by path with a multi-second TTL, ignoring both the origin's no-store
and query params. hls.js reloads the playlist ~every second, always landing
inside that TTL, so it sees the live playlist as never advancing
("live playlist MISSED" forever), never establishes the timeline, and never
loads a fragment -> readyState 0 (black). Proven: rapid reads via ZAMPP1
localhost advance (404->405); the same rapid reads via the public URL are
stuck; query-busting doesn't help (proxy caches by path).

Fix: serve the playlist through GET /api/v1/playout/channels/:id/hls/index.m3u8
instead of the static /media/live path. /api/ is not proxy-cached (the live
status poll already updates fine through it), so hls.js always gets the fresh
live edge. Segment (.ts) lines are rewritten to absolute /media/live/<id>/
URLs so they still load from the static path (immutable; caching them is
correct). ProgramMonitor points hls.js at the /api playlist and sends the
session cookie (xhrSetup withCredentials) since /api is auth-gated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:57:41 -04:00
24d10fda5d fix(playout/web-ui): no-store on live HLS m3u8 so hls.js sees the live edge
Definitive root cause of the black preview, proven in-browser: the live
.m3u8 was served Cache-Control: no-cache, so the browser cached the
playlist and served a STALE copy to hls.js's reloads (cache:'default'
stuck at one MEDIA-SEQUENCE while cache:'reload' advanced). hls.js saw the
live playlist as never advancing -> "live playlist MISSED" forever ->
never established the timeline -> never loaded a fragment -> readyState 0
(black), even though the stream itself is clean and advancing server-side.

Fix: serve live HLS (/live and /media/live) with
"no-store, no-cache, must-revalidate" + Pragma no-cache so the browser
never caches the playlist and every reload fetches the fresh live edge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:45:28 -04:00
59551f28a5 fix(playout): re-encode HLS preview to uniform 2s segments so hls.js syncs
The audio-strip fix made the stream decode cleanly server-side, but the
browser monitor was still black: with -c:v copy the re-mux passed through
CasparCG's erratic real-time keyframes (segments 0.6-2.8s) and irregular
PTS. hls.js can't build a live timeline from that — it logs
"sliding 0.00 / prev-sn na / MISSED" and never loads a fragment (verified
live in-browser: readyState stays 0). A standalone ffmpeg honours
-force_key_frames, so re-encode to libx264 with a forced 2s GOP + CFR
fps=30, scaled to 720p for a light confidence monitor. Every HLS segment
is now a clean, keyframe-aligned 2.0s chunk hls.js can sync to.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:22:21 -04:00
e0e0b83810 fix(assets): live-path no longer gates on removed global growing_enabled
Growing-files mode became per-recorder (recorders.growing_enabled); the
global growing_enabled setting was removed. GET /assets/:id/live-path —
which the Premiere plugin calls to mount a still-growing master — still
required growing_enabled==='true', so Mount Live would 409 "Growing-files
mode is disabled" on any deploy where that stale key isn't set. Drop the
global gate: a status='live' asset already proves a growing recorder is
producing the file; only the editor-facing growing_smb_url is required.
Response contract is unchanged, so the plugin needs no update.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 15:05:06 -04:00
5968d4f681 feat(settings/growing): storage warning, SMB auth + CIFS mount, per-recorder growing
Implements docs/superpowers/specs/2026-05-31-storage-settings-growing-smb-design.md.

1. Storage warning banner at the top of Settings → Storage (set-once /
   path-change-corrupts-data warning).

2. Growing-files SMB credentials + system CIFS mount (Approach A):
   - settings.js: new global keys growing_smb_mount / growing_smb_username /
     growing_smb_vers; growing_smb_password is write-only (GET returns only
     growing_smb_password_exists; growing_smb_password_clear:true removes it).
   - GrowingSettingsCard: SMB mount/username/password (masked, "saved" state) +
     CIFS version fields.
   - capture Dockerfile: add cifs-utils + util-linux.
   - capture-manager: on growing start, mount //host/share at /growing using a
     root-only credentials file (creds never on the command line); unmount on
     stop; mount failure falls back to S3 streaming so a recording is never lost.
   - recorders.js: pass GROWING_SMB_* env; don't host-bind /growing when a CIFS
     mount is configured (an empty mountpoint is required).

3. Per-recorder growing mode (global toggle removed):
   - Removed the global "capture writes to local SMB share first" checkbox; the
     growing card is now SMB-infrastructure-only.
   - recorders.js reads the per-recorder recorders.growing_enabled column
     (already present from migration 014) instead of the global setting;
     RECORDER_FIELDS += growing_enabled.
   - New-recorder modal: "Growing-files mode" toggle.
   - storage.js overview: "enabled" now means the SMB landing zone is configured
     (mount source set), surfaced as smb_mount; health strip labels updated.

No DB migration required (recorders.growing_enabled exists; new settings are
key/value rows).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:50:36 -04:00
3b2f9fe6a0 fix(playout): strip comments from casparcg.config (parser aborts on them)
CasparCG 2.4.0's config parser aborts at startup (SIGABRT, exit 134) on
nearly every launch when the config contains XML comments / blank lines.
Proven empirically: identical image + comment-free config = 8/8 stable
starts; + commented config = 0/8 (all abort before logging init). Use
the compact comment-free form that the known-good image shipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:32:08 -04:00
869ae1aa83 fix(playout): use static ffmpeg, not apt, to avoid CasparCG SIGABRT
apt-get install ffmpeg pulls in ~80 transitive shared libs (libav*,
libx264, libdrm, libva...) that perturb CasparCG 2.4.0's headless
runtime linking and make it abort with SIGABRT (exit 134) on almost
every launch. Replace it with john van sickle's self-contained static
ffmpeg/ffprobe binaries in /usr/local/bin — the standalone CLI the HLS
re-muxer needs, with zero new shared libraries, keeping CasparCG's
environment identical to the known-good image.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:18:56 -04:00
abfbd034ab fix(playout): point CasparCG log/data paths at writable /media volume
The 2.4.x server aborts at startup if its configured log-path isn't
writable/creatable. /opt/casparcg is a read-only-ish symlinked install
dir; the entrypoint already mkdirs /media/casparcg/{log,data}. Point the
config there to match (the working image used these paths).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 14:11:00 -04:00
739d08d4b5 fix(web-ui): restore home screen icon fixes on feat/playout-mcr
playout tile: monitor -> signal, dashboard tile: home -> layout.
All feature-branch content (Dragon-ISO, tagline, etc.) preserved.
2026-05-31 13:56:19 -04:00
27a868aa5c fix(playout): clean video-only HLS preview via standalone ffmpeg re-mux
CasparCG's bundled FFMPEG/HLS consumer muxes a broken audio track
(aac sample_rate=0, time_base 1/0) into the preview, and silently drops
every arg that would remove it (-an, -codec:a, -g, -r all "Unused
option"). That corrupt audio black-screens the browser preview because
neither ffmpeg nor hls.js can decode the playlist.

Re-architect the preview path: CasparCG now STREAMs plain mpegts to a
UDP loopback port, and a Node-spawned STANDALONE ffmpeg (where -an
actually works) re-muxes it to clean, video-only HLS with -c:v copy.
The child process is tracked, auto-respawned while running, and killed
in stopChannel(). The PRIMARY SRT/RTMP/SDI/NDI output (with program
audio) is untouched.

Also fix the Dockerfile to match the working image: ubuntu:22.04 base +
CasparCG 2.4.0 ubuntu22 zip + NodeSource Node 20, and add a standalone
ffmpeg CLI. The old 2.3.3 tarball URL 404s. entrypoint.sh updated for
the 2.4.x bin/casparcg layout + bundled lib/ LD_LIBRARY_PATH.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 13:55:18 -04:00
1db0e81efb fix(web-ui): restore nav icon fixes on feat/playout-mcr
youtube: import, schedule: clock, playout: signal.
Keeps getting wiped by other agents writing full file replacements.
2026-05-31 13:54:16 -04:00
40e987b4a2 fix(web-ui): restore icon audit fixes on feat/playout-mcr
jobs: bulleted list (not hamburger), import: added, grid: rx=1,
hdd: cylinder, proxy: sliders. Keeps getting wiped by other agents.
2026-05-31 13:52:37 -04:00