[fix](trx-server): auto-recover audio streams and harden frontend reconnect

Implement ALSA/CPAL stream auto-recovery by recreating input/output
streams after backend callback failures with bounded retry delay.

Also improve HTTP frontend resilience by polling /status on reconnect
and after SSE errors to refresh snapshot state after broken pipes.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
2026-02-12 23:59:39 +01:00
parent 273283708e
commit d085f96838
2 changed files with 252 additions and 105 deletions
@@ -74,6 +74,7 @@ let initialized = false;
let lastEventAt = Date.now();
let es;
let esHeartbeat;
let reconnectTimer = null;
function formatFreq(hz) {
if (!Number.isFinite(hz)) return "--";
@@ -446,6 +447,26 @@ function render(update) {
}
}
function scheduleReconnect(delayMs = 1000) {
if (reconnectTimer) return;
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
connect();
}, delayMs);
}
async function pollFreshSnapshot() {
try {
const resp = await fetch("/status", { cache: "no-store" });
if (!resp.ok) return;
const data = await resp.json();
render(data);
lastEventAt = Date.now();
} catch (e) {
// Ignore network errors; connect() retry loop handles reconnection.
}
}
function connect() {
if (es) {
es.close();
@@ -453,9 +474,13 @@ function connect() {
if (esHeartbeat) {
clearInterval(esHeartbeat);
}
pollFreshSnapshot();
es = new EventSource("/events");
lastEventAt = Date.now();
es.onmessage = (evt) => {
es.onopen = () => {
pollFreshSnapshot();
};
es.onmessage = (evt) => {
try {
if (evt.data === lastRendered) return;
const data = JSON.parse(evt.data);
@@ -472,14 +497,16 @@ es.onmessage = (evt) => {
es.onerror = () => {
powerHint.textContent = "Disconnected, retrying…";
es.close();
setTimeout(connect, 1000);
pollFreshSnapshot();
scheduleReconnect(1000);
};
esHeartbeat = setInterval(() => {
const now = Date.now();
if (now - lastEventAt > 15000) {
es.close();
connect();
pollFreshSnapshot();
scheduleReconnect(250);
}
}, 5000);
}