playlists

This commit is contained in:
jr-k 2024-05-25 09:31:40 +02:00
parent 8df2aff0ee
commit 68d2f59846
3 changed files with 290 additions and 91 deletions

View File

@ -6,6 +6,7 @@ from flask import Flask, render_template, redirect, request, url_for, send_from_
from src.service.ModelStore import ModelStore
from src.interface.ObController import ObController
from src.utils import get_ip_address, get_safe_cron_descriptor
from src.model.enum.AnimationSpeed import animation_speed_duration
class PlayerController(ObController):
@ -54,7 +55,8 @@ class PlayerController(ObController):
slide_animation_enabled=self._model_store.variable().get_one_by_name('slide_animation_enabled'),
slide_animation_entrance_effect=self._model_store.variable().get_one_by_name('slide_animation_entrance_effect'),
slide_animation_exit_effect=self._model_store.variable().get_one_by_name('slide_animation_exit_effect'),
slide_animation_speed=self._model_store.variable().get_one_by_name('slide_animation_speed')
slide_animation_speed=self._model_store.variable().get_one_by_name('slide_animation_speed'),
animation_speed_duration=animation_speed_duration
)
def player_default(self):

View File

@ -1,5 +1,13 @@
from enum import Enum
animation_speed_duration = {
'slower': 3000,
'slow': 2000,
'normal': 1000,
'fast': 800,
'faster': 500,
}
class AnimationSpeed(Enum):

View File

