Release 2.8.5
This commit is contained in:
@@ -2,6 +2,13 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 2.8.5 - 2026-08-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed thumbnail capture timing so video assets are ready before the preview canvas is captured.
|
||||
- Replaced unavailable webpage thumbnails with a subdued placeholder while keeping live webpage previews intact.
|
||||
|
||||
## 2.8.4 - 2026-08-17
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pulse-signage-player",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"private": false,
|
||||
"description": "Pulse Signage player application bundle",
|
||||
"engines": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pulse-signage-web",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"private": false,
|
||||
"description": "Pulse Signage web and bridge application bundle",
|
||||
"engines": {
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pulse-signage",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pulse-signage",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"dependencies": {
|
||||
"@sparticuz/chromium": "^149.0.0",
|
||||
"animate.css": "^4.1.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pulse-signage",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"private": false,
|
||||
"description": "Pulse Signage application with MySQL and media storage",
|
||||
"engines": {
|
||||
|
||||
@@ -32,7 +32,7 @@ function buildHtmlDocument(html) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
return '<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + raw + '</body></html>';
|
||||
return '<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}html,body{background:transparent !important;}</style></head><body>' + raw + '</body></html>';
|
||||
}
|
||||
|
||||
function renderHtmlRegionContent(value) {
|
||||
@@ -40,7 +40,10 @@ function renderHtmlRegionContent(value) {
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
return '<iframe class="template-region-html-frame" sandbox="" scrolling="no" srcdoc="' + escapeHtml(buildHtmlDocument(html)) + '" title="HTML region" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
|
||||
if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
|
||||
return '<iframe class="template-region-html-frame" sandbox="" allowtransparency="true" scrolling="no" srcdoc="' + escapeHtml(html) + '" title="HTML region" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
|
||||
}
|
||||
return '<div class="template-region-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div>';
|
||||
}
|
||||
|
||||
function renderHtmlRegion(region, regionContent) {
|
||||
|
||||
@@ -142,9 +142,13 @@ function buildRegionInnerHtml(region, regionContent, baseUrl) {
|
||||
|
||||
if (regionType === 'html') {
|
||||
const html = String(rawValue || '').trim();
|
||||
return html
|
||||
? '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml('<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + html + '</body></html>') + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>'
|
||||
: '';
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
|
||||
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml(html) + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>';
|
||||
}
|
||||
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><div class="slide-preview-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div></div>';
|
||||
}
|
||||
|
||||
if (regionType === 'rtmp') {
|
||||
|
||||
@@ -203,10 +203,7 @@ function buildRegionInnerHtml(region, regionContent, baseUrl, options) {
|
||||
}
|
||||
|
||||
if (regionType === 'webpage') {
|
||||
const src = resolveAssetUrl(baseUrl, rawValue);
|
||||
return src
|
||||
? '<div class="slide-preview-region slide-preview-webpage-region" style="' + region.baseStyle + '"><iframe src="' + escapeHtml(src) + '" title="' + escapeHtml(region.label || region.region_key || 'webpage') + '" loading="eager" referrerpolicy="no-referrer" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"></iframe></div>'
|
||||
: '';
|
||||
return '<div class="slide-preview-region slide-preview-webpage-region" style="' + region.baseStyle + '"><div class="slide-preview-webpage-placeholder" style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.08);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,0.14);box-sizing:border-box;color:rgba(255,255,255,0.72);font-size:24px;font-family:Arial,sans-serif;">Webpage preview unavailable</div></div>';
|
||||
}
|
||||
|
||||
if (regionType === 'qr-code') {
|
||||
@@ -220,9 +217,13 @@ function buildRegionInnerHtml(region, regionContent, baseUrl, options) {
|
||||
|
||||
if (regionType === 'html') {
|
||||
const html = String(rawValue || '').trim();
|
||||
return html
|
||||
? '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml('<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + html + '</body></html>') + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>'
|
||||
: '';
|
||||
if (!html) {
|
||||
return '';
|
||||
}
|
||||
if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
|
||||
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml(html) + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>';
|
||||
}
|
||||
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><div class="slide-preview-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div></div>';
|
||||
}
|
||||
|
||||
if (regionType === 'rtmp') {
|
||||
@@ -301,8 +302,7 @@ async function launchBrowser() {
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-gpu'
|
||||
'--disable-dev-shm-usage'
|
||||
],
|
||||
defaultViewport: { width: 1920, height: 1080, deviceScaleFactor: 1 },
|
||||
executablePath: executablePath,
|
||||
@@ -401,14 +401,9 @@ async function captureSlideThumbnail(options) {
|
||||
}, { timeout: 30000 });
|
||||
|
||||
await page.waitForFunction(function () {
|
||||
var canvas = document.querySelector('#popup-preview-canvas');
|
||||
if (!canvas) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var images = Array.prototype.slice.call(canvas.querySelectorAll('img'));
|
||||
return images.every(function (image) {
|
||||
return image.complete && typeof image.naturalWidth === 'number';
|
||||
var videos = Array.prototype.slice.call(document.querySelectorAll('#popup-preview-canvas video'));
|
||||
return videos.every(function (video) {
|
||||
return video.readyState >= 2;
|
||||
});
|
||||
}, { timeout: 30000 });
|
||||
|
||||
@@ -417,17 +412,13 @@ async function captureSlideThumbnail(options) {
|
||||
try {
|
||||
await document.fonts.ready;
|
||||
} catch (_error) {
|
||||
// Ignore font readiness failures and fall back to the rendered frame.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await page.evaluate(function () {
|
||||
return new Promise(function (resolve) {
|
||||
window.requestAnimationFrame(function () {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
});
|
||||
await new Promise(function (resolve) {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -435,19 +426,14 @@ async function captureSlideThumbnail(options) {
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
try {
|
||||
const previewPayload = buildThumbnailPreviewPayload(slide, {
|
||||
baseUrl: baseUrl,
|
||||
fontStylesheetHref: options && options.fontStylesheetHref ? options.fontStylesheetHref : '',
|
||||
getItem: getThumbnailItem,
|
||||
getCachedImagePath: getCachedImagePath
|
||||
const previewPath = '/api/internal/slide-thumbnails/' + encodeURIComponent(String(slide.id)) + '/popup-preview';
|
||||
const previewUrl = baseUrl + previewPath;
|
||||
const canvasSize = getThumbnailCanvasSize(slide);
|
||||
await page.setViewport({
|
||||
width: Math.max(1, Number(canvasSize.width || PLAYER_VIEWPORT.width)),
|
||||
height: Math.max(1, Number(canvasSize.height || PLAYER_VIEWPORT.height)),
|
||||
deviceScaleFactor: 1
|
||||
});
|
||||
const previewPath = '/slides/popup-preview';
|
||||
const previewUrl = baseUrl + previewPath + '#' + encodeURIComponent(JSON.stringify(previewPayload));
|
||||
await page.setViewport({
|
||||
width: Math.max(1, Number(previewPayload.canvasWidth || PLAYER_VIEWPORT.width)),
|
||||
height: Math.max(1, Number(previewPayload.canvasHeight || PLAYER_VIEWPORT.height)),
|
||||
deviceScaleFactor: 1
|
||||
});
|
||||
await page.setExtraHTTPHeaders(Object.assign({}, createRequestAuthHeaders({
|
||||
method: 'GET',
|
||||
pathname: previewPath
|
||||
@@ -463,7 +449,26 @@ async function captureSlideThumbnail(options) {
|
||||
if (!canvas) {
|
||||
throw new Error('Popup preview did not produce a slide canvas.');
|
||||
}
|
||||
await canvas.screenshot({ path: fullSizePath });
|
||||
await page.evaluate(function () {
|
||||
var canvasElement = document.querySelector('#popup-preview-canvas');
|
||||
if (canvasElement) {
|
||||
canvasElement.style.transform = 'none';
|
||||
canvasElement.style.transformOrigin = 'top left';
|
||||
}
|
||||
});
|
||||
const canvasBounds = await canvas.boundingBox();
|
||||
if (!canvasBounds) {
|
||||
throw new Error('Popup preview canvas has no screenshot bounds.');
|
||||
}
|
||||
await page.screenshot({
|
||||
path: fullSizePath,
|
||||
clip: {
|
||||
x: Math.max(0, canvasBounds.x),
|
||||
y: Math.max(0, canvasBounds.y),
|
||||
width: Math.max(1, canvasBounds.width),
|
||||
height: Math.max(1, canvasBounds.height)
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
await page.close().catch(function () {
|
||||
return null;
|
||||
|
||||
@@ -2383,6 +2383,11 @@ html[data-bs-theme='dark'] .template-field-card .tox .tox-collection {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.template-field-card .form-label,
|
||||
.region-item .form-label {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.template-field-card .form-control,
|
||||
.template-field-card .form-select,
|
||||
.template-field-card textarea,
|
||||
|
||||
@@ -305,31 +305,12 @@ import { createSlideFormPreviewHelpers } from '/assets/js/slides/slide-form-prev
|
||||
previewBox.innerHTML = value
|
||||
? '<div class="slide-image-region-preview-shell" data-remove-' + (isVideo ? 'region-video' : isQrImage ? 'region-qr-image' : 'region-image') + '="' + escapeHtml(card.getAttribute('data-region-id') || '') + '" role="button" tabindex="0" aria-label="Remove ' + (isVideo ? 'video' : isQrImage ? 'QR image' : 'image') + '">' +
|
||||
(isVideo
|
||||
? '<video class="slide-image-region-preview" src="' + escapeHtml(value) + '" autoplay loop muted playsinline preload="metadata"></video>'
|
||||
? '<video class="slide-image-region-preview" src="' + escapeHtml(value) + '" muted playsinline preload="metadata"></video>'
|
||||
: '<img class="slide-image-region-preview" src="' + escapeHtml(value) + '" alt="' + (isQrImage ? 'Current QR image preview' : 'Current image preview') + '" />') +
|
||||
'<span class="slide-image-region-preview-remove" aria-hidden="true"><i class="bi bi-trash3" aria-hidden="true"></i></span>' +
|
||||
'</div>'
|
||||
: '<div class="slide-image-region-preview slide-image-region-preview-empty">No ' + (isVideo ? 'video' : 'image') + '</div>';
|
||||
|
||||
if (isVideo) {
|
||||
window.requestAnimationFrame(function () {
|
||||
var video = previewBox.querySelector('video.slide-image-region-preview');
|
||||
if (!video) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
video.load();
|
||||
} catch (_error) {
|
||||
// Ignore load failures; play() will retry if the browser allows it.
|
||||
}
|
||||
var playPromise = video.play && video.play();
|
||||
if (playPromise && typeof playPromise.catch === 'function') {
|
||||
playPromise.catch(function () {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function loadVideoDuration(mediaPath) {
|
||||
|
||||
@@ -99,9 +99,9 @@ test('shared iframe renderers size preview content explicitly', () => {
|
||||
assert.ok(playerHtmlRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
||||
assert.ok(playerWebpageRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"'));
|
||||
assert.ok(playerRenderHelpersSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
||||
assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"'));
|
||||
assert.ok(slideThumbnailPreviewSource.includes('slide-preview-webpage-region'));
|
||||
assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
||||
assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"'));
|
||||
assert.ok(slideThumbnailsSource.includes('slide-preview-webpage-placeholder'));
|
||||
assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user