@xiboplayer/renderer 0.3.5 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiboplayer/renderer",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "RendererLite - Fast, efficient XLF layout rendering engine",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -12,7 +12,8 @@
12
12
  "dependencies": {
13
13
  "nanoevents": "^9.1.0",
14
14
  "pdfjs-dist": "^4.10.38",
15
- "@xiboplayer/utils": "0.3.5"
15
+ "@xiboplayer/cache": "0.3.7",
16
+ "@xiboplayer/utils": "0.3.7"
16
17
  },
17
18
  "devDependencies": {
18
19
  "vitest": "^2.0.0",
package/src/layout.js CHANGED
@@ -3,6 +3,8 @@
3
3
  * Based on arexibo layout.rs
4
4
  */
5
5
 
6
+ import { cacheWidgetHtml } from '@xiboplayer/cache';
7
+
6
8
  export class LayoutTranslator {
7
9
  constructor(xmds) {
8
10
  this.xmds = xmds;
@@ -11,7 +13,7 @@ export class LayoutTranslator {
11
13
  /**
12
14
  * Translate XLF XML to playable HTML
13
15
  */
14
- async translateXLF(layoutId, xlfXml, cacheManager) {
16
+ async translateXLF(layoutId, xlfXml) {
15
17
  const parser = new DOMParser();
16
18
  const doc = parser.parseFromString(xlfXml, 'text/xml');
17
19
 
@@ -26,7 +28,7 @@ export class LayoutTranslator {
26
28
 
27
29
  const regions = [];
28
30
  for (const regionEl of doc.querySelectorAll('region')) {
29
- regions.push(await this.translateRegion(layoutId, regionEl, cacheManager));
31
+ regions.push(await this.translateRegion(layoutId, regionEl));
30
32
  }
31
33
 
32
34
  return this.generateHTML(width, height, bgcolor, regions);
@@ -35,7 +37,7 @@ export class LayoutTranslator {
35
37
  /**
36
38
  * Translate a single region
37
39
  */
38
- async translateRegion(layoutId, regionEl, cacheManager) {
40
+ async translateRegion(layoutId, regionEl) {
39
41
  const id = regionEl.getAttribute('id');
40
42
  const width = parseInt(regionEl.getAttribute('width'));
41
43
  const height = parseInt(regionEl.getAttribute('height'));
@@ -45,7 +47,7 @@ export class LayoutTranslator {
45
47
 
46
48
  const media = [];
47
49
  for (const mediaEl of regionEl.querySelectorAll('media')) {
48
- media.push(await this.translateMedia(layoutId, id, mediaEl, cacheManager));
50
+ media.push(await this.translateMedia(layoutId, id, mediaEl));
49
51
  }
50
52
 
51
53
  return {
@@ -62,7 +64,7 @@ export class LayoutTranslator {
62
64
  /**
63
65
  * Translate a single media item
64
66
  */
65
- async translateMedia(layoutId, regionId, mediaEl, cacheManager) {
67
+ async translateMedia(layoutId, regionId, mediaEl) {
66
68
  const type = mediaEl.getAttribute('type');
67
69
  const duration = parseInt(mediaEl.getAttribute('duration') || '10');
68
70
  const id = mediaEl.getAttribute('id');
@@ -127,7 +129,7 @@ export class LayoutTranslator {
127
129
  console.log(`[Layout] Got resource HTML (${raw.length} chars)`);
128
130
 
129
131
  // Store widget HTML in cache and save cache key for iframe src generation
130
- const widgetCacheKey = await cacheManager.cacheWidgetHtml(layoutId, regionId, id, raw);
132
+ const widgetCacheKey = await cacheWidgetHtml(layoutId, regionId, id, raw);
131
133
  options.widgetCacheKey = widgetCacheKey;
132
134
 
133
135
  // Success - break retry loop
@@ -150,10 +152,11 @@ export class LayoutTranslator {
150
152
  if (!raw && lastError) {
151
153
  console.warn(`[Layout] All retries failed, checking for cached widget HTML...`);
152
154
 
153
- // Try to get cached widget HTML
155
+ // Try to get cached widget HTML directly from Cache API
154
156
  try {
155
157
  const cachedKey = `/cache/widget/${layoutId}/${regionId}/${id}.html`;
156
- const cached = await cacheManager.cache.match(new Request(window.location.origin + '/player' + cachedKey));
158
+ const cache = await caches.open('xibo-media-v1');
159
+ const cached = await cache.match(new Request(window.location.origin + '/player' + cachedKey));
157
160
 
158
161
  if (cached) {
159
162
  raw = await cached.text();
@@ -378,6 +378,7 @@ export class RendererLite {
378
378
  duration: layoutDurationAttr ? parseInt(layoutDurationAttr) : 0, // 0 = calculate from widgets
379
379
  bgcolor: layoutEl.getAttribute('bgcolor') || '#000000',
380
380
  background: layoutEl.getAttribute('background') || null, // Background image fileId
381
+ enableStat: layoutEl.getAttribute('enableStat') !== '0', // absent or "1" = enabled
381
382
  regions: []
382
383
  };
383
384
 
@@ -495,6 +496,7 @@ export class RendererLite {
495
496
  useDuration, // Whether to use specified duration (1) or media length (0)
496
497
  id,
497
498
  fileId, // Media library file ID for cache lookup
499
+ enableStat: mediaEl.getAttribute('enableStat') !== '0', // absent or "1" = enabled
498
500
  options,
499
501
  raw,
500
502
  transitions,
@@ -1318,7 +1320,8 @@ export class RendererLite {
1318
1320
  this.emit('widgetStart', {
1319
1321
  widgetId: widget.id, regionId, layoutId: this.currentLayoutId,
1320
1322
  mediaId: parseInt(widget.fileId || widget.id) || null,
1321
- type: widget.type, duration: widget.duration
1323
+ type: widget.type, duration: widget.duration,
1324
+ enableStat: widget.enableStat
1322
1325
  });
1323
1326
  }
1324
1327
  } catch (error) {
@@ -1342,7 +1345,8 @@ export class RendererLite {
1342
1345
  this.emit('widgetEnd', {
1343
1346
  widgetId: widget.id, regionId, layoutId: this.currentLayoutId,
1344
1347
  mediaId: parseInt(widget.fileId || widget.id) || null,
1345
- type: widget.type
1348
+ type: widget.type,
1349
+ enableStat: widget.enableStat
1346
1350
  });
1347
1351
  }
1348
1352
  }
@@ -157,6 +157,86 @@ describe('RendererLite', () => {
157
157
  });
158
158
  });
159
159
 
160
+ describe('enableStat parsing', () => {
161
+ it('should parse enableStat="1" as true on layout', () => {
162
+ const xlf = `
163
+ <layout enableStat="1">
164
+ <region id="r1">
165
+ <media id="m1" type="image" duration="10"></media>
166
+ </region>
167
+ </layout>
168
+ `;
169
+ const layout = renderer.parseXlf(xlf);
170
+ expect(layout.enableStat).toBe(true);
171
+ });
172
+
173
+ it('should parse enableStat="0" as false on layout', () => {
174
+ const xlf = `
175
+ <layout enableStat="0">
176
+ <region id="r1">
177
+ <media id="m1" type="image" duration="10"></media>
178
+ </region>
179
+ </layout>
180
+ `;
181
+ const layout = renderer.parseXlf(xlf);
182
+ expect(layout.enableStat).toBe(false);
183
+ });
184
+
185
+ it('should default enableStat to true when absent on layout', () => {
186
+ const xlf = `
187
+ <layout>
188
+ <region id="r1">
189
+ <media id="m1" type="image" duration="10"></media>
190
+ </region>
191
+ </layout>
192
+ `;
193
+ const layout = renderer.parseXlf(xlf);
194
+ expect(layout.enableStat).toBe(true);
195
+ });
196
+
197
+ it('should parse enableStat="0" as false on widget', () => {
198
+ const xlf = `
199
+ <layout>
200
+ <region id="r1">
201
+ <media id="m1" type="image" duration="10" enableStat="0">
202
+ <options><uri>test.png</uri></options>
203
+ </media>
204
+ </region>
205
+ </layout>
206
+ `;
207
+ const layout = renderer.parseXlf(xlf);
208
+ expect(layout.regions[0].widgets[0].enableStat).toBe(false);
209
+ });
210
+
211
+ it('should parse enableStat="1" as true on widget', () => {
212
+ const xlf = `
213
+ <layout>
214
+ <region id="r1">
215
+ <media id="m1" type="image" duration="10" enableStat="1">
216
+ <options><uri>test.png</uri></options>
217
+ </media>
218
+ </region>
219
+ </layout>
220
+ `;
221
+ const layout = renderer.parseXlf(xlf);
222
+ expect(layout.regions[0].widgets[0].enableStat).toBe(true);
223
+ });
224
+
225
+ it('should default enableStat to true when absent on widget', () => {
226
+ const xlf = `
227
+ <layout>
228
+ <region id="r1">
229
+ <media id="m1" type="image" duration="10">
230
+ <options><uri>test.png</uri></options>
231
+ </media>
232
+ </region>
233
+ </layout>
234
+ `;
235
+ const layout = renderer.parseXlf(xlf);
236
+ expect(layout.regions[0].widgets[0].enableStat).toBe(true);
237
+ });
238
+ });
239
+
160
240
  describe('Region Creation', () => {
161
241
  it('should create region element with correct positioning', async () => {
162
242
  const regionConfig = {