@xiboplayer/cache 0.5.5 → 0.5.7
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/package.json +2 -2
- package/src/download-manager.js +32 -4
- package/src/widget-html.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/cache",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Offline caching and download management with parallel chunk downloads",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"spark-md5": "^3.0.2",
|
|
15
|
-
"@xiboplayer/utils": "0.5.
|
|
15
|
+
"@xiboplayer/utils": "0.5.7"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"vitest": "^2.0.0",
|
package/src/download-manager.js
CHANGED
|
@@ -34,6 +34,12 @@ const DEFAULT_MAX_CHUNKS_PER_FILE = 3; // Max parallel chunk downloads per file
|
|
|
34
34
|
const CHUNK_THRESHOLD = 100 * 1024 * 1024; // Files > 100MB get chunked
|
|
35
35
|
const MAX_RETRIES = 3;
|
|
36
36
|
const RETRY_DELAY_MS = 500; // Fast: 500ms, 1s, 1.5s → total ~3s
|
|
37
|
+
|
|
38
|
+
// getData (widget data) retry config — CMS "cache not ready" (HTTP 500) resolves
|
|
39
|
+
// when the XTR task runs (30-120s). Use longer backoff to ride it out.
|
|
40
|
+
const GETDATA_MAX_RETRIES = 4;
|
|
41
|
+
const GETDATA_RETRY_DELAYS = [15_000, 30_000, 60_000, 120_000]; // 15s, 30s, 60s, 120s
|
|
42
|
+
const GETDATA_REENQUEUE_DELAY_MS = 60_000; // Re-add to queue after 60s if all retries fail
|
|
37
43
|
const URGENT_CONCURRENCY = 2; // Slots when urgent chunk is active (bandwidth focus)
|
|
38
44
|
const FETCH_TIMEOUT_MS = 600_000; // 10 minutes — 100MB chunk at ~2 Mbps
|
|
39
45
|
const HEAD_TIMEOUT_MS = 15_000; // 15 seconds for HEAD requests
|
|
@@ -127,6 +133,8 @@ export class DownloadTask {
|
|
|
127
133
|
this.blob = null;
|
|
128
134
|
this._parentFile = null;
|
|
129
135
|
this._priority = PRIORITY.normal;
|
|
136
|
+
// Widget data (getData) uses longer retry backoff — CMS "cache not ready" is transient
|
|
137
|
+
this.isGetData = fileInfo.isGetData || false;
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
getUrl() {
|
|
@@ -144,7 +152,9 @@ export class DownloadTask {
|
|
|
144
152
|
headers['Range'] = `bytes=${this.rangeStart}-${this.rangeEnd}`;
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
|
|
155
|
+
const maxRetries = this.isGetData ? GETDATA_MAX_RETRIES : MAX_RETRIES;
|
|
156
|
+
|
|
157
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
148
158
|
const ac = new AbortController();
|
|
149
159
|
const timer = setTimeout(() => ac.abort(), FETCH_TIMEOUT_MS);
|
|
150
160
|
try {
|
|
@@ -163,10 +173,12 @@ export class DownloadTask {
|
|
|
163
173
|
|
|
164
174
|
} catch (error) {
|
|
165
175
|
const msg = ac.signal.aborted ? `Timeout after ${FETCH_TIMEOUT_MS / 1000}s` : error.message;
|
|
166
|
-
if (attempt <
|
|
167
|
-
const delay =
|
|
176
|
+
if (attempt < maxRetries) {
|
|
177
|
+
const delay = this.isGetData
|
|
178
|
+
? GETDATA_RETRY_DELAYS[attempt - 1]
|
|
179
|
+
: RETRY_DELAY_MS * attempt;
|
|
168
180
|
const chunkLabel = this.chunkIndex != null ? ` chunk ${this.chunkIndex}` : '';
|
|
169
|
-
log.warn(`[DownloadTask] ${this.fileInfo.type}/${this.fileInfo.id}${chunkLabel} attempt ${attempt}/${
|
|
181
|
+
log.warn(`[DownloadTask] ${this.fileInfo.type}/${this.fileInfo.id}${chunkLabel} attempt ${attempt}/${maxRetries} failed: ${msg}. Retrying in ${delay / 1000}s...`);
|
|
170
182
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
171
183
|
} else {
|
|
172
184
|
this.state = 'failed';
|
|
@@ -859,6 +871,22 @@ export class DownloadQueue {
|
|
|
859
871
|
this.running--;
|
|
860
872
|
task._parentFile._runningCount--;
|
|
861
873
|
this._activeTasks = this._activeTasks.filter(t => t !== task);
|
|
874
|
+
|
|
875
|
+
// getData (widget data): defer re-enqueue instead of permanent failure.
|
|
876
|
+
// CMS "cache not ready" resolves when the XTR task runs (30-120s).
|
|
877
|
+
if (task.isGetData) {
|
|
878
|
+
log.warn(`[DownloadQueue] getData ${key} failed all retries, scheduling re-enqueue in ${GETDATA_REENQUEUE_DELAY_MS / 1000}s`);
|
|
879
|
+
setTimeout(() => {
|
|
880
|
+
task.state = 'pending';
|
|
881
|
+
task._parentFile.state = 'downloading';
|
|
882
|
+
this.queue.push(task);
|
|
883
|
+
log.info(`[DownloadQueue] getData ${key} re-enqueued for retry`);
|
|
884
|
+
this.processQueue();
|
|
885
|
+
}, GETDATA_REENQUEUE_DELAY_MS);
|
|
886
|
+
this.processQueue();
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
|
|
862
890
|
this.processQueue();
|
|
863
891
|
task._parentFile.onTaskFailed(task, err);
|
|
864
892
|
});
|
package/src/widget-html.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { createLogger } from '@xiboplayer/utils';
|
|
16
|
+
import { rewriteUrlForProxy } from './download-manager.js';
|
|
16
17
|
|
|
17
18
|
const log = createLogger('Cache');
|
|
18
19
|
const CACHE_NAME = 'xibo-media-v1';
|
|
@@ -110,7 +111,7 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
|
|
|
110
111
|
if (existing) return; // Already cached
|
|
111
112
|
|
|
112
113
|
try {
|
|
113
|
-
const resp = await fetch(originalUrl);
|
|
114
|
+
const resp = await fetch(rewriteUrlForProxy(originalUrl));
|
|
114
115
|
if (!resp.ok) {
|
|
115
116
|
log.warn(`Failed to fetch static resource: ${filename} (HTTP ${resp.status})`);
|
|
116
117
|
return;
|
|
@@ -149,7 +150,7 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
|
|
|
149
150
|
if (existingFont) return; // Already cached (by SW or previous widget)
|
|
150
151
|
|
|
151
152
|
try {
|
|
152
|
-
const fontResp = await fetch(fontUrl);
|
|
153
|
+
const fontResp = await fetch(rewriteUrlForProxy(fontUrl));
|
|
153
154
|
if (!fontResp.ok) {
|
|
154
155
|
log.warn(`Failed to fetch font: ${fontFile} (HTTP ${fontResp.status})`);
|
|
155
156
|
return;
|