import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import captureRoutes from './routes/capture.js'; dotenv.config(); const app = express(); const PORT = process.env.PORT || 3001; // Middleware app.use(cors()); app.use(express.json()); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok' }); }); // Routes app.use('/capture', captureRoutes); // Start server app.listen(PORT, () => { console.log(`Wild Dragon Capture Service listening on port ${PORT}`); }); async function bootstrapAutoStart() { const recorderId = process.env.RECORDER_ID; const sourceType = process.env.SOURCE_TYPE; if (!recorderId || !sourceType) { console.log('[bootstrap] no RECORDER_ID/SOURCE_TYPE - on-demand sidecar'); return; } const projectId = process.env.PROJECT_ID; const clipName = process.env.CLIP_NAME; if (!projectId || !clipName) { console.error('[bootstrap] missing PROJECT_ID or CLIP_NAME - cannot start'); return; } const listen = process.env.LISTEN === '1' || process.env.LISTEN === 'true'; const listenPort = process.env.LISTEN_PORT ? parseInt(process.env.LISTEN_PORT, 10) : undefined; const streamKey = process.env.STREAM_KEY || undefined; const sourceUrl = process.env.SOURCE_URL || undefined; if (sourceType === 'sdi') { console.warn('[bootstrap] SDI auto-start not supported'); return; } console.log(`[bootstrap] starting ${sourceType} ingest (listen=${listen} port=${listenPort || 'n/a'})...`); try { const session = await captureManager.start({ assetId: process.env.ASSET_ID || null, projectId, binId: process.env.BIN_ID || null, clipName, sourceType, sourceUrl, listen, listenPort, streamKey, }); console.log(`[bootstrap] session ${session.sessionId} started for clip ${clipName}`); } catch (err) { console.error('[bootstrap] failed to start capture:', err); } } let shuttingDown = false; async function gracefulShutdown(signal) { if (shuttingDown) return; shuttingDown = true; console.log(`[shutdown] ${signal} received`); const status = captureManager.getStatus(); if (status.recording) { console.log(`[shutdown] stopping active session ${status.sessionId}...`); try { const completed = await captureManager.stop(status.sessionId); console.log(`[shutdown] session ${completed.sessionId} finalised; duration=${completed.duration}s`); try { const res = await fetch(`${MAM_API_URL}/api/v1/assets`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: completed.projectId, binId: completed.binId, clipName: completed.clipName, sourceType: completed.sourceType, hiresKey: completed.hiresKey, proxyKey: completed.proxyKey, needsProxy: completed.proxyKey === null, duration: completed.duration, capturedAt: completed.startedAt, }), }); if (!res.ok) { console.warn(`[shutdown] mam-api /assets returned ${res.status}: ${await res.text()}`); } else { console.log('[shutdown] asset registered with mam-api'); } } catch (mamErr) { console.error('[shutdown] failed to register asset:', mamErr.message); } } catch (err) { console.error('[shutdown] error during stop:', err); } } server.close(() => { console.log('[shutdown] http server closed - exiting'); process.exit(0); }); setTimeout(() => process.exit(0), 5000).unref(); } process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT'));