Merge pull request #29 from jr-k/feature/auto-refresh-player-if-changes

Refreshable player and some player customizations through settings
This commit is contained in:
JRK 2024-05-08 18:53:56 +02:00 committed by GitHub
commit 5f5f2d3743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 131 additions and 26 deletions

View File

@ -189,6 +189,24 @@ button.purple:hover {
border: 1px solid #fff; border: 1px solid #fff;
} }
.alert {
margin: 10px 30px 20px 30px;
padding: 20px 50px;
flex: 1;
align-self: stretch;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.alert-success {
color: rgb(14, 239, 95);
border: 1px solid rgb(14, 239, 95);
background: rgba(14, 239, 95, .2);
border-radius: 4px;
}
.panel { .panel {
background: rgba(255, 255, 255, 0.15); background: rgba(255, 255, 255, 0.15);
@ -548,7 +566,7 @@ form .actions button {
form .actions button.green:hover { form .actions button.green:hover {
background: white; background: white;
color: rgb(14, 239, 95); color: rgb(14, 239, 95);
border-color: rgb(14, 239, 95) border-color: rgb(14, 239, 95);
} }
form .actions button.modal-close { form .actions button.modal-close {

View File

@ -29,7 +29,8 @@ jQuery(document).ready(function ($) {
}); });
$('#variable-edit-value').replaceWith($select); $('#variable-edit-value').replaceWith($select);
} else { } else {
$('#variable-edit-value').replaceWith('<input type="text" name="value" id="variable-edit-value" required="required" />'); var type = variable.type === 'int' ? 'number' : 'text';
$('#variable-edit-value').replaceWith('<input type="'+type+'" name="value" id="variable-edit-value" required="required" />');
} }
showModal('modal-variable-edit'); showModal('modal-variable-edit');

View File

@ -1,6 +1,8 @@
{ {
"slideshow_page_title": "Schedule Overview", "slideshow_page_title": "Schedule Overview",
"slideshow_goto_player": "Go to player", "slideshow_goto_player": "Go to player",
"slideshow_refresh_player": "Refresh player",
"slideshow_refresh_player_success": "A player refresh has been schedueld, it should happen soon enough (%time% seconds maximum)",
"slideshow_slide_button_add": "Add a slide", "slideshow_slide_button_add": "Add a slide",
"slideshow_slide_panel_active": "Active slides", "slideshow_slide_panel_active": "Active slides",
"slideshow_slide_panel_inactive": "Inactive slides", "slideshow_slide_panel_inactive": "Inactive slides",
@ -71,6 +73,8 @@
"settings_variable_desc_fleet_enabled": "Enable fleet screen management view", "settings_variable_desc_fleet_enabled": "Enable fleet screen management view",
"settings_variable_desc_external_url": "External url (i.e: https://screen-01.company.com or http://10.10.3.100)", "settings_variable_desc_external_url": "External url (i.e: https://screen-01.company.com or http://10.10.3.100)",
"settings_variable_desc_slide_upload_limit": "Slide upload limit (in bytes, 32*1024*1024 for 32MB)", "settings_variable_desc_slide_upload_limit": "Slide upload limit (in bytes, 32*1024*1024 for 32MB)",
"settings_variable_desc_default_slide_duration": "Intro slide duration (in seconds)",
"settings_variable_desc_polling_interval": "Refresh interval applied for settings to the player (in seconds)",
"settings_variable_desc_slide_animation_enabled": "Enable animation effect between slides", "settings_variable_desc_slide_animation_enabled": "Enable animation effect between slides",
"settings_variable_desc_slide_animation_entrance_effect": "Slide animation entrance effect", "settings_variable_desc_slide_animation_entrance_effect": "Slide animation entrance effect",
@ -79,6 +83,7 @@
"settings_variable_desc_ro_editable": "Last application reboot datetime", "settings_variable_desc_ro_editable": "Last application reboot datetime",
"settings_variable_desc_ro_last_slide_update": "Last slide update datetime", "settings_variable_desc_ro_last_slide_update": "Last slide update datetime",
"settings_variable_desc_ro_refresh_player_request": "Last player refresh request datetime",
"sysinfo_page_title": "System infos", "sysinfo_page_title": "System infos",
"sysinfo_panel_button_restart": "Restart", "sysinfo_panel_button_restart": "Restart",
@ -120,5 +125,6 @@
"enum_animation_speed_fast": "Fast", "enum_animation_speed_fast": "Fast",
"enum_animation_speed_faster": "Faster", "enum_animation_speed_faster": "Faster",
"enum_variable_section_general": "General", "enum_variable_section_general": "General",
"enum_variable_section_animation": "Animation" "enum_variable_section_player_animation": "Player animation",
"enum_variable_section_player_options": "Options du lecteur"
} }

View File

@ -1,6 +1,8 @@
{ {
"slideshow_page_title": "Vue Planning", "slideshow_page_title": "Vue Planning",
"slideshow_goto_player": "Voir le lecteur", "slideshow_goto_player": "Voir le lecteur",
"slideshow_refresh_player": "Rafraîchir le lecteur",
"slideshow_refresh_player_success": "Un rafraîchissement du lecteur a été programmé, il devrait avoir lieu sous peu (%time% secondes maximum)",
"slideshow_slide_button_add": "Ajouter une slide", "slideshow_slide_button_add": "Ajouter une slide",
"slideshow_slide_panel_active": "Slides actives", "slideshow_slide_panel_active": "Slides actives",
"slideshow_slide_panel_inactive": "Slides inactives", "slideshow_slide_panel_inactive": "Slides inactives",
@ -71,6 +73,8 @@
"settings_variable_desc_fleet_enabled": "Activer la gestion de flotte des écrans", "settings_variable_desc_fleet_enabled": "Activer la gestion de flotte des écrans",
"settings_variable_desc_external_url": "URL externe (i.e: https://screen-01.company.com or http://10.10.3.100)", "settings_variable_desc_external_url": "URL externe (i.e: https://screen-01.company.com or http://10.10.3.100)",
"settings_variable_desc_slide_upload_limit": "Limite d'upload du fichier d'une slide (en octets, 32*1024*1024 pour 32Mo)", "settings_variable_desc_slide_upload_limit": "Limite d'upload du fichier d'une slide (en octets, 32*1024*1024 pour 32Mo)",
"settings_variable_desc_default_slide_duration": "Durée de la slide d'introduction (en secondes)",
"settings_variable_desc_polling_interval": "Intervalle de rafraîchissement des paramètres à appliquer au lecteur (en secondes)",
"settings_variable_desc_slide_animation_enabled": "Activer les effets d'animation entre les slides", "settings_variable_desc_slide_animation_enabled": "Activer les effets d'animation entre les slides",
"settings_variable_desc_slide_animation_entrance_effect": "Effet d'animation d'arrivée de la slide", "settings_variable_desc_slide_animation_entrance_effect": "Effet d'animation d'arrivée de la slide",
@ -79,6 +83,7 @@
"settings_variable_desc_ro_editable": "Date de dernier redémarrage de l'application", "settings_variable_desc_ro_editable": "Date de dernier redémarrage de l'application",
"settings_variable_desc_ro_last_slide_update": "Date de dernière modification d'une slide", "settings_variable_desc_ro_last_slide_update": "Date de dernière modification d'une slide",
"settings_variable_desc_ro_refresh_player_request": "Date de dernière demande de rafraîchissement du lecteur",
"sysinfo_page_title": "Système", "sysinfo_page_title": "Système",
"sysinfo_panel_button_restart": "Redémarrer", "sysinfo_panel_button_restart": "Redémarrer",
@ -120,5 +125,6 @@
"enum_animation_speed_fast": "Rapide", "enum_animation_speed_fast": "Rapide",
"enum_animation_speed_faster": "Très rapide", "enum_animation_speed_faster": "Très rapide",
"enum_variable_section_general": "Général", "enum_variable_section_general": "Général",
"enum_variable_section_animation": "Animation" "enum_variable_section_player_animation": "Animation du lecteur",
"enum_variable_section_player_options": "Options du lecteur"
} }

View File

@ -24,7 +24,8 @@ class PlayerController(ObController):
playlists = { playlists = {
'loop': playlist_loop, 'loop': playlist_loop,
'cron': playlist_cron 'cron': playlist_cron,
'hard_refresh_request': self._model_store.variable().get_one_by_name("refresh_player_request").as_int()
} }
return playlists return playlists
@ -38,6 +39,8 @@ class PlayerController(ObController):
return render_template( return render_template(
'player/player.jinja.html', 'player/player.jinja.html',
items=json.dumps(self._get_playlist()), items=json.dumps(self._get_playlist()),
default_slide_duration=self._model_store.variable().get_one_by_name('default_slide_duration'),
polling_interval=self._model_store.variable().get_one_by_name('polling_interval'),
slide_animation_enabled=self._model_store.variable().get_one_by_name('slide_animation_enabled'), 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_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_exit_effect=self._model_store.variable().get_one_by_name('slide_animation_exit_effect'),

View File

@ -1,3 +1,4 @@
import time
import json import json
from flask import Flask, render_template, redirect, request, url_for from flask import Flask, render_template, redirect, request, url_for
@ -21,4 +22,12 @@ class SettingsController(ObController):
def settings_variable_edit(self): def settings_variable_edit(self):
self._model_store.variable().update_form(request.form['id'], request.form['value']) self._model_store.variable().update_form(request.form['id'], request.form['value'])
self._post_update(request.form['id'])
return redirect(url_for('settings_variable_list')) return redirect(url_for('settings_variable_list'))
def _post_update(self, id: str):
variable = self._model_store.variable().get(id)
if variable.refresh_player:
self._model_store.variable().update_by_name("refresh_player_request", time.time())

View File

@ -21,6 +21,7 @@ class SlideshowController(ObController):
self._app.add_url_rule('/slideshow/slide/toggle', 'slideshow_slide_toggle', self.slideshow_slide_toggle, methods=['POST']) self._app.add_url_rule('/slideshow/slide/toggle', 'slideshow_slide_toggle', self.slideshow_slide_toggle, methods=['POST'])
self._app.add_url_rule('/slideshow/slide/delete', 'slideshow_slide_delete', self.slideshow_slide_delete, methods=['DELETE']) self._app.add_url_rule('/slideshow/slide/delete', 'slideshow_slide_delete', self.slideshow_slide_delete, methods=['DELETE'])
self._app.add_url_rule('/slideshow/slide/position', 'slideshow_slide_position', self.slideshow_slide_position, methods=['POST']) self._app.add_url_rule('/slideshow/slide/position', 'slideshow_slide_position', self.slideshow_slide_position, methods=['POST'])
self._app.add_url_rule('/slideshow/player-refresh', 'slideshow_player_refresh', self.slideshow_player_refresh, methods=['GET'])
def manage(self): def manage(self):
return redirect(url_for('slideshow_slide_list')) return redirect(url_for('slideshow_slide_list'))
@ -88,6 +89,15 @@ class SlideshowController(ObController):
self._post_update() self._post_update()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
def slideshow_player_refresh(self):
self._model_store.variable().update_by_name("refresh_player_request", time.time())
return redirect(
url_for(
'slideshow_slide_list',
refresh_player=self._model_store.variable().get_one_by_name('polling_interval').as_int()
)
)
def _post_update(self): def _post_update(self):
self._model_store.variable().update_by_name("last_slide_update", time.time()) self._model_store.variable().update_by_name("last_slide_update", time.time())

View File

@ -56,7 +56,7 @@ class ObPlugin(abc.ABC):
def get_plugin_variable_name(self, name: str) -> str: def get_plugin_variable_name(self, name: str) -> str:
return "{}_{}".format(self.get_plugin_variable_prefix(), name) return "{}_{}".format(self.get_plugin_variable_prefix(), name)
def add_variable(self, name: str, value='', section: str = '', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None) -> Variable: def add_variable(self, name: str, value='', section: str = '', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None, refresh_player: bool = False) -> Variable:
return self._model_store.variable().set_variable( return self._model_store.variable().set_variable(
name=self.get_plugin_variable_name(name), name=self.get_plugin_variable_name(name),
section=section, section=section,
@ -65,6 +65,7 @@ class ObPlugin(abc.ABC):
editable=editable, editable=editable,
description=description, description=description,
unit=unit, unit=unit,
refresh_player=refresh_player,
selectables=selectables if isinstance(selectables, dict) else None, selectables=selectables if isinstance(selectables, dict) else None,
plugin=self.use_id(), plugin=self.use_id(),
) )

View File

@ -30,6 +30,7 @@ class VariableManager(ModelManager):
"selectables", "selectables",
"type", "type",
"unit", "unit",
"refresh_player",
"value" "value"
] ]
@ -39,7 +40,7 @@ class VariableManager(ModelManager):
self._var_map = {} self._var_map = {}
self.reload() self.reload()
def set_variable(self, name: str, value, type: VariableType, editable: bool, description: str, plugin: Optional[None] = None, selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None, section: str = '') -> Variable: def set_variable(self, name: str, value, type: VariableType, editable: bool, description: str, plugin: Optional[None] = None, selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None, section: str = '', refresh_player: bool = False) -> Variable:
if isinstance(value, bool) and value: if isinstance(value, bool) and value:
value = '1' value = '1'
elif isinstance(value, bool) and not value: elif isinstance(value, bool) and not value:
@ -54,6 +55,7 @@ class VariableManager(ModelManager):
"value": value, "value": value,
"type": type.value, "type": type.value,
"editable": editable, "editable": editable,
"refresh_player": refresh_player,
"description": description, "description": description,
"plugin": plugin, "plugin": plugin,
"unit": unit.value if unit else None, "unit": unit.value if unit else None,
@ -76,6 +78,9 @@ class VariableManager(ModelManager):
if variable.section != default_var['section']: if variable.section != default_var['section']:
self._db.update_by_id(variable.id, {"section": default_var['section']}) self._db.update_by_id(variable.id, {"section": default_var['section']})
if variable.refresh_player != default_var['refresh_player']:
self._db.update_by_id(variable.id, {"refresh_player": default_var['refresh_player']})
if not same_selectables: if not same_selectables:
self._db.update_by_id(variable.id, {"selectables": default_var['selectables']}) self._db.update_by_id(variable.id, {"selectables": default_var['selectables']})
@ -87,18 +92,27 @@ class VariableManager(ModelManager):
def reload(self) -> None: def reload(self) -> None:
default_vars = [ default_vars = [
# Editable (Customizable settings) # Editable (Customizable settings)
{"name": "lang", "section": self.t(VariableSection.GENERAL), "value": "en", "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_lang'), "selectables": {"en": "English", "fr": "French"}},
{"name": "fleet_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_fleet_enabled')}, ### General
{"name": "external_url", "section": self.t(VariableSection.GENERAL), "value": "", "type": VariableType.STRING, "editable": True, "description": self.t('settings_variable_desc_external_url')}, {"name": "lang", "section": self.t(VariableSection.GENERAL), "value": "en", "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_lang'), "selectables": {"en": "English", "fr": "French"}, "refresh_player": False},
{"name": "slide_upload_limit", "section": self.t(VariableSection.ANIMATION), "value": 32 * 1024 * 1024, "unit": VariableUnit.BYTE, "type": VariableType.INT, "editable": True, "description": self.t('settings_variable_desc_slide_upload_limit')}, {"name": "fleet_enabled", "section": self.t(VariableSection.GENERAL), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_fleet_enabled'), "refresh_player": False},
{"name": "slide_animation_enabled", "section": self.t(VariableSection.ANIMATION), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_slide_animation_enabled')}, {"name": "external_url", "section": self.t(VariableSection.GENERAL), "value": "", "type": VariableType.STRING, "editable": True, "description": self.t('settings_variable_desc_external_url'), "refresh_player": False},
{"name": "slide_animation_entrance_effect", "section": self.t(VariableSection.ANIMATION), "value": AnimationEntranceEffect.FADE_IN.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_entrance_effect'), "selectables": enum_to_dict(AnimationEntranceEffect)}, {"name": "slide_upload_limit", "section": self.t(VariableSection.GENERAL), "value": 32 * 1024 * 1024, "unit": VariableUnit.BYTE, "type": VariableType.INT, "editable": True, "description": self.t('settings_variable_desc_slide_upload_limit'), "refresh_player": False},
{"name": "slide_animation_exit_effect", "section": self.t(VariableSection.ANIMATION), "value": AnimationExitEffect.NONE.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_exit_effect'), "selectables": enum_to_dict(AnimationExitEffect)},
{"name": "slide_animation_speed", "section": self.t(VariableSection.ANIMATION), "value": AnimationSpeed.NORMAL.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_speed'), "selectables": self.t(AnimationSpeed)}, ### Player Options
{"name": "default_slide_duration", "section": self.t(VariableSection.PLAYER_OPTIONS), "value": 3, "unit": VariableUnit.SECOND, "type": VariableType.INT, "editable": True, "description": self.t('settings_variable_desc_default_slide_duration'), "refresh_player": False},
{"name": "polling_interval", "section": self.t(VariableSection.PLAYER_OPTIONS), "value": 5, "unit": VariableUnit.SECOND, "type": VariableType.INT, "editable": True, "description": self.t('settings_variable_desc_polling_interval'), "refresh_player": True},
### Player Animation
{"name": "slide_animation_enabled", "section": self.t(VariableSection.PLAYER_ANIMATION), "value": False, "type": VariableType.BOOL, "editable": True, "description": self.t('settings_variable_desc_slide_animation_enabled'), "refresh_player": True},
{"name": "slide_animation_entrance_effect", "section": self.t(VariableSection.PLAYER_ANIMATION), "value": AnimationEntranceEffect.FADE_IN.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_entrance_effect'), "selectables": enum_to_dict(AnimationEntranceEffect), "refresh_player": True},
{"name": "slide_animation_exit_effect", "section": self.t(VariableSection.PLAYER_ANIMATION), "value": AnimationExitEffect.NONE.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_exit_effect'), "selectables": enum_to_dict(AnimationExitEffect), "refresh_player": True},
{"name": "slide_animation_speed", "section": self.t(VariableSection.PLAYER_ANIMATION), "value": AnimationSpeed.NORMAL.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": self.t('settings_variable_desc_slide_animation_speed'), "selectables": self.t(AnimationSpeed), "refresh_player": True},
# Not editable (System information) # Not editable (System information)
{"name": "last_restart", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_editable')}, {"name": "last_restart", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_editable')},
{"name": "last_slide_update", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_last_slide_update')}, {"name": "last_slide_update", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_last_slide_update')},
{"name": "refresh_player_request", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_refresh_player_request')},
] ]
for default_var in default_vars: for default_var in default_vars:

View File

@ -12,7 +12,8 @@ class Variable:
def __init__(self, name: str = '', section: str = '', description: str = '', type: Union[VariableType, str] = VariableType.STRING, def __init__(self, name: str = '', section: str = '', description: str = '', type: Union[VariableType, str] = VariableType.STRING,
value: Union[int, bool, str] = '', editable: bool = True, id: Optional[str] = None, value: Union[int, bool, str] = '', editable: bool = True, id: Optional[str] = None,
plugin: Optional[str] = None, selectables: Optional[List[Selectable]] = None, unit: Optional[VariableUnit] = None): plugin: Optional[str] = None, selectables: Optional[List[Selectable]] = None, unit: Optional[VariableUnit] = None,
refresh_player: bool = False):
self._id = id if id else None self._id = id if id else None
self._name = name self._name = name
self._section = section self._section = section
@ -22,6 +23,7 @@ class Variable:
self._value = value self._value = value
self._editable = editable self._editable = editable
self._plugin = plugin self._plugin = plugin
self._refresh_player = refresh_player
self._selectables = selectables self._selectables = selectables
@property @property
@ -87,6 +89,14 @@ class Variable:
def editable(self, value: bool): def editable(self, value: bool):
self._editable = value self._editable = value
@property
def refresh_player(self) -> bool:
return self._refresh_player
@refresh_player.setter
def refresh_player(self, value: bool):
self._refresh_player = value
@property @property
def value(self) -> Union[int, bool, str]: def value(self) -> Union[int, bool, str]:
return self._value return self._value
@ -113,6 +123,7 @@ class Variable:
f"unit='{self.unit}',\n" \ f"unit='{self.unit}',\n" \
f"description='{self.description}',\n" \ f"description='{self.description}',\n" \
f"editable='{self.editable}',\n" \ f"editable='{self.editable}',\n" \
f"refresh_player='{self.refresh_player}',\n" \
f"plugin='{self.plugin}',\n" \ f"plugin='{self.plugin}',\n" \
f"selectables='{self.selectables}',\n" \ f"selectables='{self.selectables}',\n" \
f")" f")"
@ -130,6 +141,7 @@ class Variable:
"unit": self.unit.value if self.unit else None, "unit": self.unit.value if self.unit else None,
"description": self.description, "description": self.description,
"editable": self.editable, "editable": self.editable,
"refresh_player": self.refresh_player,
"plugin": self.plugin, "plugin": self.plugin,
"selectables": [selectable.to_dict() for selectable in self.selectables] if isinstance(self._selectables, list) else None "selectables": [selectable.to_dict() for selectable in self.selectables] if isinstance(self._selectables, list) else None
} }
@ -161,6 +173,8 @@ class Variable:
value / 1024 / 1024, value / 1024 / 1024,
"MB" "MB"
) )
elif self.unit == VariableUnit.SECOND:
value = "{}{}".format(value, "s")
return value return value

View File

@ -4,4 +4,5 @@ from enum import Enum
class VariableSection(Enum): class VariableSection(Enum):
GENERAL = 'general' GENERAL = 'general'
ANIMATION = 'animation' PLAYER_OPTIONS = 'player_options'
PLAYER_ANIMATION = 'player_animation'

View File

@ -4,3 +4,4 @@ from enum import Enum
class VariableUnit(Enum): class VariableUnit(Enum):
BYTE = 'byte' BYTE = 'byte'
SECOND = 'second'

View File

@ -19,16 +19,21 @@
</head> </head>
<body> <body>
<div id="FirstSlide" class="slide" style="z-index: 1000;"> <div id="FirstSlide" class="slide" style="z-index: 1000;">
{% if default_slide_duration.eval() > 0 %}
<iframe src="/player/default"></iframe> <iframe src="/player/default"></iframe>
{% endif %}
</div> </div>
<div id="SecondSlide" class="slide" style="z-index: 500;"> <div id="SecondSlide" class="slide" style="z-index: 500;">
{% if default_slide_duration.eval() > 0 %}
<iframe src="/player/default"></iframe> <iframe src="/player/default"></iframe>
{% endif %}
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var items = {{items | safe}}; var items = {{items | safe}};
var duration = 3000 / 1; var duration = {{ default_slide_duration.eval() * 1000 }};
var playlistCheck = 10 * 1000; // 10 seconds check var playlistCheck = {{ polling_interval.eval() * 1000 }};
var curItemIndex = 0; var curItemIndex = 0;
var needHardRefresh = null;
var nextReady = true; var nextReady = true;
var itemCheck = setInterval(function () { var itemCheck = setInterval(function () {
fetch('player/playlist').then(function(response) { fetch('player/playlist').then(function(response) {
@ -37,6 +42,12 @@
} }
}).then(function(data) { }).then(function(data) {
items = data; items = data;
if (needHardRefresh === null) {
needHardRefresh = items.hard_refresh_request;
} else if (needHardRefresh != items.hard_refresh_request) {
document.location.reload();
}
}).catch(function(err) { }).catch(function(err) {
console.error(err); console.error(err);
}); });

View File

@ -28,15 +28,25 @@
{{ HOOK(H_FLEETMODE_SLIDESHOW_TOOLBAR_ACTIONS) }} {{ HOOK(H_FLEETMODE_SLIDESHOW_TOOLBAR_ACTIONS) }}
{% endif %} {% endif %}
<a href="/" target="_blank" class="btn" title="{{ l.slideshow_goto_player }}">
<a href="/" target="_blank" class="btn"> <i class="fa fa-play"></i>
<i class="fa fa-play icon-left"></i>
{{ l.slideshow_goto_player }}
</a> </a>
<a href="{{ url_for('slideshow_player_refresh') }}" class="btn" title="{{ l.slideshow_refresh_player }}">
<i class="fa fa-refresh"></i>
</a>
<button class="purple slide-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.slideshow_slide_button_add }}</button> <button class="purple slide-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.slideshow_slide_button_add }}</button>
{{ HOOK(H_SLIDESHOW_TOOLBAR_ACTIONS_END) }} {{ HOOK(H_SLIDESHOW_TOOLBAR_ACTIONS_END) }}
</div> </div>
</div> </div>
{% if request.args.get('refresh_player') %}
<div class="alert alert-success">
<i class="fa fa-refresh icon-left"></i>
{{ l.slideshow_refresh_player_success|replace('%time%', request.args.get('refresh_player')) }}
</div>
{% endif %}
<div class="panel"> <div class="panel">
<div class="panel-body"> <div class="panel-body">
<h3>{{ l.slideshow_slide_panel_active }}</h3> <h3>{{ l.slideshow_slide_panel_active }}</h3>