@xiboplayer/pwa 0.5.13 → 0.5.14
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/{index-rMG3kuqV.js → index-Bcgq4p6v.js} +2 -2
- package/dist/assets/{index-rMG3kuqV.js.map → index-Bcgq4p6v.js.map} +1 -1
- package/dist/assets/index-BczIv3fg.js +2 -0
- package/dist/assets/{index-BzTeZiFu.js.map → index-BczIv3fg.js.map} +1 -1
- package/dist/assets/{index-dSvc2qBf.js → index-BeGa2zKC.js} +2 -2
- package/dist/assets/{index-dSvc2qBf.js.map → index-BeGa2zKC.js.map} +1 -1
- package/dist/assets/{index-B9S4-Tg6.js → index-COInxuZI.js} +2 -2
- package/dist/assets/{index-B9S4-Tg6.js.map → index-COInxuZI.js.map} +1 -1
- package/dist/assets/{index-BrJYfvV-.js → index-DAhOdNyB.js} +2 -2
- package/dist/assets/{index-BrJYfvV-.js.map → index-DAhOdNyB.js.map} +1 -1
- package/dist/assets/{index-RNYmTctp.js → index-DBYNSQLp.js} +2 -2
- package/dist/assets/{index-RNYmTctp.js.map → index-DBYNSQLp.js.map} +1 -1
- package/dist/assets/{index-DBeB6nHq.js → index-DgqUapYt.js} +2 -2
- package/dist/assets/{index-DBeB6nHq.js.map → index-DgqUapYt.js.map} +1 -1
- package/dist/assets/{index-DD2ZPwTV.js → index-FlRMd3Sh.js} +2 -2
- package/dist/assets/{index-DD2ZPwTV.js.map → index-FlRMd3Sh.js.map} +1 -1
- package/dist/assets/{index-B39PWpun.js → index-ZD5qisoE.js} +2 -2
- package/dist/assets/{index-B39PWpun.js.map → index-ZD5qisoE.js.map} +1 -1
- package/dist/assets/{main-BJ4a67Bs.js → main-DstA7-TG.js} +3 -3
- package/dist/assets/main-DstA7-TG.js.map +1 -0
- package/dist/assets/{setup-Cal-4t_7.js → setup-CKgHUoum.js} +2 -2
- package/dist/assets/{setup-Cal-4t_7.js.map → setup-CKgHUoum.js.map} +1 -1
- package/dist/index.html +12 -1
- package/dist/setup.html +1 -1
- package/dist/sw-pwa.js +1 -1
- package/package.json +13 -13
- package/dist/assets/index-BzTeZiFu.js +0 -2
- package/dist/assets/main-BJ4a67Bs.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"./modulepreload-polyfill-B5Qt9EMX.js";import{b as e}from"./cms-api-B3gEu-pj.js";import{R as P,X as K}from"./xmds-client-CX0t7Oes.js";const $="0.5.
|
|
2
|
-
//# sourceMappingURL=setup-
|
|
1
|
+
import"./modulepreload-polyfill-B5Qt9EMX.js";import{b as e}from"./cms-api-B3gEu-pj.js";import{R as P,X as K}from"./xmds-client-CX0t7Oes.js";const $="0.5.14";document.getElementById("version-footer").textContent=`Xibo Player v${$}`;const L=document.getElementById("setup-form"),h=document.getElementById("error"),c=document.getElementById("submit-btn"),S=document.getElementById("phase-unlock"),I=document.getElementById("phase-setup"),b=document.getElementById("phase-waiting"),C=document.getElementById("countdown"),T=document.getElementById("waiting-display-name"),z=document.getElementById("btn-back"),N=document.getElementById("success-flash"),E=e.hardwareKey;document.getElementById("hw-key-display").textContent=E;document.getElementById("hw-key-waiting").textContent=E;const j=e.cmsUrl||"";let m=null,r=null,x=15,l=null;async function v(){if(l)return l;try{const t=new P(e);await t.registerDisplay(),console.log("[Setup] Using REST transport"),l=t}catch(t){console.log("[Setup] REST unavailable, using XMDS/SOAP:",t.message),l=new K(e)}return l}function y(t){S.classList.remove("active"),I.classList.remove("active"),b.classList.remove("active"),t.classList.add("active")}function O(t){h.textContent=t,h.style.display="block"}function _(){h.style.display="none"}function f(){let t=x;C.textContent=t,clearInterval(r),r=setInterval(()=>{t--,C.textContent=Math.max(0,t),t<=0&&clearInterval(r)},1e3)}async function w(){if(document.getElementById("cms-url").value.trim().replace(/\/$/,"")!==j){console.log("[Setup] CMS URL changed — wiping cached content");const n=await caches.keys();await Promise.all(n.map(a=>caches.delete(a)))}N.classList.add("active"),setTimeout(()=>{window.location.href="/player/index.html"},800)}async function M(){try{const n=await(await v()).registerDisplay();if(console.log("[Setup] Poll result:",n.code,n.message),n.code==="READY"){clearInterval(m),clearInterval(r),w();return}f()}catch(t){console.warn("[Setup] Poll failed:",t.message),f()}}function R(t){T.textContent=t,y(b),f(),m=setInterval(M,x*1e3)}function A(){clearInterval(m),clearInterval(r),m=null}async function X(t,n){const a=document.getElementById("api-client-id").value.trim(),o=document.getElementById("api-client-secret").value.trim();if(!a||!o)return!1;try{const s=await fetch(`/rest-proxy?cms=${encodeURIComponent(t)}&path=/api/authorize/access_token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:a,client_secret:o})});if(!s.ok)throw new Error(`OAuth2 failed: ${s.status}`);const{access_token:i}=await s.json(),u=await fetch(`/rest-proxy?cms=${encodeURIComponent(t)}&path=/api/display&hardwareKey=${encodeURIComponent(n)}`,{headers:{Authorization:`Bearer ${i}`}});if(!u.ok)throw new Error(`Find display failed: ${u.status}`);const p=await u.json();if(!p.length)return console.log("[Setup] Display not found via API (may not be registered yet)"),!1;const g=p[0];if(g.licensed===1)return console.log("[Setup] Display already authorized"),!0;const B=await fetch(`/rest-proxy?cms=${encodeURIComponent(t)}&path=/api/display/authorise/${g.displayId}`,{method:"PUT",headers:{Authorization:`Bearer ${i}`}});if(!B.ok)throw new Error(`Authorize failed: ${B.status}`);return console.log("[Setup] Display auto-authorized via proxy!"),e.apiClientId=a,e.apiClientSecret=o,!0}catch(s){return console.warn("[Setup] Auto-authorize failed:",s.message),!1}}const d=e.isConfigured()&&e.cmsKey,k=new URLSearchParams(window.location.search).has("unlocked"),D=document.getElementById("btn-back-player");d&&!k&&y(S);(d||k)&&(D.style.display="block",document.getElementById("cms-url").value=e.cmsUrl,document.getElementById("cms-key").value=e.cmsKey,document.getElementById("display-name").value=e.displayName,e.apiClientId&&(document.getElementById("api-client-id").value=e.apiClientId,document.getElementById("api-client-secret").value=e.apiClientSecret||""));function U(){A(),window.location.href="/player/index.html"}D.addEventListener("click",U);(d||k)&&document.addEventListener("keydown",t=>{t.key==="Escape"&&(t.preventDefault(),U())});document.getElementById("unlock-form").addEventListener("submit",t=>{t.preventDefault();const n=document.getElementById("unlock-error");document.getElementById("unlock-key").value.trim()===e.cmsKey?(n.style.display="none",document.getElementById("cms-url").value=e.cmsUrl,document.getElementById("cms-key").value=e.cmsKey,document.getElementById("display-name").value=e.displayName,e.apiClientId&&(document.getElementById("api-client-id").value=e.apiClientId,document.getElementById("api-client-secret").value=e.apiClientSecret||""),y(I)):(n.textContent="Incorrect CMS key",n.style.display="block")});!d&&e.isConfigured()&&(document.getElementById("cms-url").value=e.cmsUrl,document.getElementById("cms-key").value=e.cmsKey,document.getElementById("display-name").value=e.displayName,e.apiClientId&&(document.getElementById("api-client-id").value=e.apiClientId,document.getElementById("api-client-secret").value=e.apiClientSecret||""));L.addEventListener("submit",async t=>{t.preventDefault(),_();const n=document.getElementById("cms-url").value.trim().replace(/\/$/,""),a=document.getElementById("cms-key").value.trim(),o=document.getElementById("display-name").value.trim();try{if(c.textContent="Saving...",c.disabled=!0,!(await fetch("/config",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cmsUrl:n,cmsKey:a,displayName:o})})).ok)throw new Error("Failed to save config");e.data.cmsUrl=n,e.data.cmsKey=a,e.data.displayName=o,e.save(),Y(e.hardwareKey),c.textContent="Connecting...",l=null;const i=await v();if((await i.registerDisplay()).code==="READY")w();else{if(c.textContent="Authorizing...",await X(n,E)&&(await i.registerDisplay()).code==="READY"){w();return}R(o)}}catch(s){O(`Connection failed: ${s.message}`),c.textContent="Connect",c.disabled=!1}});z.addEventListener("click",()=>{A(),y(I),c.textContent="Connect",c.disabled=!1});async function Y(t){try{const n=indexedDB.open("xibo-hw-backup",1);n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains("keys")||a.createObjectStore("keys")},n.onsuccess=()=>{const a=n.result,o=a.transaction("keys","readwrite");o.objectStore("keys").put(t,"hardwareKey"),o.oncomplete=()=>a.close()}}catch(n){console.warn("[Setup] IndexedDB backup failed:",n)}}c.disabled=!1;c.textContent="Connect";e.isConfigured()&&!d&&(async()=>{try{(await(await v()).registerDisplay()).code==="READY"?window.location.href="/player/index.html":R(e.displayName)}catch{console.log("[Setup] Auto-check failed, showing form")}})();
|
|
2
|
+
//# sourceMappingURL=setup-CKgHUoum.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-Cal-4t_7.js","sources":["../../setup.html?html-proxy&index=1.js"],"sourcesContent":["\n import { config } from '@xiboplayer/utils';\n import { RestClient, XmdsClient } from '@xiboplayer/xmds';\n\n // Inject version from Vite build\n const appVersion = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '?';\n document.getElementById('version-footer').textContent = `Xibo Player v${appVersion}`;\n\n // ── Elements ──\n const form = document.getElementById('setup-form');\n const errorEl = document.getElementById('error');\n const submitBtn = document.getElementById('submit-btn');\n const phaseUnlock = document.getElementById('phase-unlock');\n const phaseSetup = document.getElementById('phase-setup');\n const phaseWaiting = document.getElementById('phase-waiting');\n const countdownEl = document.getElementById('countdown');\n const waitingNameEl = document.getElementById('waiting-display-name');\n const btnBack = document.getElementById('btn-back');\n const successFlash = document.getElementById('success-flash');\n\n // Show hardware key\n const hwKey = config.hardwareKey;\n document.getElementById('hw-key-display').textContent = hwKey;\n document.getElementById('hw-key-waiting').textContent = hwKey;\n\n // ── State ──\n const previousCmsUrl = config.cmsUrl || '';\n let pollTimer = null;\n let countdownTimer = null;\n let pollSeconds = 15;\n\n // ── Transport: try REST first, fall back to SOAP ──\n let transport = null;\n async function getTransport() {\n if (transport) return transport;\n try {\n const rest = new RestClient(config);\n await rest.registerDisplay();\n console.log('[Setup] Using REST transport');\n transport = rest;\n } catch (e) {\n console.log('[Setup] REST unavailable, using XMDS/SOAP:', e.message);\n transport = new XmdsClient(config);\n }\n return transport;\n }\n\n // ── Helpers ──\n function showPhase(phase) {\n phaseUnlock.classList.remove('active');\n phaseSetup.classList.remove('active');\n phaseWaiting.classList.remove('active');\n phase.classList.add('active');\n }\n\n function showError(msg) {\n errorEl.textContent = msg;\n errorEl.style.display = 'block';\n }\n\n function hideError() {\n errorEl.style.display = 'none';\n }\n\n function startCountdown() {\n let remaining = pollSeconds;\n countdownEl.textContent = remaining;\n clearInterval(countdownTimer);\n countdownTimer = setInterval(() => {\n remaining--;\n countdownEl.textContent = Math.max(0, remaining);\n if (remaining <= 0) clearInterval(countdownTimer);\n }, 1000);\n }\n\n async function showSuccess() {\n // If CMS URL changed, wipe all cached content (media, layouts, widgets\n // from the old CMS are useless) and the SW's cached index.html (which\n // may have a stale config injection script).\n const newUrl = document.getElementById('cms-url').value.trim().replace(/\\/$/, '');\n if (newUrl !== previousCmsUrl) {\n console.log('[Setup] CMS URL changed — wiping cached content');\n const cacheNames = await caches.keys();\n await Promise.all(cacheNames.map(name => caches.delete(name)));\n }\n successFlash.classList.add('active');\n setTimeout(() => {\n window.location.href = '/player/index.html';\n }, 800);\n }\n\n // ── Authorization Polling ──\n async function checkAuthorization() {\n try {\n const client = await getTransport();\n const result = await client.registerDisplay();\n console.log('[Setup] Poll result:', result.code, result.message);\n\n if (result.code === 'READY') {\n // Authorized!\n clearInterval(pollTimer);\n clearInterval(countdownTimer);\n showSuccess();\n return;\n }\n\n // Still waiting — restart countdown\n startCountdown();\n } catch (error) {\n console.warn('[Setup] Poll failed:', error.message);\n startCountdown();\n }\n }\n\n function startPolling(displayName) {\n waitingNameEl.textContent = displayName;\n showPhase(phaseWaiting);\n startCountdown();\n pollTimer = setInterval(checkAuthorization, pollSeconds * 1000);\n }\n\n function stopPolling() {\n clearInterval(pollTimer);\n clearInterval(countdownTimer);\n pollTimer = null;\n }\n\n // ── Auto-authorize via CMS API (through rest-proxy to avoid CORS) ──\n async function tryAutoAuthorize(cmsUrl, hardwareKey) {\n const clientId = document.getElementById('api-client-id').value.trim();\n const clientSecret = document.getElementById('api-client-secret').value.trim();\n if (!clientId || !clientSecret) return false;\n\n try {\n // Step 1: OAuth2 token via proxy\n const tokenResp = await fetch(`/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/authorize/access_token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret }),\n });\n if (!tokenResp.ok) throw new Error(`OAuth2 failed: ${tokenResp.status}`);\n const { access_token } = await tokenResp.json();\n\n // Step 2: Find display by hardwareKey via proxy\n const displayResp = await fetch(\n `/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/display&hardwareKey=${encodeURIComponent(hardwareKey)}`,\n { headers: { Authorization: `Bearer ${access_token}` } }\n );\n if (!displayResp.ok) throw new Error(`Find display failed: ${displayResp.status}`);\n const displays = await displayResp.json();\n if (!displays.length) { console.log('[Setup] Display not found via API (may not be registered yet)'); return false; }\n\n const display = displays[0];\n if (display.licensed === 1) { console.log('[Setup] Display already authorized'); return true; }\n\n // Step 3: Authorize display via proxy\n const authResp = await fetch(\n `/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/display/authorise/${display.displayId}`,\n { method: 'PUT', headers: { Authorization: `Bearer ${access_token}` } }\n );\n if (!authResp.ok) throw new Error(`Authorize failed: ${authResp.status}`);\n\n console.log('[Setup] Display auto-authorized via proxy!');\n config.apiClientId = clientId;\n config.apiClientSecret = clientSecret;\n return true;\n } catch (error) {\n console.warn('[Setup] Auto-authorize failed:', error.message);\n return false;\n }\n }\n\n // ── Unlock gate for reconfiguration ──\n const isReconfigure = config.isConfigured() && config.cmsKey;\n const alreadyUnlocked = new URLSearchParams(window.location.search).has('unlocked');\n const btnBackPlayer = document.getElementById('btn-back-player');\n if (isReconfigure && !alreadyUnlocked) {\n // Show unlock phase instead of setup form\n showPhase(phaseUnlock);\n }\n if (isReconfigure || alreadyUnlocked) {\n // Show \"Back to Player\" on the setup form\n btnBackPlayer.style.display = 'block';\n // Pre-fill form with existing config\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n }\n\n // ── Back to Player (cancel reconfiguration) ──\n function goBackToPlayer() {\n stopPolling();\n window.location.href = '/player/index.html';\n }\n btnBackPlayer.addEventListener('click', goBackToPlayer);\n\n // Esc also goes back to player when reconfiguring\n if (isReconfigure || alreadyUnlocked) {\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n goBackToPlayer();\n }\n });\n }\n\n // ── Unlock form handler ──\n document.getElementById('unlock-form').addEventListener('submit', (e) => {\n e.preventDefault();\n const unlockError = document.getElementById('unlock-error');\n const enteredKey = document.getElementById('unlock-key').value.trim();\n\n if (enteredKey === config.cmsKey) {\n unlockError.style.display = 'none';\n // Pre-fill form with existing config\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n showPhase(phaseSetup);\n } else {\n unlockError.textContent = 'Incorrect CMS key';\n unlockError.style.display = 'block';\n }\n });\n\n // ── Pre-fill if first-time setup (no unlock gate) ──\n if (!isReconfigure && config.isConfigured()) {\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n }\n\n // ── Form Submit ──\n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n hideError();\n\n const cmsUrl = document.getElementById('cms-url').value.trim().replace(/\\/$/, '');\n const cmsKey = document.getElementById('cms-key').value.trim();\n const displayName = document.getElementById('display-name').value.trim();\n\n // Write config.json via proxy (master config file)\n // The boot path (proxy injection → Config.load()) handles the rest.\n try {\n submitBtn.textContent = 'Saving...';\n submitBtn.disabled = true;\n\n const saveResp = await fetch('/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ cmsUrl, cmsKey, displayName }),\n });\n if (!saveResp.ok) throw new Error('Failed to save config');\n\n // Also update in-memory config for the test connection below\n config.data.cmsUrl = cmsUrl;\n config.data.cmsKey = cmsKey;\n config.data.displayName = displayName;\n config.save();\n\n // Backup hardware key to IndexedDB for stability\n backupHardwareKey(config.hardwareKey);\n\n // Test connection\n submitBtn.textContent = 'Connecting...';\n\n // Try REST first, fall back to SOAP\n transport = null; // reset transport for fresh detection\n const client = await getTransport();\n const result = await client.registerDisplay();\n\n if (result.code === 'READY') {\n showSuccess();\n } else {\n // Try auto-authorize via CMS API if credentials provided\n submitBtn.textContent = 'Authorizing...';\n const autoAuthed = await tryAutoAuthorize(cmsUrl, hwKey);\n\n if (autoAuthed) {\n // Verify registration\n const recheck = await client.registerDisplay();\n if (recheck.code === 'READY') {\n showSuccess();\n return;\n }\n }\n\n // Not authorized yet — switch to polling phase\n startPolling(displayName);\n }\n } catch (error) {\n showError(`Connection failed: ${error.message}`);\n submitBtn.textContent = 'Connect';\n submitBtn.disabled = false;\n }\n });\n\n // ── Back button ──\n btnBack.addEventListener('click', () => {\n stopPolling();\n showPhase(phaseSetup);\n submitBtn.textContent = 'Connect';\n submitBtn.disabled = false;\n });\n\n // ── IndexedDB hardware key backup ──\n async function backupHardwareKey(key) {\n try {\n const req = indexedDB.open('xibo-hw-backup', 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains('keys')) {\n db.createObjectStore('keys');\n }\n };\n req.onsuccess = () => {\n const db = req.result;\n const tx = db.transaction('keys', 'readwrite');\n tx.objectStore('keys').put(key, 'hardwareKey');\n tx.oncomplete = () => db.close();\n };\n } catch (e) {\n console.warn('[Setup] IndexedDB backup failed:', e);\n }\n }\n\n // ── Enable submit ──\n submitBtn.disabled = false;\n submitBtn.textContent = 'Connect';\n\n // ── Auto-resume polling if already registered but not authorized ──\n // Skip auto-resume when reconfiguring (user explicitly opened setup)\n if (config.isConfigured() && !isReconfigure) {\n (async () => {\n try {\n const client = await getTransport();\n const result = await client.registerDisplay();\n if (result.code === 'READY') {\n window.location.href = '/player/index.html';\n } else {\n // Not authorized — go straight to polling\n startPolling(config.displayName);\n }\n } catch (e) {\n // Connection failed, show form\n console.log('[Setup] Auto-check failed, showing form');\n }\n })();\n }\n "],"names":["appVersion","form","errorEl","submitBtn","phaseUnlock","phaseSetup","phaseWaiting","countdownEl","waitingNameEl","btnBack","successFlash","hwKey","config","previousCmsUrl","pollTimer","countdownTimer","pollSeconds","transport","getTransport","rest","RestClient","e","XmdsClient","showPhase","phase","showError","msg","hideError","startCountdown","remaining","showSuccess","cacheNames","name","checkAuthorization","result","error","startPolling","displayName","stopPolling","tryAutoAuthorize","cmsUrl","hardwareKey","clientId","clientSecret","tokenResp","access_token","displayResp","displays","display","authResp","isReconfigure","alreadyUnlocked","btnBackPlayer","goBackToPlayer","unlockError","cmsKey","backupHardwareKey","client","key","req","db","tx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"setup-CKgHUoum.js","sources":["../../setup.html?html-proxy&index=1.js"],"sourcesContent":["\n import { config } from '@xiboplayer/utils';\n import { RestClient, XmdsClient } from '@xiboplayer/xmds';\n\n // Inject version from Vite build\n const appVersion = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '?';\n document.getElementById('version-footer').textContent = `Xibo Player v${appVersion}`;\n\n // ── Elements ──\n const form = document.getElementById('setup-form');\n const errorEl = document.getElementById('error');\n const submitBtn = document.getElementById('submit-btn');\n const phaseUnlock = document.getElementById('phase-unlock');\n const phaseSetup = document.getElementById('phase-setup');\n const phaseWaiting = document.getElementById('phase-waiting');\n const countdownEl = document.getElementById('countdown');\n const waitingNameEl = document.getElementById('waiting-display-name');\n const btnBack = document.getElementById('btn-back');\n const successFlash = document.getElementById('success-flash');\n\n // Show hardware key\n const hwKey = config.hardwareKey;\n document.getElementById('hw-key-display').textContent = hwKey;\n document.getElementById('hw-key-waiting').textContent = hwKey;\n\n // ── State ──\n const previousCmsUrl = config.cmsUrl || '';\n let pollTimer = null;\n let countdownTimer = null;\n let pollSeconds = 15;\n\n // ── Transport: try REST first, fall back to SOAP ──\n let transport = null;\n async function getTransport() {\n if (transport) return transport;\n try {\n const rest = new RestClient(config);\n await rest.registerDisplay();\n console.log('[Setup] Using REST transport');\n transport = rest;\n } catch (e) {\n console.log('[Setup] REST unavailable, using XMDS/SOAP:', e.message);\n transport = new XmdsClient(config);\n }\n return transport;\n }\n\n // ── Helpers ──\n function showPhase(phase) {\n phaseUnlock.classList.remove('active');\n phaseSetup.classList.remove('active');\n phaseWaiting.classList.remove('active');\n phase.classList.add('active');\n }\n\n function showError(msg) {\n errorEl.textContent = msg;\n errorEl.style.display = 'block';\n }\n\n function hideError() {\n errorEl.style.display = 'none';\n }\n\n function startCountdown() {\n let remaining = pollSeconds;\n countdownEl.textContent = remaining;\n clearInterval(countdownTimer);\n countdownTimer = setInterval(() => {\n remaining--;\n countdownEl.textContent = Math.max(0, remaining);\n if (remaining <= 0) clearInterval(countdownTimer);\n }, 1000);\n }\n\n async function showSuccess() {\n // If CMS URL changed, wipe all cached content (media, layouts, widgets\n // from the old CMS are useless) and the SW's cached index.html (which\n // may have a stale config injection script).\n const newUrl = document.getElementById('cms-url').value.trim().replace(/\\/$/, '');\n if (newUrl !== previousCmsUrl) {\n console.log('[Setup] CMS URL changed — wiping cached content');\n const cacheNames = await caches.keys();\n await Promise.all(cacheNames.map(name => caches.delete(name)));\n }\n successFlash.classList.add('active');\n setTimeout(() => {\n window.location.href = '/player/index.html';\n }, 800);\n }\n\n // ── Authorization Polling ──\n async function checkAuthorization() {\n try {\n const client = await getTransport();\n const result = await client.registerDisplay();\n console.log('[Setup] Poll result:', result.code, result.message);\n\n if (result.code === 'READY') {\n // Authorized!\n clearInterval(pollTimer);\n clearInterval(countdownTimer);\n showSuccess();\n return;\n }\n\n // Still waiting — restart countdown\n startCountdown();\n } catch (error) {\n console.warn('[Setup] Poll failed:', error.message);\n startCountdown();\n }\n }\n\n function startPolling(displayName) {\n waitingNameEl.textContent = displayName;\n showPhase(phaseWaiting);\n startCountdown();\n pollTimer = setInterval(checkAuthorization, pollSeconds * 1000);\n }\n\n function stopPolling() {\n clearInterval(pollTimer);\n clearInterval(countdownTimer);\n pollTimer = null;\n }\n\n // ── Auto-authorize via CMS API (through rest-proxy to avoid CORS) ──\n async function tryAutoAuthorize(cmsUrl, hardwareKey) {\n const clientId = document.getElementById('api-client-id').value.trim();\n const clientSecret = document.getElementById('api-client-secret').value.trim();\n if (!clientId || !clientSecret) return false;\n\n try {\n // Step 1: OAuth2 token via proxy\n const tokenResp = await fetch(`/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/authorize/access_token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret }),\n });\n if (!tokenResp.ok) throw new Error(`OAuth2 failed: ${tokenResp.status}`);\n const { access_token } = await tokenResp.json();\n\n // Step 2: Find display by hardwareKey via proxy\n const displayResp = await fetch(\n `/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/display&hardwareKey=${encodeURIComponent(hardwareKey)}`,\n { headers: { Authorization: `Bearer ${access_token}` } }\n );\n if (!displayResp.ok) throw new Error(`Find display failed: ${displayResp.status}`);\n const displays = await displayResp.json();\n if (!displays.length) { console.log('[Setup] Display not found via API (may not be registered yet)'); return false; }\n\n const display = displays[0];\n if (display.licensed === 1) { console.log('[Setup] Display already authorized'); return true; }\n\n // Step 3: Authorize display via proxy\n const authResp = await fetch(\n `/rest-proxy?cms=${encodeURIComponent(cmsUrl)}&path=/api/display/authorise/${display.displayId}`,\n { method: 'PUT', headers: { Authorization: `Bearer ${access_token}` } }\n );\n if (!authResp.ok) throw new Error(`Authorize failed: ${authResp.status}`);\n\n console.log('[Setup] Display auto-authorized via proxy!');\n config.apiClientId = clientId;\n config.apiClientSecret = clientSecret;\n return true;\n } catch (error) {\n console.warn('[Setup] Auto-authorize failed:', error.message);\n return false;\n }\n }\n\n // ── Unlock gate for reconfiguration ──\n const isReconfigure = config.isConfigured() && config.cmsKey;\n const alreadyUnlocked = new URLSearchParams(window.location.search).has('unlocked');\n const btnBackPlayer = document.getElementById('btn-back-player');\n if (isReconfigure && !alreadyUnlocked) {\n // Show unlock phase instead of setup form\n showPhase(phaseUnlock);\n }\n if (isReconfigure || alreadyUnlocked) {\n // Show \"Back to Player\" on the setup form\n btnBackPlayer.style.display = 'block';\n // Pre-fill form with existing config\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n }\n\n // ── Back to Player (cancel reconfiguration) ──\n function goBackToPlayer() {\n stopPolling();\n window.location.href = '/player/index.html';\n }\n btnBackPlayer.addEventListener('click', goBackToPlayer);\n\n // Esc also goes back to player when reconfiguring\n if (isReconfigure || alreadyUnlocked) {\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n goBackToPlayer();\n }\n });\n }\n\n // ── Unlock form handler ──\n document.getElementById('unlock-form').addEventListener('submit', (e) => {\n e.preventDefault();\n const unlockError = document.getElementById('unlock-error');\n const enteredKey = document.getElementById('unlock-key').value.trim();\n\n if (enteredKey === config.cmsKey) {\n unlockError.style.display = 'none';\n // Pre-fill form with existing config\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n showPhase(phaseSetup);\n } else {\n unlockError.textContent = 'Incorrect CMS key';\n unlockError.style.display = 'block';\n }\n });\n\n // ── Pre-fill if first-time setup (no unlock gate) ──\n if (!isReconfigure && config.isConfigured()) {\n document.getElementById('cms-url').value = config.cmsUrl;\n document.getElementById('cms-key').value = config.cmsKey;\n document.getElementById('display-name').value = config.displayName;\n if (config.apiClientId) {\n document.getElementById('api-client-id').value = config.apiClientId;\n document.getElementById('api-client-secret').value = config.apiClientSecret || '';\n }\n }\n\n // ── Form Submit ──\n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n hideError();\n\n const cmsUrl = document.getElementById('cms-url').value.trim().replace(/\\/$/, '');\n const cmsKey = document.getElementById('cms-key').value.trim();\n const displayName = document.getElementById('display-name').value.trim();\n\n // Write config.json via proxy (master config file)\n // The boot path (proxy injection → Config.load()) handles the rest.\n try {\n submitBtn.textContent = 'Saving...';\n submitBtn.disabled = true;\n\n const saveResp = await fetch('/config', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ cmsUrl, cmsKey, displayName }),\n });\n if (!saveResp.ok) throw new Error('Failed to save config');\n\n // Also update in-memory config for the test connection below\n config.data.cmsUrl = cmsUrl;\n config.data.cmsKey = cmsKey;\n config.data.displayName = displayName;\n config.save();\n\n // Backup hardware key to IndexedDB for stability\n backupHardwareKey(config.hardwareKey);\n\n // Test connection\n submitBtn.textContent = 'Connecting...';\n\n // Try REST first, fall back to SOAP\n transport = null; // reset transport for fresh detection\n const client = await getTransport();\n const result = await client.registerDisplay();\n\n if (result.code === 'READY') {\n showSuccess();\n } else {\n // Try auto-authorize via CMS API if credentials provided\n submitBtn.textContent = 'Authorizing...';\n const autoAuthed = await tryAutoAuthorize(cmsUrl, hwKey);\n\n if (autoAuthed) {\n // Verify registration\n const recheck = await client.registerDisplay();\n if (recheck.code === 'READY') {\n showSuccess();\n return;\n }\n }\n\n // Not authorized yet — switch to polling phase\n startPolling(displayName);\n }\n } catch (error) {\n showError(`Connection failed: ${error.message}`);\n submitBtn.textContent = 'Connect';\n submitBtn.disabled = false;\n }\n });\n\n // ── Back button ──\n btnBack.addEventListener('click', () => {\n stopPolling();\n showPhase(phaseSetup);\n submitBtn.textContent = 'Connect';\n submitBtn.disabled = false;\n });\n\n // ── IndexedDB hardware key backup ──\n async function backupHardwareKey(key) {\n try {\n const req = indexedDB.open('xibo-hw-backup', 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains('keys')) {\n db.createObjectStore('keys');\n }\n };\n req.onsuccess = () => {\n const db = req.result;\n const tx = db.transaction('keys', 'readwrite');\n tx.objectStore('keys').put(key, 'hardwareKey');\n tx.oncomplete = () => db.close();\n };\n } catch (e) {\n console.warn('[Setup] IndexedDB backup failed:', e);\n }\n }\n\n // ── Enable submit ──\n submitBtn.disabled = false;\n submitBtn.textContent = 'Connect';\n\n // ── Auto-resume polling if already registered but not authorized ──\n // Skip auto-resume when reconfiguring (user explicitly opened setup)\n if (config.isConfigured() && !isReconfigure) {\n (async () => {\n try {\n const client = await getTransport();\n const result = await client.registerDisplay();\n if (result.code === 'READY') {\n window.location.href = '/player/index.html';\n } else {\n // Not authorized — go straight to polling\n startPolling(config.displayName);\n }\n } catch (e) {\n // Connection failed, show form\n console.log('[Setup] Auto-check failed, showing form');\n }\n })();\n }\n "],"names":["appVersion","form","errorEl","submitBtn","phaseUnlock","phaseSetup","phaseWaiting","countdownEl","waitingNameEl","btnBack","successFlash","hwKey","config","previousCmsUrl","pollTimer","countdownTimer","pollSeconds","transport","getTransport","rest","RestClient","e","XmdsClient","showPhase","phase","showError","msg","hideError","startCountdown","remaining","showSuccess","cacheNames","name","checkAuthorization","result","error","startPolling","displayName","stopPolling","tryAutoAuthorize","cmsUrl","hardwareKey","clientId","clientSecret","tokenResp","access_token","displayResp","displays","display","authResp","isReconfigure","alreadyUnlocked","btnBackPlayer","goBackToPlayer","unlockError","cmsKey","backupHardwareKey","client","key","req","db","tx"],"mappings":"4IAKI,MAAMA,EAAsD,SAC5D,SAAS,eAAe,gBAAgB,EAAE,YAAc,gBAAgBA,CAAU,GAGlF,MAAMC,EAAO,SAAS,eAAe,YAAY,EAC3CC,EAAU,SAAS,eAAe,OAAO,EACzCC,EAAY,SAAS,eAAe,YAAY,EAChDC,EAAc,SAAS,eAAe,cAAc,EACpDC,EAAa,SAAS,eAAe,aAAa,EAClDC,EAAe,SAAS,eAAe,eAAe,EACtDC,EAAc,SAAS,eAAe,WAAW,EACjDC,EAAgB,SAAS,eAAe,sBAAsB,EAC9DC,EAAU,SAAS,eAAe,UAAU,EAC5CC,EAAe,SAAS,eAAe,eAAe,EAGtDC,EAAQC,EAAO,YACrB,SAAS,eAAe,gBAAgB,EAAE,YAAcD,EACxD,SAAS,eAAe,gBAAgB,EAAE,YAAcA,EAGxD,MAAME,EAAiBD,EAAO,QAAU,GACxC,IAAIE,EAAY,KACZC,EAAiB,KACjBC,EAAc,GAGdC,EAAY,KAChB,eAAeC,GAAe,CAC5B,GAAID,EAAW,OAAOA,EACtB,GAAI,CACF,MAAME,EAAO,IAAIC,EAAWR,CAAM,EAClC,MAAMO,EAAK,gBAAA,EACX,QAAQ,IAAI,8BAA8B,EAC1CF,EAAYE,CACd,OAASE,EAAG,CACV,QAAQ,IAAI,6CAA8CA,EAAE,OAAO,EACnEJ,EAAY,IAAIK,EAAWV,CAAM,CACnC,CACA,OAAOK,CACT,CAGA,SAASM,EAAUC,EAAO,CACxBpB,EAAY,UAAU,OAAO,QAAQ,EACrCC,EAAW,UAAU,OAAO,QAAQ,EACpCC,EAAa,UAAU,OAAO,QAAQ,EACtCkB,EAAM,UAAU,IAAI,QAAQ,CAC9B,CAEA,SAASC,EAAUC,EAAK,CACtBxB,EAAQ,YAAcwB,EACtBxB,EAAQ,MAAM,QAAU,OAC1B,CAEA,SAASyB,GAAY,CACnBzB,EAAQ,MAAM,QAAU,MAC1B,CAEA,SAAS0B,GAAiB,CACxB,IAAIC,EAAYb,EAChBT,EAAY,YAAcsB,EAC1B,cAAcd,CAAc,EAC5BA,EAAiB,YAAY,IAAM,CACjCc,IACAtB,EAAY,YAAc,KAAK,IAAI,EAAGsB,CAAS,EAC3CA,GAAa,GAAG,cAAcd,CAAc,CAClD,EAAG,GAAI,CACT,CAEA,eAAee,GAAc,CAK3B,GADe,SAAS,eAAe,SAAS,EAAE,MAAM,OAAO,QAAQ,MAAO,EAAE,IACjEjB,EAAgB,CAC7B,QAAQ,IAAI,iDAAiD,EAC7D,MAAMkB,EAAa,MAAM,OAAO,KAAA,EAChC,MAAM,QAAQ,IAAIA,EAAW,OAAY,OAAO,OAAOC,CAAI,CAAC,CAAC,CAC/D,CACAtB,EAAa,UAAU,IAAI,QAAQ,EACnC,WAAW,IAAM,CACf,OAAO,SAAS,KAAO,oBACzB,EAAG,GAAG,CACR,CAGA,eAAeuB,GAAqB,CAClC,GAAI,CAEF,MAAMC,EAAS,MADA,MAAMhB,EAAA,GACO,gBAAA,EAG5B,GAFA,QAAQ,IAAI,uBAAwBgB,EAAO,KAAMA,EAAO,OAAO,EAE3DA,EAAO,OAAS,QAAS,CAE3B,cAAcpB,CAAS,EACvB,cAAcC,CAAc,EAC5Be,EAAA,EACA,MACF,CAGAF,EAAA,CACF,OAASO,EAAO,CACd,QAAQ,KAAK,uBAAwBA,EAAM,OAAO,EAClDP,EAAA,CACF,CACF,CAEA,SAASQ,EAAaC,EAAa,CACjC7B,EAAc,YAAc6B,EAC5Bd,EAAUjB,CAAY,EACtBsB,EAAA,EACAd,EAAY,YAAYmB,EAAoBjB,EAAc,GAAI,CAChE,CAEA,SAASsB,GAAc,CACrB,cAAcxB,CAAS,EACvB,cAAcC,CAAc,EAC5BD,EAAY,IACd,CAGA,eAAeyB,EAAiBC,EAAQC,EAAa,CACnD,MAAMC,EAAW,SAAS,eAAe,eAAe,EAAE,MAAM,KAAA,EAC1DC,EAAe,SAAS,eAAe,mBAAmB,EAAE,MAAM,KAAA,EACxE,GAAI,CAACD,GAAY,CAACC,EAAc,MAAO,GAEvC,GAAI,CAEF,MAAMC,EAAY,MAAM,MAAM,mBAAmB,mBAAmBJ,CAAM,CAAC,oCAAqC,CAC9G,OAAQ,OACR,QAAS,CAAE,eAAgB,mCAAA,EAC3B,KAAM,IAAI,gBAAgB,CAAE,WAAY,qBAAsB,UAAWE,EAAU,cAAeC,CAAA,CAAc,CAAA,CACjH,EACD,GAAI,CAACC,EAAU,GAAI,MAAM,IAAI,MAAM,kBAAkBA,EAAU,MAAM,EAAE,EACvE,KAAM,CAAE,aAAAC,CAAA,EAAiB,MAAMD,EAAU,KAAA,EAGnCE,EAAc,MAAM,MACxB,mBAAmB,mBAAmBN,CAAM,CAAC,kCAAkC,mBAAmBC,CAAW,CAAC,GAC9G,CAAE,QAAS,CAAE,cAAe,UAAUI,CAAY,GAAG,CAAE,EAEzD,GAAI,CAACC,EAAY,GAAI,MAAM,IAAI,MAAM,wBAAwBA,EAAY,MAAM,EAAE,EACjF,MAAMC,EAAW,MAAMD,EAAY,KAAA,EACnC,GAAI,CAACC,EAAS,OAAU,eAAQ,IAAI,+DAA+D,EAAU,GAE7G,MAAMC,EAAUD,EAAS,CAAC,EAC1B,GAAIC,EAAQ,WAAa,EAAK,eAAQ,IAAI,oCAAoC,EAAU,GAGxF,MAAMC,EAAW,MAAM,MACrB,mBAAmB,mBAAmBT,CAAM,CAAC,gCAAgCQ,EAAQ,SAAS,GAC9F,CAAE,OAAQ,MAAO,QAAS,CAAE,cAAe,UAAUH,CAAY,EAAA,CAAG,CAAE,EAExE,GAAI,CAACI,EAAS,GAAI,MAAM,IAAI,MAAM,qBAAqBA,EAAS,MAAM,EAAE,EAExE,eAAQ,IAAI,4CAA4C,EACxDrC,EAAO,YAAc8B,EACrB9B,EAAO,gBAAkB+B,EAClB,EACT,OAASR,EAAO,CACd,eAAQ,KAAK,iCAAkCA,EAAM,OAAO,EACrD,EACT,CACF,CAGA,MAAMe,EAAgBtC,EAAO,aAAA,GAAkBA,EAAO,OAChDuC,EAAkB,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,UAAU,EAC5EC,EAAgB,SAAS,eAAe,iBAAiB,EAC3DF,GAAiB,CAACC,GAEpB5B,EAAUnB,CAAW,GAEnB8C,GAAiBC,KAEnBC,EAAc,MAAM,QAAU,QAE9B,SAAS,eAAe,SAAS,EAAE,MAAQxC,EAAO,OAClD,SAAS,eAAe,SAAS,EAAE,MAAQA,EAAO,OAClD,SAAS,eAAe,cAAc,EAAE,MAAQA,EAAO,YACnDA,EAAO,cACT,SAAS,eAAe,eAAe,EAAE,MAAQA,EAAO,YACxD,SAAS,eAAe,mBAAmB,EAAE,MAAQA,EAAO,iBAAmB,KAKnF,SAASyC,GAAiB,CACxBf,EAAA,EACA,OAAO,SAAS,KAAO,oBACzB,CACAc,EAAc,iBAAiB,QAASC,CAAc,GAGlDH,GAAiBC,IACnB,SAAS,iBAAiB,UAAY9B,GAAM,CACtCA,EAAE,MAAQ,WACZA,EAAE,eAAA,EACFgC,EAAA,EAEJ,CAAC,EAIH,SAAS,eAAe,aAAa,EAAE,iBAAiB,SAAWhC,GAAM,CACvEA,EAAE,eAAA,EACF,MAAMiC,EAAc,SAAS,eAAe,cAAc,EACvC,SAAS,eAAe,YAAY,EAAE,MAAM,KAAA,IAE5C1C,EAAO,QACxB0C,EAAY,MAAM,QAAU,OAE5B,SAAS,eAAe,SAAS,EAAE,MAAQ1C,EAAO,OAClD,SAAS,eAAe,SAAS,EAAE,MAAQA,EAAO,OAClD,SAAS,eAAe,cAAc,EAAE,MAAQA,EAAO,YACnDA,EAAO,cACT,SAAS,eAAe,eAAe,EAAE,MAAQA,EAAO,YACxD,SAAS,eAAe,mBAAmB,EAAE,MAAQA,EAAO,iBAAmB,IAEjFW,EAAUlB,CAAU,IAEpBiD,EAAY,YAAc,oBAC1BA,EAAY,MAAM,QAAU,QAEhC,CAAC,EAGG,CAACJ,GAAiBtC,EAAO,iBAC3B,SAAS,eAAe,SAAS,EAAE,MAAQA,EAAO,OAClD,SAAS,eAAe,SAAS,EAAE,MAAQA,EAAO,OAClD,SAAS,eAAe,cAAc,EAAE,MAAQA,EAAO,YACnDA,EAAO,cACT,SAAS,eAAe,eAAe,EAAE,MAAQA,EAAO,YACxD,SAAS,eAAe,mBAAmB,EAAE,MAAQA,EAAO,iBAAmB,KAKnFX,EAAK,iBAAiB,SAAU,MAAOoB,GAAM,CAC3CA,EAAE,eAAA,EACFM,EAAA,EAEA,MAAMa,EAAS,SAAS,eAAe,SAAS,EAAE,MAAM,OAAO,QAAQ,MAAO,EAAE,EAC1Ee,EAAS,SAAS,eAAe,SAAS,EAAE,MAAM,KAAA,EAClDlB,EAAc,SAAS,eAAe,cAAc,EAAE,MAAM,KAAA,EAIlE,GAAI,CASF,GARAlC,EAAU,YAAc,YACxBA,EAAU,SAAW,GAOjB,EALa,MAAM,MAAM,UAAW,CACtC,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAU,CAAE,OAAAqC,EAAQ,OAAAe,EAAQ,YAAAlB,EAAa,CAAA,CACrD,GACa,GAAI,MAAM,IAAI,MAAM,uBAAuB,EAGzDzB,EAAO,KAAK,OAAS4B,EACrB5B,EAAO,KAAK,OAAS2C,EACrB3C,EAAO,KAAK,YAAcyB,EAC1BzB,EAAO,KAAA,EAGP4C,EAAkB5C,EAAO,WAAW,EAGpCT,EAAU,YAAc,gBAGxBc,EAAY,KACZ,MAAMwC,EAAS,MAAMvC,EAAA,EAGrB,IAFe,MAAMuC,EAAO,gBAAA,GAEjB,OAAS,QAClB3B,EAAA,MACK,CAKL,GAHA3B,EAAU,YAAc,iBACL,MAAMoC,EAAiBC,EAAQ7B,CAAK,IAIrC,MAAM8C,EAAO,gBAAA,GACjB,OAAS,QAAS,CAC5B3B,EAAA,EACA,MACF,CAIFM,EAAaC,CAAW,CAC1B,CACF,OAASF,EAAO,CACdV,EAAU,sBAAsBU,EAAM,OAAO,EAAE,EAC/ChC,EAAU,YAAc,UACxBA,EAAU,SAAW,EACvB,CACF,CAAC,EAGDM,EAAQ,iBAAiB,QAAS,IAAM,CACtC6B,EAAA,EACAf,EAAUlB,CAAU,EACpBF,EAAU,YAAc,UACxBA,EAAU,SAAW,EACvB,CAAC,EAGD,eAAeqD,EAAkBE,EAAK,CACpC,GAAI,CACF,MAAMC,EAAM,UAAU,KAAK,iBAAkB,CAAC,EAC9CA,EAAI,gBAAkB,IAAM,CAC1B,MAAMC,EAAKD,EAAI,OACVC,EAAG,iBAAiB,SAAS,MAAM,GACtCA,EAAG,kBAAkB,MAAM,CAE/B,EACAD,EAAI,UAAY,IAAM,CACpB,MAAMC,EAAKD,EAAI,OACTE,EAAKD,EAAG,YAAY,OAAQ,WAAW,EAC7CC,EAAG,YAAY,MAAM,EAAE,IAAIH,EAAK,aAAa,EAC7CG,EAAG,WAAa,IAAMD,EAAG,MAAA,CAC3B,CACF,OAASvC,EAAG,CACV,QAAQ,KAAK,mCAAoCA,CAAC,CACpD,CACF,CAGAlB,EAAU,SAAW,GACrBA,EAAU,YAAc,UAIpBS,EAAO,gBAAkB,CAACsC,IAC3B,SAAY,CACX,GAAI,EAEa,MADA,MAAMhC,EAAA,GACO,gBAAA,GACjB,OAAS,QAClB,OAAO,SAAS,KAAO,qBAGvBkB,EAAaxB,EAAO,WAAW,CAEnC,MAAY,CAEV,QAAQ,IAAI,yCAAyC,CACvD,CACF,GAAA"}
|
package/dist/index.html
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
height: 100%;
|
|
89
89
|
}
|
|
90
90
|
</style>
|
|
91
|
-
<script type="module" crossorigin src="./assets/main-
|
|
91
|
+
<script type="module" crossorigin src="./assets/main-DstA7-TG.js"></script>
|
|
92
92
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-B5Qt9EMX.js">
|
|
93
93
|
<link rel="modulepreload" crossorigin href="./assets/cms-api-B3gEu-pj.js">
|
|
94
94
|
<link rel="modulepreload" crossorigin href="./assets/widget-html-DvXXB6at.js">
|
|
@@ -103,6 +103,17 @@
|
|
|
103
103
|
<!-- Player rendering container -->
|
|
104
104
|
<div id="player-container"></div>
|
|
105
105
|
|
|
106
|
+
<!-- Remove status bar overlay unless explicitly enabled in config -->
|
|
107
|
+
<script>
|
|
108
|
+
try {
|
|
109
|
+
var cfg = JSON.parse(localStorage.getItem('xibo_config') || '{}');
|
|
110
|
+
var hover = cfg.controls && cfg.controls.mouse && cfg.controls.mouse.statusBarOnHover;
|
|
111
|
+
if (hover !== true) {
|
|
112
|
+
document.getElementById('overlay').remove();
|
|
113
|
+
}
|
|
114
|
+
} catch (e) {}
|
|
115
|
+
</script>
|
|
116
|
+
|
|
106
117
|
<!-- Check for configuration before loading player -->
|
|
107
118
|
<script>
|
|
108
119
|
// TEMP: Force debug level for testing (shows video controls, verbose logging)
|
package/dist/setup.html
CHANGED
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
color: #444;
|
|
288
288
|
}
|
|
289
289
|
</style>
|
|
290
|
-
<script type="module" crossorigin src="./assets/setup-
|
|
290
|
+
<script type="module" crossorigin src="./assets/setup-CKgHUoum.js"></script>
|
|
291
291
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-B5Qt9EMX.js">
|
|
292
292
|
<link rel="modulepreload" crossorigin href="./assets/cms-api-B3gEu-pj.js">
|
|
293
293
|
<link rel="modulepreload" crossorigin href="./assets/xmds-client-CX0t7Oes.js">
|
package/dist/sw-pwa.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as O,L as q,B as x,a as G}from"./assets/widget-html-DvXXB6at.js";import{VERSION as z}from"./assets/index-RNYmTctp.js";import"./assets/cms-api-B3gEu-pj.js";const C=(()=>{var o;return typeof self<"u"&&((o=self.registration)!=null&&o.scope)?new URL(self.registration.scope).pathname.replace(/\/$/,""):"/player/pwa"})();function H(o,t=1){if(o===0)return"0 Bytes";if(o<1024)return`${o} Bytes`;const e=o/1024;if(e<1024)return`${e.toFixed(t)} KB`;const s=e/1024;return s<1024?`${s.toFixed(t)} MB`:`${(s/1024).toFixed(t)} GB`}class E{constructor(t){this.name=t,this.level=typeof self<"u"&&self.swLogLevel||"INFO"}debug(...t){this.level==="DEBUG"&&console.log(`[${this.name}] DEBUG:`,...t)}info(...t){(this.level==="DEBUG"||this.level==="INFO")&&console.log(`[${this.name}]`,...t)}warn(...t){console.warn(`[${this.name}]`,...t)}error(...t){console.error(`[${this.name}]`,...t)}}function j(o){o||(o=new E("ChunkConfig"));const t=typeof navigator<"u"&&navigator.deviceMemory||null;let e=4;if(t)e=t,o.info("Detected device memory:",t,"GB");else if(typeof navigator<"u"){const c=navigator.userAgent.toLowerCase();c.includes("raspberry pi")||c.includes("armv6")?(e=.5,o.info("Detected Pi Zero (512 MB RAM estimated)")):c.includes("armv7")?(e=1,o.info("Detected ARM device (1 GB RAM estimated)")):o.info("Using default RAM estimate:",e,"GB")}let s,n,a,r;return e<=.5?(s=10*1024*1024,n=25,a=25*1024*1024,r=1,o.info("Low-memory config: 10 MB chunks, 25 MB cache, 1 concurrent download")):e<=1?(s=20*1024*1024,n=50,a=50*1024*1024,r=2,o.info("1GB-RAM config: 20 MB chunks, 50 MB cache, 2 concurrent downloads")):e<=2?(s=30*1024*1024,n=100,a=75*1024*1024,r=2,o.info("2GB-RAM config: 30 MB chunks, 100 MB cache, 2 concurrent downloads")):e<=4?(s=50*1024*1024,n=200,a=100*1024*1024,r=4,o.info("4GB-RAM config: 50 MB chunks, 200 MB cache, 4 concurrent downloads")):(s=100*1024*1024,n=500,a=200*1024*1024,r=6,o.info("High-RAM config: 100 MB chunks, 500 MB cache, 6 concurrent downloads")),{chunkSize:s,blobCacheSize:n,threshold:a,concurrency:r}}class X{constructor(t){this.downloadManager=t,this.pendingFetches=new Map,this.log=new E("SW")}async handleRequest(t){var h;const e=new URL(t.request.url);if(this.log.info("handleRequest called for:",e.href),e.pathname===C+"/"||e.pathname===C+"/index.html"||e.pathname===C+"/setup.html")return this.log.info("Static page, passing to network:",e.pathname),fetch(t.request);if((e.pathname.includes("xmds.php")||e.pathname.includes("pwa/file"))&&(e.searchParams.get("fileType")==="bundle"||e.searchParams.get("fileType")==="fontCss"||e.searchParams.get("fileType")==="font"))return this._handleWidgetResource(t,e);if((e.pathname.includes("xmds.php")||e.pathname.includes("pwa/file"))&&e.searchParams.has("file")){const l=e.searchParams.get("file"),d=l.split(".")[0],p=e.searchParams.get("type"),w=p==="L"?"layout":"media";this.log.info("XMDS request:",l,"type:",p,"→ /store/"+w+"/"+d);const $=`/store/${w}/${d}`;try{const S=await fetch($);if(S.ok)return new Response(S.body,{headers:{"Content-Type":S.headers.get("Content-Type")||"video/mp4","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000","Accept-Ranges":"bytes"}});(h=S.body)==null||h.cancel()}catch{}return this.log.info("XMDS file not stored, passing through:",l),fetch(t.request)}if(e.pathname.startsWith(C+"/cache/static/"))return this._handleStaticResource(e);if(!e.pathname.startsWith(C+"/cache/"))return this.log.info("NOT a cache request, returning null:",e.pathname),null;if(this.log.info("Cache request:",e.pathname),e.pathname.startsWith(C+"/cache/widget/"))return this._handleWidgetHtml(e);const s=e.pathname.replace(/\.json$/,""),n=t.request.method,a=t.request.headers.get("Range");a?this.log.info(n,s,`Range: ${a}`):this.log.info(n,s);const c=`/store/${s.replace(C+"/cache/","").split("/").join("/")}`;try{const l={method:n};a&&(l.headers={Range:a});const d=await fetch(c,l);return d.ok||d.status===206?d:d.status===404?this._handleNotStored(s,t,n,a):d}catch(l){return this.log.error("Proxy fetch error:",l.message),this._handleNotStored(s,t,n,a)}}async _handleNotStored(t,e,s,n){const a=t.split("/"),r=a[a.length-2],c=a[a.length-1];let h=null;for(const[,l]of this.downloadManager.queue.active.entries())if(l.fileInfo.type===r&&String(l.fileInfo.id)===c){h=l;break}if(h){this.log.info("Download in progress, waiting:",t);try{await h.wait();const d=`/store/${t.replace(C+"/cache/","").split("/").join("/")}`,p={method:s};n&&(p.headers={Range:n});const w=await fetch(d,p);if(w.ok||w.status===206)return this.log.info("Download complete, serving from store:",t),w}catch(l){return this.log.error("Download failed:",t,l),new Response("Download failed: "+l.message,{status:500})}}return this.log.info("Not found:",t),new Response("Not found",{status:404,headers:{"Cache-Control":"no-store"}})}async _handleWidgetResource(t,e){var a;const s=e.searchParams.get("file");this.log.info("Widget resource request:",s);try{const r=await fetch(`/store/static/${s}`);if(r.ok)return this.log.info("Serving widget resource from store:",s),r;(a=r.body)==null||a.cancel()}catch{}if(this.pendingFetches.has(s))return this.log.info("Deduplicating widget resource fetch:",s),(await this.pendingFetches.get(s)).clone();this.log.info("Fetching widget resource from CMS:",s);const n=(async()=>{try{const r=await fetch(t.request);if(r.ok){const c=s.split(".").pop().toLowerCase(),h={js:"application/javascript",css:"text/css",otf:"font/otf",ttf:"font/ttf",woff:"font/woff",woff2:"font/woff2",eot:"application/vnd.ms-fontobject",svg:"image/svg+xml"}[c]||r.headers.get("Content-Type")||"application/octet-stream",d=await r.clone().blob();return fetch(`/store/static/${s}`,{method:"PUT",headers:{"Content-Type":h},body:d}).then(p=>{var w;return(w=p.body)==null?void 0:w.cancel()}).catch(p=>this.log.warn("Failed to store widget resource:",s,p)),this.log.info("Stored widget resource:",s,`(${h})`),r}else return this.log.warn("Widget resource not available (",r.status,"):",s,"- NOT storing"),r}catch(r){return this.log.error("Failed to fetch widget resource:",s,r),new Response("Failed to fetch widget resource",{status:502,statusText:"Bad Gateway",headers:{"Content-Type":"text/plain"}})}})();this.pendingFetches.set(s,n);try{return(await n).clone()}finally{this.pendingFetches.delete(s)}}async _handleStaticResource(t){var s;const e=t.pathname.split("/").pop();this.log.info("Static resource request:",e);try{const n=await fetch(`/store/static/${e}`);if(n.ok)return this.log.info("Serving static resource from store:",e),new Response(n.body,{headers:{"Content-Type":n.headers.get("Content-Type")||"application/octet-stream","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000"}});(s=n.body)==null||s.cancel()}catch{}return this.log.warn("Static resource not stored:",e),new Response("Resource not stored",{status:404,headers:{"Content-Type":"text/plain","Cache-Control":"no-store"}})}async _handleWidgetHtml(t){this.log.info("Widget HTML request:",t.pathname);const s=`/store/${t.pathname.replace(C+"/cache/","").split("/").join("/")}`;try{const n=await fetch(s);if(n.ok)return new Response(n.body,{headers:{"Content-Type":"text/html; charset=utf-8","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000"}})}catch{}return new Response("<!DOCTYPE html><html><body>Widget not found</body></html>",{status:404,headers:{"Content-Type":"text/html","Cache-Control":"no-store"}})}}function P(o,t){const e=new Set,s=o.matchAll(/<media[^>]+fileId="(\d+)"/g);for(const r of s)e.add(r[1]);const n=o.matchAll(/<media\s+([^>]+)>/g);for(const r of n){const c=r[1];if(!c.includes("fileId=")){const h=c.match(/\bid="(\d+)"/);h&&e.add(h[1])}}const a=o.matchAll(/<layout[^>]+background="(\d+)"/g);for(const r of a)e.add(r[1]);return t&&t.debug(`extractMediaIdsFromXlf: found ${e.size} IDs: ${[...e].join(", ")} (XLF ${o.length} bytes)`),e}class K{constructor(t,e){this.downloadManager=t,this.config=e,this.log=new E("SW Message")}async handleMessage(t){const{type:e,data:s}=t.data;switch(e==="GET_DOWNLOAD_PROGRESS"?this.log.debug("Received:",e):this.log.info("Received:",e),e){case"PING":return this.log.info("PING received, broadcasting SW_READY"),(await self.clients.matchAll()).forEach(a=>{a.postMessage({type:"SW_READY"})}),{success:!0};case"DOWNLOAD_FILES":return await this.handleDownloadFiles(s);case"PRIORITIZE_DOWNLOAD":return this.handlePrioritizeDownload(s.fileType,s.fileId);case"CLEAR_CACHE":return{success:!0};case"GET_DOWNLOAD_PROGRESS":return await this.handleGetProgress();case"DELETE_FILES":return await this.handleDeleteFiles(s.files);case"PRIORITIZE_LAYOUT_FILES":return this.downloadManager.prioritizeLayoutFiles(s.mediaIds),{success:!0};case"URGENT_CHUNK":return this.handleUrgentChunk(s.fileType,s.fileId,s.chunkIndex);case"GET_ALL_FILES":return await this.handleGetAllFiles();default:return this.log.warn("Unknown message type:",e),{success:!1,error:"Unknown message type"}}}async handleDeleteFiles(t){if(!t||!Array.isArray(t))return{success:!1,error:"No files provided"};try{const s=await(await fetch("/store/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({files:t})})).json();return this.log.info(`Purge complete: ${s.deleted}/${s.total} files deleted`),{success:!0,deleted:s.deleted,total:s.total}}catch(e){return this.log.error("Delete failed:",e.message),{success:!1,error:e.message}}}handlePrioritizeDownload(t,e){this.log.info("Prioritize request:",`${t}/${e}`);const s=this.downloadManager.queue.prioritize(t,e);return this.downloadManager.queue.processQueue(),{success:!0,found:s}}handleUrgentChunk(t,e,s){return this.log.info("Urgent chunk request:",`${t}/${e}`,"chunk",s),{success:!0,acted:this.downloadManager.queue.urgentChunk(t,e,s)}}async handleDownloadFiles({layoutOrder:t,files:e,layoutDependants:s}){const n=this.downloadManager,a=n.queue;let r=0;const c=[],h=new Map,l=[],d=new Map;for(const i of e)i.type==="layout"?h.set(parseInt(i.id),i):i.type==="resource"||i.code==="fonts.css"||i.path&&(i.path.includes("bundle.min")||i.path.includes("fonts"))?l.push(i):(i.path&&i.path.includes("getData")&&(i.isGetData=!0),d.set(String(i.id),i));this.log.info(`Download: ${t.length} layouts, ${d.size} media, ${l.length} resources`);const p=new Map,w=[];for(const i of t){const u=h.get(i);u!=null&&u.path&&w.push((async()=>{const y=`layout/${i}`;let f;try{(await fetch(`/store/${y}`,{method:"HEAD"})).ok&&(f=await(await fetch(`/store/${y}`)).text())}catch{}if(!f){let g=O(u.path);g+=`&storeKey=${encodeURIComponent(y)}`;const M=await fetch(g);if(!M.ok){this.log.warn(`XLF fetch failed: ${i} (${M.status})`);return}f=await M.text(),this.log.info(`Fetched XLF ${i} (${f.length} bytes)`),(await self.clients.matchAll()).forEach(m=>m.postMessage({type:"FILE_CACHED",fileId:String(i),fileType:"layout",size:f.length}))}p.set(i,P(f,this.log))})())}for(const[i,u]of h)t.includes(i)||w.push((async()=>{const y=`layout/${i}`;let f;try{(await fetch(`/store/${y}`,{method:"HEAD"})).ok&&(f=await(await fetch(`/store/${y}`)).text())}catch{}if(!f&&u.path){let g=O(u.path);g+=`&storeKey=${encodeURIComponent(y)}`;const M=await fetch(g);M.ok&&(f=await M.text(),this.log.info(`Fetched XLF ${i} (non-scheduled, ${f.length} bytes)`),(await self.clients.matchAll()).forEach(m=>m.postMessage({type:"FILE_CACHED",fileId:String(i),fileType:"layout",size:f.length})))}f&&p.set(i,P(f,this.log))})());await Promise.allSettled(w),this.log.info(`Parsed ${p.size} XLFs`);const $=new q(a);for(const i of l)await this._enqueueFile(n,$,i,c)&&r++;const S=await $.build();S.length>0&&(S.push(x),a.enqueueOrderedTasks(S));const D=new Set,B=[...p.keys()].filter(i=>!t.includes(i)),F=new Map;for(const[i,u]of d)u.saveAs&&F.set(u.saveAs,i);const _=new Map;if(s)for(const[i,u]of Object.entries(s))_.set(parseInt(i,10),u);for(const i of t){const u=p.get(i);if(!u)continue;const y=new Set(u);for(const m of B){const T=p.get(m);if(T)for(const U of T)y.add(U)}const f=_.get(i)||[];for(const m of f){const T=F.get(m);T&&y.add(T)}const g=[];for(const m of y){if(D.has(m))continue;const T=d.get(m);T&&(g.push(T),D.add(m))}if(g.length===0)continue;this.log.info(`Layout ${i}: ${g.length} media`),g.sort((m,T)=>(m.size||0)-(T.size||0));const M=new q(a);for(const m of g)await this._enqueueFile(n,M,m,c)&&r++;const A=await M.build();A.length>0&&(A.push(x),a.enqueueOrderedTasks(A))}const b=[...d.keys()].filter(i=>!D.has(i));if(b.length>0){this.log.info(`${b.length} media not in any XLF: ${b.join(", ")}`);const i=new q(a);for(const y of b){const f=d.get(y);f&&await this._enqueueFile(n,i,f,c)&&r++}const u=await i.build();u.length>0&&a.enqueueOrderedTasks(u)}const I=a.running,L=a.queue.length;return this.log.info("Downloads active:",I,", queued:",L),{success:!0,enqueuedCount:r,activeCount:I,queuedCount:L}}async _enqueueFile(t,e,s,n){if(!s.path||s.path==="null"||s.path==="undefined")return this.log.debug("Skipping file with no path:",s.id),!1;const a=`${s.type}/${s.id}`;try{if((await fetch(`/store/${a}`,{method:"HEAD"})).ok)return this.log.debug("File already stored on disk:",a),!1}catch{}if(t.getTask(a))return this.log.debug("File already downloading:",a,"- skipping duplicate"),!1;const c=e.addFile(s);if(c.state==="pending"){const h=this._notifyAfterDownload(c,s);return n.push(h),!0}return!1}async _notifyAfterDownload(t,e){try{const s=await t.wait(),n=parseInt(e.size)||s.size,a=`${e.type}/${e.id}`;if(this.log.info("Download complete:",a,`(${H(n)})`),n>this.config.chunkSize)try{await fetch("/store/mark-complete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({storeKey:a})})}catch(c){this.log.warn("Failed to mark complete:",a,c.message)}return(await self.clients.matchAll()).forEach(c=>{c.postMessage({type:"FILE_CACHED",fileId:e.id,fileType:e.type,size:n})}),this.downloadManager.queue.removeCompleted(`${e.type}/${e.id}`),s}catch(s){throw this.log.error("Download failed:",e.id,s),this.downloadManager.queue.removeCompleted(`${e.type}/${e.id}`),s}}async handleGetAllFiles(){try{return{success:!0,files:(await(await fetch("/store/list")).json()).files||[]}}catch(t){return this.log.error("Failed to list files:",t.message),{success:!0,files:[]}}}async handleGetProgress(){return{success:!0,progress:this.downloadManager.getProgress()}}}const k="2026-02-26T23:56:26.484Z",R=new E("SW"),v=j(R),N=v.chunkSize,V=v.threshold,Y=v.concurrency;R.info("Loading modular Service Worker:",k);const W=new G({concurrency:Y,chunkSize:N,chunksPerFile:2}),Z=new X(W),J=new K(W,{chunkSize:N,chunkStorageThreshold:V});async function Q(o){const t=new URL(o.request.url),e=t.pathname.replace(C+"/ic",""),s=o.request.method;R.info("Interactive Control request:",s,e);let n=null;if(s==="POST"||s==="PUT")try{n=await o.request.text()}catch{}const a=await self.clients.matchAll({type:"window"});if(a.length===0)return new Response(JSON.stringify({error:"No active player"}),{status:503,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});const r=a[0];try{const c=await new Promise((h,l)=>{const d=new MessageChannel,p=setTimeout(()=>l(new Error("IC timeout")),5e3);d.port1.onmessage=w=>{clearTimeout(p),h(w.data)},r.postMessage({type:"INTERACTIVE_CONTROL",method:s,path:e,search:t.search,body:n},[d.port2])});return new Response(c.body||"",{status:c.status||200,headers:{"Content-Type":c.contentType||"application/json","Access-Control-Allow-Origin":"*"}})}catch(c){return R.error("IC handler error:",c),new Response(JSON.stringify({error:c.message}),{status:500,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}}self.addEventListener("install",o=>{R.info("Installing... Version:",k),o.waitUntil((async()=>{if(self.registration.active)try{const e=await(await caches.open("xibo-sw-version")).match("version");if(e){const s=await e.text();if(s===k){R.info("Same version already active, skipping activation to preserve streams");return}R.info("Version changed:",s,"→",k)}}catch{}return R.info("New version, activating immediately"),self.skipWaiting()})())});self.addEventListener("activate",o=>{R.info("Activating... Version:",k,"| @xiboplayer/cache:",z),o.waitUntil(caches.keys().then(t=>Promise.all(t.filter(e=>e.startsWith("xibo-")&&e!=="xibo-sw-version").map(e=>(R.info("Deleting legacy cache:",e),caches.delete(e))))).then(async()=>(await(await caches.open("xibo-sw-version")).put("version",new Response(k)),R.info("Taking control of all clients immediately"),self.clients.claim())).then(async()=>{R.info("Notifying all clients that fetch handler is ready"),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:"SW_READY"})})}))});self.addEventListener("fetch",o=>{const t=new URL(o.request.url);if(t.pathname.startsWith(C+"/cache/")||t.pathname.startsWith(C+"/ic/")||t.pathname.startsWith("/player/")&&(t.pathname.endsWith(".html")||t.pathname==="/player/")||t.pathname.includes("xmds.php")&&t.searchParams.has("file")&&o.request.method==="GET"){if(t.pathname.startsWith(C+"/ic/")){o.respondWith(Q(o));return}o.respondWith(Z.handleRequest(o))}});self.addEventListener("message",o=>{o.waitUntil(J.handleMessage(o).then(t=>{var e;(e=o.ports[0])==null||e.postMessage(t)}))});R.info("Modular Service Worker ready");
|
|
1
|
+
import{t as O,L as q,B as x,a as G}from"./assets/widget-html-DvXXB6at.js";import{VERSION as z}from"./assets/index-DBYNSQLp.js";import"./assets/cms-api-B3gEu-pj.js";const C=(()=>{var o;return typeof self<"u"&&((o=self.registration)!=null&&o.scope)?new URL(self.registration.scope).pathname.replace(/\/$/,""):"/player/pwa"})();function H(o,t=1){if(o===0)return"0 Bytes";if(o<1024)return`${o} Bytes`;const e=o/1024;if(e<1024)return`${e.toFixed(t)} KB`;const s=e/1024;return s<1024?`${s.toFixed(t)} MB`:`${(s/1024).toFixed(t)} GB`}class E{constructor(t){this.name=t,this.level=typeof self<"u"&&self.swLogLevel||"INFO"}debug(...t){this.level==="DEBUG"&&console.log(`[${this.name}] DEBUG:`,...t)}info(...t){(this.level==="DEBUG"||this.level==="INFO")&&console.log(`[${this.name}]`,...t)}warn(...t){console.warn(`[${this.name}]`,...t)}error(...t){console.error(`[${this.name}]`,...t)}}function j(o){o||(o=new E("ChunkConfig"));const t=typeof navigator<"u"&&navigator.deviceMemory||null;let e=4;if(t)e=t,o.info("Detected device memory:",t,"GB");else if(typeof navigator<"u"){const c=navigator.userAgent.toLowerCase();c.includes("raspberry pi")||c.includes("armv6")?(e=.5,o.info("Detected Pi Zero (512 MB RAM estimated)")):c.includes("armv7")?(e=1,o.info("Detected ARM device (1 GB RAM estimated)")):o.info("Using default RAM estimate:",e,"GB")}let s,n,a,r;return e<=.5?(s=10*1024*1024,n=25,a=25*1024*1024,r=1,o.info("Low-memory config: 10 MB chunks, 25 MB cache, 1 concurrent download")):e<=1?(s=20*1024*1024,n=50,a=50*1024*1024,r=2,o.info("1GB-RAM config: 20 MB chunks, 50 MB cache, 2 concurrent downloads")):e<=2?(s=30*1024*1024,n=100,a=75*1024*1024,r=2,o.info("2GB-RAM config: 30 MB chunks, 100 MB cache, 2 concurrent downloads")):e<=4?(s=50*1024*1024,n=200,a=100*1024*1024,r=4,o.info("4GB-RAM config: 50 MB chunks, 200 MB cache, 4 concurrent downloads")):(s=100*1024*1024,n=500,a=200*1024*1024,r=6,o.info("High-RAM config: 100 MB chunks, 500 MB cache, 6 concurrent downloads")),{chunkSize:s,blobCacheSize:n,threshold:a,concurrency:r}}class X{constructor(t){this.downloadManager=t,this.pendingFetches=new Map,this.log=new E("SW")}async handleRequest(t){var h;const e=new URL(t.request.url);if(this.log.info("handleRequest called for:",e.href),e.pathname===C+"/"||e.pathname===C+"/index.html"||e.pathname===C+"/setup.html")return this.log.info("Static page, passing to network:",e.pathname),fetch(t.request);if((e.pathname.includes("xmds.php")||e.pathname.includes("pwa/file"))&&(e.searchParams.get("fileType")==="bundle"||e.searchParams.get("fileType")==="fontCss"||e.searchParams.get("fileType")==="font"))return this._handleWidgetResource(t,e);if((e.pathname.includes("xmds.php")||e.pathname.includes("pwa/file"))&&e.searchParams.has("file")){const l=e.searchParams.get("file"),d=l.split(".")[0],p=e.searchParams.get("type"),w=p==="L"?"layout":"media";this.log.info("XMDS request:",l,"type:",p,"→ /store/"+w+"/"+d);const $=`/store/${w}/${d}`;try{const S=await fetch($);if(S.ok)return new Response(S.body,{headers:{"Content-Type":S.headers.get("Content-Type")||"video/mp4","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000","Accept-Ranges":"bytes"}});(h=S.body)==null||h.cancel()}catch{}return this.log.info("XMDS file not stored, passing through:",l),fetch(t.request)}if(e.pathname.startsWith(C+"/cache/static/"))return this._handleStaticResource(e);if(!e.pathname.startsWith(C+"/cache/"))return this.log.info("NOT a cache request, returning null:",e.pathname),null;if(this.log.info("Cache request:",e.pathname),e.pathname.startsWith(C+"/cache/widget/"))return this._handleWidgetHtml(e);const s=e.pathname.replace(/\.json$/,""),n=t.request.method,a=t.request.headers.get("Range");a?this.log.info(n,s,`Range: ${a}`):this.log.info(n,s);const c=`/store/${s.replace(C+"/cache/","").split("/").join("/")}`;try{const l={method:n};a&&(l.headers={Range:a});const d=await fetch(c,l);return d.ok||d.status===206?d:d.status===404?this._handleNotStored(s,t,n,a):d}catch(l){return this.log.error("Proxy fetch error:",l.message),this._handleNotStored(s,t,n,a)}}async _handleNotStored(t,e,s,n){const a=t.split("/"),r=a[a.length-2],c=a[a.length-1];let h=null;for(const[,l]of this.downloadManager.queue.active.entries())if(l.fileInfo.type===r&&String(l.fileInfo.id)===c){h=l;break}if(h){this.log.info("Download in progress, waiting:",t);try{await h.wait();const d=`/store/${t.replace(C+"/cache/","").split("/").join("/")}`,p={method:s};n&&(p.headers={Range:n});const w=await fetch(d,p);if(w.ok||w.status===206)return this.log.info("Download complete, serving from store:",t),w}catch(l){return this.log.error("Download failed:",t,l),new Response("Download failed: "+l.message,{status:500})}}return this.log.info("Not found:",t),new Response("Not found",{status:404,headers:{"Cache-Control":"no-store"}})}async _handleWidgetResource(t,e){var a;const s=e.searchParams.get("file");this.log.info("Widget resource request:",s);try{const r=await fetch(`/store/static/${s}`);if(r.ok)return this.log.info("Serving widget resource from store:",s),r;(a=r.body)==null||a.cancel()}catch{}if(this.pendingFetches.has(s))return this.log.info("Deduplicating widget resource fetch:",s),(await this.pendingFetches.get(s)).clone();this.log.info("Fetching widget resource from CMS:",s);const n=(async()=>{try{const r=await fetch(t.request);if(r.ok){const c=s.split(".").pop().toLowerCase(),h={js:"application/javascript",css:"text/css",otf:"font/otf",ttf:"font/ttf",woff:"font/woff",woff2:"font/woff2",eot:"application/vnd.ms-fontobject",svg:"image/svg+xml"}[c]||r.headers.get("Content-Type")||"application/octet-stream",d=await r.clone().blob();return fetch(`/store/static/${s}`,{method:"PUT",headers:{"Content-Type":h},body:d}).then(p=>{var w;return(w=p.body)==null?void 0:w.cancel()}).catch(p=>this.log.warn("Failed to store widget resource:",s,p)),this.log.info("Stored widget resource:",s,`(${h})`),r}else return this.log.warn("Widget resource not available (",r.status,"):",s,"- NOT storing"),r}catch(r){return this.log.error("Failed to fetch widget resource:",s,r),new Response("Failed to fetch widget resource",{status:502,statusText:"Bad Gateway",headers:{"Content-Type":"text/plain"}})}})();this.pendingFetches.set(s,n);try{return(await n).clone()}finally{this.pendingFetches.delete(s)}}async _handleStaticResource(t){var s;const e=t.pathname.split("/").pop();this.log.info("Static resource request:",e);try{const n=await fetch(`/store/static/${e}`);if(n.ok)return this.log.info("Serving static resource from store:",e),new Response(n.body,{headers:{"Content-Type":n.headers.get("Content-Type")||"application/octet-stream","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000"}});(s=n.body)==null||s.cancel()}catch{}return this.log.warn("Static resource not stored:",e),new Response("Resource not stored",{status:404,headers:{"Content-Type":"text/plain","Cache-Control":"no-store"}})}async _handleWidgetHtml(t){this.log.info("Widget HTML request:",t.pathname);const s=`/store/${t.pathname.replace(C+"/cache/","").split("/").join("/")}`;try{const n=await fetch(s);if(n.ok)return new Response(n.body,{headers:{"Content-Type":"text/html; charset=utf-8","Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=31536000"}})}catch{}return new Response("<!DOCTYPE html><html><body>Widget not found</body></html>",{status:404,headers:{"Content-Type":"text/html","Cache-Control":"no-store"}})}}function P(o,t){const e=new Set,s=o.matchAll(/<media[^>]+fileId="(\d+)"/g);for(const r of s)e.add(r[1]);const n=o.matchAll(/<media\s+([^>]+)>/g);for(const r of n){const c=r[1];if(!c.includes("fileId=")){const h=c.match(/\bid="(\d+)"/);h&&e.add(h[1])}}const a=o.matchAll(/<layout[^>]+background="(\d+)"/g);for(const r of a)e.add(r[1]);return t&&t.debug(`extractMediaIdsFromXlf: found ${e.size} IDs: ${[...e].join(", ")} (XLF ${o.length} bytes)`),e}class K{constructor(t,e){this.downloadManager=t,this.config=e,this.log=new E("SW Message")}async handleMessage(t){const{type:e,data:s}=t.data;switch(e==="GET_DOWNLOAD_PROGRESS"?this.log.debug("Received:",e):this.log.info("Received:",e),e){case"PING":return this.log.info("PING received, broadcasting SW_READY"),(await self.clients.matchAll()).forEach(a=>{a.postMessage({type:"SW_READY"})}),{success:!0};case"DOWNLOAD_FILES":return await this.handleDownloadFiles(s);case"PRIORITIZE_DOWNLOAD":return this.handlePrioritizeDownload(s.fileType,s.fileId);case"CLEAR_CACHE":return{success:!0};case"GET_DOWNLOAD_PROGRESS":return await this.handleGetProgress();case"DELETE_FILES":return await this.handleDeleteFiles(s.files);case"PRIORITIZE_LAYOUT_FILES":return this.downloadManager.prioritizeLayoutFiles(s.mediaIds),{success:!0};case"URGENT_CHUNK":return this.handleUrgentChunk(s.fileType,s.fileId,s.chunkIndex);case"GET_ALL_FILES":return await this.handleGetAllFiles();default:return this.log.warn("Unknown message type:",e),{success:!1,error:"Unknown message type"}}}async handleDeleteFiles(t){if(!t||!Array.isArray(t))return{success:!1,error:"No files provided"};try{const s=await(await fetch("/store/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({files:t})})).json();return this.log.info(`Purge complete: ${s.deleted}/${s.total} files deleted`),{success:!0,deleted:s.deleted,total:s.total}}catch(e){return this.log.error("Delete failed:",e.message),{success:!1,error:e.message}}}handlePrioritizeDownload(t,e){this.log.info("Prioritize request:",`${t}/${e}`);const s=this.downloadManager.queue.prioritize(t,e);return this.downloadManager.queue.processQueue(),{success:!0,found:s}}handleUrgentChunk(t,e,s){return this.log.info("Urgent chunk request:",`${t}/${e}`,"chunk",s),{success:!0,acted:this.downloadManager.queue.urgentChunk(t,e,s)}}async handleDownloadFiles({layoutOrder:t,files:e,layoutDependants:s}){const n=this.downloadManager,a=n.queue;let r=0;const c=[],h=new Map,l=[],d=new Map;for(const i of e)i.type==="layout"?h.set(parseInt(i.id),i):i.type==="resource"||i.code==="fonts.css"||i.path&&(i.path.includes("bundle.min")||i.path.includes("fonts"))?l.push(i):(i.path&&i.path.includes("getData")&&(i.isGetData=!0),d.set(String(i.id),i));this.log.info(`Download: ${t.length} layouts, ${d.size} media, ${l.length} resources`);const p=new Map,w=[];for(const i of t){const u=h.get(i);u!=null&&u.path&&w.push((async()=>{const y=`layout/${i}`;let f;try{(await fetch(`/store/${y}`,{method:"HEAD"})).ok&&(f=await(await fetch(`/store/${y}`)).text())}catch{}if(!f){let g=O(u.path);g+=`&storeKey=${encodeURIComponent(y)}`;const M=await fetch(g);if(!M.ok){this.log.warn(`XLF fetch failed: ${i} (${M.status})`);return}f=await M.text(),this.log.info(`Fetched XLF ${i} (${f.length} bytes)`),(await self.clients.matchAll()).forEach(m=>m.postMessage({type:"FILE_CACHED",fileId:String(i),fileType:"layout",size:f.length}))}p.set(i,P(f,this.log))})())}for(const[i,u]of h)t.includes(i)||w.push((async()=>{const y=`layout/${i}`;let f;try{(await fetch(`/store/${y}`,{method:"HEAD"})).ok&&(f=await(await fetch(`/store/${y}`)).text())}catch{}if(!f&&u.path){let g=O(u.path);g+=`&storeKey=${encodeURIComponent(y)}`;const M=await fetch(g);M.ok&&(f=await M.text(),this.log.info(`Fetched XLF ${i} (non-scheduled, ${f.length} bytes)`),(await self.clients.matchAll()).forEach(m=>m.postMessage({type:"FILE_CACHED",fileId:String(i),fileType:"layout",size:f.length})))}f&&p.set(i,P(f,this.log))})());await Promise.allSettled(w),this.log.info(`Parsed ${p.size} XLFs`);const $=new q(a);for(const i of l)await this._enqueueFile(n,$,i,c)&&r++;const S=await $.build();S.length>0&&(S.push(x),a.enqueueOrderedTasks(S));const D=new Set,B=[...p.keys()].filter(i=>!t.includes(i)),F=new Map;for(const[i,u]of d)u.saveAs&&F.set(u.saveAs,i);const _=new Map;if(s)for(const[i,u]of Object.entries(s))_.set(parseInt(i,10),u);for(const i of t){const u=p.get(i);if(!u)continue;const y=new Set(u);for(const m of B){const T=p.get(m);if(T)for(const U of T)y.add(U)}const f=_.get(i)||[];for(const m of f){const T=F.get(m);T&&y.add(T)}const g=[];for(const m of y){if(D.has(m))continue;const T=d.get(m);T&&(g.push(T),D.add(m))}if(g.length===0)continue;this.log.info(`Layout ${i}: ${g.length} media`),g.sort((m,T)=>(m.size||0)-(T.size||0));const M=new q(a);for(const m of g)await this._enqueueFile(n,M,m,c)&&r++;const A=await M.build();A.length>0&&(A.push(x),a.enqueueOrderedTasks(A))}const b=[...d.keys()].filter(i=>!D.has(i));if(b.length>0){this.log.info(`${b.length} media not in any XLF: ${b.join(", ")}`);const i=new q(a);for(const y of b){const f=d.get(y);f&&await this._enqueueFile(n,i,f,c)&&r++}const u=await i.build();u.length>0&&a.enqueueOrderedTasks(u)}const I=a.running,L=a.queue.length;return this.log.info("Downloads active:",I,", queued:",L),{success:!0,enqueuedCount:r,activeCount:I,queuedCount:L}}async _enqueueFile(t,e,s,n){if(!s.path||s.path==="null"||s.path==="undefined")return this.log.debug("Skipping file with no path:",s.id),!1;const a=`${s.type}/${s.id}`;try{if((await fetch(`/store/${a}`,{method:"HEAD"})).ok)return this.log.debug("File already stored on disk:",a),!1}catch{}if(t.getTask(a))return this.log.debug("File already downloading:",a,"- skipping duplicate"),!1;const c=e.addFile(s);if(c.state==="pending"){const h=this._notifyAfterDownload(c,s);return n.push(h),!0}return!1}async _notifyAfterDownload(t,e){try{const s=await t.wait(),n=parseInt(e.size)||s.size,a=`${e.type}/${e.id}`;if(this.log.info("Download complete:",a,`(${H(n)})`),n>this.config.chunkSize)try{await fetch("/store/mark-complete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({storeKey:a})})}catch(c){this.log.warn("Failed to mark complete:",a,c.message)}return(await self.clients.matchAll()).forEach(c=>{c.postMessage({type:"FILE_CACHED",fileId:e.id,fileType:e.type,size:n})}),this.downloadManager.queue.removeCompleted(`${e.type}/${e.id}`),s}catch(s){throw this.log.error("Download failed:",e.id,s),this.downloadManager.queue.removeCompleted(`${e.type}/${e.id}`),s}}async handleGetAllFiles(){try{return{success:!0,files:(await(await fetch("/store/list")).json()).files||[]}}catch(t){return this.log.error("Failed to list files:",t.message),{success:!0,files:[]}}}async handleGetProgress(){return{success:!0,progress:this.downloadManager.getProgress()}}}const k="2026-02-27T12:48:50.017Z",R=new E("SW"),v=j(R),N=v.chunkSize,V=v.threshold,Y=v.concurrency;R.info("Loading modular Service Worker:",k);const W=new G({concurrency:Y,chunkSize:N,chunksPerFile:2}),Z=new X(W),J=new K(W,{chunkSize:N,chunkStorageThreshold:V});async function Q(o){const t=new URL(o.request.url),e=t.pathname.replace(C+"/ic",""),s=o.request.method;R.info("Interactive Control request:",s,e);let n=null;if(s==="POST"||s==="PUT")try{n=await o.request.text()}catch{}const a=await self.clients.matchAll({type:"window"});if(a.length===0)return new Response(JSON.stringify({error:"No active player"}),{status:503,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});const r=a[0];try{const c=await new Promise((h,l)=>{const d=new MessageChannel,p=setTimeout(()=>l(new Error("IC timeout")),5e3);d.port1.onmessage=w=>{clearTimeout(p),h(w.data)},r.postMessage({type:"INTERACTIVE_CONTROL",method:s,path:e,search:t.search,body:n},[d.port2])});return new Response(c.body||"",{status:c.status||200,headers:{"Content-Type":c.contentType||"application/json","Access-Control-Allow-Origin":"*"}})}catch(c){return R.error("IC handler error:",c),new Response(JSON.stringify({error:c.message}),{status:500,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}}self.addEventListener("install",o=>{R.info("Installing... Version:",k),o.waitUntil((async()=>{if(self.registration.active)try{const e=await(await caches.open("xibo-sw-version")).match("version");if(e){const s=await e.text();if(s===k){R.info("Same version already active, skipping activation to preserve streams");return}R.info("Version changed:",s,"→",k)}}catch{}return R.info("New version, activating immediately"),self.skipWaiting()})())});self.addEventListener("activate",o=>{R.info("Activating... Version:",k,"| @xiboplayer/cache:",z),o.waitUntil(caches.keys().then(t=>Promise.all(t.filter(e=>e.startsWith("xibo-")&&e!=="xibo-sw-version").map(e=>(R.info("Deleting legacy cache:",e),caches.delete(e))))).then(async()=>(await(await caches.open("xibo-sw-version")).put("version",new Response(k)),R.info("Taking control of all clients immediately"),self.clients.claim())).then(async()=>{R.info("Notifying all clients that fetch handler is ready"),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:"SW_READY"})})}))});self.addEventListener("fetch",o=>{const t=new URL(o.request.url);if(t.pathname.startsWith(C+"/cache/")||t.pathname.startsWith(C+"/ic/")||t.pathname.startsWith("/player/")&&(t.pathname.endsWith(".html")||t.pathname==="/player/")||t.pathname.includes("xmds.php")&&t.searchParams.has("file")&&o.request.method==="GET"){if(t.pathname.startsWith(C+"/ic/")){o.respondWith(Q(o));return}o.respondWith(Z.handleRequest(o))}});self.addEventListener("message",o=>{o.waitUntil(J.handleMessage(o).then(t=>{var e;(e=o.ports[0])==null||e.postMessage(t)}))});R.info("Modular Service Worker ready");
|
|
2
2
|
//# sourceMappingURL=sw-pwa.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/pwa",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
4
4
|
"description": "Lightweight PWA Xibo Player with RendererLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
"html2canvas": "^1.4.1",
|
|
11
11
|
"nanoevents": "^9.0.0",
|
|
12
12
|
"xml2js": "^0.6.2",
|
|
13
|
-
"@xiboplayer/cache": "0.5.
|
|
14
|
-
"@xiboplayer/
|
|
15
|
-
"@xiboplayer/
|
|
16
|
-
"@xiboplayer/schedule": "0.5.
|
|
17
|
-
"@xiboplayer/
|
|
18
|
-
"@xiboplayer/
|
|
19
|
-
"@xiboplayer/
|
|
20
|
-
"@xiboplayer/
|
|
21
|
-
"@xiboplayer/
|
|
22
|
-
"@xiboplayer/
|
|
23
|
-
"@xiboplayer/
|
|
24
|
-
"@xiboplayer/
|
|
13
|
+
"@xiboplayer/cache": "0.5.14",
|
|
14
|
+
"@xiboplayer/crypto": "0.5.14",
|
|
15
|
+
"@xiboplayer/core": "0.5.14",
|
|
16
|
+
"@xiboplayer/schedule": "0.5.14",
|
|
17
|
+
"@xiboplayer/renderer": "0.5.14",
|
|
18
|
+
"@xiboplayer/settings": "0.5.14",
|
|
19
|
+
"@xiboplayer/sw": "0.5.14",
|
|
20
|
+
"@xiboplayer/stats": "0.5.14",
|
|
21
|
+
"@xiboplayer/utils": "0.5.14",
|
|
22
|
+
"@xiboplayer/xmds": "0.5.14",
|
|
23
|
+
"@xiboplayer/xmr": "0.5.14",
|
|
24
|
+
"@xiboplayer/sync": "0.5.14"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.10.5",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{L as p,a as m,R as L}from"./main-BJ4a67Bs.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./cms-api-B3gEu-pj.js";import"./widget-html-DvXXB6at.js";const o="0.5.13",r={version:o},a=r.version;export{p as LayoutPool,m as LayoutTranslator,L as RendererLite,a as VERSION};
|
|
2
|
-
//# sourceMappingURL=index-BzTeZiFu.js.map
|