diff --git a/services/web-ui/public/modal-new-recorder.jsx b/services/web-ui/public/modal-new-recorder.jsx index d54bac5..4508f17 100644 --- a/services/web-ui/public/modal-new-recorder.jsx +++ b/services/web-ui/public/modal-new-recorder.jsx @@ -1,5 +1,34 @@ // modal-new-recorder.jsx — New Recorder dialog (SRT / RTMP / SDI) +function ProbeResult({ result }) { + if (!result.ok) { + return ( +
+ Probe failed: {result.error} +
+ ); + } + const d = result.data || {}; + const entries = Object.entries(d).filter(([, v]) => v !== null && typeof v !== 'object'); + if (entries.length === 0) { + return ( +
+ ✓ Source reachable +
+ ); + } + return ( +
+ {entries.map(([k, v]) => ( +
+ {k} + {String(v)} +
+ ))} +
+ ); +} + function NewRecorderModal({ open, onClose }) { const { PROJECTS, NODES } = window.ZAMPP_DATA; const [name, setName] = React.useState(''); @@ -18,6 +47,8 @@ function NewRecorderModal({ open, onClose }) { const [projectId, setProjectId] = React.useState(PROJECTS[0]?.id || ''); const [submitting, setSubmitting] = React.useState(false); const [submitErr, setSubmitErr] = React.useState(null); + const [probing, setProbing] = React.useState(false); + const [probeResult, setProbeResult] = React.useState(null); React.useEffect(() => { if (sourceType !== 'SDI' || sdiDevices !== null) return; @@ -26,6 +57,19 @@ function NewRecorderModal({ open, onClose }) { .catch(() => setSdiDevices([])); }, [sourceType]); + React.useEffect(() => { setProbeResult(null); }, [sourceType, srtUrl, rtmpUrl]); + + const handleProbe = () => { + setProbing(true); + setProbeResult(null); + const body = sourceType === 'SRT' + ? { source_type: 'srt', url: srtUrl } + : { source_type: 'rtmp', url: rtmpUrl }; + window.ZAMPP_API.fetch('/recorders/probe', { method: 'POST', body: JSON.stringify(body) }) + .then(r => { setProbing(false); setProbeResult({ ok: true, data: r }); }) + .catch(e => { setProbing(false); setProbeResult({ ok: false, error: e.message }); }); + }; + const handleCreate = () => { if (!name.trim()) { setSubmitErr('Recorder name is required.'); return; } if (sourceType === 'SDI' && !sdiNodeId) { setSubmitErr('Select a capture node for SDI.'); return; } @@ -99,22 +143,36 @@ function NewRecorderModal({ open, onClose }) { {sourceType === 'SRT' && (
- setSrtUrl(e.target.value)} /> +
+ setSrtUrl(e.target.value)} style={{ flex: 1 }} /> + +
Recorder connects out to this URL (caller mode).
+ {probeResult && }
)} {sourceType === 'RTMP' && (
- setRtmpUrl(e.target.value)} /> +
+ setRtmpUrl(e.target.value)} style={{ flex: 1 }} /> + +
Recorder pulls this RTMP stream.
+ {probeResult && }
)} @@ -158,7 +216,8 @@ function NewRecorderModal({ open, onClose }) { ? : NODES.map(n => { const id = n.id || n.hostname || n.name || ''; - return ; + const label = n.hostname || n.name || id; + return ; })}