Omit unsupported ffmpeg reconnect retry option
This commit is contained in:
@@ -28,6 +28,7 @@ const FFMPEG_HTTP_RECONNECT = parseBoolean(process.env.FFMPEG_HTTP_RECONNECT, tr
|
||||
const FFMPEG_HTTP_RECONNECT_DELAY_MAX = clampInteger(process.env.FFMPEG_HTTP_RECONNECT_DELAY_MAX, 2, 0, 30);
|
||||
const FFMPEG_HTTP_RECONNECT_MAX_RETRIES = clampInteger(process.env.FFMPEG_HTTP_RECONNECT_MAX_RETRIES, 4, 0, 20);
|
||||
const FFMPEG_HTTP_RECONNECT_ON_HTTP_ERROR = process.env.FFMPEG_HTTP_RECONNECT_ON_HTTP_ERROR ?? '5xx';
|
||||
const FFMPEG_CAPABILITY_TIMEOUT_MS = 3000;
|
||||
const PLAYBACK_CONNECTION_MODE = parsePlaybackConnectionMode(process.env.PLAYBACK_CONNECTION_MODE ?? process.env.PLAYBACK_MODE);
|
||||
const METADATA_PROBE_ENABLED = parseBoolean(process.env.METADATA_PROBE_ENABLED, PLAYBACK_CONNECTION_MODE !== 'relay');
|
||||
const METADATA_PROBE_TIMEOUT_MS = clampInteger(process.env.METADATA_PROBE_TIMEOUT_MS, 4 * 1000, 1000, 30 * 1000);
|
||||
@@ -77,6 +78,7 @@ let recentUrls = [];
|
||||
let recentWrite = Promise.resolve();
|
||||
let favorites = [];
|
||||
let favoritesWrite = Promise.resolve();
|
||||
let ffmpegSupportsReconnectMaxRetries = false;
|
||||
|
||||
app.disable('x-powered-by');
|
||||
app.use(express.json({ limit: '256kb' }));
|
||||
@@ -429,6 +431,7 @@ setInterval(() => {
|
||||
await Promise.all([
|
||||
loadRecentUrls(),
|
||||
loadFavorites(),
|
||||
detectFfmpegHttpReconnectSupport(),
|
||||
]);
|
||||
|
||||
server.listen(PORT, () => {
|
||||
@@ -889,6 +892,80 @@ function runFfprobe(args) {
|
||||
});
|
||||
}
|
||||
|
||||
async function detectFfmpegHttpReconnectSupport() {
|
||||
if (!FFMPEG_HTTP_RECONNECT || FFMPEG_HTTP_RECONNECT_MAX_RETRIES <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const output = await runFfmpegCapabilityCheck(['-hide_banner', '-h', 'protocol=http']);
|
||||
ffmpegSupportsReconnectMaxRetries = /^\s*-reconnect_max_retries\b/m.test(output);
|
||||
|
||||
if (!ffmpegSupportsReconnectMaxRetries) {
|
||||
logWarn('ffmpeg http option reconnect_max_retries is unsupported; omitting FFMPEG_HTTP_RECONNECT_MAX_RETRIES');
|
||||
}
|
||||
} catch (error) {
|
||||
ffmpegSupportsReconnectMaxRetries = false;
|
||||
logWarn(`ffmpeg http option detection failed; omitting FFMPEG_HTTP_RECONNECT_MAX_RETRIES error=${oneLine(error.message)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function runFfmpegCapabilityCheck(args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ffmpeg = spawn(FFMPEG_PATH, args, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
let output = '';
|
||||
let timedOut = false;
|
||||
let settled = false;
|
||||
|
||||
const finish = (callback) => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
callback();
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
timedOut = true;
|
||||
stopProcess(ffmpeg);
|
||||
}, FFMPEG_CAPABILITY_TIMEOUT_MS);
|
||||
timer.unref();
|
||||
|
||||
ffmpeg.stdout.on('data', (chunk) => {
|
||||
output = appendTail(output, chunk);
|
||||
});
|
||||
|
||||
ffmpeg.stderr.on('data', (chunk) => {
|
||||
output = appendTail(output, chunk);
|
||||
});
|
||||
|
||||
ffmpeg.on('error', (error) => {
|
||||
finish(() => reject(error));
|
||||
});
|
||||
|
||||
ffmpeg.on('close', (code, signal) => {
|
||||
finish(() => {
|
||||
if (timedOut) {
|
||||
reject(new Error('ffmpeg capability check timed out.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (code === 0) {
|
||||
resolve(output);
|
||||
return;
|
||||
}
|
||||
|
||||
const detail = redactSecrets(output).trim();
|
||||
reject(new Error(detail ? `ffmpeg capability check exited with code ${code}: ${detail}` : `ffmpeg capability check exited with code ${code ?? 'null'} signal ${signal ?? 'none'}.`));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadRecentUrls() {
|
||||
try {
|
||||
const raw = await fs.readFile(RECENT_URLS_PATH, 'utf8');
|
||||
@@ -2507,10 +2584,12 @@ function buildInputArgs(inputUrl, { seekable = true, startTime = 0 } = {}) {
|
||||
'1',
|
||||
'-reconnect_delay_max',
|
||||
String(FFMPEG_HTTP_RECONNECT_DELAY_MAX),
|
||||
'-reconnect_max_retries',
|
||||
String(FFMPEG_HTTP_RECONNECT_MAX_RETRIES),
|
||||
);
|
||||
|
||||
if (FFMPEG_HTTP_RECONNECT_MAX_RETRIES > 0 && ffmpegSupportsReconnectMaxRetries) {
|
||||
args.push('-reconnect_max_retries', String(FFMPEG_HTTP_RECONNECT_MAX_RETRIES));
|
||||
}
|
||||
|
||||
if (FFMPEG_HTTP_RECONNECT_ON_HTTP_ERROR) {
|
||||
args.push('-reconnect_on_http_error', FFMPEG_HTTP_RECONNECT_ON_HTTP_ERROR);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user