crud for slide in gui

This commit is contained in:
jr-k 2024-02-26 14:33:02 +01:00
parent ab7cc46de3
commit bf5a67a05f
14 changed files with 1103 additions and 867 deletions

View File

@ -1,7 +1,7 @@
# Obscreen
## About
Use a RaspberryPi to show a full-screen Slideshow (Kiosk-mode)
Use a RaspberryPi to show a full-screen slideshow (Kiosk-mode)
## Installation
```bash

View File

@ -1,7 +1,7 @@
config = {
"config": False,
"port": 5000,
"reverse_proxy_mode": False,
"lang": "en",
"lxfile": '/home/pi/.config/lxsession/LXDE-pi/autostart'
"config": False, # Enable autoreload for html/jinja files
"port": 5000, # Application port
"reverse_proxy_mode": False, # True if you want to use nginx on port 80
"lang": "en", # Language for manage view "fr" or "en"
"lxfile": '/home/pi/.config/lxsession/LXDE-pi/autostart' # Path to lx autostart file
}

View File

@ -1,14 +1,22 @@
{
"data": [
{
"id": 0,
"location": "data/uploads/sample.jpg",
"delay": 10,
"type": "picture"
"duration": 10,
"type": "picture",
"enabled": true,
"name": "Picture Sample",
"position": 0
},
{
"id": 1,
"location": "https://unix.org",
"delay": 20,
"type": "url"
"duration": 20,
"type": "url",
"enabled": true,
"name": "URL Sample",
"position": 1
}
]
}

535
data/www/css/manage.css Normal file
View File

@ -0,0 +1,535 @@
* {
font-family: Roboto;
}
html {
height: 100vh;
margin: 0;
padding: 0;
display: flex;
background-color: #0f0035;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
align-self: stretch;
}
body {
margin: 0;
padding: 0;
flex: 1;
background-color: #0f0035;
color: #fff;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
align-self: stretch;
}
.hidden {
display: none !important;
}
.tac {
text-align: center;
}
a {
text-decoration: none;
}
.container {
background: rgba(0, 0, 0, 0.1);
margin: auto;
display: flex;
align-self: stretch;
flex-direction: column;
justify-content: flex-start;
align-items: center;
flex: 1;
min-width: 70%;
@media only screen and (max-width: 1200px) {
min-width: 95%;
}
}
header {
text-align: center;
display: flex;
justify-content: flex-start;
align-items: center;
align-self: stretch;
padding: 0 25px 0 25px;
}
header .logo {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
header .logo img {
width: 32px;
height: 32px;
margin-right: 10px;
}
header menu {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
flex: 1;
}
header menu ul {
list-style: none;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
header menu ul li {
margin: 0 15px;
}
header menu ul li a {
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
}
header menu ul li a:hover,
header menu ul li.active a {
color: white;
}
.toolbar {
display: flex;
flex-direction: row;
padding: 0 25px 0 25px;
align-self: stretch;
}
.toolbar h2 {
padding: 0 25px 0 0;
}
.toolbar .toolbar-actions {
flex: 1;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
button {
background-color: #fff;
border-radius: 5px;
border: 1px solid #ffe333;
color: #270035;
padding: 10px 30px;
text-decoration: none;
margin: 10px;
cursor: pointer;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-weight: 400;
transition: .2s linear all;
}
button.purple {
font-weight: bold;
border: 1px solid #692fbd;
color: white;
background: #692fbd;
background: linear-gradient(90deg, #bc48ff 0%, #692fbd 100%);
}
button.green {
font-weight: bold;
color: white;
border: 1px solid #0eef5f;
background: linear-gradient(90deg, #2fde6f 0%, #13c251 100%);
}
button.normal:hover {
color: white;
border-color: #692fbd;
background: #692fbd;
background: linear-gradient(90deg, #bc48ff 0%, #692fbd 100%);
}
button.green:hover,
button.purple:hover {
border: 1px solid #fff;
}
.panel {
background: rgba(255, 255, 255, 0.15);
border-radius: 5px;
padding: 0 25px 80px 25px;
margin: 10px 25px;
border-left: 5px solid #0eef5f;
align-self: stretch;
}
.panel h3 {
color: #fff;
}
.panel-inactive {
background: white;
color: #AAA;
border-color: #BBB;
}
.panel-inactive h3 {
color: #333;
}
.panel table {
width: 100%;
margin-top: 30px;
border-collapse: collapse;
text-align: left;
font-weight: normal;
}
.panel th {
border-bottom: 1px solid #fff;
border-collapse: collapse;
padding: 10px;
font-weight: normal;
}
.panel-inactive th {
border-color: #EEE;
}
.panel td {
border-collapse: collapse;
padding: 10px;
}
.panel td a.slide-sort {
cursor: move;
}
.panel td a.slide-name {
color: white;
}
.panel-inactive td a.slide-name {
color: #AAA;
}
.panel td a.slide-name:hover {
text-decoration: underline;
}
.panel td.actions a {
background: white;
color: #333;
border: 1px solid #AAA;
border-radius: 4px;
padding: 2px;
width: 35px;
display: inline-block;
text-align: center;
margin: 0 2px;
}
.panel td.actions a:hover {
color: #0eef5f;
border-color: #0eef5f;
}
.panel td.actions a.slide-edit:hover {
color: #bc48ff;
border-color: #bc48ff;
}
.panel td.actions a.slide-delete:hover {
color: #ef0e0e;
border-color: #ef0e0e;
}
.panel td.infos {
display: flex;
flex-direction: row;
width: 400px;
justify-content: flex-start;
align-items: flex-start;
}
.panel td.infos .inner {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
}
.panel a {
color: #0eef5f;
text-decoration: none;
}
.panel a:hover {
color: #0bc44e;
}
.icon-right {
margin: 0 0 0 10px;
}
.icon-left {
margin: 0 10px 0 0;
}
.pure-material-switch {
z-index: 0;
position: relative;
display: inline-block;
}
.pure-material-switch > input {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
z-index: -1;
position: absolute;
right: 6px;
top: -8px;
display: block;
margin: 0;
border-radius: 50%;
width: 40px;
height: 40px;
background-color: rgba(0, 0, 0, 0.38);
outline: none;
opacity: 0;
transform: scale(1);
pointer-events: none;
transition: opacity 0.3s 0.1s, transform 0.2s 0.1s;
}
.pure-material-switch > span {
display: inline-block;
width: 100%;
cursor: pointer;
}
.pure-material-switch > span::before {
content: "";
float: right;
display: inline-block;
margin: 5px 0 5px 10px;
border-radius: 7px;
width: 36px;
height: 14px;
background-color: rgba(0, 0, 0, 0.38);
vertical-align: top;
transition: background-color 0.2s, opacity 0.2s;
}
.pure-material-switch > span::after {
content: "";
position: absolute;
top: 2px;
right: 16px;
border-radius: 50%;
width: 20px;
height: 20px;
background-color: rgb(255, 255, 255);
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
transition: background-color 0.2s, transform 0.2s;
}
.pure-material-switch > input:checked {
right: -10px;
background-color: rgb(14, 239, 95);
}
.pure-material-switch > input:checked + span::before {
background-color: rgba(14, 239, 95, 0.6);
}
.pure-material-switch > input:checked + span::after {
background-color: rgb(14, 239, 95);
transform: translateX(16px);
}
.pure-material-switch:hover > input {
opacity: 0.04;
}
.pure-material-switch > input:focus {
opacity: 0.12;
}
.pure-material-switch:hover > input:focus {
opacity: 0.16;
}
.pure-material-switch > input:active {
opacity: 1;
transform: scale(0);
transition: transform 0s, opacity 0s;
}
.pure-material-switch > input:active + span::before {
background-color: rgba(14, 239, 95, 0.6);
}
.pure-material-switch > input:checked:active + span::before {
background-color: rgba(0, 0, 0, 0.38);
}
.pure-material-switch > input:disabled {
opacity: 0;
}
.pure-material-switch > input:disabled + span {
color: rgb(0, 0, 0);
opacity: 0.38;
cursor: default;
}
.pure-material-switch > input:disabled + span::before {
background-color: rgba(0, 0, 0, 0.38);
}
.pure-material-switch > input:checked:disabled + span::before {
background-color: rgba(14, 239, 95, 0.6);
}
.modals {
position: fixed;
background: rgba(0, 0, 0, 0.4);
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.modals-outer {
min-width: 30%;
display: flex;
flex-direction: column;
}
.modals-outer .modal-close {
color: white;
font-size: 30px;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
margin-bottom: 20px;
margin-top: -100px;
}
.modals-inner {
background: white;
border-radius: 10px;
color: #333;
}
.modals-inner .modal h2 {
border-bottom: 1px solid #DDD;
padding: 15px 15px;
margin: 0;
}
form {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 20px;
}
form .form-group {
margin: 20px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
align-self: stretch;
flex: 1;
}
form .form-group label {
flex: 1;
padding: 10px;
text-align: right;
margin-right: 20px;
}
form .form-group .widget {
flex: 3;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
align-self: stretch;
}
form .form-group input,
form .form-group select,
form .form-group textarea {
flex: 1;
padding: 10px 5px 10px 5px;
border: 1px solid #EEE;
border-radius: 4px;
width: auto;
}
form .form-group span {
margin-left: 10px;
}
form .actions {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
margin-top: 10px;
align-self: stretch;
}
form .actions button.green,
form .actions button {
font-size: 18px;
}
form .actions button.green:hover {
background: white;
color: rgb(14, 239, 95);
border-color: rgb(14, 239, 95)
}
form .actions button.modal-close {
color: #999;
border-color: #999;
font-size: 18px;
margin: 0;
}
form .actions button.modal-close:hover {
color: #555;
border-color: #555;
}

126
data/www/js/manage.js Normal file
View File

@ -0,0 +1,126 @@
jQuery(document).ready(function ($) {
var $tableActive = $('table.active-slides');
var $tableInactive = $('table.inactive-slides');
var $modalsRoot = $('.modals');
var getId = function($el) {
return $el.is('tr') ? $el.attr('data-level') : $el.parents('tr:eq(0)').attr('data-level');
};
var updateTable = function () {
$('table').each(function () {
if ($(this).find('tbody tr.slide-item:visible').length === 0) {
$(this).find('tr.empty-tr').removeClass('hidden');
} else {
$(this).find('tr.empty-tr').addClass('hidden');
}
}).tableDnDUpdate();
updatePositions();
}
var showModal = function (modalClass) {
$modalsRoot.removeClass('hidden').find('form').trigger('reset');
$modalsRoot.find('.modal').addClass('hidden');
$modalsRoot.find('.modal.' + modalClass).removeClass('hidden');
};
var hideModal = function () {
$modalsRoot.addClass('hidden').find('form').trigger('reset');
};
var updatePositions = function (table, row) {
var positions = {};
$('.slide-item').each(function(index) {
positions[getId($(this))] = index;
});
$.ajax({
method: 'POST',
url: '/manage/slide/position',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify(positions),
});
};
var main = function() {
$("table").tableDnD({
dragHandle: 'td a.slide-sort',
onDrop: updatePositions
});
};
$(document).on('change', 'input[type=checkbox]', function () {
$.ajax({
url: 'manage/slide/toggle',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({id: getId($(this)), enabled: $(this).is(':checked')}),
method: 'POST',
});
var $tr = $(this).parents('tr:eq(0)').remove().clone();
if ($(this).is(':checked')) {
$tableActive.append($tr);
} else {
console.log($tableInactive)
$tableInactive.append($tr);
}
updateTable();
});
$(document).on('change', '#slide-add-type', function () {
var value = $(this).val();
var inputType = $(this).find('option').filter(function (i, el) {
return $(el).val() === value;
}).data('input');
$('.slide-add-object-input')
.addClass('hidden')
.prop('disabled', true)
.filter('#slide-add-object-input-' + inputType)
.removeClass('hidden')
.prop('disabled', false)
;
});
$(document).on('click', '.modal-close', function () {
hideModal();
});
$(document).on('click', '.slide-add', function () {
showModal('modal-slide-add');
});
$(document).on('click', '.slide-edit', function () {
var slide = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
console.log(slide)
showModal('modal-slide-edit');
$('#slide-edit-name').val(slide.name);
$('#slide-edit-type').val(slide.type);
$('#slide-edit-duration').val(slide.duration);
$('#slide-edit-id').val(slide.id);
});
$(document).on('click', '.slide-delete', function () {
if (confirm('Are you sure ?')) {
var $tr = $(this).parents('tr:eq(0)');
$tr.remove();
updateTable();
$.ajax({
method: 'DELETE',
url: '/manage/slide/delete',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({id: getId($(this))}),
});
}
});
$(document).keyup(function (e) {
if (e.key === "Escape") {
hideModal();
}
});
main();
});

File diff suppressed because one or more lines are too long

View File

@ -7,21 +7,14 @@ import subprocess
import sys
from enum import Enum
from flask import Flask, render_template, redirect, request, url_for, send_from_directory
from pysondb import db
from flask import Flask, render_template, redirect, request, url_for, send_from_directory, jsonify
from config import config
from src.SlideManager import SlideManager
# <classes>
class ItemType(Enum):
PICTURE = 'picture'
VIDEO = 'video'
URL = 'url'
# </classes>
# <config>
PLAYER_URL = 'http://localhost:{}'.format(config['port'])
DB = db.getDb("data/slideshow.json")
slide_manager = SlideManager()
with open('./lang/{}.json'.format(config['lang']), 'r') as file:
LANGDICT = json.load(file)
# </config>
@ -82,7 +75,11 @@ def get_ip_address():
# <web>
@app.route('/')
def index():
return render_template('player.jinja.html', items=json.dumps(DB.getAll()))
return render_template('player.jinja.html', items=json.dumps(slide_manager.to_dict(slide_manager.get_enabled_slides())))
@app.route('/playlist')
def playlist():
return jsonify(slide_manager.to_dict(slide_manager.get_enabled_slides()))
@app.route('/slide/default')
def slide_default():
@ -90,13 +87,50 @@ def slide_default():
@app.route('/manage')
def manage():
return render_template('manage.jinja.html', ipaddr=get_ip_address(), l=LANGDICT)
return render_template(
'manage.jinja.html',
ipaddr=get_ip_address(),
l=LANGDICT,
enabled_slides=slide_manager.get_enabled_slides(),
disabled_slides=slide_manager.get_disabled_slides()
)
@app.route('/manage/slide/add', methods=['POST'])
def manage_slide_add():
name = request.form['name']
print(name)
print(request.form)
response = {'message': f'Bonjour {name}, votre formulaire a été reçu !'}
return jsonify(response)
@app.route('/manage/slide/edit', methods=['POST'])
def manage_slide_edit():
slide_manager.update_form(request.form['id'], request.form['name'], request.form['duration'])
return redirect(url_for('manage'))
@app.route('/manage/slide/toggle', methods=['POST'])
def manage_slide_toggle():
data = request.get_json()
slide_manager.update_enabled(data.get('id'), data.get('enabled'))
return jsonify({'status': 'ok'})
@app.route('/manage/slide/delete', methods=['DELETE'])
def manage_slide_delete():
data = request.get_json()
slide_manager.delete(data.get('id'))
return jsonify({'status': 'ok'})
@app.route('/manage/slide/position', methods=['POST'])
def manage_slide_position():
data = request.get_json()
slide_manager.update_positions(data)
return jsonify({'status': 'ok'})
@app.errorhandler(404)
def not_found(e):
return send_from_directory('data', '404.html'), 404
return send_from_directory('views', 'error404.html'), 404
# </web>
if __name__ == '__main__':
app.run(host='0.0.0.0', port=config['port'])
app.run(host=config['bind'] if 'bind' in config else '0.0.0.0', port=config['port'])

View File

@ -1,2 +1,3 @@
flask
pysondb
uuid

54
src/SlideManager.py Normal file
View File

@ -0,0 +1,54 @@
import json
from typing import Dict, Optional, List, Tuple
from src.model.Slide import Slide
from pysondb import db
class SlideManager():
DB_FILE = "data/slideshow.json"
def __init__(self):
self._db = db.getDb(self.DB_FILE)
@staticmethod
def hydrate_object(raw_slide) -> Slide:
return Slide(**raw_slide)
@staticmethod
def hydrate_list(raw_slides) -> List[Slide]:
return [SlideManager.hydrate_object(raw_slide) for raw_slide in raw_slides]
def get_all(self, sort: bool = False) -> List[Slide]:
raw_slides = self._db.getAll()
return SlideManager.hydrate_list(sorted(raw_slides, key=lambda x: x['position']) if sort else raw_slides)
def get_enabled_slides(self) -> List[Slide]:
return [slide for slide in self.get_all(sort=True) if slide.enabled]
def get_disabled_slides(self) -> List[Slide]:
return [slide for slide in self.get_all(sort=True) if not slide.enabled]
def update_enabled(self, id: int, enabled: bool) -> None:
self._db.updateById(id, {"enabled": enabled, "position": 999})
def update_positions(self, positions: list) -> None:
for slide_id, slide_position in positions.items():
self._db.updateById(slide_id, {"position": slide_position})
def update_form(self, id: str, name: str, duration: int) -> None:
self._db.updateById(id, {"name": name, "duration": duration})
def reindent(self) -> None:
with open(self.DB_FILE, 'r') as file:
data = json.load(file)
with open(self.DB_FILE, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
def delete(self, id: int) -> None:
self._db.deleteById(id)
self.reindent()
def to_dict(self, slides: List[Slide]) -> dict:
return [slide.to_dict() for slide in slides]

94
src/model/Slide.py Normal file
View File

@ -0,0 +1,94 @@
import uuid
import json
from typing import Optional
from src.model import SlideType
class Slide:
def __init__(self, location: str, duration: int, type: SlideType, enabled: bool, name: str, position: int = 999, id: Optional[int] = None):
self._id = uuid.uuid4().int if id is None else id
self._location = location
self._duration = duration
self._type = type
self._enabled = enabled
self._name = name
self._position = position
@property
def id(self) -> int:
return self._id
@property
def location(self) -> str:
return self._location
@location.setter
def location(self, value: str):
self._location = value
@property
def type(self) -> SlideType:
return self._type
@type.setter
def type(self, value: SlideType):
self._type = value
@property
def duration(self) -> int:
return self._duration
@duration.setter
def duration(self, value: int):
self._duration = value
@property
def enabled(self) -> bool:
return self._enabled
@enabled.setter
def enabled(self, value: bool):
self._enabled = value
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str):
self._name = value
@property
def position(self) -> int:
return self._position
@position.setter
def position(self, value: int):
self._position = value
def __str__(self) -> str:
return f"Slide(" \
f"id='{self.id}',\n" \
f"name='{self.name}',\n" \
f"type='{self.type}',\n" \
f"enabled='{self.enabled}',\n" \
f"duration='{self.duration}',\n" \
f"position='{self.position}',\n" \
f"location='{self.location}',\n" \
f")"
def to_json(self) -> str:
return json.dumps(self.to_dict())
def to_dict(self) -> dict:
return {
"name": self.name,
"id": self.id,
"enabled": self.enabled,
"position": self.position,
"type": self.type,
"duration": self.duration,
"location": self.location,
}

6
src/model/SlideType.py Normal file
View File

@ -0,0 +1,6 @@
from enum import Enum
class ItemType(Enum):
PICTURE = 'picture'
VIDEO = 'video'
URL = 'url'

File diff suppressed because one or more lines are too long

View File

@ -1,52 +1,15 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<title>
Obscreen
</title>
<title>Obscreen</title>
<meta name="robots" content="noindex, nofollow">
<meta name="google" content="notranslate">
<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%;
}
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>
</head>
<body>
@ -57,114 +20,128 @@
<iframe src="/slide/default"></iframe>
</div>
<script type="text/javascript">
var items = {{items | safe}};
var delay = 3000 / 1;
var curUrl = 0;
var nextReady = true;
preloadIntoFirstSlide();
var items = {{items | safe}};
var duration = 3000 / 1;
var playlistCheck = 10 * 1000; // 10 seconds check
var curUrl = 0;
var nextReady = true;
var itemCheck = setInterval(function () {
fetch('playlist').then(function(response) {
if (response.ok) {
return response.json();
}
}).then(function(data) {
items = data;
});
}, playlistCheck);
function loadContent(element, callbackReady) {
switch (items[curUrl].type) {
case 'url':
loadUrl(element, callbackReady);
break;
case 'picture':
loadPicture(element, callbackReady);
break;
case 'video':
loadVideo(element, callbackReady);
break;
default:
loadUrl(element, callbackReady);
break;
}
function main() {
preloadIntoFirstSlide();
}
function loadUrl(element, callbackReady) {
element.innerHTML = `<iframe src="${items[curUrl].location}"></iframe>`;
callbackReady(function () {});
}
function loadContent(element, callbackReady) {
switch (items[curUrl].type) {
case 'url':
loadUrl(element, callbackReady);
break;
case 'picture':
loadPicture(element, callbackReady);
break;
case 'video':
loadVideo(element, callbackReady);
break;
default:
loadUrl(element, callbackReady);
break;
}
}
function loadPicture(element, callbackReady) {
element.innerHTML = `<img src="${items[curUrl].location}" alt="" />`;
callbackReady(function () {});
}
function loadUrl(element, callbackReady) {
element.innerHTML = `<iframe src="${items[curUrl].location}"></iframe>`;
callbackReady(function () {});
}
function loadVideo(element, callbackReady) {
element.innerHTML = `<video><source src=${items[curUrl].location} type="video/mp4" /></video>`;
var video = element.querySelector('video');
items[curUrl].handle = video;
video.addEventListener('loadedmetadata', function () {
items[curUrl].delay = Math.ceil(video.duration);
});
function loadPicture(element, callbackReady) {
element.innerHTML = `<img src="${items[curUrl].location}" alt="" />`;
callbackReady(function () {});
}
callbackReady(function () {
nextReady = false;
video.play();
video.addEventListener('ended', function () {
nextReady = true;
});
setTimeout(function() {
nextReady = true;
}, Math.ceil(items[curUrl].delay * 1.5));
});
}
function loadVideo(element, callbackReady) {
element.innerHTML = `<video><source src=${items[curUrl].location} type="video/mp4" /></video>`;
var video = element.querySelector('video');
video.addEventListener('loadedmetadata', function () {
items[curUrl].duration = Math.ceil(video.duration);
});
function preloadIntoFirstSlide() {
console.log('preloadIntoFirstSlide', items[curUrl]);
var element = document.getElementById('FirstSlide');
var callbackReady = function (onSlideStart) {
var move = function () {
if (nextReady) {
moveToFirstSlide();
onSlideStart();
} else {
setTimeout(move, 1000);
}
}
callbackReady(function () {
nextReady = false;
video.play();
video.addEventListener('ended', function () {
nextReady = true;
});
setTimeout(function () {
nextReady = true;
}, Math.ceil(items[curUrl].duration * 1.5));
});
}
setTimeout(move, delay);
};
function preloadIntoFirstSlide() {
//console.log('preloadIntoFirstSlide', items[curUrl]);
var element = document.getElementById('FirstSlide');
var callbackReady = function (onSlideStart) {
var move = function () {
if (nextReady) {
moveToFirstSlide();
onSlideStart();
} else {
setTimeout(move, 1000);
}
}
setTimeout(move, duration);
};
loadContent(element, callbackReady);
}
}
function moveToFirstSlide() {
console.log('moveToFirstSlide', items[curUrl]);
delay = items[curUrl].delay * 1000;
curUrl = (curUrl + 1) === items.length ? 0 : curUrl + 1;
document.querySelector('#FirstSlide').style.visibility = 'visible';
document.querySelector('#SecondSlide').style.visibility = 'hidden';
preloadIntoSecondSlide();
}
function moveToFirstSlide() {
//console.log('moveToFirstSlide', items[curUrl]);
duration = items[curUrl].duration * 1000;
curUrl = (curUrl + 1) === items.length ? 0 : curUrl + 1;
document.querySelector('#FirstSlide').style.visibility = 'visible';
document.querySelector('#SecondSlide').style.visibility = 'hidden';
preloadIntoSecondSlide();
}
function preloadIntoSecondSlide() {
console.log('preloadIntoSecondSlide', items[curUrl]);
var element = document.getElementById('SecondSlide');
var callbackReady = function (onSlideStart) {
var move = function () {
if (nextReady) {
moveToSecondSlide();
onSlideStart();
} else {
setTimeout(move, 1000);
}
}
function preloadIntoSecondSlide() {
//console.log('preloadIntoSecondSlide', items[curUrl]);
var element = document.getElementById('SecondSlide');
var callbackReady = function (onSlideStart) {
var move = function () {
if (nextReady) {
moveToSecondSlide();
onSlideStart();
} else {
setTimeout(move, 1000);
}
}
setTimeout(move, delay);
};
setTimeout(move, duration);
};
loadContent(element, callbackReady);
}
}
function moveToSecondSlide() {
console.log('moveToSecondSlide', items[curUrl]);
delay = items[curUrl].delay * 1000;
curUrl = (curUrl + 1) === items.length ? 0 : curUrl + 1;
document.querySelector('#FirstSlide').style.visibility = 'hidden';
document.querySelector('#SecondSlide').style.visibility = 'visible';
preloadIntoFirstSlide();
}
function moveToSecondSlide() {
//console.log('moveToSecondSlide', items[curUrl]);
duration = items[curUrl].duration * 1000;
curUrl = (curUrl + 1) === items.length ? 0 : curUrl + 1;
document.querySelector('#FirstSlide').style.visibility = 'hidden';
document.querySelector('#SecondSlide').style.visibility = 'visible';
preloadIntoFirstSlide();
}
main();
</script>
</body>
</html>