stormcloud-video-player 0.2.5 → 0.2.6
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/README.md +15 -16
- package/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +53 -363
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +0 -30
- package/lib/index.d.ts +0 -30
- package/lib/index.js +53 -363
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +0 -11
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +0 -1
- package/lib/players/HlsPlayer.cjs +0 -11
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +0 -11
- package/lib/players/index.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +0 -11
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
- package/lib/types-DOcCdwQI.d.cts +0 -78
package/lib/index.d.cts
CHANGED
|
@@ -28,15 +28,6 @@ interface StormcloudVideoPlayerConfig {
|
|
|
28
28
|
onFullscreenToggle?: () => void;
|
|
29
29
|
onControlClick?: () => void;
|
|
30
30
|
licenseKey?: string;
|
|
31
|
-
adBreakGapToleranceMs?: number;
|
|
32
|
-
maxAdsPerBreak?: number;
|
|
33
|
-
minAdDurationMs?: number;
|
|
34
|
-
enableAdPreloading?: boolean;
|
|
35
|
-
}
|
|
36
|
-
interface AdInfo {
|
|
37
|
-
duration: number;
|
|
38
|
-
vastTagUrl: string;
|
|
39
|
-
isPreloaded: boolean;
|
|
40
31
|
}
|
|
41
32
|
interface ClientInfo {
|
|
42
33
|
brand: string;
|
|
@@ -101,9 +92,6 @@ declare class StormcloudVideoPlayer {
|
|
|
101
92
|
private totalAdsInBreak;
|
|
102
93
|
private showAds;
|
|
103
94
|
private isLiveStream;
|
|
104
|
-
private preloadedAdInfo;
|
|
105
|
-
private currentAdBreakTargetDurationMs;
|
|
106
|
-
private cumulativeAdDurationMs;
|
|
107
95
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
108
96
|
load(): Promise<void>;
|
|
109
97
|
private attach;
|
|
@@ -125,20 +113,9 @@ declare class StormcloudVideoPlayer {
|
|
|
125
113
|
getTotalAdsInBreak(): number;
|
|
126
114
|
isAdPlaying(): boolean;
|
|
127
115
|
isShowingAds(): boolean;
|
|
128
|
-
getAdBreakStats(): {
|
|
129
|
-
isInAdBreak: boolean;
|
|
130
|
-
currentAdIndex: number;
|
|
131
|
-
totalAdsInBreak: number;
|
|
132
|
-
targetDurationMs: number | undefined;
|
|
133
|
-
cumulativeDurationMs: number;
|
|
134
|
-
estimatedFillRate: number | undefined;
|
|
135
|
-
remainingDurationMs: number | undefined;
|
|
136
|
-
};
|
|
137
|
-
getPreloadedAdInfo(): AdInfo[];
|
|
138
116
|
getStreamType(): "hls" | "other";
|
|
139
117
|
shouldShowNativeControls(): boolean;
|
|
140
118
|
private shouldContinueLiveStreamDuringAds;
|
|
141
|
-
loadDefaultVastFromAdstorm(adstormApiUrl: string, params?: Record<string, string>): Promise<void>;
|
|
142
119
|
private handleAdStart;
|
|
143
120
|
private findCurrentOrNextBreak;
|
|
144
121
|
private onTimeUpdate;
|
|
@@ -151,16 +128,9 @@ declare class StormcloudVideoPlayer {
|
|
|
151
128
|
private updatePtsDrift;
|
|
152
129
|
private playSingleAd;
|
|
153
130
|
private handleAdFailure;
|
|
154
|
-
private endAdBreak;
|
|
155
|
-
private shouldContinueAdBreak;
|
|
156
|
-
private canRequestMoreAds;
|
|
157
|
-
private requestAdditionalAds;
|
|
158
131
|
private startAdFailsafeTimer;
|
|
159
132
|
private clearAdFailsafeTimer;
|
|
160
133
|
private selectVastTagsForBreak;
|
|
161
|
-
private buildAdQueueForDuration;
|
|
162
|
-
private getEstimatedAdDuration;
|
|
163
|
-
private getAdInfoForQueue;
|
|
164
134
|
private getRemainingAdMs;
|
|
165
135
|
private findBreakForTime;
|
|
166
136
|
toggleMute(): void;
|
package/lib/index.d.ts
CHANGED
|
@@ -28,15 +28,6 @@ interface StormcloudVideoPlayerConfig {
|
|
|
28
28
|
onFullscreenToggle?: () => void;
|
|
29
29
|
onControlClick?: () => void;
|
|
30
30
|
licenseKey?: string;
|
|
31
|
-
adBreakGapToleranceMs?: number;
|
|
32
|
-
maxAdsPerBreak?: number;
|
|
33
|
-
minAdDurationMs?: number;
|
|
34
|
-
enableAdPreloading?: boolean;
|
|
35
|
-
}
|
|
36
|
-
interface AdInfo {
|
|
37
|
-
duration: number;
|
|
38
|
-
vastTagUrl: string;
|
|
39
|
-
isPreloaded: boolean;
|
|
40
31
|
}
|
|
41
32
|
interface ClientInfo {
|
|
42
33
|
brand: string;
|
|
@@ -101,9 +92,6 @@ declare class StormcloudVideoPlayer {
|
|
|
101
92
|
private totalAdsInBreak;
|
|
102
93
|
private showAds;
|
|
103
94
|
private isLiveStream;
|
|
104
|
-
private preloadedAdInfo;
|
|
105
|
-
private currentAdBreakTargetDurationMs;
|
|
106
|
-
private cumulativeAdDurationMs;
|
|
107
95
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
108
96
|
load(): Promise<void>;
|
|
109
97
|
private attach;
|
|
@@ -125,20 +113,9 @@ declare class StormcloudVideoPlayer {
|
|
|
125
113
|
getTotalAdsInBreak(): number;
|
|
126
114
|
isAdPlaying(): boolean;
|
|
127
115
|
isShowingAds(): boolean;
|
|
128
|
-
getAdBreakStats(): {
|
|
129
|
-
isInAdBreak: boolean;
|
|
130
|
-
currentAdIndex: number;
|
|
131
|
-
totalAdsInBreak: number;
|
|
132
|
-
targetDurationMs: number | undefined;
|
|
133
|
-
cumulativeDurationMs: number;
|
|
134
|
-
estimatedFillRate: number | undefined;
|
|
135
|
-
remainingDurationMs: number | undefined;
|
|
136
|
-
};
|
|
137
|
-
getPreloadedAdInfo(): AdInfo[];
|
|
138
116
|
getStreamType(): "hls" | "other";
|
|
139
117
|
shouldShowNativeControls(): boolean;
|
|
140
118
|
private shouldContinueLiveStreamDuringAds;
|
|
141
|
-
loadDefaultVastFromAdstorm(adstormApiUrl: string, params?: Record<string, string>): Promise<void>;
|
|
142
119
|
private handleAdStart;
|
|
143
120
|
private findCurrentOrNextBreak;
|
|
144
121
|
private onTimeUpdate;
|
|
@@ -151,16 +128,9 @@ declare class StormcloudVideoPlayer {
|
|
|
151
128
|
private updatePtsDrift;
|
|
152
129
|
private playSingleAd;
|
|
153
130
|
private handleAdFailure;
|
|
154
|
-
private endAdBreak;
|
|
155
|
-
private shouldContinueAdBreak;
|
|
156
|
-
private canRequestMoreAds;
|
|
157
|
-
private requestAdditionalAds;
|
|
158
131
|
private startAdFailsafeTimer;
|
|
159
132
|
private clearAdFailsafeTimer;
|
|
160
133
|
private selectVastTagsForBreak;
|
|
161
|
-
private buildAdQueueForDuration;
|
|
162
|
-
private getEstimatedAdDuration;
|
|
163
|
-
private getAdInfoForQueue;
|
|
164
134
|
private getRemainingAdMs;
|
|
165
135
|
private findBreakForTime;
|
|
166
136
|
toggleMute(): void;
|
package/lib/index.js
CHANGED
|
@@ -68,8 +68,6 @@ function createImaController(video, options) {
|
|
|
68
68
|
let adsLoadedPromise;
|
|
69
69
|
let adsLoadedResolve;
|
|
70
70
|
let adsLoadedReject;
|
|
71
|
-
let currentAdDuration = 0;
|
|
72
|
-
let preloadedAds = [];
|
|
73
71
|
function makeAdsRequest(google, vastTagUrl) {
|
|
74
72
|
const adsRequest = new google.ima.AdsRequest();
|
|
75
73
|
adsRequest.adTagUrl = vastTagUrl;
|
|
@@ -176,7 +174,6 @@ function createImaController(video, options) {
|
|
|
176
174
|
} catch {
|
|
177
175
|
}
|
|
178
176
|
adPlaying = false;
|
|
179
|
-
currentAdDuration = 0;
|
|
180
177
|
video.muted = originalMutedState;
|
|
181
178
|
if (adContainerEl)
|
|
182
179
|
adContainerEl.style.pointerEvents = "none";
|
|
@@ -248,22 +245,9 @@ function createImaController(video, options) {
|
|
|
248
245
|
emit("content_resume");
|
|
249
246
|
}
|
|
250
247
|
);
|
|
251
|
-
adsManager.addEventListener(AdEvent.STARTED, (adEvent) => {
|
|
252
|
-
console.log("[IMA] Ad started");
|
|
253
|
-
try {
|
|
254
|
-
const ad = adEvent.getAd();
|
|
255
|
-
if (ad && ad.getDuration) {
|
|
256
|
-
currentAdDuration = ad.getDuration();
|
|
257
|
-
console.log(`[IMA] Ad duration: ${currentAdDuration}s`);
|
|
258
|
-
}
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.warn("[IMA] Could not get ad duration:", error);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
248
|
adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
|
|
264
249
|
console.log("[IMA] All ads completed");
|
|
265
250
|
adPlaying = false;
|
|
266
|
-
currentAdDuration = 0;
|
|
267
251
|
video.muted = originalMutedState;
|
|
268
252
|
if (adContainerEl) adContainerEl.style.pointerEvents = "none";
|
|
269
253
|
if (!options?.continueLiveStreamDuringAds) {
|
|
@@ -448,84 +432,6 @@ function createImaController(video, options) {
|
|
|
448
432
|
}
|
|
449
433
|
}
|
|
450
434
|
return 1;
|
|
451
|
-
},
|
|
452
|
-
getAdDuration() {
|
|
453
|
-
return currentAdDuration;
|
|
454
|
-
},
|
|
455
|
-
async preloadAds(vastTagUrls) {
|
|
456
|
-
console.log(`[IMA] Preloading ${vastTagUrls.length} ads`);
|
|
457
|
-
const adInfos = [];
|
|
458
|
-
for (const vastTagUrl of vastTagUrls) {
|
|
459
|
-
try {
|
|
460
|
-
await ensureImaLoaded();
|
|
461
|
-
const google = window.google;
|
|
462
|
-
const tempAdsLoader = new google.ima.AdsLoader(adDisplayContainer);
|
|
463
|
-
const adInfo = await new Promise((resolve, reject) => {
|
|
464
|
-
const timeout = setTimeout(() => {
|
|
465
|
-
reject(new Error("Preload timeout"));
|
|
466
|
-
}, 5e3);
|
|
467
|
-
tempAdsLoader.addEventListener(
|
|
468
|
-
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
|
|
469
|
-
(evt) => {
|
|
470
|
-
clearTimeout(timeout);
|
|
471
|
-
try {
|
|
472
|
-
const tempAdsManager = evt.getAdsManager(video);
|
|
473
|
-
let duration = 30;
|
|
474
|
-
try {
|
|
475
|
-
const ads = tempAdsManager.getCuePoints?.() || [];
|
|
476
|
-
if (ads.length > 0) {
|
|
477
|
-
duration = 15;
|
|
478
|
-
}
|
|
479
|
-
} catch {
|
|
480
|
-
}
|
|
481
|
-
tempAdsManager.destroy();
|
|
482
|
-
resolve({
|
|
483
|
-
duration,
|
|
484
|
-
vastTagUrl,
|
|
485
|
-
isPreloaded: true
|
|
486
|
-
});
|
|
487
|
-
} catch (error) {
|
|
488
|
-
clearTimeout(timeout);
|
|
489
|
-
reject(error);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
);
|
|
493
|
-
tempAdsLoader.addEventListener(
|
|
494
|
-
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
495
|
-
(errorEvent) => {
|
|
496
|
-
clearTimeout(timeout);
|
|
497
|
-
console.warn(
|
|
498
|
-
`[IMA] Preload error for ${vastTagUrl}:`,
|
|
499
|
-
errorEvent.getError()
|
|
500
|
-
);
|
|
501
|
-
resolve({
|
|
502
|
-
duration: 30,
|
|
503
|
-
vastTagUrl,
|
|
504
|
-
isPreloaded: false
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
);
|
|
508
|
-
const adsRequest = new google.ima.AdsRequest();
|
|
509
|
-
adsRequest.adTagUrl = vastTagUrl;
|
|
510
|
-
tempAdsLoader.requestAds(adsRequest);
|
|
511
|
-
});
|
|
512
|
-
adInfos.push(adInfo);
|
|
513
|
-
tempAdsLoader.destroy();
|
|
514
|
-
} catch (error) {
|
|
515
|
-
console.warn(`[IMA] Failed to preload ad ${vastTagUrl}:`, error);
|
|
516
|
-
adInfos.push({
|
|
517
|
-
duration: 30,
|
|
518
|
-
vastTagUrl,
|
|
519
|
-
isPreloaded: false
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
preloadedAds = adInfos;
|
|
524
|
-
console.log(
|
|
525
|
-
`[IMA] Preloaded ${adInfos.length} ads with total duration:`,
|
|
526
|
-
adInfos.reduce((sum, ad) => sum + ad.duration, 0)
|
|
527
|
-
);
|
|
528
|
-
return adInfos;
|
|
529
435
|
}
|
|
530
436
|
};
|
|
531
437
|
}
|
|
@@ -758,8 +664,6 @@ var StormcloudVideoPlayer = class {
|
|
|
758
664
|
this.totalAdsInBreak = 0;
|
|
759
665
|
this.showAds = false;
|
|
760
666
|
this.isLiveStream = false;
|
|
761
|
-
this.preloadedAdInfo = [];
|
|
762
|
-
this.cumulativeAdDurationMs = 0;
|
|
763
667
|
this.config = config;
|
|
764
668
|
this.video = config.videoElement;
|
|
765
669
|
this.ima = createImaController(this.video, {
|
|
@@ -931,28 +835,16 @@ var StormcloudVideoPlayer = class {
|
|
|
931
835
|
this.ima.initialize();
|
|
932
836
|
this.ima.on("all_ads_completed", () => {
|
|
933
837
|
if (!this.inAdBreak) return;
|
|
934
|
-
const actualAdDuration = this.ima.getAdDuration();
|
|
935
|
-
if (actualAdDuration > 0) {
|
|
936
|
-
this.cumulativeAdDurationMs += actualAdDuration * 1e3;
|
|
937
|
-
if (this.config.debugAdTiming) {
|
|
938
|
-
console.log(
|
|
939
|
-
`[StormcloudVideoPlayer] Ad completed. Duration: ${actualAdDuration}s, Cumulative: ${this.cumulativeAdDurationMs}ms`
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
838
|
const remaining = this.getRemainingAdMs();
|
|
944
|
-
|
|
945
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
839
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
946
840
|
const next = this.adPodQueue.shift();
|
|
947
841
|
this.currentAdIndex++;
|
|
948
842
|
this.playSingleAd(next).catch(() => {
|
|
949
843
|
});
|
|
950
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
951
|
-
this.requestAdditionalAds().catch(() => {
|
|
952
|
-
this.endAdBreak();
|
|
953
|
-
});
|
|
954
844
|
} else {
|
|
955
|
-
this.
|
|
845
|
+
this.currentAdIndex = 0;
|
|
846
|
+
this.totalAdsInBreak = 0;
|
|
847
|
+
this.showAds = false;
|
|
956
848
|
}
|
|
957
849
|
});
|
|
958
850
|
this.ima.on("ad_error", () => {
|
|
@@ -961,16 +853,11 @@ var StormcloudVideoPlayer = class {
|
|
|
961
853
|
}
|
|
962
854
|
if (!this.inAdBreak) return;
|
|
963
855
|
const remaining = this.getRemainingAdMs();
|
|
964
|
-
|
|
965
|
-
if (shouldContinue && this.adPodQueue.length > 0) {
|
|
856
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
966
857
|
const next = this.adPodQueue.shift();
|
|
967
858
|
this.currentAdIndex++;
|
|
968
859
|
this.playSingleAd(next).catch(() => {
|
|
969
860
|
});
|
|
970
|
-
} else if (shouldContinue && this.canRequestMoreAds()) {
|
|
971
|
-
this.requestAdditionalAds().catch(() => {
|
|
972
|
-
this.handleAdFailure();
|
|
973
|
-
});
|
|
974
861
|
} else {
|
|
975
862
|
this.handleAdFailure();
|
|
976
863
|
}
|
|
@@ -1461,25 +1348,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1461
1348
|
isShowingAds() {
|
|
1462
1349
|
return this.showAds;
|
|
1463
1350
|
}
|
|
1464
|
-
getAdBreakStats() {
|
|
1465
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs != null ? Math.max(
|
|
1466
|
-
0,
|
|
1467
|
-
this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs
|
|
1468
|
-
) : void 0;
|
|
1469
|
-
const estimatedFillRate = this.currentAdBreakTargetDurationMs != null && this.currentAdBreakTargetDurationMs > 0 ? this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100 : void 0;
|
|
1470
|
-
return {
|
|
1471
|
-
isInAdBreak: this.inAdBreak,
|
|
1472
|
-
currentAdIndex: this.currentAdIndex,
|
|
1473
|
-
totalAdsInBreak: this.totalAdsInBreak,
|
|
1474
|
-
targetDurationMs: this.currentAdBreakTargetDurationMs,
|
|
1475
|
-
cumulativeDurationMs: this.cumulativeAdDurationMs,
|
|
1476
|
-
estimatedFillRate,
|
|
1477
|
-
remainingDurationMs
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
getPreloadedAdInfo() {
|
|
1481
|
-
return [...this.preloadedAdInfo];
|
|
1482
|
-
}
|
|
1483
1351
|
getStreamType() {
|
|
1484
1352
|
const url = this.config.src.toLowerCase();
|
|
1485
1353
|
if (url.includes(".m3u8") || url.includes("/hls/") || url.includes("application/vnd.apple.mpegurl")) {
|
|
@@ -1503,70 +1371,59 @@ var StormcloudVideoPlayer = class {
|
|
|
1503
1371
|
}
|
|
1504
1372
|
return true;
|
|
1505
1373
|
}
|
|
1506
|
-
async
|
|
1507
|
-
const usp = new URLSearchParams(params || {});
|
|
1508
|
-
const url = `${adstormApiUrl}?${usp.toString()}`;
|
|
1509
|
-
const res = await fetch(url);
|
|
1510
|
-
if (!res.ok) throw new Error(`Failed to fetch adstorm ads: ${res.status}`);
|
|
1511
|
-
const data = await res.json();
|
|
1512
|
-
const tag = data?.adTagUrl || data?.vastTagUrl || data?.tagUrl;
|
|
1513
|
-
if (typeof tag === "string" && tag.length > 0) {
|
|
1514
|
-
this.apiVastTagUrl = tag;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
async handleAdStart(marker) {
|
|
1374
|
+
async handleAdStart(_marker) {
|
|
1518
1375
|
const scheduled = this.findCurrentOrNextBreak(
|
|
1519
1376
|
this.video.currentTime * 1e3
|
|
1520
1377
|
);
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
"[StormcloudVideoPlayer] Starting ad break with target duration:",
|
|
1533
|
-
{
|
|
1534
|
-
targetDurationMs,
|
|
1535
|
-
scte35Duration: marker.durationSeconds,
|
|
1536
|
-
scheduledDuration: scheduled?.durationMs
|
|
1378
|
+
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1379
|
+
let vastTagUrl;
|
|
1380
|
+
let adsNumber = 1;
|
|
1381
|
+
if (this.apiVastTagUrl) {
|
|
1382
|
+
vastTagUrl = this.apiVastTagUrl;
|
|
1383
|
+
if (this.vastConfig) {
|
|
1384
|
+
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1385
|
+
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1386
|
+
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1387
|
+
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1388
|
+
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1537
1389
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1390
|
+
}
|
|
1391
|
+
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
1392
|
+
this.currentAdIndex = 0;
|
|
1393
|
+
this.totalAdsInBreak = adsNumber;
|
|
1542
1394
|
if (this.config.debugAdTiming) {
|
|
1543
|
-
console.log(
|
|
1395
|
+
console.log(
|
|
1396
|
+
`[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
|
|
1397
|
+
vastTagUrl
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
} else if (tags && tags.length > 0) {
|
|
1401
|
+
vastTagUrl = tags[0];
|
|
1402
|
+
const rest = tags.slice(1);
|
|
1403
|
+
this.adPodQueue = rest;
|
|
1404
|
+
this.currentAdIndex = 0;
|
|
1405
|
+
this.totalAdsInBreak = tags.length;
|
|
1406
|
+
if (this.config.debugAdTiming) {
|
|
1407
|
+
console.log(
|
|
1408
|
+
"[StormcloudVideoPlayer] Using scheduled VAST tag:",
|
|
1409
|
+
vastTagUrl
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
} else {
|
|
1413
|
+
if (this.config.debugAdTiming) {
|
|
1414
|
+
console.log("[StormcloudVideoPlayer] No VAST tag available for ad");
|
|
1544
1415
|
}
|
|
1545
1416
|
return;
|
|
1546
1417
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
this.showAds = true;
|
|
1552
|
-
if (this.config.debugAdTiming) {
|
|
1553
|
-
const totalEstimatedDuration = this.preloadedAdInfo.reduce(
|
|
1554
|
-
(sum, ad) => sum + ad.duration,
|
|
1555
|
-
0
|
|
1556
|
-
);
|
|
1557
|
-
console.log("[StormcloudVideoPlayer] Ad queue built:", {
|
|
1558
|
-
totalAds: adQueue.length,
|
|
1559
|
-
estimatedTotalDuration: totalEstimatedDuration,
|
|
1560
|
-
targetDuration: targetDurationMs ? targetDurationMs / 1e3 : "unknown",
|
|
1561
|
-
fillRate: targetDurationMs ? totalEstimatedDuration * 1e3 / targetDurationMs : "unknown"
|
|
1562
|
-
});
|
|
1418
|
+
if (vastTagUrl) {
|
|
1419
|
+
this.showAds = true;
|
|
1420
|
+
this.currentAdIndex++;
|
|
1421
|
+
await this.playSingleAd(vastTagUrl);
|
|
1563
1422
|
}
|
|
1564
|
-
this.
|
|
1565
|
-
|
|
1566
|
-
if (targetDurationMs != null) {
|
|
1567
|
-
this.expectedAdBreakDurationMs = targetDurationMs;
|
|
1423
|
+
if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
|
|
1424
|
+
this.expectedAdBreakDurationMs = scheduled.durationMs;
|
|
1568
1425
|
this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
|
|
1569
|
-
this.scheduleAdStopCountdown(
|
|
1426
|
+
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
1570
1427
|
}
|
|
1571
1428
|
}
|
|
1572
1429
|
findCurrentOrNextBreak(nowMs) {
|
|
@@ -1683,89 +1540,25 @@ var StormcloudVideoPlayer = class {
|
|
|
1683
1540
|
"[StormcloudVideoPlayer] Handling ad failure - resuming content"
|
|
1684
1541
|
);
|
|
1685
1542
|
}
|
|
1686
|
-
this.endAdBreak();
|
|
1687
|
-
if (this.video.paused) {
|
|
1688
|
-
this.video.play().catch(() => {
|
|
1689
|
-
if (this.config.debugAdTiming) {
|
|
1690
|
-
console.error(
|
|
1691
|
-
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
endAdBreak() {
|
|
1698
|
-
if (this.config.debugAdTiming) {
|
|
1699
|
-
const targetDuration = this.currentAdBreakTargetDurationMs ? this.currentAdBreakTargetDurationMs / 1e3 : "unknown";
|
|
1700
|
-
const actualDuration = this.cumulativeAdDurationMs / 1e3;
|
|
1701
|
-
const fillRate = this.currentAdBreakTargetDurationMs ? (this.cumulativeAdDurationMs / this.currentAdBreakTargetDurationMs * 100).toFixed(1) : "unknown";
|
|
1702
|
-
console.log("[StormcloudVideoPlayer] Ad break ended:", {
|
|
1703
|
-
targetDurationSeconds: targetDuration,
|
|
1704
|
-
actualDurationSeconds: actualDuration,
|
|
1705
|
-
fillRate: `${fillRate}%`,
|
|
1706
|
-
totalAdsPlayed: this.currentAdIndex
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
1543
|
this.inAdBreak = false;
|
|
1710
1544
|
this.expectedAdBreakDurationMs = void 0;
|
|
1711
1545
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
1712
|
-
this.currentAdBreakTargetDurationMs = void 0;
|
|
1713
|
-
this.cumulativeAdDurationMs = 0;
|
|
1714
1546
|
this.clearAdStartTimer();
|
|
1715
1547
|
this.clearAdStopTimer();
|
|
1716
1548
|
this.clearAdFailsafeTimer();
|
|
1717
1549
|
this.adPodQueue = [];
|
|
1718
|
-
this.preloadedAdInfo = [];
|
|
1719
1550
|
this.showAds = false;
|
|
1720
1551
|
this.currentAdIndex = 0;
|
|
1721
1552
|
this.totalAdsInBreak = 0;
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
if (remainingMs > 500) {
|
|
1725
|
-
return true;
|
|
1726
|
-
}
|
|
1727
|
-
if (this.currentAdBreakTargetDurationMs) {
|
|
1728
|
-
const targetRemainingMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1729
|
-
const minAdDuration = this.config.minAdDurationMs ?? 5e3;
|
|
1730
|
-
if (targetRemainingMs > minAdDuration) {
|
|
1553
|
+
if (this.video.paused) {
|
|
1554
|
+
this.video.play().catch(() => {
|
|
1731
1555
|
if (this.config.debugAdTiming) {
|
|
1732
|
-
console.
|
|
1733
|
-
|
|
1556
|
+
console.error(
|
|
1557
|
+
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1734
1558
|
);
|
|
1735
1559
|
}
|
|
1736
|
-
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
return false;
|
|
1740
|
-
}
|
|
1741
|
-
canRequestMoreAds() {
|
|
1742
|
-
const maxAdsPerBreak = this.config.maxAdsPerBreak ?? 10;
|
|
1743
|
-
if (this.currentAdIndex >= maxAdsPerBreak) {
|
|
1744
|
-
return false;
|
|
1745
|
-
}
|
|
1746
|
-
return !!this.apiVastTagUrl;
|
|
1747
|
-
}
|
|
1748
|
-
async requestAdditionalAds() {
|
|
1749
|
-
if (!this.currentAdBreakTargetDurationMs || !this.apiVastTagUrl) {
|
|
1750
|
-
throw new Error(
|
|
1751
|
-
"Cannot request additional ads without target duration and VAST URL"
|
|
1752
|
-
);
|
|
1753
|
-
}
|
|
1754
|
-
const remainingDurationMs = this.currentAdBreakTargetDurationMs - this.cumulativeAdDurationMs;
|
|
1755
|
-
const estimatedAdDurationMs = this.getEstimatedAdDuration() * 1e3;
|
|
1756
|
-
if (remainingDurationMs < estimatedAdDurationMs * 0.5) {
|
|
1757
|
-
throw new Error("Not enough time remaining for additional ads");
|
|
1758
|
-
}
|
|
1759
|
-
if (this.config.debugAdTiming) {
|
|
1760
|
-
console.log(
|
|
1761
|
-
`[StormcloudVideoPlayer] Requesting additional ads for remaining ${remainingDurationMs}ms`
|
|
1762
|
-
);
|
|
1560
|
+
});
|
|
1763
1561
|
}
|
|
1764
|
-
this.adPodQueue.push(this.apiVastTagUrl);
|
|
1765
|
-
this.totalAdsInBreak++;
|
|
1766
|
-
const next = this.adPodQueue.shift();
|
|
1767
|
-
this.currentAdIndex++;
|
|
1768
|
-
await this.playSingleAd(next);
|
|
1769
1562
|
}
|
|
1770
1563
|
startAdFailsafeTimer() {
|
|
1771
1564
|
this.clearAdFailsafeTimer();
|
|
@@ -1799,109 +1592,6 @@ var StormcloudVideoPlayer = class {
|
|
|
1799
1592
|
}
|
|
1800
1593
|
return [b.vastTagUrl];
|
|
1801
1594
|
}
|
|
1802
|
-
async buildAdQueueForDuration(targetDurationMs) {
|
|
1803
|
-
const adQueue = [];
|
|
1804
|
-
let baseVastTagUrl = this.apiVastTagUrl;
|
|
1805
|
-
if (!baseVastTagUrl) {
|
|
1806
|
-
const scheduled = this.findCurrentOrNextBreak(
|
|
1807
|
-
this.video.currentTime * 1e3
|
|
1808
|
-
);
|
|
1809
|
-
const scheduledTags = this.selectVastTagsForBreak(scheduled);
|
|
1810
|
-
if (scheduledTags && scheduledTags.length > 0) {
|
|
1811
|
-
baseVastTagUrl = scheduledTags[0];
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
if (!baseVastTagUrl) {
|
|
1815
|
-
return adQueue;
|
|
1816
|
-
}
|
|
1817
|
-
if (!targetDurationMs) {
|
|
1818
|
-
let adsNumber = 1;
|
|
1819
|
-
if (this.vastConfig) {
|
|
1820
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1821
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1822
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1823
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1824
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
return new Array(adsNumber).fill(baseVastTagUrl);
|
|
1828
|
-
}
|
|
1829
|
-
const targetDurationSeconds = targetDurationMs / 1e3;
|
|
1830
|
-
let cumulativeDurationSeconds = 0;
|
|
1831
|
-
const maxAdsToTry = 10;
|
|
1832
|
-
let adsAdded = 0;
|
|
1833
|
-
if (this.config.debugAdTiming) {
|
|
1834
|
-
console.log(
|
|
1835
|
-
`[StormcloudVideoPlayer] Attempting to fill ${targetDurationSeconds}s with ads`
|
|
1836
|
-
);
|
|
1837
|
-
}
|
|
1838
|
-
while (cumulativeDurationSeconds < targetDurationSeconds && adsAdded < maxAdsToTry) {
|
|
1839
|
-
adQueue.push(baseVastTagUrl);
|
|
1840
|
-
adsAdded++;
|
|
1841
|
-
const estimatedAdDuration = this.getEstimatedAdDuration();
|
|
1842
|
-
cumulativeDurationSeconds += estimatedAdDuration;
|
|
1843
|
-
if (this.config.debugAdTiming) {
|
|
1844
|
-
console.log(
|
|
1845
|
-
`[StormcloudVideoPlayer] Added ad ${adsAdded}, cumulative duration: ${cumulativeDurationSeconds}s`
|
|
1846
|
-
);
|
|
1847
|
-
}
|
|
1848
|
-
const remainingDuration = targetDurationSeconds - cumulativeDurationSeconds;
|
|
1849
|
-
const toleranceSeconds = (this.config.adBreakGapToleranceMs ?? 2e3) / 1e3;
|
|
1850
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration >= -toleranceSeconds) {
|
|
1851
|
-
if (this.config.debugAdTiming) {
|
|
1852
|
-
console.log(
|
|
1853
|
-
`[StormcloudVideoPlayer] Within tolerance, adding final ad. Overage: ${-remainingDuration}s`
|
|
1854
|
-
);
|
|
1855
|
-
}
|
|
1856
|
-
break;
|
|
1857
|
-
}
|
|
1858
|
-
if (remainingDuration < estimatedAdDuration && remainingDuration < -toleranceSeconds) {
|
|
1859
|
-
if (this.config.debugAdTiming) {
|
|
1860
|
-
console.log(
|
|
1861
|
-
`[StormcloudVideoPlayer] Would exceed duration by too much, stopping at ${adsAdded} ads`
|
|
1862
|
-
);
|
|
1863
|
-
}
|
|
1864
|
-
break;
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
return adQueue;
|
|
1868
|
-
}
|
|
1869
|
-
getEstimatedAdDuration() {
|
|
1870
|
-
if (this.vastConfig) {
|
|
1871
|
-
return 15;
|
|
1872
|
-
}
|
|
1873
|
-
return 30;
|
|
1874
|
-
}
|
|
1875
|
-
async getAdInfoForQueue(adQueue) {
|
|
1876
|
-
if (this.config.enableAdPreloading === false) {
|
|
1877
|
-
if (this.config.debugAdTiming) {
|
|
1878
|
-
console.log(
|
|
1879
|
-
"[StormcloudVideoPlayer] Ad preloading disabled, using estimates"
|
|
1880
|
-
);
|
|
1881
|
-
}
|
|
1882
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1883
|
-
duration: this.getEstimatedAdDuration(),
|
|
1884
|
-
vastTagUrl,
|
|
1885
|
-
isPreloaded: false
|
|
1886
|
-
}));
|
|
1887
|
-
}
|
|
1888
|
-
try {
|
|
1889
|
-
const adInfos = await this.ima.preloadAds(adQueue);
|
|
1890
|
-
return adInfos;
|
|
1891
|
-
} catch (error) {
|
|
1892
|
-
if (this.config.debugAdTiming) {
|
|
1893
|
-
console.warn(
|
|
1894
|
-
"[StormcloudVideoPlayer] Failed to preload ads, using estimates:",
|
|
1895
|
-
error
|
|
1896
|
-
);
|
|
1897
|
-
}
|
|
1898
|
-
return adQueue.map((vastTagUrl) => ({
|
|
1899
|
-
duration: this.getEstimatedAdDuration(),
|
|
1900
|
-
vastTagUrl,
|
|
1901
|
-
isPreloaded: false
|
|
1902
|
-
}));
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
1595
|
getRemainingAdMs() {
|
|
1906
1596
|
if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
|
|
1907
1597
|
return 0;
|