unified-video-framework 1.4.371 → 1.4.373
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 +1 -1
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +6 -0
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.d.ts +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.d.ts.map +1 -1
- package/packages/web/dist/ads/GoogleAdsManager.js +18 -3
- package/packages/web/dist/ads/GoogleAdsManager.js.map +1 -1
- package/packages/web/dist/chapters/CreditsButtonController.js +1 -1
- package/packages/web/dist/chapters/CreditsButtonController.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerView.js +19 -1
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +6 -0
- package/packages/web/src/ads/GoogleAdsManager.ts +86 -60
- package/packages/web/src/chapters/CreditsButtonController.ts +1 -1
- package/packages/web/src/react/WebPlayerView.tsx +23 -1
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
export interface GoogleAdsConfig {
|
|
14
14
|
// Ad tag URL (VAST/VMAP)
|
|
15
15
|
adTagUrl: string;
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Optional: Specific ad break times (for mid-rolls)
|
|
18
18
|
// If not provided, uses VMAP schedule from ad server
|
|
19
19
|
midrollTimes?: number[]; // e.g., [30, 60, 120] = ads at 30s, 60s, 120s
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
// Companion ad containers
|
|
22
22
|
companionAdSlots?: Array<{
|
|
23
23
|
containerId: string; // HTML element ID
|
|
24
24
|
width: number;
|
|
25
25
|
height: number;
|
|
26
26
|
}>;
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Callbacks
|
|
29
29
|
onAdStart?: () => void;
|
|
30
30
|
onAdEnd?: () => void;
|
|
@@ -48,11 +48,11 @@ export class GoogleAdsManager {
|
|
|
48
48
|
this.video = video;
|
|
49
49
|
this.adContainer = adContainer;
|
|
50
50
|
this.config = config;
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
// Add focus handler to resume ads after click-through
|
|
53
53
|
this.setupFocusHandler();
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
/**
|
|
57
57
|
* Setup focus handler to resume ads when window regains focus
|
|
58
58
|
*/
|
|
@@ -66,7 +66,7 @@ export class GoogleAdsManager {
|
|
|
66
66
|
setTimeout(() => this.resumeAdPlayback(), 200);
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
// Also check visibility change (tab switch)
|
|
71
71
|
document.addEventListener('visibilitychange', () => {
|
|
72
72
|
if (!document.hidden && this.isAdPlaying) {
|
|
@@ -113,30 +113,30 @@ export class GoogleAdsManager {
|
|
|
113
113
|
*/
|
|
114
114
|
private setupAdsLoader(): void {
|
|
115
115
|
const google = (window as any).google;
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
// Create ad display container
|
|
118
118
|
this.adDisplayContainer = new google.ima.AdDisplayContainer(
|
|
119
119
|
this.adContainer,
|
|
120
120
|
this.video
|
|
121
121
|
);
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
// Create ads loader
|
|
124
124
|
this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer);
|
|
125
|
-
|
|
125
|
+
|
|
126
126
|
// Register for ads loaded event
|
|
127
127
|
this.adsLoader.addEventListener(
|
|
128
128
|
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
|
|
129
129
|
(event: any) => this.onAdsManagerLoaded(event),
|
|
130
130
|
false
|
|
131
131
|
);
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
// Register for error event
|
|
134
134
|
this.adsLoader.addEventListener(
|
|
135
135
|
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
136
136
|
(event: any) => this.onAdError(event),
|
|
137
137
|
false
|
|
138
138
|
);
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
// Signal when video content completes (for post-rolls)
|
|
141
141
|
this.video.addEventListener('ended', () => {
|
|
142
142
|
if (!this.isAdPlaying) {
|
|
@@ -150,17 +150,17 @@ export class GoogleAdsManager {
|
|
|
150
150
|
*/
|
|
151
151
|
requestAds(): void {
|
|
152
152
|
const google = (window as any).google;
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
try {
|
|
155
155
|
const adsRequest = new google.ima.AdsRequest();
|
|
156
156
|
adsRequest.adTagUrl = this.config.adTagUrl;
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
// Set video dimensions
|
|
159
159
|
adsRequest.linearAdSlotWidth = this.video.clientWidth;
|
|
160
160
|
adsRequest.linearAdSlotHeight = this.video.clientHeight;
|
|
161
161
|
adsRequest.nonLinearAdSlotWidth = this.video.clientWidth;
|
|
162
162
|
adsRequest.nonLinearAdSlotHeight = Math.floor(this.video.clientHeight / 3);
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
// Set companion ad slots if provided
|
|
165
165
|
if (this.config.companionAdSlots && this.config.companionAdSlots.length > 0) {
|
|
166
166
|
const companionAdSlots = this.config.companionAdSlots.map(slot => {
|
|
@@ -169,7 +169,7 @@ export class GoogleAdsManager {
|
|
|
169
169
|
adsRequest.setAdWillAutoPlay(true);
|
|
170
170
|
adsRequest.setAdWillPlayMuted(false);
|
|
171
171
|
}
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
// Request ads
|
|
174
174
|
this.adsLoader.requestAds(adsRequest);
|
|
175
175
|
} catch (error) {
|
|
@@ -194,16 +194,16 @@ export class GoogleAdsManager {
|
|
|
194
194
|
*/
|
|
195
195
|
private onAdsManagerLoaded(event: any): void {
|
|
196
196
|
const google = (window as any).google;
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
// Setup ads rendering settings
|
|
199
199
|
const adsRenderingSettings = new google.ima.AdsRenderingSettings();
|
|
200
200
|
adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
|
|
201
201
|
adsRenderingSettings.enablePreloading = true;
|
|
202
202
|
adsRenderingSettings.mute = false; // ✅ Enable audio for ads
|
|
203
|
-
|
|
203
|
+
|
|
204
204
|
// Get the ads manager
|
|
205
205
|
this.adsManager = event.getAdsManager(this.video, adsRenderingSettings);
|
|
206
|
-
|
|
206
|
+
|
|
207
207
|
// Extract cue points (ad break times) from VMAP/ad server
|
|
208
208
|
try {
|
|
209
209
|
const cuePoints = this.adsManager.getCuePoints();
|
|
@@ -215,9 +215,9 @@ export class GoogleAdsManager {
|
|
|
215
215
|
if (time === -1) return -1; // Post-roll (will be converted to video duration later)
|
|
216
216
|
return time; // Mid-roll at specific time
|
|
217
217
|
});
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
console.log('📍 Ad cue points detected (pre/mid/post):', allCuePoints);
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
// Notify callback with all cue points
|
|
222
222
|
if (this.config.onAdCuePoints) {
|
|
223
223
|
this.config.onAdCuePoints(allCuePoints);
|
|
@@ -226,10 +226,10 @@ export class GoogleAdsManager {
|
|
|
226
226
|
} catch (error) {
|
|
227
227
|
console.warn('Could not extract ad cue points:', error);
|
|
228
228
|
}
|
|
229
|
-
|
|
229
|
+
|
|
230
230
|
// Setup ads manager event listeners
|
|
231
231
|
this.setupAdsManagerListeners();
|
|
232
|
-
|
|
232
|
+
|
|
233
233
|
try {
|
|
234
234
|
// Initialize ads manager
|
|
235
235
|
this.adsManager.init(
|
|
@@ -237,12 +237,12 @@ export class GoogleAdsManager {
|
|
|
237
237
|
this.video.clientHeight,
|
|
238
238
|
google.ima.ViewMode.NORMAL
|
|
239
239
|
);
|
|
240
|
-
|
|
240
|
+
|
|
241
241
|
// Start ads
|
|
242
242
|
this.adsManager.start();
|
|
243
243
|
} catch (error) {
|
|
244
244
|
console.error('Error starting ads:', error);
|
|
245
|
-
this.video.play().catch(() => {});
|
|
245
|
+
this.video.play().catch(() => { });
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -251,7 +251,7 @@ export class GoogleAdsManager {
|
|
|
251
251
|
*/
|
|
252
252
|
private setupAdsManagerListeners(): void {
|
|
253
253
|
const google = (window as any).google;
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
// Content pause - ad is about to play
|
|
256
256
|
this.adsManager.addEventListener(
|
|
257
257
|
google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
|
|
@@ -259,21 +259,45 @@ export class GoogleAdsManager {
|
|
|
259
259
|
console.log('Ad: Content paused');
|
|
260
260
|
this.isAdPlaying = true;
|
|
261
261
|
this.video.pause();
|
|
262
|
+
|
|
263
|
+
// Strict enforcement: Prevent video from playing during ads
|
|
264
|
+
const preventPlayDuringAd = (e: Event) => {
|
|
265
|
+
if (this.isAdPlaying) {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
this.video.pause();
|
|
268
|
+
console.warn('Blocked video play attempt during ad');
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Add play event listener to block any play attempts
|
|
273
|
+
this.video.addEventListener('play', preventPlayDuringAd);
|
|
274
|
+
|
|
275
|
+
// Store cleanup function to remove listener later
|
|
276
|
+
(this.video as any).__adPlayBlocker = preventPlayDuringAd;
|
|
277
|
+
|
|
262
278
|
this.config.onAdStart?.();
|
|
263
279
|
}
|
|
264
280
|
);
|
|
265
|
-
|
|
281
|
+
|
|
266
282
|
// Content resume - ad finished
|
|
267
283
|
this.adsManager.addEventListener(
|
|
268
284
|
google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
|
|
269
285
|
() => {
|
|
270
286
|
console.log('Ad: Content resume');
|
|
271
287
|
this.isAdPlaying = false;
|
|
288
|
+
|
|
289
|
+
// Remove play blocker
|
|
290
|
+
const preventPlayDuringAd = (this.video as any).__adPlayBlocker;
|
|
291
|
+
if (preventPlayDuringAd) {
|
|
292
|
+
this.video.removeEventListener('play', preventPlayDuringAd);
|
|
293
|
+
delete (this.video as any).__adPlayBlocker;
|
|
294
|
+
}
|
|
295
|
+
|
|
272
296
|
this.config.onAdEnd?.();
|
|
273
|
-
this.video.play().catch(() => {});
|
|
297
|
+
this.video.play().catch(() => { });
|
|
274
298
|
}
|
|
275
299
|
);
|
|
276
|
-
|
|
300
|
+
|
|
277
301
|
// Ad started
|
|
278
302
|
this.adsManager.addEventListener(
|
|
279
303
|
google.ima.AdEvent.Type.STARTED,
|
|
@@ -285,19 +309,19 @@ export class GoogleAdsManager {
|
|
|
285
309
|
skippable: ad.getSkipTimeOffset() !== -1,
|
|
286
310
|
title: ad.getTitle(),
|
|
287
311
|
});
|
|
288
|
-
|
|
312
|
+
|
|
289
313
|
// Sync ads mute state with video element
|
|
290
314
|
this.isMuted = this.video.muted;
|
|
291
315
|
console.log(`Ad started - video.muted=${this.video.muted}, isMuted=${this.isMuted}`);
|
|
292
|
-
|
|
316
|
+
|
|
293
317
|
// Check if video is actually muted and show unmute button only then
|
|
294
318
|
if (this.isMuted) {
|
|
295
319
|
this.showUnmuteButton();
|
|
296
320
|
}
|
|
297
|
-
|
|
321
|
+
|
|
298
322
|
}
|
|
299
323
|
);
|
|
300
|
-
|
|
324
|
+
|
|
301
325
|
// Ad completed
|
|
302
326
|
this.adsManager.addEventListener(
|
|
303
327
|
google.ima.AdEvent.Type.COMPLETE,
|
|
@@ -305,7 +329,7 @@ export class GoogleAdsManager {
|
|
|
305
329
|
console.log('Ad completed');
|
|
306
330
|
}
|
|
307
331
|
);
|
|
308
|
-
|
|
332
|
+
|
|
309
333
|
// All ads completed
|
|
310
334
|
this.adsManager.addEventListener(
|
|
311
335
|
google.ima.AdEvent.Type.ALL_ADS_COMPLETED,
|
|
@@ -314,13 +338,13 @@ export class GoogleAdsManager {
|
|
|
314
338
|
this.config.onAllAdsComplete?.();
|
|
315
339
|
}
|
|
316
340
|
);
|
|
317
|
-
|
|
341
|
+
|
|
318
342
|
// Ad error
|
|
319
343
|
this.adsManager.addEventListener(
|
|
320
344
|
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
321
345
|
(event: any) => this.onAdError(event)
|
|
322
346
|
);
|
|
323
|
-
|
|
347
|
+
|
|
324
348
|
// Ad skipped
|
|
325
349
|
this.adsManager.addEventListener(
|
|
326
350
|
google.ima.AdEvent.Type.SKIPPED,
|
|
@@ -328,7 +352,7 @@ export class GoogleAdsManager {
|
|
|
328
352
|
console.log('Ad skipped by user');
|
|
329
353
|
}
|
|
330
354
|
);
|
|
331
|
-
|
|
355
|
+
|
|
332
356
|
// Ad paused (by click-through)
|
|
333
357
|
this.adsManager.addEventListener(
|
|
334
358
|
google.ima.AdEvent.Type.PAUSED,
|
|
@@ -337,7 +361,7 @@ export class GoogleAdsManager {
|
|
|
337
361
|
// Don't mark as not playing - keep isAdPlaying true so we can resume
|
|
338
362
|
}
|
|
339
363
|
);
|
|
340
|
-
|
|
364
|
+
|
|
341
365
|
// Ad playing (resume after pause)
|
|
342
366
|
this.adsManager.addEventListener(
|
|
343
367
|
google.ima.AdEvent.Type.PLAYING,
|
|
@@ -353,17 +377,17 @@ export class GoogleAdsManager {
|
|
|
353
377
|
private onAdError(event: any): void {
|
|
354
378
|
const error = event.getError?.();
|
|
355
379
|
console.error('Ad error:', error?.getMessage?.() || error);
|
|
356
|
-
|
|
380
|
+
|
|
357
381
|
this.config.onAdError?.(error);
|
|
358
|
-
|
|
382
|
+
|
|
359
383
|
// Destroy ads manager on error
|
|
360
384
|
if (this.adsManager) {
|
|
361
385
|
this.adsManager.destroy();
|
|
362
386
|
}
|
|
363
|
-
|
|
387
|
+
|
|
364
388
|
// Resume content playback
|
|
365
389
|
this.isAdPlaying = false;
|
|
366
|
-
this.video.play().catch(() => {});
|
|
390
|
+
this.video.play().catch(() => { });
|
|
367
391
|
}
|
|
368
392
|
|
|
369
393
|
/**
|
|
@@ -396,10 +420,12 @@ export class GoogleAdsManager {
|
|
|
396
420
|
/**
|
|
397
421
|
* Resize ads
|
|
398
422
|
*/
|
|
399
|
-
resize(width: number, height: number): void {
|
|
423
|
+
resize(width: number, height: number, viewMode?: any): void {
|
|
400
424
|
const google = (window as any).google;
|
|
401
|
-
if (this.adsManager) {
|
|
402
|
-
|
|
425
|
+
if (this.adsManager && google && google.ima) {
|
|
426
|
+
// Use provided viewMode or default to NORMAL
|
|
427
|
+
const mode = viewMode || google.ima.ViewMode.NORMAL;
|
|
428
|
+
this.adsManager.resize(width, height, mode);
|
|
403
429
|
}
|
|
404
430
|
}
|
|
405
431
|
|
|
@@ -427,7 +453,7 @@ export class GoogleAdsManager {
|
|
|
427
453
|
if (this.unmuteButton) {
|
|
428
454
|
this.unmuteButton.remove();
|
|
429
455
|
}
|
|
430
|
-
|
|
456
|
+
|
|
431
457
|
// Create unmute button (matching WebPlayer.ts style)
|
|
432
458
|
this.unmuteButton = document.createElement('button');
|
|
433
459
|
this.unmuteButton.id = 'ad-unmute-btn';
|
|
@@ -439,13 +465,13 @@ export class GoogleAdsManager {
|
|
|
439
465
|
</svg>
|
|
440
466
|
<span class="uvf-unmute-text">Tap to unmute</span>
|
|
441
467
|
`;
|
|
442
|
-
|
|
468
|
+
|
|
443
469
|
// Click handler to unmute
|
|
444
470
|
this.unmuteButton.addEventListener('click', (e) => {
|
|
445
471
|
e.stopPropagation();
|
|
446
472
|
this.toggleAdMute();
|
|
447
473
|
});
|
|
448
|
-
|
|
474
|
+
|
|
449
475
|
// Add styles if not already added
|
|
450
476
|
if (!document.getElementById('uvf-unmute-styles')) {
|
|
451
477
|
const style = document.createElement('style');
|
|
@@ -517,33 +543,33 @@ export class GoogleAdsManager {
|
|
|
517
543
|
`;
|
|
518
544
|
document.head.appendChild(style);
|
|
519
545
|
}
|
|
520
|
-
|
|
546
|
+
|
|
521
547
|
// Add to ad container
|
|
522
548
|
this.adContainer.appendChild(this.unmuteButton);
|
|
523
549
|
console.log('Unmute button displayed (matching player style)');
|
|
524
550
|
}
|
|
525
|
-
|
|
551
|
+
|
|
526
552
|
/**
|
|
527
553
|
* Toggle ad mute state
|
|
528
554
|
*/
|
|
529
555
|
private toggleAdMute(): void {
|
|
530
556
|
this.isMuted = !this.isMuted;
|
|
531
|
-
|
|
557
|
+
|
|
532
558
|
if (this.adsManager) {
|
|
533
559
|
this.adsManager.setVolume(this.isMuted ? 0 : 1);
|
|
534
560
|
console.log(`Ad ${this.isMuted ? 'muted' : 'unmuted'}`);
|
|
535
561
|
}
|
|
536
|
-
|
|
562
|
+
|
|
537
563
|
// Sync mute state with video element
|
|
538
564
|
this.video.muted = this.isMuted;
|
|
539
|
-
|
|
565
|
+
|
|
540
566
|
// Hide button if unmuted
|
|
541
567
|
if (!this.isMuted && this.unmuteButton) {
|
|
542
568
|
this.unmuteButton.remove();
|
|
543
569
|
this.unmuteButton = null;
|
|
544
570
|
}
|
|
545
571
|
}
|
|
546
|
-
|
|
572
|
+
|
|
547
573
|
/**
|
|
548
574
|
* Resume ad playback after click-through
|
|
549
575
|
*/
|
|
@@ -553,9 +579,9 @@ export class GoogleAdsManager {
|
|
|
553
579
|
if (!this.adsManager || !this.isAdPlaying) {
|
|
554
580
|
return;
|
|
555
581
|
}
|
|
556
|
-
|
|
582
|
+
|
|
557
583
|
console.log('Attempting to resume ad playback...');
|
|
558
|
-
|
|
584
|
+
|
|
559
585
|
// Try multiple resume methods
|
|
560
586
|
try {
|
|
561
587
|
this.adsManager.resume();
|
|
@@ -563,7 +589,7 @@ export class GoogleAdsManager {
|
|
|
563
589
|
} catch (e) {
|
|
564
590
|
console.warn('resume() failed:', e);
|
|
565
591
|
}
|
|
566
|
-
|
|
592
|
+
|
|
567
593
|
// Also try playing the video element directly
|
|
568
594
|
if (this.video) {
|
|
569
595
|
try {
|
|
@@ -584,7 +610,7 @@ export class GoogleAdsManager {
|
|
|
584
610
|
console.error('Error resuming ad playback:', error);
|
|
585
611
|
}
|
|
586
612
|
}
|
|
587
|
-
|
|
613
|
+
|
|
588
614
|
/**
|
|
589
615
|
* Hide unmute button
|
|
590
616
|
*/
|
|
@@ -600,17 +626,17 @@ export class GoogleAdsManager {
|
|
|
600
626
|
*/
|
|
601
627
|
destroy(): void {
|
|
602
628
|
this.hideUnmuteButton();
|
|
603
|
-
|
|
629
|
+
|
|
604
630
|
if (this.adsManager) {
|
|
605
631
|
this.adsManager.destroy();
|
|
606
632
|
this.adsManager = null;
|
|
607
633
|
}
|
|
608
|
-
|
|
634
|
+
|
|
609
635
|
if (this.adsLoader) {
|
|
610
636
|
this.adsLoader.destroy();
|
|
611
637
|
this.adsLoader = null;
|
|
612
638
|
}
|
|
613
|
-
|
|
639
|
+
|
|
614
640
|
this.isAdPlaying = false;
|
|
615
641
|
}
|
|
616
642
|
}
|
|
@@ -1166,7 +1166,29 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
|
|
|
1166
1166
|
(player as any).on('onBuffering', props.onBuffering);
|
|
1167
1167
|
}
|
|
1168
1168
|
if (props.onFullscreenChange && typeof (player as any).on === 'function') {
|
|
1169
|
-
(player as any).on('onFullscreenChanged',
|
|
1169
|
+
(player as any).on('onFullscreenChanged', (isFullscreen: boolean) => {
|
|
1170
|
+
// Resize Google Ads if active
|
|
1171
|
+
if (adsManagerRef.current) {
|
|
1172
|
+
const google = (window as any).google;
|
|
1173
|
+
if (google && google.ima) {
|
|
1174
|
+
const container = containerRef.current;
|
|
1175
|
+
const width = isFullscreen
|
|
1176
|
+
? window.screen.width
|
|
1177
|
+
: (container?.clientWidth || window.innerWidth);
|
|
1178
|
+
const height = isFullscreen
|
|
1179
|
+
? window.screen.height
|
|
1180
|
+
: (container?.clientHeight || window.innerHeight);
|
|
1181
|
+
const viewMode = isFullscreen
|
|
1182
|
+
? google.ima.ViewMode.FULLSCREEN
|
|
1183
|
+
: google.ima.ViewMode.NORMAL;
|
|
1184
|
+
|
|
1185
|
+
adsManagerRef.current.resize(width, height, viewMode);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// Call user's fullscreen callback
|
|
1190
|
+
props.onFullscreenChange?.(isFullscreen);
|
|
1191
|
+
});
|
|
1170
1192
|
}
|
|
1171
1193
|
if (props.onPictureInPictureChange && typeof (player as any).on === 'function') {
|
|
1172
1194
|
(player as any).on('onPictureInPicturechange', props.onPictureInPictureChange);
|