stormcloud-video-player 0.2.8 → 0.2.10
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/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +413 -100
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +413 -100
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +150 -48
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +150 -48
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +150 -48
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +94 -32
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +413 -100
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/utils/tracking.cjs +7 -1
- package/lib/utils/tracking.cjs.map +1 -1
- package/package.json +1 -1
package/lib/players/index.cjs
CHANGED
|
@@ -115,9 +115,22 @@ function createImaController(video, options) {
|
|
|
115
115
|
'script[data-ima="true"]'
|
|
116
116
|
);
|
|
117
117
|
if (existing) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
if (window.google?.ima) {
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
}
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
const timeout = setTimeout(() => {
|
|
123
|
+
reject(new Error("IMA SDK load timeout"));
|
|
124
|
+
}, 1e4);
|
|
125
|
+
existing.addEventListener("load", () => {
|
|
126
|
+
clearTimeout(timeout);
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
existing.addEventListener("error", () => {
|
|
130
|
+
clearTimeout(timeout);
|
|
131
|
+
reject(new Error("IMA SDK load failed"));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
121
134
|
}
|
|
122
135
|
return new Promise((resolve, reject) => {
|
|
123
136
|
const script = document.createElement("script");
|
|
@@ -146,6 +159,17 @@ function createImaController(video, options) {
|
|
|
146
159
|
adsRequest.adTagUrl = vastTagUrl;
|
|
147
160
|
adsLoader.requestAds(adsRequest);
|
|
148
161
|
}
|
|
162
|
+
function destroyAdsManager() {
|
|
163
|
+
if (adsManager) {
|
|
164
|
+
try {
|
|
165
|
+
console.log("[IMA] Destroying existing ads manager");
|
|
166
|
+
adsManager.destroy();
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.warn("[IMA] Error destroying ads manager:", error);
|
|
169
|
+
}
|
|
170
|
+
adsManager = void 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
149
173
|
return {
|
|
150
174
|
initialize() {
|
|
151
175
|
ensureImaLoaded().then(() => {
|
|
@@ -178,9 +202,22 @@ function createImaController(video, options) {
|
|
|
178
202
|
},
|
|
179
203
|
async requestAds(vastTagUrl) {
|
|
180
204
|
console.log("[IMA] Requesting ads:", vastTagUrl);
|
|
205
|
+
if (adPlaying) {
|
|
206
|
+
console.warn(
|
|
207
|
+
"[IMA] Cannot request new ads while an ad is playing. Call stop() first."
|
|
208
|
+
);
|
|
209
|
+
return Promise.reject(
|
|
210
|
+
new Error("Ad already playing - cannot request new ads")
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
destroyAdsManager();
|
|
214
|
+
adsLoadedReject = void 0;
|
|
215
|
+
adsLoadedResolve = void 0;
|
|
216
|
+
let currentReject;
|
|
181
217
|
adsLoadedPromise = new Promise((resolve, reject) => {
|
|
182
218
|
adsLoadedResolve = resolve;
|
|
183
219
|
adsLoadedReject = reject;
|
|
220
|
+
currentReject = reject;
|
|
184
221
|
setTimeout(() => {
|
|
185
222
|
if (adsLoadedReject) {
|
|
186
223
|
adsLoadedReject(new Error("Ad request timeout"));
|
|
@@ -202,7 +239,7 @@ function createImaController(video, options) {
|
|
|
202
239
|
container.style.top = "0";
|
|
203
240
|
container.style.right = "0";
|
|
204
241
|
container.style.bottom = "0";
|
|
205
|
-
container.style.display = "
|
|
242
|
+
container.style.display = "none";
|
|
206
243
|
container.style.alignItems = "center";
|
|
207
244
|
container.style.justifyContent = "center";
|
|
208
245
|
container.style.pointerEvents = "none";
|
|
@@ -242,14 +279,14 @@ function createImaController(video, options) {
|
|
|
242
279
|
AdErrorEvent.AD_ERROR,
|
|
243
280
|
(errorEvent) => {
|
|
244
281
|
console.error("[IMA] Ad error:", errorEvent.getError());
|
|
245
|
-
|
|
246
|
-
adsManager?.destroy?.();
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
282
|
+
destroyAdsManager();
|
|
249
283
|
adPlaying = false;
|
|
250
284
|
video.muted = originalMutedState;
|
|
251
|
-
if (adContainerEl)
|
|
285
|
+
if (adContainerEl) {
|
|
252
286
|
adContainerEl.style.pointerEvents = "none";
|
|
287
|
+
adContainerEl.style.display = "none";
|
|
288
|
+
console.log("[IMA] Ad container hidden after error");
|
|
289
|
+
}
|
|
253
290
|
if (adsLoadedReject) {
|
|
254
291
|
adsLoadedReject(new Error("Ad playback error"));
|
|
255
292
|
adsLoadedReject = void 0;
|
|
@@ -282,8 +319,6 @@ function createImaController(video, options) {
|
|
|
282
319
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
283
320
|
() => {
|
|
284
321
|
console.log("[IMA] Content pause requested");
|
|
285
|
-
originalMutedState = video.muted;
|
|
286
|
-
video.muted = true;
|
|
287
322
|
if (!options?.continueLiveStreamDuringAds) {
|
|
288
323
|
video.pause();
|
|
289
324
|
console.log("[IMA] Video paused (VOD mode)");
|
|
@@ -292,20 +327,34 @@ function createImaController(video, options) {
|
|
|
292
327
|
"[IMA] Video continues playing but muted (Live mode)"
|
|
293
328
|
);
|
|
294
329
|
}
|
|
330
|
+
video.muted = true;
|
|
295
331
|
adPlaying = true;
|
|
296
|
-
if (adContainerEl)
|
|
297
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
298
332
|
emit("content_pause");
|
|
299
333
|
}
|
|
300
334
|
);
|
|
335
|
+
adsManager.addEventListener(AdEvent.STARTED, () => {
|
|
336
|
+
console.log("[IMA] Ad started playing");
|
|
337
|
+
if (adContainerEl) {
|
|
338
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
339
|
+
adContainerEl.style.display = "flex";
|
|
340
|
+
console.log(
|
|
341
|
+
"[IMA] Ad container visibility set to flex with pointer events enabled"
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
301
345
|
adsManager.addEventListener(
|
|
302
346
|
AdEvent.CONTENT_RESUME_REQUESTED,
|
|
303
347
|
() => {
|
|
304
348
|
console.log("[IMA] Content resume requested");
|
|
305
349
|
adPlaying = false;
|
|
306
350
|
video.muted = originalMutedState;
|
|
307
|
-
if (adContainerEl)
|
|
351
|
+
if (adContainerEl) {
|
|
308
352
|
adContainerEl.style.pointerEvents = "none";
|
|
353
|
+
adContainerEl.style.display = "none";
|
|
354
|
+
console.log(
|
|
355
|
+
"[IMA] Ad container hidden - pointer events disabled"
|
|
356
|
+
);
|
|
357
|
+
}
|
|
309
358
|
if (!options?.continueLiveStreamDuringAds) {
|
|
310
359
|
video.play()?.catch(() => {
|
|
311
360
|
});
|
|
@@ -322,7 +371,13 @@ function createImaController(video, options) {
|
|
|
322
371
|
console.log("[IMA] All ads completed");
|
|
323
372
|
adPlaying = false;
|
|
324
373
|
video.muted = originalMutedState;
|
|
325
|
-
if (adContainerEl)
|
|
374
|
+
if (adContainerEl) {
|
|
375
|
+
adContainerEl.style.pointerEvents = "none";
|
|
376
|
+
adContainerEl.style.display = "none";
|
|
377
|
+
console.log(
|
|
378
|
+
"[IMA] Ad container hidden after all ads completed"
|
|
379
|
+
);
|
|
380
|
+
}
|
|
326
381
|
if (!options?.continueLiveStreamDuringAds) {
|
|
327
382
|
video.play().catch(() => {
|
|
328
383
|
});
|
|
@@ -365,6 +420,13 @@ function createImaController(video, options) {
|
|
|
365
420
|
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
366
421
|
(adErrorEvent) => {
|
|
367
422
|
console.error("[IMA] Ads loader error:", adErrorEvent.getError());
|
|
423
|
+
adPlaying = false;
|
|
424
|
+
video.muted = originalMutedState;
|
|
425
|
+
if (adContainerEl) adContainerEl.style.pointerEvents = "none";
|
|
426
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
427
|
+
video.play().catch(() => {
|
|
428
|
+
});
|
|
429
|
+
}
|
|
368
430
|
if (adsLoadedReject) {
|
|
369
431
|
adsLoadedReject(new Error("Ads loader error"));
|
|
370
432
|
adsLoadedReject = void 0;
|
|
@@ -380,11 +442,9 @@ function createImaController(video, options) {
|
|
|
380
442
|
return adsLoadedPromise;
|
|
381
443
|
} catch (error) {
|
|
382
444
|
console.error("[IMA] Failed to request ads:", error);
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
adsLoadedResolve = void 0;
|
|
387
|
-
}
|
|
445
|
+
currentReject?.(error);
|
|
446
|
+
adsLoadedReject = void 0;
|
|
447
|
+
adsLoadedResolve = void 0;
|
|
388
448
|
return Promise.reject(error);
|
|
389
449
|
}
|
|
390
450
|
},
|
|
@@ -404,14 +464,6 @@ function createImaController(video, options) {
|
|
|
404
464
|
const height = video.clientHeight || 360;
|
|
405
465
|
console.log(`[IMA] Initializing ads manager (${width}x${height})`);
|
|
406
466
|
adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
|
|
407
|
-
if (!options?.continueLiveStreamDuringAds) {
|
|
408
|
-
console.log("[IMA] Pausing video for ad playback (VOD mode)");
|
|
409
|
-
video.pause();
|
|
410
|
-
} else {
|
|
411
|
-
console.log(
|
|
412
|
-
"[IMA] Keeping video playing but muted for ad playback (Live mode)"
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
467
|
adPlaying = true;
|
|
416
468
|
console.log("[IMA] Starting ad playback");
|
|
417
469
|
adsManager.start();
|
|
@@ -429,10 +481,16 @@ function createImaController(video, options) {
|
|
|
429
481
|
async stop() {
|
|
430
482
|
adPlaying = false;
|
|
431
483
|
video.muted = originalMutedState;
|
|
484
|
+
if (adContainerEl) {
|
|
485
|
+
adContainerEl.style.pointerEvents = "none";
|
|
486
|
+
adContainerEl.style.display = "none";
|
|
487
|
+
console.log("[IMA] Ad container hidden after stop");
|
|
488
|
+
}
|
|
432
489
|
try {
|
|
433
490
|
adsManager?.stop?.();
|
|
434
491
|
} catch {
|
|
435
492
|
}
|
|
493
|
+
destroyAdsManager();
|
|
436
494
|
if (!options?.continueLiveStreamDuringAds) {
|
|
437
495
|
video.play().catch(() => {
|
|
438
496
|
});
|
|
@@ -442,12 +500,13 @@ function createImaController(video, options) {
|
|
|
442
500
|
}
|
|
443
501
|
},
|
|
444
502
|
destroy() {
|
|
445
|
-
|
|
446
|
-
adsManager?.destroy?.();
|
|
447
|
-
} catch {
|
|
448
|
-
}
|
|
503
|
+
destroyAdsManager();
|
|
449
504
|
adPlaying = false;
|
|
450
505
|
video.muted = originalMutedState;
|
|
506
|
+
if (adContainerEl) {
|
|
507
|
+
adContainerEl.style.pointerEvents = "none";
|
|
508
|
+
adContainerEl.style.display = "none";
|
|
509
|
+
}
|
|
451
510
|
try {
|
|
452
511
|
adsLoader?.destroy?.();
|
|
453
512
|
} catch {
|
|
@@ -455,6 +514,9 @@ function createImaController(video, options) {
|
|
|
455
514
|
if (adContainerEl?.parentElement) {
|
|
456
515
|
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
457
516
|
}
|
|
517
|
+
adContainerEl = void 0;
|
|
518
|
+
adDisplayContainer = void 0;
|
|
519
|
+
adsLoader = void 0;
|
|
458
520
|
},
|
|
459
521
|
isAdPlaying() {
|
|
460
522
|
return adPlaying;
|
|
@@ -510,6 +572,7 @@ function createImaController(video, options) {
|
|
|
510
572
|
}
|
|
511
573
|
|
|
512
574
|
// src/utils/tracking.ts
|
|
575
|
+
var cachedBrowserId = null;
|
|
513
576
|
function getClientInfo() {
|
|
514
577
|
const ua = navigator.userAgent;
|
|
515
578
|
const platform = navigator.platform;
|
|
@@ -652,6 +715,9 @@ function getClientInfo() {
|
|
|
652
715
|
};
|
|
653
716
|
}
|
|
654
717
|
async function getBrowserID(clientInfo) {
|
|
718
|
+
if (cachedBrowserId) {
|
|
719
|
+
return cachedBrowserId;
|
|
720
|
+
}
|
|
655
721
|
const fingerprintString = JSON.stringify(clientInfo);
|
|
656
722
|
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
657
723
|
try {
|
|
@@ -662,6 +728,7 @@ async function getBrowserID(clientInfo) {
|
|
|
662
728
|
);
|
|
663
729
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
664
730
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
731
|
+
cachedBrowserId = hashHex;
|
|
665
732
|
return hashHex;
|
|
666
733
|
} catch (error) {
|
|
667
734
|
console.warn(
|
|
@@ -678,7 +745,8 @@ async function getBrowserID(clientInfo) {
|
|
|
678
745
|
const fallbackHash = Math.abs(hash).toString(16).padStart(8, "0");
|
|
679
746
|
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
680
747
|
const random = Math.random().toString(16).substring(2, 14).padStart(12, "0");
|
|
681
|
-
|
|
748
|
+
cachedBrowserId = (fallbackHash + timestamp + random).padEnd(64, "0");
|
|
749
|
+
return cachedBrowserId;
|
|
682
750
|
}
|
|
683
751
|
async function sendInitialTracking(licenseKey) {
|
|
684
752
|
try {
|
|
@@ -926,17 +994,8 @@ var StormcloudVideoPlayer = class {
|
|
|
926
994
|
this.video.muted = !!this.config.muted;
|
|
927
995
|
this.ima.initialize();
|
|
928
996
|
this.ima.on("all_ads_completed", () => {
|
|
929
|
-
if (
|
|
930
|
-
|
|
931
|
-
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
932
|
-
const next = this.adPodQueue.shift();
|
|
933
|
-
this.currentAdIndex++;
|
|
934
|
-
this.playSingleAd(next).catch(() => {
|
|
935
|
-
});
|
|
936
|
-
} else {
|
|
937
|
-
this.currentAdIndex = 0;
|
|
938
|
-
this.totalAdsInBreak = 0;
|
|
939
|
-
this.showAds = false;
|
|
997
|
+
if (this.config.debugAdTiming) {
|
|
998
|
+
console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
|
|
940
999
|
}
|
|
941
1000
|
});
|
|
942
1001
|
this.ima.on("ad_error", () => {
|
|
@@ -967,6 +1026,26 @@ var StormcloudVideoPlayer = class {
|
|
|
967
1026
|
);
|
|
968
1027
|
}
|
|
969
1028
|
this.clearAdFailsafeTimer();
|
|
1029
|
+
if (!this.inAdBreak) return;
|
|
1030
|
+
const remaining = this.getRemainingAdMs();
|
|
1031
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
1032
|
+
const next = this.adPodQueue.shift();
|
|
1033
|
+
this.currentAdIndex++;
|
|
1034
|
+
if (this.config.debugAdTiming) {
|
|
1035
|
+
console.log(
|
|
1036
|
+
`[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
this.playSingleAd(next).catch(() => {
|
|
1040
|
+
});
|
|
1041
|
+
} else {
|
|
1042
|
+
if (this.config.debugAdTiming) {
|
|
1043
|
+
console.log("[StormcloudVideoPlayer] Ad pod completed");
|
|
1044
|
+
}
|
|
1045
|
+
this.currentAdIndex = 0;
|
|
1046
|
+
this.totalAdsInBreak = 0;
|
|
1047
|
+
this.showAds = false;
|
|
1048
|
+
}
|
|
970
1049
|
});
|
|
971
1050
|
this.video.addEventListener("timeupdate", () => {
|
|
972
1051
|
this.onTimeUpdate(this.video.currentTime);
|
|
@@ -1367,17 +1446,21 @@ var StormcloudVideoPlayer = class {
|
|
|
1367
1446
|
return void 0;
|
|
1368
1447
|
}
|
|
1369
1448
|
initializeTracking() {
|
|
1370
|
-
sendInitialTracking(this.config.licenseKey).
|
|
1449
|
+
sendInitialTracking(this.config.licenseKey).then(() => {
|
|
1450
|
+
this.heartbeatInterval = window.setInterval(() => {
|
|
1451
|
+
this.sendHeartbeatIfNeeded();
|
|
1452
|
+
}, 5e3);
|
|
1453
|
+
}).catch((error) => {
|
|
1371
1454
|
if (this.config.debugAdTiming) {
|
|
1372
1455
|
console.warn(
|
|
1373
1456
|
"[StormcloudVideoPlayer] Failed to send initial tracking:",
|
|
1374
1457
|
error
|
|
1375
1458
|
);
|
|
1376
1459
|
}
|
|
1460
|
+
this.heartbeatInterval = window.setInterval(() => {
|
|
1461
|
+
this.sendHeartbeatIfNeeded();
|
|
1462
|
+
}, 5e3);
|
|
1377
1463
|
});
|
|
1378
|
-
this.heartbeatInterval = window.setInterval(() => {
|
|
1379
|
-
this.sendHeartbeatIfNeeded();
|
|
1380
|
-
}, 5e3);
|
|
1381
1464
|
}
|
|
1382
1465
|
sendHeartbeatIfNeeded() {
|
|
1383
1466
|
const now = Date.now();
|
|
@@ -1612,9 +1695,21 @@ var StormcloudVideoPlayer = class {
|
|
|
1612
1695
|
if (this.config.debugAdTiming) {
|
|
1613
1696
|
console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
|
|
1614
1697
|
}
|
|
1698
|
+
if (this.ima.isAdPlaying()) {
|
|
1699
|
+
if (this.config.debugAdTiming) {
|
|
1700
|
+
console.warn(
|
|
1701
|
+
"[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
this.ima.updateOriginalMutedState(this.video.muted);
|
|
1615
1707
|
this.startAdFailsafeTimer();
|
|
1616
1708
|
try {
|
|
1617
1709
|
await this.ima.requestAds(vastTagUrl);
|
|
1710
|
+
if (this.config.debugAdTiming) {
|
|
1711
|
+
console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
|
|
1712
|
+
}
|
|
1618
1713
|
await this.ima.play();
|
|
1619
1714
|
if (this.config.debugAdTiming) {
|
|
1620
1715
|
console.log("[StormcloudVideoPlayer] Ad playback started successfully");
|
|
@@ -1642,6 +1737,13 @@ var StormcloudVideoPlayer = class {
|
|
|
1642
1737
|
this.showAds = false;
|
|
1643
1738
|
this.currentAdIndex = 0;
|
|
1644
1739
|
this.totalAdsInBreak = 0;
|
|
1740
|
+
const originalMutedState = this.ima.getOriginalMutedState();
|
|
1741
|
+
this.video.muted = originalMutedState;
|
|
1742
|
+
if (this.config.debugAdTiming) {
|
|
1743
|
+
console.log(
|
|
1744
|
+
`[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1645
1747
|
if (this.video.paused) {
|
|
1646
1748
|
this.video.play()?.catch(() => {
|
|
1647
1749
|
if (this.config.debugAdTiming) {
|