@xiboplayer/pwa 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{chunk-config-Dez8rjcF.js → chunk-config-CtGszMx2.js} +2 -2
- package/dist/assets/{chunk-config-Dez8rjcF.js.map → chunk-config-CtGszMx2.js.map} +1 -1
- package/dist/assets/{index-kkwSU3X8.js → index-5h0f0o88.js} +3 -3
- package/dist/assets/index-5h0f0o88.js.map +1 -0
- package/dist/assets/{index-kgYr440z.js → index-B9GIC3M0.js} +2 -2
- package/dist/assets/{index-kgYr440z.js.map → index-B9GIC3M0.js.map} +1 -1
- package/dist/assets/{index-BDDIykK4.js → index-BDSqvUhR.js} +2 -2
- package/dist/assets/{index-BDDIykK4.js.map → index-BDSqvUhR.js.map} +1 -1
- package/dist/assets/{index-C91YJpfZ.js → index-BDvux0OF.js} +2 -2
- package/dist/assets/{index-C91YJpfZ.js.map → index-BDvux0OF.js.map} +1 -1
- package/dist/assets/{index-CNbx97sr.js → index-BxpD1DTX.js} +2 -2
- package/dist/assets/{index-CNbx97sr.js.map → index-BxpD1DTX.js.map} +1 -1
- package/dist/assets/{index-BOlriOmL.js → index-CE38S9TE.js} +2 -2
- package/dist/assets/{index-BOlriOmL.js.map → index-CE38S9TE.js.map} +1 -1
- package/dist/assets/{index-D1l8ZE3W.js → index-Cl94z6w6.js} +2 -2
- package/dist/assets/{index-D1l8ZE3W.js.map → index-Cl94z6w6.js.map} +1 -1
- package/dist/assets/index-DBfpBHOx.js +2 -0
- package/dist/assets/{index-Dn6nFUZu.js.map → index-DBfpBHOx.js.map} +1 -1
- package/dist/assets/index-IpZ9x4sq.js +2 -0
- package/dist/assets/{index-0wMtWLCA.js.map → index-IpZ9x4sq.js.map} +1 -1
- package/dist/assets/{index-Bw952aoR.js → index-bu0rBE8x.js} +2 -2
- package/dist/assets/{index-Bw952aoR.js.map → index-bu0rBE8x.js.map} +1 -1
- package/dist/assets/{main-DqHeYLmM.js → main-BMbDOb9z.js} +13 -13
- package/dist/assets/main-BMbDOb9z.js.map +1 -0
- package/dist/assets/{protocol-detector-DcYcgi8J.js → protocol-detector-DBvKoxwk.js} +2 -2
- package/dist/assets/{protocol-detector-DcYcgi8J.js.map → protocol-detector-DBvKoxwk.js.map} +1 -1
- package/dist/assets/{setup-C-k9Ij4-.js → setup-XWkrt320.js} +2 -2
- package/dist/assets/{setup-C-k9Ij4-.js.map → setup-XWkrt320.js.map} +1 -1
- package/dist/assets/{sync-manager-ZNXrnSxt.js → sync-manager-DjnEDn56.js} +2 -2
- package/dist/assets/{sync-manager-ZNXrnSxt.js.map → sync-manager-DjnEDn56.js.map} +1 -1
- package/dist/assets/widget-html-Bo9WgcY3.js +2 -0
- package/dist/assets/widget-html-Bo9WgcY3.js.map +1 -0
- package/dist/index.html +3 -3
- package/dist/setup.html +3 -3
- package/dist/sw-pwa.js +1 -1
- package/package.json +13 -13
- package/dist/assets/index-0wMtWLCA.js +0 -2
- package/dist/assets/index-Dn6nFUZu.js +0 -2
- package/dist/assets/index-kkwSU3X8.js.map +0 -1
- package/dist/assets/main-DqHeYLmM.js.map +0 -1
- package/dist/assets/widget-html-BKxutfYG.js +0 -2
- package/dist/assets/widget-html-BKxutfYG.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-Bw952aoR.js","sources":["../../../xmr/src/xmr-client.js","../../../xmr/src/xmr-wrapper.js","../../../xmr/src/index.js"],"sourcesContent":["import { createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('XmrClient');\n\n/**\n * Native XMR (Xibo Message Relay) WebSocket Client\n *\n * Drop-in replacement for @xibosignage/xibo-communication-framework.\n * Uses a generic action dispatcher — emit(message.action, message) — so\n * every CMS action works automatically without a hardcoded if-else chain.\n *\n * API-compatible with the upstream Xmr class:\n * new XmrClient(channel) → .init() → .start(url, key) → .on(event, cb)\n */\n\nexport class XmrClient {\n /**\n * @param {string} channel - XMR channel identifier (e.g. \"player-HWKEY\")\n */\n constructor(channel) {\n this.channel = channel;\n this.url = null;\n this.cmsKey = null;\n this.socket = null;\n this.isConnected = false;\n this.isConnectionWanted = false;\n this.lastMessageAt = 0;\n this._interval = null;\n this._listeners = new Map(); // event → Set<callback>\n }\n\n /**\n * Register an event listener.\n * @param {string} event\n * @param {Function} callback\n * @returns {Function} Unsubscribe function\n */\n on(event, callback) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(callback);\n return () => this._listeners.get(event)?.delete(callback);\n }\n\n /**\n * Emit an event to all registered listeners.\n * @param {string} event\n * @param {...*} args\n */\n emit(event, ...args) {\n const listeners = this._listeners.get(event);\n if (!listeners) return;\n for (const cb of listeners) {\n try {\n cb(...args);\n } catch (e) {\n log.error(`Listener error for '${event}':`, e);\n }\n }\n }\n\n /**\n * Initialize the reconnect interval (60s health check).\n * Same cadence as upstream framework.\n */\n async init() {\n if (this._interval) return;\n this._interval = setInterval(() => {\n if (this.isConnectionWanted && !this.isActive()) {\n this.start(this.url || 'DISABLED', this.cmsKey || 'n/a');\n }\n }, 60_000);\n }\n\n /**\n * Connect to XMR WebSocket server.\n * @param {string} url - WebSocket URL (ws:// or wss://)\n * @param {string} cmsKey - CMS authentication key\n */\n async start(url, cmsKey) {\n this.url = url;\n this.cmsKey = cmsKey;\n this.isConnectionWanted = true;\n\n // Close existing socket if any\n if (this.socket) {\n try { this.socket.close(); } catch (_) { /* ignore */ }\n this.socket = null;\n this.isConnected = false;\n }\n\n try {\n this.socket = new WebSocket(url);\n } catch (e) {\n this.emit('error', 'Failed to connect');\n return;\n }\n\n this.socket.addEventListener('open', () => {\n this.socket.send(JSON.stringify({\n type: 'init',\n key: this.cmsKey,\n channel: this.channel,\n }));\n this.isConnected = true;\n this.lastMessageAt = Date.now();\n this.emit('connected');\n });\n\n this.socket.addEventListener('close', () => {\n this.isConnected = false;\n this.emit('disconnected');\n });\n\n this.socket.addEventListener('error', () => {\n this.emit('error', 'error');\n });\n\n this.socket.addEventListener('message', (event) => {\n this.lastMessageAt = Date.now();\n\n // Heartbeat\n if (event.data === 'H') return;\n\n // JSON action message\n try {\n const message = JSON.parse(event.data);\n if (!message.action) return;\n\n // TTL check: createdDt (ISO 8601) + ttl seconds > now\n if (message.createdDt && message.ttl) {\n const created = Date.parse(message.createdDt);\n if (!isNaN(created)) {\n const expiresAt = created + parseInt(message.ttl) * 1000;\n if (expiresAt < Date.now()) return; // expired\n }\n }\n\n // Generic dispatch — every CMS action works automatically\n this.emit(message.action, message);\n } catch (e) {\n log.error('Failed to parse message:', e);\n }\n });\n }\n\n /**\n * Stop the connection and clear the reconnect interval.\n */\n async stop() {\n this.isConnectionWanted = false;\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n this.isConnected = false;\n }\n }\n\n /**\n * Send a message to the server via WebSocket.\n * @param {string} action - Action name\n * @param {*} data - Data payload\n */\n async send(action, data) {\n if (!this.socket || !this.isConnected) {\n throw new Error('Not connected');\n }\n this.socket.send(JSON.stringify({ action, ...data }));\n }\n\n /**\n * Check if the connection is active (connected + message within 15min).\n * @returns {boolean}\n */\n isActive() {\n return this.isConnected && (Date.now() - this.lastMessageAt) < 15 * 60 * 1000;\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n/**\n * XMR (Xibo Message Relay) Wrapper\n *\n * Integrates the native XmrClient (xmr-client.js) to enable real-time\n * push commands from CMS via WebSocket.\n *\n * Connection lifecycle is delegated to XmrClient, which has a\n * built-in 60s health-check interval that reconnects automatically.\n * This wrapper only routes events to player callbacks.\n *\n * Supported commands:\n * - collectNow: Trigger immediate XMDS collection cycle\n * - screenShot/screenshot: Capture and upload screenshot\n * - licenceCheck: No-op for Linux clients (always valid)\n * - changeLayout: Switch to a specific layout immediately\n * - overlayLayout: Push overlay layout on top of current content\n * - revertToSchedule: Return to normal scheduled content\n * - purgeAll: Clear all cached files and re-download\n * - commandAction: Execute a player command (HTTP only in browser)\n * - triggerWebhook: Fire a webhook trigger action\n * - dataUpdate: Force refresh of data connectors\n * - rekey: RSA key pair rotation (for XMR encryption)\n * - criteriaUpdate: Update display criteria and re-collect\n * - currentGeoLocation: Report current geo location to CMS\n */\n\nimport { XmrClient } from './xmr-client.js';\nimport { createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('XMR');\n\nexport class XmrWrapper {\n /**\n * @param {Object} config - Player configuration\n * @param {Object} player - Player instance for callbacks\n */\n constructor(config, player) {\n this.config = config;\n this.player = player;\n this.xmr = null;\n this.connected = false;\n }\n\n /**\n * Initialize and start XMR connection.\n *\n * Creates a single Xmr instance and lets the framework manage\n * reconnection via its internal 60s health-check timer.\n * Calling start() again on an already-running instance is safe —\n * the framework skips if already connected to the same URL.\n *\n * @param {string} xmrUrl - WebSocket URL (ws:// or wss://)\n * @param {string} cmsKey - CMS authentication key\n * @returns {Promise<boolean>} Success status\n */\n async start(xmrUrl, cmsKey) {\n try {\n // Reuse existing instance — the framework handles reconnection.\n // Only create a new instance on first call or after stop().\n if (!this.xmr) {\n log.info('Initializing connection to:', xmrUrl);\n const channel = this.config.xmrChannel || `player-${this.config.hardwareKey}`;\n this.xmr = new XmrClient(channel);\n this.setupEventHandlers();\n await this.xmr.init();\n }\n\n await this.xmr.start(xmrUrl, cmsKey);\n this.connected = true;\n log.info('Connected successfully');\n\n return true;\n } catch (error) {\n log.warn('Failed to start:', error.message);\n log.info('Framework will retry automatically every 60s');\n\n return false;\n }\n }\n\n /**\n * Setup event handlers for CMS commands\n */\n setupEventHandlers() {\n if (!this.xmr) return;\n\n // Connection events\n this.xmr.on('connected', () => {\n log.info('WebSocket connected');\n this.connected = true;\n this.player.emit?.('xmr-status', { connected: true });\n });\n\n this.xmr.on('disconnected', () => {\n log.warn('WebSocket disconnected (framework will reconnect)');\n this.connected = false;\n this.player.emit?.('xmr-status', { connected: false });\n });\n\n this.xmr.on('error', (error) => {\n log.error('WebSocket error:', error);\n });\n\n // CMS command: Collect Now\n this.xmr.on('collectNow', async () => {\n log.info('Received collectNow command from CMS');\n try {\n await this.player.collect();\n log.debug('collectNow completed successfully');\n } catch (error) {\n log.error('collectNow failed:', error);\n }\n });\n\n // CMS command: Screenshot\n this.xmr.on('screenShot', async () => {\n log.info('Received screenShot command from CMS');\n try {\n await this.player.captureScreenshot();\n log.debug('screenShot completed successfully');\n } catch (error) {\n log.error('screenShot failed:', error);\n }\n });\n\n // CMS command: License Check (no-op for Linux clients)\n this.xmr.on('licenceCheck', () => {\n log.debug('Received licenceCheck (no-op for Linux client)');\n });\n\n // CMS command: Change Layout\n // Payload may be a layoutId string or an object with { layoutId, duration, downloadRequired, changeMode }\n this.xmr.on('changeLayout', async (data) => {\n const layoutId = typeof data === 'object' ? (data.layoutId || data) : data;\n const duration = typeof data === 'object' ? (parseInt(data.duration) || 0) : 0;\n const changeMode = typeof data === 'object' ? (data.changeMode || 'replace') : 'replace';\n log.info('Received changeLayout command:', layoutId, duration ? `duration=${duration}s` : '', changeMode !== 'replace' ? `mode=${changeMode}` : '');\n try {\n if (typeof data === 'object' && data.downloadRequired === true) {\n log.info('changeLayout: downloadRequired — triggering collection first');\n await this.player.collect();\n }\n await this.player.changeLayout(layoutId, { duration, changeMode });\n log.debug('changeLayout completed successfully');\n } catch (error) {\n log.error('changeLayout failed:', error);\n }\n });\n\n // CMS command: Overlay Layout\n // Payload may be a layoutId string or an object with { layoutId, duration, downloadRequired }\n this.xmr.on('overlayLayout', async (data) => {\n const layoutId = typeof data === 'object' ? (data.layoutId || data) : data;\n const duration = typeof data === 'object' ? (parseInt(data.duration) || 0) : 0;\n log.info('Received overlayLayout command:', layoutId, duration ? `duration=${duration}s` : '');\n try {\n if (typeof data === 'object' && data.downloadRequired === true) {\n log.info('overlayLayout: downloadRequired — triggering collection first');\n await this.player.collect();\n }\n await this.player.overlayLayout(layoutId, { duration });\n log.debug('overlayLayout completed successfully');\n } catch (error) {\n log.error('overlayLayout failed:', error);\n }\n });\n\n // CMS command: Revert to Schedule\n this.xmr.on('revertToSchedule', async () => {\n log.info('Received revertToSchedule command');\n try {\n await this.player.revertToSchedule();\n log.debug('revertToSchedule completed successfully');\n } catch (error) {\n log.error('revertToSchedule failed:', error);\n }\n });\n\n // CMS command: Purge All\n this.xmr.on('purgeAll', async () => {\n log.info('Received purgeAll command');\n try {\n await this.player.purgeAll();\n log.debug('purgeAll completed successfully');\n } catch (error) {\n log.error('purgeAll failed:', error);\n }\n });\n\n // CMS command: Execute Command\n // Resolve command from local display settings (from RegisterDisplay), not from XMR payload\n this.xmr.on('commandAction', async (data) => {\n const commandCode = data?.commandCode || data;\n log.info('Received commandAction command:', commandCode);\n try {\n const localCommands = this.player.displayCommands || data?.commands;\n await this.player.executeCommand(commandCode, localCommands);\n log.debug('commandAction completed successfully');\n } catch (error) {\n log.error('commandAction failed:', error);\n }\n });\n\n // CMS command: Trigger Webhook\n this.xmr.on('triggerWebhook', async (data) => {\n log.info('Received triggerWebhook command:', data);\n try {\n this.player.triggerWebhook(data?.triggerCode || data);\n log.debug('triggerWebhook completed successfully');\n } catch (error) {\n log.error('triggerWebhook failed:', error);\n }\n });\n\n // CMS command: Data Update (force refresh data connectors)\n this.xmr.on('dataUpdate', async () => {\n log.info('Received dataUpdate command');\n try {\n this.player.refreshDataConnectors();\n log.debug('dataUpdate completed successfully');\n } catch (error) {\n log.error('dataUpdate failed:', error);\n }\n });\n\n // CMS command: Rekey (RSA key pair rotation) — spec event name is 'rekeyAction'\n this.xmr.on('rekeyAction', async () => {\n log.info('Received rekeyAction command - rotating RSA key pair');\n try {\n this.config.data.xmrPubKey = '';\n this.config.data.xmrPrivKey = '';\n await this.config.ensureXmrKeyPair();\n await this.player.collect();\n log.info('RSA key pair rotated successfully');\n } catch (error) {\n log.error('Key rotation failed:', error);\n }\n });\n\n // CMS command: Criteria Update\n this.xmr.on('criteriaUpdate', async (data) => {\n log.info('Received criteriaUpdate command:', data);\n try {\n await this.player.collect();\n log.debug('criteriaUpdate completed successfully');\n } catch (error) {\n log.error('criteriaUpdate failed:', error);\n }\n });\n\n // CMS command: Current Geo Location\n // Dual-path: if data has coordinates, CMS is telling us our location.\n // If data is empty/no coordinates, CMS is asking us to report our location.\n this.xmr.on('currentGeoLocation', async (data) => {\n log.info('Received currentGeoLocation command:', data);\n try {\n const hasCoordinates = data && data.latitude != null && data.longitude != null;\n\n if (hasCoordinates) {\n if (this.player.reportGeoLocation) {\n this.player.reportGeoLocation(data);\n log.debug('currentGeoLocation: coordinates applied');\n } else {\n log.warn('Geo location reporting not implemented in player');\n }\n } else {\n if (this.player.requestGeoLocation) {\n await this.player.requestGeoLocation();\n log.debug('currentGeoLocation: browser location requested');\n } else {\n log.warn('Geo location request not implemented in player');\n }\n }\n } catch (error) {\n log.error('currentGeoLocation failed:', error);\n }\n });\n }\n\n /**\n * Stop XMR connection and clean up the framework instance.\n * The framework's internal 60s timer is cleared when the instance\n * is discarded, so no reconnection will occur after stop().\n */\n async stop() {\n if (!this.xmr) return;\n\n try {\n await this.xmr.stop();\n this.connected = false;\n this.xmr = null;\n log.info('Stopped');\n } catch (error) {\n log.error('Error stopping:', error);\n }\n }\n\n /**\n * Check if XMR is connected\n * @returns {boolean}\n */\n isConnected() {\n return this.connected;\n }\n\n /**\n * Send a message to CMS (if needed for future features)\n * @param {string} action - Action name\n * @param {Object} data - Data payload\n */\n async send(action, data) {\n if (!this.connected || !this.xmr) {\n log.warn('Cannot send - not connected');\n return false;\n }\n\n try {\n await this.xmr.send(action, data);\n return true;\n } catch (error) {\n log.error('Error sending:', error);\n return false;\n }\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n// @xiboplayer/xmr - XMR WebSocket client\nimport pkg from '../package.json' with { type: 'json' };\nexport const VERSION = pkg.version;\nexport { XmrWrapper } from './xmr-wrapper.js';\n"],"names":["log","createLogger","XmrClient","channel","event","callback","_a","args","listeners","cb","e","url","cmsKey","message","created","action","data","XmrWrapper","config","player","xmrUrl","error","_b","layoutId","duration","changeMode","commandCode","localCommands","VERSION","pkg"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-bu0rBE8x.js","sources":["../../../xmr/src/xmr-client.js","../../../xmr/src/xmr-wrapper.js","../../../xmr/src/index.js"],"sourcesContent":["import { createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('XmrClient');\n\n/**\n * Native XMR (Xibo Message Relay) WebSocket Client\n *\n * Drop-in replacement for @xibosignage/xibo-communication-framework.\n * Uses a generic action dispatcher — emit(message.action, message) — so\n * every CMS action works automatically without a hardcoded if-else chain.\n *\n * API-compatible with the upstream Xmr class:\n * new XmrClient(channel) → .init() → .start(url, key) → .on(event, cb)\n */\n\nexport class XmrClient {\n /**\n * @param {string} channel - XMR channel identifier (e.g. \"player-HWKEY\")\n */\n constructor(channel) {\n this.channel = channel;\n this.url = null;\n this.cmsKey = null;\n this.socket = null;\n this.isConnected = false;\n this.isConnectionWanted = false;\n this.lastMessageAt = 0;\n this._interval = null;\n this._listeners = new Map(); // event → Set<callback>\n }\n\n /**\n * Register an event listener.\n * @param {string} event\n * @param {Function} callback\n * @returns {Function} Unsubscribe function\n */\n on(event, callback) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(callback);\n return () => this._listeners.get(event)?.delete(callback);\n }\n\n /**\n * Emit an event to all registered listeners.\n * @param {string} event\n * @param {...*} args\n */\n emit(event, ...args) {\n const listeners = this._listeners.get(event);\n if (!listeners) return;\n for (const cb of listeners) {\n try {\n cb(...args);\n } catch (e) {\n log.error(`Listener error for '${event}':`, e);\n }\n }\n }\n\n /**\n * Initialize the reconnect interval (60s health check).\n * Same cadence as upstream framework.\n */\n async init() {\n if (this._interval) return;\n this._interval = setInterval(() => {\n if (this.isConnectionWanted && !this.isActive()) {\n this.start(this.url || 'DISABLED', this.cmsKey || 'n/a');\n }\n }, 60_000);\n }\n\n /**\n * Connect to XMR WebSocket server.\n * @param {string} url - WebSocket URL (ws:// or wss://)\n * @param {string} cmsKey - CMS authentication key\n */\n async start(url, cmsKey) {\n this.url = url;\n this.cmsKey = cmsKey;\n this.isConnectionWanted = true;\n\n // Close existing socket if any\n if (this.socket) {\n try { this.socket.close(); } catch (_) { /* ignore */ }\n this.socket = null;\n this.isConnected = false;\n }\n\n try {\n this.socket = new WebSocket(url);\n } catch (e) {\n this.emit('error', 'Failed to connect');\n return;\n }\n\n this.socket.addEventListener('open', () => {\n this.socket.send(JSON.stringify({\n type: 'init',\n key: this.cmsKey,\n channel: this.channel,\n }));\n this.isConnected = true;\n this.lastMessageAt = Date.now();\n this.emit('connected');\n });\n\n this.socket.addEventListener('close', () => {\n this.isConnected = false;\n this.emit('disconnected');\n });\n\n this.socket.addEventListener('error', () => {\n this.emit('error', 'error');\n });\n\n this.socket.addEventListener('message', (event) => {\n this.lastMessageAt = Date.now();\n\n // Heartbeat\n if (event.data === 'H') return;\n\n // JSON action message\n try {\n const message = JSON.parse(event.data);\n if (!message.action) return;\n\n // TTL check: createdDt (ISO 8601) + ttl seconds > now\n if (message.createdDt && message.ttl) {\n const created = Date.parse(message.createdDt);\n if (!isNaN(created)) {\n const expiresAt = created + parseInt(message.ttl) * 1000;\n if (expiresAt < Date.now()) return; // expired\n }\n }\n\n // Generic dispatch — every CMS action works automatically\n this.emit(message.action, message);\n } catch (e) {\n log.error('Failed to parse message:', e);\n }\n });\n }\n\n /**\n * Stop the connection and clear the reconnect interval.\n */\n async stop() {\n this.isConnectionWanted = false;\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n this.isConnected = false;\n }\n }\n\n /**\n * Send a message to the server via WebSocket.\n * @param {string} action - Action name\n * @param {*} data - Data payload\n */\n async send(action, data) {\n if (!this.socket || !this.isConnected) {\n throw new Error('Not connected');\n }\n this.socket.send(JSON.stringify({ action, ...data }));\n }\n\n /**\n * Check if the connection is active (connected + message within 15min).\n * @returns {boolean}\n */\n isActive() {\n return this.isConnected && (Date.now() - this.lastMessageAt) < 15 * 60 * 1000;\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n/**\n * XMR (Xibo Message Relay) Wrapper\n *\n * Integrates the native XmrClient (xmr-client.js) to enable real-time\n * push commands from CMS via WebSocket.\n *\n * Connection lifecycle is delegated to XmrClient, which has a\n * built-in 60s health-check interval that reconnects automatically.\n * This wrapper only routes events to player callbacks.\n *\n * Supported commands:\n * - collectNow: Trigger immediate XMDS collection cycle\n * - screenShot/screenshot: Capture and upload screenshot\n * - licenceCheck: No-op for Linux clients (always valid)\n * - changeLayout: Switch to a specific layout immediately\n * - overlayLayout: Push overlay layout on top of current content\n * - revertToSchedule: Return to normal scheduled content\n * - purgeAll: Clear all cached files and re-download\n * - commandAction: Execute a player command (HTTP only in browser)\n * - triggerWebhook: Fire a webhook trigger action\n * - dataUpdate: Force refresh of data connectors\n * - rekey: RSA key pair rotation (for XMR encryption)\n * - criteriaUpdate: Update display criteria and re-collect\n * - currentGeoLocation: Report current geo location to CMS\n */\n\nimport { XmrClient } from './xmr-client.js';\nimport { createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('XMR');\n\nexport class XmrWrapper {\n /**\n * @param {Object} config - Player configuration\n * @param {Object} player - Player instance for callbacks\n */\n constructor(config, player) {\n this.config = config;\n this.player = player;\n this.xmr = null;\n this.connected = false;\n }\n\n /**\n * Initialize and start XMR connection.\n *\n * Creates a single Xmr instance and lets the framework manage\n * reconnection via its internal 60s health-check timer.\n * Calling start() again on an already-running instance is safe —\n * the framework skips if already connected to the same URL.\n *\n * @param {string} xmrUrl - WebSocket URL (ws:// or wss://)\n * @param {string} cmsKey - CMS authentication key\n * @returns {Promise<boolean>} Success status\n */\n async start(xmrUrl, cmsKey) {\n try {\n // Reuse existing instance — the framework handles reconnection.\n // Only create a new instance on first call or after stop().\n if (!this.xmr) {\n log.info('Initializing connection to:', xmrUrl);\n const channel = this.config.xmrChannel || `player-${this.config.hardwareKey}`;\n this.xmr = new XmrClient(channel);\n this.setupEventHandlers();\n await this.xmr.init();\n }\n\n await this.xmr.start(xmrUrl, cmsKey);\n this.connected = true;\n log.info('Connected successfully');\n\n return true;\n } catch (error) {\n log.warn('Failed to start:', error.message);\n log.info('Framework will retry automatically every 60s');\n\n return false;\n }\n }\n\n /**\n * Setup event handlers for CMS commands\n */\n setupEventHandlers() {\n if (!this.xmr) return;\n\n // Connection events\n this.xmr.on('connected', () => {\n log.info('WebSocket connected');\n this.connected = true;\n this.player.emit?.('xmr-status', { connected: true });\n });\n\n this.xmr.on('disconnected', () => {\n log.warn('WebSocket disconnected (framework will reconnect)');\n this.connected = false;\n this.player.emit?.('xmr-status', { connected: false });\n });\n\n this.xmr.on('error', (error) => {\n log.error('WebSocket error:', error);\n });\n\n // CMS command: Collect Now\n this.xmr.on('collectNow', async () => {\n log.info('Received collectNow command from CMS');\n try {\n await this.player.collect();\n log.debug('collectNow completed successfully');\n } catch (error) {\n log.error('collectNow failed:', error);\n }\n });\n\n // CMS command: Screenshot\n this.xmr.on('screenShot', async () => {\n log.info('Received screenShot command from CMS');\n try {\n await this.player.captureScreenshot();\n log.debug('screenShot completed successfully');\n } catch (error) {\n log.error('screenShot failed:', error);\n }\n });\n\n // CMS command: License Check (no-op for Linux clients)\n this.xmr.on('licenceCheck', () => {\n log.debug('Received licenceCheck (no-op for Linux client)');\n });\n\n // CMS command: Change Layout\n // Payload may be a layoutId string or an object with { layoutId, duration, downloadRequired, changeMode }\n this.xmr.on('changeLayout', async (data) => {\n const layoutId = typeof data === 'object' ? (data.layoutId || data) : data;\n const duration = typeof data === 'object' ? (parseInt(data.duration) || 0) : 0;\n const changeMode = typeof data === 'object' ? (data.changeMode || 'replace') : 'replace';\n log.info('Received changeLayout command:', layoutId, duration ? `duration=${duration}s` : '', changeMode !== 'replace' ? `mode=${changeMode}` : '');\n try {\n if (typeof data === 'object' && data.downloadRequired === true) {\n log.info('changeLayout: downloadRequired — triggering collection first');\n await this.player.collect();\n }\n await this.player.changeLayout(layoutId, { duration, changeMode });\n log.debug('changeLayout completed successfully');\n } catch (error) {\n log.error('changeLayout failed:', error);\n }\n });\n\n // CMS command: Overlay Layout\n // Payload may be a layoutId string or an object with { layoutId, duration, downloadRequired }\n this.xmr.on('overlayLayout', async (data) => {\n const layoutId = typeof data === 'object' ? (data.layoutId || data) : data;\n const duration = typeof data === 'object' ? (parseInt(data.duration) || 0) : 0;\n log.info('Received overlayLayout command:', layoutId, duration ? `duration=${duration}s` : '');\n try {\n if (typeof data === 'object' && data.downloadRequired === true) {\n log.info('overlayLayout: downloadRequired — triggering collection first');\n await this.player.collect();\n }\n await this.player.overlayLayout(layoutId, { duration });\n log.debug('overlayLayout completed successfully');\n } catch (error) {\n log.error('overlayLayout failed:', error);\n }\n });\n\n // CMS command: Revert to Schedule\n this.xmr.on('revertToSchedule', async () => {\n log.info('Received revertToSchedule command');\n try {\n await this.player.revertToSchedule();\n log.debug('revertToSchedule completed successfully');\n } catch (error) {\n log.error('revertToSchedule failed:', error);\n }\n });\n\n // CMS command: Purge All\n this.xmr.on('purgeAll', async () => {\n log.info('Received purgeAll command');\n try {\n await this.player.purgeAll();\n log.debug('purgeAll completed successfully');\n } catch (error) {\n log.error('purgeAll failed:', error);\n }\n });\n\n // CMS command: Execute Command\n // Resolve command from local display settings (from RegisterDisplay), not from XMR payload\n this.xmr.on('commandAction', async (data) => {\n const commandCode = data?.commandCode || data;\n log.info('Received commandAction command:', commandCode);\n try {\n const localCommands = this.player.displayCommands || data?.commands;\n await this.player.executeCommand(commandCode, localCommands);\n log.debug('commandAction completed successfully');\n } catch (error) {\n log.error('commandAction failed:', error);\n }\n });\n\n // CMS command: Trigger Webhook\n this.xmr.on('triggerWebhook', async (data) => {\n log.info('Received triggerWebhook command:', data);\n try {\n this.player.triggerWebhook(data?.triggerCode || data);\n log.debug('triggerWebhook completed successfully');\n } catch (error) {\n log.error('triggerWebhook failed:', error);\n }\n });\n\n // CMS command: Data Update (force refresh data connectors)\n this.xmr.on('dataUpdate', async () => {\n log.info('Received dataUpdate command');\n try {\n this.player.refreshDataConnectors();\n log.debug('dataUpdate completed successfully');\n } catch (error) {\n log.error('dataUpdate failed:', error);\n }\n });\n\n // CMS command: Rekey (RSA key pair rotation) — spec event name is 'rekeyAction'\n this.xmr.on('rekeyAction', async () => {\n log.info('Received rekeyAction command - rotating RSA key pair');\n try {\n this.config.data.xmrPubKey = '';\n this.config.data.xmrPrivKey = '';\n await this.config.ensureXmrKeyPair();\n await this.player.collect();\n log.info('RSA key pair rotated successfully');\n } catch (error) {\n log.error('Key rotation failed:', error);\n }\n });\n\n // CMS command: Criteria Update\n this.xmr.on('criteriaUpdate', async (data) => {\n log.info('Received criteriaUpdate command:', data);\n try {\n await this.player.collect();\n log.debug('criteriaUpdate completed successfully');\n } catch (error) {\n log.error('criteriaUpdate failed:', error);\n }\n });\n\n // CMS command: Current Geo Location\n // Dual-path: if data has coordinates, CMS is telling us our location.\n // If data is empty/no coordinates, CMS is asking us to report our location.\n this.xmr.on('currentGeoLocation', async (data) => {\n log.info('Received currentGeoLocation command:', data);\n try {\n const hasCoordinates = data && data.latitude != null && data.longitude != null;\n\n if (hasCoordinates) {\n if (this.player.reportGeoLocation) {\n this.player.reportGeoLocation(data);\n log.debug('currentGeoLocation: coordinates applied');\n } else {\n log.warn('Geo location reporting not implemented in player');\n }\n } else {\n if (this.player.requestGeoLocation) {\n await this.player.requestGeoLocation();\n log.debug('currentGeoLocation: browser location requested');\n } else {\n log.warn('Geo location request not implemented in player');\n }\n }\n } catch (error) {\n log.error('currentGeoLocation failed:', error);\n }\n });\n }\n\n /**\n * Stop XMR connection and clean up the framework instance.\n * The framework's internal 60s timer is cleared when the instance\n * is discarded, so no reconnection will occur after stop().\n */\n async stop() {\n if (!this.xmr) return;\n\n try {\n await this.xmr.stop();\n this.connected = false;\n this.xmr = null;\n log.info('Stopped');\n } catch (error) {\n log.error('Error stopping:', error);\n }\n }\n\n /**\n * Check if XMR is connected\n * @returns {boolean}\n */\n isConnected() {\n return this.connected;\n }\n\n /**\n * Send a message to CMS (if needed for future features)\n * @param {string} action - Action name\n * @param {Object} data - Data payload\n */\n async send(action, data) {\n if (!this.connected || !this.xmr) {\n log.warn('Cannot send - not connected');\n return false;\n }\n\n try {\n await this.xmr.send(action, data);\n return true;\n } catch (error) {\n log.error('Error sending:', error);\n return false;\n }\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n// @xiboplayer/xmr - XMR WebSocket client\nimport pkg from '../package.json' with { type: 'json' };\nexport const VERSION = pkg.version;\nexport { XmrWrapper } from './xmr-wrapper.js';\n"],"names":["log","createLogger","XmrClient","channel","event","callback","_a","args","listeners","cb","e","url","cmsKey","message","created","action","data","XmrWrapper","config","player","xmrUrl","error","_b","layoutId","duration","changeMode","commandCode","localCommands","VERSION","pkg"],"mappings":"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"}
|