mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 10:00:46 -04:00
converge everything into one data folder
This commit is contained in:
@@ -6,6 +6,7 @@ const EventEmitter = require('events');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('audioForwardService');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { resolveRuntimePath } = require('../../helpers/dataPaths');
|
||||
const roverManager = require('../roverManager');
|
||||
const turnService = require('../turnService');
|
||||
const { isMuted, isVerified, verificationEvents } = require('../verificationService');
|
||||
@@ -25,7 +26,13 @@ const streamSuffix =
|
||||
typeof audioForwardConfig.streamSuffix === 'string' && audioForwardConfig.streamSuffix.trim()
|
||||
? audioForwardConfig.streamSuffix.trim()
|
||||
: '-fwd';
|
||||
const runtimeDir = path.resolve(audioForwardConfig.runtimeDir || '/tmp/mrr-audio-forward');
|
||||
/*
|
||||
FIFOs and uploaded clips are disposable, but they are deliberately created
|
||||
and managed by this application. A fixed path below SERVER_DATA_DIR keeps the
|
||||
Node process from writing to an unrelated host temp directory and prevents a
|
||||
configuration value from escaping the server's filesystem boundary.
|
||||
*/
|
||||
const runtimeDir = resolveRuntimePath('audio-forward');
|
||||
const uploadsDir = path.join(runtimeDir, 'uploads');
|
||||
const maxUploadBytes = Number.isFinite(audioForwardConfig.maxUploadBytes)
|
||||
? Math.max(256 * 1024, Math.floor(audioForwardConfig.maxUploadBytes))
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fsp = require('fs/promises');
|
||||
const path = require('path');
|
||||
const { resolveRoverSnapshotDir } = require('../../helpers/dataPaths');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
const { getRoomCameraState } = require('../roomCameraService');
|
||||
const { getReplayHealthSnapshot } = require('../replayEngineV2');
|
||||
|
||||
const ROVER_SNAPSHOT_DIR = process.env.ROVER_SNAPSHOT_DIR || '/var/lib/rover-snapshots';
|
||||
const ROVER_SNAPSHOT_DIR = resolveRoverSnapshotDir();
|
||||
const HEALTH_INTERVAL_MS = 5000;
|
||||
const ROOM_CAMERA_STALE_MS = 5000;
|
||||
const ROVER_SNAPSHOT_STALE_MS = 5000;
|
||||
|
||||
@@ -5,7 +5,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const yaml = require('js-yaml');
|
||||
const { resolveDataPath } = require('../../helpers/dataPaths');
|
||||
const { resolveDataDir, resolveDataPath } = require('../../helpers/dataPaths');
|
||||
const { buildMediaMtxConfig } = require('./config');
|
||||
|
||||
function createMediaMtxSupervisor(deps) {
|
||||
@@ -52,6 +52,16 @@ function createMediaMtxSupervisor(deps) {
|
||||
logger.info(`Starting MediaMTX with generated config ${configPath}`);
|
||||
child = spawnProcess(mediaMtxBin, [configPath], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
/*
|
||||
MediaMTX passes its environment to runOnReady hooks. Supplying the
|
||||
resolved value here also covers development starts where
|
||||
SERVER_DATA_DIR was omitted, so the installed snapshot writer and every
|
||||
Node snapshot reader still converge on the same canonical data root.
|
||||
*/
|
||||
env: {
|
||||
...process.env,
|
||||
SERVER_DATA_DIR: resolveDataDir(),
|
||||
},
|
||||
});
|
||||
|
||||
forwardLines(child.stdout, 'info');
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// MediaMTX Supervisor Tests
|
||||
// Purpose: Verifies that generated MediaMTX state and child hooks inherit the server's single data root.
|
||||
// Scope: Uses a child-process double and a temporary directory; no listener or background process is started.
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const EventEmitter = require('events');
|
||||
const { PassThrough } = require('stream');
|
||||
const { createMediaMtxSupervisor } = require('./supervisor');
|
||||
|
||||
test('passes the resolved data root to MediaMTX runOnReady hooks', () => {
|
||||
const temporaryDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'multirover-mediamtx-supervisor-'));
|
||||
const generatedConfigPath = path.join(temporaryDataDir, 'mediamtx.yml');
|
||||
const previousDataDir = process.env.SERVER_DATA_DIR;
|
||||
let invocation = null;
|
||||
|
||||
process.env.SERVER_DATA_DIR = temporaryDataDir;
|
||||
|
||||
const spawnProcess = (command, args, options) => {
|
||||
const child = new EventEmitter();
|
||||
child.stdout = new PassThrough();
|
||||
child.stderr = new PassThrough();
|
||||
child.kill = (signal) => {
|
||||
child.emit('exit', 0, signal);
|
||||
};
|
||||
invocation = { command, args, options };
|
||||
return child;
|
||||
};
|
||||
|
||||
const logger = {
|
||||
info() {},
|
||||
warn() {},
|
||||
error() {},
|
||||
};
|
||||
|
||||
try {
|
||||
const supervisor = createMediaMtxSupervisor({
|
||||
config: { media: { additionalHosts: ['media.example.test'] } },
|
||||
serverPort: 8080,
|
||||
logger,
|
||||
mediaMtxBin: '/test/bin/mediamtx',
|
||||
snapshotWriterPath: '/test/bin/rover-snapshot-writer',
|
||||
configPath: generatedConfigPath,
|
||||
spawnProcess,
|
||||
});
|
||||
|
||||
supervisor.start();
|
||||
|
||||
assert.equal(invocation.command, '/test/bin/mediamtx');
|
||||
assert.deepEqual(invocation.args, [generatedConfigPath]);
|
||||
assert.equal(invocation.options.env.SERVER_DATA_DIR, temporaryDataDir);
|
||||
assert.equal(fs.existsSync(generatedConfigPath), true);
|
||||
|
||||
supervisor.stop();
|
||||
} finally {
|
||||
/*
|
||||
Restore process-global state and delete only the test-owned directory so a
|
||||
failed assertion cannot alter later tests or leave generated YAML behind.
|
||||
*/
|
||||
if (previousDataDir === undefined) delete process.env.SERVER_DATA_DIR;
|
||||
else process.env.SERVER_DATA_DIR = previousDataDir;
|
||||
fs.rmSync(temporaryDataDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
@@ -10,6 +10,7 @@ const { Cam } = require('onvif');
|
||||
const io = require('../../globals/io');
|
||||
const logger = require('../../globals/logger').child('ptzCamera');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { resolveRoverSnapshotDir } = require('../../helpers/dataPaths');
|
||||
const { isFeatureEnabled } = require('../../helpers/features');
|
||||
const {
|
||||
shouldUseSnapshotsForNonTurnVideo,
|
||||
@@ -43,7 +44,7 @@ const STOP_MOTION = Object.freeze({ pan: 0, tilt: 0, zoom: 0 });
|
||||
// explicitly disables replay for the camera.
|
||||
const DEFAULT_REPLAY_ENABLED = true;
|
||||
const DEFAULT_PTZ_COLOR = '#387bf8';
|
||||
const SNAPSHOT_DIR = process.env.ROVER_SNAPSHOT_DIR || '/var/lib/rover-snapshots';
|
||||
const SNAPSHOT_DIR = resolveRoverSnapshotDir();
|
||||
const SNAPSHOT_POLL_MS = 300;
|
||||
const SNAPSHOT_STREAM_INTERVAL_MS = 2000;
|
||||
const SPOTLIGHT_VERIFY_DELAY_MS = 1200;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Replay Builder Pipeline
|
||||
// Purpose: Assembles selected buffered segments into final replay output video with optional sidebar.
|
||||
// Scope: Owns concat/probe/layout/transcode pipeline and returns replay buffer plus source usage metadata.
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { resolveRuntimePath } = require('../../helpers/dataPaths');
|
||||
const { getActiveDrivers } = require('../turnService');
|
||||
const { getNickname } = require('../nicknameService');
|
||||
const { getRecentMessages } = require('../chatService');
|
||||
@@ -124,7 +124,15 @@ function createReplayBuilder({ execFileAsync, fsp, ensureDir, renderSidebarVideo
|
||||
durationMs: BUILD_DURATION_MS,
|
||||
});
|
||||
const resolvedTitle = sanitizeReplayTitle(title, resolveDefaultReplayTitle(requester, sources));
|
||||
const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'mrr-replay-v2-'));
|
||||
/*
|
||||
A replay build is disposable, but every intermediate concat list, pinned
|
||||
segment, and ffmpeg output is created on the server's behalf. Create a
|
||||
unique workspace below SERVER_DATA_DIR so the application never spills
|
||||
those writes into the host-wide temporary directory.
|
||||
*/
|
||||
const buildRoot = resolveRuntimePath('replay-builds');
|
||||
await ensureDir(buildRoot);
|
||||
const tmpDir = await fsp.mkdtemp(path.join(buildRoot, 'build-'));
|
||||
|
||||
try {
|
||||
const usedSources = [];
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
const { execFile } = require('child_process');
|
||||
const EventEmitter = require('events');
|
||||
const fsp = require('fs/promises');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const logger = require('../../globals/logger').child('roomCameraReplay');
|
||||
const { resolveRuntimePath } = require('../../helpers/dataPaths');
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
@@ -118,7 +118,15 @@ async function buildRoomCameraReplayVideo({ cameraId = null } = {}, { getRoomCam
|
||||
});
|
||||
if (!cameraEntries.length) throw new Error('No camera frames available yet');
|
||||
|
||||
const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'rover-replay-'));
|
||||
/*
|
||||
Hundreds of frame images can be produced for one room-camera replay. They
|
||||
are temporary, but the Node application owns them, so both the workspace
|
||||
and final intermediate video stay under the configured data root until the
|
||||
existing finally block removes them.
|
||||
*/
|
||||
const buildRoot = resolveRuntimePath('room-camera-replay-builds');
|
||||
await fsp.mkdir(buildRoot, { recursive: true });
|
||||
const tmpDir = await fsp.mkdtemp(path.join(buildRoot, 'build-'));
|
||||
try {
|
||||
const firstFramePaths = [];
|
||||
for (let i = 0; i < cameraEntries.length; i += 1) {
|
||||
|
||||
@@ -5,8 +5,9 @@ const EventEmitter = require('events');
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
const logger = require('../../globals/logger').child('roverSnapshot');
|
||||
const { resolveRoverSnapshotDir } = require('../../helpers/dataPaths');
|
||||
|
||||
const SNAPSHOT_DIR = process.env.ROVER_SNAPSHOT_DIR || '/var/lib/rover-snapshots';
|
||||
const SNAPSHOT_DIR = resolveRoverSnapshotDir();
|
||||
const POLL_INTERVAL_MS = 300;
|
||||
const roverState = new Map();
|
||||
const events = new EventEmitter();
|
||||
|
||||
Reference in New Issue
Block a user