diff --git a/services/mam-api/src/routes/recorders.js b/services/mam-api/src/routes/recorders.js index be03e41..c1496d9 100644 --- a/services/mam-api/src/routes/recorders.js +++ b/services/mam-api/src/routes/recorders.js @@ -46,6 +46,31 @@ function generateClipName(recorderName) { return `${recorderName}_${year}${month}${day}_${hours}${minutes}${seconds}`; } +/** + * Build Docker PortBindings and ExposedPorts for listener-mode recorders. + * Returns { portBindings, exposedPorts } — both empty objects for non-listener sources. + */ +function buildPortConfig(sourceType, sourceConfig) { + const portBindings = {}; + const exposedPorts = {}; + + if (sourceConfig && sourceConfig.mode === 'listener') { + if (sourceType === 'srt') { + const port = String(sourceConfig.listen_port || 9000); + const proto = `${port}/udp`; + portBindings[proto] = [{ HostPort: port }]; + exposedPorts[proto] = {}; + } else if (sourceType === 'rtmp') { + const port = String(sourceConfig.listen_port || 1935); + const proto = `${port}/tcp`; + portBindings[proto] = [{ HostPort: port }]; + exposedPorts[proto] = {}; + } + } + + return { portBindings, exposedPorts }; +} + // GET / - List all recorders router.get('/', async (req, res, next) => { try { @@ -172,30 +197,57 @@ router.post('/:id/start', async (req, res, next) => { // Generate clip name with timestamp const clipName = generateClipName(recorder.name); + // Determine source config and whether this is a listener-mode recorder + const sourceConfig = recorder.source_config || {}; + const isListener = sourceConfig.mode === 'listener'; + const sourceType = recorder.source_type; + + // Build port bindings for listener-mode SRT/RTMP containers + const { portBindings, exposedPorts } = buildPortConfig(sourceType, sourceConfig); + + // Build container environment — pass all source params so the capture + // service can auto-start recording on container startup + const env = [ + `S3_ENDPOINT=${s3Endpoint}`, + `S3_BUCKET=${s3Bucket}`, + `S3_ACCESS_KEY=${s3AccessKey}`, + `S3_SECRET_KEY=${s3SecretKey}`, + `S3_REGION=${process.env.S3_REGION || 'us-east-1'}`, + `MAM_API_URL=${mamApiUrl}`, + `RECORDER_ID=${id}`, + `SOURCE_TYPE=${sourceType}`, + `SOURCE_CONFIG=${JSON.stringify(sourceConfig)}`, + `RECORDING_CODEC=${recorder.recording_codec}`, + `RECORDING_RESOLUTION=${recorder.recording_resolution}`, + `PROXY_ENABLED=${recorder.proxy_enabled}`, + `PROXY_CODEC=${recorder.proxy_codec}`, + `PROXY_RESOLUTION=${recorder.proxy_resolution}`, + `PROJECT_ID=${recorder.project_id}`, + `CLIP_NAME=${clipName}`, + ]; + + // Add source-specific env vars for SRT/RTMP + if (sourceType === 'srt' || sourceType === 'rtmp') { + env.push(`LISTEN=${isListener ? '1' : '0'}`); + if (isListener) { + env.push(`LISTEN_PORT=${sourceConfig.listen_port || (sourceType === 'srt' ? 9000 : 1935)}`); + if (sourceType === 'rtmp' && sourceConfig.stream_key) { + env.push(`STREAM_KEY=${sourceConfig.stream_key}`); + } + } else if (sourceConfig.url) { + env.push(`SOURCE_URL=${sourceConfig.url}`); + } + } + // Build container config const containerConfig = { Image: 'wild-dragon-capture:latest', - Env: [ - `S3_ENDPOINT=${s3Endpoint}`, - `S3_BUCKET=${s3Bucket}`, - `S3_ACCESS_KEY=${s3AccessKey}`, - `S3_SECRET_KEY=${s3SecretKey}`, - `S3_REGION=${process.env.S3_REGION || 'us-east-1'}`, - `MAM_API_URL=${mamApiUrl}`, - `RECORDER_ID=${id}`, - `SOURCE_TYPE=${recorder.source_type}`, - `SOURCE_CONFIG=${JSON.stringify(recorder.source_config)}`, - `RECORDING_CODEC=${recorder.recording_codec}`, - `RECORDING_RESOLUTION=${recorder.recording_resolution}`, - `PROXY_ENABLED=${recorder.proxy_enabled}`, - `PROXY_CODEC=${recorder.proxy_codec}`, - `PROXY_RESOLUTION=${recorder.proxy_resolution}`, - `PROJECT_ID=${recorder.project_id}`, - `CLIP_NAME=${clipName}`, - ], + Env: env, + ExposedPorts: Object.keys(exposedPorts).length > 0 ? exposedPorts : undefined, HostConfig: { Privileged: true, NetworkMode: dockerNetwork, + PortBindings: Object.keys(portBindings).length > 0 ? portBindings : undefined, }, Hostname: `recorder-${recorder.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`, };