mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-11-15 03:02:17 -05:00
replay
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
// 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 };
|
||||
Reference in New Issue
Block a user