@xiboplayer/renderer 0.7.3 → 0.7.5

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.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "RendererLite - Fast, efficient XLF layout rendering engine",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -12,9 +12,9 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "pdfjs-dist": "^4.10.38",
15
- "@xiboplayer/schedule": "0.7.3",
16
- "@xiboplayer/utils": "0.7.3",
17
- "@xiboplayer/cache": "0.7.3"
15
+ "@xiboplayer/cache": "0.7.5",
16
+ "@xiboplayer/schedule": "0.7.5",
17
+ "@xiboplayer/utils": "0.7.5"
18
18
  },
19
19
  "devDependencies": {
20
20
  "jsdom": "^25.0.1",
@@ -1517,11 +1517,10 @@ export class RendererLite {
1517
1517
  el.play().catch(() => {});
1518
1518
  };
1519
1519
  el.addEventListener('seeked', playAfterSeek);
1520
- // Fallback: if seeked doesn't fire (already at 0), try play directly
1521
- if (el.currentTime === 0 && el.readyState >= 2) {
1522
- el.removeEventListener('seeked', playAfterSeek);
1523
- el.play().catch(() => {});
1524
- }
1520
+ // Always call play() for preloaded-then-paused videos, seeked may not
1521
+ // fire (currentTime already 0) and readyState may be < 2 (not buffered yet).
1522
+ // play() handles both cases: if not ready, it queues; if ready, it plays.
1523
+ el.play().catch(() => {});
1525
1524
  }
1526
1525
 
1527
1526
  /**
@@ -2300,10 +2299,9 @@ export class RendererLite {
2300
2299
  }
2301
2300
 
2302
2301
  // Detect video duration for dynamic layout timing (when useDuration=0)
2303
- // Capture the layout ID at creation time — if the layout changes before
2304
- // loadedmetadata fires (e.g. video was preloaded for next layout), we must
2305
- // NOT update the current layout's duration with a different layout's video.
2306
- const createdForLayoutId = this.currentLayoutId;
2302
+ // Capture the layout ID at creation time — during preload, _preloadingLayoutId
2303
+ // is the target layout (currentLayoutId is still the playing layout).
2304
+ const createdForLayoutId = this._preloadingLayoutId || this.currentLayoutId;
2307
2305
  const onLoadedMetadata = () => {
2308
2306
  const videoDuration = video.duration;
2309
2307
  this.log.info(`Video ${storedAs} duration detected: ${videoDuration}s`);
@@ -2464,7 +2462,7 @@ export class RendererLite {
2464
2462
  audio.addEventListener('ended', onAudioEnded);
2465
2463
 
2466
2464
  // Detect audio duration for dynamic layout timing (when useDuration=0)
2467
- const audioCreatedForLayoutId = this.currentLayoutId;
2465
+ const audioCreatedForLayoutId = this._preloadingLayoutId || this.currentLayoutId;
2468
2466
  const onAudioLoadedMetadata = () => {
2469
2467
  const audioDuration = Math.floor(audio.duration);
2470
2468
  this.log.info(`Audio ${storedAs} duration detected: ${audioDuration}s`);
@@ -2917,4 +2917,55 @@ describe('RendererLite', () => {
2917
2917
  vi.useRealTimers();
2918
2918
  });
2919
2919
  });
2920
+
2921
+ // ── Video layout ID tracking during preload ──────────────────────
2922
+ // Regression test: createdForLayoutId must use _preloadingLayoutId
2923
+ // during preload, not currentLayoutId (which is the *playing* layout).
2924
+ // Without this, video duration updates for preloaded layouts are
2925
+ // rejected, causing layouts to play with wrong (10s) duration.
2926
+
2927
+ describe('Video createdForLayoutId during preload', () => {
2928
+ it('should capture _preloadingLayoutId for video elements created during preload', () => {
2929
+ renderer.currentLayoutId = 100; // currently playing
2930
+ renderer._preloadingLayoutId = 200; // preloading next
2931
+
2932
+ // The fix: _preloadingLayoutId || currentLayoutId should give 200
2933
+ const capturedId = renderer._preloadingLayoutId || renderer.currentLayoutId;
2934
+ expect(capturedId).toBe(200);
2935
+ });
2936
+
2937
+ it('should fall back to currentLayoutId when not preloading', () => {
2938
+ renderer.currentLayoutId = 100;
2939
+ renderer._preloadingLayoutId = null;
2940
+
2941
+ const capturedId = renderer._preloadingLayoutId || renderer.currentLayoutId;
2942
+ expect(capturedId).toBe(100);
2943
+ });
2944
+
2945
+ it('should allow duration update when preloaded layout becomes current', () => {
2946
+ // Simulate: video created during preload of layout 200
2947
+ renderer._preloadingLayoutId = 200;
2948
+ const createdForLayoutId = renderer._preloadingLayoutId || renderer.currentLayoutId;
2949
+
2950
+ // Now layout 200 swaps in
2951
+ renderer.currentLayoutId = 200;
2952
+ renderer._preloadingLayoutId = null;
2953
+
2954
+ // Duration update check should pass
2955
+ expect(renderer.currentLayoutId === createdForLayoutId).toBe(true);
2956
+ });
2957
+
2958
+ it('should reject duration update when a different layout is current', () => {
2959
+ // Video created during preload of layout 200
2960
+ renderer._preloadingLayoutId = 200;
2961
+ const createdForLayoutId = renderer._preloadingLayoutId || renderer.currentLayoutId;
2962
+
2963
+ // But layout 300 swaps in instead (e.g., schedule changed)
2964
+ renderer.currentLayoutId = 300;
2965
+ renderer._preloadingLayoutId = null;
2966
+
2967
+ // Duration update should be rejected — wrong layout
2968
+ expect(renderer.currentLayoutId === createdForLayoutId).toBe(false);
2969
+ });
2970
+ });
2920
2971
  });