diff --git a/src/servers/api/tasks/task-executor.ts b/src/servers/api/tasks/task-executor.ts index ed951787..e6544e11 100644 --- a/src/servers/api/tasks/task-executor.ts +++ b/src/servers/api/tasks/task-executor.ts @@ -235,6 +235,13 @@ async function handleRun(ws: ServerWebSocket, msg: RunMessage) { }, }); + // Keep the WebSocket alive during long silent phases (e.g. ffmpeg's faststart pass rewrites a huge + // file for minutes with no output). Bun's default 120s idle timeout would otherwise close the + // socket → close(ws) → killTree kills the task mid-run. A ping resets the idle timer. + const keepAlive = setInterval(() => { + try { ws.ping(); } catch { /* socket gone */ } + }, 30_000); + const stdoutReader = proc.stdout.getReader(); const stderrReader = proc.stderr.getReader(); const decoder = new TextDecoder(); @@ -257,6 +264,7 @@ async function handleRun(ws: ServerWebSocket, msg: RunMessage) { proc.exited, ]); + clearInterval(keepAlive); activeProcs.delete(ws); cleanup(); send(ws, { type: 'exit', code: exitCode });