@xiboplayer/pwa 0.7.21 → 0.7.23
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/__vite-browser-external-DeMPM02e.js +2 -0
- package/dist/assets/__vite-browser-external-DeMPM02e.js.map +1 -0
- package/dist/assets/chunk-DzMEjpoC.js +1 -0
- package/dist/assets/{html2canvas-BAfZNSwU.js → html2canvas-EikzC5d8.js} +2 -2
- package/dist/assets/{html2canvas-BAfZNSwU.js.map → html2canvas-EikzC5d8.js.map} +1 -1
- package/dist/assets/main-CRdq5ifQ.js +3 -0
- package/dist/assets/{main-vwJkNw4Y.js.map → main-CRdq5ifQ.js.map} +1 -1
- package/dist/assets/main-DTR2QDcF.js +108 -0
- package/dist/assets/main-DTR2QDcF.js.map +1 -0
- package/dist/assets/{pdf-Bxz9Nzto.js → pdf-CMz6puSt.js} +1 -1
- package/dist/assets/{pdf-Bxz9Nzto.js.map → pdf-CMz6puSt.js.map} +1 -1
- package/dist/assets/{setup-B4gZX38p.js → setup-Bw8T9Qq6.js} +2 -2
- package/dist/assets/{setup-B4gZX38p.js.map → setup-Bw8T9Qq6.js.map} +1 -1
- package/dist/assets/src-A5KHvitf.js +2 -0
- package/dist/assets/{src-CROvYSP8.js.map → src-A5KHvitf.js.map} +1 -1
- package/dist/assets/{src-DAB0dqGG.js → src-BHsN2u2P.js} +2 -2
- package/dist/assets/{src-DAB0dqGG.js.map → src-BHsN2u2P.js.map} +1 -1
- package/dist/assets/src-BLUMUwZR.js +1 -0
- package/dist/assets/src-BXXcWcHh.js +1 -0
- package/dist/assets/src-BxSOopk7.js +1 -0
- package/dist/assets/{src-WDu491CE.js → src-BxaX1gGg.js} +2 -2
- package/dist/assets/{src-WDu491CE.js.map → src-BxaX1gGg.js.map} +1 -1
- package/dist/assets/{src-BtVLiVYZ.js → src-CCAyzQUp.js} +3 -3
- package/dist/assets/{src-BtVLiVYZ.js.map → src-CCAyzQUp.js.map} +1 -1
- package/dist/assets/src-CWJcD3kA.js +1 -0
- package/dist/assets/{src-Cx3tXAAu.js → src-CZ1k5h23.js} +3 -3
- package/dist/assets/{src-Cx3tXAAu.js.map → src-CZ1k5h23.js.map} +1 -1
- package/dist/assets/src-ClrziKzV.js +16 -0
- package/dist/assets/src-ClrziKzV.js.map +1 -0
- package/dist/assets/{src-C_Lx4lXp.js → src-CtjjclS4.js} +2 -2
- package/dist/assets/{src-C_Lx4lXp.js.map → src-CtjjclS4.js.map} +1 -1
- package/dist/assets/src-CuVaZcMo.js +2 -0
- package/dist/assets/{src-B_BNICay.js.map → src-CuVaZcMo.js.map} +1 -1
- package/dist/assets/src-Cy5OUviT.js +1 -0
- package/dist/assets/src-DK5BYonP.js +630 -0
- package/dist/assets/src-DK5BYonP.js.map +1 -0
- package/dist/assets/src-Dk-W3N33.js +1 -0
- package/dist/assets/{src-cUopH0nN.js → src-xPTO7Ts6.js} +3 -3
- package/dist/assets/{src-cUopH0nN.js.map → src-xPTO7Ts6.js.map} +1 -1
- package/dist/assets/sync-manager-zf1tikPt.js +2 -0
- package/dist/assets/sync-manager-zf1tikPt.js.map +1 -0
- package/dist/index.html +1 -1
- package/dist/setup.html +3 -4
- package/dist/sw-pwa.js +2 -2
- package/dist/sw-pwa.js.map +1 -1
- package/package.json +15 -13
- package/dist/assets/chunk-7ZXdHUL4.js +0 -1
- package/dist/assets/main-oacre7st.js +0 -108
- package/dist/assets/main-oacre7st.js.map +0 -1
- package/dist/assets/main-vwJkNw4Y.js +0 -3
- package/dist/assets/src-B_BNICay.js +0 -2
- package/dist/assets/src-Bjt9ooXK.js +0 -16
- package/dist/assets/src-Bjt9ooXK.js.map +0 -1
- package/dist/assets/src-CKpVxGpH.js +0 -629
- package/dist/assets/src-CKpVxGpH.js.map +0 -1
- package/dist/assets/src-CROvYSP8.js +0 -2
- package/dist/assets/sync-manager-8Z-qwkod.js +0 -2
- package/dist/assets/sync-manager-8Z-qwkod.js.map +0 -1
package/dist/sw-pwa.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var e={name:`@xiboplayer/utils`,version:`0.7.
|
|
2
|
-
`)}\n-----END ${t}-----`}async function c(){let e=await crypto.subtle.generateKey({name:`RSA-OAEP`,modulusLength:1024,publicExponent:new Uint8Array([1,0,1]),hash:`SHA-256`},!0,[`encrypt`,`decrypt`]),t=await crypto.subtle.exportKey(`spki`,e.publicKey),n=await crypto.subtle.exportKey(`pkcs8`,e.privateKey);return{publicKeyPem:s(t,`PUBLIC KEY`),privateKeyPem:s(n,`PRIVATE KEY`)}}function ee(e){return!e||typeof e!=`string`?!1:/^-----BEGIN (PUBLIC KEY|PRIVATE KEY)-----\n[A-Za-z0-9+/=\n]+\n-----END \1-----$/.test(e.trim())}function l(e,t,n,r={}){return typeof indexedDB>`u`?Promise.reject(Error(`IndexedDB not available`)):new Promise((i,a)=>{let o=indexedDB.open(e,t);o.onupgradeneeded=()=>{let e=o.result;if(!e.objectStoreNames.contains(n)){let t=r.keyPath?{keyPath:r.keyPath,autoIncrement:!0}:void 0,i=e.createObjectStore(n,t);r.indexName&&r.indexKey&&i.createIndex(r.indexName,r.indexKey,{unique:!1})}},o.onsuccess=()=>i(o.result),o.onerror=()=>a(o.error)})}var u=a(`Config`),d=`xibo_global`,f=`xibo_cms:`,p=`xibo_active_cms`,m=`xibo-hw-backup`,h=1,g=new Set([`hardwareKey`,`xmrPubKey`,`xmrPrivKey`]);function _(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);t>>>=0;let n=t+1234567;for(let t=0;t<e.length;t++)n^=e.charCodeAt(t)+1,n+=(n<<1)+(n<<4)+(n<<7)+(n<<8)+(n<<24);return n>>>=0,(t.toString(16).padStart(8,`0`)+n.toString(16).padStart(8,`0`)).substring(0,12)}function v(e){if(!e)return null;try{let t=new URL(e),n=t.origin;return`${t.hostname}-${_(n)}`}catch{return`unknown-${_(e)}`}}function y(){let e={},t={cmsUrl:e.CMS_URL||``,cmsKey:e.CMS_KEY||``,displayName:e.DISPLAY_NAME||``,hardwareKey:e.HARDWARE_KEY||``,xmrChannel:e.XMR_CHANNEL||``,googleGeoApiKey:e.GOOGLE_GEO_API_KEY||``};return Object.values(t).some(e=>e!==``)?t:null}var b=new class{constructor(){this._activeCmsId=null,this.data=this.load(),this._fromEnv||this._restoreHardwareKeyFromBackup()}load(){let e=y();return e?(this._fromEnv=!0,e):typeof localStorage>`u`?{cmsUrl:``,cmsKey:``,displayName:``,hardwareKey:``,xmrChannel:``}:localStorage.getItem(d)?this._loadSplit():this._loadFresh()}_loadSplit(){let e={};try{e=JSON.parse(localStorage.getItem(d)||`{}`)}catch(e){u.error(`Failed to parse xibo_global:`,e)}let t=localStorage.getItem(p)||null;this._activeCmsId=t;let n={};if(t)try{let e=localStorage.getItem(f+t);e&&(n=JSON.parse(e))}catch(e){u.error(`Failed to parse CMS config:`,e)}let r={...e,...n};return this._validateConfig(r)}_loadFresh(){return this._validateConfig({})}_validateConfig(e){let t=!1;return!e.hardwareKey||e.hardwareKey.length<10?(u.warn(`Missing/invalid hardwareKey — generating`),e.hardwareKey=this.generateStableHardwareKey(),this._backupHardwareKey(e.hardwareKey),t=!0):u.info(`✓ Loaded existing hardwareKey:`,e.hardwareKey),e.xmrChannel||(u.warn(`Missing xmrChannel — generating`),e.xmrChannel=this.generateXmrChannel(),t=!0),e.cmsUrl=e.cmsUrl||``,e.cmsKey=e.cmsKey||``,e.displayName=e.displayName||``,t&&typeof localStorage<`u`&&this._saveSplit(e),e}save(){typeof localStorage>`u`||this._saveSplit(this.data)}_saveSplit(e){if(typeof localStorage>`u`)return;let t={},n={};for(let[r,i]of Object.entries(e))g.has(r)?t[r]=i:n[r]=i;localStorage.setItem(d,JSON.stringify(t));let r=v(e.cmsUrl);r&&(localStorage.setItem(f+r,JSON.stringify(n)),localStorage.setItem(p,r),this._activeCmsId=r),localStorage.setItem(`xibo_config`,JSON.stringify(e))}switchCms(e){if(typeof localStorage>`u`)throw Error(`switchCms requires localStorage (browser only)`);this.save();let t=v(e);if(!t)throw Error(`Invalid CMS URL`);let n=localStorage.getItem(f+t),r={},i=!0;if(n)try{r=JSON.parse(n),i=!1,u.info(`Switching to existing CMS profile: ${t}`)}catch(e){u.error(`Failed to parse target CMS config:`,e)}else u.info(`Creating new CMS profile: ${t}`),r={cmsUrl:e,cmsKey:``,displayName:``,xmrChannel:this.generateXmrChannel()},localStorage.setItem(f+t,JSON.stringify(r));localStorage.setItem(p,t),this._activeCmsId=t;let a={};try{a=JSON.parse(localStorage.getItem(d)||`{}`)}catch{}return this.data={...a,...r},this.data.cmsUrl||(this.data.cmsUrl=e),{cmsId:t,isNew:i}}listCmsProfiles(){if(typeof localStorage>`u`)return[];let e=[],t=localStorage.getItem(p)||null;for(let n=0;n<localStorage.length;n++){let r=localStorage.key(n);if(!r.startsWith(f))continue;let i=r.slice(9);try{let n=JSON.parse(localStorage.getItem(r));e.push({cmsId:i,cmsUrl:n.cmsUrl||``,displayName:n.displayName||``,isActive:i===t})}catch{}}return e}get activeCmsId(){if(this._activeCmsId)return this._activeCmsId;let e=v(this.data?.cmsUrl);return this._activeCmsId=e,e}isConfigured(){return!!(this.data.cmsUrl&&this.data.cmsKey&&this.data.displayName)}async _backupKeys(e){try{let t=await l(m,h,`keys`),n=t.transaction(`keys`,`readwrite`),r=n.objectStore(`keys`);for(let[t,n]of Object.entries(e))r.put(n,t);n.oncomplete=()=>{u.info(`Keys backed up to IndexedDB:`,Object.keys(e).join(`, `)),t.close()}}catch{}}_backupHardwareKey(e){this._backupKeys({hardwareKey:e})}async _restoreHardwareKeyFromBackup(){try{let e=await l(m,h,`keys`),t=e.transaction(`keys`,`readonly`).objectStore(`keys`),n=await new Promise(e=>{let n=t.get(`hardwareKey`);n.onsuccess=()=>e(n.result),n.onerror=()=>e(null)});e.close(),n&&n!==this.data.hardwareKey?(u.info(`Restoring hardware key from IndexedDB backup:`,n),u.info(`(was:`,this.data.hardwareKey,`)`),this.data.hardwareKey=n,this.save()):!n&&this.data.hardwareKey&&this._backupHardwareKey(this.data.hardwareKey)}catch{}}generateStableHardwareKey(){if(typeof crypto<`u`&&crypto.randomUUID){let e=`pwa-`+crypto.randomUUID().replace(/-/g,``).substring(0,28);return u.info(`Generated new UUID-based hardware key:`,e),e}let e=`pwa-`+Array.from({length:28},()=>Math.floor(Math.random()*16).toString(16)).join(``);return u.info(`Generated new random hardware key:`,e),e}generateXmrChannel(){return`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}async ensureXmrKeyPair(){if(this.data.xmrPubKey&&ee(this.data.xmrPubKey))return;u.info(`Generating RSA key pair for XMR registration...`);let{publicKeyPem:e,privateKeyPem:t}=await c();this.data.xmrPubKey=e,this.data.xmrPrivKey=t,this.save(),typeof indexedDB<`u`&&this._backupKeys({xmrPubKey:e,xmrPrivKey:t}),u.info(`RSA key pair generated and saved`)}get cmsUrl(){return this.data.cmsUrl}set cmsUrl(e){this.data.cmsUrl=e,this.save()}get cmsKey(){return this.data.cmsKey}set cmsKey(e){this.data.cmsKey=e,this.save()}get displayName(){return this.data.displayName}set displayName(e){this.data.displayName=e,this.save()}get hardwareKey(){return this.data.hardwareKey||(u.error(`CRITICAL: hardwareKey missing! Generating emergency key.`),this.data.hardwareKey=this.generateStableHardwareKey(),this.save()),this.data.hardwareKey}get xmrChannel(){return this.data.xmrChannel||(u.warn(`xmrChannel missing at access time — generating`),this.data.xmrChannel=this.generateXmrChannel(),this.save()),this.data.xmrChannel}get xmrPubKey(){return this.data.xmrPubKey||``}get xmrPrivKey(){return this.data.xmrPrivKey||``}get googleGeoApiKey(){return this.data.googleGeoApiKey||``}set googleGeoApiKey(e){this.data.googleGeoApiKey=e,this.save()}get controls(){return this.data.controls||{}}get transport(){return this.data.transport||`auto`}get debug(){return this.data.debug||{}}};a(`FetchRetry`),a(`CmsApi`),e.version;var x=b.data?.playerApiBase||`/player/api/v2`,S={media:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0},layout:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0},dataset:{maxRetries:4,retryDelayMs:0,retryDelays:[15e3,3e4,6e4,12e4],maxReenqueues:5,reenqueueDelayMs:6e4,skipHead:!0,cacheTtl:300},static:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0}};function C(e){return S[e]||S.media}var w=a(`Download`),te=6,T=50*1024*1024,E=3,D=100*1024*1024,O=2,k=6e5,A=15e3;function j(e){return{mp4:`video/mp4`,webm:`video/webm`,mp3:`audio/mpeg`,png:`image/png`,jpg:`image/jpeg`,jpeg:`image/jpeg`,gif:`image/gif`,svg:`image/svg+xml`,webp:`image/webp`,css:`text/css`,js:`application/javascript`,ttf:`font/ttf`,otf:`font/otf`,woff:`font/woff`,woff2:`font/woff2`,xml:`application/xml`,xlf:`application/xml`}[(e.path||e.code||``).split(`.`).pop()?.split(`?`)[0]?.toLowerCase()]||`application/octet-stream`}var M={normal:0,layout:1,high:2,urgent:3},N=Symbol(`BARRIER`);function P(e){try{let t=new URL(e,`http://localhost`),n=t.searchParams.get(`X-Amz-Date`),r=t.searchParams.get(`X-Amz-Expires`);if(n&&r){let e=n.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/,`$1-$2-$3T$4:$5:$6Z`),t=new Date(e).getTime()/1e3;return isNaN(t)?1/0:t+parseInt(r,10)}return 1/0}catch{return 1/0}}function F(e,t=30){let n=P(e);return n===1/0?!1:Date.now()/1e3>=n-t}var I=class{constructor(e,t={}){this.fileInfo=e,this.chunkIndex=t.chunkIndex??null,this.rangeStart=t.rangeStart??null,this.rangeEnd=t.rangeEnd??null,this.state=`pending`,this.blob=null,this._parentFile=null,this._priority=M.normal,this._typeConfig=C(e.type)}getUrl(){let e=this.fileInfo.path;if(F(e))throw Error(`URL expired for ${this.fileInfo.type}/${this.fileInfo.id} — waiting for fresh URL from next collection cycle`);return e}async start(){this.state=`downloading`;let e={};this.rangeStart!=null&&(e.Range=`bytes=${this.rangeStart}-${this.rangeEnd}`),this.chunkIndex!=null&&(e[`X-Store-Chunk-Index`]=String(this.chunkIndex),this._parentFile&&(e[`X-Store-Num-Chunks`]=String(this._parentFile.totalChunks),e[`X-Store-Chunk-Size`]=String(this._parentFile.options.chunkSize||104857600))),this.fileInfo.md5&&(e[`X-Store-MD5`]=this.fileInfo.md5),this.fileInfo.updateInterval&&(e[`X-Cache-TTL`]=String(this.fileInfo.updateInterval)),this.fileInfo.cmsDownloadUrl&&(e[`X-Cms-Download-Url`]=this.fileInfo.cmsDownloadUrl);let t=this._typeConfig.maxRetries;for(let n=1;n<=t;n++){let r=new AbortController,i=setTimeout(()=>r.abort(),k);try{let t=this.getUrl(),n={signal:r.signal};Object.keys(e).length>0&&(n.headers=e);let i=await fetch(t,n);if(!i.ok&&i.status!==206)throw Error(`Fetch failed: ${i.status}`);return this.blob=await i.blob(),this.state=`complete`,this.blob}catch(e){let i=r.signal.aborted?`Timeout after ${k/1e3}s`:e.message;if(n<t){let e=this._typeConfig.retryDelays?.[n-1]??this._typeConfig.retryDelayMs*n,r=this.chunkIndex==null?``:` chunk ${this.chunkIndex}`;w.warn(`[DownloadTask] ${this.fileInfo.type}/${this.fileInfo.id}${r} attempt ${n}/${t} failed: ${i}. Retrying in ${e/1e3}s...`),await new Promise(t=>setTimeout(t,e))}else throw this.state=`failed`,r.signal.aborted?Error(i):e}finally{clearTimeout(i)}}}},L=class{constructor(e,t={}){this.fileInfo=e,this.options=t,this.state=`pending`,this.tasks=[],this.completedChunks=0,this.totalChunks=0,this.totalBytes=0,this.downloadedBytes=0,this.onChunkDownloaded=null,this.skipChunks=e.skipChunks||new Set,this._contentType=`application/octet-stream`,this._chunkBlobs=new Map,this._runningCount=0,this._resolve=null,this._reject=null,this._promise=new Promise((e,t)=>{this._resolve=e,this._reject=t}),this._promise.catch(()=>{})}getUrl(){let e=this.fileInfo.path;if(F(e))throw Error(`URL expired for ${this.fileInfo.type}/${this.fileInfo.id} — waiting for fresh URL from next collection cycle`);return e}wait(){return this._promise}async prepare(e){try{this.state=`preparing`;let{id:t,type:n,size:r}=this.fileInfo;w.info(`[FileDownload] Starting:`,`${n}/${t}`),this.totalBytes=r&&r>0?parseInt(r):0,this._contentType=j(this.fileInfo);let i=C(this.fileInfo.type).skipHead;if(this.totalBytes===0&&!i){let e=this.getUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),A);try{let n=await fetch(e,{method:`HEAD`,signal:t.signal});n.ok&&(this.totalBytes=parseInt(n.headers.get(`Content-Length`)||`0`),this._contentType=n.headers.get(`Content-Type`)||this._contentType)}finally{clearTimeout(n)}}w.info(`[FileDownload] File size:`,(this.totalBytes/1024/1024).toFixed(1),`MB`);let a=this.options.chunkSize||T;if(this.totalBytes>D){let e=[];for(let t=0;t<this.totalBytes;t+=a)e.push({start:t,end:Math.min(t+a-1,this.totalBytes-1),index:e.length});this.totalChunks=e.length;let r=e.filter(e=>!this.skipChunks.has(e.index)),i=e.length-r.length;for(let t of e)this.skipChunks.has(t.index)&&(this.downloadedBytes+=t.end-t.start+1);if(r.length===0){w.info(`[FileDownload] All chunks already cached, nothing to download`),this.state=`complete`,this._resolve(new Blob([],{type:this._contentType}));return}i>0&&w.info(`[FileDownload] Resuming: ${i} chunks cached, ${r.length} to download`);let o=i>0;if(o){let e=r.sort((e,t)=>e.index-t.index);for(let t of e){let e=new I(this.fileInfo,{chunkIndex:t.index,rangeStart:t.start,rangeEnd:t.end});e._parentFile=this,e._priority=M.normal,this.tasks.push(e)}}else for(let t of r){let n=new I(this.fileInfo,{chunkIndex:t.index,rangeStart:t.start,rangeEnd:t.end});n._parentFile=this,n._priority=t.index===0||t.index===e.length-1?M.high:M.normal,this.tasks.push(n)}let s=this.tasks.filter(e=>e._priority>=M.high).length;w.info(`[FileDownload] ${n}/${t}: ${this.tasks.length} chunks`+(s>0?` (${s} priority)`:``)+(o?` (resume)`:``))}else{this.totalChunks=1;let e=new I(this.fileInfo,{});e._parentFile=this,this.tasks.push(e)}e.enqueueChunkTasks(this.tasks),this.state=`downloading`}catch(e){w.error(`[FileDownload] Prepare failed:`,`${this.fileInfo.type}/${this.fileInfo.id}`,e),this.state=`failed`,this._reject(e)}}async onTaskComplete(e){if(this.completedChunks++,this.downloadedBytes+=e.blob.size,e.chunkIndex!=null&&this._chunkBlobs.set(e.chunkIndex,e.blob),this.options.onProgress&&this.options.onProgress(this.downloadedBytes,this.totalBytes),this.onChunkDownloaded&&e.chunkIndex!=null)try{await this.onChunkDownloaded(e.chunkIndex,e.blob,this.totalChunks)}catch(e){w.warn(`[FileDownload] onChunkDownloaded callback error:`,e)}if(this.completedChunks===this.tasks.length&&this.state!==`complete`){this.state=`complete`;let{type:t,id:n}=this.fileInfo;if(e.chunkIndex==null)w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(${e.blob.size} bytes)`),this._resolve(e.blob);else if(this.onChunkDownloaded)w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(progressive, ${this.totalChunks} chunks)`),this._resolve(new Blob([],{type:this._contentType}));else{let e=[];for(let t=0;t<this.totalChunks;t++){let n=this._chunkBlobs.get(t);n&&e.push(n)}let r=new Blob(e,{type:this._contentType});w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(${r.size} bytes, reassembled)`),this._resolve(r)}this._chunkBlobs.clear()}}onTaskFailed(e,t){if(!(this.state===`complete`||this.state===`failed`)){if(t.message?.includes(`URL expired`)){let t=e.chunkIndex==null?``:` chunk ${e.chunkIndex}`;w.warn(`[FileDownload] URL expired, dropping${t}:`,`${this.fileInfo.type}/${this.fileInfo.id}`),this.tasks=this.tasks.filter(t=>t!==e),(this.tasks.length===0||this.completedChunks>=this.tasks.length)&&(this.state=`complete`,this._urlExpired=!0,this._resolve(new Blob([],{type:this._contentType})));return}w.error(`[FileDownload] Failed:`,`${this.fileInfo.type}/${this.fileInfo.id}`,t),this.state=`failed`,this._reject(t)}}},R=class{constructor(e){this.queue=e,this._filesToPrepare=[],this._tasks=[],this._maxPreparing=2}addFile(e){let t=z.stableKey(e);if(this.queue.active.has(t)){let n=this.queue.active.get(t);if(e.path&&e.path!==n.fileInfo.path){let t=P(n.fileInfo.path);P(e.path)>t&&(n.fileInfo.path=e.path)}return n}let n=new L(e,{chunkSize:this.queue.chunkSize,calculateMD5:this.queue.calculateMD5,onProgress:this.queue.onProgress});return this.queue.active.set(t,n),this._filesToPrepare.push(n),n}enqueueChunkTasks(e){this._tasks.push(...e)}async build(){return await this._prepareAll(),this._sortWithBarriers()}async _prepareAll(){await new Promise(e=>{let t=0,n=0,r=()=>{for(;t<this._maxPreparing&&n<this._filesToPrepare.length;){let i=this._filesToPrepare[n++];t++,i.prepare(this).finally(()=>{t--,n>=this._filesToPrepare.length&&t===0?e():r()})}};this._filesToPrepare.length===0?e():r()})}_sortWithBarriers(){let e=[],t=[],n=[],r=[];for(let i of this._tasks)if(i.chunkIndex==null)e.push(i);else if(i.chunkIndex===0)t.push(i);else{let e=i._parentFile?.totalChunks||0;e>1&&i.chunkIndex===e-1?n.push(i):r.push(i)}e.sort((e,t)=>(e._parentFile?.totalBytes||0)-(t._parentFile?.totalBytes||0)),r.sort((e,t)=>e.chunkIndex-t.chunkIndex);let i=[...e,...t,...n];return r.length>0&&i.push(N,...r),i}},z=class e{constructor(e={}){this.concurrency=e.concurrency||te,this.chunkSize=e.chunkSize||T,this.maxChunksPerFile=e.chunksPerFile||E,this.calculateMD5=e.calculateMD5,this.onProgress=e.onProgress,this.queue=[],this.active=new Map,this._activeTasks=[],this.running=0,this._prepareQueue=[],this._preparingCount=0,this._maxPreparing=2,this.paused=!1,this._reenqueueTimers=new Set}static stableKey(e){return`${e.type}/${e.id}`}enqueue(t){let n=e.stableKey(t);if(this.active.has(n)){let e=this.active.get(n);if(t.path&&t.path!==e.fileInfo.path){let r=P(e.fileInfo.path);P(t.path)>r&&(w.info(`[DownloadQueue] Refreshing URL for`,n),e.fileInfo.path=t.path)}return e}let r=new L(t,{chunkSize:this.chunkSize,calculateMD5:this.calculateMD5,onProgress:this.onProgress});return this.active.set(n,r),w.info(`[DownloadQueue] Enqueued:`,n),this._schedulePrepare(r),r}_schedulePrepare(e){this._prepareQueue.push(e),this._processPrepareQueue()}_processPrepareQueue(){for(;this._preparingCount<this._maxPreparing&&this._prepareQueue.length>0;){let e=this._prepareQueue.shift();this._preparingCount++,e.prepare(this).finally(()=>{this._preparingCount--,this._processPrepareQueue()})}}enqueueChunkTasks(e){for(let t of e)this.queue.push(t);this._sortQueue(),w.info(`[DownloadQueue] ${e.length} tasks added (${this.queue.length} pending, ${this.running} active)`),this.processQueue()}enqueueOrderedTasks(e){let t=0,n=0;for(let r of e)r===N?(this.queue.push(N),n++):(this.queue.push(r),t++);w.info(`[DownloadQueue] Ordered queue: ${t} tasks, ${n} barriers (${this.queue.length} pending, ${this.running} active)`),this.processQueue()}_sortQueue(){this.queue.sort((e,t)=>t._priority-e._priority)}prioritizeLayoutFiles(e,t=M.high){let n=new Set(e.map(String)),r=0;for(let e of this.queue){let i=e._parentFile?.fileInfo.saveAs||String(e._parentFile?.fileInfo.id);n.has(i)&&e._priority<t&&(e._priority=t,r++)}for(let e of this._activeTasks){let r=e._parentFile?.fileInfo.saveAs||String(e._parentFile?.fileInfo.id);n.has(r)&&e._priority<t&&(e._priority=t)}this._sortQueue(),w.info(`[DownloadQueue] Layout files prioritized:`,n.size,`files,`,r,`tasks boosted to`,t)}urgentChunk(e,t,n){let r=`${e}/${t}`,i=this.active.get(r);if(!i)return w.info(`[DownloadQueue] urgentChunk: file not active:`,r,`chunk`,n),!1;if(this._activeTasks.some(e=>e._parentFile===i&&e.chunkIndex===n&&e.state===`downloading`)){let e=this._activeTasks.find(e=>e._parentFile===i&&e.chunkIndex===n);return e&&e._priority<M.urgent?(e._priority=M.urgent,w.info(`[DownloadQueue] URGENT: ${r} chunk ${n} (already in-flight, limiting slots)`),!0):(w.info(`[DownloadQueue] urgentChunk: already urgent:`,r,`chunk`,n),!1)}let a=this.queue.findIndex(e=>e!==N&&e._parentFile===i&&e.chunkIndex===n);if(a===-1)return w.info(`[DownloadQueue] urgentChunk: chunk not in queue:`,r,`chunk`,n),!1;let o=this.queue.splice(a,1)[0];return o._priority=M.urgent,this.queue.unshift(o),w.info(`[DownloadQueue] URGENT: ${r} chunk ${n} (moved to front)`),this.processQueue(),!0}processQueue(){if(this.paused)return;let e=this.queue.some(e=>e!==N&&e._priority>=M.urgent)||this._activeTasks?.some(e=>e._priority>=M.urgent&&e.state===`downloading`),t=e?O:this.concurrency,n=e?M.urgent:0;for(;this.running<t&&this.queue.length>0;){let e=this.queue[0];if(e===N){if(this.running>0)break;this.queue.shift();continue}if(e._priority<n||!this._canStartTask(e)){let e=!1;for(let t=1;t<this.queue.length&&this.queue[t]!==N;t++){let r=this.queue[t];if(r._priority>=n&&this._canStartTask(r)){this.queue.splice(t,1),this._startTask(r),e=!0;break}}if(!e)break;continue}this.queue.shift(),this._startTask(e)}this.queue.length===0&&this.running===0&&w.info(`[DownloadQueue] All downloads complete`)}_canStartTask(e){return e._parentFile._runningCount<this.maxChunksPerFile}_startTask(e){this.running++,e._parentFile._runningCount++,this._activeTasks.push(e);let t=`${e.fileInfo.type}/${e.fileInfo.id}`,n=e.chunkIndex==null?``:` chunk ${e.chunkIndex}`;w.info(`[DownloadQueue] Starting: ${t}${n} (${this.running}/${this.concurrency} active)`),e.start().then(()=>(this.running--,e._parentFile._runningCount--,this._activeTasks=this._activeTasks.filter(t=>t!==e),w.info(`[DownloadQueue] Fetched: ${t}${n} (${this.running} active, ${this.queue.length} pending)`),this.processQueue(),e._parentFile.onTaskComplete(e))).catch(n=>{this.running--,e._parentFile._runningCount--,this._activeTasks=this._activeTasks.filter(t=>t!==e);let{maxReenqueues:r,reenqueueDelayMs:i}=e._typeConfig;if(r>0){if(e._reenqueueCount=(e._reenqueueCount||0)+1,e._reenqueueCount>r){w.error(`[DownloadQueue] ${t} exceeded ${r} re-enqueues, failing permanently`),this.processQueue(),e._parentFile.onTaskFailed(e,n);return}w.warn(`[DownloadQueue] ${t} failed all retries (attempt ${e._reenqueueCount}/${r}), scheduling re-enqueue in ${i/1e3}s`);let a=setTimeout(()=>{this._reenqueueTimers.delete(a),e.state=`pending`,e._parentFile.state=`downloading`,this.queue.push(e),w.info(`[DownloadQueue] ${t} re-enqueued for retry`),this.processQueue()},i);this._reenqueueTimers.add(a),this.processQueue();return}this.processQueue(),e._parentFile.onTaskFailed(e,n)})}removeCompleted(e){let t=this.active.get(e);t&&(t.state===`complete`||t.state===`failed`)&&(this.queue=this.queue.filter(e=>e===N||e._parentFile!==t),this.active.delete(e))}getTask(e){return this.active.get(e)||null}getProgress(){let e={};for(let[t,n]of this.active.entries())n.state===`complete`||n.state===`failed`||(e[t]={downloaded:n.downloadedBytes,total:n.totalBytes,percent:n.totalBytes>0?(n.downloadedBytes/n.totalBytes*100).toFixed(1):0,state:n.state});return e}clear(){this.queue=[],this.active.clear(),this.running=0,this._prepareQueue=[],this._preparingCount=0;for(let e of this._reenqueueTimers)clearTimeout(e);this._reenqueueTimers.clear()}},B=class{constructor(e={}){this.queue=new z(e)}enqueue(e){return this.queue.enqueue(e)}getTask(e){return this.queue.getTask(e)}getProgress(){return this.queue.getProgress()}prioritizeLayoutFiles(e,t){this.queue.prioritizeLayoutFiles(e,t),this.queue.processQueue()}urgentChunk(e,t,n){return this.queue.urgentChunk(e,t,n)}createTaskBuilder(){return new R(this.queue)}enqueueOrderedTasks(e){this.queue.enqueueOrderedTasks(e)}removeCompleted(e){this.queue.removeCompleted(e)}get running(){return this.queue.running}get queued(){return this.queue.queue.length}clear(){this.queue.clear()}},V={name:`@xiboplayer/cache`,version:`0.7.21`,description:`Offline caching and download management with parallel chunk downloads`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./cache":`./src/cache.js`,"./store-client":`./src/store-client.js`,"./download-manager":`./src/download-manager.js`,"./cache-analyzer":`./src/cache-analyzer.js`,"./widget-html":`./src/widget-html.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/utils":`workspace:*`,"spark-md5":`^3.0.2`},devDependencies:{jsdom:`^29.0.2`,vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`cache`,`offline`,`download`,`indexeddb`],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/cache`},homepage:`https://xiboplayer.org`},H=a(`Cache`);new class{constructor(){this.dependants=new Map}addDependant(e,t){let n=String(e);this.dependants.has(n)||this.dependants.set(n,new Set),this.dependants.get(n).add(String(t))}removeLayoutDependants(e){let t=String(e),n=[];for(let[e,r]of this.dependants)r.delete(t),r.size===0&&(this.dependants.delete(e),n.push(e));return n.length>0&&H.info(`${n.length} media files orphaned after layout ${e} removed:`,n),n}isMediaReferenced(e){let t=this.dependants.get(String(e));return t?t.size>0:!1}async clearAll(){this.dependants.clear()}},a(`StoreClient`),a(`CacheAnalyzer`),a(`Cache`),typeof window<`u`&&window.location.pathname.replace(/\/[^/]*$/,``).replace(/\/$/,``);var U=V.version,W=typeof self<`u`&&self.registration?.scope?new URL(self.registration.scope).pathname.replace(/\/$/,``):`/player/pwa`,G=class{constructor(e){this.downloadManager=e,this.log=a(`SW`)}async handleRequest(e){let t=new URL(e.request.url);return t.pathname===W+`/`||t.pathname===W+`/index.html`||t.pathname===W+`/setup.html`||t.pathname.startsWith(x+`/`)?fetch(e.request):t.pathname.includes(`xmds.php`)&&t.searchParams.has(`file`)?this._handleXmdsFile(e,t):fetch(e.request)}_handleXmdsFile(e,t){let n=t.searchParams.get(`file`),r=t.searchParams.get(`type`),i=t.searchParams.get(`itemId`),a;a=r===`L`?`${x}/layouts/${i}`:r===`P`?`${x}/dependencies/${n}`:`${x}/media/file/${n}`,this.log.info(`XMDS redirect: ${r}/${n} → ${a}`);let o=new Headers(e.request.headers);return o.set(`X-Cms-Download-Url`,t.href),fetch(a,{headers:o})}},K=e=>(e.path||``).split(`?`)[0].replace(/^\/+/,``)||`${e.type||`media`}/${e.id}`,q=class{constructor(e,t){this.downloadManager=e,this.config=t,this.log=a(`SW Message`)}async handleMessage(e){let{type:t,data:n}=e.data;switch(this.log.info(`Received:`,t),t){case`PING`:return this.log.info(`PING received, broadcasting SW_READY`),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:`SW_READY`})}),{success:!0};case`DELETE_FILES`:return await this.handleDeleteFiles(n.files);case`GET_ALL_FILES`:return await this.handleGetAllFiles();case`CLEAR_CACHE`:return{success:!0};default:return this.log.warn(`Unknown message type:`,t),{success:!1,error:`Unknown message type`}}}async handleDeleteFiles(e){if(!e||!Array.isArray(e))return{success:!1,error:`No files provided`};try{let t=e.map(e=>({...e,key:K(e)})),n=await(await fetch(`/store/delete`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({files:t})})).json();return this.log.info(`Purge complete: ${n.deleted}/${n.total} files deleted`),{success:!0,deleted:n.deleted,total:n.total}}catch(e){return this.log.error(`Delete failed:`,e.message),{success:!1,error:e.message}}}async handleGetAllFiles(){try{return{success:!0,files:(await(await fetch(`/store/list`)).json()).files||[]}}catch(e){return this.log.error(`Failed to list files:`,e.message),{success:!0,files:[]}}}};function J(e){e||=a(`ChunkConfig`);let t=typeof navigator<`u`&&navigator.deviceMemory||null,n=4;if(t)n=t,e.info(`Detected device memory:`,t,`GB`);else if(typeof navigator<`u`){let t=navigator.userAgent.toLowerCase();t.includes(`raspberry pi`)||t.includes(`armv6`)?(n=.5,e.info(`Detected Pi Zero (512 MB RAM estimated)`)):t.includes(`armv7`)?(n=1,e.info(`Detected ARM device (1 GB RAM estimated)`)):e.info(`Using default RAM estimate:`,n,`GB`)}let r,i,o,s;return n<=.5?(r=10*1024*1024,i=25,o=25*1024*1024,s=1,e.info(`Low-memory config: 10 MB chunks, 25 MB cache, 1 concurrent download`)):n<=1?(r=20*1024*1024,i=50,o=50*1024*1024,s=2,e.info(`1GB-RAM config: 20 MB chunks, 50 MB cache, 2 concurrent downloads`)):n<=2?(r=30*1024*1024,i=100,o=75*1024*1024,s=2,e.info(`2GB-RAM config: 30 MB chunks, 100 MB cache, 2 concurrent downloads`)):n<=4?(r=50*1024*1024,i=200,o=100*1024*1024,s=4,e.info(`4GB-RAM config: 50 MB chunks, 200 MB cache, 4 concurrent downloads`)):(r=100*1024*1024,i=500,o=200*1024*1024,s=4,e.info(`High-RAM config: 100 MB chunks, 500 MB cache, 4 concurrent downloads`)),{chunkSize:r,blobCacheSize:i,threshold:o,concurrency:s}}var Y=`2026-04-16T11:03:03.243Z`,X=a(`SW`),Z=J(X),Q=Z.chunkSize,ne=Z.threshold,re=Z.concurrency;X.info(`Loading modular Service Worker:`,Y);var $=new B({concurrency:re,chunkSize:Q,chunksPerFile:2}),ie=new G($),ae=new q($,{chunkSize:Q,chunkStorageThreshold:ne});async function oe(e){let t=new URL(e.request.url),n=t.pathname.replace(W+`/ic`,``),r=e.request.method;X.info(`Interactive Control request:`,r,n);let i=null;if(r===`POST`||r===`PUT`)try{i=await e.request.text()}catch{}let 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":`*`}});let o=a[0];try{let e=await new Promise((e,a)=>{let s=new MessageChannel,c=setTimeout(()=>a(Error(`IC timeout`)),5e3);s.port1.onmessage=t=>{clearTimeout(c),e(t.data)},o.postMessage({type:`INTERACTIVE_CONTROL`,method:r,path:n,search:t.search,body:i},[s.port2])});return new Response(e.body||``,{status:e.status||200,headers:{"Content-Type":e.contentType||`application/json`,"Access-Control-Allow-Origin":`*`}})}catch(e){return X.error(`IC handler error:`,e),new Response(JSON.stringify({error:e.message}),{status:500,headers:{"Content-Type":`application/json`,"Access-Control-Allow-Origin":`*`}})}}self.addEventListener(`install`,e=>{X.info(`Installing... Version:`,Y),e.waitUntil((async()=>{if(self.registration.active)try{let e=await(await caches.open(`xibo-sw-version`)).match(`version`);if(e){let t=await e.text();if(t===Y){X.info(`Same version already active, skipping activation to preserve streams`);return}X.info(`Version changed:`,t,`→`,Y)}}catch{}return X.info(`New version, activating immediately`),self.skipWaiting()})())}),self.addEventListener(`activate`,e=>{X.info(`Activating... Version:`,Y,`| @xiboplayer/cache:`,U),e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>e.startsWith(`xibo-`)&&e!==`xibo-sw-version`).map(e=>(X.info(`Deleting legacy cache:`,e),caches.delete(e))))).then(async()=>(await(await caches.open(`xibo-sw-version`)).put(`version`,new Response(Y)),X.info(`Taking control of all clients immediately`),self.clients.claim())).then(async()=>{X.info(`Notifying all clients that fetch handler is ready`),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:`SW_READY`})})}))}),self.addEventListener(`fetch`,e=>{let t=new URL(e.request.url);if(t.pathname.startsWith(W+`/ic/`)||t.pathname.startsWith(`/player/`)&&(t.pathname.endsWith(`.html`)||t.pathname===`/player/`)||t.pathname.includes(`xmds.php`)&&t.searchParams.has(`file`)&&e.request.method===`GET`){if(t.pathname.startsWith(W+`/ic/`)){e.respondWith(oe(e));return}e.respondWith(ie.handleRequest(e))}}),self.addEventListener(`message`,e=>{e.waitUntil(ae.handleMessage(e).then(t=>{e.ports[0]?.postMessage(t)}))}),X.info(`Modular Service Worker ready`);
|
|
1
|
+
var e={name:`@xiboplayer/utils`,version:`0.7.23`,description:`Shared utilities for xiboplayer packages`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./logger":`./src/logger.js`,"./event-emitter":`./src/event-emitter.js`,"./config":`./src/config.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/crypto":`workspace:*`},devDependencies:{vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`utilities`,`logger`,`event-emitter`],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/utils`},homepage:`https://xiboplayer.org`},t={DEBUG:0,INFO:1,WARNING:2,ERROR:3,NONE:4},n=[],r=class{constructor(e,t=null){this.name=e,this.useGlobal=t===null,this.useGlobal||this.setLevel(t)}_ts(){let e=new Date;return`${String(e.getHours()).padStart(2,`0`)}:${String(e.getMinutes()).padStart(2,`0`)}:${String(e.getSeconds()).padStart(2,`0`)}.${String(e.getMilliseconds()).padStart(3,`0`)}`}setLevel(e){this.useGlobal=!1,typeof e==`string`?this.level=t[e.toUpperCase()]??t.INFO:this.level=e}getEffectiveLevel(){return this.useGlobal?i.level:this.level}debug(...e){this.getEffectiveLevel()<=t.DEBUG&&console.log(`${this._ts()} [${this.name}] DEBUG:`,...e),o(`debug`,this.name,e)}info(...e){this.getEffectiveLevel()<=t.INFO&&console.log(`${this._ts()} [${this.name}]`,...e),o(`info`,this.name,e)}warn(...e){this.getEffectiveLevel()<=t.WARNING&&console.warn(`${this._ts()} [${this.name}]`,...e),o(`warning`,this.name,e)}error(...e){this.getEffectiveLevel()<=t.ERROR&&console.error(`${this._ts()} [${this.name}]`,...e),o(`error`,this.name,e)}log(e,...t){switch(e.toUpperCase()){case`DEBUG`:return this.debug(...t);case`INFO`:return this.info(...t);case`WARNING`:case`WARN`:return this.warn(...t);case`ERROR`:return this.error(...t)}}},i={level:t.WARNING,setGlobalLevel(e){typeof e==`string`?this.level=t[e.toUpperCase()]??t.INFO:this.level=e,console.log(`[Logger] Global log level set to: ${this.getLevelName(this.level)}`)},getLevelName(e){return Object.keys(t).find(n=>t[n]===e)||`UNKNOWN`}};if(typeof window<`u`){let e=new URLSearchParams(window.location.search).get(`logLevel`),t=localStorage.getItem(`xibo_log_level`),n=null;try{n=JSON.parse(localStorage.getItem(`xibo_config`)||`{}`).logLevel||null}catch{}e?i.setGlobalLevel(e):t?i.setGlobalLevel(t):n?i.setGlobalLevel(n):i.setGlobalLevel(`WARNING`)}else typeof self<`u`&&self.swLogLevel&&i.setGlobalLevel(self.swLogLevel);function a(e,t=null){return new r(e,t)}function o(e,t,r){if(n.length!==0)for(let i of n)try{i({level:e,name:t,args:r})}catch{}}function s(e,t){let n=new Uint8Array(e),r=``;for(let e=0;e<n.length;e++)r+=String.fromCharCode(n[e]);let i=btoa(r),a=[];for(let e=0;e<i.length;e+=64)a.push(i.substring(e,e+64));return`-----BEGIN ${t}-----\n${a.join(`
|
|
2
|
+
`)}\n-----END ${t}-----`}async function c(){let e=await crypto.subtle.generateKey({name:`RSA-OAEP`,modulusLength:1024,publicExponent:new Uint8Array([1,0,1]),hash:`SHA-256`},!0,[`encrypt`,`decrypt`]),t=await crypto.subtle.exportKey(`spki`,e.publicKey),n=await crypto.subtle.exportKey(`pkcs8`,e.privateKey);return{publicKeyPem:s(t,`PUBLIC KEY`),privateKeyPem:s(n,`PRIVATE KEY`)}}function ee(e){return!e||typeof e!=`string`?!1:/^-----BEGIN (PUBLIC KEY|PRIVATE KEY)-----\n[A-Za-z0-9+/=\n]+\n-----END \1-----$/.test(e.trim())}function l(e,t,n,r={}){return typeof indexedDB>`u`?Promise.reject(Error(`IndexedDB not available`)):new Promise((i,a)=>{let o=indexedDB.open(e,t);o.onupgradeneeded=()=>{let e=o.result;if(!e.objectStoreNames.contains(n)){let t=r.keyPath?{keyPath:r.keyPath,autoIncrement:!0}:void 0,i=e.createObjectStore(n,t);r.indexName&&r.indexKey&&i.createIndex(r.indexName,r.indexKey,{unique:!1})}},o.onsuccess=()=>i(o.result),o.onerror=()=>a(o.error)})}var u=a(`Config`),d=`xibo_global`,f=`xibo_cms:`,p=`xibo_active_cms`,m=`xibo-hw-backup`,h=1,g=new Set([`hardwareKey`,`xmrPubKey`,`xmrPrivKey`]);function _(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);t>>>=0;let n=t+1234567;for(let t=0;t<e.length;t++)n^=e.charCodeAt(t)+1,n+=(n<<1)+(n<<4)+(n<<7)+(n<<8)+(n<<24);return n>>>=0,(t.toString(16).padStart(8,`0`)+n.toString(16).padStart(8,`0`)).substring(0,12)}function v(e){if(!e)return null;try{let t=new URL(e),n=t.origin;return`${t.hostname}-${_(n)}`}catch{return`unknown-${_(e)}`}}function y(){let e={},t={cmsUrl:e.CMS_URL||``,cmsKey:e.CMS_KEY||``,displayName:e.DISPLAY_NAME||``,hardwareKey:e.HARDWARE_KEY||``,xmrChannel:e.XMR_CHANNEL||``,googleGeoApiKey:e.GOOGLE_GEO_API_KEY||``};return Object.values(t).some(e=>e!==``)?t:null}var b=new class{constructor(){this._activeCmsId=null,this.data=this.load(),this._fromEnv||this._restoreHardwareKeyFromBackup()}load(){let e=y();return e?(this._fromEnv=!0,e):typeof localStorage>`u`?{cmsUrl:``,cmsKey:``,displayName:``,hardwareKey:``,xmrChannel:``}:localStorage.getItem(d)?this._loadSplit():this._loadFresh()}_loadSplit(){let e={};try{e=JSON.parse(localStorage.getItem(d)||`{}`)}catch(e){u.error(`Failed to parse xibo_global:`,e)}let t=localStorage.getItem(p)||null;this._activeCmsId=t;let n={};if(t)try{let e=localStorage.getItem(f+t);e&&(n=JSON.parse(e))}catch(e){u.error(`Failed to parse CMS config:`,e)}let r={...e,...n};return this._validateConfig(r)}_loadFresh(){return this._validateConfig({})}_validateConfig(e){let t=!1;return!e.hardwareKey||e.hardwareKey.length<10?(u.warn(`Missing/invalid hardwareKey — generating`),e.hardwareKey=this.generateStableHardwareKey(),this._backupHardwareKey(e.hardwareKey),t=!0):u.info(`✓ Loaded existing hardwareKey:`,e.hardwareKey),e.xmrChannel||(u.warn(`Missing xmrChannel — generating`),e.xmrChannel=this.generateXmrChannel(),t=!0),e.cmsUrl=e.cmsUrl||``,e.cmsKey=e.cmsKey||``,e.displayName=e.displayName||``,t&&typeof localStorage<`u`&&this._saveSplit(e),e}save(){typeof localStorage>`u`||this._saveSplit(this.data)}_saveSplit(e){if(typeof localStorage>`u`)return;let t={},n={};for(let[r,i]of Object.entries(e))g.has(r)?t[r]=i:n[r]=i;localStorage.setItem(d,JSON.stringify(t));let r=v(e.cmsUrl);r&&(localStorage.setItem(f+r,JSON.stringify(n)),localStorage.setItem(p,r),this._activeCmsId=r),localStorage.setItem(`xibo_config`,JSON.stringify(e))}switchCms(e){if(typeof localStorage>`u`)throw Error(`switchCms requires localStorage (browser only)`);this.save();let t=v(e);if(!t)throw Error(`Invalid CMS URL`);let n=localStorage.getItem(f+t),r={},i=!0;if(n)try{r=JSON.parse(n),i=!1,u.info(`Switching to existing CMS profile: ${t}`)}catch(e){u.error(`Failed to parse target CMS config:`,e)}else u.info(`Creating new CMS profile: ${t}`),r={cmsUrl:e,cmsKey:``,displayName:``,xmrChannel:this.generateXmrChannel()},localStorage.setItem(f+t,JSON.stringify(r));localStorage.setItem(p,t),this._activeCmsId=t;let a={};try{a=JSON.parse(localStorage.getItem(d)||`{}`)}catch{}return this.data={...a,...r},this.data.cmsUrl||(this.data.cmsUrl=e),{cmsId:t,isNew:i}}listCmsProfiles(){if(typeof localStorage>`u`)return[];let e=[],t=localStorage.getItem(p)||null;for(let n=0;n<localStorage.length;n++){let r=localStorage.key(n);if(!r.startsWith(f))continue;let i=r.slice(9);try{let n=JSON.parse(localStorage.getItem(r));e.push({cmsId:i,cmsUrl:n.cmsUrl||``,displayName:n.displayName||``,isActive:i===t})}catch{}}return e}get activeCmsId(){if(this._activeCmsId)return this._activeCmsId;let e=v(this.data?.cmsUrl);return this._activeCmsId=e,e}isConfigured(){return!!(this.data.cmsUrl&&this.data.cmsKey&&this.data.displayName)}async _backupKeys(e){try{let t=await l(m,h,`keys`),n=t.transaction(`keys`,`readwrite`),r=n.objectStore(`keys`);for(let[t,n]of Object.entries(e))r.put(n,t);n.oncomplete=()=>{u.info(`Keys backed up to IndexedDB:`,Object.keys(e).join(`, `)),t.close()}}catch{}}_backupHardwareKey(e){this._backupKeys({hardwareKey:e})}async _restoreHardwareKeyFromBackup(){try{let e=await l(m,h,`keys`),t=e.transaction(`keys`,`readonly`).objectStore(`keys`),n=await new Promise(e=>{let n=t.get(`hardwareKey`);n.onsuccess=()=>e(n.result),n.onerror=()=>e(null)});e.close(),n&&n!==this.data.hardwareKey?(u.info(`Restoring hardware key from IndexedDB backup:`,n),u.info(`(was:`,this.data.hardwareKey,`)`),this.data.hardwareKey=n,this.save()):!n&&this.data.hardwareKey&&this._backupHardwareKey(this.data.hardwareKey)}catch{}}generateStableHardwareKey(){if(typeof crypto<`u`&&crypto.randomUUID){let e=`pwa-`+crypto.randomUUID().replace(/-/g,``).substring(0,28);return u.info(`Generated new UUID-based hardware key:`,e),e}let e=`pwa-`+Array.from({length:28},()=>Math.floor(Math.random()*16).toString(16)).join(``);return u.info(`Generated new random hardware key:`,e),e}generateXmrChannel(){return`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}async ensureXmrKeyPair(){if(this.data.xmrPubKey&&ee(this.data.xmrPubKey))return;u.info(`Generating RSA key pair for XMR registration...`);let{publicKeyPem:e,privateKeyPem:t}=await c();this.data.xmrPubKey=e,this.data.xmrPrivKey=t,this.save(),typeof indexedDB<`u`&&this._backupKeys({xmrPubKey:e,xmrPrivKey:t}),u.info(`RSA key pair generated and saved`)}get cmsUrl(){return this.data.cmsUrl}set cmsUrl(e){this.data.cmsUrl=e,this.save()}get cmsKey(){return this.data.cmsKey}set cmsKey(e){this.data.cmsKey=e,this.save()}get displayName(){return this.data.displayName}set displayName(e){this.data.displayName=e,this.save()}get hardwareKey(){return this.data.hardwareKey||(u.error(`CRITICAL: hardwareKey missing! Generating emergency key.`),this.data.hardwareKey=this.generateStableHardwareKey(),this.save()),this.data.hardwareKey}get xmrChannel(){return this.data.xmrChannel||(u.warn(`xmrChannel missing at access time — generating`),this.data.xmrChannel=this.generateXmrChannel(),this.save()),this.data.xmrChannel}get xmrPubKey(){return this.data.xmrPubKey||``}get xmrPrivKey(){return this.data.xmrPrivKey||``}get googleGeoApiKey(){return this.data.googleGeoApiKey||``}set googleGeoApiKey(e){this.data.googleGeoApiKey=e,this.save()}get controls(){return this.data.controls||{}}get transport(){return this.data.transport||`auto`}get debug(){return this.data.debug||{}}};a(`FetchRetry`),a(`CmsApi`),e.version;var x=b.data?.playerApiBase||`/player/api/v2`,S={media:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0},layout:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0},dataset:{maxRetries:4,retryDelayMs:0,retryDelays:[15e3,3e4,6e4,12e4],maxReenqueues:5,reenqueueDelayMs:6e4,skipHead:!0,cacheTtl:300},static:{maxRetries:3,retryDelayMs:500,retryDelays:null,maxReenqueues:0,reenqueueDelayMs:0,skipHead:!1,cacheTtl:1/0}};function C(e){return S[e]||S.media}var w=a(`Download`),te=6,T=50*1024*1024,E=3,D=100*1024*1024,O=2,k=6e5,A=15e3;function j(e){return{mp4:`video/mp4`,webm:`video/webm`,mp3:`audio/mpeg`,png:`image/png`,jpg:`image/jpeg`,jpeg:`image/jpeg`,gif:`image/gif`,svg:`image/svg+xml`,webp:`image/webp`,css:`text/css`,js:`application/javascript`,ttf:`font/ttf`,otf:`font/otf`,woff:`font/woff`,woff2:`font/woff2`,xml:`application/xml`,xlf:`application/xml`}[(e.path||e.code||``).split(`.`).pop()?.split(`?`)[0]?.toLowerCase()]||`application/octet-stream`}var M={normal:0,layout:1,high:2,urgent:3},N=Symbol(`BARRIER`);function P(e){try{let t=new URL(e,`http://localhost`),n=t.searchParams.get(`X-Amz-Date`),r=t.searchParams.get(`X-Amz-Expires`);if(n&&r){let e=n.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/,`$1-$2-$3T$4:$5:$6Z`),t=new Date(e).getTime()/1e3;return isNaN(t)?1/0:t+parseInt(r,10)}return 1/0}catch{return 1/0}}function F(e,t=30){let n=P(e);return n===1/0?!1:Date.now()/1e3>=n-t}var I=class{constructor(e,t={}){this.fileInfo=e,this.chunkIndex=t.chunkIndex??null,this.rangeStart=t.rangeStart??null,this.rangeEnd=t.rangeEnd??null,this.state=`pending`,this.blob=null,this._parentFile=null,this._priority=M.normal,this._typeConfig=C(e.type)}getUrl(){let e=this.fileInfo.path;if(F(e))throw Error(`URL expired for ${this.fileInfo.type}/${this.fileInfo.id} — waiting for fresh URL from next collection cycle`);return e}async start(){this.state=`downloading`;let e={};this.rangeStart!=null&&(e.Range=`bytes=${this.rangeStart}-${this.rangeEnd}`),this.chunkIndex!=null&&(e[`X-Store-Chunk-Index`]=String(this.chunkIndex),this._parentFile&&(e[`X-Store-Num-Chunks`]=String(this._parentFile.totalChunks),e[`X-Store-Chunk-Size`]=String(this._parentFile.options.chunkSize||104857600))),this.fileInfo.md5&&(e[`X-Store-MD5`]=this.fileInfo.md5),this.fileInfo.updateInterval&&(e[`X-Cache-TTL`]=String(this.fileInfo.updateInterval)),this.fileInfo.cmsDownloadUrl&&(e[`X-Cms-Download-Url`]=this.fileInfo.cmsDownloadUrl);let t=this._typeConfig.maxRetries;for(let n=1;n<=t;n++){let r=new AbortController,i=setTimeout(()=>r.abort(),k);try{let t=this.getUrl(),n={signal:r.signal};Object.keys(e).length>0&&(n.headers=e);let i=await fetch(t,n);if(!i.ok&&i.status!==206)throw Error(`Fetch failed: ${i.status}`);return this.blob=await i.blob(),this.state=`complete`,this.blob}catch(e){let i=r.signal.aborted?`Timeout after ${k/1e3}s`:e.message;if(n<t){let e=this._typeConfig.retryDelays?.[n-1]??this._typeConfig.retryDelayMs*n,r=this.chunkIndex==null?``:` chunk ${this.chunkIndex}`;w.warn(`[DownloadTask] ${this.fileInfo.type}/${this.fileInfo.id}${r} attempt ${n}/${t} failed: ${i}. Retrying in ${e/1e3}s...`),await new Promise(t=>setTimeout(t,e))}else throw this.state=`failed`,r.signal.aborted?Error(i):e}finally{clearTimeout(i)}}}},L=class{constructor(e,t={}){this.fileInfo=e,this.options=t,this.state=`pending`,this.tasks=[],this.completedChunks=0,this.totalChunks=0,this.totalBytes=0,this.downloadedBytes=0,this.onChunkDownloaded=null,this.skipChunks=e.skipChunks||new Set,this._contentType=`application/octet-stream`,this._chunkBlobs=new Map,this._runningCount=0,this._resolve=null,this._reject=null,this._promise=new Promise((e,t)=>{this._resolve=e,this._reject=t}),this._promise.catch(()=>{})}getUrl(){let e=this.fileInfo.path;if(F(e))throw Error(`URL expired for ${this.fileInfo.type}/${this.fileInfo.id} — waiting for fresh URL from next collection cycle`);return e}wait(){return this._promise}async prepare(e){try{this.state=`preparing`;let{id:t,type:n,size:r}=this.fileInfo;w.info(`[FileDownload] Starting:`,`${n}/${t}`),this.totalBytes=r&&r>0?parseInt(r):0,this._contentType=j(this.fileInfo);let i=C(this.fileInfo.type).skipHead;if(this.totalBytes===0&&!i){let e=this.getUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),A);try{let n=await fetch(e,{method:`HEAD`,signal:t.signal});n.ok&&(this.totalBytes=parseInt(n.headers.get(`Content-Length`)||`0`),this._contentType=n.headers.get(`Content-Type`)||this._contentType)}finally{clearTimeout(n)}}w.info(`[FileDownload] File size:`,(this.totalBytes/1024/1024).toFixed(1),`MB`);let a=this.options.chunkSize||T;if(this.totalBytes>D){let e=[];for(let t=0;t<this.totalBytes;t+=a)e.push({start:t,end:Math.min(t+a-1,this.totalBytes-1),index:e.length});this.totalChunks=e.length;let r=e.filter(e=>!this.skipChunks.has(e.index)),i=e.length-r.length;for(let t of e)this.skipChunks.has(t.index)&&(this.downloadedBytes+=t.end-t.start+1);if(r.length===0){w.info(`[FileDownload] All chunks already cached, nothing to download`),this.state=`complete`,this._resolve(new Blob([],{type:this._contentType}));return}i>0&&w.info(`[FileDownload] Resuming: ${i} chunks cached, ${r.length} to download`);let o=i>0;if(o){let e=r.sort((e,t)=>e.index-t.index);for(let t of e){let e=new I(this.fileInfo,{chunkIndex:t.index,rangeStart:t.start,rangeEnd:t.end});e._parentFile=this,e._priority=M.normal,this.tasks.push(e)}}else for(let t of r){let n=new I(this.fileInfo,{chunkIndex:t.index,rangeStart:t.start,rangeEnd:t.end});n._parentFile=this,n._priority=t.index===0||t.index===e.length-1?M.high:M.normal,this.tasks.push(n)}let s=this.tasks.filter(e=>e._priority>=M.high).length;w.info(`[FileDownload] ${n}/${t}: ${this.tasks.length} chunks`+(s>0?` (${s} priority)`:``)+(o?` (resume)`:``))}else{this.totalChunks=1;let e=new I(this.fileInfo,{});e._parentFile=this,this.tasks.push(e)}e.enqueueChunkTasks(this.tasks),this.state=`downloading`}catch(e){w.error(`[FileDownload] Prepare failed:`,`${this.fileInfo.type}/${this.fileInfo.id}`,e),this.state=`failed`,this._reject(e)}}async onTaskComplete(e){if(this.completedChunks++,this.downloadedBytes+=e.blob.size,e.chunkIndex!=null&&this._chunkBlobs.set(e.chunkIndex,e.blob),this.options.onProgress&&this.options.onProgress(this.downloadedBytes,this.totalBytes),this.onChunkDownloaded&&e.chunkIndex!=null)try{await this.onChunkDownloaded(e.chunkIndex,e.blob,this.totalChunks)}catch(e){w.warn(`[FileDownload] onChunkDownloaded callback error:`,e)}if(this.completedChunks===this.tasks.length&&this.state!==`complete`){this.state=`complete`;let{type:t,id:n}=this.fileInfo;if(e.chunkIndex==null)w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(${e.blob.size} bytes)`),this._resolve(e.blob);else if(this.onChunkDownloaded)w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(progressive, ${this.totalChunks} chunks)`),this._resolve(new Blob([],{type:this._contentType}));else{let e=[];for(let t=0;t<this.totalChunks;t++){let n=this._chunkBlobs.get(t);n&&e.push(n)}let r=new Blob(e,{type:this._contentType});w.info(`[FileDownload] Complete:`,`${t}/${n}`,`(${r.size} bytes, reassembled)`),this._resolve(r)}this._chunkBlobs.clear()}}onTaskFailed(e,t){if(!(this.state===`complete`||this.state===`failed`)){if(t.message?.includes(`URL expired`)){let t=e.chunkIndex==null?``:` chunk ${e.chunkIndex}`;w.warn(`[FileDownload] URL expired, dropping${t}:`,`${this.fileInfo.type}/${this.fileInfo.id}`),this.tasks=this.tasks.filter(t=>t!==e),(this.tasks.length===0||this.completedChunks>=this.tasks.length)&&(this.state=`complete`,this._urlExpired=!0,this._resolve(new Blob([],{type:this._contentType})));return}w.error(`[FileDownload] Failed:`,`${this.fileInfo.type}/${this.fileInfo.id}`,t),this.state=`failed`,this._reject(t)}}},R=class{constructor(e){this.queue=e,this._filesToPrepare=[],this._tasks=[],this._maxPreparing=2}addFile(e){let t=z.stableKey(e);if(this.queue.active.has(t)){let n=this.queue.active.get(t);if(e.path&&e.path!==n.fileInfo.path){let t=P(n.fileInfo.path);P(e.path)>t&&(n.fileInfo.path=e.path)}return n}let n=new L(e,{chunkSize:this.queue.chunkSize,calculateMD5:this.queue.calculateMD5,onProgress:this.queue.onProgress});return this.queue.active.set(t,n),this._filesToPrepare.push(n),n}enqueueChunkTasks(e){this._tasks.push(...e)}async build(){return await this._prepareAll(),this._sortWithBarriers()}async _prepareAll(){await new Promise(e=>{let t=0,n=0,r=()=>{for(;t<this._maxPreparing&&n<this._filesToPrepare.length;){let i=this._filesToPrepare[n++];t++,i.prepare(this).finally(()=>{t--,n>=this._filesToPrepare.length&&t===0?e():r()})}};this._filesToPrepare.length===0?e():r()})}_sortWithBarriers(){let e=[],t=[],n=[],r=[];for(let i of this._tasks)if(i.chunkIndex==null)e.push(i);else if(i.chunkIndex===0)t.push(i);else{let e=i._parentFile?.totalChunks||0;e>1&&i.chunkIndex===e-1?n.push(i):r.push(i)}e.sort((e,t)=>(e._parentFile?.totalBytes||0)-(t._parentFile?.totalBytes||0)),r.sort((e,t)=>e.chunkIndex-t.chunkIndex);let i=[...e,...t,...n];return r.length>0&&i.push(N,...r),i}},z=class e{constructor(e={}){this.concurrency=e.concurrency||te,this.chunkSize=e.chunkSize||T,this.maxChunksPerFile=e.chunksPerFile||E,this.calculateMD5=e.calculateMD5,this.onProgress=e.onProgress,this.queue=[],this.active=new Map,this._activeTasks=[],this.running=0,this._prepareQueue=[],this._preparingCount=0,this._maxPreparing=2,this.paused=!1,this._reenqueueTimers=new Set}static stableKey(e){return`${e.type}/${e.id}`}enqueue(t){let n=e.stableKey(t);if(this.active.has(n)){let e=this.active.get(n);if(t.path&&t.path!==e.fileInfo.path){let r=P(e.fileInfo.path);P(t.path)>r&&(w.info(`[DownloadQueue] Refreshing URL for`,n),e.fileInfo.path=t.path)}return e}let r=new L(t,{chunkSize:this.chunkSize,calculateMD5:this.calculateMD5,onProgress:this.onProgress});return this.active.set(n,r),w.info(`[DownloadQueue] Enqueued:`,n),this._schedulePrepare(r),r}_schedulePrepare(e){this._prepareQueue.push(e),this._processPrepareQueue()}_processPrepareQueue(){for(;this._preparingCount<this._maxPreparing&&this._prepareQueue.length>0;){let e=this._prepareQueue.shift();this._preparingCount++,e.prepare(this).finally(()=>{this._preparingCount--,this._processPrepareQueue()})}}enqueueChunkTasks(e){for(let t of e)this.queue.push(t);this._sortQueue(),w.info(`[DownloadQueue] ${e.length} tasks added (${this.queue.length} pending, ${this.running} active)`),this.processQueue()}enqueueOrderedTasks(e){let t=0,n=0;for(let r of e)r===N?(this.queue.push(N),n++):(this.queue.push(r),t++);w.info(`[DownloadQueue] Ordered queue: ${t} tasks, ${n} barriers (${this.queue.length} pending, ${this.running} active)`),this.processQueue()}_sortQueue(){this.queue.sort((e,t)=>t._priority-e._priority)}prioritizeLayoutFiles(e,t=M.high){let n=new Set(e.map(String)),r=0;for(let e of this.queue){let i=e._parentFile?.fileInfo.saveAs||String(e._parentFile?.fileInfo.id);n.has(i)&&e._priority<t&&(e._priority=t,r++)}for(let e of this._activeTasks){let r=e._parentFile?.fileInfo.saveAs||String(e._parentFile?.fileInfo.id);n.has(r)&&e._priority<t&&(e._priority=t)}this._sortQueue(),w.info(`[DownloadQueue] Layout files prioritized:`,n.size,`files,`,r,`tasks boosted to`,t)}urgentChunk(e,t,n){let r=`${e}/${t}`,i=this.active.get(r);if(!i)return w.info(`[DownloadQueue] urgentChunk: file not active:`,r,`chunk`,n),!1;if(this._activeTasks.some(e=>e._parentFile===i&&e.chunkIndex===n&&e.state===`downloading`)){let e=this._activeTasks.find(e=>e._parentFile===i&&e.chunkIndex===n);return e&&e._priority<M.urgent?(e._priority=M.urgent,w.info(`[DownloadQueue] URGENT: ${r} chunk ${n} (already in-flight, limiting slots)`),!0):(w.info(`[DownloadQueue] urgentChunk: already urgent:`,r,`chunk`,n),!1)}let a=this.queue.findIndex(e=>e!==N&&e._parentFile===i&&e.chunkIndex===n);if(a===-1)return w.info(`[DownloadQueue] urgentChunk: chunk not in queue:`,r,`chunk`,n),!1;let o=this.queue.splice(a,1)[0];return o._priority=M.urgent,this.queue.unshift(o),w.info(`[DownloadQueue] URGENT: ${r} chunk ${n} (moved to front)`),this.processQueue(),!0}processQueue(){if(this.paused)return;let e=this.queue.some(e=>e!==N&&e._priority>=M.urgent)||this._activeTasks?.some(e=>e._priority>=M.urgent&&e.state===`downloading`),t=e?O:this.concurrency,n=e?M.urgent:0;for(;this.running<t&&this.queue.length>0;){let e=this.queue[0];if(e===N){if(this.running>0)break;this.queue.shift();continue}if(e._priority<n||!this._canStartTask(e)){let e=!1;for(let t=1;t<this.queue.length&&this.queue[t]!==N;t++){let r=this.queue[t];if(r._priority>=n&&this._canStartTask(r)){this.queue.splice(t,1),this._startTask(r),e=!0;break}}if(!e)break;continue}this.queue.shift(),this._startTask(e)}this.queue.length===0&&this.running===0&&w.info(`[DownloadQueue] All downloads complete`)}_canStartTask(e){return e._parentFile._runningCount<this.maxChunksPerFile}_startTask(e){this.running++,e._parentFile._runningCount++,this._activeTasks.push(e);let t=`${e.fileInfo.type}/${e.fileInfo.id}`,n=e.chunkIndex==null?``:` chunk ${e.chunkIndex}`;w.info(`[DownloadQueue] Starting: ${t}${n} (${this.running}/${this.concurrency} active)`),e.start().then(()=>(this.running--,e._parentFile._runningCount--,this._activeTasks=this._activeTasks.filter(t=>t!==e),w.info(`[DownloadQueue] Fetched: ${t}${n} (${this.running} active, ${this.queue.length} pending)`),this.processQueue(),e._parentFile.onTaskComplete(e))).catch(n=>{this.running--,e._parentFile._runningCount--,this._activeTasks=this._activeTasks.filter(t=>t!==e);let{maxReenqueues:r,reenqueueDelayMs:i}=e._typeConfig;if(r>0){if(e._reenqueueCount=(e._reenqueueCount||0)+1,e._reenqueueCount>r){w.error(`[DownloadQueue] ${t} exceeded ${r} re-enqueues, failing permanently`),this.processQueue(),e._parentFile.onTaskFailed(e,n);return}w.warn(`[DownloadQueue] ${t} failed all retries (attempt ${e._reenqueueCount}/${r}), scheduling re-enqueue in ${i/1e3}s`);let a=setTimeout(()=>{this._reenqueueTimers.delete(a),e.state=`pending`,e._parentFile.state=`downloading`,this.queue.push(e),w.info(`[DownloadQueue] ${t} re-enqueued for retry`),this.processQueue()},i);this._reenqueueTimers.add(a),this.processQueue();return}this.processQueue(),e._parentFile.onTaskFailed(e,n)})}removeCompleted(e){let t=this.active.get(e);t&&(t.state===`complete`||t.state===`failed`)&&(this.queue=this.queue.filter(e=>e===N||e._parentFile!==t),this.active.delete(e))}getTask(e){return this.active.get(e)||null}getProgress(){let e={};for(let[t,n]of this.active.entries())n.state===`complete`||n.state===`failed`||(e[t]={downloaded:n.downloadedBytes,total:n.totalBytes,percent:n.totalBytes>0?(n.downloadedBytes/n.totalBytes*100).toFixed(1):0,state:n.state});return e}clear(){this.queue=[],this.active.clear(),this.running=0,this._prepareQueue=[],this._preparingCount=0;for(let e of this._reenqueueTimers)clearTimeout(e);this._reenqueueTimers.clear()}},B=class{constructor(e={}){this.queue=new z(e)}enqueue(e){return this.queue.enqueue(e)}getTask(e){return this.queue.getTask(e)}getProgress(){return this.queue.getProgress()}prioritizeLayoutFiles(e,t){this.queue.prioritizeLayoutFiles(e,t),this.queue.processQueue()}urgentChunk(e,t,n){return this.queue.urgentChunk(e,t,n)}createTaskBuilder(){return new R(this.queue)}enqueueOrderedTasks(e){this.queue.enqueueOrderedTasks(e)}removeCompleted(e){this.queue.removeCompleted(e)}get running(){return this.queue.running}get queued(){return this.queue.queue.length}clear(){this.queue.clear()}},V={name:`@xiboplayer/cache`,version:`0.7.23`,description:`Offline caching and download management with parallel chunk downloads`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./cache":`./src/cache.js`,"./store-client":`./src/store-client.js`,"./download-manager":`./src/download-manager.js`,"./cache-analyzer":`./src/cache-analyzer.js`,"./widget-html":`./src/widget-html.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/utils":`workspace:*`,"spark-md5":`^3.0.2`},devDependencies:{jsdom:`^29.0.2`,vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`cache`,`offline`,`download`,`indexeddb`],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/cache`},homepage:`https://xiboplayer.org`},H=a(`Cache`);new class{constructor(){this.dependants=new Map}addDependant(e,t){let n=String(e);this.dependants.has(n)||this.dependants.set(n,new Set),this.dependants.get(n).add(String(t))}removeLayoutDependants(e){let t=String(e),n=[];for(let[e,r]of this.dependants)r.delete(t),r.size===0&&(this.dependants.delete(e),n.push(e));return n.length>0&&H.info(`${n.length} media files orphaned after layout ${e} removed:`,n),n}isMediaReferenced(e){let t=this.dependants.get(String(e));return t?t.size>0:!1}async clearAll(){this.dependants.clear()}},a(`StoreClient`),a(`CacheAnalyzer`),a(`Cache`),typeof window<`u`&&window.location.pathname.replace(/\/[^/]*$/,``).replace(/\/$/,``);var U=V.version,W=typeof self<`u`&&self.registration?.scope?new URL(self.registration.scope).pathname.replace(/\/$/,``):`/player/pwa`,G=class{constructor(e){this.downloadManager=e,this.log=a(`SW`)}async handleRequest(e){let t=new URL(e.request.url);return t.pathname===W+`/`||t.pathname===W+`/index.html`||t.pathname===W+`/setup.html`||t.pathname.startsWith(x+`/`)?fetch(e.request):t.pathname.includes(`xmds.php`)&&t.searchParams.has(`file`)?this._handleXmdsFile(e,t):fetch(e.request)}_handleXmdsFile(e,t){let n=t.searchParams.get(`file`),r=t.searchParams.get(`type`),i=t.searchParams.get(`itemId`),a;a=r===`L`?`${x}/layouts/${i}`:r===`P`?`${x}/dependencies/${n}`:`${x}/media/file/${n}`,this.log.info(`XMDS redirect: ${r}/${n} → ${a}`);let o=new Headers(e.request.headers);return o.set(`X-Cms-Download-Url`,t.href),fetch(a,{headers:o})}},K=e=>(e.path||``).split(`?`)[0].replace(/^\/+/,``)||`${e.type||`media`}/${e.id}`,q=class{constructor(e,t){this.downloadManager=e,this.config=t,this.log=a(`SW Message`)}async handleMessage(e){let{type:t,data:n}=e.data;switch(this.log.info(`Received:`,t),t){case`PING`:return this.log.info(`PING received, broadcasting SW_READY`),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:`SW_READY`})}),{success:!0};case`DELETE_FILES`:return await this.handleDeleteFiles(n.files);case`GET_ALL_FILES`:return await this.handleGetAllFiles();case`CLEAR_CACHE`:return{success:!0};default:return this.log.warn(`Unknown message type:`,t),{success:!1,error:`Unknown message type`}}}async handleDeleteFiles(e){if(!e||!Array.isArray(e))return{success:!1,error:`No files provided`};try{let t=e.map(e=>({...e,key:K(e)})),n=await(await fetch(`/store/delete`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({files:t})})).json();return this.log.info(`Purge complete: ${n.deleted}/${n.total} files deleted`),{success:!0,deleted:n.deleted,total:n.total}}catch(e){return this.log.error(`Delete failed:`,e.message),{success:!1,error:e.message}}}async handleGetAllFiles(){try{return{success:!0,files:(await(await fetch(`/store/list`)).json()).files||[]}}catch(e){return this.log.error(`Failed to list files:`,e.message),{success:!0,files:[]}}}};function J(e){e||=a(`ChunkConfig`);let t=typeof navigator<`u`&&navigator.deviceMemory||null,n=4;if(t)n=t,e.info(`Detected device memory:`,t,`GB`);else if(typeof navigator<`u`){let t=navigator.userAgent.toLowerCase();t.includes(`raspberry pi`)||t.includes(`armv6`)?(n=.5,e.info(`Detected Pi Zero (512 MB RAM estimated)`)):t.includes(`armv7`)?(n=1,e.info(`Detected ARM device (1 GB RAM estimated)`)):e.info(`Using default RAM estimate:`,n,`GB`)}let r,i,o,s;return n<=.5?(r=10*1024*1024,i=25,o=25*1024*1024,s=1,e.info(`Low-memory config: 10 MB chunks, 25 MB cache, 1 concurrent download`)):n<=1?(r=20*1024*1024,i=50,o=50*1024*1024,s=2,e.info(`1GB-RAM config: 20 MB chunks, 50 MB cache, 2 concurrent downloads`)):n<=2?(r=30*1024*1024,i=100,o=75*1024*1024,s=2,e.info(`2GB-RAM config: 30 MB chunks, 100 MB cache, 2 concurrent downloads`)):n<=4?(r=50*1024*1024,i=200,o=100*1024*1024,s=4,e.info(`4GB-RAM config: 50 MB chunks, 200 MB cache, 4 concurrent downloads`)):(r=100*1024*1024,i=500,o=200*1024*1024,s=4,e.info(`High-RAM config: 100 MB chunks, 500 MB cache, 4 concurrent downloads`)),{chunkSize:r,blobCacheSize:i,threshold:o,concurrency:s}}var Y=`2026-04-16T23:29:15.036Z`,X=a(`SW`),Z=J(X),Q=Z.chunkSize,ne=Z.threshold,re=Z.concurrency;X.info(`Loading modular Service Worker:`,Y);var $=new B({concurrency:re,chunkSize:Q,chunksPerFile:2}),ie=new G($),ae=new q($,{chunkSize:Q,chunkStorageThreshold:ne});async function oe(e){let t=new URL(e.request.url),n=t.pathname.replace(W+`/ic`,``),r=e.request.method;X.info(`Interactive Control request:`,r,n);let i=null;if(r===`POST`||r===`PUT`)try{i=await e.request.text()}catch{}let 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":`*`}});let o=a[0];try{let e=await new Promise((e,a)=>{let s=new MessageChannel,c=setTimeout(()=>a(Error(`IC timeout`)),5e3);s.port1.onmessage=t=>{clearTimeout(c),e(t.data)},o.postMessage({type:`INTERACTIVE_CONTROL`,method:r,path:n,search:t.search,body:i},[s.port2])});return new Response(e.body||``,{status:e.status||200,headers:{"Content-Type":e.contentType||`application/json`,"Access-Control-Allow-Origin":`*`}})}catch(e){return X.error(`IC handler error:`,e),new Response(JSON.stringify({error:e.message}),{status:500,headers:{"Content-Type":`application/json`,"Access-Control-Allow-Origin":`*`}})}}self.addEventListener(`install`,e=>{X.info(`Installing... Version:`,Y),e.waitUntil((async()=>{if(self.registration.active)try{let e=await(await caches.open(`xibo-sw-version`)).match(`version`);if(e){let t=await e.text();if(t===Y){X.info(`Same version already active, skipping activation to preserve streams`);return}X.info(`Version changed:`,t,`→`,Y)}}catch{}return X.info(`New version, activating immediately`),self.skipWaiting()})())}),self.addEventListener(`activate`,e=>{X.info(`Activating... Version:`,Y,`| @xiboplayer/cache:`,U),e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>e.startsWith(`xibo-`)&&e!==`xibo-sw-version`).map(e=>(X.info(`Deleting legacy cache:`,e),caches.delete(e))))).then(async()=>(await(await caches.open(`xibo-sw-version`)).put(`version`,new Response(Y)),X.info(`Taking control of all clients immediately`),self.clients.claim())).then(async()=>{X.info(`Notifying all clients that fetch handler is ready`),(await self.clients.matchAll()).forEach(e=>{e.postMessage({type:`SW_READY`})})}))}),self.addEventListener(`fetch`,e=>{let t=new URL(e.request.url);if(t.pathname.startsWith(W+`/ic/`)||t.pathname.startsWith(`/player/`)&&(t.pathname.endsWith(`.html`)||t.pathname===`/player/`)||t.pathname.includes(`xmds.php`)&&t.searchParams.has(`file`)&&e.request.method===`GET`){if(t.pathname.startsWith(W+`/ic/`)){e.respondWith(oe(e));return}e.respondWith(ie.handleRequest(e))}}),self.addEventListener(`message`,e=>{e.waitUntil(ae.handleMessage(e).then(t=>{e.ports[0]?.postMessage(t)}))}),X.info(`Modular Service Worker ready`);
|
|
3
3
|
//# sourceMappingURL=sw-pwa.js.map
|