obscreen/views/player/player.jinja.html
2024-05-25 14:48:16 +02:00

411 lines
14 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="fr">
<head>
<title>Obscreen</title>
<meta name="robots" content="noindex, nofollow">
<meta name="google" content="notranslate">
<link rel="shortcut icon" href="{{ STATIC_PREFIX }}/favicon.ico">
{% if slide_animation_enabled.eval() %}
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/animate.min.css" />
{% endif %}
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; background-color: white; display: flex; flex-direction: row; justify-content: center; align-items: center; }
.slide { display: flex; flex-direction: row; justify-content: center; align-items: center; background: black; }
.slide, iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; padding-top: 0; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; }
.slide iframe { background: white; }
.slide img { height: 100%; }
</style>
<script type="application/javascript" src="{{ STATIC_PREFIX }}js/lib/is-cron-now.js"></script>
</head>
<body>
<div id="IntroSlide" class="slide" style="z-index: 10000;">
{% if default_slide_duration.eval() > 0 %}
<iframe src="/player/default"></iframe>
{% endif %}
</div>
<div id="CronSlide" class="slide" style="z-index: 0;">
</div>
<div id="FirstSlide" class="slide slide-loop" style="z-index: 500;">
</div>
<div id="SecondSlide" class="slide slide-loop" style="z-index: 1000;">
</div>
<script type="text/javascript">
// Backend config
var items = {{items | safe}};
var introDuration = {{ default_slide_duration.eval() * 1000 }};
var playlistCheckResolutionMs = {{ polling_interval.eval() * 1000 }};
// Backend flag updates
var needHardRefresh = null;
// Frontend config
var syncedWithTime = false;
var tickRefreshResolutionMs = 100;
// Frontend flag updates
var hasMoveOnce = false;
var forcePreload = false;
// Player states infos
var PLAY_STATE_PLAYING = 0, PLAY_STATE_PAUSE = 1;
var playState = PLAY_STATE_PLAYING;
var isPlaying = function() {return playState === PLAY_STATE_PLAYING;};
var isPaused = function() {return playState === PLAY_STATE_PAUSE;};
var pauseClockValue = null;
// Animations
var animate = {{ 'true' if slide_animation_enabled.eval() else 'false' }};
var animate_speed = "animate__{{ slide_animation_speed.eval()|default("normal") }}";
var animation_speed_duration = {{ animation_speed_duration[slide_animation_speed.eval()] if slide_animation_enabled.eval() else 0 }};
var animate_transitions = [
"animate__{{ slide_animation_entrance_effect.eval()|default("fadeIn") }}",
"animate__{{ slide_animation_exit_effect.eval()|default("none") }}"
];
// Slide flow management
var SLIDE_TOP_Z = '1000';
var SLIDE_BOTTOM_Z = '500';
var clockValue = 0;
var curItemIndex = -1;
var secondsBeforeNext = 0;
var nextReady = true;
var introSlide = document.getElementById('IntroSlide');
var cronSlide = document.getElementById('CronSlide');
var firstSlide = document.getElementById('FirstSlide');
var secondSlide = document.getElementById('SecondSlide');
var curSlide = secondSlide;
var nextSlide = firstSlide;
var cronItemIndex = null;
// Functions
var itemCheck = setInterval(function () {
fetch('player/playlist').then(function(response) {
if (response.ok) {
return response.json();
}
}).then(function(data) {
items = data;
if (needHardRefresh === null) {
needHardRefresh = items.hard_refresh_request;
} else if (needHardRefresh != items.hard_refresh_request) {
document.location.reload();
}
}).catch(function(err) {
console.error(err);
});
}, playlistCheckResolutionMs);
var getLoopDuration = function() {
let totalDuration = 0;
for (var i = 0; i < items.loop.length; i++) {
var item = items.loop[i];
totalDuration += safe_duration(item);
}
return totalDuration;
};
var resume = function() {
playState = PLAY_STATE_PLAYING;
};
var play = function() {
resume();
};
var pause = function() {
pauseClockValue = clockValue;
playState = PLAY_STATE_PAUSE;
};
var stop = function() {
pause();
};
var seek = function(timeInSeconds) {
if (forcePreload) {
return;
}
if (syncedWithTime) {
return console.warn('You can\'t seek with synced playlists');
}
var maxDuration = getLoopDuration();
if (timeInSeconds > maxDuration) {
timeInSeconds = maxDuration - 1;
console.warn('Max duration is ' + timeInSeconds + ' seconds');
}
if (timeInSeconds < 0) {
timeInSeconds = 0;
}
clockValue = timeInSeconds * 1000;
forcePreload = true;
pause();
};
var lookupPreviousItem = function() {
return (curItemIndex - 1 < 0) ? items.loop[items.loop.length - 1] : items.loop[curItemIndex - 1];
};
var lookupNextItem = function() {
return (curItemIndex + 1 >= items.loop.length) ? items.loop[0] : items.loop[curItemIndex + 1];
};
var lookupCurrentItem = function() {
return items.loop[curItemIndex];
}
var getEmptySlide = function() {
return Array.from(document.getElementsByClassName('slide-loop')).filter(slide => slide.innerHTML.replaceAll(/\s/g,'') === '')[0];
};
var refreshSlidesOrder = function() {
curSlide = Array.from(document.getElementsByClassName('slide')).filter(function(slide) {
return getComputedStyle(slide).zIndex === SLIDE_TOP_Z;
})[0];
nextSlide = Array.from(document.getElementsByClassName('slide')).filter(function(slide) {
return getComputedStyle(slide).zIndex === SLIDE_BOTTOM_Z;
})[0];
//console.log("top is", SLIDE_TOP_Z, curSlide, "bottom is", SLIDE_BOTTOM_Z, nextSlide)
};
var safe_duration = function(item) {
if (!item) {
return tickRefreshResolutionMs/1000;
}
return item.duration + Math.ceil(animation_speed_duration/1000);
};
var main = function() {
setTimeout(function() {
introSlide.remove();
setInterval(checkAndMoveSlide, tickRefreshResolutionMs);
setInterval(checkAndMoveCron, 1000);
}, introDuration);
};
var preloadSlide = function(slide, item) {
//console.log('Preload', slide, item.name)
var element = document.getElementById(slide);
var callbackReady = function() {};
loadContent(element, callbackReady, item);
};
var tickClockValue = function() {
if (isPaused()) {
return pauseClockValue;
}
if (syncedWithTime) {
clockValue = Date.now();
} else {
clockValue += tickRefreshResolutionMs;
}
};
function checkAndMoveSlide() {
tickClockValue();
var timeInCurrentLoop = (clockValue/1000) % getLoopDuration();
var accumulatedTime = 0;
for (var i = 0; i < items.loop.length; i++) {
var item = items.loop[i];
if (i === curItemIndex) {
secondsBeforeNext = accumulatedTime + safe_duration(item) - timeInCurrentLoop;
//console.log("remaining:", secondsBeforeNext, "clock:",clockValue, curItemIndex);
}
if (timeInCurrentLoop < accumulatedTime + safe_duration(item)) {
if (curItemIndex !== i) {
//console.log('change to ', i , item.name)
curItemIndex = i;
var emptySlide = getEmptySlide();
if ((emptySlide && !hasMoveOnce) || forcePreload) {
//console.log('init preload');
var slide = emptySlide ? emptySlide : nextSlide;
preloadSlide(slide.attributes['id'].value, item);
if (!hasMoveOnce && syncedWithTime) {
if (accumulatedTime + safe_duration(item) - timeInCurrentLoop < 1) {
// Prevent glitch when syncedWithTime for first init
continue;
}
}
hasMoveOnce = true;
}
moveToNextSlide();
}
break;
}
accumulatedTime += safe_duration(item);
}
}
function moveToNextSlide() {
refreshSlidesOrder();
nextSlide.style.zIndex = SLIDE_TOP_Z; // first
curSlide.style.zIndex = SLIDE_BOTTOM_Z; // second
//console.log("curSlide", curSlide.attributes['id'].value, curSlide.style.zIndex, "to", "next", nextSlide.attributes['id'].value, nextSlide.style.zIndex);
//console.log("###");
var loadingNextSlide = function () {
if (forcePreload) {
forcePreload = false;
play();
}
if (isPaused() && !syncedWithTime) {
return setTimeout(loadingNextSlide, 500);
}
refreshSlidesOrder();
preloadSlide(nextSlide.attributes['id'].value, lookupNextItem());
};
if (animate) {
nextSlide.classList.add('animate__animated', animate_transitions[0], animate_speed);
nextSlide.onanimationend = function() {
nextSlide.classList.remove(animate_transitions[0], animate_speed);
loadingNextSlide();
};
curSlide.classList.add('animate__animated', animate_transitions[1], animate_speed);
curSlide.onanimationend = function() {
curSlide.classList.remove(animate_transitions[1], animate_speed);
};
} else {
loadingNextSlide();
}
}
function loadContent(element, callbackReady, item) {
switch (item.type) {
case 'url':
loadUrl(element, callbackReady, item);
break;
case 'picture':
loadPicture(element, callbackReady, item);
break;
case 'video':
loadVideo(element, callbackReady, item);
break;
case 'youtube':
loadYoutube(element, callbackReady, item);
break;
default:
loadUrl(element, callbackReady, item);
break;
}
}
function loadUrl(element, callbackReady, item, delay) {
element.innerHTML = `<iframe src="${item.location}"></iframe>`;
callbackReady(function () {});
}
function loadPicture(element, callbackReady, item) {
element.innerHTML = `<img src="${item.location}" alt="" />`;
callbackReady(function () {});
}
function loadYoutube(element, callbackReady, item) {
element.innerHTML = `youtube`;
callbackReady(function () {});
var loadingDelayMs = 1000;
var delayNoisyContentJIT = Math.max(100, (lookupCurrentItem().duration * 1000) - loadingDelayMs);
delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;
var autoplayLoader = function() {
if (secondsBeforeNext * 1000 > loadingDelayMs) {
return setTimeout(autoplayLoader, 500);
}
if (element.innerHTML === 'youtube') {
element.innerHTML = `<iframe src="https://www.youtube.com/embed/${item.location}?version=3&autoplay=1&showinfo=0&controls=0&modestbranding=1&fs=1&rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>`;
}
}
setTimeout(autoplayLoader, delayNoisyContentJIT);
}
function loadVideo(element, callbackReady, item) {
element.innerHTML = `<video><source src=${item.location} type="video/mp4" /></video>`;
var video = element.querySelector('video');
callbackReady(function () {});
var loadingDelayMs = 0;
var delayNoisyContentJIT = Math.max(100, (lookupCurrentItem().duration * 1000) - loadingDelayMs);
delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;
video.addEventListener('loadedmetadata', function () {
if (item.duration !== video.duration) {
console.warn('Given duration ' + item.duration + 's is different from video file ' + Math.ceil(video.duration) + 's');
}
});
var autoplayLoader = function() {
if (secondsBeforeNext * 1000 > loadingDelayMs) {
return setTimeout(autoplayLoader, 500);
}
if (element.innerHTML.match('<video>')) {
video.play();
}
}
setTimeout(autoplayLoader, delayNoisyContentJIT);
}
var checkAndMoveCron = function() {
if ((new Date()).getSeconds() != 0) {
return;
}
for (var i = 0; i < items.cron.length; i++) {
var item = items.cron[i];
if (cron.isActive(item.cron_schedule) && cronItemIndex !== i) {
moveToCronSlide(i);
}
if (cron.isActive(item.cron_schedule_end) && cronItemIndex === i) {
stopCronSlide();
}
}
};
function moveToCronSlide(cronSlideIndex) {
var item = items.cron[cronSlideIndex];
cronItemIndex = cronSlideIndex;
pause();
var callbackReady = function() {
cronSlide.style.zIndex = '2000';
setTimeout(function() {
stopCronSlide();
}, safe_duration(item) * 1000);
};
loadContent(cronSlide, callbackReady, item);
}
function stopCronSlide() {
cronItemIndex = null;
cronSlide.style.zIndex = '0';
play();
}
main();
</script>
</body>
</html>