commit
03c2a47410
File diff suppressed because one or more lines are too long
@ -2,23 +2,19 @@ jQuery(document).ready(function ($) {
|
||||
const $tableActive = $('table.active-slides');
|
||||
const $tableInactive = $('table.inactive-slides');
|
||||
|
||||
const getCronDateTime = function(cronExpression) {
|
||||
const [minutes, hours, day, month, _, year] = cronExpression.split(' ');
|
||||
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const loadDateTimePicker = function($els) {
|
||||
const d = new Date();
|
||||
|
||||
$els.each(function() {
|
||||
var $el = $(this);
|
||||
if (!$el.val()) {
|
||||
$el.val(prettyTimestamp(Date.now()).slice(0, -4));
|
||||
}
|
||||
$el.flatpickr({
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
allowInput: false,
|
||||
allowInvalidPreload: false,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
defaultHour: d.getHours(),
|
||||
defaultMinute: d.getMinutes(),
|
||||
onChange: function(selectedDates, dateStr, instance) {
|
||||
const d = selectedDates[0];
|
||||
const $target = $el.parents('.widget:eq(0)').find('.target');
|
||||
@ -81,6 +77,7 @@ jQuery(document).ready(function ($) {
|
||||
const $scheduleStartGroup = $modal.find('.slide-schedule-group');
|
||||
const $scheduleEndGroup = $modal.find('.slide-schedule-end-group');
|
||||
const $durationGroup = $modal.find('.slide-duration-group');
|
||||
const $isNotificationGroup = $modal.find('.slide-notification-group');
|
||||
|
||||
const $triggerStart = $scheduleStartGroup.find('.trigger');
|
||||
const $triggerEnd = $scheduleEndGroup.find('.trigger');
|
||||
@ -90,42 +87,88 @@ jQuery(document).ready(function ($) {
|
||||
|
||||
const $datetimepickerStart = $scheduleStartGroup.find('.datetimepicker');
|
||||
const $datetimepickerEnd = $scheduleEndGroup.find('.datetimepicker');
|
||||
const $isNotification = $isNotificationGroup.find('.trigger');
|
||||
|
||||
const isNotification = $isNotification.prop('checked');
|
||||
let isLoopStart = $triggerStart.val() === 'loop';
|
||||
let isCronStart = $triggerStart.val() === 'cron';
|
||||
|
||||
function updateScheduleChoices(isNotification, isLoopStart, isCronStart) {
|
||||
let scheduleStartChoices = $.extend({}, schedule_start_choices);
|
||||
let scheduleEndChoices = $.extend({}, schedule_end_choices);
|
||||
|
||||
if (!isNotification || isLoopStart) {
|
||||
delete scheduleStartChoices['cron'];
|
||||
delete scheduleEndChoices['duration'];
|
||||
}
|
||||
|
||||
if (isNotification) {
|
||||
delete scheduleStartChoices['loop'];
|
||||
delete scheduleEndChoices['stayloop'];
|
||||
|
||||
if (isCronStart) {
|
||||
delete scheduleEndChoices['datetime'];
|
||||
}
|
||||
}
|
||||
|
||||
return { scheduleStartChoices, scheduleEndChoices };
|
||||
}
|
||||
|
||||
function applyChoices() {
|
||||
const { scheduleStartChoices, scheduleEndChoices } = updateScheduleChoices(isNotification, isLoopStart, isCronStart);
|
||||
recreateSelectOptions($triggerStart, scheduleStartChoices);
|
||||
recreateSelectOptions($triggerEnd, scheduleEndChoices);
|
||||
}
|
||||
|
||||
applyChoices();
|
||||
|
||||
isLoopStart = $triggerStart.val() === 'loop';
|
||||
isCronStart = $triggerStart.val() === 'cron';
|
||||
|
||||
const isCronStart = $triggerStart.val() === 'cron';
|
||||
const isCronEnd = $triggerEnd.val() === 'cron';
|
||||
const isDatetimeStart = $triggerStart.val() === 'datetime';
|
||||
const isDatetimeEnd = $triggerEnd.val() === 'datetime';
|
||||
const isLoopStart = $triggerStart.val() === 'loop';
|
||||
const isStayloopEnd = $triggerEnd.val() === 'stayloop';
|
||||
const isDurationEnd = $triggerEnd.val() === 'duration';
|
||||
|
||||
const flushValueStart = isLoopStart;
|
||||
const flushValueEnd = isLoopStart || isDurationEnd;
|
||||
const flushDuration = !isLoopStart && !isDurationEnd;
|
||||
const flushValueEnd = isLoopStart || isStayloopEnd || isDurationEnd;
|
||||
const flushDuration = isNotification && isDatetimeEnd;
|
||||
|
||||
$targetCronFieldStart.toggleClass('hidden', !isCronStart);
|
||||
$targetCronFieldEnd.toggleClass('hidden', !isCronEnd);
|
||||
$datetimepickerStart.toggleClass('hidden', !isDatetimeStart);
|
||||
$datetimepickerEnd.toggleClass('hidden', !isDatetimeEnd);
|
||||
function toggleVisibility() {
|
||||
$targetCronFieldStart.toggleClass('hidden', !isCronStart);
|
||||
$targetCronFieldEnd.toggleClass('hidden', !isCronEnd);
|
||||
$datetimepickerStart.toggleClass('hidden', !isDatetimeStart);
|
||||
$datetimepickerEnd.toggleClass('hidden', !isDatetimeEnd);
|
||||
|
||||
$durationGroup.toggleClass('hidden', !isLoopStart && !isDurationEnd);
|
||||
$scheduleEndGroup.toggleClass('hidden', isLoopStart);
|
||||
$durationGroup.toggleClass('hidden', isNotification && isDatetimeEnd);
|
||||
$scheduleEndGroup.toggleClass('hidden', isLoopStart);
|
||||
|
||||
$durationGroup.find('.widget input').prop('required', $durationGroup.is(':visible'));
|
||||
|
||||
if (flushValueStart) {
|
||||
$targetCronFieldStart.val('');
|
||||
$datetimepickerStart.val('');
|
||||
$durationGroup.find('.widget input').prop('required', $durationGroup.is(':visible'));
|
||||
}
|
||||
|
||||
if (flushValueEnd) {
|
||||
$targetCronFieldEnd.val('');
|
||||
$datetimepickerEnd.val('');
|
||||
function flushValues() {
|
||||
if (flushValueStart) {
|
||||
$targetCronFieldStart.val('');
|
||||
$datetimepickerStart.val('');
|
||||
}
|
||||
|
||||
if (flushValueEnd) {
|
||||
$targetCronFieldEnd.val('');
|
||||
$datetimepickerEnd.val('');
|
||||
}
|
||||
|
||||
if (flushDuration) {
|
||||
$targetDuration.val('1');
|
||||
}
|
||||
}
|
||||
|
||||
if (flushDuration) {
|
||||
$targetDuration.val('0');
|
||||
}
|
||||
toggleVisibility();
|
||||
flushValues();
|
||||
applyChoices();
|
||||
};
|
||||
|
||||
|
||||
const main = function () {
|
||||
$("table").tableDnD({
|
||||
dragHandle: 'td a.slide-sort',
|
||||
@ -156,7 +199,7 @@ jQuery(document).ready(function ($) {
|
||||
updateTable();
|
||||
});
|
||||
|
||||
$(document).on('change', '.modal-slide select.trigger', function () {
|
||||
$(document).on('change', '.modal-slide select.trigger, .modal-slide input.trigger', function () {
|
||||
inputSchedulerUpdate();
|
||||
});
|
||||
|
||||
@ -179,6 +222,7 @@ jQuery(document).ready(function ($) {
|
||||
|
||||
const hasCronEnd = slide.cron_schedule_end && slide.cron_schedule_end.length > 0;
|
||||
const hasDateTimeEnd = hasCronEnd && validateCronDateTime(slide.cron_schedule_end);
|
||||
const isNotification = slide.is_notification;
|
||||
|
||||
let location = slide.location;
|
||||
|
||||
@ -191,12 +235,13 @@ jQuery(document).ready(function ($) {
|
||||
$('#slide-edit-type').val(slide.type);
|
||||
$('#slide-edit-location').val(location).prop('disabled', !slide.is_editable);
|
||||
$('#slide-edit-duration').val(slide.duration);
|
||||
$('#slide-edit-is-notification').prop('checked', isNotification);
|
||||
|
||||
$('#slide-edit-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron || hasDateTime);
|
||||
$('#slide-edit-cron-schedule-trigger').val(hasDateTime ? 'datetime' : (hasCron ? 'cron' : 'loop'));
|
||||
|
||||
$('#slide-edit-cron-schedule-end').val(slide.cron_schedule_end).toggleClass('hidden', !hasCronEnd || hasDateTimeEnd);
|
||||
$('#slide-edit-cron-schedule-end-trigger').val(hasDateTimeEnd ? 'datetime' : (hasCronEnd ? 'cron' : 'duration'));
|
||||
$('#slide-edit-cron-schedule-end-trigger').val(hasDateTimeEnd ? 'datetime' : (hasCronEnd ? 'cron' : (isNotification ? 'duration' : 'stayloop')));
|
||||
|
||||
$('#slide-edit-cron-schedule-datetimepicker').toggleClass('hidden', !hasDateTime).val(
|
||||
hasDateTime ? getCronDateTime(slide.cron_schedule) : ''
|
||||
|
||||
@ -3,6 +3,11 @@ const prettyTimestamp = function(timestamp) {
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')} `
|
||||
};
|
||||
|
||||
const getCronDateTime = function(cronExpression) {
|
||||
const [minutes, hours, day, month, _, year] = cronExpression.split(' ');
|
||||
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const validateCronDateTime = function(cronExpression) {
|
||||
const pattern = /^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$/;
|
||||
return pattern.test(cronExpression);
|
||||
@ -28,4 +33,26 @@ const modifyDate = function(date, seconds) {
|
||||
const clone = new Date(date.getTime());
|
||||
clone.setSeconds(clone.getSeconds() + seconds);
|
||||
return clone;
|
||||
};
|
||||
};
|
||||
|
||||
const recreateSelectOptions = function($selectElement, options) {
|
||||
if (!$selectElement.is('select')) {
|
||||
throw new Error("Element is not a <select>");
|
||||
}
|
||||
|
||||
const selectedValue = $selectElement.val();
|
||||
$selectElement.empty();
|
||||
|
||||
$.each(options, function(key, label) {
|
||||
$selectElement.append($('<option>', {
|
||||
value: key,
|
||||
html: label
|
||||
}));
|
||||
});
|
||||
|
||||
if ($selectElement.find(`option[value="${selectedValue}"]`).length > 0) {
|
||||
$selectElement.val(selectedValue);
|
||||
} else {
|
||||
$selectElement.prop('selectedIndex', 0);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
select.select-item-picker,
|
||||
a.btn,
|
||||
.btn,
|
||||
button {
|
||||
background-color: $white;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -49,6 +49,12 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.panel td .td-secondary {
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.panel td a.item.sort {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
// Import base styles
|
||||
@import 'base/html';
|
||||
|
||||
@import 'base/tachyons';
|
||||
|
||||
// Import layout styles
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"slideshow_slide_panel_th_cron_scheduled": "Scheduled Start",
|
||||
"slideshow_slide_panel_th_activity": "Activity",
|
||||
"slideshow_slide_panel_td_cron_scheduled_loop": "Loop",
|
||||
"slideshow_slide_panel_td_cron_scheduled_notify": "Notify",
|
||||
"slideshow_slide_panel_td_cron_scheduled_bad_cron": "Bad cron value",
|
||||
"slideshow_slide_form_add_title": "Add Slide",
|
||||
"slideshow_slide_form_add_submit": "Add",
|
||||
@ -28,10 +29,12 @@
|
||||
"slideshow_slide_form_label_object": "Object",
|
||||
"slideshow_slide_form_label_duration": "Duration",
|
||||
"slideshow_slide_form_label_duration_unit": "seconds",
|
||||
"slideshow_slide_form_label_is_notification": "Act as notification",
|
||||
"slideshow_slide_form_label_cron_scheduled": "Start",
|
||||
"slideshow_slide_form_label_cron_scheduled_end": "End",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "In the loop",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "Always in loop",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration": "Duration",
|
||||
"slideshow_slide_form_label_cron_scheduled_stayloop": "Follow the loop",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration_unit": "seconds",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Time",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime_placeholder": "Set a date and time",
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"slideshow_slide_panel_th_cron_scheduled": "Programmation",
|
||||
"slideshow_slide_panel_th_activity": "Options",
|
||||
"slideshow_slide_panel_td_cron_scheduled_loop": "En boucle",
|
||||
"slideshow_slide_panel_td_cron_scheduled_notify": "Notifie",
|
||||
"slideshow_slide_panel_td_cron_scheduled_bad_cron": "Mauvaise valeur cron",
|
||||
"slideshow_slide_form_add_title": "Ajout d'une slide",
|
||||
"slideshow_slide_form_add_submit": "Ajouter",
|
||||
@ -28,10 +29,12 @@
|
||||
"slideshow_slide_form_label_object": "Objet",
|
||||
"slideshow_slide_form_label_duration": "Durée",
|
||||
"slideshow_slide_form_label_duration_unit": "secondes",
|
||||
"slideshow_slide_form_label_is_notification": "Agit comme une notification",
|
||||
"slideshow_slide_form_label_cron_scheduled": "Début",
|
||||
"slideshow_slide_form_label_cron_scheduled_end": "Fin",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "Dans la boucle",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "Toujours en boucle",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration": "Durée",
|
||||
"slideshow_slide_form_label_cron_scheduled_stayloop": "Suit la boucle",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration_unit": "secondes",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime": "Date & Heure",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime_placeholder": "Choisir une date et une heure",
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"slideshow_slide_panel_th_cron_scheduled": "Avvia programmazione",
|
||||
"slideshow_slide_panel_th_activity": "Attivita",
|
||||
"slideshow_slide_panel_td_cron_scheduled_loop": "Loop",
|
||||
"slideshow_slide_panel_td_cron_scheduled_notify": "Notificare",
|
||||
"slideshow_slide_panel_td_cron_scheduled_bad_cron": "Valore cron errato",
|
||||
"slideshow_slide_form_add_title": "Aggiungi Slide",
|
||||
"slideshow_slide_form_add_submit": "Aggiungi",
|
||||
@ -28,10 +29,12 @@
|
||||
"slideshow_slide_form_label_object": "Oggetto",
|
||||
"slideshow_slide_form_label_duration": "Durata",
|
||||
"slideshow_slide_form_label_duration_unit": "Secondi",
|
||||
"slideshow_slide_form_label_is_notification": "Agisce come una notifica",
|
||||
"slideshow_slide_form_label_cron_scheduled": "Inizio",
|
||||
"slideshow_slide_form_label_cron_scheduled_end": "Fine",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "Ciclo continuo",
|
||||
"slideshow_slide_form_label_cron_scheduled_loop": "Sempre in loop",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration": "Durata",
|
||||
"slideshow_slide_form_label_cron_scheduled_stayloop": "Seguire il ciclo",
|
||||
"slideshow_slide_form_label_cron_scheduled_duration_unit": "secondi",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime": "Data e ora",
|
||||
"slideshow_slide_form_label_cron_scheduled_datetime_placeholder": "Imposta Data e ora",
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from flask import Flask, render_template, redirect, request, url_for, send_from_directory, jsonify, abort
|
||||
|
||||
from src.service.ModelStore import ModelStore
|
||||
from src.interface.ObController import ObController
|
||||
from src.util.utils import get_safe_cron_descriptor
|
||||
from src.util.utils import get_safe_cron_descriptor, is_valid_cron_date_time, get_cron_date_time
|
||||
from src.util.UtilNetwork import get_ip_address, get_safe_remote_addr
|
||||
from src.model.enum.AnimationSpeed import animation_speed_duration
|
||||
|
||||
@ -18,20 +20,37 @@ class PlayerController(ObController):
|
||||
playlist = self._model_store.playlist().get(playlist_id)
|
||||
|
||||
playlist_loop = []
|
||||
playlist_cron = []
|
||||
playlist_notifications = []
|
||||
|
||||
for slide in slides:
|
||||
if 'cron_schedule' in slide and slide['cron_schedule']:
|
||||
if get_safe_cron_descriptor(slide['cron_schedule']):
|
||||
playlist_cron.append(slide)
|
||||
has_valid_start_date = 'cron_schedule' in slide and slide['cron_schedule'] and get_safe_cron_descriptor(slide['cron_schedule']) and is_valid_cron_date_time(slide['cron_schedule'])
|
||||
has_valid_end_date = 'cron_schedule_end' in slide and slide['cron_schedule_end'] and get_safe_cron_descriptor(slide['cron_schedule_end']) and is_valid_cron_date_time(slide['cron_schedule_end'])
|
||||
|
||||
if slide['is_notification'] and has_valid_start_date:
|
||||
if has_valid_start_date:
|
||||
playlist_notifications.append(slide)
|
||||
else:
|
||||
logging.warn('Slide {} is notification but start date is invalid'.format(slide['name']))
|
||||
else:
|
||||
playlist_loop.append(slide)
|
||||
if has_valid_start_date:
|
||||
start_date = get_cron_date_time(slide['cron_schedule'], object=True)
|
||||
if datetime.now() <= start_date:
|
||||
continue
|
||||
|
||||
if has_valid_end_date:
|
||||
end_date = get_cron_date_time(slide['cron_schedule_end'], object=True)
|
||||
if datetime.now() >= end_date:
|
||||
continue
|
||||
|
||||
playlist_loop.append(slide)
|
||||
else:
|
||||
playlist_loop.append(slide)
|
||||
|
||||
playlists = {
|
||||
'playlist_id': playlist.id if playlist else None,
|
||||
'time_sync': playlist.time_sync if playlist else self._model_store.variable().get_one_by_name("playlist_default_time_sync").as_bool(),
|
||||
'loop': playlist_loop,
|
||||
'cron': playlist_cron,
|
||||
'notifications': playlist_notifications,
|
||||
'hard_refresh_request': self._model_store.variable().get_one_by_name("refresh_player_request").as_int()
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ class SlideshowController(ObController):
|
||||
name=request.form['name'],
|
||||
type=str_to_enum(request.form['type'], SlideType),
|
||||
duration=request.form['duration'],
|
||||
is_notification=True if 'is_notification' in request.form else False,
|
||||
playlist_id=request.form['playlist_id'] if 'playlist_id' in request.form and request.form['playlist_id'] else None,
|
||||
cron_schedule=get_optional_string(request.form['cron_schedule']),
|
||||
cron_schedule_end=get_optional_string(request.form['cron_schedule_end']),
|
||||
@ -82,6 +83,7 @@ class SlideshowController(ObController):
|
||||
id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
duration=request.form['duration'],
|
||||
is_notification=True if 'is_notification' in request.form else False,
|
||||
cron_schedule=request.form['cron_schedule'],
|
||||
cron_schedule_end=request.form['cron_schedule_end'],
|
||||
location=request.form['location'] if 'location' in request.form and request.form['location'] else None
|
||||
|
||||
@ -20,6 +20,7 @@ class SlideManager(ModelManager):
|
||||
"name CHAR(255)",
|
||||
"type CHAR(30)",
|
||||
"enabled INTEGER DEFAULT 0",
|
||||
"is_notification INTEGER DEFAULT 0",
|
||||
"playlist_id INTEGER",
|
||||
"duration INTEGER",
|
||||
"position INTEGER",
|
||||
@ -116,7 +117,7 @@ class SlideManager(ModelManager):
|
||||
for slide_id, slide_position in positions.items():
|
||||
self._db.update_by_id(self.TABLE_NAME, slide_id, {"position": slide_position})
|
||||
|
||||
def update_form(self, id: int, name: str, duration: int, cron_schedule: Optional[str] = '', cron_schedule_end: Optional[str] = '', location: Optional[str] = None) -> Slide:
|
||||
def update_form(self, id: int, name: str, duration: int, is_notification: bool = False, cron_schedule: Optional[str] = '', cron_schedule_end: Optional[str] = '', location: Optional[str] = None) -> Slide:
|
||||
slide = self.get(id)
|
||||
|
||||
if not slide:
|
||||
@ -125,6 +126,7 @@ class SlideManager(ModelManager):
|
||||
form = {
|
||||
"name": name,
|
||||
"duration": duration,
|
||||
"is_notification": True if is_notification else False,
|
||||
"cron_schedule": get_optional_string(cron_schedule),
|
||||
"cron_schedule_end": get_optional_string(cron_schedule_end)
|
||||
}
|
||||
|
||||
@ -8,13 +8,14 @@ from src.util.utils import str_to_enum
|
||||
|
||||
class Slide:
|
||||
|
||||
def __init__(self, location: str = '', playlist_id: Optional[int] = None, duration: int = 3, type: Union[SlideType, str] = SlideType.URL, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[int] = None, cron_schedule: Optional[str] = None, cron_schedule_end: Optional[str] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
||||
def __init__(self, location: str = '', playlist_id: Optional[int] = None, duration: int = 3, type: Union[SlideType, str] = SlideType.URL, is_notification: bool = False, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[int] = None, cron_schedule: Optional[str] = None, cron_schedule_end: Optional[str] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
||||
self._id = id if id else None
|
||||
self._location = location
|
||||
self._playlist_id = playlist_id
|
||||
self._duration = duration
|
||||
self._type = str_to_enum(type, SlideType) if isinstance(type, str) else type
|
||||
self._enabled = enabled
|
||||
self._is_notification = is_notification
|
||||
self._name = name
|
||||
self._position = position
|
||||
self._cron_schedule = cron_schedule
|
||||
@ -116,6 +117,14 @@ class Slide:
|
||||
def enabled(self, value: bool):
|
||||
self._enabled = bool(value)
|
||||
|
||||
@property
|
||||
def is_notification(self) -> bool:
|
||||
return bool(self._is_notification)
|
||||
|
||||
@is_notification.setter
|
||||
def is_notification(self, value: bool):
|
||||
self._is_notification = bool(value)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
@ -138,6 +147,7 @@ class Slide:
|
||||
f"name='{self.name}',\n" \
|
||||
f"type='{self.type}',\n" \
|
||||
f"enabled='{self.enabled}',\n" \
|
||||
f"is_notification='{self.is_notification}',\n" \
|
||||
f"duration='{self.duration}',\n" \
|
||||
f"position='{self.position}',\n" \
|
||||
f"location='{self.location}',\n" \
|
||||
@ -163,6 +173,7 @@ class Slide:
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"enabled": self.enabled,
|
||||
"is_notification": self.is_notification,
|
||||
"position": self.position,
|
||||
"type": self.type.value,
|
||||
"duration": self.duration,
|
||||
|
||||
@ -9,7 +9,7 @@ from src.model.hook.HookRegistration import HookRegistration
|
||||
from src.model.hook.StaticHookRegistration import StaticHookRegistration
|
||||
from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
|
||||
from src.constant.WebDirConstant import WebDirConstant
|
||||
from src.util.utils import get_safe_cron_descriptor, is_validate_cron_date_time, seconds_to_hhmmss, am_i_in_docker
|
||||
from src.util.utils import get_safe_cron_descriptor, is_valid_cron_date_time, seconds_to_hhmmss, am_i_in_docker
|
||||
|
||||
|
||||
class TemplateRenderer:
|
||||
@ -39,7 +39,7 @@ class TemplateRenderer:
|
||||
cron_descriptor=self.cron_descriptor,
|
||||
str=str,
|
||||
seconds_to_hhmmss=seconds_to_hhmmss,
|
||||
is_validate_cron_date_time=is_validate_cron_date_time,
|
||||
is_valid_cron_date_time=is_valid_cron_date_time,
|
||||
l=self._model_store.lang().map(),
|
||||
t=self._model_store.lang().translate,
|
||||
)
|
||||
|
||||
@ -8,6 +8,7 @@ import unicodedata
|
||||
import platform
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict
|
||||
from enum import Enum
|
||||
from cron_descriptor import ExpressionDescriptor
|
||||
@ -58,13 +59,19 @@ def camel_to_snake(camel: str) -> str:
|
||||
return CAMEL_CASE_TO_SNAKE_CASE_PATTERN.sub('_', camel).lower()
|
||||
|
||||
|
||||
def is_validate_cron_date_time(expression) -> bool:
|
||||
def is_valid_cron_date_time(expression: str) -> bool:
|
||||
pattern = re.compile(r'^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+\*\s+(\d+)$')
|
||||
return bool(pattern.match(expression))
|
||||
|
||||
|
||||
def get_cron_date_time(cron_expression: str, object: bool) -> str:
|
||||
minutes, hours, day, month, _, year = cron_expression.split(' ')
|
||||
formatted_date_time = f"{year}-{month.zfill(2)}-{day.zfill(2)} {hours.zfill(2)}:{minutes.zfill(2)}"
|
||||
return datetime.strptime(formatted_date_time, '%Y-%m-%d %H:%M') if object else formatted_date_time
|
||||
|
||||
|
||||
def get_safe_cron_descriptor(expression: str, use_24hour_time_format=True, locale_code: Optional[str] = None) -> str:
|
||||
if is_validate_cron_date_time(expression):
|
||||
if is_valid_cron_date_time(expression):
|
||||
[minutes, hours, day, month, _, year] = expression.split(' ')
|
||||
return "{}-{}-{} at {}:{}".format(
|
||||
year,
|
||||
@ -233,4 +240,4 @@ def restart(debug: bool) -> None:
|
||||
pass
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
1.19.5
|
||||
1.20.0
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<iframe src="/player/default"></iframe>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="CronSlide" class="slide" style="z-index: 0;">
|
||||
<div id="NotificationSlide" class="slide" style="z-index: 0;">
|
||||
|
||||
</div>
|
||||
<div id="FirstSlide" class="slide slide-loop" style="z-index: 500;">
|
||||
@ -74,12 +74,12 @@
|
||||
let curItemIndex = -1;
|
||||
let secondsBeforeNext = 0;
|
||||
const introSlide = document.getElementById('IntroSlide');
|
||||
const cronSlide = document.getElementById('CronSlide');
|
||||
const notificationSlide = document.getElementById('NotificationSlide');
|
||||
const firstSlide = document.getElementById('FirstSlide');
|
||||
const secondSlide = document.getElementById('SecondSlide');
|
||||
let curSlide = secondSlide;
|
||||
let nextSlide = firstSlide;
|
||||
let cronItemIndex = null;
|
||||
let notificationItemIndex = null;
|
||||
|
||||
// Functions
|
||||
const itemCheck = setInterval(function() {
|
||||
@ -189,7 +189,7 @@
|
||||
};
|
||||
|
||||
const main = function() {
|
||||
setInterval(checkAndMoveCron, 1000);
|
||||
setInterval(checkAndMoveNotifications, 1000);
|
||||
setTimeout(function() {
|
||||
if (items.loop.length === 0) {
|
||||
return setTimeout(main, 5000);
|
||||
@ -372,9 +372,9 @@
|
||||
setTimeout(autoplayLoader, delayNoisyContentJIT);
|
||||
}
|
||||
|
||||
const checkAndMoveCron = function() {
|
||||
for (let i = 0; i < items.cron.length; i++) {
|
||||
const item = items.cron[i];
|
||||
const checkAndMoveNotifications = function() {
|
||||
for (let i = 0; i < items.notifications.length; i++) {
|
||||
const item = items.notifications[i];
|
||||
|
||||
const now = new Date();
|
||||
const isFullyElapsedMinute = (new Date()).getSeconds() === 0;
|
||||
@ -383,52 +383,52 @@
|
||||
const hasDateTime = hasCron && validateCronDateTime(item.cron_schedule);
|
||||
const hasDateTimeEnd = hasCronEnd && validateCronDateTime(item.cron_schedule_end);
|
||||
|
||||
if (cronItemIndex !== i && hasDateTime) {
|
||||
if (notificationItemIndex !== i && hasDateTime) {
|
||||
const startDate = cronToDateTimeObject(item.cron_schedule);
|
||||
const endDate = hasDateTimeEnd ? cronToDateTimeObject(item.cron_schedule_end) : modifyDate(startDate, item.duration);
|
||||
|
||||
if (now >= startDate && now < endDate) {
|
||||
moveToCronSlide(i);
|
||||
moveToNotificationSlide(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (cronItemIndex === i && hasDateTime) {
|
||||
if (notificationItemIndex === i && hasDateTime) {
|
||||
const startDate = cronToDateTimeObject(item.cron_schedule);
|
||||
const endDate = hasDateTimeEnd ? cronToDateTimeObject(item.cron_schedule_end) : modifyDate(startDate, item.duration);
|
||||
|
||||
if (now >= endDate) {
|
||||
stopCronSlide();
|
||||
stopNotificationSlide();
|
||||
}
|
||||
}
|
||||
|
||||
if (cronItemIndex !== i && isFullyElapsedMinute && hasCron && cron.isActive(item.cron_schedule)) {
|
||||
moveToCronSlide(i);
|
||||
if (notificationItemIndex !== i && isFullyElapsedMinute && hasCron && cron.isActive(item.cron_schedule)) {
|
||||
moveToNotificationSlide(i);
|
||||
}
|
||||
|
||||
if (cronItemIndex === i && isFullyElapsedMinute && hasCronEnd && cron.isActive(item.cron_schedule_end)) {
|
||||
stopCronSlide();
|
||||
if (notificationItemIndex === i && isFullyElapsedMinute && hasCronEnd && cron.isActive(item.cron_schedule_end)) {
|
||||
stopNotificationSlide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const moveToCronSlide = function(cronSlideIndex) {
|
||||
const item = items.cron[cronSlideIndex];
|
||||
cronItemIndex = cronSlideIndex;
|
||||
const moveToNotificationSlide = function(notificationSlideIndex) {
|
||||
const item = items.notifications[notificationSlideIndex];
|
||||
notificationItemIndex = notificationSlideIndex;
|
||||
pause();
|
||||
const callbackReady = function() {
|
||||
cronSlide.style.zIndex = '2000';
|
||||
notificationSlide.style.zIndex = '2000';
|
||||
if (!item.cron_schedule_end) {
|
||||
setTimeout(function() {
|
||||
stopCronSlide();
|
||||
stopNotificationSlide();
|
||||
}, safe_duration(item) * 1000);
|
||||
}
|
||||
};
|
||||
loadContent(cronSlide, callbackReady, item);
|
||||
loadContent(notificationSlide, callbackReady, item);
|
||||
};
|
||||
|
||||
const stopCronSlide = function() {
|
||||
cronItemIndex = null;
|
||||
cronSlide.style.zIndex = '0';
|
||||
const stopNotificationSlide = function() {
|
||||
notificationItemIndex = null;
|
||||
notificationSlide.style.zIndex = '0';
|
||||
play();
|
||||
};
|
||||
|
||||
|
||||
@ -61,8 +61,12 @@
|
||||
{% if slide.cron_schedule %}
|
||||
{% set cron_desc = cron_descriptor(slide.cron_schedule) %}
|
||||
{% if cron_desc %}
|
||||
{% if is_validate_cron_date_time(slide.cron_schedule) %}
|
||||
📆 {{ cron_desc }}
|
||||
{% if is_valid_cron_date_time(slide.cron_schedule) %}
|
||||
{% if slide.is_notification %}
|
||||
🔔 {{ l.slideshow_slide_panel_td_cron_scheduled_notify }} <span class="td-secondary">{{ cron_desc }}</span>
|
||||
{% else %}
|
||||
🔄 {{ l.slideshow_slide_panel_td_cron_scheduled_loop }}<span class="td-secondary">{{ cron_desc }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
⏳ {{ cron_desc }}
|
||||
{% endif %}
|
||||
@ -77,8 +81,12 @@
|
||||
{% if slide.cron_schedule_end %}
|
||||
{% set cron_desc_end = cron_descriptor(slide.cron_schedule_end) %}
|
||||
{% if cron_desc_end %}
|
||||
{% if is_validate_cron_date_time(slide.cron_schedule_end) %}
|
||||
📆 {{ cron_desc_end }}
|
||||
{% if is_valid_cron_date_time(slide.cron_schedule_end) %}
|
||||
{% if slide.is_notification %}
|
||||
📆<span class="td-secondary">{{ cron_desc_end }}</span>
|
||||
{% else %}
|
||||
⏱️ {{ slide.duration }} {{ l.slideshow_slide_panel_th_duration_unit }}<span class="td-secondary">{{ cron_desc_end }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
⏳ {{ cron_desc_end }}
|
||||
{% endif %}
|
||||
|
||||
@ -10,6 +10,18 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block add_js %}
|
||||
<script type="text/javascript">
|
||||
var schedule_start_choices = {
|
||||
'loop': '{{ l.slideshow_slide_form_label_cron_scheduled_loop }}',
|
||||
'datetime': '{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}',
|
||||
'cron': '{{ l.slideshow_slide_form_label_cron_scheduled_cron }}',
|
||||
};
|
||||
var schedule_end_choices = {
|
||||
'stayloop': '{{ l.slideshow_slide_form_label_cron_scheduled_stayloop }}',
|
||||
'datetime': '{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}',
|
||||
'duration': '{{ l.slideshow_slide_form_label_cron_scheduled_duration }}',
|
||||
};
|
||||
</script>
|
||||
<script src="{{ STATIC_PREFIX }}js/lib/flatpickr.min.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/lib/tablednd-fixed.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/slideshow/slides.js"></script>
|
||||
@ -31,6 +43,7 @@
|
||||
<a href="{% if current_playlist %}{{ url_for('player_use', playlist_slug_or_id=current_playlist.slug) }}{% else %}{{ url_for('player') }}{% endif %}" target="_blank" class="btn" title="{{ l.slideshow_goto_player }}">
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('slideshow_player_refresh') }}" class="btn" title="{{ l.slideshow_refresh_player }}">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</a>
|
||||
@ -81,7 +94,6 @@
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modals hidden">
|
||||
<div class="modals-outer">
|
||||
|
||||
@ -45,6 +45,14 @@
|
||||
{{ l.slideshow_slide_form_section_scheduling }}
|
||||
</h3>
|
||||
|
||||
<div class="form-group slide-notification-group">
|
||||
<label for="slide-add-is-notification">{{ l.slideshow_slide_form_label_is_notification }}</label>
|
||||
<div class="widget">
|
||||
<input type="checkbox" class="trigger slide-is-notification" name="is_notification" id="slide-add-is-notification" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group slide-schedule-group">
|
||||
<label for="slide-add-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
|
||||
<div class="widget">
|
||||
|
||||
@ -43,6 +43,13 @@
|
||||
{{ l.slideshow_slide_form_section_scheduling }}
|
||||
</h3>
|
||||
|
||||
<div class="form-group slide-notification-group">
|
||||
<label for="slide-edit-is-notification">{{ l.slideshow_slide_form_label_is_notification }}</label>
|
||||
<div class="widget">
|
||||
<input type="checkbox" class="trigger slide-is-notification" name="is_notification" id="slide-edit-is-notification" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group slide-schedule-group">
|
||||
<label for="slide-edit-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
|
||||
<div class="widget">
|
||||
@ -62,7 +69,6 @@
|
||||
<select id="slide-edit-cron-schedule-end-trigger" class="trigger">
|
||||
<option value="duration">{{ l.slideshow_slide_form_label_cron_scheduled_duration }}</option>
|
||||
<option value="datetime">{{ l.slideshow_slide_form_label_cron_scheduled_datetime }}</option>
|
||||
<option value="cron">{{ l.slideshow_slide_form_label_cron_scheduled_cron }}</option>
|
||||
</select>
|
||||
<input type="text" id="slide-edit-cron-schedule-end-datetimepicker" class="datetimepicker" value="" placeholder="{{ l.slideshow_slide_form_label_cron_scheduled_datetime_placeholder }}" />
|
||||
<input type="text" name="cron_schedule_end" id="slide-edit-cron-schedule-end" class="target hidden" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user