openvibers/openvibe.live Commits

Live: restream stream keys never reach the log through ffmpeg's own error text (#140)

OP OpenVibers committed 29 days ago · 0dcb4fd1a5 · 2 files, +13 −3

Changes

ddiff --git a/server/streaming/restream-manager.js b/server/streaming/restream-manager.js
iindex c0e15d12..440f73c8 100644
---- a/server/streaming/restream-manager.js
++++ b/server/streaming/restream-manager.js
@@@ -946,7 +946,8 @@ class RestreamManager extends EventEmitter {
if (session.status === 'stopped') return; // intentional stop
const duration = Date.now() - (session.startedAt || Date.now());
-- const lastLines = stderrBuf.split('\n').filter(Boolean).slice(-5).join(' | ');
++ // ffmpeg names the output URL in its errors ("Error opening output rtmps://…/app/<key>"): keys never reach a log.
++ const lastLines = require('../utils/redact').redactUrl(stderrBuf.split('\n').filter(Boolean).slice(-5).join(' | '));
console.log(`[Restream] FFmpeg exited for ${session.key}: code=${code}, ran ${(duration / 1000).toFixed(1)}s`);
if (lastLines) console.log(`[Restream] stderr: ${lastLines.slice(0, 300)}`);
@@@ -974,7 +975,7 @@ class RestreamManager extends EventEmitter {
setTimeout(async () => {
if (liveConfirmed || session.process !== proc || session.status !== 'starting') return;
if (progressSeenAt) { confirmLive(); return; } // progress arrived but no frames yet: treat as connected
-- session.lastErrorRaw = stderrBuf.split('\n').filter(Boolean).slice(-3).join(' | ') || 'no output progress';
++ session.lastErrorRaw = require('../utils/redact').redactUrl(stderrBuf.split('\n').filter(Boolean).slice(-3).join(' | ')) || 'no output progress';
session.lastError = `No response from the ingest within ${LIVE_ACK_TIMEOUT_MS / 1000}s — ${RestreamManager.friendlyFfmpegError(session.lastErrorRaw, session.destination?.platform)}`;
console.warn(`[Restream] ${session.key}: ${session.lastError}`);
try { proc.kill('SIGTERM'); } catch { /* */ }
ddiff --git a/test/restream-manager-lifecycle.test.js b/test/restream-manager-lifecycle.test.js
iindex 8de364bd..ea7adbb2 100644
---- a/test/restream-manager-lifecycle.test.js
++++ b/test/restream-manager-lifecycle.test.js
@@@ -110,8 +110,17 @@ manager.sessions.delete('1:9');
// Failure path: an output that cannot be opened ends with a friendly error and a scheduled restart.
const bad = { ...session, key: 't:2', destId: 2, status: 'starting', process: null, progress: null, destination: { platform: 'custom', server_url: 'rtmp://127.0.0.1:1' } };
manager.sessions.set(bad.key, bad);
-- manager._spawnFFmpeg(bad, ['-hide_banner', '-loglevel', 'warning', '-f', 'lavfi', '-i', 'testsrc=duration=1:size=64x64:rate=10', '-c:v', 'libx264', '-preset', 'ultrafast', '-f', 'flv', 'rtmp://127.0.0.1:1/live/k']);
++ // ffmpeg names the output URL in its error; the stream key must not reach the log or the stored error.
++ const logged = [];
++ console.log = (...a) => logged.push(a.join(' '));
++ console.warn = (...a) => logged.push(a.join(' '));
++ manager._spawnFFmpeg(bad, ['-hide_banner', '-loglevel', 'warning', '-f', 'lavfi', '-i', 'testsrc=duration=1:size=64x64:rate=10', '-c:v', 'libx264', '-preset', 'ultrafast', '-f', 'flv', 'rtmp://127.0.0.1:1/live/sekritStreamKey123']);
await new Promise((r) => bad.process.on('close', () => setTimeout(r, 50)));
++ console.log = () => {};
++ console.warn = () => {};
++ assert.ok(logged.some((l) => l.includes('[Restream] stderr:')), 'the stderr tail is logged');
++ assert.ok(!logged.some((l) => l.includes('sekritStreamKey123')), `no stream key in the log: ${logged.filter((l) => l.includes('sekrit')).join(' / ')}`);
++ assert.ok(!String(bad.lastErrorRaw).includes('sekritStreamKey123') && !String(bad.lastError).includes('sekritStreamKey123'), 'nor in the stored error');
assert.strictEqual(bad.status, 'error');
// FFmpeg can report either a refused connection or an RTMP rejection in restricted environments.
assert.match(bad.lastError, /Could not connect to the destination|the destination rejected the stream key/, bad.lastError);