@xiboplayer/renderer 0.6.7 → 0.6.9
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 +4 -4
- package/src/layout-pool.js +5 -0
- package/src/renderer-lite.js +43 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/renderer",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
4
4
|
"description": "RendererLite - Fast, efficient XLF layout rendering engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"nanoevents": "^9.1.0",
|
|
15
15
|
"pdfjs-dist": "^4.10.38",
|
|
16
|
-
"@xiboplayer/cache": "0.6.
|
|
17
|
-
"@xiboplayer/
|
|
18
|
-
"@xiboplayer/
|
|
16
|
+
"@xiboplayer/cache": "0.6.9",
|
|
17
|
+
"@xiboplayer/utils": "0.6.9",
|
|
18
|
+
"@xiboplayer/schedule": "0.6.9"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"vitest": "^2.0.0",
|
package/src/layout-pool.js
CHANGED
|
@@ -193,6 +193,11 @@ export class LayoutPool {
|
|
|
193
193
|
a.load();
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
// Destroy PDF documents and release GPU canvas backing stores
|
|
197
|
+
container.querySelectorAll('.pdf-widget').forEach(el => {
|
|
198
|
+
if (el._pdfDestroy) el._pdfDestroy();
|
|
199
|
+
});
|
|
200
|
+
|
|
196
201
|
if (videoCount > 0) {
|
|
197
202
|
log.info(`Released ${videoCount} video(s)${hlsCount ? ` (${hlsCount} HLS)` : ''}`);
|
|
198
203
|
}
|
package/src/renderer-lite.js
CHANGED
|
@@ -1291,6 +1291,14 @@ export class RendererLite {
|
|
|
1291
1291
|
// Emit layout start event
|
|
1292
1292
|
this.emit('layoutStart', layoutId, layout);
|
|
1293
1293
|
|
|
1294
|
+
// Report calculated duration so the schedule queue/timeline uses it
|
|
1295
|
+
// instead of the 60s default. For layouts with unprobed videos, this
|
|
1296
|
+
// is an estimate that will be corrected by updateLayoutDuration().
|
|
1297
|
+
if (layout.duration > 0) {
|
|
1298
|
+
const final_ = !this._hasUnprobedVideos();
|
|
1299
|
+
this.emit('layoutDurationUpdated', layoutId, layout.duration, final_);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1294
1302
|
// Start all regions (except drawers — they're action-triggered)
|
|
1295
1303
|
for (const [regionId, region] of this.regions) {
|
|
1296
1304
|
if (region.isDrawer) continue;
|
|
@@ -1681,6 +1689,11 @@ export class RendererLite {
|
|
|
1681
1689
|
element.style.opacity = '1';
|
|
1682
1690
|
}
|
|
1683
1691
|
|
|
1692
|
+
// Resume PDF page cycling if this widget was previously paused
|
|
1693
|
+
if (element._pdfResume) {
|
|
1694
|
+
element._pdfResume();
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1684
1697
|
// Start audio overlays attached to this widget
|
|
1685
1698
|
this._startAudioOverlays(widget);
|
|
1686
1699
|
|
|
@@ -2087,7 +2100,9 @@ export class RendererLite {
|
|
|
2087
2100
|
if (!region) return;
|
|
2088
2101
|
|
|
2089
2102
|
const { widget, animPromise } = this._hideWidget(region, widgetIndex);
|
|
2090
|
-
|
|
2103
|
+
// Emit widgetEnd immediately — don't wait for exit animation.
|
|
2104
|
+
// If we await animPromise first, a pool eviction can remove the DOM element,
|
|
2105
|
+
// causing the animation's onfinish to never fire and widgetEnd to be lost.
|
|
2091
2106
|
if (widget) {
|
|
2092
2107
|
this.emit('widgetEnd', {
|
|
2093
2108
|
widgetId: widget.id, regionId, layoutId: this.currentLayoutId,
|
|
@@ -2096,6 +2111,7 @@ export class RendererLite {
|
|
|
2096
2111
|
enableStat: widget.enableStat
|
|
2097
2112
|
});
|
|
2098
2113
|
}
|
|
2114
|
+
if (animPromise) await animPromise;
|
|
2099
2115
|
}
|
|
2100
2116
|
|
|
2101
2117
|
/**
|
|
@@ -2605,7 +2621,7 @@ export class RendererLite {
|
|
|
2605
2621
|
|
|
2606
2622
|
await cyclePage();
|
|
2607
2623
|
|
|
2608
|
-
//
|
|
2624
|
+
// Pause: stop page cycling (called by _hideWidget during region cycling / replay)
|
|
2609
2625
|
container._pdfCleanup = () => {
|
|
2610
2626
|
stopped = true;
|
|
2611
2627
|
if (cycleTimer) clearTimeout(cycleTimer);
|
|
@@ -2614,7 +2630,18 @@ export class RendererLite {
|
|
|
2614
2630
|
activeRenderTask.cancel();
|
|
2615
2631
|
activeRenderTask = null;
|
|
2616
2632
|
}
|
|
2617
|
-
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2635
|
+
// Resume: restart page cycling from page 1 (called by _showWidget on reuse)
|
|
2636
|
+
container._pdfResume = () => {
|
|
2637
|
+
stopped = false;
|
|
2638
|
+
currentPage = 1;
|
|
2639
|
+
cyclePage();
|
|
2640
|
+
};
|
|
2641
|
+
|
|
2642
|
+
// Destroy: release GPU + PDF resources (called on element removal / eviction)
|
|
2643
|
+
container._pdfDestroy = () => {
|
|
2644
|
+
container._pdfCleanup();
|
|
2618
2645
|
canvas.width = 0;
|
|
2619
2646
|
canvas.height = 0;
|
|
2620
2647
|
pdf.destroy();
|
|
@@ -2961,12 +2988,16 @@ export class RendererLite {
|
|
|
2961
2988
|
}
|
|
2962
2989
|
|
|
2963
2990
|
const oldLayoutId = this.currentLayoutId;
|
|
2991
|
+
const alreadyEmittedEnd = this.layoutEndEmitted;
|
|
2964
2992
|
|
|
2965
2993
|
this.layoutEndEmitted = false;
|
|
2966
|
-
|
|
2967
|
-
|
|
2994
|
+
// Keep currentLayout/currentLayoutId until widgets are stopped,
|
|
2995
|
+
// so widgetEnd events carry the correct layoutId (not null).
|
|
2968
2996
|
|
|
2969
2997
|
if (oldLayoutId && this.layoutPool.has(oldLayoutId)) {
|
|
2998
|
+
// Stop all widgets before evicting (symmetric widgetEnd events)
|
|
2999
|
+
this._clearRegionTimers(this.regions);
|
|
3000
|
+
this._stopAllRegionWidgets(this.regions, (rid, idx) => this.stopWidget(rid, idx));
|
|
2970
3001
|
// Old layout was preloaded — evict from pool (safe: removes its wrapper div)
|
|
2971
3002
|
this.layoutPool.evict(oldLayoutId);
|
|
2972
3003
|
} else {
|
|
@@ -3000,6 +3031,9 @@ export class RendererLite {
|
|
|
3000
3031
|
}
|
|
3001
3032
|
}
|
|
3002
3033
|
|
|
3034
|
+
// Now safe to clear old layout state — widgets have been stopped with correct layoutId
|
|
3035
|
+
this.currentLayout = null;
|
|
3036
|
+
this.currentLayoutId = null;
|
|
3003
3037
|
this.regions.clear();
|
|
3004
3038
|
|
|
3005
3039
|
// ── Activate preloaded layout ──
|
|
@@ -3015,7 +3049,8 @@ export class RendererLite {
|
|
|
3015
3049
|
// Emit layoutEnd for old layout AFTER setting new currentLayoutId —
|
|
3016
3050
|
// the listener guard in main.ts sees the new layout already playing
|
|
3017
3051
|
// and skips advance, while stats/tracking still run.
|
|
3018
|
-
if (
|
|
3052
|
+
// Skip if the layout timer already emitted layoutEnd (avoids double stats).
|
|
3053
|
+
if (oldLayoutId && !alreadyEmittedEnd) {
|
|
3019
3054
|
this.emit('layoutEnd', oldLayoutId);
|
|
3020
3055
|
}
|
|
3021
3056
|
|
|
@@ -3354,12 +3389,13 @@ export class RendererLite {
|
|
|
3354
3389
|
if (!region) return;
|
|
3355
3390
|
|
|
3356
3391
|
const { widget, animPromise } = this._hideWidget(region, widgetIndex);
|
|
3357
|
-
|
|
3392
|
+
// Emit immediately — don't wait for exit animation (same fix as stopWidget)
|
|
3358
3393
|
if (widget) {
|
|
3359
3394
|
this.emit('overlayWidgetEnd', {
|
|
3360
3395
|
overlayId, widgetId: widget.id, regionId, type: widget.type
|
|
3361
3396
|
});
|
|
3362
3397
|
}
|
|
3398
|
+
if (animPromise) await animPromise;
|
|
3363
3399
|
}
|
|
3364
3400
|
|
|
3365
3401
|
/**
|