@xiboplayer/renderer 0.5.6 → 0.5.8

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.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "RendererLite - Fast, efficient XLF layout rendering engine",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -12,8 +12,8 @@
12
12
  "dependencies": {
13
13
  "nanoevents": "^9.1.0",
14
14
  "pdfjs-dist": "^4.10.38",
15
- "@xiboplayer/cache": "0.5.6",
16
- "@xiboplayer/utils": "0.5.6"
15
+ "@xiboplayer/utils": "0.5.8",
16
+ "@xiboplayer/cache": "0.5.8"
17
17
  },
18
18
  "devDependencies": {
19
19
  "vitest": "^2.0.0",
@@ -689,30 +689,33 @@ export class RendererLite {
689
689
  maxRegionDuration = Math.max(maxRegionDuration, regionDuration);
690
690
  }
691
691
 
692
- // If we calculated a different duration, update layout
693
- if (maxRegionDuration > 0 && maxRegionDuration !== this.currentLayout.duration) {
692
+ // If we calculated a LONGER duration, update layout.
693
+ // Never downgrade — widgets with useDuration=0 start at duration=0
694
+ // until loadedmetadata fires, so early calculations undercount.
695
+ if (maxRegionDuration > 0 && maxRegionDuration > this.currentLayout.duration) {
694
696
  const oldDuration = this.currentLayout.duration;
695
697
  this.currentLayout.duration = maxRegionDuration;
696
698
 
697
699
  this.log.info(`Layout duration updated: ${oldDuration}s → ${maxRegionDuration}s (based on video metadata)`);
698
700
  this.emit('layoutDurationUpdated', this.currentLayoutId, maxRegionDuration);
699
701
 
700
- // Reset layout timer with new durationbut only if a timer is already running.
702
+ // Reset layout timer with REMAINING timenot full duration.
701
703
  // If startLayoutTimerWhenReady() hasn't fired yet (still waiting for widgets),
702
704
  // it will pick up the updated duration when it starts the timer.
703
705
  if (this.layoutTimer) {
704
706
  clearTimeout(this.layoutTimer);
705
707
 
706
- const layoutDurationMs = this.currentLayout.duration * 1000;
708
+ const elapsed = Date.now() - (this._layoutTimerStartedAt || Date.now());
709
+ const remainingMs = Math.max(1000, this.currentLayout.duration * 1000 - elapsed);
707
710
  this.layoutTimer = setTimeout(() => {
708
711
  this.log.info(`Layout ${this.currentLayoutId} duration expired (${this.currentLayout.duration}s)`);
709
712
  if (this.currentLayoutId) {
710
713
  this.layoutEndEmitted = true;
711
714
  this.emit('layoutEnd', this.currentLayoutId);
712
715
  }
713
- }, layoutDurationMs);
716
+ }, remainingMs);
714
717
 
715
- this.log.info(`Layout timer reset to ${this.currentLayout.duration}s`);
718
+ this.log.info(`Layout timer adjusted to ${(remainingMs / 1000).toFixed(1)}s remaining (elapsed ${(elapsed / 1000).toFixed(1)}s of ${this.currentLayout.duration}s)`);
716
719
  } else {
717
720
  this.log.info(`Layout duration updated to ${maxRegionDuration}s (timer not yet started, will use new value)`);
718
721
  }
@@ -1941,17 +1944,28 @@ export class RendererLite {
1941
1944
  }
1942
1945
 
1943
1946
  // Detect video duration for dynamic layout timing (when useDuration=0)
1947
+ // Capture the layout ID at creation time — if the layout changes before
1948
+ // loadedmetadata fires (e.g. video was preloaded for next layout), we must
1949
+ // NOT update the current layout's duration with a different layout's video.
1950
+ const createdForLayoutId = this.currentLayoutId;
1944
1951
  video.addEventListener('loadedmetadata', () => {
1945
1952
  const videoDuration = Math.floor(video.duration);
1946
1953
  this.log.info(`Video ${fileId} duration detected: ${videoDuration}s`);
1947
1954
 
1948
- // If widget has useDuration=0, update widget duration with actual video length
1955
+ // Always update widget duration it's the widget's own data, safe
1956
+ // even if this video was preloaded for a different layout.
1949
1957
  if (widget.duration === 0 || widget.useDuration === 0) {
1950
1958
  widget.duration = videoDuration;
1951
1959
  this.log.info(`Updated widget ${widget.id} duration to ${videoDuration}s (useDuration=0)`);
1952
1960
 
1953
- // Recalculate layout duration if needed
1954
- this.updateLayoutDuration();
1961
+ // Only recalculate current layout's timer if this video belongs to it.
1962
+ // Preloaded layouts will pick up the corrected widget.duration when
1963
+ // they start playing (via updateLayoutDuration() in swapToPreloadedLayout).
1964
+ if (this.currentLayoutId === createdForLayoutId) {
1965
+ this.updateLayoutDuration();
1966
+ } else {
1967
+ this.log.info(`Video ${fileId} duration set but layout timer not updated (preloaded for layout ${createdForLayoutId}, current is ${this.currentLayoutId})`);
1968
+ }
1955
1969
  }
1956
1970
  });
1957
1971
 
@@ -2086,6 +2100,7 @@ export class RendererLite {
2086
2100
  });
2087
2101
 
2088
2102
  // Detect audio duration for dynamic layout timing (when useDuration=0)
2103
+ const audioCreatedForLayoutId = this.currentLayoutId;
2089
2104
  audio.addEventListener('loadedmetadata', () => {
2090
2105
  const audioDuration = Math.floor(audio.duration);
2091
2106
  this.log.info(`Audio ${fileId} duration detected: ${audioDuration}s`);
@@ -2093,7 +2108,12 @@ export class RendererLite {
2093
2108
  if (widget.duration === 0 || widget.useDuration === 0) {
2094
2109
  widget.duration = audioDuration;
2095
2110
  this.log.info(`Updated widget ${widget.id} duration to ${audioDuration}s (useDuration=0)`);
2096
- this.updateLayoutDuration();
2111
+
2112
+ if (this.currentLayoutId === audioCreatedForLayoutId) {
2113
+ this.updateLayoutDuration();
2114
+ } else {
2115
+ this.log.info(`Audio ${fileId} duration set but layout timer not updated (preloaded for layout ${audioCreatedForLayoutId}, current is ${this.currentLayoutId})`);
2116
+ }
2097
2117
  }
2098
2118
  });
2099
2119