Digital signage player — debugging gotchas#
KGC TV CMS / kgc-signage projektből kihegyezett buktatók, amik bármely HTML5-alapú signage-rendszerre érvényesek.
1. <video oncanplay> minden loop-restartnál újra tüzel#
Tünet: A videó-item lejátszik egyszer, aztán fekete képernyő (vagy random villogás multi-item playlist esetén). Loop attribute be van állítva, mégis megáll.
Ok: A HTML5 video canplay event NEM csak az első betöltéskor tüzel. Loop-restart, seek, codec-újraindítás esetén is újra eldob egy canplay-t. Ha a canplay callback triggereli a "ready / next-item" logikát, akkor minden loop-restart "tovább-rotate"-ol → a következő slot üres → fekete képernyő.
Fix — guard flag:
let readyFired = false;
const fireOnce = (durationMs) => {
if (readyFired) return;
readyFired = true;
ready(durationMs);
};
v.addEventListener('canplay', () => fireOnce(durationMs));
v.addEventListener('error', () => fireOnce(2000));
Ugyanígy alkalmazandó <img onload> és <iframe onload> esetén is — bár ritkább, de cache-reload trigger-elheti.
Belt-and-suspenders loop: mellette v.loop = true + v.addEventListener('ended', () => { v.currentTime=0; v.play(); }) — codec-quirk esetén is biztosan loop-ol.
2. Mixed-content blokk HTTPS player-iframe-ben#
Tünet: A player oldal teljesen fekete vagy a 16:9 zóna iframe-je nem rajzolódik. Nincs HTTP error, csak a tartalom hiányzik.
Ok: Az HTTPS-en hostolt admin/player nem tud HTTP-iframe-et megjeleníteni — modern böngészők hard-blokkolják. Egy KGC TV CMS-nél: a player URL https://kivetito.kisgepcentrum.hu/s/utcafront, de az iframe http://72.62.92.98:8200/tv-nyitva.html → blokkolva.
Fix: Lokális /static/ mappa, ugyanazon HTTPS-domain-en szerválva. Pl. /opt/kgc-signage/public/static/tv-nyitva.html → https://kivetito.kisgepcentrum.hu/static/tv-nyitva.html. Egyetlen app.use('/static', express.static(...)) az Express-en.
Hogyan diagnosztizáld: F12 → Console: Mixed Content: ... was loaded over HTTPS, but requested an insecure resource ... üzenet.
3. Multer LIMIT_UNEXPECTED_FILE field=files: count-limit, NEM field-name mismatch#
Tünet: Sok fájlos upload mid-stream megáll, server log: MulterError: Unexpected field, field: 'files'. A field neve EGYEZIK a config-gal — mégis "unexpected".
Ok: Multer array(name, count)-nál ha a count-limit átléped (pl. 50 db a default, és 51-ediket küldsz), akkor az 51. file LIMIT_UNEXPECTED_FILE-ként dől ki a saját nevével. Félrevezető hibaüzenet — nem a field-name a baj, a darabszám.
Fix: 1. Server: upload.array('files', 500) (bumpold a count-ot) 2. Frontend: auto-batching 50-es chunk-okra. Egy batch megszakadása csak azt érinti — a többi már sikerült.
const BATCH_SIZE = 50;
for (let i = 0; i < files.length; i += BATCH_SIZE) {
const batch = files.slice(i, i + BATCH_SIZE);
await uploadBatch(batch, i / BATCH_SIZE);
}
4. 100+ video-thumbnail UI lefagyasztja az admin-t#
Tünet: A média-tár 200+ fájllal megnyitása 5-10s-ig laggol. Kulcsklikk nem reagál.
Ok: Minden <video> elem preload="metadata"-val megpróbálja letölteni az első frame-et — egyszerre 200+ HTTP request, ami a böngésző connection-poolját (~6 párhuzamos) blokkolja.
Fix — IntersectionObserver lazy-load:
// Kezdetben preload="none" — semmi nem tölt
const v = document.createElement('video');
v.preload = 'none';
v.muted = true;
// Ahogy a viewport-ba scrollozódik (200px buffer), aktiválódik
const obs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.preload = 'metadata';
e.target.load();
// Safari-trükk: első frame megjelenítése
e.target.addEventListener('loadedmetadata', () => {
if (e.target.currentTime === 0) e.target.currentTime = 0.1;
}, { once: true });
obs.unobserve(e.target);
}
});
}, { rootMargin: '200px' });
obs.observe(v);
Plus képhez: <img loading="lazy" decoding="async">.
5. Anti-flicker: single-item playlist ne rotáljon#
Tünet: 1-elemes playlist iframe-je 30s-enként újratöltődik (rebuild flicker), mert a polling triggereli az item-rotation-t.
Ok: A pollozó player a queue.length-től függetlenül setTimeout(playNextInZone, durationMs)-t ütemez. Single-item esetén a "következő" ugyanaz az item → re-load → flicker.
Fix: Ha queue.length <= 1, ne ütemezz rotation-t. A polling úgyis észleli ha változik a playlist, és akkor cseréli.
loadIntoSlot(standbySlot, item, (durationMs) => {
swap();
if (queue.length <= 1) return; // ← single-item → no rotation
nextTimer = setTimeout(() => playNext(), durationMs);
});
Kapcsolódó#
- 02-Projects/kgc-tv-cms — referencia-implementáció
- 02-Projects/kgc-kivetitok — bolti deployment kontextus
- 08-Sessions/2026-05-03-kgc-kivetit — eredet-session