@ -18,23 +18,69 @@
<script type="application/javascript" src="{{ STATIC_PREFIX }}js/lib/is-cron-now.js"></script>
</head>
<body>
<div id="FirstSlide" class="slide" style="z-index: 1000;">
<div id="IntroSlide" class="slide" style="z-index: 10000;">
{% if default_slide_duration.eval() > 0 %}
<iframe src="/player/default"></iframe>
{% endif %}
</div>
<div id="SecondSlide" class="slide" style="z-index: 500;">
{% if default_slide_duration.eval() > 0 %}
<iframe src="/player/default"></iframe>
{% endif %}
<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 duration = {{ default_slide_duration.eval() * 1000 }};
var playlistCheck = {{ polling_interval.eval() * 1000 }};
var curItemIndex = 0;
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 = 250;
// Frontend flag updates
var hasMoveOnce = 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) {
@ -51,101 +97,181 @@
}).catch(function(err) {
console.error(err);
});
}, playlistCheck);
var animate = {{ 'true' if slide_animation_enabled.eval() else 'false' }};
var animate_transitions = [
"animate__{{ slide_animation_entrance_effect.eval()|default("fadeIn") }}",
"animate__{{ slide_animation_exit_effect.eval()|default("none") }}"
];
var animate_speed = "animate__{{ slide_animation_speed.eval()|default("normal") }}";
var firstSlide = document.getElementById('FirstSlide');
var secondSlide = document.getElementById('SecondSlide');
var previousSlide = secondSlide;
var curSlide = firstSlide;
var cronState = {
active: false,
itemIndex: null,
interval: null
}, 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 cronTick = 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) && cronState.itemIndex != i) {
cronState.active = true;
cronState.itemIndex = i;
var callbackReady = function (onSlideStart) {
onSlideStart();
moveToSlide(curSlide.attributes['id'].value, item);
var move = function () {
if (nextReady) {
curItemIndex = (curItemIndex + 1) === items.loop.length ? 0 : curItemIndex + 1;
cronState.active = false;
cronState.itemIndex = null;
} else {
setTimeout(move, 1000);
}
}
setTimeout(move, item.duration * 1000);
};
loadContent(curSlide, callbackReady, item);
}
}
var resume = function() {
playState = PLAY_STATE_PLAYING;
};
function main() {
preloadSlide('SecondSlide', items.loop[curItemIndex])
cronState.interval = setInterval(cronTick, 1000);
var play = function() {
resume();
};
var pause = function() {
pauseClockValue = clockValue;
playState = PLAY_STATE_PAUSE;
};
var stop = function() {
pause();
};
var seek = function(timeInSeconds) {
if (syncedWithTime) {
return console.warn('You can\'t call seek with synced playlists');
}
var maxDuration = getLoopDuration();
if (timeInSeconds > maxDuration) {
timeInSeconds = maxDuration;
console.warn('Max duration is ' + maxDuration + ' seconds');
}
if (timeInSeconds < 0) {
timeInSeconds = 0;
}
clockValue = timeInSeconds * 1000;
};
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];
}
function preloadSlide(slide, item) {
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];
};
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(cronTick, 1000);
}, introDuration);
};
var preloadSlide = function(slide, item) {
console.log('Preload', slide, item.name)
var element = document.getElementById(slide);
var callbackReady = function (onSlideStart) {
var move = function () {
if (nextReady && !cronState.active) {
moveToSlide(slide, item);
onSlideStart();
} else {
setTimeout(move, 1000);
}
var callbackReady = function(onSlideStart) {
onSlideStart();
};
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);
}
setTimeout(move, duration);
};
loadContent(element, callbackReady, item);
if (timeInCurrentLoop < accumulatedTime + safe_duration(item)) {
if (curItemIndex !== i) {
console.log('change to ', i , item.name)
curItemIndex = i;
var emptySlide = getEmptySlide();
if (emptySlide && !hasMoveOnce) {
preloadSlide(emptySlide.attributes['id'].value, item);
hasMoveOnce = true;
}
moveToNextSlide();
}
break;
}
accumulatedTime += safe_duration(item);
}
}
function moveToSlide(slide, item) {
curSlide = document.getElementById(slide);
previousSlide = curSlide == firstSlide ? secondSlide : firstSlide;
function moveToNextSlide() {
refreshSlidesOrder();
nextSlide.style.zIndex = SLIDE_TOP_Z; // first
curSlide.style.zIndex = SLIDE_BOTTOM_Z; // second
duration = item.duration * 1000;
curItemIndex = (curItemIndex + 1) === items.loop.length ? 0 : curItemIndex + 1;
curSlide.style.zIndex = 1000;
previousSlide.style.zIndex = 500;
console.log(curSlide.attributes['id'].value, "to", nextSlide.attributes['id'].value)
if (animate) {
curSlide.classList.add('animate__animated', animate_transitions[0], animate_speed);
curSlide.onanimationend = () => {
curSlide.classList.remove(animate_transitions[0], animate_speed);
preloadSlide(previousSlide.attributes['id'].value, items.loop[curItemIndex]);
nextSlide.classList.add('animate__animated', animate_transitions[0], animate_speed);
var loadingNextSlide = function () {
if (isPaused()) {
return setTimeout(loadingNextSlide, 500);
}
if (secondsBeforeNext * 1000 > 1000) {
return setTimeout(loadingNextSlide, 500);
}
console.log('pursuing... loading')
nextSlide.classList.remove(animate_transitions[0], animate_speed);
preloadSlide(curSlide.attributes['id'].value, lookupNextItem());
refreshSlidesOrder();
};
previousSlide.classList.add('animate__animated', animate_transitions[1], animate_speed);
previousSlide.onanimationend = () => {
previousSlide.classList.remove(animate_transitions[1], animate_speed);
nextSlide.onanimationend = function() { loadingNextSlide(); };
curSlide.classList.add('animate__animated', animate_transitions[1], animate_speed);
var unloadingNextSlide = function () {
if (isPaused()) {
return setTimeout(unloadingNextSlide, 500);
}
if (secondsBeforeNext * 1000 > 1000) {
return setTimeout(unloadingNextSlide, 500);
}
console.log('pursuing... unloading')
curSlide.classList.remove(animate_transitions[1], animate_speed);
};
curSlide.onanimationend = function() { unloadingNextSlide(); };
} else {
preloadSlide(previousSlide.attributes['id'].value, items.loop[curItemIndex]);
preloadSlide(curSlide.attributes['id'].value, lookupNextItem());
refreshSlidesOrder();
}
}
@ -180,12 +306,27 @@
}
function loadYoutube(element, callbackReady, item) {
element.innerHTML = ``;
element.innerHTML = `youtube`;
callbackReady(function () {});
setTimeout(function() {
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>`;
}, Math.max(100, duration - 2000));
var loadingDelayMs = 1000;
var delayNoisyContentJIT = Math.max(100, (lookupCurrentItem().duration * 1000) - loadingDelayMs);
delayNoisyContentJIT = lookupCurrentItem().id !== item.id ? delayNoisyContentJIT : 0;
var autoplayLoader = function() {
if (isPaused()) {
return setTimeout(autoplayLoader, 500);
}
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) {
@ -208,6 +349,54 @@
callbackReady(onSlideStart);
}
var cronTick = 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) {
setTimeout(function() {
moveToCronSlide(i);
}, 100);
}
}
};
function moveToCronSlide(cronSlideIndex) {
var item = items.cron[cronSlideIndex];
var savedSlide = curSlide.innerHTML;
cronItemIndex = cronSlideIndex;
pause();
var callbackReady = function(onSlideStart) {
onSlideStart();
setTimeout(function() {
console.log('STOOOP')
cronItemIndex = null;
{#curSlide.innerHTML = savedSlide;#}
refreshSlidesOrder()
SLIDE_TOP_Z = (parseInt(SLIDE_TOP_Z) * 5).toString();
SLIDE_BOTTOM_Z = (parseInt(SLIDE_BOTTOM_Z) * 5).toString();
curSlide.style.zIndex = SLIDE_BOTTOM_Z;
cronSlide.style.zIndex = SLIDE_TOP_Z;
moveToNextSlide();
play();
}, safe_duration(item) * 1000);
};
refreshSlidesOrder()
SLIDE_TOP_Z = (parseInt(SLIDE_TOP_Z) * 5).toString();
SLIDE_BOTTOM_Z = (parseInt(SLIDE_BOTTOM_Z) * 5).toString();
cronSlide.style.zIndex = SLIDE_BOTTOM_Z;
curSlide.style.zIndex = SLIDE_TOP_Z;
moveToNextSlide();
loadContent(cronSlide, callbackReady, item);
}
main();
</script>
</body>