Skip to content

Instantly share code, notes, and snippets.

@kohlerdominik
Created February 19, 2026 20:29
Show Gist options
  • Select an option

  • Save kohlerdominik/bcbb648d41d7c9b9b02a1469b1c1f68e to your computer and use it in GitHub Desktop.

Select an option

Save kohlerdominik/bcbb648d41d7c9b9b02a1469b1c1f68e to your computer and use it in GitHub Desktop.
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
This is a digital signage player for Samsung Tizen / Embedded screens.
It buffers the entire video into RAM (Blob) before playing to prevent high traffic.
Plays video ./video.mp4 in a loop, after loading has finished.
TO CHANGE VIDEO: Edit the <video> tag attribute: data-remote-src="PATH/TO/YOUR/VIDEO.mp4"
IMPORTANT: The video file MUST be on the same domain, or the server must enable CORS.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Video Player for Samsung Smart Signage Display</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: black;
}
/* Video setup */
#background-video {
position: fixed;
top: 50%;
left: 50%;
min-width: 100%;
min-height: 100%;
transform: translate(-50%, -50%);
object-fit: cover;
z-index: 10;
opacity: 0;
}
/* Loading Overlay */
#loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: sans-serif;
z-index: 100;
}
#loading-title {
font-size: 60px;
font-weight: bold;
margin-bottom: 10px;
}
#status-log {
font-size: 18px;
color: #aaaaaa;
letter-spacing: 2px;
margin-top: 15px;
}
/* Optional Welcome Message */
#welcome-message {
display: none; /* Set to block|none */
position: fixed;
top: 0;
left: 50%;
min-width: 90%;
transform: translate(-50%, 10%);
z-index: 1000;
padding: 20px;
background-color: rgba(255, 255, 0, 0.8);
border-radius: 4px;
color: white;
text-shadow: 0px 0px 3px black;
text-align: center;
font-family: sans-serif;
font-weight: bold;
font-size: 40px;
}
</style>
</head>
<body>
<div id="loading-screen">
<div id="loading-title">Updating Content...</div>
<div id="status-log">Initializing...</div>
</div>
<video id="background-video" muted playsinline data-remote-src="video.mp4">
Your browser does not support the video tag.
</video>
<script>
/**
* This script manages memory-efficient video loading with real-time data tracking.
* It pipes the network stream through a counter to display both percentage and
* megabyte progress, then converts the stream directly into a Blob. This avoids
* Tizen storage quotas and minimizes memory overhead while ensuring a fresh download.
*/
document.addEventListener('DOMContentLoaded', async () => {
const player = document.getElementById('background-video');
const overlay = document.getElementById('loading-screen');
const logger = document.getElementById('status-log');
const sourceUrl = player.getAttribute('data-remote-src');
function log(msg) {
console.log("[Signage]: " + msg);
if (logger) logger.innerText = msg;
}
if (!player || !sourceUrl) {
log("Critical Error: Elements or Source missing");
return;
}
function initialize() {
log("Connecting to server...");
const xhr = new XMLHttpRequest();
// Add a cache-busting timestamp to ensure fresh download
const urlWithCacheBuster = sourceUrl + (sourceUrl.includes('?') ? '&' : '?') + 't=' + new Date().getTime();
xhr.open('GET', urlWithCacheBuster, true);
xhr.responseType = 'blob';
xhr.onprogress = (event) => {
if (event.lengthComputable) {
const totalBytes = event.total;
const receivedBytes = event.loaded;
const totalMB = (totalBytes / 1048576).toFixed(1);
const receivedMB = (receivedBytes / 1048576).toFixed(1);
const percent = Math.round((receivedBytes / totalBytes) * 100);
log(`Downloading: ${percent}% (${receivedMB}MB / ${totalMB}MB)`);
} else {
const receivedMB = (event.loaded / 1048576).toFixed(1);
log(`Downloading: ${receivedMB} MB (Total Size Unknown)`);
}
};
xhr.onload = function() {
if (xhr.status === 200) {
log("Finalizing buffer...");
const videoBlob = xhr.response;
const videoUrl = URL.createObjectURL(videoBlob);
player.src = videoUrl;
player.loop = true;
player.play().then(() => {
log("Playback successful");
player.style.opacity = "1";
if (overlay) {
overlay.style.opacity = "0";
setTimeout(() => overlay.style.display = "none", 800);
}
}).catch((e) => {
log("Autoplay blocked/failed: " + e.message);
});
} else if (xhr.status === 404) {
log(`No video was found at: ${sourceUrl}`);
} else {
log(`CRITICAL: Server returned ${xhr.status}`);
}
};
xhr.onerror = function() {
log("CRITICAL: Network Error");
};
xhr.send();
}
initialize();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment