Switch to Rok viprime embed widget on main page (test how DVR + native controls behave)
This commit is contained in:
parent
63679fe786
commit
4caa948d98
188
views/index.ejs
188
views/index.ejs
@ -506,21 +506,7 @@
|
||||
</div>
|
||||
|
||||
<div class="player-frame" id="playerFrame">
|
||||
<video id="v" autoplay muted playsinline tabindex="-1"
|
||||
disablepictureinpicture
|
||||
controlsList="nodownload nofullscreen noremoteplayback noplaybackrate"></video>
|
||||
|
||||
<div class="player-msg visible" id="msg">
|
||||
<div class="spinner"></div>
|
||||
<div>Stream wird geladen…</div>
|
||||
</div>
|
||||
|
||||
<div class="player-overlay">
|
||||
<div class="player-controls">
|
||||
<button class="pctl" id="btnAudio" aria-label="Ton umschalten" title="Ton umschalten">🔇</button>
|
||||
<button class="pctl" id="btnFs" aria-label="Vollbild" title="Vollbild">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://livestream1.viprime.net/embed/stream1_dvr.js"></script>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -631,177 +617,5 @@
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const HLS_URL = <%- JSON.stringify(hlsUrl) %>;
|
||||
const video = document.getElementById('v');
|
||||
const msg = document.getElementById('msg');
|
||||
const playerFrame = document.getElementById('playerFrame');
|
||||
|
||||
let hls = null;
|
||||
function showMsg(html) { msg.innerHTML = html; msg.classList.add('visible'); }
|
||||
function hideMsg() { msg.classList.remove('visible'); }
|
||||
|
||||
function attach() {
|
||||
try { hls && hls.destroy(); } catch (e) {}
|
||||
hls = null;
|
||||
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = HLS_URL;
|
||||
video.play().catch(() => {});
|
||||
video.addEventListener('playing', hideMsg, { once: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.Hls && window.Hls.isSupported()) {
|
||||
hls = new Hls({
|
||||
liveDurationInfinity: true,
|
||||
liveSyncDurationCount: 4,
|
||||
backBufferLength: 30,
|
||||
maxBufferLength: 60,
|
||||
manifestLoadingTimeOut: 10000,
|
||||
manifestLoadingMaxRetry: 6,
|
||||
fragLoadingTimeOut: 20000,
|
||||
fragLoadingMaxRetry: 6,
|
||||
startLevel: -1,
|
||||
capLevelToPlayerSize: false,
|
||||
abrEwmaDefaultEstimate: 3000000,
|
||||
testBandwidth: true,
|
||||
abrBandWidthFactor: 0.95,
|
||||
abrBandWidthUpFactor: 0.7,
|
||||
});
|
||||
hls.loadSource(HLS_URL);
|
||||
hls.attachMedia(video);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
// ABR enabled across ALL variants (1080p / 720p / 480p)
|
||||
// Rok synced all 3 variants — same MEDIA-SEQUENCE base, encoder restarted.
|
||||
hideMsg();
|
||||
video.play().catch(() => setTimeout(() => video.play().catch(() => {}), 200));
|
||||
});
|
||||
hls.on(Hls.Events.ERROR, (_e, data) => {
|
||||
if (!data.fatal) return;
|
||||
showMsg('<div class="spinner"></div><div>Verbindung wird wiederhergestellt…</div>');
|
||||
setTimeout(() => { attach(); }, 3000);
|
||||
});
|
||||
} else {
|
||||
showMsg('<div>HLS wird von diesem Browser nicht unterstützt.</div>');
|
||||
}
|
||||
}
|
||||
attach();
|
||||
|
||||
// Stall detection
|
||||
let lastT = 0, stalls = 0;
|
||||
setInterval(() => {
|
||||
if (video.paused) { video.play().catch(() => {}); return; }
|
||||
if (video.currentTime === lastT && !video.seeking) {
|
||||
stalls++;
|
||||
if (stalls >= 3) {
|
||||
stalls = 0;
|
||||
showMsg('<div class="spinner"></div><div>Verbindung wird wiederhergestellt…</div>');
|
||||
setTimeout(() => { attach(); }, 500);
|
||||
}
|
||||
} else stalls = 0;
|
||||
lastT = video.currentTime;
|
||||
}, 5000);
|
||||
|
||||
// Live-only mode (Twitch style) — pause is disabled.
|
||||
// If anything pauses the video, resume immediately AND seek to the live edge.
|
||||
function seekToLive() {
|
||||
try {
|
||||
if (hls && hls.liveSyncPosition) {
|
||||
video.currentTime = hls.liveSyncPosition;
|
||||
} else if (video.seekable && video.seekable.length > 0) {
|
||||
video.currentTime = video.seekable.end(video.seekable.length - 1);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
video.addEventListener('pause', () => {
|
||||
setTimeout(() => {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}, 80);
|
||||
});
|
||||
// If user seeks back, force forward to live
|
||||
video.addEventListener('seeked', () => {
|
||||
if (!hls) return;
|
||||
if (hls.liveSyncPosition && video.currentTime < hls.liveSyncPosition - 10) {
|
||||
video.currentTime = hls.liveSyncPosition;
|
||||
}
|
||||
});
|
||||
// Block right-click context menu
|
||||
video.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||
video.disablePictureInPicture = true;
|
||||
video.setAttribute('disablePictureInPicture', 'true');
|
||||
try { video.disableRemotePlayback = true; } catch (e) {}
|
||||
|
||||
// Block keyboard shortcuts that could pause: spacebar, K, Media keys
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const tag = (e.target && e.target.tagName) || '';
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA') return;
|
||||
if (e.key === ' ' || e.key === 'Spacebar' || e.code === 'Space' ||
|
||||
e.key === 'k' || e.key === 'K' ||
|
||||
e.key === 'MediaPlayPause' || e.key === 'MediaPause') {
|
||||
e.preventDefault();
|
||||
if (video.paused) {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
// Audio toggle (custom button)
|
||||
const btnAudio = document.getElementById('btnAudio');
|
||||
const btnFs = document.getElementById('btnFs');
|
||||
function updateAudio() {
|
||||
btnAudio.textContent = video.muted ? '🔇' : '🔊';
|
||||
btnAudio.title = video.muted ? 'Ton einschalten' : 'Stummschalten';
|
||||
}
|
||||
updateAudio();
|
||||
btnAudio.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
video.muted = !video.muted;
|
||||
video.volume = 1.0;
|
||||
updateAudio();
|
||||
});
|
||||
|
||||
// Fullscreen — handle iOS Safari separately
|
||||
function enterFullscreen() {
|
||||
if (typeof video.webkitEnterFullscreen === 'function' && !document.fullscreenEnabled) {
|
||||
try { video.webkitEnterFullscreen(); return; } catch (e) {}
|
||||
}
|
||||
if (video.requestFullscreen) { video.requestFullscreen().catch(() => {}); return; }
|
||||
if (video.webkitRequestFullscreen) { video.webkitRequestFullscreen(); return; }
|
||||
if (playerFrame.requestFullscreen) { playerFrame.requestFullscreen().catch(() => {}); return; }
|
||||
if (playerFrame.webkitRequestFullscreen) { playerFrame.webkitRequestFullscreen(); return; }
|
||||
if (typeof video.webkitEnterFullscreen === 'function') {
|
||||
try { video.webkitEnterFullscreen(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
function exitFullscreen() {
|
||||
if (document.exitFullscreen) { document.exitFullscreen().catch(() => {}); return; }
|
||||
if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); return; }
|
||||
if (typeof video.webkitExitFullscreen === 'function') { try { video.webkitExitFullscreen(); } catch (e) {} }
|
||||
}
|
||||
function isFullscreen() {
|
||||
return !!(document.fullscreenElement || document.webkitFullscreenElement || video.webkitDisplayingFullscreen);
|
||||
}
|
||||
btnFs.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (isFullscreen()) exitFullscreen();
|
||||
else enterFullscreen();
|
||||
});
|
||||
// Tap on video = audio toggle, double = fullscreen
|
||||
video.addEventListener('click', () => { video.muted = !video.muted; video.volume = 1.0; updateAudio(); });
|
||||
video.addEventListener('dblclick', (e) => { e.preventDefault(); btnFs.click(); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user