Release v2.4.2

This commit is contained in:
2026-08-03 12:45:27 +01:00
parent 2b9cabdab2
commit e95928f31c
141 changed files with 7694 additions and 5078 deletions
+48 -24
View File
@@ -60,7 +60,7 @@ function createPlayerPlaylistService(options) {
try {
const [screenRows] = await pool.query('SELECT id, name, slug, playlist_id, created_at, modified_at, created_by, modified_by FROM d_screens WHERE slug = ?', [slug]);
if (!screenRows.length) {
return { screen: null, playlist: null, slides: [], rssFeeds: [], apiSources: [], scheduleGroups: [] };
return { screen: null, playlist: null, slides: [], rssFeeds: [], apiSources: [], timetableGroups: [] };
}
const screen = screenRows[0];
@@ -71,8 +71,8 @@ function createPlayerPlaylistService(options) {
slides: [],
rssFeeds: [],
apiSources: [],
scheduleGroups: [],
revision: getPlaylistRevision(screen, null, [], [], [], [], [])
timetableGroups: [],
revision: getPlaylistRevision(screen, null, [], [], [], [], [], [])
};
await writeSnapshot(slug, payloadWithoutPlaylist);
return payloadWithoutPlaylist;
@@ -82,7 +82,7 @@ function createPlayerPlaylistService(options) {
const playlist = playlistRows[0] || null;
const [slideRows] = await pool.query(`
SELECT sl.id, sl.title, sl.template_id, sl.content_json, sl.created_at, sl.modified_at,
ps.position, ps.duration_seconds AS duration_seconds, ps.use_video_duration, ps.schedule_mode, ps.schedule_start_datetime, ps.schedule_end_datetime, ps.schedule_start_time, ps.schedule_end_time, ps.schedule_days_json,
ps.position, ps.duration_seconds AS duration_seconds, ps.use_video_duration, ps.disable_audio,
st.name AS template_name, cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height, cs.width AS canvas_width, cs.height AS canvas_height
FROM c_playlist_slides ps
JOIN c_slides sl ON sl.id = ps.slide_id
@@ -91,6 +91,21 @@ function createPlayerPlaylistService(options) {
WHERE ps.playlist_id = ?
ORDER BY ps.position ASC, ps.id ASC
`, [screen.playlist_id]);
const [scheduleRuleRows] = await pool.query(`
SELECT r.id, r.playlist_slide_id, r.position, r.start_datetime, r.end_datetime, r.start_time, r.end_time, r.schedule_days_json, r.created_at, r.modified_at, r.created_by, r.modified_by
FROM c_playlist_slide_schedule_rules r
JOIN c_playlist_slides ps ON ps.id = r.playlist_slide_id
WHERE ps.playlist_id = ?
ORDER BY r.playlist_slide_id ASC, r.position ASC, r.id ASC
`, [screen.playlist_id]);
const rulesBySlideId = {};
scheduleRuleRows.forEach(function (rule) {
const slideId = Number(rule.playlist_slide_id);
if (!rulesBySlideId[slideId]) {
rulesBySlideId[slideId] = [];
}
rulesBySlideId[slideId].push(rule);
});
const templateIds = slideRows
.filter(function (slide) { return slide.template_id; })
@@ -138,10 +153,17 @@ function createPlayerPlaylistService(options) {
const slides = slideRows.map(function (slide) {
const storedDuration = Number(slide.duration_seconds || 0);
const videoDuration = slide.use_video_duration ? getVideoRegionDurationSeconds(slide.content_json) : null;
const disableAudio = slide.disable_audio === undefined || slide.disable_audio === null ? true : Boolean(slide.disable_audio);
const videoCacheBust = String(slide.modified_at || slide.content_json || slide.id || '');
const content = common.parseJsonSafe(slide.content_json) || {};
Object.keys(content).forEach(function (key) {
const region = content[key];
if (region && typeof region === 'object') {
const regionType = String(region.type || '').trim().toLowerCase();
if (regionType === 'video' || regionType === 'rtmp') {
region.disable_audio = disableAudio;
}
}
if (region && typeof region === 'object' && String(region.type || '').trim().toLowerCase() === 'video') {
region.cache_bust = videoCacheBust;
}
@@ -152,12 +174,8 @@ function createPlayerPlaylistService(options) {
modified_at: slide.modified_at,
duration_seconds: videoDuration || storedDuration,
use_video_duration: Boolean(slide.use_video_duration),
schedule_mode: slide.schedule_mode,
schedule_start_datetime: slide.schedule_start_datetime,
schedule_end_datetime: slide.schedule_end_datetime,
schedule_start_time: slide.schedule_start_time,
schedule_end_time: slide.schedule_end_time,
schedule_days_json: slide.schedule_days_json,
disable_audio: disableAudio,
scheduleRules: rulesBySlideId[Number(slide.id)] || [],
template_id: slide.template_id,
template: slide.template_id ? templatesById[slide.template_id] || null : null,
content: content
@@ -187,14 +205,14 @@ function createPlayerPlaylistService(options) {
});
}
let scheduleGroups = [];
if (typeof common.fetchSchedulesData === 'function') {
const scheduleData = await common.fetchSchedulesData(pool);
scheduleGroups = Array.isArray(scheduleData && scheduleData.scheduleGroups) ? scheduleData.scheduleGroups : [];
let timetableGroups = [];
if (typeof common.fetchTimetablesData === 'function') {
const timetableData = await common.fetchTimetablesData(pool);
timetableGroups = Array.isArray(timetableData && timetableData.timetableGroups) ? timetableData.timetableGroups : [];
}
const revision = getPlaylistRevision(screen, playlist, slideRows, templateRows, regionRows, rssFeeds, apiSources, scheduleGroups);
const payload = { screen: screen, playlist: playlist, slides: slides, rssFeeds: rssFeeds, apiSources: apiSources, scheduleGroups: scheduleGroups, revision: revision };
const revision = getPlaylistRevision(screen, playlist, slideRows, scheduleRuleRows, templateRows, regionRows, rssFeeds, apiSources, timetableGroups);
const payload = { screen: screen, playlist: playlist, slides: slides, rssFeeds: rssFeeds, apiSources: apiSources, timetableGroups: timetableGroups, revision: revision };
await writeSnapshot(slug, payload);
return payload;
} catch (error) {
@@ -211,7 +229,7 @@ function createPlayerPlaylistService(options) {
hash.update('\0');
}
function getPlaylistRevision(screen, playlist, slideRows, templateRows, regionRows, rssFeeds, apiSources, scheduleGroups) {
function getPlaylistRevision(screen, playlist, slideRows, scheduleRuleRows, templateRows, regionRows, rssFeeds, apiSources, timetableGroups) {
const hash = crypto.createHash('sha1');
updatePlaylistRevisionHash(hash, screen && screen.id);
@@ -232,12 +250,18 @@ function createPlayerPlaylistService(options) {
updatePlaylistRevisionHash(hash, slide.position);
updatePlaylistRevisionHash(hash, slide.duration_seconds);
updatePlaylistRevisionHash(hash, slide.use_video_duration);
updatePlaylistRevisionHash(hash, slide.schedule_mode);
updatePlaylistRevisionHash(hash, slide.schedule_start_datetime);
updatePlaylistRevisionHash(hash, slide.schedule_end_datetime);
updatePlaylistRevisionHash(hash, slide.schedule_start_time);
updatePlaylistRevisionHash(hash, slide.schedule_end_time);
updatePlaylistRevisionHash(hash, slide.schedule_days_json);
updatePlaylistRevisionHash(hash, slide.disable_audio);
});
(Array.isArray(scheduleRuleRows) ? scheduleRuleRows : []).forEach(function (rule) {
updatePlaylistRevisionHash(hash, rule.id);
updatePlaylistRevisionHash(hash, rule.playlist_slide_id);
updatePlaylistRevisionHash(hash, rule.position);
updatePlaylistRevisionHash(hash, rule.start_datetime);
updatePlaylistRevisionHash(hash, rule.end_datetime);
updatePlaylistRevisionHash(hash, rule.start_time);
updatePlaylistRevisionHash(hash, rule.end_time);
updatePlaylistRevisionHash(hash, rule.schedule_days_json);
});
(Array.isArray(templateRows) ? templateRows : []).forEach(function (template) {
@@ -268,7 +292,7 @@ function createPlayerPlaylistService(options) {
updatePlaylistRevisionHash(hash, JSON.stringify(rssFeeds || []));
updatePlaylistRevisionHash(hash, JSON.stringify(apiSources || []));
updatePlaylistRevisionHash(hash, JSON.stringify(scheduleGroups || []));
updatePlaylistRevisionHash(hash, JSON.stringify(timetableGroups || []));
return hash.digest('hex');
}
+1 -1
View File
@@ -165,7 +165,7 @@ function renderSlideMarkup(markup, shouldFade) {
video.autoplay = true;
video.loop = true;
video.muted = true;
video.muted = !(video.dataset && video.dataset.disableAudio === '0');
video.playsInline = true;
function startPlayback() {
+81 -18
View File
@@ -340,31 +340,82 @@ function parseTimeToMinutes(value) {
return Number(match[1]) * 60 + Number(match[2]);
}
// Determine whether a slide should be shown at the current time.
function isSlideActive(slide, now) {
const mode = String(slide.schedule_mode || 'always');
if (mode === 'always') {
return true;
function normalizeScheduleRule(rule) {
const input = rule && typeof rule === 'object' ? rule : {};
const startDatetime = String(input.start_datetime || input.startDateTime || '').trim();
const endDatetime = String(input.end_datetime || input.endDateTime || '').trim();
const startTime = String(input.start_time || input.startTime || '').trim();
const endTime = String(input.end_time || input.endTime || '').trim();
const days = parseScheduleDays(input.days);
if (startDatetime || endDatetime) {
if (!startDatetime || !endDatetime) {
return null;
}
}
if (mode === 'dates') {
const start = slide.schedule_start_datetime ? new Date(slide.schedule_start_datetime) : null;
const end = slide.schedule_end_datetime ? new Date(slide.schedule_end_datetime) : null;
if (!start || !end || Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
return false;
if (startTime || endTime) {
if (!startTime || !endTime) {
return null;
}
return now >= start && now <= end;
}
if (mode === 'times') {
const days = parseScheduleDays(slide.schedule_days_json);
if (!days.length) {
if (!startDatetime && !endDatetime && !startTime && !endTime && !days.length) {
return null;
}
return {
start_datetime: startDatetime || null,
end_datetime: endDatetime || null,
start_time: startTime || null,
end_time: endTime || null,
days: days
};
}
function parseScheduleRules(value) {
let rawRules = [];
if (Array.isArray(value)) {
rawRules = value;
} else {
const raw = String(value || '').trim();
if (!raw) {
return [];
}
try {
const parsed = JSON.parse(raw);
rawRules = Array.isArray(parsed) ? parsed : [];
} catch (_error) {
return [];
}
}
return rawRules.map(normalizeScheduleRule).filter(Boolean);
}
function matchesScheduleRule(rule, now) {
const normalized = normalizeScheduleRule(rule);
if (!normalized) {
return false;
}
if (normalized.start_datetime && normalized.end_datetime) {
const start = new Date(normalized.start_datetime);
const end = new Date(normalized.end_datetime);
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
return false;
}
const day = now.getDay();
if (days.indexOf(day) === -1) {
if (now < start || now > end) {
return false;
}
const startMinutes = parseTimeToMinutes(slide.schedule_start_time);
const endMinutes = parseTimeToMinutes(slide.schedule_end_time);
}
if (normalized.days.length && normalized.days.indexOf(now.getDay()) === -1) {
return false;
}
if (normalized.start_time && normalized.end_time) {
const startMinutes = parseTimeToMinutes(normalized.start_time);
const endMinutes = parseTimeToMinutes(normalized.end_time);
if (startMinutes === null || endMinutes === null) {
return false;
}
@@ -374,8 +425,20 @@ function isSlideActive(slide, now) {
}
return nowMinutes >= startMinutes || nowMinutes <= endMinutes;
}
return true;
}
// Determine whether a slide should be shown at the current time.
function isSlideActive(slide, now) {
const rules = Array.isArray(slide.scheduleRules) ? slide.scheduleRules : [];
if (!rules.length) {
return true;
}
return rules.some(function (rule) {
return matchesScheduleRule(rule, now);
});
}
// Build the cache key for a template layout.
function getTemplateLayoutCacheKey(template) {
var viewportKey = window.innerWidth + 'x' + window.innerHeight;
+1 -1
View File
@@ -8,7 +8,7 @@ function renderRtmpRegion(region, regionContent) {
return '';
}
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><video class="template-region-rtmp-video" data-rtmp-source="' + escapeHtml(url) + '" data-rtmp-disable-audio="' + (disableAudio ? '1' : '0') + '" data-rtmp-skip-unavailable="' + (skipUnavailable ? '1' : '0') + '" autoplay playsinline preload="auto" tabindex="-1" disablepictureinpicture></video><div class="template-region-placeholder template-region-rtmp-placeholder">Loading RTMP stream...</div></div>';
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><video class="template-region-rtmp-video" data-rtmp-source="' + escapeHtml(url) + '" data-rtmp-disable-audio="' + (disableAudio ? '1' : '0') + '" data-rtmp-skip-unavailable="' + (skipUnavailable ? '1' : '0') + '" autoplay playsinline preload="auto" tabindex="-1" disablepictureinpicture' + (disableAudio ? ' muted' : '') + '></video><div class="template-region-placeholder template-region-rtmp-placeholder">Loading RTMP stream...</div></div>';
}
function getRtmpSessionUrl(sourceUrl, disableAudio) {
+22 -15
View File
@@ -1,4 +1,4 @@
// Schedule region rendering for live playback.
// Timetable region rendering for live playback.
var registry = window.pulsePlayerRegionTypes;
@@ -97,7 +97,7 @@ function sanitizeRichText(html) {
});
}
function substituteScheduleVariables(html, entry) {
function substituteTimetableVariables(html, entry) {
var source = String(html || '');
return source.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, function (_match, expression) {
if (!entry || typeof entry !== 'object') {
@@ -117,16 +117,16 @@ function renderTemplate(template, context) {
if (!source) {
return '';
}
return substituteScheduleVariables(source, context);
return substituteTimetableVariables(source, context);
}
function getScheduleGroups() {
return Array.isArray(initialData && initialData.scheduleGroups) ? initialData.scheduleGroups : [];
function getTimetableGroups() {
return Array.isArray(initialData && initialData.timetableGroups) ? initialData.timetableGroups : [];
}
function getGroupById(groupId, groups) {
var normalizedId = Number(groupId || 0);
return (Array.isArray(groups) ? groups : getScheduleGroups()).find(function (group) {
return (Array.isArray(groups) ? groups : getTimetableGroups()).find(function (group) {
return Number(group.id) === normalizedId;
}) || null;
}
@@ -174,6 +174,12 @@ function getVisibleEntries(groupId, displayMode, maxItems, groups) {
return isUpcoming(entry, now);
});
if (!entries.length && mode === 'upcoming') {
entries = getEntries(groupId, groups).filter(function (entry) {
return isLive(entry, now);
});
}
return entries.slice(0, Math.max(1, Number(maxItems || 5)));
}
@@ -216,22 +222,23 @@ function getTextStyle(region, regionContent) {
function renderRegion(region, regionContent) {
var value = String(regionContent && (regionContent.value !== undefined ? regionContent.value : regionContent.text !== undefined ? regionContent.text : '') || '').trim();
var style = getTextStyle(region, regionContent || {});
var groups = getScheduleGroups();
var groupId = regionContent && regionContent.schedule_group_id !== undefined ? regionContent.schedule_group_id : '';
var groups = getTimetableGroups();
var groupId = regionContent && regionContent.timetable_group_id !== undefined ? regionContent.timetable_group_id : '';
var displayMode = regionContent && regionContent.display_mode ? regionContent.display_mode : 'upcoming';
var maxItems = regionContent && regionContent.max_items !== undefined ? regionContent.max_items : 5;
var group = getGroupById(groupId, groups);
var entries = getVisibleEntries(groupId, displayMode, maxItems, groups);
if (!entries.length) {
entries = getEntries(groupId, groups).slice(0, Math.max(1, Number(maxItems || 5)));
}
var width = Math.max(1, Math.round(Number(region && region.pixelWidth ? region.pixelWidth : 0) || 1));
var height = Math.max(1, Math.round(Number(region && region.pixelHeight ? region.pixelHeight : 0) || 1));
var canvasScale = Number(region && region.canvasScale ? region.canvasScale : 1) || 1;
var contentStyle = 'width:' + width + 'px;height:' + height + 'px;transform:scale(' + canvasScale + ');transform-origin:top left;' + (style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '') + (style.font_size ? 'font-size:' + Math.max(1, Math.round(Number(style.font_size))) + 'px;' : '') + (style.font_color ? 'color:' + escapeHtml(style.font_color) + ';' : '');
if (!value) {
return '<div class="template-region schedule" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="width:100%;height:100%;overflow:hidden;' + (style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '') + (style.font_size ? 'font-size:' + Math.max(1, Math.round(Number(style.font_size))) + 'px;' : '') + (style.font_color ? 'color:' + escapeHtml(style.font_color) + ';' : '') + '"></div></div>';
return '<div class="template-region timetable" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + ' overflow:hidden;"></div></div>';
}
return '<div class="template-region schedule" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="width:100%;height:100%;overflow:hidden;' + (style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '') + (style.font_size ? 'font-size:' + Math.max(1, Math.round(Number(style.font_size))) + 'px;' : '') + (style.font_color ? 'color:' + escapeHtml(style.font_color) + ';' : '') + '">' + entries.map(function (entry, index) {
return '<div class="schedule-region-entry" data-schedule-entry-index="' + index + '">' + sanitizeRichText(substituteScheduleVariables(value, Object.assign({}, entry || {}, {
return '<div class="template-region timetable" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + ' overflow:hidden;">' + entries.map(function (entry, index) {
return '<div class="timetable-region-entry" data-timetable-entry-index="' + index + '">' + sanitizeRichText(substituteTimetableVariables(value, Object.assign({}, entry || {}, {
start: entry && entry.start_datetime !== undefined ? entry.start_datetime : '',
end: entry && entry.end_datetime !== undefined ? entry.end_datetime : '',
group: group || {},
@@ -241,6 +248,6 @@ function renderRegion(region, regionContent) {
}).join('') + '</div></div>';
}
registry.register('schedule', {
registry.register('timetable', {
renderRegion: renderRegion
});
+4 -3
View File
@@ -111,6 +111,7 @@ function renderVideoRegion(region, regionContent) {
var regionKey = getVideoRegionCacheKey(region);
var cachedSrc = regionKey ? String(videoRegionLastGoodSrcCache[regionKey] || '').trim() : '';
var cachedSrcVersioned = appendCacheBust(cachedSrc, regionContent && regionContent.cache_bust);
var disableAudio = regionContent && regionContent.disable_audio === undefined ? true : Boolean(regionContent && regionContent.disable_audio);
if (!requestedSrc) {
return '';
@@ -121,7 +122,7 @@ function renderVideoRegion(region, regionContent) {
videoRegionLastGoodSrcCache[regionKey] = requestedSrc;
}
setVideoSourceAvailability(requestedSrc, true);
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(requestedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" autoplay muted loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(requestedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" data-disable-audio="' + (disableAudio ? '1' : '0') + '" autoplay' + (disableAudio ? ' muted' : '') + ' loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
}
var requestedState = getVideoSourceAvailability(requestedSrc);
@@ -131,7 +132,7 @@ function renderVideoRegion(region, regionContent) {
if (regionKey) {
videoRegionLastGoodSrcCache[regionKey] = requestedSrc;
}
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(requestedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" autoplay muted loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(requestedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" data-disable-audio="' + (disableAudio ? '1' : '0') + '" autoplay' + (disableAudio ? ' muted' : '') + ' loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
}
scheduleVideoSourceProbe(regionKey, requestedSrc, false);
@@ -140,7 +141,7 @@ function renderVideoRegion(region, regionContent) {
if (cachedSrc !== requestedSrc) {
logVideoRegionStatus('Keeping the previous playable video until the new mirrored file finishes transferring.', 'region=' + regionKey + ' old=' + cachedSrc + ' new=' + requestedSrc);
}
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(cachedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" autoplay muted loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
return '<div class="template-region video" style="' + region.baseStyle + '"><video src="' + escapeHtml(cachedSrcVersioned) + '" title="' + escapeHtml(region.label) + '" data-disable-audio="' + (disableAudio ? '1' : '0') + '" autoplay' + (disableAudio ? ' muted' : '') + ' loop playsinline preload="auto" disablepictureinpicture oncanplay="this.play().catch(function () {})" onloadedmetadata="this.play().catch(function () {})"></video></div>';
}
return '';
+1 -1
View File
@@ -4,7 +4,7 @@ const Handlebars = require('handlebars');
const path = require('path');
const { mediaKind, safeJsonForScript, getPlayerPageTemplate, getPlayerClientNameScript, getPlayerPageOfflineScript, getPlayerPagePlaylistScript, getPlayerPageCommandsScript, getPlayerPageRenderingScript, getPlayerPagePlaybackScript, getAnnouncementIconsDataScript, getPlayerAnnouncementTemplatesDataScript, getPlayerAnnouncementTemplatesScript, getPlayerPageAnnouncementsScript, getPlayerPageScript, getPlayerRegionScripts, getPlayerOnboardingLandingScript, getPlayerOnboardingFormScript } = require('./render-helpers');
const { createThumbnailPreviewBootstrapScript } = require('./thumbnail-preview');
const { createPageAuthBundle, createPageFetchAuthScript } = require('../request-auth');
const { createPageAuthBundle, createPageFetchAuthScript } = require('#src/request-auth');
const { getFontStylesheetHref } = require('#src/web/lib/media/font-library');
const PLAYER_MEDIA_DIR = path.join(__dirname, '..', '..', 'media');
+4 -4
View File
@@ -3,7 +3,7 @@
const fs = require('fs');
const express = require('express');
const path = require('path');
const { getSharedSecret, createPageAuthBundle, verifyPageAuthToken, verifyRequestAuth } = require('../request-auth');
const { getSharedSecret, createPageAuthBundle, verifyPageAuthToken, verifyRequestAuth } = require('#src/request-auth');
const { buildThumbnailPreviewData } = require('./thumbnail-preview');
const TRANSIENT_DB_ERROR_CODES = ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'EPIPE', 'ENOTFOUND', 'PROTOCOL_CONNECTION_LOST', 'POOL_CLOSED', 'ERR_POOL_CLOSED'];
@@ -259,9 +259,9 @@ function registerPlayerRoutes(app, options) {
});
}
if (typeof common.fetchSchedulesData === 'function') {
const scheduleData = await common.fetchSchedulesData(pool);
data.scheduleGroups = Array.isArray(scheduleData && scheduleData.scheduleGroups) ? scheduleData.scheduleGroups : [];
if (typeof common.fetchTimetablesData === 'function') {
const timetableData = await common.fetchTimetablesData(pool);
data.timetableGroups = Array.isArray(timetableData && timetableData.timetableGroups) ? timetableData.timetableGroups : [];
}
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
+2 -2
View File
@@ -2,8 +2,8 @@
const crypto = require('crypto');
const { WebSocketServer, WebSocket } = require('ws');
const { isClientNameAvailable } = require('../data/client-name-check');
const { verifyPageAuthToken, verifyRequestAuth } = require('../request-auth');
const { isClientNameAvailable } = require('#src/data/client-name-check');
const { verifyPageAuthToken, verifyRequestAuth } = require('#src/request-auth');
function normalizePlayerPublicBaseUrl(pageUrl) {
const value = String(pageUrl || '').trim();
+1 -1
View File
@@ -15,7 +15,7 @@ function buildThumbnailPreviewData(slide) {
slides: [slide],
rssFeeds: [],
apiSources: [],
scheduleGroups: [],
timetableGroups: [],
revision: String(slide.modified_at || slide.id || Date.now())
};
}