@xiboplayer/cache 0.2.0 → 0.3.1

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/README.md CHANGED
@@ -1,14 +1,16 @@
1
- # @xiboplayer/cache Documentation
1
+ # @xiboplayer/cache
2
2
 
3
3
  **Offline caching and download management with parallel chunk downloads.**
4
4
 
5
5
  ## Overview
6
6
 
7
- The `@xiboplayer/cache` package provides:
7
+ Manages media downloads and offline storage for Xibo players:
8
8
 
9
- - **CacheManager** - IndexedDB-based media storage
10
- - **CacheProxy** - Service Worker integration
11
- - **DownloadManager** - Parallel chunk downloads (4x faster)
9
+ - **Parallel chunk downloads** large files (100MB+) split into configurable chunks, downloaded concurrently
10
+ - **Header+trailer first** MP4 moov atom fetched first for instant playback start before full download
11
+ - **MD5 verification** integrity checking with CRC32-based skip optimization
12
+ - **Download queue** — flat queue with barriers for layout-ordered downloading
13
+ - **CacheProxy** — browser-side proxy that communicates with a Service Worker backend
12
14
 
13
15
  ## Installation
14
16
 
@@ -19,100 +21,30 @@ npm install @xiboplayer/cache
19
21
  ## Usage
20
22
 
21
23
  ```javascript
22
- import { CacheManager, DownloadManager } from '@xiboplayer/cache';
24
+ import { CacheProxy } from '@xiboplayer/cache';
23
25
 
24
- // Initialize cache
25
- const cache = new CacheManager();
26
- await cache.initialize();
26
+ const cache = new CacheProxy();
27
+ await cache.init();
27
28
 
28
- // Download with parallel chunks
29
- const downloader = new DownloadManager(cache);
30
- await downloader.downloadFile(url, { chunkSize: 1024 * 1024 });
29
+ // Request downloads (delegated to Service Worker)
30
+ await cache.requestDownload({ layoutOrder, files });
31
31
 
32
- // Retrieve from cache
33
- const blob = await cache.get(url);
32
+ // Check if a file is cached
33
+ const isCached = await cache.has(fileId);
34
34
  ```
35
35
 
36
- ## Features
36
+ ## Exports
37
37
 
38
- ### Parallel Chunk Downloads
39
-
40
- Downloads large files in 4 concurrent chunks (configurable), achieving 2-4x speed improvement over sequential downloads.
41
-
42
- ```javascript
43
- const CONCURRENT_CHUNKS = 4; // Adjust 2-6 based on network
44
- ```
45
-
46
- ### Cache Validation
47
-
48
- Automatically validates cached entries:
49
- - Content-Type verification
50
- - Size validation (> 100 bytes)
51
- - Corrupted entry detection
52
-
53
- ### Blob URL Lifecycle
54
-
55
- Proper blob URL management prevents memory leaks:
56
- - Layout-scoped tracking
57
- - Automatic revocation on layout switch
58
- - Media URL cleanup
59
-
60
- ## API Reference
61
-
62
- ### CacheManager
63
-
64
- ```javascript
65
- class CacheManager {
66
- async initialize()
67
- async get(key)
68
- async set(key, blob)
69
- async has(key)
70
- async delete(key)
71
- async clear()
72
- async getSize()
73
- }
74
- ```
75
-
76
- ### DownloadManager
77
-
78
- ```javascript
79
- class DownloadManager {
80
- constructor(cacheManager, options)
81
- async downloadFile(url, options)
82
- async downloadBatch(urls)
83
- getProgress(url)
84
- }
85
- ```
86
-
87
- ### CacheProxy
88
-
89
- ```javascript
90
- class CacheProxy {
91
- constructor(cacheManager)
92
- register(serviceWorkerUrl)
93
- unregister()
94
- update()
95
- }
96
- ```
97
-
98
- ## Performance
99
-
100
- | Operation | Time |
101
- |-----------|------|
102
- | 1GB file download | 1-2 min (vs 5 min sequential) |
103
- | Cache lookup | <10ms |
104
- | Cache write | <50ms |
38
+ | Export | Description |
39
+ |--------|-------------|
40
+ | `CacheProxy` | Browser-side proxy communicating with SW backend |
41
+ | `DownloadManager` | Core download queue with barrier-based ordering |
105
42
 
