284 lines
7.1 KiB
HTML
Executable File
284 lines
7.1 KiB
HTML
Executable File
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>CANCOM Simple Signage Player</title>
|
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
|
|
|
<style>
|
|
html, body {
|
|
font-family: system-ui, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: black;
|
|
overflow: hidden;
|
|
}
|
|
|
|
img, video, iframe {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background: black;
|
|
display: none;
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 1;
|
|
}
|
|
|
|
img, iframe {
|
|
object-fit: contain;
|
|
border: 0;
|
|
}
|
|
|
|
video {
|
|
object-fit: contain;
|
|
}
|
|
|
|
.newsticker-text {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
position: relative;
|
|
height: 100%;
|
|
}
|
|
|
|
.newsticker-track {
|
|
display: inline-block;
|
|
white-space: nowrap;
|
|
|
|
position: absolute;
|
|
left: 100%;
|
|
top: 0;
|
|
|
|
height: 40px;
|
|
line-height: 40px;
|
|
animation: ticker-scroll 40s linear infinite;
|
|
}
|
|
|
|
.newsticker-track span {
|
|
padding-right: 4rem;
|
|
}
|
|
|
|
@keyframes ticker-scroll {
|
|
from { transform: translateX(0); }
|
|
to { transform: translateX(-100vw); }
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
|
|
<img id="image">
|
|
<video id="video" muted autoplay playsinline></video>
|
|
<iframe id="iframe"></iframe>
|
|
|
|
{% if newsticker_enabled %}
|
|
<div id="newsticker-bar" style="position:fixed;left:0;right:0;bottom:0;height:40px;z-index:10000;background:#DA002D;color:#fff;display:flex;align-items:center;overflow:hidden;">
|
|
<div id="newsticker-text" class="newsticker-text">
|
|
<div class="newsticker-track">
|
|
<span>{{ newsticker_text }}</span>
|
|
</div>
|
|
</div>
|
|
<div id="newsticker-clock" style="padding:0 18px 0 24px;font-size:1.1em;font-family:monospace;min-width:90px;text-align:right;"></div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
// Newsticker Uhrzeit
|
|
function updateClock() {
|
|
const el = document.getElementById('newsticker-clock');
|
|
if (!el) return;
|
|
const now = new Date();
|
|
el.textContent = now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
}
|
|
setInterval(updateClock, 1000);
|
|
updateClock();
|
|
/* -----------------------------
|
|
Daten vom Server
|
|
----------------------------- */
|
|
const normalFiles = {{ normal_files | tojson }};
|
|
const prioFiles = {{ prio_files | tojson }};
|
|
const interval = {{ interval }} * 1000;
|
|
const screen = "{{ screen }}";
|
|
|
|
/* -----------------------------
|
|
Player State
|
|
----------------------------- */
|
|
let normalIndex = 0;
|
|
let prioIndex = 0;
|
|
let mode = "normal"; // "normal" oder "prio"
|
|
|
|
const img = document.getElementById("image");
|
|
const vid = document.getElementById("video");
|
|
const iframe = document.getElementById("iframe");
|
|
|
|
/* -----------------------------
|
|
Hilfsfunktionen
|
|
----------------------------- */
|
|
function normalizeItem(item) {
|
|
if (typeof item === "string") {
|
|
const lower = item.toLowerCase();
|
|
if (lower.startsWith("http://") || lower.startsWith("https://")) {
|
|
return { kind: "url", url: item, zoom: 1.0 };
|
|
}
|
|
return { kind: "file", name: item };
|
|
}
|
|
// Item is already an object (from backend) - ensure zoom is set
|
|
if (item.kind === "url" && !("zoom" in item)) {
|
|
item.zoom = 1.0;
|
|
}
|
|
return item;
|
|
}
|
|
|
|
function isVideo(item) {
|
|
return item.kind === "file" && item.name.toLowerCase().endsWith(".mp4");
|
|
}
|
|
|
|
function isImage(item) {
|
|
return item.kind === "file" && /\.(jpg|jpeg|png)$/i.test(item.name);
|
|
}
|
|
|
|
function isHtml(item) {
|
|
return item.kind === "file" && /\.(html?|htm)$/i.test(item.name);
|
|
}
|
|
|
|
function getNextItem() {
|
|
const normalizedNormal = normalFiles.map(normalizeItem);
|
|
const normalizedPrio = prioFiles.map(normalizeItem);
|
|
|
|
// ✅ Sonderfall: nur Priority vorhanden
|
|
if (normalizedNormal.length === 0 && normalizedPrio.length > 0) {
|
|
return {
|
|
item: normalizedPrio[prioIndex++ % normalizedPrio.length],
|
|
isPrio: true
|
|
};
|
|
}
|
|
|
|
// ✅ Sonderfall: nur normale Playlist vorhanden
|
|
if (normalizedPrio.length === 0 && normalizedNormal.length > 0) {
|
|
return {
|
|
item: normalizedNormal[normalIndex++ % normalizedNormal.length],
|
|
isPrio: false
|
|
};
|
|
}
|
|
|
|
// ✅ Normal-Phase
|
|
if (mode === "normal") {
|
|
const item = normalizedNormal[normalIndex++];
|
|
if (normalIndex >= normalizedNormal.length) {
|
|
normalIndex = 0;
|
|
if (normalizedPrio.length > 0) {
|
|
mode = "prio"; // ➜ nach kompletter Normal-Playlist wechseln
|
|
}
|
|
}
|
|
return { item, isPrio: false };
|
|
}
|
|
|
|
// ✅ Priority-Phase
|
|
if (mode === "prio") {
|
|
const item = normalizedPrio[prioIndex++];
|
|
if (prioIndex >= normalizedPrio.length) {
|
|
prioIndex = 0;
|
|
mode = "normal"; // ➜ nach kompletter Priority zurück
|
|
}
|
|
return { item, isPrio: true };
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/* -----------------------------
|
|
Medien abspielen
|
|
----------------------------- */
|
|
function playNext() {
|
|
const entry = getNextItem();
|
|
if (!entry) return;
|
|
|
|
const item = entry.item;
|
|
const basePath = entry.isPrio ? "priority" : screen;
|
|
|
|
img.style.display = "none";
|
|
vid.style.display = "none";
|
|
iframe.style.display = "none";
|
|
vid.pause();
|
|
vid.src = "";
|
|
iframe.src = "";
|
|
iframe.style.transform = "scale(1)"; // Reset zoom
|
|
|
|
if (item.kind === "url") {
|
|
iframe.style.display = "block";
|
|
iframe.src = item.url;
|
|
// Apply zoom factor with browser-like behavior
|
|
// Zoom < 1.0: mehr Inhalt sichtbar (wie Browser herauszoomen)
|
|
// Zoom > 1.0: weniger Inhalt sichtbar (wie Browser reinzoomen)
|
|
const zoom = item.zoom || 1.0;
|
|
|
|
if (zoom !== 1.0) {
|
|
// Adjust iframe size and scale to simulate real browser zoom
|
|
const scale = 1 / zoom; // Inverted for proper zoom behavior
|
|
iframe.style.width = `${100 * scale}vw`;
|
|
iframe.style.height = `${100 * scale}vh`;
|
|
iframe.style.transform = `translate(-50%, -50%) scale(${zoom})`;
|
|
} else {
|
|
iframe.style.width = `100vw`;
|
|
iframe.style.height = `100vh`;
|
|
iframe.style.transform = `translate(-50%, -50%)`;
|
|
}
|
|
setTimeout(playNext, interval);
|
|
return;
|
|
}
|
|
|
|
const src = `/media/${basePath}/${item.name}`;
|
|
if (isVideo(item)) {
|
|
vid.style.display = "block";
|
|
vid.src = src;
|
|
vid.onended = playNext;
|
|
vid.play();
|
|
} else if (isHtml(item)) {
|
|
iframe.style.display = "block";
|
|
iframe.src = src;
|
|
iframe.style.width = `100vw`;
|
|
iframe.style.height = `100vh`;
|
|
iframe.style.transform = `translate(-50%, -50%)`;
|
|
setTimeout(playNext, interval);
|
|
} else {
|
|
img.style.display = "block";
|
|
img.src = src;
|
|
setTimeout(playNext, interval);
|
|
}
|
|
}
|
|
|
|
/* -----------------------------
|
|
Auto-Reload bei Änderungen
|
|
----------------------------- */
|
|
let lastHash = null;
|
|
|
|
async function checkForUpdates() {
|
|
try {
|
|
const res = await fetch(`/playlist/${screen}/hash`, { cache: "no-store" });
|
|
const hash = await res.text();
|
|
|
|
if (lastHash && lastHash !== hash) {
|
|
location.reload();
|
|
}
|
|
lastHash = hash;
|
|
} catch (e) {
|
|
console.warn("Playlist-Check fehlgeschlagen", e);
|
|
}
|
|
}
|
|
|
|
/* -----------------------------
|
|
Start
|
|
----------------------------- */
|
|
playNext();
|
|
setInterval(checkForUpdates, 5000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|
|
|