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
+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 '';