106
43
  ## Dependencies
107
44
 
108
- - `@xiboplayer/utils` - Logger, EventEmitter
109
-
110
- ## Related Packages
111
-
112
- - [@xiboplayer/core](../../core/docs/) - Player core
113
- - [@xiboplayer/sw](../../sw/docs/) - Service Worker toolkit
45
+ - `@xiboplayer/utils` logger, events
46
+ - `spark-md5` — MD5 hashing for file verification
114
47
 
115
48
  ---
116
49
 
117
- **Package Version**: 1.0.0
118
- **Last Updated**: 2026-02-10
50
+ **Part of the [XiboPlayer SDK](https://github.com/xibo-players/xiboplayer)** | [MCP Server](https://github.com/xibo-players/xiboplayer/tree/main/mcp-server) for AI-assisted development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiboplayer/cache",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
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.2.0"
15
+ "@xiboplayer/utils": "0.3.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "vitest": "^2.0.0",
@@ -170,14 +170,19 @@ class ServiceWorkerBackend extends EventEmitter {
170
170
 
171
171
  /**
172
172
  * Request downloads from Service Worker (non-blocking)
173
- * @param {Array} files - Array of { id, type, path, md5 }
173
+ * @param {Object|Array} payload - Either { layouts: [{ layoutId, mediaFiles }] } or flat Array of files
174
174
  * @returns {Promise<void>}
175
175
  */
176
- async requestDownload(files) {
176
+ async requestDownload(payload) {
177
177
  if (!this.controller) {
178
178
  throw new Error('Service Worker not available');
179
179
  }
180
180
 
181
+ // Support both grouped and flat payload (backward compat)
182
+ const data = Array.isArray(payload)
183
+ ? { files: payload }
184
+ : payload;
185
+
181
186
  return new Promise((resolve, reject) => {
182
187
  const messageChannel = new MessageChannel();
183
188
 
@@ -195,7 +200,7 @@ class ServiceWorkerBackend extends EventEmitter {
195
200
  this.controller.postMessage(
196
201
  {
197
202
  type: 'DOWNLOAD_FILES',
198
- data: { files }
203
+ data
199
204
  },
200
205
  [messageChannel.port2]
201
206
  );
package/src/cache.test.js CHANGED
@@ -217,7 +217,7 @@ describe('CacheManager', () => {
217
217
  beforeEach(async () => {
218
218
  await manager.init();
219
219
 
220
- // Helper: create a mock blob with arrayBuffer() support
220
+ // Helper: create a mock blob with arrayBuffer()/stream() support
221
221
  function createMockBlob(content, type) {
222
222
  const blob = new Blob([content], { type });
223
223
  // Polyfill arrayBuffer() for jsdom environments that lack it
@@ -230,6 +230,15 @@ describe('CacheManager', () => {
230
230
  });
231
231
  };
232
232
  }
233
+ // Polyfill stream() for jsdom environments that lack it
234
+ if (!blob.stream) {
235
+ blob.stream = () => new ReadableStream({
236
+ start(controller) {
237
+ controller.enqueue(new TextEncoder().encode(content));
238
+ controller.close();
239
+ }
240
+ });
241
+ }
233
242
  return blob;
234
243
  }
235
244
 
@@ -516,7 +525,8 @@ describe('CacheManager', () => {
516
525
 
517
526
  const retrieved = await manager.getCachedFile('media', '1');
518
527
 
519
- expect(retrieved).toBeInstanceOf(Blob);
528
+ expect(retrieved).toBeTruthy();
529
+ expect(retrieved.size).toBeGreaterThan(0);
520
530
  });
521
531
 
522
532
  it('should return null for non-cached file', async () => {