[ai] improve web stability
This commit is contained in:
104
web/script.js
104
web/script.js
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user