diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bc266..ba09674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. - No unreleased changes recorded yet. +## 1.5.8 - 2026-07-25 + +### Fixed + +- Slide thumbnail refresh now uses the player-rendered preview again and signs the preview GET request in the same way the player verifies it. +- Background tasks settings page and client script were repaired after a syntax break so the web app can boot cleanly. + ## 1.5.7 - 2026-07-25 ### Fixed diff --git a/package.json b/package.json index e9a2bbe..ee7885c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "1.5.7", + "version": "1.5.8", "private": false, "description": "Pulse Signage application with MySQL and media storage", "repository": { diff --git a/src/web/lib/slide-thumbnails.js b/src/web/lib/slide-thumbnails.js index 3252459..75ef2fb 100644 --- a/src/web/lib/slide-thumbnails.js +++ b/src/web/lib/slide-thumbnails.js @@ -120,124 +120,6 @@ function buildRegionInnerHtml(region, regionContent, baseUrl) { return buildTextRegionMarkup(region, regionContent); } -function buildSlideHtml(slide, baseUrl) { - const canvasSize = getCanvasSize(slide); - const template = slide && slide.template ? slide.template : null; - const backgroundColor = sanitizeTextColor(template && template.background_color ? template.background_color : '#111111', '#111111'); - const backgroundImagePath = template && template.background_image_path ? resolveAssetUrl(baseUrl, template.background_image_path) : ''; - - if (!template) { - const mediaPath = String(slide && slide.media_path || '').trim(); - const mediaUrl = resolveAssetUrl(baseUrl, mediaPath); - const kind = mediaKind(mediaPath || slide.media_type || ''); - const mediaMarkup = kind === 'image' && mediaUrl - ? '' + escapeHtml(slide.title || 'slide') + '' - : ''; - - return [ - '', - '', - ' ', - ' ', - ' ', - ' ', - ' ', - ' ', - ' ', - '
', - mediaMarkup ? '
' + mediaMarkup + '
' : '', - '
', - ' ', - '' - ].join('\n'); - } - - const regions = Array.isArray(template && template.regions) ? template.regions.slice().sort(function (left, right) { - return Number(left.z_index || 0) - Number(right.z_index || 0) || Number(left.id || 0) - Number(right.id || 0); - }) : []; - - const regionsHtml = regions.map(function (region) { - const content = getRegionContent(slide, region); - const left = Math.max(0, Number(region.x || 0)); - const top = Math.max(0, Number(region.y || 0)); - const width = Math.max(1, Number(region.width || 1)); - const height = Math.max(1, Number(region.height || 1)); - const regionType = String(region.region_type || 'text').toLowerCase(); - const pixelWidth = Math.max(1, Math.round(Number(region.width || 0) || 1)); - const pixelHeight = Math.max(1, Math.round(Number(region.height || 0) || 1)); - const innerHtml = regionType === 'text' - ? buildTextRegionMarkup(Object.assign({}, region, { baseStyle: 'left:' + left + '%;top:' + top + '%;width:' + width + '%;height:' + height + '%;z-index:' + Number(region.z_index || 0) + ';', pixelWidth: pixelWidth, pixelHeight: pixelHeight }), content) - : buildRegionInnerHtml(Object.assign({}, region, { baseStyle: 'left:' + left + '%;top:' + top + '%;width:' + width + '%;height:' + height + '%;z-index:' + Number(region.z_index || 0) + ';', pixelWidth: pixelWidth, pixelHeight: pixelHeight }), content, baseUrl); - - if (!innerHtml) { - return ''; - } - - return [ - '
', - innerHtml, - '
' - ].join(''); - }).join(''); - - return [ - '', - '', - ' ', - ' ', - ' ', - ' ', - ' ', - ' ', - ' ', - '
', - '
', - backgroundImagePath ? ' ' : '', - regionsHtml, - '
', - '
', - ' ', - '' - ].join('\n'); -} - async function launchBrowser() { let executablePath = SYSTEM_CHROMIUM_PATHS.find(function (candidate) { return fs.existsSync(candidate); @@ -312,12 +194,13 @@ async function captureSlideThumbnail(options) { const previewUrl = baseUrl + previewPath; await page.setExtraHTTPHeaders(createRequestAuthHeaders({ method: 'GET', - pathname: previewPath, - body: {} + pathname: previewPath })); await page.setViewport(PLAYER_VIEWPORT); await page.goto(previewUrl, { waitUntil: 'domcontentloaded', timeout: 30000 }); - await page.waitForSelector('.slide-canvas', { timeout: 30000, visible: true }); + await page.waitForFunction(function () { + return Boolean(document.querySelector('.slide-canvas')); + }, { timeout: 30000 }); const canvas = await page.$('.slide-canvas'); if (!canvas) { throw new Error('Player render did not produce a slide canvas.'); diff --git a/src/web/public/js/settings/background-tasks.js b/src/web/public/js/settings/background-tasks.js index 80ee5b9..ae7e233 100644 --- a/src/web/public/js/settings/background-tasks.js +++ b/src/web/public/js/settings/background-tasks.js @@ -174,26 +174,6 @@ }; } - function findFilterLink(target) { - if (!target || !target.closest) { - return null; - } - - var link = target.closest('a[data-background-tasks-nav]'); - if (!link) { - return null; - } - - var href = String(link.getAttribute('href') || '').trim(); - if (!href || href === '#') { - return null; - } - - return { - href: href - }; - } - function initPaginationNavigation() { document.addEventListener('click', function (event) { if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { @@ -217,24 +197,6 @@ loadPage(nextUrl.toString(), { sectionNames: [paginationLink.sectionName] }); return; } - - var filterLink = findFilterLink(event.target); - if (!filterLink) { - return; - } - - var filterUrl = getPageUrl(filterLink.href); - if (!filterUrl) { - return; - } - - if (filterUrl.pathname === window.location.pathname && filterUrl.search === window.location.search && filterUrl.hash === window.location.hash) { - event.preventDefault(); - return; - } - - event.preventDefault(); - loadPage(filterUrl.toString(), { sectionNames: ['tasks'] }); }, true); window.addEventListener('popstate', function () { diff --git a/src/web/routes/settings/background-tasks-page.js b/src/web/routes/settings/background-tasks-page.js index 57ab515..aa08b84 100644 --- a/src/web/routes/settings/background-tasks-page.js +++ b/src/web/routes/settings/background-tasks-page.js @@ -2,7 +2,6 @@ const { renderView } = require('../../view'); const { hasAnyPermission } = require('../../../rbac'); const TASKS_PER_PAGE = 10; -const TASK_STATUSES = new Set(['queued', 'running', 'failed', 'completed', 'canceled']); function formatIntervalLabel(intervalMs) { const value = Math.max(1, Number(intervalMs) || 0); @@ -22,11 +21,6 @@ function parsePageNumber(value) { return Math.max(1, pageNumber); } -function normalizeStatusFilter(value) { - const status = String(value || '').trim().toLowerCase(); - return TASK_STATUSES.has(status) ? status : ''; -} - function normalizeSourceFilter(sourceType, sourceId) { const normalizedSourceType = String(sourceType || '').trim(); const normalizedSourceId = Math.floor(Number(sourceId) || 0); @@ -92,11 +86,7 @@ function buildPagination(totalItems, currentPage, pageParam, queryState) { }; } -function taskMatchesFilters(task, statusFilter, sourceFilter) { - if (statusFilter && task.status !== statusFilter) { - return false; - } - +function taskMatchesFilters(task, sourceFilter) { if (!sourceFilter || !sourceFilter.sourceType || !sourceFilter.sourceId) { return true; } @@ -106,12 +96,26 @@ function taskMatchesFilters(task, statusFilter, sourceFilter) { } function formatStatusLabel(status) { - const normalizedStatus = String(status || '').trim().toLowerCase(); - if (!normalizedStatus) { - return 'All tasks'; + const normalized = String(status || '').trim().toLowerCase(); + if (!normalized) { + return ''; } - - return normalizedStatus.charAt(0).toUpperCase() + normalizedStatus.slice(1); + if (normalized === 'queued') { + return 'Queued'; + } + if (normalized === 'running') { + return 'Running'; + } + if (normalized === 'completed') { + return 'Completed'; + } + if (normalized === 'failed') { + return 'Failed'; + } + if (normalized === 'canceled') { + return 'Canceled'; + } + return normalized.charAt(0).toUpperCase() + normalized.slice(1); } function buildTaskSourceFilterUrl(queryState, task, sourceFilter) { @@ -133,17 +137,6 @@ function buildTaskSourceFilterUrl(queryState, task, sourceFilter) { return buildQueryString(nextQuery); } -function buildStatusFilterUrl(queryState, statusFilter) { - const nextQuery = Object.assign({}, queryState, { - page: 1, - status: statusFilter && statusFilter.status === statusFilter.targetStatus ? '' : statusFilter.targetStatus, - sourceType: statusFilter && statusFilter.sourceType ? statusFilter.sourceType : '', - sourceId: statusFilter && statusFilter.sourceId ? statusFilter.sourceId : '' - }); - - return buildQueryString(nextQuery); -} - module.exports = function renderBackgroundTasksPage(data, message, currentUser) { const tasks = (data && data.tasks) || []; const recurringTasks = (data && data.recurringTasks) || []; @@ -152,16 +145,14 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser) const queryState = { page: parsePageNumber(data && data.page), recurringPage: parsePageNumber(data && data.recurringPage), - status: normalizeStatusFilter(data && data.status), sourceType: '', sourceId: '' }; const sourceFilter = normalizeSourceFilter(data && data.sourceType, data && data.sourceId); - queryState.status = normalizeStatusFilter(data && data.status); queryState.sourceType = sourceFilter.sourceType; queryState.sourceId = sourceFilter.sourceId; const filteredTasks = tasks.filter(function (task) { - return taskMatchesFilters(task, queryState.status, sourceFilter); + return taskMatchesFilters(task, sourceFilter); }); const pagination = buildPagination(filteredTasks.length, queryState.page, 'page', queryState); const recurringPagination = buildPagination(recurringTasks.length, queryState.recurringPage, 'recurringPage', queryState); @@ -193,18 +184,8 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser) variant: 'success' } ].map(function (card) { - const isActive = queryState.status === card.status; - const statusFilter = { - status: queryState.status, - sourceType: queryState.sourceType, - sourceId: queryState.sourceId, - targetStatus: card.status - }; - return Object.assign({}, card, { - active: isActive, - href: buildStatusFilterUrl(queryState, statusFilter), - ariaLabel: isActive ? `Clear ${card.label.toLowerCase()} filter` : `Filter tasks by ${card.label.toLowerCase()}` + active: false }); }); @@ -216,7 +197,7 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser) sourceIdLabel: task.metadata && task.metadata.sourceId ? Number(task.metadata.sourceId) : '' }); }); - const hasActiveFilters = Boolean(queryState.status || queryState.sourceType || queryState.sourceId); + const hasActiveFilters = Boolean(queryState.sourceType || queryState.sourceId); return renderView('settings/background-tasks/index', { title: 'Background tasks', @@ -233,7 +214,6 @@ module.exports = function renderBackgroundTasksPage(data, message, currentUser) }), summary: summary, statusCards: statusCards, - activeStatusLabel: formatStatusLabel(queryState.status), activeSourceLabel: queryState.sourceType && queryState.sourceId ? `${queryState.sourceType} #${queryState.sourceId}` : '', clearFiltersUrl: buildQueryString({ recurringPage: queryState.recurringPage }), hasActiveFilters: hasActiveFilters, diff --git a/src/web/views/settings/background-tasks/index.hbs b/src/web/views/settings/background-tasks/index.hbs index 690f3d0..704f90d 100644 --- a/src/web/views/settings/background-tasks/index.hbs +++ b/src/web/views/settings/background-tasks/index.hbs @@ -3,7 +3,7 @@ @@ -11,7 +11,7 @@ {{#if hasActiveFilters}}
- Showing results for {{#if activeStatusLabel}}{{activeStatusLabel}}{{else}}all tasks{{/if}}{{#if activeSourceLabel}} from {{activeSourceLabel}}{{/if}}. + {{#if activeSourceLabel}}Showing results from {{activeSourceLabel}}.{{/if}}
Clear filters
@@ -21,14 +21,12 @@
{{#each statusCards}}
- - {{/each}}