2026-04-07 21:58:29 -04:00
|
|
|
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}`);
|
|
|
|
|
});
|
2026-05-18 07:29:50 -04:00
|
|
|
|
|
|
|
|
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'));
|