Add company dashboard improvements and upload/auth features

This commit is contained in:
2026-01-23 20:21:11 +01:00
parent 1394ef6f67
commit ea3d0164f2
14 changed files with 1004 additions and 112 deletions

View File

@@ -8,16 +8,15 @@
html, body { height: 100%; width: 100%; margin: 0; background: #000; overflow: hidden; }
#stage { position: fixed; inset: 0; width: 100vw; height: 100vh; background: #000; }
img, video, iframe { width: 100%; height: 100%; object-fit: contain; border: 0; }
.notice { position: fixed; left: 12px; bottom: 12px; color: #bbb; font: 14px/1.3 sans-serif; }
/* removed bottom-left status text */
</style>
</head>
<body>
<div id="stage"></div>
<div class="notice" id="notice"></div>
<script>
const token = "{{ display.token }}";
const stage = document.getElementById('stage');
const notice = document.getElementById('notice');
function setNotice(_text) { /* intentionally no-op: notice UI removed */ }
const isPreview = new URLSearchParams(window.location.search).get('preview') === '1';
@@ -39,6 +38,9 @@
let idx = 0;
let timer = null;
let es = null;
let esRetryMs = 1000;
async function fetchPlaylist() {
const qs = sid ? `?sid=${encodeURIComponent(sid)}` : '';
const res = await fetch(`/api/display/${token}/playlist${qs}`, { cache: 'no-store' });
@@ -56,7 +58,7 @@
function next() {
if (!playlist || !playlist.items || playlist.items.length === 0) {
notice.textContent = 'No playlist assigned.';
setNotice('No playlist assigned.');
clearStage();
return;
}
@@ -65,7 +67,7 @@
idx = (idx + 1) % playlist.items.length;
clearStage();
notice.textContent = playlist.playlist ? `${playlist.display}${playlist.playlist.name}` : playlist.display;
setNotice(playlist.playlist ? `${playlist.display}${playlist.playlist.name}` : playlist.display);
if (item.type === 'image') {
const el = document.createElement('img');
@@ -109,10 +111,14 @@
next();
} catch (e) {
clearStage();
notice.textContent = e && e.message ? e.message : 'Unable to load playlist.';
setNotice(e && e.message ? e.message : 'Unable to load playlist.');
// keep retrying; if a slot frees up the display will start automatically.
}
// refresh playlist every 60s
// Open live event stream: when server signals a change, reload playlist immediately.
connectEvents();
// Fallback refresh (in case SSE is blocked by a proxy/network): every 5 minutes.
setInterval(async () => {
try {
playlist = await fetchPlaylist();
@@ -122,9 +128,47 @@
}
} catch(e) {
clearStage();
notice.textContent = e && e.message ? e.message : 'Unable to load playlist.';
setNotice(e && e.message ? e.message : 'Unable to load playlist.');
}
}, 60000);
}, 300000);
}
function connectEvents() {
if (isPreview) return; // preview shouldn't consume a slot / keep a long-lived connection
try { if (es) es.close(); } catch(e) { /* ignore */ }
const qs = sid ? `?sid=${encodeURIComponent(sid)}` : '';
es = new EventSource(`/api/display/${token}/events${qs}`);
es.addEventListener('changed', async () => {
try {
const newPlaylist = await fetchPlaylist();
// If content changed, restart from the beginning.
const oldStr = JSON.stringify(playlist);
const newStr = JSON.stringify(newPlaylist);
playlist = newPlaylist;
if (oldStr !== newStr) {
idx = 0;
next();
}
esRetryMs = 1000; // reset backoff on success
} catch(e) {
// leave current playback; we'll retry via reconnect handler
}
});
es.onerror = () => {
try { es.close(); } catch(e) { /* ignore */ }
es = null;
// Exponential backoff up to 30s
const wait = esRetryMs;
esRetryMs = Math.min(30000, Math.floor(esRetryMs * 1.7));
setTimeout(connectEvents, wait);
};
}
start();