diff --git a/CHANGELOG.md b/CHANGELOG.md index bb3d91f..363913a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## 2.6.13 - 2026-08-08 + +### Fixed + +- The player bundle now keeps the browser-only QR export dependencies lazy-loaded, so the remote player image no longer needs `puppeteer-core` or `@sparticuz/chromium` at startup. +- Remote player startup sync now falls back to `WEB_INTERNAL_URL` when it is configured, which avoids 404s when the bridge is not the correct sync target. +- The player startup sync and media-write info logs were removed to keep normal remote-player operation quieter. + ## 2.6.12 - 2026-08-08 ### Fixed diff --git a/build/package.player.json b/build/package.player.json index af8c98d..604489e 100644 --- a/build/package.player.json +++ b/build/package.player.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-player", - "version": "2.6.12", + "version": "2.6.13", "private": false, "description": "Pulse Signage player application bundle", "main": "src/common.js", diff --git a/build/package.web.json b/build/package.web.json index 100aa5c..a71e64e 100644 --- a/build/package.web.json +++ b/build/package.web.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-web", - "version": "2.6.12", + "version": "2.6.13", "private": false, "description": "Pulse Signage web and bridge application bundle", "main": "src/common.js", diff --git a/package-lock.json b/package-lock.json index 59e5768..6571521 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pulse-signage", - "version": "2.5.9", + "version": "2.6.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pulse-signage", - "version": "2.5.9", + "version": "2.6.13", "dependencies": { "@sparticuz/chromium": "^137.0.0", "animate.css": "^4.1.1", diff --git a/package.json b/package.json index 8a70d47..e44513e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.6.12", + "version": "2.6.13", "private": false, "description": "Pulse Signage application with MySQL and media storage", "repository": { diff --git a/src/data/qr-code.js b/src/data/qr-code.js index 908bb25..b4d6411 100644 --- a/src/data/qr-code.js +++ b/src/data/qr-code.js @@ -1,8 +1,6 @@ const fs = require('fs'); const path = require('path'); const QRCodeStyling = require(path.join(__dirname, '..', 'web', 'public', 'vendor', 'qr-code-styling', 'qr-code-styling.js')); -const puppeteer = require('puppeteer-core'); -const chromiumModule = require('@sparticuz/chromium'); const QR_PNG_WIDTH = 2048; const QR_STYLING_SCRIPT_PATH = path.join(__dirname, '..', 'web', 'public', 'vendor', 'qr-code-styling', 'qr-code-styling.js'); const SYSTEM_CHROMIUM_PATHS = [ @@ -13,11 +11,6 @@ const SYSTEM_CHROMIUM_PATHS = [ '/usr/local/bin/chromium', '/snap/bin/chromium' ].filter(Boolean); -const chromium = chromiumModule && typeof chromiumModule.executablePath === 'function' - ? chromiumModule - : chromiumModule && chromiumModule.default && typeof chromiumModule.default.executablePath === 'function' - ? chromiumModule.default - : chromiumModule; let qrBrowserPromise = null; function escapeXml(value) { @@ -353,6 +346,13 @@ function getQrBrowser() { } qrBrowserPromise = (async function () { + const puppeteer = require('puppeteer-core'); + const chromiumModule = require('@sparticuz/chromium'); + const chromium = chromiumModule && typeof chromiumModule.executablePath === 'function' + ? chromiumModule + : chromiumModule && chromiumModule.default && typeof chromiumModule.default.executablePath === 'function' + ? chromiumModule.default + : chromiumModule; let executablePath = SYSTEM_CHROMIUM_PATHS.find(function (candidate) { return fs.existsSync(candidate); }) || ''; diff --git a/src/player.js b/src/player.js index d582169..25cabd7 100644 --- a/src/player.js +++ b/src/player.js @@ -22,6 +22,7 @@ async function start() { const PORT = Number(process.env.PLAYER_PORT || 8081); const PLAYER_PUBLIC_URL = String(process.env.PLAYER_PUBLIC_URL || process.env.PLAYER_BASE_URL || '').trim().replace(/\/$/, ''); const BRIDGE_PUBLIC_URL = String(process.env.BRIDGE_PUBLIC_URL || '').trim().replace(/\/$/, ''); + const WEB_INTERNAL_URL = String(process.env.WEB_INTERNAL_URL || '').trim().replace(/\/$/, ''); const isRemotePlayer = Boolean(BRIDGE_PUBLIC_URL); const PLAYER_INTERNAL_URL = String(isRemotePlayer ? BRIDGE_PUBLIC_URL : (process.env.PLAYER_INTERNAL_URL || PLAYER_PUBLIC_URL || process.env.PLAYER_BASE_URL || '')).trim().replace(/\/$/, ''); const PLAYER_DEVICE_ID = getConfiguredPlayerIdentifier(); @@ -133,7 +134,8 @@ async function start() { } async function triggerWebMediaSync() { - if (!isRemotePlayer || !BRIDGE_PUBLIC_URL) { + const syncBaseUrl = WEB_INTERNAL_URL || BRIDGE_PUBLIC_URL; + if (!isRemotePlayer || !syncBaseUrl) { return false; } @@ -144,16 +146,12 @@ async function start() { }; try { - console.info('[player] Triggering startup media sync', { - playerIdentifier: PLAYER_DEVICE_ID, - bridgeBaseUrl: BRIDGE_PUBLIC_URL - }); const authHeaders = createRequestAuthHeaders({ method: 'POST', pathname: '/api/internal/sync/player-media', body: requestBody }); - const response = await fetch(`${BRIDGE_PUBLIC_URL}/api/internal/sync/player-media`, { + const response = await fetch(`${syncBaseUrl}/api/internal/sync/player-media`, { method: 'POST', headers: Object.assign({ Accept: 'application/json', @@ -162,11 +160,6 @@ async function start() { body: JSON.stringify(requestBody) }); - console.info('[player] Startup media sync response', { - ok: Boolean(response && response.ok), - status: response && response.status ? response.status : null - }); - return Boolean(response && response.ok); } catch (_error) { console.warn('[player] Startup media sync failed'); @@ -175,7 +168,8 @@ async function start() { } async function triggerWebFontSync() { - if (!isRemotePlayer || !BRIDGE_PUBLIC_URL) { + const syncBaseUrl = WEB_INTERNAL_URL || BRIDGE_PUBLIC_URL; + if (!isRemotePlayer || !syncBaseUrl) { return false; } @@ -186,16 +180,12 @@ async function start() { }; try { - console.info('[player] Triggering startup font sync', { - playerIdentifier: PLAYER_DEVICE_ID, - bridgeBaseUrl: BRIDGE_PUBLIC_URL - }); const authHeaders = createRequestAuthHeaders({ method: 'POST', pathname: '/api/internal/sync/player-font', body: requestBody }); - const response = await fetch(`${BRIDGE_PUBLIC_URL}/api/internal/sync/player-font`, { + const response = await fetch(`${syncBaseUrl}/api/internal/sync/player-font`, { method: 'POST', headers: Object.assign({ Accept: 'application/json', @@ -204,11 +194,6 @@ async function start() { body: JSON.stringify(requestBody) }); - console.info('[player] Startup font sync response', { - ok: Boolean(response && response.ok), - status: response && response.status ? response.status : null - }); - return Boolean(response && response.ok); } catch (_error) { console.warn('[player] Startup font sync failed'); @@ -257,18 +242,9 @@ async function start() { response.error = 'Invalid media payload.'; } else { const bodyBuffer = Buffer.from(bodyBase64, 'base64'); - console.info('[player] Writing media file', { - relativePath: relativePath, - filePath: filePath, - bytes: bodyBuffer.length - }); await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); await fs.promises.writeFile(filePath, bodyBuffer); response.ok = true; - console.info('[player] Media file written', { - relativePath: relativePath, - filePath: filePath - }); } } else if (command === 'media-delete') { const relativePath = String(payload.relativePath || payload.filename || '').trim();