Release v2.10.6
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m15s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 34s

This commit is contained in:
2026-08-29 14:59:06 +01:00
parent ca9f38f3b9
commit 8c0c22156e
30 changed files with 922 additions and 531 deletions
+13 -18
View File
@@ -36,22 +36,6 @@ function normalizeBaseUrl(value) {
return String(value || '').trim().replace(/\/$/, '');
}
function appendPlayerDeviceIdToUrl(baseUrl, playerIdentifier) {
const targetBaseUrl = normalizeBaseUrl(baseUrl);
const deviceId = String(playerIdentifier || '').trim();
if (!targetBaseUrl || !deviceId) {
return targetBaseUrl;
}
try {
const url = new URL(targetBaseUrl);
url.searchParams.set('deviceId', deviceId);
return url.toString().replace(/\/$/, '');
} catch (_error) {
return targetBaseUrl;
}
}
function normalizePlayerRowBaseUrl(player) {
return normalizeBaseUrl(player && player.internal_base_url);
}
@@ -68,6 +52,7 @@ function createUploadSyncService(options) {
const pool = options && options.pool;
const common = options && options.common;
const configuredPlayerInternalBaseUrl = String(options && options.playerInternalBaseUrl || '').replace(/\/$/, '');
const configuredBridgeInternalBaseUrl = String(options && options.bridgeInternalBaseUrl || '').trim().replace(/\/$/, '');
const playerSnapshotCache = options && options.playerSnapshotCache;
const notifyPlayerScreens = options && options.notifyPlayerScreens;
const backgroundTaskQueue = options && options.backgroundTaskQueue;
@@ -545,6 +530,9 @@ function createUploadSyncService(options) {
}
const playerMetadata = await getPlayerTaskMetadata(preferredPlayerIdentifier, targetBaseUrl);
const mediaBaseUrl = playerMetadata && playerMetadata.playerIdentifier && !isLocalLikeBaseUrl(targetBaseUrl) && configuredBridgeInternalBaseUrl
? configuredBridgeInternalBaseUrl
: targetBaseUrl;
const relativePath = getUploadRelativePath(uploadPath);
const sourcePath = resolveUploadFilePath(localUploadDir, uploadPath);
if (!relativePath || !sourcePath) {
@@ -566,11 +554,13 @@ function createUploadSyncService(options) {
pathname: `/api/media/${encodeURIComponent(relativePath)}`,
body: fileBuffer
});
const mediaUploadUrl = appendPlayerDeviceIdToUrl(`${targetBaseUrl}/api/media/${encodeURIComponent(relativePath)}`, playerMetadata && playerMetadata.playerIdentifier);
const mediaUploadUrl = `${mediaBaseUrl}/api/media/${encodeURIComponent(relativePath)}`;
const playerDeviceId = String(playerMetadata && playerMetadata.playerIdentifier || '').trim();
const response = await fetch(mediaUploadUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
...(playerDeviceId ? { 'x-pulse-player-device-id': playerDeviceId } : {}),
...authHeaders
},
body: fileBuffer
@@ -601,6 +591,9 @@ function createUploadSyncService(options) {
}
const playerMetadata = await getPlayerTaskMetadata(preferredPlayerIdentifier, targetBaseUrl);
const mediaBaseUrl = playerMetadata && playerMetadata.playerIdentifier && !isLocalLikeBaseUrl(targetBaseUrl) && configuredBridgeInternalBaseUrl
? configuredBridgeInternalBaseUrl
: targetBaseUrl;
const relativePath = getUploadRelativePath(uploadPath);
if (!relativePath) {
return false;
@@ -610,11 +603,13 @@ function createUploadSyncService(options) {
method: 'DELETE',
pathname: `/api/media/${encodeURIComponent(relativePath)}`
});
const mediaDeleteUrl = appendPlayerDeviceIdToUrl(`${targetBaseUrl}/api/media/${encodeURIComponent(relativePath)}`, playerMetadata && playerMetadata.playerIdentifier);
const mediaDeleteUrl = `${mediaBaseUrl}/api/media/${encodeURIComponent(relativePath)}`;
const playerDeviceId = String(playerMetadata && playerMetadata.playerIdentifier || '').trim();
const response = await fetch(mediaDeleteUrl, {
method: 'DELETE',
headers: {
Accept: 'application/json',
...(playerDeviceId ? { 'x-pulse-player-device-id': playerDeviceId } : {}),
...authHeaders
}
});
+20 -2
View File
@@ -1,6 +1,6 @@
// Route screen refresh commands to the physical player hosting each screen.
function createNotifyPlayerScreens(forwardPlayerCommand, getScreenConnections, forwardPlayerCommandToBaseUrl, resolvePlayerBaseUrl) {
function createNotifyPlayerScreens(forwardPlayerCommand, getScreenConnections, forwardPlayerCommandToBaseUrl, resolvePlayerBaseUrl, forwardPlayerCommandToDevice) {
if (typeof forwardPlayerCommand !== 'function') {
throw new Error('createNotifyPlayerScreens requires a player command sender.');
}
@@ -19,17 +19,35 @@ function createNotifyPlayerScreens(forwardPlayerCommand, getScreenConnections, f
if (typeof getScreenConnections === 'function' && typeof forwardPlayerCommandToBaseUrl === 'function') {
const screenState = await getScreenConnections(slug);
const connections = Array.isArray(screenState && screenState.connections) ? screenState.connections : [];
const deviceIds = Array.from(new Set(connections.map(function (connection) {
return String(connection && connection.playerDeviceId || '').trim();
}).filter(Boolean)));
const playerBaseUrls = Array.from(new Set(connections.map(function (connection) {
if (String(connection && connection.playerDeviceId || '').trim()) {
return '';
}
return String(connection && connection.playerPublicBaseUrl || '').trim().replace(/\/$/, '');
}).filter(Boolean)));
const deviceResults = deviceIds.length && typeof forwardPlayerCommandToDevice === 'function'
? await Promise.all(deviceIds.map(function (deviceId) {
return forwardPlayerCommandToDevice(deviceId, {
command: commandOrPayload || 'refresh',
screenSlug: slug
});
}))
: [];
let baseUrlResults = [];
if (playerBaseUrls.length) {
const resolvedPlayerBaseUrls = typeof resolvePlayerBaseUrl === 'function'
? await Promise.all(playerBaseUrls.map(function (playerBaseUrl) { return resolvePlayerBaseUrl(playerBaseUrl); }))
: playerBaseUrls;
return Promise.all(resolvedPlayerBaseUrls.filter(Boolean).map(function (playerBaseUrl) {
baseUrlResults = await Promise.all(resolvedPlayerBaseUrls.filter(Boolean).map(function (playerBaseUrl) {
return forwardPlayerCommandToBaseUrl(playerBaseUrl, slug, commandOrPayload || 'refresh');
}));
}
if (deviceResults.length || baseUrlResults.length) {
return deviceResults.concat(baseUrlResults);
}
}
return forwardPlayerCommand(slug, commandOrPayload || 'refresh');
})).then(function (results) {
+17
View File
@@ -243,6 +243,23 @@ function createPlayerActionService(options) {
}
async function forwardAnnouncementRefresh(slug) {
try {
const screenState = await getScreenConnections(slug);
const deviceIds = Array.from(new Set((screenState && Array.isArray(screenState.connections) ? screenState.connections : []).map(function (connection) {
return String(connection && connection.playerDeviceId || '').trim();
}).filter(Boolean)));
if (deviceIds.length) {
const deviceResults = await Promise.all(deviceIds.map(function (deviceId) {
return forwardPlayerCommandToDevice(deviceId, {
command: 'announcement-refresh',
screenSlug: slug
});
}));
return deviceResults;
}
} catch (_error) {
}
const resolvedPlayerInternalBaseUrl = await getPlayerInternalBaseUrl();
const targetBaseUrls = Array.from(new Set([
resolvedPlayerInternalBaseUrl,