Created
February 19, 2026 20:29
-
-
Save kohlerdominik/bcbb648d41d7c9b9b02a1469b1c1f68e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
| 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