@xiboplayer/pwa 0.7.16 → 0.7.17
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/{main-BxyDlH8x.js → main-CnQTxL1o.js} +4 -4
- package/dist/assets/{main-BxyDlH8x.js.map → main-CnQTxL1o.js.map} +1 -1
- package/dist/assets/{main-DpLXt_A1.js → main-DZZRd32h.js} +3 -3
- package/dist/assets/{main-DpLXt_A1.js.map → main-DZZRd32h.js.map} +1 -1
- package/dist/assets/{pdf-CfAvK6QT.js → pdf-CEqPPzMl.js} +2 -2
- package/dist/assets/{pdf-CfAvK6QT.js.map → pdf-CEqPPzMl.js.map} +1 -1
- package/dist/assets/{setup-Dr31Tx8X.js → setup-CxMnBRfM.js} +2 -2
- package/dist/assets/{setup-Dr31Tx8X.js.map → setup-CxMnBRfM.js.map} +1 -1
- package/dist/assets/{src-BCLLyeh_.js → src-BaaaVytN.js} +2 -2
- package/dist/assets/{src-BCLLyeh_.js.map → src-BaaaVytN.js.map} +1 -1
- package/dist/assets/{src-B-j3XW3-.js → src-CNTwwhBq.js} +3 -3
- package/dist/assets/{src-B-j3XW3-.js.map → src-CNTwwhBq.js.map} +1 -1
- package/dist/assets/{src-DAN2Deef.js → src-CdA_SXE_.js} +2 -2
- package/dist/assets/{src-DAN2Deef.js.map → src-CdA_SXE_.js.map} +1 -1
- package/dist/assets/{src-BSReyr0v.js → src-Cvry9zYs.js} +2 -2
- package/dist/assets/{src-BSReyr0v.js.map → src-Cvry9zYs.js.map} +1 -1
- package/dist/assets/{src-vZDSh8Wj.js → src-DDl_ym1Y.js} +2 -2
- package/dist/assets/{src-vZDSh8Wj.js.map → src-DDl_ym1Y.js.map} +1 -1
- package/dist/assets/{src-DlR7dlm-.js → src-DFEuLLEl.js} +2 -2
- package/dist/assets/{src-DlR7dlm-.js.map → src-DFEuLLEl.js.map} +1 -1
- package/dist/assets/{src-Xntm4zXY.js → src-DHhjVCet.js} +3 -3
- package/dist/assets/{src-Xntm4zXY.js.map → src-DHhjVCet.js.map} +1 -1
- package/dist/assets/{src-D8PF0mN4.js → src-DWLlXvs6.js} +2 -2
- package/dist/assets/{src-D8PF0mN4.js.map → src-DWLlXvs6.js.map} +1 -1
- package/dist/assets/{src-Kaan1uxV.js → src-DYcXC04Y.js} +2 -2
- package/dist/assets/{src-Kaan1uxV.js.map → src-DYcXC04Y.js.map} +1 -1
- package/dist/assets/{src-bWHpkXu3.js → src-re534rLt.js} +2 -2
- package/dist/assets/{src-bWHpkXu3.js.map → src-re534rLt.js.map} +1 -1
- package/dist/assets/{sync-manager-D6eMk5NM.js → sync-manager-lMIYGCuU.js} +2 -2
- package/dist/assets/{sync-manager-D6eMk5NM.js.map → sync-manager-lMIYGCuU.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/setup.html +3 -3
- package/dist/sw-pwa.js +2 -2
- package/dist/sw-pwa.js.map +1 -1
- package/package.json +13 -13
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as e,u as t}from"./src-
|
|
2
|
-
//# sourceMappingURL=src-
|
|
1
|
+
import{c as e,u as t}from"./src-CNTwwhBq.js";var n={name:`@xiboplayer/settings`,version:`0.7.17`,description:`CMS settings management and application for xiboplayer`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./manager":`./src/settings.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/utils":`workspace:*`},devDependencies:{jsdom:`^29.0.1`,vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`settings`,`configuration`,`display-settings`],author:`Pau Aliagas <linuxnow@gmail.com>`,license:`AGPL-3.0-or-later`,repository:{type:`git`,url:`git+https://github.com/xibo-players/xiboplayer.git`,directory:`packages/settings`},homepage:`https://xiboplayer.org`},r=t(`DisplaySettings`),i=class extends e{constructor(){super(),this.settings={collectInterval:300,displayName:`Unknown Display`,sizeX:1920,sizeY:1080,statsEnabled:!1,aggregationLevel:`Individual`,logLevel:`error`,xmrNetworkAddress:null,xmrWebSocketAddress:null,xmrCmsKey:null,preventSleep:!0,embeddedServerPort:9696,screenshotInterval:120,downloadStartWindow:null,downloadEndWindow:null,licenceCode:null,isSspEnabled:!1}}applySettings(e){if(!e)return r.warn(`No settings provided`),{changed:[],settings:this.settings};let t=[],n=this.settings.collectInterval;return this.settings.collectInterval=this.parseCollectInterval(e.collectInterval||e.CollectInterval),this.settings.displayName=e.displayName||e.DisplayName||this.settings.displayName,this.settings.sizeX=parseInt(e.sizeX||e.SizeX||this.settings.sizeX),this.settings.sizeY=parseInt(e.sizeY||e.SizeY||this.settings.sizeY),this.settings.statsEnabled=this.parseBoolean(e.statsEnabled||e.StatsEnabled),this.settings.aggregationLevel=e.aggregationLevel||e.AggregationLevel||this.settings.aggregationLevel,this.settings.logLevel=e.logLevel||e.LogLevel||this.settings.logLevel,this.settings.xmrNetworkAddress=e.xmrNetworkAddress||e.XmrNetworkAddress||this.settings.xmrNetworkAddress,this.settings.xmrWebSocketAddress=e.xmrWebSocketAddress||e.XmrWebSocketAddress||this.settings.xmrWebSocketAddress,this.settings.xmrCmsKey=e.xmrCmsKey||e.XmrCmsKey||this.settings.xmrCmsKey,this.settings.preventSleep=this.parseBoolean(e.preventSleep||e.PreventSleep,!0),this.settings.embeddedServerPort=parseInt(e.embeddedServerPort||e.EmbeddedServerPort||this.settings.embeddedServerPort),this.settings.screenshotInterval=parseInt(e.screenshotInterval||e.ScreenshotInterval||this.settings.screenshotInterval),this.settings.downloadStartWindow=e.downloadStartWindow||e.DownloadStartWindow||this.settings.downloadStartWindow,this.settings.downloadEndWindow=e.downloadEndWindow||e.DownloadEndWindow||this.settings.downloadEndWindow,this.settings.licenceCode=e.licenceCode||e.LicenceCode||this.settings.licenceCode,this.settings.isSspEnabled=this.parseBoolean(e.isAdspaceEnabled||e.IsAdspaceEnabled),n!==this.settings.collectInterval&&(t.push(`collectInterval`),this.emit(`interval-changed`,this.settings.collectInterval)),this.emit(`settings-applied`,this.settings,t),r.info(`Applied settings:`,{collectInterval:this.settings.collectInterval,displayName:this.settings.displayName,statsEnabled:this.settings.statsEnabled,changes:t}),{changed:t,settings:this.settings}}parseCollectInterval(e){let t=parseInt(e,10);return isNaN(t)||t<60?300:t>86400?86400:t}parseBoolean(e,t=!1){return e===!0||e===!1?e:e===`1`||e===1?!0:e===`0`||e===0?!1:t}getCollectInterval(){return this.settings.collectInterval}getDisplayName(){return this.settings.displayName}getDisplaySize(){return{width:this.settings.sizeX,height:this.settings.sizeY}}isStatsEnabled(){return this.settings.statsEnabled}getAllSettings(){return{...this.settings}}getSetting(e,t=null){return this.settings[e]===void 0?t:this.settings[e]}isInDownloadWindow(){if(!this.settings.downloadStartWindow||!this.settings.downloadEndWindow||this.settings.downloadStartWindow===`:`||this.settings.downloadEndWindow===`:`)return!0;try{let e=new Date,t=e.getHours()*60+e.getMinutes(),n=this.parseTimeWindow(this.settings.downloadStartWindow),r=this.parseTimeWindow(this.settings.downloadEndWindow);return n>r?t>=n||t<r:t>=n&&t<r}catch(e){return r.warn(`Failed to parse download window:`,e),!0}}parseTimeWindow(e){if(!e||typeof e!=`string`)throw Error(`Invalid time window format`);let t=e.split(`:`);if(t.length!==2)throw Error(`Invalid time window format (expected HH:MM)`);let n=parseInt(t[0],10),r=parseInt(t[1],10);if(isNaN(n)||isNaN(r)||n<0||n>23||r<0||r>59)throw Error(`Invalid time window values`);return n*60+r}getNextDownloadWindow(){if(!this.settings.downloadStartWindow||!this.settings.downloadEndWindow||this.settings.downloadStartWindow===`:`||this.settings.downloadEndWindow===`:`)return null;try{let e=new Date,t=e.getHours()*60+e.getMinutes(),n=this.parseTimeWindow(this.settings.downloadStartWindow),r=new Date(e);return t<n||r.setDate(r.getDate()+1),r.setHours(Math.floor(n/60),n%60,0,0),r}catch(e){return r.warn(`Failed to calculate next download window:`,e),null}}shouldTakeScreenshot(e){return e?(Date.now()-e.getTime())/1e3>=this.settings.screenshotInterval:!0}},a=n.version;export{i as DisplaySettings,a as VERSION};
|
|
2
|
+
//# sourceMappingURL=src-CdA_SXE_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-DAN2Deef.js","names":["pkg"],"sources":["../../../settings/package.json","../../../settings/src/settings.js","../../../settings/src/index.js"],"sourcesContent":["{\n \"name\": \"@xiboplayer/settings\",\n \"version\": \"0.7.16\",\n \"description\": \"CMS settings management and application for xiboplayer\",\n \"type\": \"module\",\n \"main\": \"./src/index.js\",\n \"types\": \"./src/index.d.ts\",\n \"exports\": {\n \".\": \"./src/index.js\",\n \"./manager\": \"./src/settings.js\"\n },\n \"scripts\": {\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@xiboplayer/utils\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"jsdom\": \"^29.0.1\",\n \"vitest\": \"^4.1.2\"\n },\n \"keywords\": [\n \"xibo\",\n \"digital-signage\",\n \"settings\",\n \"configuration\",\n \"display-settings\"\n ],\n \"author\": \"Pau Aliagas <linuxnow@gmail.com>\",\n \"license\": \"AGPL-3.0-or-later\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/xibo-players/xiboplayer.git\",\n \"directory\": \"packages/settings\"\n },\n \"homepage\": \"https://xiboplayer.org\"\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n/**\n * DisplaySettings - CMS display settings management\n *\n * Parses and applies configuration from RegisterDisplay response.\n * Based on upstream electron-player implementation.\n *\n * Architecture:\n * ┌─────────────────────────────────────────────────────┐\n * │ PlayerCore │\n * │ - Receives RegisterDisplay response │\n * │ - Passes to DisplaySettings.applySettings() │\n * └─────────────────────────────────────────────────────┘\n * ↓\n * ┌─────────────────────────────────────────────────────┐\n * │ DisplaySettings (this module) │\n * │ - Parse all CMS settings │\n * │ - Validate and normalize values │\n * │ - Apply collection interval │\n * │ - Check download windows │\n * │ - Handle screenshot requests │\n * │ - Emit events on changes │\n * └─────────────────────────────────────────────────────┘\n * ↓\n * ┌─────────────────────────────────────────────────────┐\n * │ Platform Layer (PWA/Electron/Mobile) │\n * │ - Listen for setting change events │\n * │ - Update UI with display name │\n * │ - Handle screenshot requests │\n * │ - Respect download windows │\n * └─────────────────────────────────────────────────────┘\n *\n * Usage:\n * const settings = new DisplaySettings();\n * settings.applySettings(regResult.settings);\n *\n * // Get settings\n * const collectInterval = settings.getCollectInterval();\n * const canDownload = settings.isInDownloadWindow();\n *\n * // Listen for changes\n * settings.on('interval-changed', (newInterval) => { ... });\n */\n\nimport { EventEmitter, createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('DisplaySettings');\n\nexport class DisplaySettings extends EventEmitter {\n constructor() {\n super();\n\n // Current settings (with defaults)\n this.settings = {\n // Collection\n collectInterval: 300, // seconds (5 minutes default)\n\n // Display info\n displayName: 'Unknown Display',\n sizeX: 1920,\n sizeY: 1080,\n\n // Stats\n statsEnabled: false,\n aggregationLevel: 'Individual', // or 'Aggregate'\n\n // Logging\n logLevel: 'error', // 'error', 'audit', 'info', 'debug'\n\n // XMR\n xmrNetworkAddress: null,\n xmrWebSocketAddress: null,\n xmrCmsKey: null,\n\n // Features\n preventSleep: true,\n embeddedServerPort: 9696,\n screenshotInterval: 120, // seconds\n\n // Download windows\n downloadStartWindow: null,\n downloadEndWindow: null,\n\n // License\n licenceCode: null,\n\n // SSP (ad space)\n isSspEnabled: false,\n };\n }\n\n /**\n * Apply settings from RegisterDisplay response\n * @param {Object} settings - Raw settings from CMS\n * @returns {Object} Applied settings with changes\n */\n applySettings(settings) {\n if (!settings) {\n log.warn('No settings provided');\n return { changed: [], settings: this.settings };\n }\n\n const changes = [];\n const oldInterval = this.settings.collectInterval;\n\n // Parse all settings with defaults\n // Handle both lowercase and CamelCase (uppercase first letter)\n this.settings.collectInterval = this.parseCollectInterval(settings.collectInterval || settings.CollectInterval);\n this.settings.displayName = settings.displayName || settings.DisplayName || this.settings.displayName;\n this.settings.sizeX = parseInt(settings.sizeX || settings.SizeX || this.settings.sizeX);\n this.settings.sizeY = parseInt(settings.sizeY || settings.SizeY || this.settings.sizeY);\n\n // Stats\n this.settings.statsEnabled = this.parseBoolean(settings.statsEnabled || settings.StatsEnabled);\n this.settings.aggregationLevel = settings.aggregationLevel || settings.AggregationLevel || this.settings.aggregationLevel;\n\n // Logging\n this.settings.logLevel = settings.logLevel || settings.LogLevel || this.settings.logLevel;\n\n // XMR\n this.settings.xmrNetworkAddress = settings.xmrNetworkAddress || settings.XmrNetworkAddress || this.settings.xmrNetworkAddress;\n this.settings.xmrWebSocketAddress = settings.xmrWebSocketAddress || settings.XmrWebSocketAddress || this.settings.xmrWebSocketAddress;\n this.settings.xmrCmsKey = settings.xmrCmsKey || settings.XmrCmsKey || this.settings.xmrCmsKey;\n\n // Features\n this.settings.preventSleep = this.parseBoolean(settings.preventSleep || settings.PreventSleep, true);\n this.settings.embeddedServerPort = parseInt(settings.embeddedServerPort || settings.EmbeddedServerPort || this.settings.embeddedServerPort);\n this.settings.screenshotInterval = parseInt(settings.screenshotInterval || settings.ScreenshotInterval || this.settings.screenshotInterval);\n\n // Download windows\n this.settings.downloadStartWindow = settings.downloadStartWindow || settings.DownloadStartWindow || this.settings.downloadStartWindow;\n this.settings.downloadEndWindow = settings.downloadEndWindow || settings.DownloadEndWindow || this.settings.downloadEndWindow;\n\n // License\n this.settings.licenceCode = settings.licenceCode || settings.LicenceCode || this.settings.licenceCode;\n\n // SSP\n this.settings.isSspEnabled = this.parseBoolean(settings.isAdspaceEnabled || settings.IsAdspaceEnabled);\n\n // Detect changes\n if (oldInterval !== this.settings.collectInterval) {\n changes.push('collectInterval');\n this.emit('interval-changed', this.settings.collectInterval);\n }\n\n // Emit generic settings-applied event\n this.emit('settings-applied', this.settings, changes);\n\n log.info('Applied settings:', {\n collectInterval: this.settings.collectInterval,\n displayName: this.settings.displayName,\n statsEnabled: this.settings.statsEnabled,\n changes\n });\n\n return { changed: changes, settings: this.settings };\n }\n\n /**\n * Parse collection interval (seconds)\n * @param {*} value - Raw value from CMS\n * @returns {number} Collection interval in seconds\n */\n parseCollectInterval(value) {\n const interval = parseInt(value, 10);\n\n // Validate range (minimum 60s, maximum 86400s = 24h)\n if (isNaN(interval) || interval < 60) {\n return 300; // 5 minutes default\n }\n\n if (interval > 86400) {\n return 86400; // 24 hours max\n }\n\n return interval;\n }\n\n /**\n * Parse boolean setting\n * @param {*} value - Raw value from CMS (string '1' or '0', or boolean)\n * @param {boolean} defaultValue - Default if not set\n * @returns {boolean}\n */\n parseBoolean(value, defaultValue = false) {\n if (value === true || value === false) {\n return value;\n }\n\n if (value === '1' || value === 1) {\n return true;\n }\n\n if (value === '0' || value === 0) {\n return false;\n }\n\n return defaultValue;\n }\n\n /**\n * Get collection interval in seconds\n * @returns {number}\n */\n getCollectInterval() {\n return this.settings.collectInterval;\n }\n\n /**\n * Get display name\n * @returns {string}\n */\n getDisplayName() {\n return this.settings.displayName;\n }\n\n /**\n * Get display size\n * @returns {{ width: number, height: number }}\n */\n getDisplaySize() {\n return {\n width: this.settings.sizeX,\n height: this.settings.sizeY\n };\n }\n\n /**\n * Check if stats are enabled\n * @returns {boolean}\n */\n isStatsEnabled() {\n return this.settings.statsEnabled;\n }\n\n /**\n * Get all settings\n * @returns {Object}\n */\n getAllSettings() {\n return { ...this.settings };\n }\n\n /**\n * Get a specific setting by key\n * @param {string} key - Setting key\n * @param {*} defaultValue - Default value if not set\n * @returns {*}\n */\n getSetting(key, defaultValue = null) {\n return this.settings[key] !== undefined ? this.settings[key] : defaultValue;\n }\n\n /**\n * Check if current time is within download window\n * @returns {boolean}\n */\n isInDownloadWindow() {\n // If no download window configured, always allow\n // CMS sends \":\" when unconfigured, treat as empty\n if (!this.settings.downloadStartWindow || !this.settings.downloadEndWindow ||\n this.settings.downloadStartWindow === ':' || this.settings.downloadEndWindow === ':') {\n return true;\n }\n\n try {\n const now = new Date();\n const currentTime = now.getHours() * 60 + now.getMinutes();\n\n const start = this.parseTimeWindow(this.settings.downloadStartWindow);\n const end = this.parseTimeWindow(this.settings.downloadEndWindow);\n\n // Handle overnight window (e.g., 22:00 - 06:00)\n if (start > end) {\n // Overnight: allow if AFTER start OR BEFORE end\n return currentTime >= start || currentTime < end;\n } else {\n // Same day: allow if AFTER start AND BEFORE end\n return currentTime >= start && currentTime < end;\n }\n } catch (error) {\n log.warn('Failed to parse download window:', error);\n return true; // Allow downloads if parsing fails\n }\n }\n\n /**\n * Parse time window string to minutes since midnight\n * @param {string} timeStr - Time string (e.g., \"14:30\", \"22:00\")\n * @returns {number} Minutes since midnight\n */\n parseTimeWindow(timeStr) {\n if (!timeStr || typeof timeStr !== 'string') {\n throw new Error('Invalid time window format');\n }\n\n const parts = timeStr.split(':');\n if (parts.length !== 2) {\n throw new Error('Invalid time window format (expected HH:MM)');\n }\n\n const hours = parseInt(parts[0], 10);\n const minutes = parseInt(parts[1], 10);\n\n if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {\n throw new Error('Invalid time window values');\n }\n\n return hours * 60 + minutes;\n }\n\n /**\n * Get next download window start time\n * @returns {Date|null} Next window start, or null if always allowed\n */\n getNextDownloadWindow() {\n if (!this.settings.downloadStartWindow || !this.settings.downloadEndWindow ||\n this.settings.downloadStartWindow === ':' || this.settings.downloadEndWindow === ':') {\n return null;\n }\n\n try {\n const now = new Date();\n const currentTime = now.getHours() * 60 + now.getMinutes();\n const start = this.parseTimeWindow(this.settings.downloadStartWindow);\n\n const nextWindow = new Date(now);\n\n if (currentTime < start) {\n // Window is later today\n nextWindow.setHours(Math.floor(start / 60), start % 60, 0, 0);\n } else {\n // Window is tomorrow\n nextWindow.setDate(nextWindow.getDate() + 1);\n nextWindow.setHours(Math.floor(start / 60), start % 60, 0, 0);\n }\n\n return nextWindow;\n } catch (error) {\n log.warn('Failed to calculate next download window:', error);\n return null;\n }\n }\n\n /**\n * Check if screenshot interval has elapsed\n * @param {Date} lastScreenshot - Last screenshot timestamp\n * @returns {boolean}\n */\n shouldTakeScreenshot(lastScreenshot) {\n if (!lastScreenshot) {\n return true;\n }\n\n const elapsed = (Date.now() - lastScreenshot.getTime()) / 1000;\n return elapsed >= this.settings.screenshotInterval;\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n// @xiboplayer/settings - CMS settings management\nimport pkg from '../package.json' with { type: 'json' };\nexport const VERSION = pkg.version;\n\n/**\n * Settings manager for xiboplayer\n * @module @xiboplayer/settings\n */\nexport { DisplaySettings } from './settings.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"src-CdA_SXE_.js","names":["pkg"],"sources":["../../../settings/package.json","../../../settings/src/settings.js","../../../settings/src/index.js"],"sourcesContent":["{\n \"name\": \"@xiboplayer/settings\",\n \"version\": \"0.7.17\",\n \"description\": \"CMS settings management and application for xiboplayer\",\n \"type\": \"module\",\n \"main\": \"./src/index.js\",\n \"types\": \"./src/index.d.ts\",\n \"exports\": {\n \".\": \"./src/index.js\",\n \"./manager\": \"./src/settings.js\"\n },\n \"scripts\": {\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@xiboplayer/utils\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"jsdom\": \"^29.0.1\",\n \"vitest\": \"^4.1.2\"\n },\n \"keywords\": [\n \"xibo\",\n \"digital-signage\",\n \"settings\",\n \"configuration\",\n \"display-settings\"\n ],\n \"author\": \"Pau Aliagas <linuxnow@gmail.com>\",\n \"license\": \"AGPL-3.0-or-later\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/xibo-players/xiboplayer.git\",\n \"directory\": \"packages/settings\"\n },\n \"homepage\": \"https://xiboplayer.org\"\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n/**\n * DisplaySettings - CMS display settings management\n *\n * Parses and applies configuration from RegisterDisplay response.\n * Based on upstream electron-player implementation.\n *\n * Architecture:\n * ┌─────────────────────────────────────────────────────┐\n * │ PlayerCore │\n * │ - Receives RegisterDisplay response │\n * │ - Passes to DisplaySettings.applySettings() │\n * └─────────────────────────────────────────────────────┘\n * ↓\n * ┌─────────────────────────────────────────────────────┐\n * │ DisplaySettings (this module) │\n * │ - Parse all CMS settings │\n * │ - Validate and normalize values │\n * │ - Apply collection interval │\n * │ - Check download windows │\n * │ - Handle screenshot requests │\n * │ - Emit events on changes │\n * └─────────────────────────────────────────────────────┘\n * ↓\n * ┌─────────────────────────────────────────────────────┐\n * │ Platform Layer (PWA/Electron/Mobile) │\n * │ - Listen for setting change events │\n * │ - Update UI with display name │\n * │ - Handle screenshot requests │\n * │ - Respect download windows │\n * └─────────────────────────────────────────────────────┘\n *\n * Usage:\n * const settings = new DisplaySettings();\n * settings.applySettings(regResult.settings);\n *\n * // Get settings\n * const collectInterval = settings.getCollectInterval();\n * const canDownload = settings.isInDownloadWindow();\n *\n * // Listen for changes\n * settings.on('interval-changed', (newInterval) => { ... });\n */\n\nimport { EventEmitter, createLogger } from '@xiboplayer/utils';\n\nconst log = createLogger('DisplaySettings');\n\nexport class DisplaySettings extends EventEmitter {\n constructor() {\n super();\n\n // Current settings (with defaults)\n this.settings = {\n // Collection\n collectInterval: 300, // seconds (5 minutes default)\n\n // Display info\n displayName: 'Unknown Display',\n sizeX: 1920,\n sizeY: 1080,\n\n // Stats\n statsEnabled: false,\n aggregationLevel: 'Individual', // or 'Aggregate'\n\n // Logging\n logLevel: 'error', // 'error', 'audit', 'info', 'debug'\n\n // XMR\n xmrNetworkAddress: null,\n xmrWebSocketAddress: null,\n xmrCmsKey: null,\n\n // Features\n preventSleep: true,\n embeddedServerPort: 9696,\n screenshotInterval: 120, // seconds\n\n // Download windows\n downloadStartWindow: null,\n downloadEndWindow: null,\n\n // License\n licenceCode: null,\n\n // SSP (ad space)\n isSspEnabled: false,\n };\n }\n\n /**\n * Apply settings from RegisterDisplay response\n * @param {Object} settings - Raw settings from CMS\n * @returns {Object} Applied settings with changes\n */\n applySettings(settings) {\n if (!settings) {\n log.warn('No settings provided');\n return { changed: [], settings: this.settings };\n }\n\n const changes = [];\n const oldInterval = this.settings.collectInterval;\n\n // Parse all settings with defaults\n // Handle both lowercase and CamelCase (uppercase first letter)\n this.settings.collectInterval = this.parseCollectInterval(settings.collectInterval || settings.CollectInterval);\n this.settings.displayName = settings.displayName || settings.DisplayName || this.settings.displayName;\n this.settings.sizeX = parseInt(settings.sizeX || settings.SizeX || this.settings.sizeX);\n this.settings.sizeY = parseInt(settings.sizeY || settings.SizeY || this.settings.sizeY);\n\n // Stats\n this.settings.statsEnabled = this.parseBoolean(settings.statsEnabled || settings.StatsEnabled);\n this.settings.aggregationLevel = settings.aggregationLevel || settings.AggregationLevel || this.settings.aggregationLevel;\n\n // Logging\n this.settings.logLevel = settings.logLevel || settings.LogLevel || this.settings.logLevel;\n\n // XMR\n this.settings.xmrNetworkAddress = settings.xmrNetworkAddress || settings.XmrNetworkAddress || this.settings.xmrNetworkAddress;\n this.settings.xmrWebSocketAddress = settings.xmrWebSocketAddress || settings.XmrWebSocketAddress || this.settings.xmrWebSocketAddress;\n this.settings.xmrCmsKey = settings.xmrCmsKey || settings.XmrCmsKey || this.settings.xmrCmsKey;\n\n // Features\n this.settings.preventSleep = this.parseBoolean(settings.preventSleep || settings.PreventSleep, true);\n this.settings.embeddedServerPort = parseInt(settings.embeddedServerPort || settings.EmbeddedServerPort || this.settings.embeddedServerPort);\n this.settings.screenshotInterval = parseInt(settings.screenshotInterval || settings.ScreenshotInterval || this.settings.screenshotInterval);\n\n // Download windows\n this.settings.downloadStartWindow = settings.downloadStartWindow || settings.DownloadStartWindow || this.settings.downloadStartWindow;\n this.settings.downloadEndWindow = settings.downloadEndWindow || settings.DownloadEndWindow || this.settings.downloadEndWindow;\n\n // License\n this.settings.licenceCode = settings.licenceCode || settings.LicenceCode || this.settings.licenceCode;\n\n // SSP\n this.settings.isSspEnabled = this.parseBoolean(settings.isAdspaceEnabled || settings.IsAdspaceEnabled);\n\n // Detect changes\n if (oldInterval !== this.settings.collectInterval) {\n changes.push('collectInterval');\n this.emit('interval-changed', this.settings.collectInterval);\n }\n\n // Emit generic settings-applied event\n this.emit('settings-applied', this.settings, changes);\n\n log.info('Applied settings:', {\n collectInterval: this.settings.collectInterval,\n displayName: this.settings.displayName,\n statsEnabled: this.settings.statsEnabled,\n changes\n });\n\n return { changed: changes, settings: this.settings };\n }\n\n /**\n * Parse collection interval (seconds)\n * @param {*} value - Raw value from CMS\n * @returns {number} Collection interval in seconds\n */\n parseCollectInterval(value) {\n const interval = parseInt(value, 10);\n\n // Validate range (minimum 60s, maximum 86400s = 24h)\n if (isNaN(interval) || interval < 60) {\n return 300; // 5 minutes default\n }\n\n if (interval > 86400) {\n return 86400; // 24 hours max\n }\n\n return interval;\n }\n\n /**\n * Parse boolean setting\n * @param {*} value - Raw value from CMS (string '1' or '0', or boolean)\n * @param {boolean} defaultValue - Default if not set\n * @returns {boolean}\n */\n parseBoolean(value, defaultValue = false) {\n if (value === true || value === false) {\n return value;\n }\n\n if (value === '1' || value === 1) {\n return true;\n }\n\n if (value === '0' || value === 0) {\n return false;\n }\n\n return defaultValue;\n }\n\n /**\n * Get collection interval in seconds\n * @returns {number}\n */\n getCollectInterval() {\n return this.settings.collectInterval;\n }\n\n /**\n * Get display name\n * @returns {string}\n */\n getDisplayName() {\n return this.settings.displayName;\n }\n\n /**\n * Get display size\n * @returns {{ width: number, height: number }}\n */\n getDisplaySize() {\n return {\n width: this.settings.sizeX,\n height: this.settings.sizeY\n };\n }\n\n /**\n * Check if stats are enabled\n * @returns {boolean}\n */\n isStatsEnabled() {\n return this.settings.statsEnabled;\n }\n\n /**\n * Get all settings\n * @returns {Object}\n */\n getAllSettings() {\n return { ...this.settings };\n }\n\n /**\n * Get a specific setting by key\n * @param {string} key - Setting key\n * @param {*} defaultValue - Default value if not set\n * @returns {*}\n */\n getSetting(key, defaultValue = null) {\n return this.settings[key] !== undefined ? this.settings[key] : defaultValue;\n }\n\n /**\n * Check if current time is within download window\n * @returns {boolean}\n */\n isInDownloadWindow() {\n // If no download window configured, always allow\n // CMS sends \":\" when unconfigured, treat as empty\n if (!this.settings.downloadStartWindow || !this.settings.downloadEndWindow ||\n this.settings.downloadStartWindow === ':' || this.settings.downloadEndWindow === ':') {\n return true;\n }\n\n try {\n const now = new Date();\n const currentTime = now.getHours() * 60 + now.getMinutes();\n\n const start = this.parseTimeWindow(this.settings.downloadStartWindow);\n const end = this.parseTimeWindow(this.settings.downloadEndWindow);\n\n // Handle overnight window (e.g., 22:00 - 06:00)\n if (start > end) {\n // Overnight: allow if AFTER start OR BEFORE end\n return currentTime >= start || currentTime < end;\n } else {\n // Same day: allow if AFTER start AND BEFORE end\n return currentTime >= start && currentTime < end;\n }\n } catch (error) {\n log.warn('Failed to parse download window:', error);\n return true; // Allow downloads if parsing fails\n }\n }\n\n /**\n * Parse time window string to minutes since midnight\n * @param {string} timeStr - Time string (e.g., \"14:30\", \"22:00\")\n * @returns {number} Minutes since midnight\n */\n parseTimeWindow(timeStr) {\n if (!timeStr || typeof timeStr !== 'string') {\n throw new Error('Invalid time window format');\n }\n\n const parts = timeStr.split(':');\n if (parts.length !== 2) {\n throw new Error('Invalid time window format (expected HH:MM)');\n }\n\n const hours = parseInt(parts[0], 10);\n const minutes = parseInt(parts[1], 10);\n\n if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {\n throw new Error('Invalid time window values');\n }\n\n return hours * 60 + minutes;\n }\n\n /**\n * Get next download window start time\n * @returns {Date|null} Next window start, or null if always allowed\n */\n getNextDownloadWindow() {\n if (!this.settings.downloadStartWindow || !this.settings.downloadEndWindow ||\n this.settings.downloadStartWindow === ':' || this.settings.downloadEndWindow === ':') {\n return null;\n }\n\n try {\n const now = new Date();\n const currentTime = now.getHours() * 60 + now.getMinutes();\n const start = this.parseTimeWindow(this.settings.downloadStartWindow);\n\n const nextWindow = new Date(now);\n\n if (currentTime < start) {\n // Window is later today\n nextWindow.setHours(Math.floor(start / 60), start % 60, 0, 0);\n } else {\n // Window is tomorrow\n nextWindow.setDate(nextWindow.getDate() + 1);\n nextWindow.setHours(Math.floor(start / 60), start % 60, 0, 0);\n }\n\n return nextWindow;\n } catch (error) {\n log.warn('Failed to calculate next download window:', error);\n return null;\n }\n }\n\n /**\n * Check if screenshot interval has elapsed\n * @param {Date} lastScreenshot - Last screenshot timestamp\n * @returns {boolean}\n */\n shouldTakeScreenshot(lastScreenshot) {\n if (!lastScreenshot) {\n return true;\n }\n\n const elapsed = (Date.now() - lastScreenshot.getTime()) / 1000;\n return elapsed >= this.settings.screenshotInterval;\n }\n}\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (c) 2024-2026 Pau Aliagas <linuxnow@gmail.com>\n// @xiboplayer/settings - CMS settings management\nimport pkg from '../package.json' with { type: 'json' };\nexport const VERSION = pkg.version;\n\n/**\n * Settings manager for xiboplayer\n * @module @xiboplayer/settings\n */\nexport { DisplaySettings } from './settings.js';\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{n as e}from"./chunk-7ZXdHUL4.js";import{u as t}from"./src-B-j3XW3-.js";var n={name:`@xiboplayer/schedule`,version:`0.7.16`,description:`Complete scheduling solution: campaigns, dayparting, interrupts, and overlays`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./schedule":`./src/schedule.js`,"./interrupts":`./src/interrupts.js`,"./overlays":`./src/overlays.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/utils":`workspace:*`},devDependencies:{vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`scheduling`,`dayparting`,`campaigns`,`interrupts`,`overlays`,`shareOfVoice`],author:`Pau Aliagas <linuxnow@gmail.com>`,license:`AGPL-3.0-or-later`,repository:{type:`git`,url:`git+https://github.com/xibo-players/xiboplayer.git`,directory:`packages/schedule`},homepage:`https://xiboplayer.org`},r=t(`schedule:criteria`),i=[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`],a={weatherTemp:`temperature`,weatherHumidity:`humidity`,weatherWindSpeed:`windSpeed`,weatherCondition:`condition`,weatherCloudCover:`cloudCover`};function o(e,t,n={},o={}){switch(e){case`dayOfWeek`:return i[t.getDay()];case`dayOfMonth`:return String(t.getDate());case`month`:return String(t.getMonth()+1);case`hour`:return String(t.getHours());case`isoDay`:return String(t.getDay()===0?7:t.getDay());default:if(a[e]){let t=a[e];return o[t]===void 0?(r.debug(`Weather metric "${e}" requested but no weather data available`),null):String(o[t])}return n[e]===void 0?(r.debug(`Unknown metric: ${e}`),null):String(n[e])}}function s(e,t,n,i){if(e===null)return!1;if(i===`number`){let r=parseFloat(e),i=parseFloat(n);if(isNaN(r)||isNaN(i))return!1;switch(t){case`equals`:return r===i;case`notEquals`:return r!==i;case`greaterThan`:return r>i;case`greaterThanOrEquals`:return r>=i;case`lessThan`:return r<i;case`lessThanOrEquals`:return r<=i;default:return!1}}let a=e.toLowerCase(),o=n.toLowerCase();switch(t){case`equals`:return a===o;case`notEquals`:return a!==o;case`contains`:return a.includes(o);case`notContains`:return!a.includes(o);case`startsWith`:return a.startsWith(o);case`endsWith`:return a.endsWith(o);case`in`:return o.split(`,`).map(e=>e.trim().toLowerCase()).includes(a);case`greaterThan`:return a>o;case`lessThan`:return a<o;default:return r.debug(`Unknown condition: ${t}`),!1}}function c(e,t={}){if(!e||e.length===0)return!0;let n=t.now||new Date,i=t.displayProperties||{},a=t.weatherData||{};for(let t of e){let e=o(t.metric,n,i,a);if(!s(e,t.condition,t.value,t.type))return r.debug(`Criteria failed: ${t.metric} ${t.condition} "${t.value}" (actual: "${e}")`),!1}return!0}function l(e){return parseInt(String(e).replace(`.xlf`,``),10)}function u(e,t=null){let n=new DOMParser().parseFromString(e,`text/xml`).querySelector(`layout`);if(!n)return{duration:60,isDynamic:!1};let r=parseInt(n.getAttribute(`duration`)||`0`,10);if(r>0)return{duration:r,isDynamic:!1};let i=0,a=!1;for(let e of n.querySelectorAll(`region`)){let n=e.getAttribute(`type`);if(n===`drawer`)continue;let r=n===`canvas`,o=0;for(let n of e.querySelectorAll(`media`)){let e=parseInt(n.getAttribute(`duration`)||`0`,10),i=parseInt(n.getAttribute(`useDuration`)||`1`,10),s=n.getAttribute(`fileId`)||``,c=t?.get(s),l;c===void 0?e>0&&i!==0?l=e:(l=60,a=!0):l=c,r?o=Math.max(o,l):o+=l}i=Math.max(i,o)}return{duration:i>0?i:60,isDynamic:a}}function d(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function f(e,t,n){if(!t||t===0)return!0;let r=n-36e5,i=e.filter(e=>e>r);if(i.length>=t)return!1;if(i.length>0){let e=36e5/t;if(n-Math.max(...i)<e)return!1}return!0}function p(e,t,n){let r=e.filter(e=>!e.maxPlaysPerHour||e.maxPlaysPerHour===0?!0:f(t.get(e.file)||[],e.maxPlaysPerHour,n));if(r.length===0)return[];let i=Math.max(...r.map(e=>e.priority));return r.filter(e=>e.priority===i).map(e=>e.file)}function m(e,t,n={}){let r=n.from||new Date,i=n.hours||2,a=new Date(r.getTime()+i*36e5);n.currentLayoutStartedAt;let o=n.defaultLayout||null,s=n.durations||null;if(!e||e.length===0)return[];let c=[],l=new Date(r),u=t%e.length;for(;l<a&&c.length<500;){let t=e[u],n=s&&s.get(t.layoutId)||t.duration,r=l.getTime()+n*1e3;c.push({layoutFile:t.layoutId,startTime:new Date(l),endTime:new Date(r),duration:n,isDefault:o?t.layoutId===o:!1}),l=new Date(r),u=(u+1)%e.length}return c}function h(e,t){for(e=Math.abs(Math.round(e)),t=Math.abs(Math.round(t));t;)[e,t]=[t,e%t];return e}function g(e,t){return e===0||t===0?0:Math.abs(Math.round(e)*Math.round(t))/h(e,t)}function _(e){return e.reduce((e,t)=>g(e,t),1)}function v(e,t,n={}){let{defaultLayout:r=null,defaultDuration:i=60}=n;if(e.length===0&&!r)return{queue:[],periodSeconds:0};let a=new Map;for(let t of e)t.duration>0&&a.set(t.file,t.duration);let o=e=>t.get(e)||a.get(e)||i,s=e.filter(e=>e.maxPlaysPerHour>0),c;s.length>0?(c=_(s.map(e=>Math.round(3600/e.maxPlaysPerHour))),c>7200&&(c=7200)):c=e.reduce((e,t)=>e+o(t.file),0)+(r&&!e.some(e=>e.file===r)?o(r):0)||i;let l=[],u=new Map,f=0,m=c*1e3;for(;f<m&&l.length<500;){let t=p(e,u,f);if(t.length===0){if(r){let e=o(r);l.push({layoutId:r,duration:e}),f+=e*1e3}else f+=6e4;continue}for(let n=0;n<t.length&&f<m&&l.length<500;n++){let r=t[n],i=o(r);if(l.push({layoutId:r,duration:i}),u.has(r)||u.set(r,[]),u.get(r).push(f),f+=i*1e3,!d(t,p(e,u,f)))break}}if(l.length===0&&r){let e=o(r);l.push({layoutId:r,duration:e})}return{queue:l,periodSeconds:c}}var y=t(`Schedule`),b=class{constructor(e={}){this.schedule=null,this.playHistory=new Map,this.interruptScheduler=e.interruptScheduler||null,this.displayProperties=e.displayProperties||{},this.weatherData={},this.playerLocation=null,this._layoutMetadata=new Map,this._scheduleQueue=null,this._queuePosition=0,this._queueLayoutSet=null}setSchedule(e){this.schedule=e,this._invalidateQueue()}setWeatherData(e){this.weatherData=e||{}}getDataConnectors(){return this.schedule?.dataConnectors||[]}getDependantsMap(){let e=new Map;if(!this.schedule)return e;let t=this.schedule.dependants||[],n=n=>{let r=l(n.file||n.id),i=[...t,...n.dependants||[]];i.length>0&&e.set(r,i)};if(this.schedule.layouts)for(let e of this.schedule.layouts)n(e);if(this.schedule.campaigns)for(let e of this.schedule.campaigns)for(let t of e.layouts)n(t);return e}isRecurringScheduleActive(e,t){if(!e.recurrenceType)return!0;if(e.recurrenceRange&&t>new Date(e.recurrenceRange))return!1;switch(e.recurrenceType){case`Week`:if(e.recurrenceRepeatsOn){let n=this.getIsoDayOfWeek(t);if(!e.recurrenceRepeatsOn.split(`,`).map(e=>parseInt(e.trim())).includes(n))return!1}return!0;case`Day`:{let n=e.recurrenceDetail||1;if(n>1&&e.fromdt){let r=new Date(e.fromdt),i=t.getTime()-r.getTime(),a=Math.floor(i/864e5);if(a<0||a%n!==0)return!1}return!0}case`Month`:{if(e.recurrenceRepeatsOn){let n=e.recurrenceRepeatsOn.split(`,`).map(e=>parseInt(e.trim())),r=t.getDate();if(!n.includes(r))return!1}let n=e.recurrenceDetail||1;if(n>1&&e.fromdt){let r=new Date(e.fromdt),i=(t.getFullYear()-r.getFullYear())*12+t.getMonth()-r.getMonth();if(i<0||i%n!==0)return!1}return!0}default:return y.debug(`Unsupported recurrence type: ${e.recurrenceType}`),!0}}getIsoDayOfWeek(e){let t=e.getDay();return t===0?7:t}isTimeActive(e,t){let n=e.fromdt?new Date(e.fromdt):null,r=e.todt?new Date(e.todt):null;if(e.recurrenceType===`Week`||e.recurrenceType===`Day`||e.recurrenceType===`Month`){if(n&&r){let e=t.getHours()*3600+t.getMinutes()*60+t.getSeconds(),i=n.getHours()*3600+n.getMinutes()*60+n.getSeconds(),a=r.getHours()*3600+r.getMinutes()*60+r.getSeconds();return i<=a?e>=i&&e<=a:e>=i||e<=a}return!0}return!(n&&t<n||r&&t>r)}getCurrentLayouts(){return this._getLayoutsAt(new Date)}getLayoutsAtTime(e){return this._getLayoutsAt(e,{skipRateLimiting:!0,skipInterrupts:!0,quiet:!0})}getAllLayoutsAtTime(e){if(!this.schedule)return[];let t=e,n=[];if(this.schedule.layouts)for(let e of this.schedule.layouts)this.isRecurringScheduleActive(e,t)&&this.isTimeActive(e,t)&&(e.criteria&&e.criteria.length>0&&!c(e.criteria,{now:t,displayProperties:this.displayProperties,weatherData:this.weatherData})||e.isGeoAware&&e.geoLocation&&!this.isWithinGeoFence(e.geoLocation)||n.push({file:e.file,priority:e.priority||0,maxPlaysPerHour:e.maxPlaysPerHour||0,duration:e.duration||0}));if(this.schedule.campaigns){for(let e of this.schedule.campaigns)if(this.isRecurringScheduleActive(e,t)&&this.isTimeActive(e,t))for(let t of e.layouts)n.push({file:t.file,priority:e.priority||0,maxPlaysPerHour:t.maxPlaysPerHour||0,duration:t.duration||0})}return n}detectConflicts(e={}){let t=e.from||new Date,n=e.hours||24,r=new Date(t.getTime()+n*36e5),i=6e4,a=[],o=null;for(let e=t.getTime();e<r.getTime();e+=i){let t=new Date(e),n=this.getAllLayoutsAtTime(t);if(n.length===0){o&&=(a.push(o),null);continue}let r=Math.max(...n.map(e=>e.priority)),s=n.filter(e=>e.priority<r);if(s.length===0){o&&=(a.push(o),null);continue}let c=n.filter(e=>e.priority===r),l=c.map(e=>e.file).sort().join(`,`),u=s.map(e=>`${e.file}:${e.priority}`).sort().join(`,`);o&&o._winnerKey===l&&o._hiddenKey===u?o.endTime=new Date(e+i):(o&&a.push(o),o={startTime:new Date(e),endTime:new Date(e+i),winner:{file:c[0].file,priority:r},hidden:s.map(e=>({file:e.file,priority:e.priority})),_winnerKey:l,_hiddenKey:u})}o&&a.push(o);for(let e of a)delete e._winnerKey,delete e._hiddenKey;return a}_getLayoutsAt(e,t={}){if(!this.schedule)return[];let{skipRateLimiting:n=!1,skipInterrupts:r=!1,quiet:i=!1}=t,a=i?()=>{}:(...e)=>y.info(...e),o=[];if(this._maxActivePriority=0,this.schedule.campaigns)for(let t of this.schedule.campaigns)this.isRecurringScheduleActive(t,e)&&this.isTimeActive(t,e)&&(this._maxActivePriority=Math.max(this._maxActivePriority,t.priority||0),o.push({type:`campaign`,priority:t.priority,layouts:t.layouts,campaignId:t.id}));if(this.schedule.layouts){for(let t of this.schedule.layouts)if(this.isRecurringScheduleActive(t,e)&&this.isTimeActive(t,e)){if(t.criteria&&t.criteria.length>0&&!c(t.criteria,{now:e,displayProperties:this.displayProperties,weatherData:this.weatherData})){a(`[Schedule] Layout`,t.id,`filtered by criteria`);continue}if(t.isGeoAware&&t.geoLocation&&!this.isWithinGeoFence(t.geoLocation)){a(`[Schedule] Layout`,t.id,`filtered by geofence`);continue}if(this._maxActivePriority=Math.max(this._maxActivePriority,t.priority||0),!n&&!this.canPlayLayout(t.id,t.maxPlaysPerHour)){a(`[Schedule] Layout`,t.id,`filtered by maxPlaysPerHour (limit:`,t.maxPlaysPerHour,`)`);continue}o.push({type:`layout`,priority:t.priority||0,layouts:[t],layoutId:t.id})}}if(o.length===0)return this.schedule.default?[this.schedule.default]:[];let s=Math.max(...o.map(e=>e.priority));a(`[Schedule] Max priority:`,s,`from`,o.length,`active items`);let l=[];for(let e of o)e.priority===s?(a(`[Schedule] Including priority`,e.priority,`layouts:`,e.layouts.map(e=>e.file)),l.push(...e.layouts)):a(`[Schedule] Skipping priority`,e.priority,`< max`,s);this._layoutMetadata.clear();for(let e of l)this._layoutMetadata.set(e.file,{syncEvent:e.syncEvent||!1,shareOfVoice:e.shareOfVoice||0,scheduleid:e.scheduleid,priority:e.priority||0});if(!r&&this.interruptScheduler){let{normalLayouts:e,interruptLayouts:t}=this.interruptScheduler.separateLayouts(l);if(t.length>0){a(`[Schedule] Found`,t.length,`interrupt layouts with shareOfVoice`);let n=this.interruptScheduler.processInterrupts(e,t).map(e=>e.file);return a(`[Schedule] Final layouts (with interrupts):`,n),n}}let u=l.map(e=>e.file);return a(`[Schedule] Final layouts:`,u),u}shouldCheckSchedule(e){return e?Date.now()-e>=6e4:!0}canPlayLayout(e,t){return f(this.playHistory.get(e)||[],t,Date.now())}recordPlay(e){this.playHistory.has(e)||this.playHistory.set(e,[]);let t=this.playHistory.get(e);t.push(Date.now());let n=Date.now()-3600*1e3,r=t.filter(e=>e>n);this.playHistory.set(e,r),y.info(`Recorded play for layout ${e} (${r.length} plays in last hour)`)}isSyncEvent(e){return this._layoutMetadata.get(e)?.syncEvent===!0}getLayoutMetadata(e){return this._layoutMetadata.get(e)||null}getScheduleQueue(e,t={}){let n=this.getAllLayoutsAtTime(new Date),r=n.map(e=>`${e.file}:${e.priority}:${e.maxPlaysPerHour}`).sort().join(`|`);if(this._scheduleQueue&&this._queueLayoutSet===r)return this._scheduleQueue;let i=v(n,e,{defaultLayout:this.schedule?.default||null,defaultDuration:60,dynamicLayouts:t.dynamicLayouts||new Set}),a=this._queueLayoutSet;return this._scheduleQueue=i,this._queueLayoutSet=r,a!==r&&(this._queuePosition=0),i.queue.length>0&&(y.info(`[Schedule] Built queue: ${i.queue.length} entries, period ${i.periodSeconds}s (pos ${this._queuePosition})`),y.info(`[Schedule] Queue: ${i.queue.map(e=>`${e.layoutId}(${e.duration}s)`).join(` → `)}`)),i}popNextFromQueue(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);if(n.length===0)return null;let r=n[this._queuePosition%n.length];return this._queuePosition=(this._queuePosition+1)%n.length,r}getQueuePosition(){return this._queuePosition}rewindQueue(e,t,n={}){let{queue:r}=this.getScheduleQueue(t,n);if(r.length===0)return null;this._queuePosition=(this._queuePosition-e+r.length*e)%r.length;let i=r[this._queuePosition];return this._queuePosition=(this._queuePosition+1)%r.length,i}peekNextInQueue(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);return n.length===0?null:n[this._queuePosition%n.length]}peekAfterNext(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);return n.length<=1?null:n[(this._queuePosition+1)%n.length]}invalidateQueue(){this._invalidateQueue()}_invalidateQueue(){this._scheduleQueue=null}hasSyncEvents(){for(let e of this._layoutMetadata.values())if(e.syncEvent)return!0;return!1}getActiveActions(){if(!this.schedule?.actions)return[];let e=new Date;return this.schedule.actions.filter(t=>this.isTimeActive(t,e))}getCommands(){return this.schedule?.commands||[]}findActionByTrigger(e){return this.getActiveActions().find(t=>t.triggerCode===e)||null}clearPlayHistory(){this.playHistory.clear(),y.info(`Play history cleared`)}setLocation(e,t){this.playerLocation={latitude:e,longitude:t},y.info(`Location set: ${e}, ${t}`)}setDisplayProperties(e){this.displayProperties=e||{}}isWithinGeoFence(e,t=500){if(!this.playerLocation)return y.debug(`No player location, skipping geofence check`),!0;if(!e)return!0;let n=e.split(`,`).map(e=>parseFloat(e.trim()));if(n.length<2||isNaN(n[0])||isNaN(n[1]))return y.warn(`Invalid geoLocation format:`,e),!0;let r=n[0],i=n[1],a=n[2]||t,o=this.haversineDistance(this.playerLocation.latitude,this.playerLocation.longitude,r,i),s=o<=a;return y.info(`Geofence: ${o.toFixed(0)}m from (${r},${i}), radius ${a}m → ${s?`WITHIN`:`OUTSIDE`}`),s}haversineDistance(e,t,n,r){let i=e=>e*Math.PI/180,a=i(n-e),o=i(r-t),s=Math.sin(a/2)**2+Math.cos(i(e))*Math.cos(i(n))*Math.sin(o/2)**2;return 6371e3*2*Math.atan2(Math.sqrt(s),Math.sqrt(1-s))}},x=new b,S=t(`schedule:interrupts`),C=class{constructor(){this.interruptCommittedDurations=new Map}isInterrupt(e){return!!(e.shareOfVoice&&e.shareOfVoice>0)}resetCommittedDurations(){this.interruptCommittedDurations.clear(),S.debug(`Reset interrupt committed durations`)}getCommittedDuration(e){return this.interruptCommittedDurations.get(e)||0}addCommittedDuration(e,t){let n=this.getCommittedDuration(e);this.interruptCommittedDurations.set(e,n+t)}isInterruptDurationSatisfied(e){if(!e.shareOfVoice)return!0;let t=e.id||e.file,n=e.shareOfVoice/100*3600;return this.getCommittedDuration(t)>=n}getRequiredSeconds(e){return e.shareOfVoice?e.shareOfVoice/100*3600:0}processInterrupts(e,t){if(!t||t.length===0)return S.debug(`No interrupt layouts, returning normal layouts`),e;if(!e||e.length===0)return S.warn(`No normal layouts available, interrupts will fill entire hour`),this.fillHourWithInterrupts(t);S.info(`Processing ${t.length} interrupt layouts with ${e.length} normal layouts`);for(let e of t){let t=e.id||e.file;this.interruptCommittedDurations.set(t,0)}let n=[],r=0,i=0,a=!1;for(;!a;){if(i>=t.length){i=0;let e=!0;for(let n of t)if(!this.isInterruptDurationSatisfied(n)){e=!1;break}if(e){a=!0;break}}let e=t[i];if(!this.isInterruptDurationSatisfied(e)){let t=e.id||e.file;this.addCommittedDuration(t,e.duration),r+=e.duration,n.push(e)}i++}if(S.debug(`Resolved ${n.length} interrupt plays (${r}s total)`),r>=3600)return S.info(`Interrupts fill entire hour (>= 3600s), no room for normal layouts`),n;let o=3600-r,s=this.fillTimeWithLayouts(e,o);S.debug(`Resolved ${s.length} normal plays (${o}s target)`);let c=this.interleaveLayouts(s,n);return S.info(`Final loop: ${c.length} layouts (${s.length} normal + ${n.length} interrupts)`),c}fillTimeWithLayouts(e,t){let n=[],r=t,i=0;for(;r>0;){i>=e.length&&(i=0);let t=e[i];n.push(t),r-=t.duration,i++}return n}fillHourWithInterrupts(e){return this.fillTimeWithLayouts(e,3600)}interleaveLayouts(e,t){let n=[],r=Math.max(e.length,t.length),i=Math.ceil(1*r/e.length),a=Math.floor(1*r/t.length);S.debug(`Interleaving: pickCount=${r}, normalPick=${i}, interruptPick=${a}`);let o=0,s=0,c=0;for(let l=0;l<r;l++)l%i===0&&(o>=e.length&&(o=0),n.push(e[o]),c+=e[o].duration,o++),l%a===0&&s<t.length&&(n.push(t[s]),c+=t[s].duration,s++);for(;c<3600;)o>=e.length&&(o=0),n.push(e[o]),c+=e[o].duration,o++;return S.debug(`Interleaved ${n.length} layouts, total duration: ${c}s`),n}separateLayouts(e){let t=[],n=[];for(let r of e)this.isInterrupt(r)?n.push(r):t.push(r);return{normalLayouts:t,interruptLayouts:n}}};new C;var w=t(`schedule:overlays`),T=class{constructor(){this.overlays=[],this.displayProperties={},this.scheduleManager=null,w.debug(`OverlayScheduler initialized`)}setScheduleManager(e){this.scheduleManager=e}setDisplayProperties(e){this.displayProperties=e||{}}setOverlays(e){this.overlays=e||[],w.info(`Loaded ${this.overlays.length} overlay(s)`)}getCurrentOverlays(){if(!this.overlays||this.overlays.length===0)return[];let e=new Date,t=[];for(let n of this.overlays){if(!this.isTimeActive(n,e)){w.debug(`Overlay ${n.file} not in time window`);continue}if(n.isGeoAware&&n.geoLocation&&this.scheduleManager&&!this.scheduleManager.isWithinGeoFence(n.geoLocation)){w.debug(`Overlay ${n.file} filtered by geofence`);continue}if(n.criteria&&n.criteria.length>0&&!c(n.criteria,{now:e,displayProperties:this.displayProperties})){w.debug(`Overlay ${n.file} filtered by criteria`);continue}t.push(n)}return t.sort((e,t)=>{let n=e.priority||0;return(t.priority||0)-n}),t.length>0&&w.info(`Active overlays: ${t.length}`),t}isTimeActive(e,t){if(this.scheduleManager){let n={...e};return!n.fromdt&&n.fromDt&&(n.fromdt=n.fromDt),!n.todt&&n.toDt&&(n.todt=n.toDt),this.scheduleManager.isTimeActive(n,t)}let n=e.fromdt||e.fromDt?new Date(e.fromdt||e.fromDt):null,r=e.todt||e.toDt?new Date(e.todt||e.toDt):null;return!(n&&t<n||r&&t>r)}shouldCheckOverlays(e){return e?Date.now()-e>=6e4:!0}getOverlayByFile(e){return this.overlays.find(t=>t.file===e)||null}clear(){this.overlays=[],w.debug(`Cleared all overlays`)}};new T;var E=e({InterruptScheduler:()=>C,OverlayScheduler:()=>T,ScheduleManager:()=>b,VERSION:()=>D,buildScheduleQueue:()=>v,calculateTimeline:()=>m,parseLayoutDuration:()=>u,parseLayoutFile:()=>l,scheduleManager:()=>x}),D=n.version;export{l as i,m as n,u as r,E as t};
|
|
2
|
-
//# sourceMappingURL=src-
|
|
1
|
+
import{n as e}from"./chunk-7ZXdHUL4.js";import{u as t}from"./src-CNTwwhBq.js";var n={name:`@xiboplayer/schedule`,version:`0.7.17`,description:`Complete scheduling solution: campaigns, dayparting, interrupts, and overlays`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./schedule":`./src/schedule.js`,"./interrupts":`./src/interrupts.js`,"./overlays":`./src/overlays.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/utils":`workspace:*`},devDependencies:{vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`scheduling`,`dayparting`,`campaigns`,`interrupts`,`overlays`,`shareOfVoice`],author:`Pau Aliagas <linuxnow@gmail.com>`,license:`AGPL-3.0-or-later`,repository:{type:`git`,url:`git+https://github.com/xibo-players/xiboplayer.git`,directory:`packages/schedule`},homepage:`https://xiboplayer.org`},r=t(`schedule:criteria`),i=[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`],a={weatherTemp:`temperature`,weatherHumidity:`humidity`,weatherWindSpeed:`windSpeed`,weatherCondition:`condition`,weatherCloudCover:`cloudCover`};function o(e,t,n={},o={}){switch(e){case`dayOfWeek`:return i[t.getDay()];case`dayOfMonth`:return String(t.getDate());case`month`:return String(t.getMonth()+1);case`hour`:return String(t.getHours());case`isoDay`:return String(t.getDay()===0?7:t.getDay());default:if(a[e]){let t=a[e];return o[t]===void 0?(r.debug(`Weather metric "${e}" requested but no weather data available`),null):String(o[t])}return n[e]===void 0?(r.debug(`Unknown metric: ${e}`),null):String(n[e])}}function s(e,t,n,i){if(e===null)return!1;if(i===`number`){let r=parseFloat(e),i=parseFloat(n);if(isNaN(r)||isNaN(i))return!1;switch(t){case`equals`:return r===i;case`notEquals`:return r!==i;case`greaterThan`:return r>i;case`greaterThanOrEquals`:return r>=i;case`lessThan`:return r<i;case`lessThanOrEquals`:return r<=i;default:return!1}}let a=e.toLowerCase(),o=n.toLowerCase();switch(t){case`equals`:return a===o;case`notEquals`:return a!==o;case`contains`:return a.includes(o);case`notContains`:return!a.includes(o);case`startsWith`:return a.startsWith(o);case`endsWith`:return a.endsWith(o);case`in`:return o.split(`,`).map(e=>e.trim().toLowerCase()).includes(a);case`greaterThan`:return a>o;case`lessThan`:return a<o;default:return r.debug(`Unknown condition: ${t}`),!1}}function c(e,t={}){if(!e||e.length===0)return!0;let n=t.now||new Date,i=t.displayProperties||{},a=t.weatherData||{};for(let t of e){let e=o(t.metric,n,i,a);if(!s(e,t.condition,t.value,t.type))return r.debug(`Criteria failed: ${t.metric} ${t.condition} "${t.value}" (actual: "${e}")`),!1}return!0}function l(e){return parseInt(String(e).replace(`.xlf`,``),10)}function u(e,t=null){let n=new DOMParser().parseFromString(e,`text/xml`).querySelector(`layout`);if(!n)return{duration:60,isDynamic:!1};let r=parseInt(n.getAttribute(`duration`)||`0`,10);if(r>0)return{duration:r,isDynamic:!1};let i=0,a=!1;for(let e of n.querySelectorAll(`region`)){let n=e.getAttribute(`type`);if(n===`drawer`)continue;let r=n===`canvas`,o=0;for(let n of e.querySelectorAll(`media`)){let e=parseInt(n.getAttribute(`duration`)||`0`,10),i=parseInt(n.getAttribute(`useDuration`)||`1`,10),s=n.getAttribute(`fileId`)||``,c=t?.get(s),l;c===void 0?e>0&&i!==0?l=e:(l=60,a=!0):l=c,r?o=Math.max(o,l):o+=l}i=Math.max(i,o)}return{duration:i>0?i:60,isDynamic:a}}function d(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function f(e,t,n){if(!t||t===0)return!0;let r=n-36e5,i=e.filter(e=>e>r);if(i.length>=t)return!1;if(i.length>0){let e=36e5/t;if(n-Math.max(...i)<e)return!1}return!0}function p(e,t,n){let r=e.filter(e=>!e.maxPlaysPerHour||e.maxPlaysPerHour===0?!0:f(t.get(e.file)||[],e.maxPlaysPerHour,n));if(r.length===0)return[];let i=Math.max(...r.map(e=>e.priority));return r.filter(e=>e.priority===i).map(e=>e.file)}function m(e,t,n={}){let r=n.from||new Date,i=n.hours||2,a=new Date(r.getTime()+i*36e5);n.currentLayoutStartedAt;let o=n.defaultLayout||null,s=n.durations||null;if(!e||e.length===0)return[];let c=[],l=new Date(r),u=t%e.length;for(;l<a&&c.length<500;){let t=e[u],n=s&&s.get(t.layoutId)||t.duration,r=l.getTime()+n*1e3;c.push({layoutFile:t.layoutId,startTime:new Date(l),endTime:new Date(r),duration:n,isDefault:o?t.layoutId===o:!1}),l=new Date(r),u=(u+1)%e.length}return c}function h(e,t){for(e=Math.abs(Math.round(e)),t=Math.abs(Math.round(t));t;)[e,t]=[t,e%t];return e}function g(e,t){return e===0||t===0?0:Math.abs(Math.round(e)*Math.round(t))/h(e,t)}function _(e){return e.reduce((e,t)=>g(e,t),1)}function v(e,t,n={}){let{defaultLayout:r=null,defaultDuration:i=60}=n;if(e.length===0&&!r)return{queue:[],periodSeconds:0};let a=new Map;for(let t of e)t.duration>0&&a.set(t.file,t.duration);let o=e=>t.get(e)||a.get(e)||i,s=e.filter(e=>e.maxPlaysPerHour>0),c;s.length>0?(c=_(s.map(e=>Math.round(3600/e.maxPlaysPerHour))),c>7200&&(c=7200)):c=e.reduce((e,t)=>e+o(t.file),0)+(r&&!e.some(e=>e.file===r)?o(r):0)||i;let l=[],u=new Map,f=0,m=c*1e3;for(;f<m&&l.length<500;){let t=p(e,u,f);if(t.length===0){if(r){let e=o(r);l.push({layoutId:r,duration:e}),f+=e*1e3}else f+=6e4;continue}for(let n=0;n<t.length&&f<m&&l.length<500;n++){let r=t[n],i=o(r);if(l.push({layoutId:r,duration:i}),u.has(r)||u.set(r,[]),u.get(r).push(f),f+=i*1e3,!d(t,p(e,u,f)))break}}if(l.length===0&&r){let e=o(r);l.push({layoutId:r,duration:e})}return{queue:l,periodSeconds:c}}var y=t(`Schedule`),b=class{constructor(e={}){this.schedule=null,this.playHistory=new Map,this.interruptScheduler=e.interruptScheduler||null,this.displayProperties=e.displayProperties||{},this.weatherData={},this.playerLocation=null,this._layoutMetadata=new Map,this._scheduleQueue=null,this._queuePosition=0,this._queueLayoutSet=null}setSchedule(e){this.schedule=e,this._invalidateQueue()}setWeatherData(e){this.weatherData=e||{}}getDataConnectors(){return this.schedule?.dataConnectors||[]}getDependantsMap(){let e=new Map;if(!this.schedule)return e;let t=this.schedule.dependants||[],n=n=>{let r=l(n.file||n.id),i=[...t,...n.dependants||[]];i.length>0&&e.set(r,i)};if(this.schedule.layouts)for(let e of this.schedule.layouts)n(e);if(this.schedule.campaigns)for(let e of this.schedule.campaigns)for(let t of e.layouts)n(t);return e}isRecurringScheduleActive(e,t){if(!e.recurrenceType)return!0;if(e.recurrenceRange&&t>new Date(e.recurrenceRange))return!1;switch(e.recurrenceType){case`Week`:if(e.recurrenceRepeatsOn){let n=this.getIsoDayOfWeek(t);if(!e.recurrenceRepeatsOn.split(`,`).map(e=>parseInt(e.trim())).includes(n))return!1}return!0;case`Day`:{let n=e.recurrenceDetail||1;if(n>1&&e.fromdt){let r=new Date(e.fromdt),i=t.getTime()-r.getTime(),a=Math.floor(i/864e5);if(a<0||a%n!==0)return!1}return!0}case`Month`:{if(e.recurrenceRepeatsOn){let n=e.recurrenceRepeatsOn.split(`,`).map(e=>parseInt(e.trim())),r=t.getDate();if(!n.includes(r))return!1}let n=e.recurrenceDetail||1;if(n>1&&e.fromdt){let r=new Date(e.fromdt),i=(t.getFullYear()-r.getFullYear())*12+t.getMonth()-r.getMonth();if(i<0||i%n!==0)return!1}return!0}default:return y.debug(`Unsupported recurrence type: ${e.recurrenceType}`),!0}}getIsoDayOfWeek(e){let t=e.getDay();return t===0?7:t}isTimeActive(e,t){let n=e.fromdt?new Date(e.fromdt):null,r=e.todt?new Date(e.todt):null;if(e.recurrenceType===`Week`||e.recurrenceType===`Day`||e.recurrenceType===`Month`){if(n&&r){let e=t.getHours()*3600+t.getMinutes()*60+t.getSeconds(),i=n.getHours()*3600+n.getMinutes()*60+n.getSeconds(),a=r.getHours()*3600+r.getMinutes()*60+r.getSeconds();return i<=a?e>=i&&e<=a:e>=i||e<=a}return!0}return!(n&&t<n||r&&t>r)}getCurrentLayouts(){return this._getLayoutsAt(new Date)}getLayoutsAtTime(e){return this._getLayoutsAt(e,{skipRateLimiting:!0,skipInterrupts:!0,quiet:!0})}getAllLayoutsAtTime(e){if(!this.schedule)return[];let t=e,n=[];if(this.schedule.layouts)for(let e of this.schedule.layouts)this.isRecurringScheduleActive(e,t)&&this.isTimeActive(e,t)&&(e.criteria&&e.criteria.length>0&&!c(e.criteria,{now:t,displayProperties:this.displayProperties,weatherData:this.weatherData})||e.isGeoAware&&e.geoLocation&&!this.isWithinGeoFence(e.geoLocation)||n.push({file:e.file,priority:e.priority||0,maxPlaysPerHour:e.maxPlaysPerHour||0,duration:e.duration||0}));if(this.schedule.campaigns){for(let e of this.schedule.campaigns)if(this.isRecurringScheduleActive(e,t)&&this.isTimeActive(e,t))for(let t of e.layouts)n.push({file:t.file,priority:e.priority||0,maxPlaysPerHour:t.maxPlaysPerHour||0,duration:t.duration||0})}return n}detectConflicts(e={}){let t=e.from||new Date,n=e.hours||24,r=new Date(t.getTime()+n*36e5),i=6e4,a=[],o=null;for(let e=t.getTime();e<r.getTime();e+=i){let t=new Date(e),n=this.getAllLayoutsAtTime(t);if(n.length===0){o&&=(a.push(o),null);continue}let r=Math.max(...n.map(e=>e.priority)),s=n.filter(e=>e.priority<r);if(s.length===0){o&&=(a.push(o),null);continue}let c=n.filter(e=>e.priority===r),l=c.map(e=>e.file).sort().join(`,`),u=s.map(e=>`${e.file}:${e.priority}`).sort().join(`,`);o&&o._winnerKey===l&&o._hiddenKey===u?o.endTime=new Date(e+i):(o&&a.push(o),o={startTime:new Date(e),endTime:new Date(e+i),winner:{file:c[0].file,priority:r},hidden:s.map(e=>({file:e.file,priority:e.priority})),_winnerKey:l,_hiddenKey:u})}o&&a.push(o);for(let e of a)delete e._winnerKey,delete e._hiddenKey;return a}_getLayoutsAt(e,t={}){if(!this.schedule)return[];let{skipRateLimiting:n=!1,skipInterrupts:r=!1,quiet:i=!1}=t,a=i?()=>{}:(...e)=>y.info(...e),o=[];if(this._maxActivePriority=0,this.schedule.campaigns)for(let t of this.schedule.campaigns)this.isRecurringScheduleActive(t,e)&&this.isTimeActive(t,e)&&(this._maxActivePriority=Math.max(this._maxActivePriority,t.priority||0),o.push({type:`campaign`,priority:t.priority,layouts:t.layouts,campaignId:t.id}));if(this.schedule.layouts){for(let t of this.schedule.layouts)if(this.isRecurringScheduleActive(t,e)&&this.isTimeActive(t,e)){if(t.criteria&&t.criteria.length>0&&!c(t.criteria,{now:e,displayProperties:this.displayProperties,weatherData:this.weatherData})){a(`[Schedule] Layout`,t.id,`filtered by criteria`);continue}if(t.isGeoAware&&t.geoLocation&&!this.isWithinGeoFence(t.geoLocation)){a(`[Schedule] Layout`,t.id,`filtered by geofence`);continue}if(this._maxActivePriority=Math.max(this._maxActivePriority,t.priority||0),!n&&!this.canPlayLayout(t.id,t.maxPlaysPerHour)){a(`[Schedule] Layout`,t.id,`filtered by maxPlaysPerHour (limit:`,t.maxPlaysPerHour,`)`);continue}o.push({type:`layout`,priority:t.priority||0,layouts:[t],layoutId:t.id})}}if(o.length===0)return this.schedule.default?[this.schedule.default]:[];let s=Math.max(...o.map(e=>e.priority));a(`[Schedule] Max priority:`,s,`from`,o.length,`active items`);let l=[];for(let e of o)e.priority===s?(a(`[Schedule] Including priority`,e.priority,`layouts:`,e.layouts.map(e=>e.file)),l.push(...e.layouts)):a(`[Schedule] Skipping priority`,e.priority,`< max`,s);this._layoutMetadata.clear();for(let e of l)this._layoutMetadata.set(e.file,{syncEvent:e.syncEvent||!1,shareOfVoice:e.shareOfVoice||0,scheduleid:e.scheduleid,priority:e.priority||0});if(!r&&this.interruptScheduler){let{normalLayouts:e,interruptLayouts:t}=this.interruptScheduler.separateLayouts(l);if(t.length>0){a(`[Schedule] Found`,t.length,`interrupt layouts with shareOfVoice`);let n=this.interruptScheduler.processInterrupts(e,t).map(e=>e.file);return a(`[Schedule] Final layouts (with interrupts):`,n),n}}let u=l.map(e=>e.file);return a(`[Schedule] Final layouts:`,u),u}shouldCheckSchedule(e){return e?Date.now()-e>=6e4:!0}canPlayLayout(e,t){return f(this.playHistory.get(e)||[],t,Date.now())}recordPlay(e){this.playHistory.has(e)||this.playHistory.set(e,[]);let t=this.playHistory.get(e);t.push(Date.now());let n=Date.now()-3600*1e3,r=t.filter(e=>e>n);this.playHistory.set(e,r),y.info(`Recorded play for layout ${e} (${r.length} plays in last hour)`)}isSyncEvent(e){return this._layoutMetadata.get(e)?.syncEvent===!0}getLayoutMetadata(e){return this._layoutMetadata.get(e)||null}getScheduleQueue(e,t={}){let n=this.getAllLayoutsAtTime(new Date),r=n.map(e=>`${e.file}:${e.priority}:${e.maxPlaysPerHour}`).sort().join(`|`);if(this._scheduleQueue&&this._queueLayoutSet===r)return this._scheduleQueue;let i=v(n,e,{defaultLayout:this.schedule?.default||null,defaultDuration:60,dynamicLayouts:t.dynamicLayouts||new Set}),a=this._queueLayoutSet;return this._scheduleQueue=i,this._queueLayoutSet=r,a!==r&&(this._queuePosition=0),i.queue.length>0&&(y.info(`[Schedule] Built queue: ${i.queue.length} entries, period ${i.periodSeconds}s (pos ${this._queuePosition})`),y.info(`[Schedule] Queue: ${i.queue.map(e=>`${e.layoutId}(${e.duration}s)`).join(` → `)}`)),i}popNextFromQueue(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);if(n.length===0)return null;let r=n[this._queuePosition%n.length];return this._queuePosition=(this._queuePosition+1)%n.length,r}getQueuePosition(){return this._queuePosition}rewindQueue(e,t,n={}){let{queue:r}=this.getScheduleQueue(t,n);if(r.length===0)return null;this._queuePosition=(this._queuePosition-e+r.length*e)%r.length;let i=r[this._queuePosition];return this._queuePosition=(this._queuePosition+1)%r.length,i}peekNextInQueue(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);return n.length===0?null:n[this._queuePosition%n.length]}peekAfterNext(e,t={}){let{queue:n}=this.getScheduleQueue(e,t);return n.length<=1?null:n[(this._queuePosition+1)%n.length]}invalidateQueue(){this._invalidateQueue()}_invalidateQueue(){this._scheduleQueue=null}hasSyncEvents(){for(let e of this._layoutMetadata.values())if(e.syncEvent)return!0;return!1}getActiveActions(){if(!this.schedule?.actions)return[];let e=new Date;return this.schedule.actions.filter(t=>this.isTimeActive(t,e))}getCommands(){return this.schedule?.commands||[]}findActionByTrigger(e){return this.getActiveActions().find(t=>t.triggerCode===e)||null}clearPlayHistory(){this.playHistory.clear(),y.info(`Play history cleared`)}setLocation(e,t){this.playerLocation={latitude:e,longitude:t},y.info(`Location set: ${e}, ${t}`)}setDisplayProperties(e){this.displayProperties=e||{}}isWithinGeoFence(e,t=500){if(!this.playerLocation)return y.debug(`No player location, skipping geofence check`),!0;if(!e)return!0;let n=e.split(`,`).map(e=>parseFloat(e.trim()));if(n.length<2||isNaN(n[0])||isNaN(n[1]))return y.warn(`Invalid geoLocation format:`,e),!0;let r=n[0],i=n[1],a=n[2]||t,o=this.haversineDistance(this.playerLocation.latitude,this.playerLocation.longitude,r,i),s=o<=a;return y.info(`Geofence: ${o.toFixed(0)}m from (${r},${i}), radius ${a}m → ${s?`WITHIN`:`OUTSIDE`}`),s}haversineDistance(e,t,n,r){let i=e=>e*Math.PI/180,a=i(n-e),o=i(r-t),s=Math.sin(a/2)**2+Math.cos(i(e))*Math.cos(i(n))*Math.sin(o/2)**2;return 6371e3*2*Math.atan2(Math.sqrt(s),Math.sqrt(1-s))}},x=new b,S=t(`schedule:interrupts`),C=class{constructor(){this.interruptCommittedDurations=new Map}isInterrupt(e){return!!(e.shareOfVoice&&e.shareOfVoice>0)}resetCommittedDurations(){this.interruptCommittedDurations.clear(),S.debug(`Reset interrupt committed durations`)}getCommittedDuration(e){return this.interruptCommittedDurations.get(e)||0}addCommittedDuration(e,t){let n=this.getCommittedDuration(e);this.interruptCommittedDurations.set(e,n+t)}isInterruptDurationSatisfied(e){if(!e.shareOfVoice)return!0;let t=e.id||e.file,n=e.shareOfVoice/100*3600;return this.getCommittedDuration(t)>=n}getRequiredSeconds(e){return e.shareOfVoice?e.shareOfVoice/100*3600:0}processInterrupts(e,t){if(!t||t.length===0)return S.debug(`No interrupt layouts, returning normal layouts`),e;if(!e||e.length===0)return S.warn(`No normal layouts available, interrupts will fill entire hour`),this.fillHourWithInterrupts(t);S.info(`Processing ${t.length} interrupt layouts with ${e.length} normal layouts`);for(let e of t){let t=e.id||e.file;this.interruptCommittedDurations.set(t,0)}let n=[],r=0,i=0,a=!1;for(;!a;){if(i>=t.length){i=0;let e=!0;for(let n of t)if(!this.isInterruptDurationSatisfied(n)){e=!1;break}if(e){a=!0;break}}let e=t[i];if(!this.isInterruptDurationSatisfied(e)){let t=e.id||e.file;this.addCommittedDuration(t,e.duration),r+=e.duration,n.push(e)}i++}if(S.debug(`Resolved ${n.length} interrupt plays (${r}s total)`),r>=3600)return S.info(`Interrupts fill entire hour (>= 3600s), no room for normal layouts`),n;let o=3600-r,s=this.fillTimeWithLayouts(e,o);S.debug(`Resolved ${s.length} normal plays (${o}s target)`);let c=this.interleaveLayouts(s,n);return S.info(`Final loop: ${c.length} layouts (${s.length} normal + ${n.length} interrupts)`),c}fillTimeWithLayouts(e,t){let n=[],r=t,i=0;for(;r>0;){i>=e.length&&(i=0);let t=e[i];n.push(t),r-=t.duration,i++}return n}fillHourWithInterrupts(e){return this.fillTimeWithLayouts(e,3600)}interleaveLayouts(e,t){let n=[],r=Math.max(e.length,t.length),i=Math.ceil(1*r/e.length),a=Math.floor(1*r/t.length);S.debug(`Interleaving: pickCount=${r}, normalPick=${i}, interruptPick=${a}`);let o=0,s=0,c=0;for(let l=0;l<r;l++)l%i===0&&(o>=e.length&&(o=0),n.push(e[o]),c+=e[o].duration,o++),l%a===0&&s<t.length&&(n.push(t[s]),c+=t[s].duration,s++);for(;c<3600;)o>=e.length&&(o=0),n.push(e[o]),c+=e[o].duration,o++;return S.debug(`Interleaved ${n.length} layouts, total duration: ${c}s`),n}separateLayouts(e){let t=[],n=[];for(let r of e)this.isInterrupt(r)?n.push(r):t.push(r);return{normalLayouts:t,interruptLayouts:n}}};new C;var w=t(`schedule:overlays`),T=class{constructor(){this.overlays=[],this.displayProperties={},this.scheduleManager=null,w.debug(`OverlayScheduler initialized`)}setScheduleManager(e){this.scheduleManager=e}setDisplayProperties(e){this.displayProperties=e||{}}setOverlays(e){this.overlays=e||[],w.info(`Loaded ${this.overlays.length} overlay(s)`)}getCurrentOverlays(){if(!this.overlays||this.overlays.length===0)return[];let e=new Date,t=[];for(let n of this.overlays){if(!this.isTimeActive(n,e)){w.debug(`Overlay ${n.file} not in time window`);continue}if(n.isGeoAware&&n.geoLocation&&this.scheduleManager&&!this.scheduleManager.isWithinGeoFence(n.geoLocation)){w.debug(`Overlay ${n.file} filtered by geofence`);continue}if(n.criteria&&n.criteria.length>0&&!c(n.criteria,{now:e,displayProperties:this.displayProperties})){w.debug(`Overlay ${n.file} filtered by criteria`);continue}t.push(n)}return t.sort((e,t)=>{let n=e.priority||0;return(t.priority||0)-n}),t.length>0&&w.info(`Active overlays: ${t.length}`),t}isTimeActive(e,t){if(this.scheduleManager){let n={...e};return!n.fromdt&&n.fromDt&&(n.fromdt=n.fromDt),!n.todt&&n.toDt&&(n.todt=n.toDt),this.scheduleManager.isTimeActive(n,t)}let n=e.fromdt||e.fromDt?new Date(e.fromdt||e.fromDt):null,r=e.todt||e.toDt?new Date(e.todt||e.toDt):null;return!(n&&t<n||r&&t>r)}shouldCheckOverlays(e){return e?Date.now()-e>=6e4:!0}getOverlayByFile(e){return this.overlays.find(t=>t.file===e)||null}clear(){this.overlays=[],w.debug(`Cleared all overlays`)}};new T;var E=e({InterruptScheduler:()=>C,OverlayScheduler:()=>T,ScheduleManager:()=>b,VERSION:()=>D,buildScheduleQueue:()=>v,calculateTimeline:()=>m,parseLayoutDuration:()=>u,parseLayoutFile:()=>l,scheduleManager:()=>x}),D=n.version;export{l as i,m as n,u as r,E as t};
|
|
2
|
+
//# sourceMappingURL=src-Cvry9zYs.js.map
|