[ai] improve web stability

This commit is contained in:
2025-08-18 22:34:23 +02:00
parent b6ac157207
commit 0a08709160
2 changed files with 138 additions and 18 deletions

View File

@@ -1,6 +1,88 @@
setInterval(getState, 4000);
setInterval(updateProgress, 500); // Update progress every second
/* Global single-flight queue for XMLHttpRequest
- Serializes all XHR to 1 at a time
- Adds timeouts (GET ~3.5s, POST ~6s, /upload 10min)
- Deduplicates idempotent GETs to same URL (drops duplicates)
This reduces concurrent load on the ESP32 web server and SD card. */
(function(){
var origOpen = XMLHttpRequest.prototype.open;
var origSend = XMLHttpRequest.prototype.send;
var queue = [];
var active = null;
var inflightKeys = new Set();
function keyOf(xhr){ return (xhr.__method || 'GET') + ' ' + (xhr.__url || ''); }
function startNext(){
if (active || queue.length === 0) return;
var item = queue.shift();
active = item;
inflightKeys.add(item.key);
var xhr = item.xhr;
var timeoutMs = item.timeoutMs;
var timer = null;
function cleanup() {
active = null;
inflightKeys.delete(item.key);
if (timer) { clearTimeout(timer); timer = null; }
setTimeout(startNext, 0);
}
xhr.addEventListener('loadend', cleanup);
if (timeoutMs > 0 && !xhr.__skipTimeout) {
timer = setTimeout(function(){
try { xhr.abort(); } catch(e){}
}, timeoutMs);
}
item.origSend.call(xhr, item.body);
}
XMLHttpRequest.prototype.open = function(method, url, async){
this.__method = (method || 'GET').toUpperCase();
this.__url = url || '';
return origOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body){
var key = keyOf(this);
var isIdempotentGET = (this.__method === 'GET');
var timeoutMs;
if ((this.__url || '').indexOf('/upload') !== -1) {
timeoutMs = 600000; // 10 minutes for uploads
} else if (this.__method === 'GET') {
timeoutMs = 3500;
} else {
timeoutMs = 6000;
}
if (isIdempotentGET && inflightKeys.has(key)) {
// Drop duplicate GET to same resource
return;
}
if (isIdempotentGET) {
for (var i = 0; i < queue.length; i++) {
if (queue[i].key === key) {
// Already queued; keep most recent body if any
queue[i].body = body;
return;
}
}
}
var item = { xhr: this, body: body, key: key, timeoutMs: timeoutMs, origSend: origSend };
queue.push(item);
startNext();
};
})();
/* Dynamic content loaders for playlist and mapping (avoid heavy template processing on server) */
function bindPlaylistClicks() {
var container = document.getElementById('playlistContainer');
@@ -107,16 +189,22 @@ function getState() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var state = JSON.parse(xhr.response);
isPlaying = state['playing'];
if (isPlaying) {
songStartTime = Date.now() - state['time'] * 1000;
currentSongLength = state['length'] * 1000;
if (xhr.status >= 200 && xhr.status < 300) {
try {
var state = JSON.parse(xhr.responseText || xhr.response || '{}');
isPlaying = !!state['playing'];
if (isPlaying) {
songStartTime = Date.now() - ((state['time'] || 0) * 1000);
currentSongLength = ((state['length'] || 0) * 1000);
}
lastStateUpdateTime = Date.now();
displayState(state);
} catch (e) {
// Ignore parse errors; will retry on next poll
}
}
lastStateUpdateTime = Date.now();
displayState(state);
}
}
};
xhr.open("GET","/state", true);
xhr.send();
}