erster Upload

This commit is contained in:
Erik Thiele
2026-05-27 21:59:21 +02:00
parent 414448aff5
commit 08b57d447f
10 changed files with 7956 additions and 0 deletions

181
templates/display.html Normal file
View File

@@ -0,0 +1,181 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
<title>Videoplayer Display</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%; height: 100%;
background: #000;
overflow: hidden;
font-family: system-ui, sans-serif;
}
#stage {
width: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
#stage video,
#stage img {
width: 100%; height: 100%;
object-fit: contain;
display: none;
}
#stage audio {
display: none;
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 80%;
max-width: 400px;
}
#empty {
color: #444;
font-size: 1.1rem;
}
</style>
</head>
<body>
<div id="stage">
<video id="video" muted playsinline></video>
<audio id="audio" muted controls></audio>
<img id="image" alt="">
<div id="empty">Warte auf Steuerung…</div>
</div>
<script>
const video = document.getElementById('video');
const audio = document.getElementById('audio');
const image = document.getElementById('image');
const empty = document.getElementById('empty');
let lastVersion = -1;
let lastCurrentName = null;
let audioUnlocked = false;
// AudioContext-Trick: entsperrt Audio in Chromium-basierten Browsern
(function unlockAudio() {
try {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
ctx.resume();
const buf = ctx.createBuffer(1, 1, 22050);
const src = ctx.createBufferSource();
src.buffer = buf;
src.connect(ctx.destination);
src.start();
// Sobald der Buffer abgespielt wurde, gilt Audio als entsperrt
src.onended = () => { audioUnlocked = true; };
audioUnlocked = true;
} catch (_) {}
})();
// Klick auf die Seite entsperrt Audio endgültig
document.addEventListener('click', () => {
audioUnlocked = true;
video.muted = false;
audio.muted = false;
if (video.src && video.paused) video.play().catch(() => {});
if (audio.src && audio.paused) audio.play().catch(() => {});
}, { once: true });
function showOnly(el) {
[video, audio, image, empty].forEach(e => { e.style.display = 'none'; });
if (el) el.style.display = el === empty ? 'flex' : 'block';
}
async function tryPlay(player) {
player.muted = false;
try {
await player.play();
return true;
} catch (_) {
player.muted = true;
try {
await player.play();
return false;
} catch (_) {
return false;
}
}
}
async function poll() {
try {
const res = await fetch('/api/state');
const state = await res.json();
const versionChanged = state.version !== lastVersion;
if (versionChanged) {
const prevVersion = lastVersion;
lastVersion = state.version;
const currentName = state.current ? state.current.name : null;
const currentChanged = currentName !== lastCurrentName;
lastCurrentName = currentName;
// Seek
if (state.seek && prevVersion >= 0 && state.current &&
(state.current.kind === 'video' || state.current.kind === 'audio')) {
const player = state.current.kind === 'video' ? video : audio;
if (player.duration && isFinite(player.duration)) {
player.currentTime = Math.max(0, Math.min(player.duration, player.currentTime + state.seek));
}
fetch('/api/seek-ack', { method: 'POST' }).catch(() => {});
}
// Neues Medium laden
if (currentChanged) {
if (state.current) {
const src = `/media/${encodeURIComponent(state.current.name)}`;
const kind = state.current.kind;
if (kind === 'video') {
video.src = src;
video.oncanplay = () => {
if (lastCurrentName === state.current?.name && state.playing) {
tryPlay(video);
}
};
showOnly(video);
} else if (kind === 'audio') {
audio.src = src;
audio.oncanplay = () => {
if (lastCurrentName === state.current?.name && state.playing) {
tryPlay(audio);
}
};
showOnly(audio);
} else {
image.src = src;
showOnly(image);
}
} else {
showOnly(empty);
}
}
}
// Play/pause: immer ausführen
if (state.current && (state.current.kind === 'video' || state.current.kind === 'audio')) {
const player = state.current.kind === 'video' ? video : audio;
player.volume = state.volume ?? 1.0;
if (state.playing && player.src) {
tryPlay(player);
} else if (!state.playing) {
player.pause();
}
}
} catch (_) {}
}
window.addEventListener('load', () => {
poll();
setInterval(poll, 1500);
});
</script>
</body>
</html>