diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b82016..059afde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## 2.10.4 - 2026-08-29 + +### Fixed + +- Fixed remote client move commands so they route through the player bridge to the correct browser tab using the physical player identity and connection ID. +- Removed the obsolete `PLAYER_BASE_URL` configuration fallback; command routing uses `PLAYER_INTERNAL_URL` locally or the player bridge remotely, while `PLAYER_PUBLIC_URL` remains available for kiosk launcher and direct player access. +- Removed the player URL list from screen-group add/edit pages and expanded the remaining form card to full width. + ## 2.10.3 - 2026-08-28 ### Fixed diff --git a/build/package.player.json b/build/package.player.json index c7450ef..a453225 100644 --- a/build/package.player.json +++ b/build/package.player.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-player", - "version": "2.10.3", + "version": "2.10.4", "private": false, "description": "Pulse Signage player application bundle", "engines": { diff --git a/build/package.web.json b/build/package.web.json index 90998d3..e32aaf3 100644 --- a/build/package.web.json +++ b/build/package.web.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-web", - "version": "2.10.3", + "version": "2.10.4", "private": false, "description": "Pulse Signage web and bridge application bundle", "engines": { diff --git a/docker-compose/.env.remote.example b/docker-compose/.env.remote.example index 298436e..1789fbd 100644 --- a/docker-compose/.env.remote.example +++ b/docker-compose/.env.remote.example @@ -6,7 +6,10 @@ PULSE_SIGNAGE_SHARED_SECRET="" # Remote player PLAYER_IDENTIFIER="player-remote" -PLAYER_PUBLIC_URL="http://remote-player.example.com:8081" + +# Optional URL used by the kiosk launcher when the remote player is directly reachable +# PLAYER_PUBLIC_URL="http://remote-player.example.com:8081" + PLAYER_AGENT_RECONNECT_DELAY_MS=5000 # Remote bridge connectivity diff --git a/docker-compose/README.md b/docker-compose/README.md index e500af4..6534684 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -62,7 +62,6 @@ Responsibilities: Key configuration: -- `PLAYER_PUBLIC_URL` - `PLAYER_INTERNAL_URL` - `PLAYER_IDENTIFIER` - `BRIDGE_PUBLIC_URL` in remote mode @@ -118,7 +117,7 @@ Important values: - `WEB_PUBLIC_URL` - public URL of the web application - `WEB_INTERNAL_URL` - internal URL the bridge uses to call the web app directly - `PLAYER_IDENTIFIER` - unique local player identifier -- `PLAYER_PUBLIC_URL` - public URL the player advertises +- `PLAYER_PUBLIC_URL` - URL used by the kiosk launcher and direct player access - `PLAYER_INTERNAL_URL` - internal URL the web app uses for local player calls - `BRIDGE_INTERNAL_URL` - bridge URL the web app uses for player snapshot and command forwarding - `DEFAULT_ADMIN_*` - bootstrap admin account values @@ -132,7 +131,7 @@ Important values: - `PULSE_SIGNAGE_PLAYER_IMAGE` - image to run on the device, typically `.../pulse-signage-player:latest` - `PULSE_SIGNAGE_SHARED_SECRET` - must match the public stack and should be the same long random value used everywhere in the deployment - `PLAYER_IDENTIFIER` - unique remote player identifier -- `PLAYER_PUBLIC_URL` - public URL for the remote player +- `PLAYER_PUBLIC_URL` - optional URL used by the kiosk launcher when the remote player is directly reachable - `BRIDGE_PUBLIC_URL` - bridge URL the player connects back to - `PLAYER_AGENT_RECONNECT_DELAY_MS` - reconnect delay for the player agent @@ -169,7 +168,7 @@ Leave it blank only if you intentionally want to run without request signing in | `WEB_PUBLIC_URL` | web, player-bridge | Public URL of the web application. | | `WEB_INTERNAL_URL` | player-bridge | Internal web URL used by the bridge to call the dashboard app directly. | | `PLAYER_IDENTIFIER` | player | Stable player identifier. | -| `PLAYER_PUBLIC_URL` | player, remote player | Public URL advertised by the player. | +| `PLAYER_PUBLIC_URL` | player, remote player | URL used by the kiosk launcher and direct player access; optional for bridge-only remote players. | | `PLAYER_INTERNAL_URL` | web, player | Internal player URL used by the dashboard and player runtime. | | `BRIDGE_INTERNAL_URL` | web | Bridge URL used by the web app for player snapshot and command forwarding. | | `DEFAULT_ADMIN_USERNAME` | web | Bootstrap admin username. | diff --git a/docker-compose/docker-compose.remote.yml b/docker-compose/docker-compose.remote.yml index 05b0cc5..90abeb0 100644 --- a/docker-compose/docker-compose.remote.yml +++ b/docker-compose/docker-compose.remote.yml @@ -11,7 +11,7 @@ services: - "8081:8081" environment: PLAYER_IDENTIFIER: ${PLAYER_IDENTIFIER:-player-remote} - PLAYER_PUBLIC_URL: ${PLAYER_PUBLIC_URL:?PLAYER_PUBLIC_URL must be set to a routable remote-player URL} + PLAYER_PUBLIC_URL: ${PLAYER_PUBLIC_URL:-} BRIDGE_PUBLIC_URL: ${BRIDGE_PUBLIC_URL:?BRIDGE_PUBLIC_URL must be set to a routable bridge URL} PULSE_SIGNAGE_SHARED_SECRET: ${PULSE_SIGNAGE_SHARED_SECRET:-} PLAYER_AGENT_RECONNECT_DELAY_MS: ${PLAYER_AGENT_RECONNECT_DELAY_MS:-5000} diff --git a/package-lock.json b/package-lock.json index e2345d8..9664026 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pulse-signage", - "version": "2.10.3", + "version": "2.10.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pulse-signage", - "version": "2.10.3", + "version": "2.10.4", "dependencies": { "@sparticuz/chromium": "^149.0.0", "animate.css": "^4.1.1", diff --git a/package.json b/package.json index 13f0403..79c55ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.10.3", + "version": "2.10.4", "private": false, "description": "Pulse Signage application with MySQL and media storage", "engines": { diff --git a/src/player-bridge/index.js b/src/player-bridge/index.js index aea954c..7619b07 100644 --- a/src/player-bridge/index.js +++ b/src/player-bridge/index.js @@ -916,8 +916,11 @@ async function start() { return res.status(404).json({ ok: false, error: 'Player is not connected.' }); } - socket.send(JSON.stringify(payload)); - res.json({ ok: true, deviceId: deviceId, sent: true }); + const response = await sendPlayerCommandToSocket(socket, payload); + res.status(response && response.ok ? 200 : (response && response.status || 502)).json(Object.assign({ + deviceId: deviceId, + connectionId: payload.connectionId || null + }, response && typeof response === 'object' ? response : { ok: false })); } catch (error) { next(error); } @@ -975,9 +978,14 @@ async function start() { const snapshotPlayerPublicBaseUrl = String(payload.playerPublicBaseUrl || socket.publicBaseUrl || '').trim().replace(/\/$/, ''); const connections = Array.isArray(payload.connections) ? payload.connections.map(function (connection) { if (!connection || typeof connection !== 'object' || !snapshotPlayerPublicBaseUrl) { - return connection; + return connection && typeof connection === 'object' + ? Object.assign({}, connection, { playerDeviceId: deviceId }) + : connection; } - return Object.assign({}, connection, { playerPublicBaseUrl: snapshotPlayerPublicBaseUrl }); + return Object.assign({}, connection, { + playerDeviceId: deviceId, + playerPublicBaseUrl: snapshotPlayerPublicBaseUrl + }); }) : []; setScreenSnapshotSource(slug, deviceId, connections); return; diff --git a/src/player.js b/src/player.js index cda699a..a0e7a36 100644 --- a/src/player.js +++ b/src/player.js @@ -24,11 +24,11 @@ async function start() { const app = express(); const pool = String(process.env.BRIDGE_PUBLIC_URL || '').trim().replace(/\/$/, '') ? null : common.createPool(); 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 PLAYER_PUBLIC_URL = String(process.env.PLAYER_PUBLIC_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_INTERNAL_URL = String(isRemotePlayer ? BRIDGE_PUBLIC_URL : (process.env.PLAYER_INTERNAL_URL || '')).trim().replace(/\/$/, ''); const PLAYER_DEVICE_ID = getConfiguredPlayerIdentifier(); const PLAYER_AGENT_RECONNECT_DELAY_MS = Number(process.env.PLAYER_AGENT_RECONNECT_DELAY_MS || 5000); const ASSET_DIR = path.join(__dirname, 'player', 'public'); diff --git a/src/player/onboarding/index.js b/src/player/onboarding/index.js index 22a57e3..b12bba0 100644 --- a/src/player/onboarding/index.js +++ b/src/player/onboarding/index.js @@ -48,7 +48,7 @@ function getPublicBaseUrl(req, configuredUrl) { return `${protocol}://${host}`.replace(/\/$/, ''); } - const configured = String(configuredUrl || process.env.PLAYER_PUBLIC_URL || process.env.PLAYER_BASE_URL || '').trim().replace(/\/$/, ''); + const configured = String(configuredUrl || process.env.PLAYER_PUBLIC_URL || '').trim().replace(/\/$/, ''); return configured || null; } @@ -75,7 +75,7 @@ function getPlayerPublicBaseUrl(req, configuredUrl) { } function getPlayerInternalBaseUrl(configuredUrl) { - const configured = String(configuredUrl || process.env.PLAYER_INTERNAL_URL || process.env.PLAYER_BASE_URL || '').trim().replace(/\/$/, ''); + const configured = String(configuredUrl || process.env.PLAYER_INTERNAL_URL || '').trim().replace(/\/$/, ''); return configured || null; } @@ -238,7 +238,7 @@ function registerPlayerOnboardingRoutes(app, options) { const common = options && options.common ? options.common : null; const playerRuntime = options && options.playerRuntime ? options.playerRuntime : null; const onboardingStore = options && options.onboardingStore ? options.onboardingStore : null; - const playerPublicUrl = String(options && options.playerPublicBaseUrl || process.env.PLAYER_PUBLIC_URL || process.env.PLAYER_BASE_URL || '').trim().replace(/\/$/, ''); + const playerPublicUrl = String(options && options.playerPublicBaseUrl || process.env.PLAYER_PUBLIC_URL || '').trim().replace(/\/$/, ''); const bridgeBaseUrl = String(options && options.bridgeBaseUrl || process.env.BRIDGE_PUBLIC_URL || '').trim().replace(/\/$/, ''); const playerDeviceId = normalizeDeviceId(options && options.playerDeviceId); const onPairingCode = options && typeof options.onPairingCode === 'function' ? options.onPairingCode : null; diff --git a/src/player/routes.js b/src/player/routes.js index f710e33..df255af 100644 --- a/src/player/routes.js +++ b/src/player/routes.js @@ -48,7 +48,7 @@ function registerPlayerRoutes(app, options) { const playerRuntime = options && options.playerRuntime ? options.playerRuntime : null; const playerPlaylistService = options && options.playerPlaylistService ? options.playerPlaylistService : null; const rtmpStreamService = options && options.rtmpStreamService ? options.rtmpStreamService : null; - const playerInternalUrl = String(options && options.playerInternalBaseUrl || process.env.PLAYER_INTERNAL_URL || process.env.PLAYER_BASE_URL || '').trim().replace(/\/$/, ''); + const playerInternalUrl = String(options && options.playerInternalBaseUrl || process.env.PLAYER_INTERNAL_URL || '').trim().replace(/\/$/, ''); const bridgeBaseUrl = String(options && options.bridgeBaseUrl || process.env.BRIDGE_PUBLIC_URL || '').trim().replace(/\/$/, ''); const playerDeviceId = String(options && options.playerDeviceId || '').trim() || null; const snapshotDir = options && options.snapshotDir ? path.resolve(String(options.snapshotDir)) : null; diff --git a/src/web/lib/config.js b/src/web/lib/config.js index 161a4af..edff9a9 100644 --- a/src/web/lib/config.js +++ b/src/web/lib/config.js @@ -5,7 +5,7 @@ function createWebConfig() { const uploadsDir = path.join(mediaDir, 'uploads'); const thumbnailsDir = path.join(mediaDir, 'thumbnails'); const assetDir = path.join(__dirname, '..', 'public'); - const playerInternalUrl = (process.env.PLAYER_INTERNAL_URL || process.env.PLAYER_BASE_URL || 'http://player:8081').replace(/\/$/, ''); + const playerInternalUrl = (process.env.PLAYER_INTERNAL_URL || 'http://player:8081').replace(/\/$/, ''); const bridgeInternalUrl = (process.env.BRIDGE_INTERNAL_URL || 'http://player-bridge:8090').replace(/\/$/, ''); const webInternalUrl = (process.env.WEB_INTERNAL_URL || `http://127.0.0.1:${Number(process.env.WEB_PORT || 8080)}`).replace(/\/$/, ''); const sessionCookieName = 'digital_signage_session'; diff --git a/src/web/routes/admin/client-commands.js b/src/web/routes/admin/client-commands.js index 2588cf0..f0c2426 100644 --- a/src/web/routes/admin/client-commands.js +++ b/src/web/routes/admin/client-commands.js @@ -7,6 +7,7 @@ module.exports = function registerScreenCommandRoutes(app, deps) { const common = deps.common; const forwardPlayerCommand = deps.forwardPlayerCommand; const forwardPlayerCommandToBaseUrl = deps.forwardPlayerCommandToBaseUrl; + const forwardPlayerCommandToDevice = deps.forwardPlayerCommandToDevice; const getScreenConnections = deps.getScreenConnections; const isClientNameAvailable = deps.isClientNameAvailable; const findAvailableClientName = deps.findAvailableClientName; @@ -104,12 +105,51 @@ module.exports = function registerScreenCommandRoutes(app, deps) { } async function forwardPlayerCommandForConnections(screenSlug, connections, commandPayload, connectionId, deviceId) { - const targetBaseUrls = await resolvePlayerBaseUrlsForConnections(connections); + const connectionList = Array.isArray(connections) ? connections : []; + const targetedConnections = connectionId + ? connectionList.filter(function (connection) { + const candidateConnectionId = String(connection && connection.id || '').trim(); + const candidateClientId = String(connection && connection.clientId || '').trim(); + const candidateDeviceId = String(connection && connection.deviceId || '').trim(); + return candidateConnectionId === connectionId + || candidateClientId === connectionId + || candidateDeviceId === connectionId; + }) + : (deviceId + ? connectionList.filter(function (connection) { + return String(connection && connection.deviceId || '').trim() === String(deviceId).trim(); + }) + : connectionList); + const remoteConnections = targetedConnections.filter(function (connection) { + return String(connection && connection.playerDeviceId || '').trim(); + }); + const localConnections = targetedConnections.filter(function (connection) { + return !String(connection && connection.playerDeviceId || '').trim(); + }); + const deviceTargets = Array.from(new Set(remoteConnections.map(function (connection) { + return String(connection && connection.playerDeviceId || '').trim(); + }).filter(Boolean))); + const results = []; + if (deviceTargets.length && typeof forwardPlayerCommandToDevice === 'function') { + const remoteResults = await Promise.allSettled(deviceTargets.map(function (targetDeviceId) { + const payload = Object.assign({ screenSlug: screenSlug }, commandPayload || {}); + if (connectionId) { + payload.connectionId = connectionId; + } + return forwardPlayerCommandToDevice(targetDeviceId, payload); + })); + results.push.apply(results, remoteResults); + } + + const targetBaseUrls = await resolvePlayerBaseUrlsForConnections(localConnections); if (targetBaseUrls.length && typeof forwardPlayerCommandToBaseUrl === 'function') { - const results = await Promise.allSettled(targetBaseUrls.map(function (targetBaseUrl) { + const localResults = await Promise.allSettled(targetBaseUrls.map(function (targetBaseUrl) { return forwardPlayerCommandToBaseUrl(targetBaseUrl, screenSlug, commandPayload, connectionId || deviceId || undefined); })); + results.push.apply(results, localResults); + } + if (results.length) { return { ok: true, sent: results.filter(function (result) { @@ -373,6 +413,7 @@ module.exports = function registerScreenCommandRoutes(app, deps) { return candidateConnectionId === connectionId || candidateClientId === connectionId; }) || null; const liveDeviceId = String(liveConnection && liveConnection.deviceId || '').trim(); + const livePlayerDeviceId = String(liveConnection && liveConnection.playerDeviceId || '').trim(); const livePlayerBaseUrl = normalizeExplicitPlayerBaseUrl(liveConnection && liveConnection.playerPublicBaseUrl); if (!connectionId && !submittedPlayerBaseUrl && !legacyDeviceId && !liveDeviceId) { @@ -382,7 +423,9 @@ module.exports = function registerScreenCommandRoutes(app, deps) { return res.status(400).json({ error: 'Target screen is required' }); } - if (livePlayerBaseUrl || submittedPlayerBaseUrl) { + if (livePlayerDeviceId) { + physicalPlayerId = livePlayerDeviceId; + } else if (livePlayerBaseUrl || submittedPlayerBaseUrl) { const [playerRows] = await pool.query( 'SELECT identifier, public_base_url FROM d_players WHERE public_base_url = ? LIMIT 1', [livePlayerBaseUrl || submittedPlayerBaseUrl] @@ -523,7 +566,7 @@ module.exports = function registerScreenCommandRoutes(app, deps) { || candidateDeviceId === physicalPlayerId; }) || liveConnections[0] || null : null; - const sourcePlayerBaseUrl = registeredPlayerBaseUrl + const sourcePlayerBaseUrl = livePlayerDeviceId ? '' : registeredPlayerBaseUrl || normalizeExplicitPlayerBaseUrl(matchedLiveConnection && matchedLiveConnection.playerPublicBaseUrl); if (sourcePlayerBaseUrl) { targetPlayerUrl = `${sourcePlayerBaseUrl}/screen/${encodeURIComponent(targetScreenSlug)}`; @@ -537,7 +580,13 @@ module.exports = function registerScreenCommandRoutes(app, deps) { connectionId: connectionId || null, screenSlug: targetScreenSlug }); - if (sourcePlayerBaseUrl) { + if (livePlayerDeviceId) { + await forwardPlayerCommandForConnections(slug, liveConnections, { + command: 'redirect', + url: targetPlayerUrl, + moveToken: moveToken || null + }, connectionId || physicalPlayerId || undefined, physicalPlayerId || null); + } else if (sourcePlayerBaseUrl) { await forwardPlayerCommandForPlayerBaseUrl(slug, sourcePlayerBaseUrl, { command: 'redirect', url: targetPlayerUrl, @@ -585,9 +634,12 @@ module.exports = function registerScreenCommandRoutes(app, deps) { : null; const livePlayerBaseUrl = normalizeExplicitPlayerBaseUrl(liveConnection && liveConnection.playerPublicBaseUrl); const targetPlayerBaseUrl = livePlayerBaseUrl || playerBaseUrl; - const result = targetPlayerBaseUrl - ? await forwardPlayerCommandForPlayerBaseUrl(slug, targetPlayerBaseUrl, commandPayload, connectionId, null) - : await forwardPlayerCommandForConnections(slug, liveConnections, commandPayload, connectionId, null); + const liveDeviceId = String(liveConnection && liveConnection.playerDeviceId || '').trim(); + const result = liveDeviceId && typeof forwardPlayerCommandToDevice === 'function' + ? await forwardPlayerCommandToDevice(liveDeviceId, Object.assign({ screenSlug: slug }, commandPayload, connectionId ? { connectionId: connectionId } : {})) + : targetPlayerBaseUrl + ? await forwardPlayerCommandForPlayerBaseUrl(slug, targetPlayerBaseUrl, commandPayload, connectionId, null) + : await forwardPlayerCommandForConnections(slug, liveConnections, commandPayload, connectionId, null); if (typeof broadcastDashboardState === 'function') { await broadcastDashboardState(); diff --git a/src/web/routes/admin/manage.js b/src/web/routes/admin/manage.js index 7ac9f95..0d3bc52 100644 --- a/src/web/routes/admin/manage.js +++ b/src/web/routes/admin/manage.js @@ -15,6 +15,7 @@ module.exports = function registerManageRoutes(app, deps) { const getScreenConnections = deps.getScreenConnections; const forwardPlayerCommand = deps.forwardPlayerCommand; const forwardPlayerCommandToBaseUrl = deps.forwardPlayerCommandToBaseUrl; + const forwardPlayerCommandToDevice = deps.forwardPlayerCommandToDevice; const requirePermission = deps.requirePermission; const SCREEN_NAME_MAX_LENGTH = 255; @@ -88,12 +89,30 @@ module.exports = function registerManageRoutes(app, deps) { } async function forwardPlayerCommandForConnections(slug, connections, commandPayload) { - const targetBaseUrls = await resolvePlayerBaseUrlsForConnections(connections); + const connectionList = Array.isArray(connections) ? connections : []; + const remoteDeviceIds = Array.from(new Set(connectionList.map(function (connection) { + return String(connection && connection.playerDeviceId || '').trim(); + }).filter(Boolean))); + const localConnections = connectionList.filter(function (connection) { + return !String(connection && connection.playerDeviceId || '').trim(); + }); + const results = []; + if (remoteDeviceIds.length && typeof forwardPlayerCommandToDevice === 'function') { + const remoteResults = await Promise.allSettled(remoteDeviceIds.map(function (deviceId) { + return forwardPlayerCommandToDevice(deviceId, Object.assign({ screenSlug: slug }, commandPayload || {})); + })); + results.push.apply(results, remoteResults); + } + + const targetBaseUrls = await resolvePlayerBaseUrlsForConnections(localConnections); if (targetBaseUrls.length && typeof forwardPlayerCommandToBaseUrl === 'function') { - const results = await Promise.allSettled(targetBaseUrls.map(function (targetBaseUrl) { + const localResults = await Promise.allSettled(targetBaseUrls.map(function (targetBaseUrl) { return forwardPlayerCommandToBaseUrl(targetBaseUrl, slug, commandPayload); })); + results.push.apply(results, localResults); + } + if (results.length) { return { ok: true, sent: results.filter(function (result) { diff --git a/src/web/routes/register.js b/src/web/routes/register.js index 4787548..b0a3c9c 100644 --- a/src/web/routes/register.js +++ b/src/web/routes/register.js @@ -180,6 +180,7 @@ function registerSignageRoutes(app, deps) { common: deps.common, forwardPlayerCommand: deps.playerActionService.forwardPlayerCommand, forwardPlayerCommandToBaseUrl: deps.playerActionService.forwardPlayerCommandToBaseUrl, + forwardPlayerCommandToDevice: deps.playerActionService.forwardPlayerCommandToDevice, getScreenConnections: deps.playerActionService.getScreenConnections, isClientNameAvailable: deps.isClientNameAvailable, findAvailableClientName: deps.findAvailableClientName, diff --git a/src/web/routes/signage/screens/form-view-model.js b/src/web/routes/signage/screens/form-view-model.js index 6ea4172..af42af4 100644 --- a/src/web/routes/signage/screens/form-view-model.js +++ b/src/web/routes/signage/screens/form-view-model.js @@ -4,8 +4,7 @@ function buildScreenFormViewModel(screen, data, message, currentUser, isEdit) { const viewScreen = Object.assign({ name: '', slug: '', - playlist_id: null, - player_urls: [] + playlist_id: null }, screen || {}); return { diff --git a/src/web/routes/signage/screens/routes.js b/src/web/routes/signage/screens/routes.js index 9503888..8f7e576 100644 --- a/src/web/routes/signage/screens/routes.js +++ b/src/web/routes/signage/screens/routes.js @@ -1,7 +1,6 @@ // Screen route registration and dashboard wiring. const fs = require('fs'); -const { screenPlayerUrl } = require('../../../routes/common'); module.exports = function registerScreensRoutes(app, deps) { const pool = deps.pool; @@ -12,14 +11,6 @@ module.exports = function registerScreensRoutes(app, deps) { const { buildPagination } = require('../../../lib/pagination'); const requirePermission = deps.requirePermission; - function isRecentPlayerRegistration(player, staleSeconds) { - const lastSeenAt = player && player.last_seen_at; - const lastSeenAtValue = lastSeenAt instanceof Date ? lastSeenAt.getTime() : new Date(lastSeenAt).getTime(); - const cutoffTime = Date.now() - Math.max(30, Number(staleSeconds || 60)) * 1000; - - return Number.isFinite(lastSeenAtValue) && lastSeenAtValue >= cutoffTime; - } - const LIST_PAGE_SIZE = 25; const batTemplatePath = require.resolve('#root/scripts/pulse-signage-kiosk.bat'); const shTemplatePath = require.resolve('#root/scripts/pulse-signage-kiosk.sh'); @@ -90,26 +81,6 @@ module.exports = function registerScreensRoutes(app, deps) { return common.fetchPlayerPublicBaseUrl(pool); } - async function buildScreenPlayerUrls(screen) { - const playerRegistrations = typeof common.fetchPlayerRegistrations === 'function' - ? await common.fetchPlayerRegistrations(pool) - : []; - - return (Array.isArray(playerRegistrations) ? playerRegistrations : []).filter(function (player) { - return isRecentPlayerRegistration(player, 60); - }).map(function (player) { - const baseUrl = String(player && player.public_base_url || '').trim().replace(/\/$/, ''); - const playerUrl = screenPlayerUrl(screen && screen.slug ? screen.slug : '', baseUrl); - return { - identifier: String(player && player.identifier || '').trim(), - public_base_url: baseUrl || null, - player_url: playerUrl || null - }; - }).sort(function (left, right) { - return String(left && left.identifier || '').localeCompare(String(right && right.identifier || ''), undefined, { sensitivity: 'base', numeric: true }); - }); - } - function buildLauncherContent(templatePath, playerUrl) { const template = fs.readFileSync(templatePath, 'utf8'); const normalizedPlayerUrl = String(playerUrl || '').trim(); @@ -139,10 +110,6 @@ module.exports = function registerScreensRoutes(app, deps) { if (!screen) { return res.status(404).send('Screen not found'); } - screen.player_urls = await buildScreenPlayerUrls(screen); - if (screen.player_urls.length) { - screen.launcher_downloads = launcherDownloadPaths; - } screen.inUse = Boolean(await getScreenDeleteBlockMessage(pool, screen, deps.getScreenConnections)); const editData = await common.fetchScreenEditData(pool); return res.send(pages.renderScreenEditPage(screen, editData, req.query.message ? String(req.query.message) : '', req.currentUser)); @@ -168,10 +135,6 @@ module.exports = function registerScreensRoutes(app, deps) { if (!screen) { return res.status(404).send('Screen not found'); } - screen.player_urls = await buildScreenPlayerUrls(screen); - if (screen.player_urls.length) { - screen.launcher_downloads = launcherDownloadPaths; - } screen.inUse = Boolean(await getScreenDeleteBlockMessage(pool, screen, deps.getScreenConnections)); const editData = await common.fetchScreenEditData(pool); return res.send(pages.renderScreenEditPage(screen, editData, req.query.message ? String(req.query.message) : '', req.currentUser)); diff --git a/src/web/views/signage/screens/form.hbs b/src/web/views/signage/screens/form.hbs index e9a9002..cc76b4a 100644 --- a/src/web/views/signage/screens/form.hbs +++ b/src/web/views/signage/screens/form.hbs @@ -1,12 +1,12 @@
{{#if isEdit}}Update the display name and assigned playlist.{{else}}Register a player endpoint and connect it to a playlist.{{/if}}
+{{#if isEdit}}Update the display name and assigned playlist.{{else}}Create a screen group and connect it to a playlist.{{/if}}
| Player | -URL | -
|---|---|
| {{identifier}} | -- {{#if player_url}} - {{player_url}} - {{else}} - Not available - {{/if}} - | -
No player URLs are available yet.
- {{/if}} -