// screens-ingest.jsx — Upload, Recorders, Capture (SDI), Monitors const { RECORDERS, NODES, SDI_PORTS_zampp2, PROJECTS } = window.ZAMPP_DATA; /* ========== Upload ========== */ function Upload({ navigate }) { const [files, setFiles] = React.useState([ { id: 1, name: "Drone_Aerial_Lap_4.mov", size: "12.4 GB", progress: 68, status: "uploading" }, { id: 2, name: "Interview_Director.mxf", size: "2.1 GB", progress: 100, status: "done" }, { id: 3, name: "Sponsor_Logo_v4.mov", size: "120 MB", progress: 100, status: "done" }, { id: 4, name: "Pit_Cam_3.mp4", size: "4.8 GB", progress: 24, status: "uploading" }, { id: 5, name: "Backstage_Audio.wav", size: "920 MB", progress: 0, status: "queued" }, ]); React.useEffect(() => { const i = setInterval(() => { setFiles(fs => fs.map(f => { if (f.status !== "uploading") return f; const next = f.progress + Math.random() * 4; if (next >= 100) return { ...f, progress: 100, status: "done" }; return { ...f, progress: next }; })); }, 600); return () => clearInterval(i); }, []); return (

Upload

Drop video, audio, or stills — we proxy and index automatically.
Protour 2026
Master files
Drop files here or click to browse
Video, audio, and image files — up to 5 GB each
{["MOV", "MP4", "MXF", "ProRes", "DNxHR", "WAV", "AIFF"].map(f => {f})}
Queue {files.length}
{files.map(f => (
{f.name} {f.size}
{f.status === "done" ? "✓ done" : f.status === "queued" ? "queued" : `${Math.round(f.progress)}%`}
))}
); } /* ========== Recorders (live ingest dashboard) ========== */ function Recorders({ navigate, onNew }) { return (

Recorders

Live ingest from SRT, RTMP, and SDI sources
4 recording · 1 armed · 1 error
{RECORDERS.map(r => )}
); } function RecorderRow({ recorder }) { const isRec = recorder.status === "recording"; return (
{recorder.audio ? (
) : isRec ? ( ) : (
)}
{recorder.name} {recorder.status.toUpperCase()} {recorder.source}
{recorder.url}
{recorder.codec}· {recorder.res}· node: {recorder.node}
Elapsed
{recorder.elapsed}
Bitrate
{recorder.bitrate}
Health
{isRec ? ( ) : recorder.status === "error" ? ( ) : ( )}
); } function HealthBar({ value }) { const color = value > 80 ? "var(--success)" : value > 40 ? "var(--warning)" : "var(--danger)"; return (
{value}%
); } function badgeForStatus(s) { return { recording: "live", armed: "accent", idle: "neutral", error: "danger", offline: "neutral" }[s] || "neutral"; } /* ========== Capture (rich SDI port picker) ========== */ function Capture({ navigate }) { const [activePort, setActivePort] = React.useState(1); const ports = SDI_PORTS_zampp2; return (

Capture

DeckLink SDI ingest — multi-port routing across cluster nodes
); } function DeckLinkVisual({ ports, activePort, onSelect }) { return (
DeckLink Duo 2
zampp2 · 172.18.91.217
ONLINE
DECKLINK DUO 2
{ports.map(p => ( ))}
Ports: 4 Active: {ports.filter(p => p.active).length} Recording: {ports.filter(p => p.recording).length}
); } function CaptureDetail({ port }) { if (!port.active) { return (
No signal on SDI {port.idx}
Connect a source, then click Refresh.
); } return (
{port.recording && (
REC
)}
{port.signal}
{!port.recording ? ( ) : ( )}
); } function CaptureStat({ label, value }) { return (
{label}
{value}
); } /* ========== Monitors (multi-cam grid) ========== */ function Monitors({ navigate }) { const [grid, setGrid] = React.useState(4); const allFeeds = [ ...RECORDERS.filter(r => !r.audio).map(r => ({ ...r, kind: "video" })), { id: "audio1", name: "FOH Mix", kind: "audio" }, { id: "audio2", name: "Stage Mics", kind: "audio" }, { id: "audio3", name: "PGM Bus", kind: "audio" }, ]; const feeds = allFeeds.slice(0, grid * grid - (grid === 2 ? 1 : 0)); const gridSize = grid; return (

Monitors

Multi-cam live monitoring across all active feeds
{[2, 3, 4].map(n => ( ))}
{feeds.map((f, i) => ( ))}
); } function MonitorTile({ feed, seed }) { if (feed.kind === "audio") { return (
LIVE {feed.name}
); } return (
{feed.status === "recording" && REC} {feed.status === "armed" && ARMED} {feed.status === "error" && ERR} {feed.status === "idle" && IDLE}
{feed.name} {feed.elapsed && feed.elapsed !== "00:00:00" && {feed.elapsed}}
); } Object.assign(window, { Upload, Recorders, Capture, Monitors });