From 7cf45ceee8812f9da1a56c3488ef20cb4596d4c1 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Tue, 14 Apr 2026 10:03:26 -0400 Subject: [PATCH] feat: multi-destination SRT in ConfigPanel --- frontend/src/components/ConfigPanel.tsx | 130 +++++++++++++++++++++--- 1 file changed, 115 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/ConfigPanel.tsx b/frontend/src/components/ConfigPanel.tsx index 6d805fb..502bff8 100644 --- a/frontend/src/components/ConfigPanel.tsx +++ b/frontend/src/components/ConfigPanel.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { RecorderConfig, CodecType } from '../types'; +import { RecorderConfig, CodecType, SRTDestination } from '../types'; interface ConfigPanelProps { portIndex: number; @@ -14,7 +14,7 @@ export function ConfigPanel({ portIndex, currentConfig, onSave, onClose }: Confi const [qualityProfile, setQualityProfile] = useState('hq'); const [recordingPath, setRecordingPath] = useState(`/recordings/port_${portIndex}_{timestamp}.mxf`); const [srtEnabled, setSrtEnabled] = useState(false); - const [srtDestination, setSrtDestination] = useState(''); + const [srtDestinations, setSrtDestinations] = useState([]); const [previewEnabled, setPreviewEnabled] = useState(true); useEffect(() => { @@ -24,20 +24,41 @@ export function ConfigPanel({ portIndex, currentConfig, onSave, onClose }: Confi setQualityProfile(currentConfig.quality_profile); setRecordingPath(currentConfig.recording_path); setSrtEnabled(currentConfig.srt_enabled); - setSrtDestination(currentConfig.srt_destination); + setSrtDestinations(currentConfig.srt_destinations ?? []); setPreviewEnabled(currentConfig.preview_enabled); } }, [currentConfig]); const handleSave = () => { onSave({ - port_index: portIndex, codec, bitrate, quality_profile: qualityProfile, - recording_path: recordingPath, srt_enabled: srtEnabled, - srt_destination: srtDestination, preview_enabled: previewEnabled, + port_index: portIndex, + codec, + bitrate, + quality_profile: qualityProfile, + recording_path: recordingPath, + srt_enabled: srtEnabled, + srt_destination: srtDestinations[0]?.url ?? '', + srt_destinations: srtDestinations, + preview_enabled: previewEnabled, }); onClose(); }; + const addDestination = () => { + setSrtDestinations(prev => [ + ...prev, + { url: '', label: `Dest ${prev.length + 1}`, enabled: true }, + ]); + }; + + const removeDestination = (idx: number) => { + setSrtDestinations(prev => prev.filter((_, i) => i !== idx)); + }; + + const updateDestination = (idx: number, field: keyof SRTDestination, value: string | boolean) => { + setSrtDestinations(prev => prev.map((d, i) => i === idx ? { ...d, [field]: value } : d)); + }; + const inputStyle: React.CSSProperties = { background: 'rgba(0,0,0,0.35)', border: '1px solid var(--border)', borderRadius: 3, padding: '9px 12px', fontFamily: 'var(--font-mono)', fontSize: 13, @@ -45,12 +66,10 @@ export function ConfigPanel({ portIndex, currentConfig, onSave, onClose }: Confi }; const selectStyle: React.CSSProperties = { - ...inputStyle, - cursor: 'pointer', appearance: 'none' as const, + ...inputStyle, cursor: 'pointer', appearance: 'none' as const, backgroundImage: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%237a8fa8' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E\")", backgroundRepeat: 'no-repeat', backgroundPosition: 'right 12px center', - paddingRight: 32, - background: 'rgba(0,0,0,0.35)', + paddingRight: 32, background: 'rgba(0,0,0,0.35)', }; const labelStyle: React.CSSProperties = { @@ -80,7 +99,7 @@ export function ConfigPanel({ portIndex, currentConfig, onSave, onClose }: Confi style={{ background: 'var(--bg-panel)', border: '1px solid var(--border-bright)', - borderRadius: 4, width: '100%', maxWidth: 500, + borderRadius: 4, width: '100%', maxWidth: 520, maxHeight: '90vh', overflowY: 'auto', boxShadow: '0 24px 80px rgba(0,0,0,0.8)', animation: 'modalIn 0.2s ease', @@ -157,7 +176,7 @@ export function ConfigPanel({ portIndex, currentConfig, onSave, onClose }: Confi {/* Transport section */}
- Transport + SRT Transport
+ {srtEnabled && ( -
- - setSrtDestination(e.target.value)} placeholder="srt://host:9000" style={inputStyle} /> +
+ {srtDestinations.map((dest, idx) => ( +
+
+ + Destination {idx + 1} + +
+ + +
+
+
+ + updateDestination(idx, 'label', e.target.value)} + placeholder={`Destination ${idx + 1}`} + style={inputStyle} + /> +
+
+ + updateDestination(idx, 'url', e.target.value)} + placeholder="srt://host:9000" + style={inputStyle} + /> +
+
+ ))} + + + + {srtDestinations.length === 0 && ( +

+ No destinations configured — click Add Destination +

+ )}
)}