Auto-reload web player when server code changes

Player polls /api/version every 30s and reloads if the hash changes.
Server hash now includes player/index.html and sw.js so player code
updates are detected without requiring a hard refresh.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ScreenTinker 2026-04-13 22:44:47 -05:00
parent ad3095cdf5
commit 2104c9cc9f
2 changed files with 25 additions and 0 deletions

View file

@ -287,6 +287,7 @@
startHeartbeat(); startHeartbeat();
startPlaylistRefresh(); startPlaylistRefresh();
startVersionCheck();
}); });
socket.on('device:paired', (data) => { socket.on('device:paired', (data) => {
@ -465,6 +466,27 @@
}, 300000); // 5 minutes fallback }, 300000); // 5 minutes fallback
} }
// ==================== Auto-reload on code update ====================
let knownServerHash = null;
let versionCheckTimer = null;
function startVersionCheck() {
if (versionCheckTimer) clearInterval(versionCheckTimer);
// Initial fetch to learn current hash
fetch(config.serverUrl + '/api/version').then(r => r.json()).then(data => {
knownServerHash = data.hash;
console.log('Server version:', data.version, 'hash:', data.hash);
}).catch(() => {});
// Poll every 30s
versionCheckTimer = setInterval(() => {
fetch(config.serverUrl + '/api/version').then(r => r.json()).then(data => {
if (knownServerHash && data.hash !== knownServerHash) {
console.log('Server code updated, reloading...', knownServerHash, '->', data.hash);
location.reload();
}
}).catch(() => {});
}, 30000);
}
// ==================== Playlist ==================== // ==================== Playlist ====================
function handlePlaylistUpdate(data) { function handlePlaylistUpdate(data) {
// Check if device is suspended (trial expired / over limit) // Check if device is suspended (trial expired / over limit)

View file

@ -235,6 +235,9 @@ function updateFrontendHash() {
'js/views/activity.js', 'js/views/kiosk.js'].map(f => { 'js/views/activity.js', 'js/views/kiosk.js'].map(f => {
try { return fs.readFileSync(path.join(config.frontendDir, f)); } catch { return ''; } try { return fs.readFileSync(path.join(config.frontendDir, f)); } catch { return ''; }
}); });
// Include player files in hash so web players detect code updates
try { files.push(fs.readFileSync(path.join(__dirname, 'player', 'index.html'))); } catch {}
try { files.push(fs.readFileSync(path.join(__dirname, 'player', 'sw.js'))); } catch {}
frontendHash = crypto.createHash('md5').update(Buffer.concat(files.map(f => Buffer.from(f)))).digest('hex').slice(0, 8); frontendHash = crypto.createHash('md5').update(Buffer.concat(files.map(f => Buffer.from(f)))).digest('hex').slice(0, 8);
} catch { frontendHash = Date.now().toString(36); } } catch { frontendHash = Date.now().toString(36); }
} }