mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-11-14 18:58:49 -05:00
75 lines
2.7 KiB
JavaScript
75 lines
2.7 KiB
JavaScript
// Replay Worker Manager
|
|
// Purpose: Starts/stops ffmpeg segment workers and keeps active worker set aligned with desired sources.
|
|
// Scope: Owns worker process lifecycle and restart behavior for replay segment capture.
|
|
const { spawn } = require('child_process');
|
|
const fsp = require('fs/promises');
|
|
const logger = require('../../globals/logger').child('replayEngineV2');
|
|
const { FFMPEG_BIN } = require('./constants');
|
|
const { workers, pendingWorkerStarts } = require('./state');
|
|
const { sourceKey, sourceDirForKey, listDesiredSources, buildWorkerArgs } = require('./sources');
|
|
|
|
async function ensureDir(dir) {
|
|
await fsp.mkdir(dir, { recursive: true });
|
|
}
|
|
|
|
function createWorkerManager({ getActiveSegmentRoot }) {
|
|
function startWorker(source) {
|
|
const key = sourceKey(source);
|
|
if (workers.has(key) || pendingWorkerStarts.has(key)) return;
|
|
pendingWorkerStarts.add(key);
|
|
const args = buildWorkerArgs(getActiveSegmentRoot(), source);
|
|
const dir = sourceDirForKey(getActiveSegmentRoot(), key);
|
|
ensureDir(dir)
|
|
.then(() => {
|
|
if (workers.has(key)) {
|
|
pendingWorkerStarts.delete(key);
|
|
return;
|
|
}
|
|
const proc = spawn(FFMPEG_BIN, args, { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
workers.set(key, { key, source, proc });
|
|
pendingWorkerStarts.delete(key);
|
|
proc.stderr.on('data', (chunk) => {
|
|
const text = String(chunk || '').trim();
|
|
if (!text) return;
|
|
logger.warn('worker stderr', { key, text: text.slice(0, 500) });
|
|
});
|
|
proc.on('exit', (code, signal) => {
|
|
const current = workers.get(key);
|
|
if (current?.proc === proc) workers.delete(key);
|
|
logger.warn('worker exited', { key, code, signal });
|
|
setTimeout(() => {
|
|
const desired = listDesiredSources().find((entry) => sourceKey(entry) === key);
|
|
if (desired && !workers.has(key)) startWorker(desired);
|
|
}, 1500);
|
|
});
|
|
})
|
|
.catch((err) => {
|
|
pendingWorkerStarts.delete(key);
|
|
logger.warn('failed to start worker', { key, error: err.message });
|
|
});
|
|
}
|
|
|
|
function stopWorker(key) {
|
|
const worker = workers.get(key);
|
|
if (!worker) return;
|
|
try { worker.proc.kill('SIGTERM'); } catch {}
|
|
workers.delete(key);
|
|
}
|
|
|
|
async function syncWorkers() {
|
|
const desired = listDesiredSources();
|
|
const desiredKeys = new Set(desired.map(sourceKey));
|
|
for (const source of desired) {
|
|
const key = sourceKey(source);
|
|
if (!workers.has(key)) startWorker(source);
|
|
}
|
|
for (const key of Array.from(workers.keys())) {
|
|
if (!desiredKeys.has(key)) stopWorker(key);
|
|
}
|
|
}
|
|
|
|
return { startWorker, stopWorker, syncWorkers };
|
|
}
|
|
|
|
module.exports = { createWorkerManager };
|