stormcloud-video-player 0.2.12 → 0.2.14
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 +100 -1
- package/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +1206 -151
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +77 -2
- package/lib/index.d.ts +77 -2
- package/lib/index.js +1196 -151
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +1100 -114
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +2 -2
- package/lib/players/FilePlayer.cjs +14 -7
- package/lib/players/FilePlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +1108 -119
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +1122 -126
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +561 -0
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
- package/lib/sdk/hlsAdPlayer.d.cts +10 -0
- package/lib/sdk/ima.cjs +176 -23
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/sdk/ima.d.cts +1 -1
- package/lib/{types-GpA_hKek.d.cts → types-mVgmKmzM.d.cts} +3 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +1107 -119
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/browserCompat.cjs +229 -0
- package/lib/utils/browserCompat.cjs.map +1 -0
- package/lib/utils/browserCompat.d.cts +36 -0
- package/lib/utils/polyfills.cjs +253 -0
- package/lib/utils/polyfills.cjs.map +1 -0
- package/lib/utils/polyfills.d.cts +11 -0
- package/lib/utils/tracking.cjs +10 -9
- package/lib/utils/tracking.cjs.map +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2,7 +2,196 @@
|
|
|
2
2
|
import React, { useEffect, useRef, useMemo } from "react";
|
|
3
3
|
|
|
4
4
|
// src/player/StormcloudVideoPlayer.ts
|
|
5
|
-
import
|
|
5
|
+
import Hls2 from "hls.js";
|
|
6
|
+
|
|
7
|
+
// src/utils/browserCompat.ts
|
|
8
|
+
function getChromeVersion(ua) {
|
|
9
|
+
const match = ua.match(/Chrome\/(\d+)/);
|
|
10
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
11
|
+
}
|
|
12
|
+
function getWebKitVersion(ua) {
|
|
13
|
+
const match = ua.match(/AppleWebKit\/(\d+)/);
|
|
14
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
15
|
+
}
|
|
16
|
+
function getPlatform() {
|
|
17
|
+
var _a;
|
|
18
|
+
if ("userAgentData" in navigator && ((_a = navigator.userAgentData) == null ? void 0 : _a.platform)) {
|
|
19
|
+
return navigator.userAgentData.platform;
|
|
20
|
+
}
|
|
21
|
+
const ua = navigator.userAgent;
|
|
22
|
+
if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
|
|
23
|
+
return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
|
|
24
|
+
}
|
|
25
|
+
if (/Win/i.test(ua)) {
|
|
26
|
+
return "Win32";
|
|
27
|
+
}
|
|
28
|
+
if (/Linux/i.test(ua)) {
|
|
29
|
+
return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
|
|
30
|
+
}
|
|
31
|
+
if (/CrOS/i.test(ua)) {
|
|
32
|
+
return "CrOS";
|
|
33
|
+
}
|
|
34
|
+
return navigator.platform || "Unknown";
|
|
35
|
+
}
|
|
36
|
+
function detectBrowser() {
|
|
37
|
+
const ua = navigator.userAgent;
|
|
38
|
+
const platform = getPlatform();
|
|
39
|
+
let name = "Unknown";
|
|
40
|
+
let version = "0";
|
|
41
|
+
let majorVersion = 0;
|
|
42
|
+
let isSmartTV = false;
|
|
43
|
+
let isLegacyTV = false;
|
|
44
|
+
let supportsIMA = true;
|
|
45
|
+
let supportsModernJS2 = true;
|
|
46
|
+
let recommendedAdPlayer = "ima";
|
|
47
|
+
if (/Web0S|webOS/i.test(ua)) {
|
|
48
|
+
name = "LG WebOS";
|
|
49
|
+
isSmartTV = true;
|
|
50
|
+
const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
|
|
51
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
52
|
+
if (version !== "Unknown") {
|
|
53
|
+
const parts = version.split(".");
|
|
54
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
55
|
+
}
|
|
56
|
+
} else if (/Tizen/i.test(ua)) {
|
|
57
|
+
name = "Samsung Tizen";
|
|
58
|
+
isSmartTV = true;
|
|
59
|
+
const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
|
|
60
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
61
|
+
if (version !== "Unknown") {
|
|
62
|
+
const parts = version.split(".");
|
|
63
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
64
|
+
}
|
|
65
|
+
} else if (/SMART-TV|SmartTV/i.test(ua)) {
|
|
66
|
+
name = "Smart TV";
|
|
67
|
+
isSmartTV = true;
|
|
68
|
+
} else if (/NetCast/i.test(ua)) {
|
|
69
|
+
name = "LG NetCast";
|
|
70
|
+
isSmartTV = true;
|
|
71
|
+
isLegacyTV = true;
|
|
72
|
+
} else if (/BRAVIA/i.test(ua)) {
|
|
73
|
+
name = "Sony BRAVIA";
|
|
74
|
+
isSmartTV = true;
|
|
75
|
+
}
|
|
76
|
+
const chromeVersion = getChromeVersion(ua);
|
|
77
|
+
const webkitVersion = getWebKitVersion(ua);
|
|
78
|
+
if (chromeVersion > 0) {
|
|
79
|
+
if (!isSmartTV) {
|
|
80
|
+
name = "Chrome";
|
|
81
|
+
version = chromeVersion.toString();
|
|
82
|
+
majorVersion = chromeVersion;
|
|
83
|
+
}
|
|
84
|
+
if (chromeVersion < 50) {
|
|
85
|
+
supportsIMA = false;
|
|
86
|
+
supportsModernJS2 = false;
|
|
87
|
+
isLegacyTV = true;
|
|
88
|
+
recommendedAdPlayer = "hls";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (webkitVersion > 0 && webkitVersion < 600) {
|
|
92
|
+
supportsModernJS2 = false;
|
|
93
|
+
if (isSmartTV) {
|
|
94
|
+
isLegacyTV = true;
|
|
95
|
+
supportsIMA = false;
|
|
96
|
+
recommendedAdPlayer = "hls";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
|
|
100
|
+
supportsModernJS2 = false;
|
|
101
|
+
supportsIMA = false;
|
|
102
|
+
recommendedAdPlayer = "hls";
|
|
103
|
+
}
|
|
104
|
+
if (typeof URLSearchParams === "undefined") {
|
|
105
|
+
supportsModernJS2 = false;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
name,
|
|
109
|
+
version,
|
|
110
|
+
majorVersion,
|
|
111
|
+
isSmartTV,
|
|
112
|
+
isLegacyTV,
|
|
113
|
+
platform,
|
|
114
|
+
supportsIMA,
|
|
115
|
+
supportsModernJS: supportsModernJS2,
|
|
116
|
+
recommendedAdPlayer
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function supportsGoogleIMA() {
|
|
120
|
+
const browser = detectBrowser();
|
|
121
|
+
if (browser.isLegacyTV) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (typeof document === "undefined" || typeof document.createElement !== "function") {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const video = document.createElement("video");
|
|
129
|
+
if (!video) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (typeof Promise === "undefined") {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return browser.supportsIMA;
|
|
139
|
+
}
|
|
140
|
+
function getRecommendedAdPlayer() {
|
|
141
|
+
const browser = detectBrowser();
|
|
142
|
+
return browser.recommendedAdPlayer;
|
|
143
|
+
}
|
|
144
|
+
function supportsModernJS() {
|
|
145
|
+
try {
|
|
146
|
+
return typeof Promise !== "undefined" && typeof Map !== "undefined" && typeof Set !== "undefined" && typeof Array.from !== "undefined" && typeof Object.assign !== "undefined" && typeof Array.prototype.forEach !== "undefined" && typeof String.prototype.includes !== "undefined";
|
|
147
|
+
} catch (e) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function logBrowserInfo(debug = false) {
|
|
152
|
+
if (!debug) return;
|
|
153
|
+
const browser = detectBrowser();
|
|
154
|
+
const imaSupport = supportsGoogleIMA();
|
|
155
|
+
console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
|
|
156
|
+
browser: `${browser.name} ${browser.version}`,
|
|
157
|
+
platform: browser.platform,
|
|
158
|
+
isSmartTV: browser.isSmartTV,
|
|
159
|
+
isLegacyTV: browser.isLegacyTV,
|
|
160
|
+
supportsIMA: imaSupport,
|
|
161
|
+
supportsModernJS: browser.supportsModernJS,
|
|
162
|
+
recommendedAdPlayer: browser.recommendedAdPlayer,
|
|
163
|
+
userAgent: navigator.userAgent
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function getBrowserConfigOverrides() {
|
|
167
|
+
const browser = detectBrowser();
|
|
168
|
+
const overrides = {};
|
|
169
|
+
if (browser.isLegacyTV || !browser.supportsIMA) {
|
|
170
|
+
overrides.adPlayerType = "hls";
|
|
171
|
+
}
|
|
172
|
+
if (browser.isSmartTV) {
|
|
173
|
+
overrides.allowNativeHls = true;
|
|
174
|
+
}
|
|
175
|
+
return overrides;
|
|
176
|
+
}
|
|
177
|
+
function supportsFeature(feature) {
|
|
178
|
+
switch (feature) {
|
|
179
|
+
case "ima":
|
|
180
|
+
return supportsGoogleIMA();
|
|
181
|
+
case "urlsearchparams":
|
|
182
|
+
return typeof URLSearchParams !== "undefined";
|
|
183
|
+
case "textencoder":
|
|
184
|
+
return typeof TextEncoder !== "undefined";
|
|
185
|
+
case "promises":
|
|
186
|
+
return typeof Promise !== "undefined";
|
|
187
|
+
case "fetch":
|
|
188
|
+
return typeof fetch !== "undefined";
|
|
189
|
+
case "crypto":
|
|
190
|
+
return typeof crypto !== "undefined" && typeof crypto.subtle !== "undefined";
|
|
191
|
+
default:
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
6
195
|
|
|
7
196
|
// src/sdk/ima.ts
|
|
8
197
|
function createImaController(video, options) {
|
|
@@ -20,9 +209,18 @@ function createImaController(video, options) {
|
|
|
20
209
|
}
|
|
21
210
|
}
|
|
22
211
|
function ensureImaLoaded() {
|
|
212
|
+
var _a, _b, _c;
|
|
213
|
+
if (!supportsGoogleIMA()) {
|
|
214
|
+
console.warn(
|
|
215
|
+
"[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
|
|
216
|
+
);
|
|
217
|
+
return Promise.reject(
|
|
218
|
+
new Error("Google IMA SDK not supported on this browser")
|
|
219
|
+
);
|
|
220
|
+
}
|
|
23
221
|
try {
|
|
24
222
|
const frameEl = window.frameElement;
|
|
25
|
-
const sandboxAttr = frameEl
|
|
223
|
+
const sandboxAttr = ((_a = frameEl == null ? void 0 : frameEl.getAttribute) == null ? void 0 : _a.call(frameEl, "sandbox")) || "";
|
|
26
224
|
if (sandboxAttr) {
|
|
27
225
|
const tokens = new Set(
|
|
28
226
|
sandboxAttr.split(/\s+/).map((t) => t.trim()).filter((t) => t.length > 0)
|
|
@@ -36,13 +234,13 @@ function createImaController(video, options) {
|
|
|
36
234
|
}
|
|
37
235
|
} catch {
|
|
38
236
|
}
|
|
39
|
-
if (typeof window !== "undefined" && window.google
|
|
237
|
+
if (typeof window !== "undefined" && ((_b = window.google) == null ? void 0 : _b.ima))
|
|
40
238
|
return Promise.resolve();
|
|
41
239
|
const existing = document.querySelector(
|
|
42
240
|
'script[data-ima="true"]'
|
|
43
241
|
);
|
|
44
242
|
if (existing) {
|
|
45
|
-
if (window.google
|
|
243
|
+
if ((_c = window.google) == null ? void 0 : _c.ima) {
|
|
46
244
|
return Promise.resolve();
|
|
47
245
|
}
|
|
48
246
|
return new Promise((resolve, reject) => {
|
|
@@ -100,6 +298,7 @@ function createImaController(video, options) {
|
|
|
100
298
|
return {
|
|
101
299
|
initialize() {
|
|
102
300
|
ensureImaLoaded().then(() => {
|
|
301
|
+
var _a, _b;
|
|
103
302
|
const google = window.google;
|
|
104
303
|
if (!adDisplayContainer) {
|
|
105
304
|
const container = document.createElement("div");
|
|
@@ -113,14 +312,14 @@ function createImaController(video, options) {
|
|
|
113
312
|
container.style.justifyContent = "center";
|
|
114
313
|
container.style.pointerEvents = "none";
|
|
115
314
|
container.style.zIndex = "2";
|
|
116
|
-
video.parentElement
|
|
315
|
+
(_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
|
|
117
316
|
adContainerEl = container;
|
|
118
317
|
adDisplayContainer = new google.ima.AdDisplayContainer(
|
|
119
318
|
container,
|
|
120
319
|
video
|
|
121
320
|
);
|
|
122
321
|
try {
|
|
123
|
-
adDisplayContainer.initialize
|
|
322
|
+
(_b = adDisplayContainer.initialize) == null ? void 0 : _b.call(adDisplayContainer);
|
|
124
323
|
} catch {
|
|
125
324
|
}
|
|
126
325
|
}
|
|
@@ -205,6 +404,7 @@ function createImaController(video, options) {
|
|
|
205
404
|
adsManager.addEventListener(
|
|
206
405
|
AdErrorEvent.AD_ERROR,
|
|
207
406
|
(errorEvent) => {
|
|
407
|
+
var _a;
|
|
208
408
|
console.error("[IMA] Ad error:", errorEvent.getError());
|
|
209
409
|
destroyAdsManager();
|
|
210
410
|
adPlaying = false;
|
|
@@ -235,10 +435,10 @@ function createImaController(video, options) {
|
|
|
235
435
|
"[IMA] Max retries reached, emitting ad_error"
|
|
236
436
|
);
|
|
237
437
|
emit("ad_error");
|
|
238
|
-
if (!options
|
|
438
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
239
439
|
if (video.paused) {
|
|
240
440
|
console.log("[IMA] Resuming paused video after ad error");
|
|
241
|
-
video.play()
|
|
441
|
+
(_a = video.play()) == null ? void 0 : _a.catch(() => {
|
|
242
442
|
});
|
|
243
443
|
}
|
|
244
444
|
}
|
|
@@ -249,7 +449,7 @@ function createImaController(video, options) {
|
|
|
249
449
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
250
450
|
() => {
|
|
251
451
|
console.log("[IMA] Content pause requested");
|
|
252
|
-
if (!options
|
|
452
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
253
453
|
video.pause();
|
|
254
454
|
console.log("[IMA] Video paused (VOD mode)");
|
|
255
455
|
} else {
|
|
@@ -275,6 +475,7 @@ function createImaController(video, options) {
|
|
|
275
475
|
adsManager.addEventListener(
|
|
276
476
|
AdEvent.CONTENT_RESUME_REQUESTED,
|
|
277
477
|
() => {
|
|
478
|
+
var _a;
|
|
278
479
|
console.log("[IMA] Content resume requested");
|
|
279
480
|
adPlaying = false;
|
|
280
481
|
video.muted = originalMutedState;
|
|
@@ -285,8 +486,8 @@ function createImaController(video, options) {
|
|
|
285
486
|
"[IMA] Ad container hidden - pointer events disabled"
|
|
286
487
|
);
|
|
287
488
|
}
|
|
288
|
-
if (!options
|
|
289
|
-
video.play()
|
|
489
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
490
|
+
(_a = video.play()) == null ? void 0 : _a.catch(() => {
|
|
290
491
|
});
|
|
291
492
|
console.log("[IMA] Video resumed (VOD mode)");
|
|
292
493
|
} else {
|
|
@@ -308,7 +509,7 @@ function createImaController(video, options) {
|
|
|
308
509
|
"[IMA] Ad container hidden after all ads completed"
|
|
309
510
|
);
|
|
310
511
|
}
|
|
311
|
-
if (!options
|
|
512
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
312
513
|
video.play().catch(() => {
|
|
313
514
|
});
|
|
314
515
|
console.log(
|
|
@@ -336,7 +537,7 @@ function createImaController(video, options) {
|
|
|
336
537
|
adContainerEl.style.display = "none";
|
|
337
538
|
console.log("[IMA] Ad container hidden after setup error");
|
|
338
539
|
}
|
|
339
|
-
if (!options
|
|
540
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
340
541
|
if (video.paused) {
|
|
341
542
|
console.log("[IMA] Resuming paused video after setup error");
|
|
342
543
|
video.play().catch(() => {
|
|
@@ -364,7 +565,7 @@ function createImaController(video, options) {
|
|
|
364
565
|
adContainerEl.style.display = "none";
|
|
365
566
|
console.log("[IMA] Ad container hidden after loader error");
|
|
366
567
|
}
|
|
367
|
-
if (!options
|
|
568
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
368
569
|
if (video.paused) {
|
|
369
570
|
console.log("[IMA] Resuming paused video after loader error");
|
|
370
571
|
video.play().catch(() => {
|
|
@@ -386,14 +587,15 @@ function createImaController(video, options) {
|
|
|
386
587
|
return adsLoadedPromise;
|
|
387
588
|
} catch (error) {
|
|
388
589
|
console.error("[IMA] Failed to request ads:", error);
|
|
389
|
-
currentReject
|
|
590
|
+
currentReject == null ? void 0 : currentReject(error);
|
|
390
591
|
adsLoadedReject = void 0;
|
|
391
592
|
adsLoadedResolve = void 0;
|
|
392
593
|
return Promise.reject(error);
|
|
393
594
|
}
|
|
394
595
|
},
|
|
395
596
|
async play() {
|
|
396
|
-
|
|
597
|
+
var _a, _b;
|
|
598
|
+
if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
|
|
397
599
|
console.warn(
|
|
398
600
|
"[IMA] Cannot play ad: IMA SDK or ad container not available"
|
|
399
601
|
);
|
|
@@ -415,14 +617,15 @@ function createImaController(video, options) {
|
|
|
415
617
|
} catch (error) {
|
|
416
618
|
console.error("[IMA] Error starting ad playback:", error);
|
|
417
619
|
adPlaying = false;
|
|
418
|
-
if (!options
|
|
419
|
-
video.play()
|
|
620
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
621
|
+
(_b = video.play()) == null ? void 0 : _b.catch(() => {
|
|
420
622
|
});
|
|
421
623
|
}
|
|
422
624
|
return Promise.reject(error);
|
|
423
625
|
}
|
|
424
626
|
},
|
|
425
627
|
async stop() {
|
|
628
|
+
var _a;
|
|
426
629
|
adPlaying = false;
|
|
427
630
|
video.muted = originalMutedState;
|
|
428
631
|
if (adContainerEl) {
|
|
@@ -431,11 +634,11 @@ function createImaController(video, options) {
|
|
|
431
634
|
console.log("[IMA] Ad container hidden after stop");
|
|
432
635
|
}
|
|
433
636
|
try {
|
|
434
|
-
adsManager
|
|
637
|
+
(_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
|
|
435
638
|
} catch {
|
|
436
639
|
}
|
|
437
640
|
destroyAdsManager();
|
|
438
|
-
if (!options
|
|
641
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
439
642
|
video.play().catch(() => {
|
|
440
643
|
});
|
|
441
644
|
console.log("[IMA] Video resumed after stop (VOD mode)");
|
|
@@ -444,6 +647,7 @@ function createImaController(video, options) {
|
|
|
444
647
|
}
|
|
445
648
|
},
|
|
446
649
|
destroy() {
|
|
650
|
+
var _a;
|
|
447
651
|
destroyAdsManager();
|
|
448
652
|
adPlaying = false;
|
|
449
653
|
video.muted = originalMutedState;
|
|
@@ -452,10 +656,10 @@ function createImaController(video, options) {
|
|
|
452
656
|
adContainerEl.style.display = "none";
|
|
453
657
|
}
|
|
454
658
|
try {
|
|
455
|
-
adsLoader
|
|
659
|
+
(_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
|
|
456
660
|
} catch {
|
|
457
661
|
}
|
|
458
|
-
if (adContainerEl
|
|
662
|
+
if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
|
|
459
663
|
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
460
664
|
}
|
|
461
665
|
adContainerEl = void 0;
|
|
@@ -466,7 +670,8 @@ function createImaController(video, options) {
|
|
|
466
670
|
return adPlaying;
|
|
467
671
|
},
|
|
468
672
|
resize(width, height) {
|
|
469
|
-
|
|
673
|
+
var _a;
|
|
674
|
+
if (!adsManager || !((_a = window.google) == null ? void 0 : _a.ima)) {
|
|
470
675
|
console.warn(
|
|
471
676
|
"[IMA] Cannot resize: No ads manager or IMA SDK available"
|
|
472
677
|
);
|
|
@@ -484,7 +689,8 @@ function createImaController(video, options) {
|
|
|
484
689
|
listeners.get(event).add(listener);
|
|
485
690
|
},
|
|
486
691
|
off(event, listener) {
|
|
487
|
-
|
|
692
|
+
var _a;
|
|
693
|
+
(_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
|
|
488
694
|
},
|
|
489
695
|
updateOriginalMutedState(muted) {
|
|
490
696
|
originalMutedState = muted;
|
|
@@ -515,9 +721,533 @@ function createImaController(video, options) {
|
|
|
515
721
|
};
|
|
516
722
|
}
|
|
517
723
|
|
|
724
|
+
// src/sdk/hlsAdPlayer.ts
|
|
725
|
+
import Hls from "hls.js";
|
|
726
|
+
function createHlsAdPlayer(contentVideo, options) {
|
|
727
|
+
let adPlaying = false;
|
|
728
|
+
let originalMutedState = false;
|
|
729
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
730
|
+
const licenseKey = options == null ? void 0 : options.licenseKey;
|
|
731
|
+
const mainHlsInstance = options == null ? void 0 : options.mainHlsInstance;
|
|
732
|
+
let adVideoElement;
|
|
733
|
+
let adHls;
|
|
734
|
+
let adContainerEl;
|
|
735
|
+
let currentAd;
|
|
736
|
+
let sessionId;
|
|
737
|
+
let trackingFired = {
|
|
738
|
+
impression: false,
|
|
739
|
+
start: false,
|
|
740
|
+
firstQuartile: false,
|
|
741
|
+
midpoint: false,
|
|
742
|
+
thirdQuartile: false,
|
|
743
|
+
complete: false
|
|
744
|
+
};
|
|
745
|
+
function emit(event, payload) {
|
|
746
|
+
const set = listeners.get(event);
|
|
747
|
+
if (!set) return;
|
|
748
|
+
for (const fn of Array.from(set)) {
|
|
749
|
+
try {
|
|
750
|
+
fn(payload);
|
|
751
|
+
} catch (error) {
|
|
752
|
+
console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
function generateSessionId() {
|
|
757
|
+
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
758
|
+
}
|
|
759
|
+
function fireTrackingPixels(urls) {
|
|
760
|
+
if (!urls || urls.length === 0) return;
|
|
761
|
+
urls.forEach((url) => {
|
|
762
|
+
try {
|
|
763
|
+
let trackingUrl = url;
|
|
764
|
+
if (sessionId) {
|
|
765
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
|
|
766
|
+
}
|
|
767
|
+
if (licenseKey) {
|
|
768
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
|
|
769
|
+
}
|
|
770
|
+
const img = new Image(1, 1);
|
|
771
|
+
img.src = trackingUrl;
|
|
772
|
+
console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
|
|
773
|
+
} catch (error) {
|
|
774
|
+
console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
function getMainStreamQuality() {
|
|
779
|
+
if (!mainHlsInstance || !mainHlsInstance.levels) {
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
const currentLevel = mainHlsInstance.currentLevel;
|
|
783
|
+
if (currentLevel === -1 || !mainHlsInstance.levels[currentLevel]) {
|
|
784
|
+
const autoLevel = mainHlsInstance.loadLevel;
|
|
785
|
+
if (autoLevel !== -1 && mainHlsInstance.levels[autoLevel]) {
|
|
786
|
+
const level2 = mainHlsInstance.levels[autoLevel];
|
|
787
|
+
return {
|
|
788
|
+
width: level2.width || 1920,
|
|
789
|
+
height: level2.height || 1080,
|
|
790
|
+
bitrate: level2.bitrate || 5e6
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
const level = mainHlsInstance.levels[currentLevel];
|
|
796
|
+
return {
|
|
797
|
+
width: level.width || 1920,
|
|
798
|
+
height: level.height || 1080,
|
|
799
|
+
bitrate: level.bitrate || 5e6
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function selectBestMediaFile(mediaFiles) {
|
|
803
|
+
if (mediaFiles.length === 0) {
|
|
804
|
+
throw new Error("No media files available");
|
|
805
|
+
}
|
|
806
|
+
const firstFile = mediaFiles[0];
|
|
807
|
+
if (!firstFile) {
|
|
808
|
+
throw new Error("No media files available");
|
|
809
|
+
}
|
|
810
|
+
if (mediaFiles.length === 1) {
|
|
811
|
+
return firstFile;
|
|
812
|
+
}
|
|
813
|
+
const mainQuality = getMainStreamQuality();
|
|
814
|
+
if (!mainQuality) {
|
|
815
|
+
console.log("[HlsAdPlayer] No main stream quality info, using first media file");
|
|
816
|
+
return firstFile;
|
|
817
|
+
}
|
|
818
|
+
console.log("[HlsAdPlayer] Main stream quality:", mainQuality);
|
|
819
|
+
const scoredFiles = mediaFiles.map((file) => {
|
|
820
|
+
const widthDiff = Math.abs(file.width - mainQuality.width);
|
|
821
|
+
const heightDiff = Math.abs(file.height - mainQuality.height);
|
|
822
|
+
const resolutionDiff = widthDiff + heightDiff;
|
|
823
|
+
const fileBitrate = (file.bitrate || 5e3) * 1e3;
|
|
824
|
+
const bitrateDiff = Math.abs(fileBitrate - mainQuality.bitrate);
|
|
825
|
+
const score = resolutionDiff * 2 + bitrateDiff / 1e3;
|
|
826
|
+
return { file, score, resolutionDiff, bitrateDiff };
|
|
827
|
+
});
|
|
828
|
+
scoredFiles.sort((a, b) => a.score - b.score);
|
|
829
|
+
const bestMatch = scoredFiles[0];
|
|
830
|
+
if (!bestMatch) {
|
|
831
|
+
console.log("[HlsAdPlayer] No best match found, using first media file");
|
|
832
|
+
return firstFile;
|
|
833
|
+
}
|
|
834
|
+
console.log("[HlsAdPlayer] Selected media file:", {
|
|
835
|
+
url: bestMatch.file.url,
|
|
836
|
+
resolution: `${bestMatch.file.width}x${bestMatch.file.height}`,
|
|
837
|
+
bitrate: bestMatch.file.bitrate,
|
|
838
|
+
score: bestMatch.score,
|
|
839
|
+
resolutionDiff: bestMatch.resolutionDiff,
|
|
840
|
+
bitrateDiff: bestMatch.bitrateDiff
|
|
841
|
+
});
|
|
842
|
+
return bestMatch.file;
|
|
843
|
+
}
|
|
844
|
+
function parseVastXml(xmlString) {
|
|
845
|
+
var _a, _b, _c, _d;
|
|
846
|
+
try {
|
|
847
|
+
const parser = new DOMParser();
|
|
848
|
+
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
|
849
|
+
const parserError = xmlDoc.querySelector("parsererror");
|
|
850
|
+
if (parserError) {
|
|
851
|
+
console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
const adElement = xmlDoc.querySelector("Ad");
|
|
855
|
+
if (!adElement) {
|
|
856
|
+
console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
const adId = adElement.getAttribute("id") || "unknown";
|
|
860
|
+
const title = ((_a = xmlDoc.querySelector("AdTitle")) == null ? void 0 : _a.textContent) || "Ad";
|
|
861
|
+
const durationText = ((_b = xmlDoc.querySelector("Duration")) == null ? void 0 : _b.textContent) || "00:00:30";
|
|
862
|
+
const durationParts = durationText.split(":");
|
|
863
|
+
const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
864
|
+
const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
|
|
865
|
+
const mediaFiles = [];
|
|
866
|
+
mediaFileElements.forEach((mf) => {
|
|
867
|
+
var _a2;
|
|
868
|
+
const type = mf.getAttribute("type") || "";
|
|
869
|
+
if (type === "application/x-mpegURL" || type.includes("m3u8")) {
|
|
870
|
+
const bitrateAttr = mf.getAttribute("bitrate");
|
|
871
|
+
const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
872
|
+
mediaFiles.push({
|
|
873
|
+
url: ((_a2 = mf.textContent) == null ? void 0 : _a2.trim()) || "",
|
|
874
|
+
type,
|
|
875
|
+
width: parseInt(mf.getAttribute("width") || "1920", 10),
|
|
876
|
+
height: parseInt(mf.getAttribute("height") || "1080", 10),
|
|
877
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
if (mediaFiles.length === 0) {
|
|
882
|
+
console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
const trackingUrls = {
|
|
886
|
+
impression: [],
|
|
887
|
+
start: [],
|
|
888
|
+
firstQuartile: [],
|
|
889
|
+
midpoint: [],
|
|
890
|
+
thirdQuartile: [],
|
|
891
|
+
complete: [],
|
|
892
|
+
mute: [],
|
|
893
|
+
unmute: [],
|
|
894
|
+
pause: [],
|
|
895
|
+
resume: [],
|
|
896
|
+
fullscreen: [],
|
|
897
|
+
exitFullscreen: [],
|
|
898
|
+
skip: [],
|
|
899
|
+
error: []
|
|
900
|
+
};
|
|
901
|
+
xmlDoc.querySelectorAll("Impression").forEach((el) => {
|
|
902
|
+
var _a2;
|
|
903
|
+
const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
|
|
904
|
+
if (url) trackingUrls.impression.push(url);
|
|
905
|
+
});
|
|
906
|
+
xmlDoc.querySelectorAll("Tracking").forEach((el) => {
|
|
907
|
+
var _a2;
|
|
908
|
+
const event = el.getAttribute("event");
|
|
909
|
+
const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
|
|
910
|
+
if (event && url) {
|
|
911
|
+
const eventKey = event;
|
|
912
|
+
if (trackingUrls[eventKey]) {
|
|
913
|
+
trackingUrls[eventKey].push(url);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
const clickThrough = (_d = (_c = xmlDoc.querySelector("ClickThrough")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim();
|
|
918
|
+
return {
|
|
919
|
+
id: adId,
|
|
920
|
+
title,
|
|
921
|
+
duration,
|
|
922
|
+
mediaFiles,
|
|
923
|
+
trackingUrls,
|
|
924
|
+
clickThrough
|
|
925
|
+
};
|
|
926
|
+
} catch (error) {
|
|
927
|
+
console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
function createAdVideoElement() {
|
|
932
|
+
const video = document.createElement("video");
|
|
933
|
+
video.style.position = "absolute";
|
|
934
|
+
video.style.left = "0";
|
|
935
|
+
video.style.top = "0";
|
|
936
|
+
video.style.width = "100%";
|
|
937
|
+
video.style.height = "100%";
|
|
938
|
+
video.style.objectFit = "contain";
|
|
939
|
+
video.style.backgroundColor = "#000";
|
|
940
|
+
video.playsInline = true;
|
|
941
|
+
video.muted = false;
|
|
942
|
+
return video;
|
|
943
|
+
}
|
|
944
|
+
function setupAdEventListeners() {
|
|
945
|
+
if (!adVideoElement || !currentAd) return;
|
|
946
|
+
adVideoElement.addEventListener("timeupdate", () => {
|
|
947
|
+
if (!currentAd || !adVideoElement) return;
|
|
948
|
+
const progress = adVideoElement.currentTime / currentAd.duration;
|
|
949
|
+
if (progress >= 0.25 && !trackingFired.firstQuartile) {
|
|
950
|
+
trackingFired.firstQuartile = true;
|
|
951
|
+
fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
|
|
952
|
+
}
|
|
953
|
+
if (progress >= 0.5 && !trackingFired.midpoint) {
|
|
954
|
+
trackingFired.midpoint = true;
|
|
955
|
+
fireTrackingPixels(currentAd.trackingUrls.midpoint);
|
|
956
|
+
}
|
|
957
|
+
if (progress >= 0.75 && !trackingFired.thirdQuartile) {
|
|
958
|
+
trackingFired.thirdQuartile = true;
|
|
959
|
+
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
adVideoElement.addEventListener("playing", () => {
|
|
963
|
+
if (!currentAd || trackingFired.start) return;
|
|
964
|
+
trackingFired.start = true;
|
|
965
|
+
fireTrackingPixels(currentAd.trackingUrls.start);
|
|
966
|
+
console.log("[HlsAdPlayer] Ad started playing");
|
|
967
|
+
});
|
|
968
|
+
adVideoElement.addEventListener("ended", () => {
|
|
969
|
+
if (!currentAd || trackingFired.complete) return;
|
|
970
|
+
trackingFired.complete = true;
|
|
971
|
+
fireTrackingPixels(currentAd.trackingUrls.complete);
|
|
972
|
+
console.log("[HlsAdPlayer] Ad completed");
|
|
973
|
+
handleAdComplete();
|
|
974
|
+
});
|
|
975
|
+
adVideoElement.addEventListener("error", (e) => {
|
|
976
|
+
console.error("[HlsAdPlayer] Ad video error:", e);
|
|
977
|
+
if (currentAd) {
|
|
978
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
979
|
+
}
|
|
980
|
+
handleAdError();
|
|
981
|
+
});
|
|
982
|
+
adVideoElement.addEventListener("volumechange", () => {
|
|
983
|
+
if (!currentAd) return;
|
|
984
|
+
if (adVideoElement.muted) {
|
|
985
|
+
fireTrackingPixels(currentAd.trackingUrls.mute);
|
|
986
|
+
} else {
|
|
987
|
+
fireTrackingPixels(currentAd.trackingUrls.unmute);
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
adVideoElement.addEventListener("pause", () => {
|
|
991
|
+
if (currentAd && !adVideoElement.ended) {
|
|
992
|
+
fireTrackingPixels(currentAd.trackingUrls.pause);
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
adVideoElement.addEventListener("play", () => {
|
|
996
|
+
if (currentAd && adVideoElement.currentTime > 0) {
|
|
997
|
+
fireTrackingPixels(currentAd.trackingUrls.resume);
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
function handleAdComplete() {
|
|
1002
|
+
console.log("[HlsAdPlayer] Handling ad completion");
|
|
1003
|
+
adPlaying = false;
|
|
1004
|
+
contentVideo.muted = originalMutedState;
|
|
1005
|
+
if (adContainerEl) {
|
|
1006
|
+
adContainerEl.style.display = "none";
|
|
1007
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1008
|
+
}
|
|
1009
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
1010
|
+
contentVideo.play().catch(() => {
|
|
1011
|
+
});
|
|
1012
|
+
console.log("[HlsAdPlayer] Content resumed (VOD mode)");
|
|
1013
|
+
} else {
|
|
1014
|
+
console.log("[HlsAdPlayer] Content unmuted (Live mode)");
|
|
1015
|
+
}
|
|
1016
|
+
emit("content_resume");
|
|
1017
|
+
emit("all_ads_completed");
|
|
1018
|
+
}
|
|
1019
|
+
function handleAdError() {
|
|
1020
|
+
console.log("[HlsAdPlayer] Handling ad error");
|
|
1021
|
+
adPlaying = false;
|
|
1022
|
+
contentVideo.muted = originalMutedState;
|
|
1023
|
+
if (adContainerEl) {
|
|
1024
|
+
adContainerEl.style.display = "none";
|
|
1025
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1026
|
+
}
|
|
1027
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
1028
|
+
if (contentVideo.paused) {
|
|
1029
|
+
contentVideo.play().catch(() => {
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
emit("ad_error");
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
initialize() {
|
|
1037
|
+
var _a;
|
|
1038
|
+
console.log("[HlsAdPlayer] Initializing");
|
|
1039
|
+
if (!adContainerEl) {
|
|
1040
|
+
const container = document.createElement("div");
|
|
1041
|
+
container.style.position = "absolute";
|
|
1042
|
+
container.style.left = "0";
|
|
1043
|
+
container.style.top = "0";
|
|
1044
|
+
container.style.right = "0";
|
|
1045
|
+
container.style.bottom = "0";
|
|
1046
|
+
container.style.display = "none";
|
|
1047
|
+
container.style.alignItems = "center";
|
|
1048
|
+
container.style.justifyContent = "center";
|
|
1049
|
+
container.style.pointerEvents = "none";
|
|
1050
|
+
container.style.zIndex = "2";
|
|
1051
|
+
container.style.backgroundColor = "#000";
|
|
1052
|
+
(_a = contentVideo.parentElement) == null ? void 0 : _a.appendChild(container);
|
|
1053
|
+
adContainerEl = container;
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
async requestAds(vastTagUrl) {
|
|
1057
|
+
console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
|
|
1058
|
+
if (adPlaying) {
|
|
1059
|
+
console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
|
|
1060
|
+
return Promise.reject(new Error("Ad already playing"));
|
|
1061
|
+
}
|
|
1062
|
+
try {
|
|
1063
|
+
sessionId = generateSessionId();
|
|
1064
|
+
const response = await fetch(vastTagUrl);
|
|
1065
|
+
if (!response.ok) {
|
|
1066
|
+
throw new Error(`Failed to fetch VAST: ${response.statusText}`);
|
|
1067
|
+
}
|
|
1068
|
+
const vastXml = await response.text();
|
|
1069
|
+
console.log("[HlsAdPlayer] VAST XML received");
|
|
1070
|
+
const ad = parseVastXml(vastXml);
|
|
1071
|
+
if (!ad) {
|
|
1072
|
+
throw new Error("Failed to parse VAST XML or no ads available");
|
|
1073
|
+
}
|
|
1074
|
+
currentAd = ad;
|
|
1075
|
+
console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
|
|
1076
|
+
fireTrackingPixels(ad.trackingUrls.impression);
|
|
1077
|
+
trackingFired.impression = true;
|
|
1078
|
+
return Promise.resolve();
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
console.error("[HlsAdPlayer] Error requesting ads:", error);
|
|
1081
|
+
emit("ad_error");
|
|
1082
|
+
return Promise.reject(error);
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
async play() {
|
|
1086
|
+
if (!currentAd) {
|
|
1087
|
+
console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
|
|
1088
|
+
return Promise.reject(new Error("No ad loaded"));
|
|
1089
|
+
}
|
|
1090
|
+
console.log("[HlsAdPlayer] Starting ad playback");
|
|
1091
|
+
try {
|
|
1092
|
+
if (!adVideoElement) {
|
|
1093
|
+
adVideoElement = createAdVideoElement();
|
|
1094
|
+
adContainerEl == null ? void 0 : adContainerEl.appendChild(adVideoElement);
|
|
1095
|
+
setupAdEventListeners();
|
|
1096
|
+
}
|
|
1097
|
+
trackingFired = {
|
|
1098
|
+
impression: trackingFired.impression,
|
|
1099
|
+
start: false,
|
|
1100
|
+
firstQuartile: false,
|
|
1101
|
+
midpoint: false,
|
|
1102
|
+
thirdQuartile: false,
|
|
1103
|
+
complete: false
|
|
1104
|
+
};
|
|
1105
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
1106
|
+
contentVideo.pause();
|
|
1107
|
+
console.log("[HlsAdPlayer] Content paused (VOD mode)");
|
|
1108
|
+
} else {
|
|
1109
|
+
console.log("[HlsAdPlayer] Content continues (Live mode)");
|
|
1110
|
+
}
|
|
1111
|
+
contentVideo.muted = true;
|
|
1112
|
+
adPlaying = true;
|
|
1113
|
+
if (adContainerEl) {
|
|
1114
|
+
adContainerEl.style.display = "flex";
|
|
1115
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
1116
|
+
}
|
|
1117
|
+
emit("content_pause");
|
|
1118
|
+
const mediaFile = selectBestMediaFile(currentAd.mediaFiles);
|
|
1119
|
+
if (!mediaFile) {
|
|
1120
|
+
throw new Error("No media file available for ad");
|
|
1121
|
+
}
|
|
1122
|
+
console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
|
|
1123
|
+
if (Hls.isSupported()) {
|
|
1124
|
+
if (adHls) {
|
|
1125
|
+
adHls.destroy();
|
|
1126
|
+
}
|
|
1127
|
+
adHls = new Hls({
|
|
1128
|
+
enableWorker: true,
|
|
1129
|
+
lowLatencyMode: false
|
|
1130
|
+
});
|
|
1131
|
+
adHls.loadSource(mediaFile.url);
|
|
1132
|
+
adHls.attachMedia(adVideoElement);
|
|
1133
|
+
adHls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
1134
|
+
console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
|
|
1135
|
+
adVideoElement.play().catch((error) => {
|
|
1136
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1137
|
+
handleAdError();
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
adHls.on(Hls.Events.ERROR, (event, data) => {
|
|
1141
|
+
console.error("[HlsAdPlayer] HLS error:", data);
|
|
1142
|
+
if (data.fatal) {
|
|
1143
|
+
handleAdError();
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
} else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
|
|
1147
|
+
adVideoElement.src = mediaFile.url;
|
|
1148
|
+
adVideoElement.play().catch((error) => {
|
|
1149
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1150
|
+
handleAdError();
|
|
1151
|
+
});
|
|
1152
|
+
} else {
|
|
1153
|
+
throw new Error("HLS not supported");
|
|
1154
|
+
}
|
|
1155
|
+
return Promise.resolve();
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
console.error("[HlsAdPlayer] Error playing ad:", error);
|
|
1158
|
+
handleAdError();
|
|
1159
|
+
return Promise.reject(error);
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
async stop() {
|
|
1163
|
+
console.log("[HlsAdPlayer] Stopping ad");
|
|
1164
|
+
adPlaying = false;
|
|
1165
|
+
contentVideo.muted = originalMutedState;
|
|
1166
|
+
if (adContainerEl) {
|
|
1167
|
+
adContainerEl.style.display = "none";
|
|
1168
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1169
|
+
}
|
|
1170
|
+
if (adHls) {
|
|
1171
|
+
adHls.destroy();
|
|
1172
|
+
adHls = void 0;
|
|
1173
|
+
}
|
|
1174
|
+
if (adVideoElement) {
|
|
1175
|
+
adVideoElement.pause();
|
|
1176
|
+
adVideoElement.src = "";
|
|
1177
|
+
}
|
|
1178
|
+
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
1179
|
+
contentVideo.play().catch(() => {
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
currentAd = void 0;
|
|
1183
|
+
},
|
|
1184
|
+
destroy() {
|
|
1185
|
+
console.log("[HlsAdPlayer] Destroying");
|
|
1186
|
+
adPlaying = false;
|
|
1187
|
+
contentVideo.muted = originalMutedState;
|
|
1188
|
+
if (adHls) {
|
|
1189
|
+
adHls.destroy();
|
|
1190
|
+
adHls = void 0;
|
|
1191
|
+
}
|
|
1192
|
+
if (adVideoElement) {
|
|
1193
|
+
adVideoElement.pause();
|
|
1194
|
+
adVideoElement.src = "";
|
|
1195
|
+
adVideoElement.remove();
|
|
1196
|
+
adVideoElement = void 0;
|
|
1197
|
+
}
|
|
1198
|
+
if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
|
|
1199
|
+
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
1200
|
+
}
|
|
1201
|
+
adContainerEl = void 0;
|
|
1202
|
+
currentAd = void 0;
|
|
1203
|
+
listeners.clear();
|
|
1204
|
+
},
|
|
1205
|
+
isAdPlaying() {
|
|
1206
|
+
return adPlaying;
|
|
1207
|
+
},
|
|
1208
|
+
resize(width, height) {
|
|
1209
|
+
console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
|
|
1210
|
+
if (adContainerEl) {
|
|
1211
|
+
adContainerEl.style.width = `${width}px`;
|
|
1212
|
+
adContainerEl.style.height = `${height}px`;
|
|
1213
|
+
}
|
|
1214
|
+
if (adVideoElement) {
|
|
1215
|
+
adVideoElement.style.width = `${width}px`;
|
|
1216
|
+
adVideoElement.style.height = `${height}px`;
|
|
1217
|
+
}
|
|
1218
|
+
},
|
|
1219
|
+
on(event, listener) {
|
|
1220
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
1221
|
+
listeners.get(event).add(listener);
|
|
1222
|
+
},
|
|
1223
|
+
off(event, listener) {
|
|
1224
|
+
var _a;
|
|
1225
|
+
(_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
|
|
1226
|
+
},
|
|
1227
|
+
updateOriginalMutedState(muted) {
|
|
1228
|
+
originalMutedState = muted;
|
|
1229
|
+
},
|
|
1230
|
+
getOriginalMutedState() {
|
|
1231
|
+
return originalMutedState;
|
|
1232
|
+
},
|
|
1233
|
+
setAdVolume(volume) {
|
|
1234
|
+
if (adVideoElement && adPlaying) {
|
|
1235
|
+
adVideoElement.volume = Math.max(0, Math.min(1, volume));
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
getAdVolume() {
|
|
1239
|
+
if (adVideoElement && adPlaying) {
|
|
1240
|
+
return adVideoElement.volume;
|
|
1241
|
+
}
|
|
1242
|
+
return 1;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
|
|
518
1247
|
// src/utils/tracking.ts
|
|
519
1248
|
var cachedBrowserId = null;
|
|
520
1249
|
function getClientInfo() {
|
|
1250
|
+
var _a, _b, _c, _d;
|
|
521
1251
|
const ua = navigator.userAgent;
|
|
522
1252
|
const platform = navigator.platform;
|
|
523
1253
|
const vendor = navigator.vendor || "";
|
|
@@ -525,12 +1255,12 @@ function getClientInfo() {
|
|
|
525
1255
|
const memory = navigator.deviceMemory || null;
|
|
526
1256
|
const hardwareConcurrency = navigator.hardwareConcurrency || 1;
|
|
527
1257
|
const screenInfo = {
|
|
528
|
-
width: screen
|
|
529
|
-
height: screen
|
|
530
|
-
availWidth: screen
|
|
531
|
-
availHeight: screen
|
|
532
|
-
orientation: screen
|
|
533
|
-
pixelDepth: screen
|
|
1258
|
+
width: screen == null ? void 0 : screen.width,
|
|
1259
|
+
height: screen == null ? void 0 : screen.height,
|
|
1260
|
+
availWidth: screen == null ? void 0 : screen.availWidth,
|
|
1261
|
+
availHeight: screen == null ? void 0 : screen.availHeight,
|
|
1262
|
+
orientation: ((_a = screen == null ? void 0 : screen.orientation) == null ? void 0 : _a.type) || "",
|
|
1263
|
+
pixelDepth: screen == null ? void 0 : screen.pixelDepth
|
|
534
1264
|
};
|
|
535
1265
|
let deviceType = "desktop";
|
|
536
1266
|
let brand = "Unknown";
|
|
@@ -627,10 +1357,10 @@ function getClientInfo() {
|
|
|
627
1357
|
if (vendor.includes("Samsung") || ua.includes("SM-")) brand = "Samsung";
|
|
628
1358
|
}
|
|
629
1359
|
isWebView = /wv|WebView|Linux; U;/.test(ua);
|
|
630
|
-
if (window
|
|
1360
|
+
if ((window == null ? void 0 : window.outerHeight) === 0 && (window == null ? void 0 : window.outerWidth) === 0) {
|
|
631
1361
|
isWebView = true;
|
|
632
1362
|
}
|
|
633
|
-
isWebApp = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true || window.screen
|
|
1363
|
+
isWebApp = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true || ((_c = (_b = window.screen) == null ? void 0 : _b.orientation) == null ? void 0 : _c.angle) !== void 0;
|
|
634
1364
|
return {
|
|
635
1365
|
brand,
|
|
636
1366
|
os,
|
|
@@ -651,7 +1381,7 @@ function getClientInfo() {
|
|
|
651
1381
|
deviceMemory: memory,
|
|
652
1382
|
maxTouchPoints,
|
|
653
1383
|
language: navigator.language,
|
|
654
|
-
languages: navigator.languages
|
|
1384
|
+
languages: ((_d = navigator.languages) == null ? void 0 : _d.join(",")) || "",
|
|
655
1385
|
cookieEnabled: navigator.cookieEnabled,
|
|
656
1386
|
doNotTrack: navigator.doNotTrack || "",
|
|
657
1387
|
referrer: document.referrer,
|
|
@@ -764,6 +1494,215 @@ async function sendHeartbeat(licenseKey) {
|
|
|
764
1494
|
}
|
|
765
1495
|
}
|
|
766
1496
|
|
|
1497
|
+
// src/utils/polyfills.ts
|
|
1498
|
+
function polyfillURLSearchParams() {
|
|
1499
|
+
if (typeof URLSearchParams !== "undefined") {
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
class URLSearchParamsPolyfill {
|
|
1503
|
+
constructor(init) {
|
|
1504
|
+
this.params = /* @__PURE__ */ new Map();
|
|
1505
|
+
if (typeof init === "string") {
|
|
1506
|
+
this.parseQueryString(init);
|
|
1507
|
+
} else if (init instanceof URLSearchParamsPolyfill) {
|
|
1508
|
+
init.forEach((value, key) => {
|
|
1509
|
+
this.append(key, value);
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
parseQueryString(query) {
|
|
1514
|
+
const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
|
|
1515
|
+
if (!cleanQuery) return;
|
|
1516
|
+
cleanQuery.split("&").forEach((param) => {
|
|
1517
|
+
const [key, value] = param.split("=");
|
|
1518
|
+
if (key) {
|
|
1519
|
+
const decodedKey = this.safeDecodeURIComponent(key);
|
|
1520
|
+
const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
|
|
1521
|
+
this.append(decodedKey, decodedValue);
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
safeDecodeURIComponent(str) {
|
|
1526
|
+
try {
|
|
1527
|
+
return decodeURIComponent(str.replace(/\+/g, " "));
|
|
1528
|
+
} catch (e) {
|
|
1529
|
+
return str;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
append(name, value) {
|
|
1533
|
+
const values = this.params.get(name) || [];
|
|
1534
|
+
values.push(String(value));
|
|
1535
|
+
this.params.set(name, values);
|
|
1536
|
+
}
|
|
1537
|
+
delete(name) {
|
|
1538
|
+
this.params.delete(name);
|
|
1539
|
+
}
|
|
1540
|
+
get(name) {
|
|
1541
|
+
const values = this.params.get(name);
|
|
1542
|
+
return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
|
|
1543
|
+
}
|
|
1544
|
+
getAll(name) {
|
|
1545
|
+
return this.params.get(name) || [];
|
|
1546
|
+
}
|
|
1547
|
+
has(name) {
|
|
1548
|
+
return this.params.has(name);
|
|
1549
|
+
}
|
|
1550
|
+
set(name, value) {
|
|
1551
|
+
this.params.set(name, [String(value)]);
|
|
1552
|
+
}
|
|
1553
|
+
forEach(callback) {
|
|
1554
|
+
this.params.forEach((values, key) => {
|
|
1555
|
+
values.forEach((value) => {
|
|
1556
|
+
callback(value, key, this);
|
|
1557
|
+
});
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
toString() {
|
|
1561
|
+
const parts = [];
|
|
1562
|
+
this.params.forEach((values, key) => {
|
|
1563
|
+
values.forEach((value) => {
|
|
1564
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
1565
|
+
});
|
|
1566
|
+
});
|
|
1567
|
+
return parts.join("&");
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
window.URLSearchParams = URLSearchParamsPolyfill;
|
|
1571
|
+
}
|
|
1572
|
+
function polyfillTextEncoder() {
|
|
1573
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
class TextEncoderPolyfill {
|
|
1577
|
+
constructor() {
|
|
1578
|
+
this.encoding = "utf-8";
|
|
1579
|
+
}
|
|
1580
|
+
encode(str) {
|
|
1581
|
+
const utf8 = [];
|
|
1582
|
+
for (let i = 0; i < str.length; i++) {
|
|
1583
|
+
let charcode = str.charCodeAt(i);
|
|
1584
|
+
if (charcode < 128) {
|
|
1585
|
+
utf8.push(charcode);
|
|
1586
|
+
} else if (charcode < 2048) {
|
|
1587
|
+
utf8.push(192 | charcode >> 6, 128 | charcode & 63);
|
|
1588
|
+
} else if (charcode < 55296 || charcode >= 57344) {
|
|
1589
|
+
utf8.push(
|
|
1590
|
+
224 | charcode >> 12,
|
|
1591
|
+
128 | charcode >> 6 & 63,
|
|
1592
|
+
128 | charcode & 63
|
|
1593
|
+
);
|
|
1594
|
+
} else {
|
|
1595
|
+
i++;
|
|
1596
|
+
charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
|
|
1597
|
+
utf8.push(
|
|
1598
|
+
240 | charcode >> 18,
|
|
1599
|
+
128 | charcode >> 12 & 63,
|
|
1600
|
+
128 | charcode >> 6 & 63,
|
|
1601
|
+
128 | charcode & 63
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return new Uint8Array(utf8);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
window.TextEncoder = TextEncoderPolyfill;
|
|
1609
|
+
}
|
|
1610
|
+
function polyfillPromiseFinally() {
|
|
1611
|
+
if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
|
|
1612
|
+
Promise.prototype.finally = function(callback) {
|
|
1613
|
+
const constructor = this.constructor;
|
|
1614
|
+
return this.then(
|
|
1615
|
+
(value) => constructor.resolve(callback()).then(() => value),
|
|
1616
|
+
(reason) => constructor.resolve(callback()).then(() => {
|
|
1617
|
+
throw reason;
|
|
1618
|
+
})
|
|
1619
|
+
);
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function polyfillObjectAssign() {
|
|
1624
|
+
if (typeof Object.assign !== "function") {
|
|
1625
|
+
Object.assign = function(target, ...sources) {
|
|
1626
|
+
if (target == null) {
|
|
1627
|
+
throw new TypeError("Cannot convert undefined or null to object");
|
|
1628
|
+
}
|
|
1629
|
+
const to = Object(target);
|
|
1630
|
+
for (let i = 0; i < sources.length; i++) {
|
|
1631
|
+
const nextSource = sources[i];
|
|
1632
|
+
if (nextSource != null) {
|
|
1633
|
+
for (const nextKey in nextSource) {
|
|
1634
|
+
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
|
1635
|
+
to[nextKey] = nextSource[nextKey];
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return to;
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function polyfillArrayFrom() {
|
|
1645
|
+
if (!Array.from) {
|
|
1646
|
+
Array.from = function(arrayLike, mapFn, thisArg) {
|
|
1647
|
+
const items = Object(arrayLike);
|
|
1648
|
+
if (arrayLike == null) {
|
|
1649
|
+
throw new TypeError("Array.from requires an array-like object");
|
|
1650
|
+
}
|
|
1651
|
+
const len = items.length >>> 0;
|
|
1652
|
+
const result = new Array(len);
|
|
1653
|
+
for (let i = 0; i < len; i++) {
|
|
1654
|
+
if (mapFn) {
|
|
1655
|
+
result[i] = mapFn.call(thisArg, items[i], i);
|
|
1656
|
+
} else {
|
|
1657
|
+
result[i] = items[i];
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return result;
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function polyfillStringStartsWith() {
|
|
1665
|
+
if (!String.prototype.startsWith) {
|
|
1666
|
+
String.prototype.startsWith = function(search, pos) {
|
|
1667
|
+
pos = !pos || pos < 0 ? 0 : +pos;
|
|
1668
|
+
return this.substring(pos, pos + search.length) === search;
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
function polyfillStringEndsWith() {
|
|
1673
|
+
if (!String.prototype.endsWith) {
|
|
1674
|
+
String.prototype.endsWith = function(search, length) {
|
|
1675
|
+
if (length === void 0 || length > this.length) {
|
|
1676
|
+
length = this.length;
|
|
1677
|
+
}
|
|
1678
|
+
return this.substring(length - search.length, length) === search;
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
function polyfillStringIncludes() {
|
|
1683
|
+
if (!String.prototype.includes) {
|
|
1684
|
+
String.prototype.includes = function(search, start) {
|
|
1685
|
+
if (typeof start !== "number") {
|
|
1686
|
+
start = 0;
|
|
1687
|
+
}
|
|
1688
|
+
if (start + search.length > this.length) {
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
return this.indexOf(search, start) !== -1;
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function initializePolyfills() {
|
|
1696
|
+
polyfillObjectAssign();
|
|
1697
|
+
polyfillArrayFrom();
|
|
1698
|
+
polyfillStringStartsWith();
|
|
1699
|
+
polyfillStringEndsWith();
|
|
1700
|
+
polyfillStringIncludes();
|
|
1701
|
+
polyfillURLSearchParams();
|
|
1702
|
+
polyfillTextEncoder();
|
|
1703
|
+
polyfillPromiseFinally();
|
|
1704
|
+
}
|
|
1705
|
+
|
|
767
1706
|
// src/player/StormcloudVideoPlayer.ts
|
|
768
1707
|
var StormcloudVideoPlayer = class {
|
|
769
1708
|
constructor(config) {
|
|
@@ -776,13 +1715,44 @@ var StormcloudVideoPlayer = class {
|
|
|
776
1715
|
this.totalAdsInBreak = 0;
|
|
777
1716
|
this.showAds = false;
|
|
778
1717
|
this.isLiveStream = false;
|
|
779
|
-
|
|
1718
|
+
initializePolyfills();
|
|
1719
|
+
const browserOverrides = getBrowserConfigOverrides();
|
|
1720
|
+
this.config = { ...config, ...browserOverrides };
|
|
780
1721
|
this.video = config.videoElement;
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1722
|
+
logBrowserInfo(config.debugAdTiming);
|
|
1723
|
+
this.ima = this.createAdPlayer(false);
|
|
1724
|
+
}
|
|
1725
|
+
createAdPlayer(continueLiveStreamDuringAds) {
|
|
1726
|
+
const vastMode = this.config.vastMode || "default";
|
|
1727
|
+
let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
|
|
1728
|
+
if (adPlayerType === "ima" && !supportsGoogleIMA()) {
|
|
1729
|
+
if (this.config.debugAdTiming) {
|
|
1730
|
+
console.warn(
|
|
1731
|
+
"[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
adPlayerType = "hls";
|
|
1735
|
+
}
|
|
1736
|
+
if (adPlayerType === "hls") {
|
|
1737
|
+
if (this.config.debugAdTiming) {
|
|
1738
|
+
console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
|
|
1739
|
+
}
|
|
1740
|
+
return createHlsAdPlayer(this.video, {
|
|
1741
|
+
continueLiveStreamDuringAds,
|
|
1742
|
+
...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {},
|
|
1743
|
+
...this.hls ? { mainHlsInstance: this.hls } : {}
|
|
1744
|
+
});
|
|
1745
|
+
} else {
|
|
1746
|
+
if (this.config.debugAdTiming) {
|
|
1747
|
+
console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
|
|
1748
|
+
}
|
|
1749
|
+
return createImaController(this.video, {
|
|
1750
|
+
continueLiveStreamDuringAds
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
784
1753
|
}
|
|
785
1754
|
async load() {
|
|
1755
|
+
var _a, _b;
|
|
786
1756
|
if (!this.attached) {
|
|
787
1757
|
this.attach();
|
|
788
1758
|
}
|
|
@@ -799,7 +1769,7 @@ var StormcloudVideoPlayer = class {
|
|
|
799
1769
|
this.initializeTracking();
|
|
800
1770
|
if (this.shouldUseNativeHls()) {
|
|
801
1771
|
this.video.src = this.config.src;
|
|
802
|
-
this.isLiveStream = this.config.lowLatencyMode
|
|
1772
|
+
this.isLiveStream = (_a = this.config.lowLatencyMode) != null ? _a : false;
|
|
803
1773
|
if (this.config.debugAdTiming) {
|
|
804
1774
|
console.log(
|
|
805
1775
|
"[StormcloudVideoPlayer] allowNativeHls: true - VOD mode detected:",
|
|
@@ -811,17 +1781,15 @@ var StormcloudVideoPlayer = class {
|
|
|
811
1781
|
);
|
|
812
1782
|
}
|
|
813
1783
|
this.ima.destroy();
|
|
814
|
-
this.ima =
|
|
815
|
-
continueLiveStreamDuringAds: false
|
|
816
|
-
});
|
|
1784
|
+
this.ima = this.createAdPlayer(false);
|
|
817
1785
|
this.ima.initialize();
|
|
818
1786
|
if (this.config.autoplay) {
|
|
819
|
-
await this.video.play()
|
|
820
|
-
});
|
|
1787
|
+
await ((_b = this.video.play()) == null ? void 0 : _b.catch(() => {
|
|
1788
|
+
}));
|
|
821
1789
|
}
|
|
822
1790
|
return;
|
|
823
1791
|
}
|
|
824
|
-
this.hls = new
|
|
1792
|
+
this.hls = new Hls2({
|
|
825
1793
|
enableWorker: true,
|
|
826
1794
|
backBufferLength: 30,
|
|
827
1795
|
liveDurationInfinity: true,
|
|
@@ -829,13 +1797,18 @@ var StormcloudVideoPlayer = class {
|
|
|
829
1797
|
maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
|
|
830
1798
|
...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
|
|
831
1799
|
});
|
|
832
|
-
this.hls.on(
|
|
833
|
-
|
|
1800
|
+
this.hls.on(Hls2.Events.MEDIA_ATTACHED, () => {
|
|
1801
|
+
var _a2;
|
|
1802
|
+
(_a2 = this.hls) == null ? void 0 : _a2.loadSource(this.config.src);
|
|
834
1803
|
});
|
|
835
|
-
this.hls.on(
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1804
|
+
this.hls.on(Hls2.Events.MANIFEST_PARSED, async (_, data) => {
|
|
1805
|
+
var _a2, _b2, _c, _d;
|
|
1806
|
+
this.isLiveStream = (_c = (_b2 = (_a2 = this.hls) == null ? void 0 : _a2.levels) == null ? void 0 : _b2.some(
|
|
1807
|
+
(level) => {
|
|
1808
|
+
var _a3, _b3;
|
|
1809
|
+
return ((_a3 = level == null ? void 0 : level.details) == null ? void 0 : _a3.live) === true || ((_b3 = level == null ? void 0 : level.details) == null ? void 0 : _b3.type) === "LIVE";
|
|
1810
|
+
}
|
|
1811
|
+
)) != null ? _c : false;
|
|
839
1812
|
if (this.config.debugAdTiming) {
|
|
840
1813
|
const adBehavior = this.shouldContinueLiveStreamDuringAds() ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
|
|
841
1814
|
console.log("[StormcloudVideoPlayer] Stream type detected:", {
|
|
@@ -845,33 +1818,32 @@ var StormcloudVideoPlayer = class {
|
|
|
845
1818
|
});
|
|
846
1819
|
}
|
|
847
1820
|
this.ima.destroy();
|
|
848
|
-
this.ima =
|
|
849
|
-
continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
|
|
850
|
-
});
|
|
1821
|
+
this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
|
|
851
1822
|
this.ima.initialize();
|
|
852
1823
|
if (this.config.autoplay) {
|
|
853
|
-
await this.video.play()
|
|
854
|
-
});
|
|
1824
|
+
await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
|
|
1825
|
+
}));
|
|
855
1826
|
}
|
|
856
1827
|
});
|
|
857
|
-
this.hls.on(
|
|
858
|
-
const id3Tags = (data
|
|
1828
|
+
this.hls.on(Hls2.Events.FRAG_PARSING_METADATA, (_evt, data) => {
|
|
1829
|
+
const id3Tags = ((data == null ? void 0 : data.samples) || []).map((s) => ({
|
|
859
1830
|
key: "ID3",
|
|
860
|
-
value: s
|
|
861
|
-
ptsSeconds: s
|
|
1831
|
+
value: s == null ? void 0 : s.data,
|
|
1832
|
+
ptsSeconds: s == null ? void 0 : s.pts
|
|
862
1833
|
}));
|
|
863
1834
|
id3Tags.forEach((tag) => this.onId3Tag(tag));
|
|
864
1835
|
});
|
|
865
|
-
this.hls.on(
|
|
866
|
-
|
|
867
|
-
const
|
|
1836
|
+
this.hls.on(Hls2.Events.FRAG_CHANGED, (_evt, data) => {
|
|
1837
|
+
var _a2, _b2, _c;
|
|
1838
|
+
const frag = data == null ? void 0 : data.frag;
|
|
1839
|
+
const tagList = frag == null ? void 0 : frag.tagList;
|
|
868
1840
|
if (!Array.isArray(tagList)) return;
|
|
869
1841
|
for (const entry of tagList) {
|
|
870
1842
|
let tag = "";
|
|
871
1843
|
let value = "";
|
|
872
1844
|
if (Array.isArray(entry)) {
|
|
873
|
-
tag = String(entry[0]
|
|
874
|
-
value = String(entry[1]
|
|
1845
|
+
tag = String((_a2 = entry[0]) != null ? _a2 : "");
|
|
1846
|
+
value = String((_b2 = entry[1]) != null ? _b2 : "");
|
|
875
1847
|
} else if (typeof entry === "string") {
|
|
876
1848
|
const idx = entry.indexOf(":");
|
|
877
1849
|
if (idx >= 0) {
|
|
@@ -895,8 +1867,8 @@ var StormcloudVideoPlayer = class {
|
|
|
895
1867
|
const prog = this.parseCueOutCont(value);
|
|
896
1868
|
const marker = {
|
|
897
1869
|
type: "progress",
|
|
898
|
-
...prog
|
|
899
|
-
...prog
|
|
1870
|
+
...(prog == null ? void 0 : prog.duration) !== void 0 ? { durationSeconds: prog.duration } : {},
|
|
1871
|
+
...(prog == null ? void 0 : prog.elapsed) !== void 0 ? { ptsSeconds: prog.elapsed } : {},
|
|
900
1872
|
raw: { tag, value }
|
|
901
1873
|
};
|
|
902
1874
|
this.onScte35Marker(marker);
|
|
@@ -906,7 +1878,7 @@ var StormcloudVideoPlayer = class {
|
|
|
906
1878
|
const attrs = this.parseAttributeList(value);
|
|
907
1879
|
const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
|
|
908
1880
|
const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
|
|
909
|
-
const klass = String(attrs["CLASS"]
|
|
1881
|
+
const klass = String((_c = attrs["CLASS"]) != null ? _c : "");
|
|
910
1882
|
const duration = this.toNumber(attrs["DURATION"]);
|
|
911
1883
|
if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
|
|
912
1884
|
const marker = {
|
|
@@ -922,14 +1894,15 @@ var StormcloudVideoPlayer = class {
|
|
|
922
1894
|
}
|
|
923
1895
|
}
|
|
924
1896
|
});
|
|
925
|
-
this.hls.on(
|
|
926
|
-
|
|
1897
|
+
this.hls.on(Hls2.Events.ERROR, (_evt, data) => {
|
|
1898
|
+
var _a2, _b2;
|
|
1899
|
+
if (data == null ? void 0 : data.fatal) {
|
|
927
1900
|
switch (data.type) {
|
|
928
|
-
case
|
|
929
|
-
this.hls
|
|
1901
|
+
case Hls2.ErrorTypes.NETWORK_ERROR:
|
|
1902
|
+
(_a2 = this.hls) == null ? void 0 : _a2.startLoad();
|
|
930
1903
|
break;
|
|
931
|
-
case
|
|
932
|
-
this.hls
|
|
1904
|
+
case Hls2.ErrorTypes.MEDIA_ERROR:
|
|
1905
|
+
(_b2 = this.hls) == null ? void 0 : _b2.recoverMediaError();
|
|
933
1906
|
break;
|
|
934
1907
|
default:
|
|
935
1908
|
this.destroy();
|
|
@@ -1031,11 +2004,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1031
2004
|
}
|
|
1032
2005
|
}
|
|
1033
2006
|
parseScte35FromId3(tag) {
|
|
2007
|
+
var _a, _b, _c, _d;
|
|
1034
2008
|
const text = this.decodeId3ValueToText(tag.value);
|
|
1035
2009
|
if (!text) return void 0;
|
|
1036
2010
|
const cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
|
|
1037
2011
|
if (cueOutMatch) {
|
|
1038
|
-
const arg = (cueOutMatch[1]
|
|
2012
|
+
const arg = ((_a = cueOutMatch[1]) != null ? _a : "").trim();
|
|
1039
2013
|
const dur = this.parseCueOutDuration(arg);
|
|
1040
2014
|
const marker = {
|
|
1041
2015
|
type: "start",
|
|
@@ -1047,12 +2021,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1047
2021
|
}
|
|
1048
2022
|
const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
|
|
1049
2023
|
if (cueOutContMatch) {
|
|
1050
|
-
const arg = (cueOutContMatch[1]
|
|
2024
|
+
const arg = ((_b = cueOutContMatch[1]) != null ? _b : "").trim();
|
|
1051
2025
|
const cont = this.parseCueOutCont(arg);
|
|
1052
2026
|
const marker = {
|
|
1053
2027
|
type: "progress",
|
|
1054
2028
|
...tag.ptsSeconds !== void 0 ? { ptsSeconds: tag.ptsSeconds } : {},
|
|
1055
|
-
...cont
|
|
2029
|
+
...(cont == null ? void 0 : cont.duration) !== void 0 ? { durationSeconds: cont.duration } : {},
|
|
1056
2030
|
raw: { id3: text }
|
|
1057
2031
|
};
|
|
1058
2032
|
return marker;
|
|
@@ -1068,10 +2042,10 @@ var StormcloudVideoPlayer = class {
|
|
|
1068
2042
|
}
|
|
1069
2043
|
const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
|
|
1070
2044
|
if (daterangeMatch) {
|
|
1071
|
-
const attrs = this.parseAttributeList(daterangeMatch[1]
|
|
2045
|
+
const attrs = this.parseAttributeList((_c = daterangeMatch[1]) != null ? _c : "");
|
|
1072
2046
|
const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
|
|
1073
2047
|
const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
|
|
1074
|
-
const klass = String(attrs["CLASS"]
|
|
2048
|
+
const klass = String((_d = attrs["CLASS"]) != null ? _d : "");
|
|
1075
2049
|
const duration = this.toNumber(attrs["DURATION"]);
|
|
1076
2050
|
if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
|
|
1077
2051
|
const marker = {
|
|
@@ -1128,6 +2102,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1128
2102
|
}
|
|
1129
2103
|
}
|
|
1130
2104
|
onScte35Marker(marker) {
|
|
2105
|
+
var _a, _b;
|
|
1131
2106
|
if (this.config.debugAdTiming) {
|
|
1132
2107
|
console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
|
|
1133
2108
|
type: marker.type,
|
|
@@ -1143,7 +2118,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1143
2118
|
this.expectedAdBreakDurationMs = durationMs;
|
|
1144
2119
|
this.currentAdBreakStartWallClockMs = Date.now();
|
|
1145
2120
|
const isManifestMarker = this.isManifestBasedMarker(marker);
|
|
1146
|
-
const forceImmediate = this.config.immediateManifestAds
|
|
2121
|
+
const forceImmediate = (_a = this.config.immediateManifestAds) != null ? _a : true;
|
|
1147
2122
|
if (this.config.debugAdTiming) {
|
|
1148
2123
|
console.log("[StormcloudVideoPlayer] Ad start decision:", {
|
|
1149
2124
|
isManifestMarker,
|
|
@@ -1160,7 +2135,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1160
2135
|
this.clearAdStartTimer();
|
|
1161
2136
|
this.handleAdStart(marker);
|
|
1162
2137
|
} else if (typeof marker.ptsSeconds === "number") {
|
|
1163
|
-
const tol = this.config.driftToleranceMs
|
|
2138
|
+
const tol = (_b = this.config.driftToleranceMs) != null ? _b : 1e3;
|
|
1164
2139
|
const nowMs = this.video.currentTime * 1e3;
|
|
1165
2140
|
const estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
|
|
1166
2141
|
const deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
|
|
@@ -1270,12 +2245,13 @@ var StormcloudVideoPlayer = class {
|
|
|
1270
2245
|
return void 0;
|
|
1271
2246
|
}
|
|
1272
2247
|
parseAttributeList(value) {
|
|
2248
|
+
var _a, _b, _c;
|
|
1273
2249
|
const attrs = {};
|
|
1274
2250
|
const regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
|
|
1275
2251
|
let match;
|
|
1276
2252
|
while ((match = regex.exec(value)) !== null) {
|
|
1277
|
-
const key = match[1]
|
|
1278
|
-
let rawVal = match[3]
|
|
2253
|
+
const key = (_a = match[1]) != null ? _a : "";
|
|
2254
|
+
let rawVal = (_c = (_b = match[3]) != null ? _b : match[4]) != null ? _c : "";
|
|
1279
2255
|
if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
|
|
1280
2256
|
rawVal = rawVal.slice(1, -1);
|
|
1281
2257
|
}
|
|
@@ -1439,6 +2415,43 @@ var StormcloudVideoPlayer = class {
|
|
|
1439
2415
|
}
|
|
1440
2416
|
}
|
|
1441
2417
|
async fetchAdConfiguration() {
|
|
2418
|
+
var _a, _b, _c;
|
|
2419
|
+
const vastMode = this.config.vastMode || "default";
|
|
2420
|
+
if (this.config.debugAdTiming) {
|
|
2421
|
+
console.log(
|
|
2422
|
+
"[StormcloudVideoPlayer] VAST mode:",
|
|
2423
|
+
vastMode
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2426
|
+
if (vastMode === "adstorm") {
|
|
2427
|
+
if (!this.config.licenseKey) {
|
|
2428
|
+
if (this.config.debugAdTiming) {
|
|
2429
|
+
console.warn(
|
|
2430
|
+
"[StormcloudVideoPlayer] AdStorm mode requires a license key"
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
|
|
2436
|
+
this.apiVastTagUrl = vastEndpoint;
|
|
2437
|
+
if (this.config.debugAdTiming) {
|
|
2438
|
+
console.log(
|
|
2439
|
+
"[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
|
|
2440
|
+
vastEndpoint
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
if (this.config.vastTagUrl) {
|
|
2446
|
+
this.apiVastTagUrl = this.config.vastTagUrl;
|
|
2447
|
+
if (this.config.debugAdTiming) {
|
|
2448
|
+
console.log(
|
|
2449
|
+
"[StormcloudVideoPlayer] Using custom VAST tag URL:",
|
|
2450
|
+
this.apiVastTagUrl
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
1442
2455
|
const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
1443
2456
|
if (this.config.debugAdTiming) {
|
|
1444
2457
|
console.log(
|
|
@@ -1452,25 +2465,29 @@ var StormcloudVideoPlayer = class {
|
|
|
1452
2465
|
}
|
|
1453
2466
|
const response = await fetch(apiUrl, { headers });
|
|
1454
2467
|
if (!response.ok) {
|
|
1455
|
-
|
|
2468
|
+
if (this.config.debugAdTiming) {
|
|
2469
|
+
console.warn(
|
|
2470
|
+
`[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
return;
|
|
1456
2474
|
}
|
|
1457
2475
|
const data = await response.json();
|
|
1458
|
-
const imaPayload = data.response
|
|
2476
|
+
const imaPayload = (_c = (_b = (_a = data.response) == null ? void 0 : _a.ima) == null ? void 0 : _b["publisherdesk.ima"]) == null ? void 0 : _c.payload;
|
|
1459
2477
|
if (imaPayload) {
|
|
1460
2478
|
this.apiVastTagUrl = decodeURIComponent(imaPayload);
|
|
1461
2479
|
if (this.config.debugAdTiming) {
|
|
1462
2480
|
console.log(
|
|
1463
|
-
"[StormcloudVideoPlayer] Extracted VAST tag URL:",
|
|
2481
|
+
"[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
|
|
1464
2482
|
this.apiVastTagUrl
|
|
1465
2483
|
);
|
|
1466
2484
|
}
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
});
|
|
2485
|
+
} else {
|
|
2486
|
+
if (this.config.debugAdTiming) {
|
|
2487
|
+
console.warn(
|
|
2488
|
+
"[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
|
|
2489
|
+
);
|
|
2490
|
+
}
|
|
1474
2491
|
}
|
|
1475
2492
|
}
|
|
1476
2493
|
getCurrentAdIndex() {
|
|
@@ -1493,11 +2510,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1493
2510
|
return "other";
|
|
1494
2511
|
}
|
|
1495
2512
|
shouldShowNativeControls() {
|
|
2513
|
+
var _a, _b;
|
|
1496
2514
|
const streamType = this.getStreamType();
|
|
1497
2515
|
if (streamType === "other") {
|
|
1498
|
-
return !(this.config.showCustomControls
|
|
2516
|
+
return !((_a = this.config.showCustomControls) != null ? _a : false);
|
|
1499
2517
|
}
|
|
1500
|
-
return !!(this.config.allowNativeHls && !(this.config.showCustomControls
|
|
2518
|
+
return !!(this.config.allowNativeHls && !((_b = this.config.showCustomControls) != null ? _b : false));
|
|
1501
2519
|
}
|
|
1502
2520
|
shouldContinueLiveStreamDuringAds() {
|
|
1503
2521
|
if (this.config.allowNativeHls) {
|
|
@@ -1509,28 +2527,20 @@ var StormcloudVideoPlayer = class {
|
|
|
1509
2527
|
return true;
|
|
1510
2528
|
}
|
|
1511
2529
|
async handleAdStart(_marker) {
|
|
2530
|
+
var _a;
|
|
1512
2531
|
const scheduled = this.findCurrentOrNextBreak(
|
|
1513
2532
|
this.video.currentTime * 1e3
|
|
1514
2533
|
);
|
|
1515
2534
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1516
2535
|
let vastTagUrl;
|
|
1517
|
-
let adsNumber = 1;
|
|
1518
2536
|
if (this.apiVastTagUrl) {
|
|
1519
2537
|
vastTagUrl = this.apiVastTagUrl;
|
|
1520
|
-
|
|
1521
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1522
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1523
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1524
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1525
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
2538
|
+
this.adPodQueue = [];
|
|
1529
2539
|
this.currentAdIndex = 0;
|
|
1530
|
-
this.totalAdsInBreak =
|
|
2540
|
+
this.totalAdsInBreak = 1;
|
|
1531
2541
|
if (this.config.debugAdTiming) {
|
|
1532
2542
|
console.log(
|
|
1533
|
-
|
|
2543
|
+
"[StormcloudVideoPlayer] Using VAST endpoint:",
|
|
1534
2544
|
vastTagUrl
|
|
1535
2545
|
);
|
|
1536
2546
|
}
|
|
@@ -1568,17 +2578,18 @@ var StormcloudVideoPlayer = class {
|
|
|
1568
2578
|
this.handleAdFailure();
|
|
1569
2579
|
}
|
|
1570
2580
|
}
|
|
1571
|
-
if (this.expectedAdBreakDurationMs == null && scheduled
|
|
2581
|
+
if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
|
|
1572
2582
|
this.expectedAdBreakDurationMs = scheduled.durationMs;
|
|
1573
|
-
this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs
|
|
2583
|
+
this.currentAdBreakStartWallClockMs = (_a = this.currentAdBreakStartWallClockMs) != null ? _a : Date.now();
|
|
1574
2584
|
this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
|
|
1575
2585
|
}
|
|
1576
2586
|
}
|
|
1577
2587
|
findCurrentOrNextBreak(nowMs) {
|
|
2588
|
+
var _a;
|
|
1578
2589
|
const schedule = [];
|
|
1579
2590
|
let candidate;
|
|
1580
2591
|
for (const b of schedule) {
|
|
1581
|
-
const tol = this.config.driftToleranceMs
|
|
2592
|
+
const tol = (_a = this.config.driftToleranceMs) != null ? _a : 1e3;
|
|
1582
2593
|
if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
|
|
1583
2594
|
candidate = b;
|
|
1584
2595
|
}
|
|
@@ -1594,7 +2605,8 @@ var StormcloudVideoPlayer = class {
|
|
|
1594
2605
|
}
|
|
1595
2606
|
}
|
|
1596
2607
|
async handleMidAdJoin(adBreak, nowMs) {
|
|
1597
|
-
|
|
2608
|
+
var _a;
|
|
2609
|
+
const durationMs = (_a = adBreak.durationMs) != null ? _a : 0;
|
|
1598
2610
|
const endMs = adBreak.startTimeMs + durationMs;
|
|
1599
2611
|
if (durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs) {
|
|
1600
2612
|
const remainingMs = endMs - nowMs;
|
|
@@ -1695,6 +2707,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1695
2707
|
}
|
|
1696
2708
|
}
|
|
1697
2709
|
handleAdFailure() {
|
|
2710
|
+
var _a;
|
|
1698
2711
|
if (this.config.debugAdTiming) {
|
|
1699
2712
|
console.log(
|
|
1700
2713
|
"[StormcloudVideoPlayer] Handling ad failure - resuming content",
|
|
@@ -1727,7 +2740,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1727
2740
|
if (this.config.debugAdTiming) {
|
|
1728
2741
|
console.log("[StormcloudVideoPlayer] Resuming paused video");
|
|
1729
2742
|
}
|
|
1730
|
-
this.video.play()
|
|
2743
|
+
(_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
|
|
1731
2744
|
if (this.config.debugAdTiming) {
|
|
1732
2745
|
console.error(
|
|
1733
2746
|
"[StormcloudVideoPlayer] Failed to resume video after ad failure:",
|
|
@@ -1742,8 +2755,9 @@ var StormcloudVideoPlayer = class {
|
|
|
1742
2755
|
}
|
|
1743
2756
|
}
|
|
1744
2757
|
startAdFailsafeTimer() {
|
|
2758
|
+
var _a;
|
|
1745
2759
|
this.clearAdFailsafeTimer();
|
|
1746
|
-
const failsafeMs = this.config.adFailsafeTimeoutMs
|
|
2760
|
+
const failsafeMs = (_a = this.config.adFailsafeTimeoutMs) != null ? _a : 1e4;
|
|
1747
2761
|
if (this.config.debugAdTiming) {
|
|
1748
2762
|
console.log(
|
|
1749
2763
|
`[StormcloudVideoPlayer] Starting failsafe timer (${failsafeMs}ms)`
|
|
@@ -1879,6 +2893,7 @@ var StormcloudVideoPlayer = class {
|
|
|
1879
2893
|
}
|
|
1880
2894
|
}
|
|
1881
2895
|
destroy() {
|
|
2896
|
+
var _a, _b;
|
|
1882
2897
|
this.clearAdStartTimer();
|
|
1883
2898
|
this.clearAdStopTimer();
|
|
1884
2899
|
this.clearAdFailsafeTimer();
|
|
@@ -1886,8 +2901,8 @@ var StormcloudVideoPlayer = class {
|
|
|
1886
2901
|
clearInterval(this.heartbeatInterval);
|
|
1887
2902
|
this.heartbeatInterval = void 0;
|
|
1888
2903
|
}
|
|
1889
|
-
this.hls
|
|
1890
|
-
this.ima
|
|
2904
|
+
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
2905
|
+
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
1891
2906
|
}
|
|
1892
2907
|
};
|
|
1893
2908
|
|
|
@@ -2003,7 +3018,7 @@ var StormcloudVideoPlayerComponent = React.memo(
|
|
|
2003
3018
|
}
|
|
2004
3019
|
setShowSpeedMenu(false);
|
|
2005
3020
|
};
|
|
2006
|
-
const isHlsStream = src
|
|
3021
|
+
const isHlsStream = (src == null ? void 0 : src.toLowerCase().includes(".m3u8")) || (src == null ? void 0 : src.toLowerCase().includes("/hls/"));
|
|
2007
3022
|
const shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
|
|
2008
3023
|
const criticalPropsKey = useMemo(() => {
|
|
2009
3024
|
return CRITICAL_PROPS.map((prop) => `${prop}:${props[prop]}`).join("|");
|
|
@@ -2053,13 +3068,13 @@ var StormcloudVideoPlayerComponent = React.memo(
|
|
|
2053
3068
|
player.load().then(() => {
|
|
2054
3069
|
const showNative = player.shouldShowNativeControls();
|
|
2055
3070
|
setShouldShowNativeControls(showNative);
|
|
2056
|
-
onReady
|
|
3071
|
+
onReady == null ? void 0 : onReady(player);
|
|
2057
3072
|
}).catch((error) => {
|
|
2058
3073
|
console.error(
|
|
2059
3074
|
"StormcloudVideoPlayer: Failed to load player:",
|
|
2060
3075
|
error
|
|
2061
3076
|
);
|
|
2062
|
-
onReady
|
|
3077
|
+
onReady == null ? void 0 : onReady(player);
|
|
2063
3078
|
});
|
|
2064
3079
|
return () => {
|
|
2065
3080
|
try {
|
|
@@ -2115,6 +3130,7 @@ var StormcloudVideoPlayerComponent = React.memo(
|
|
|
2115
3130
|
useEffect(() => {
|
|
2116
3131
|
if (!playerRef.current || !videoRef.current) return;
|
|
2117
3132
|
const updateStates = () => {
|
|
3133
|
+
var _a;
|
|
2118
3134
|
if (playerRef.current && videoRef.current) {
|
|
2119
3135
|
setIsMuted(playerRef.current.isMuted());
|
|
2120
3136
|
setIsPlaying(!videoRef.current.paused);
|
|
@@ -2132,13 +3148,14 @@ var StormcloudVideoPlayerComponent = React.memo(
|
|
|
2132
3148
|
);
|
|
2133
3149
|
}
|
|
2134
3150
|
setIsFullscreen(
|
|
2135
|
-
document.fullscreenElement === videoRef.current
|
|
3151
|
+
document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
|
|
2136
3152
|
);
|
|
2137
3153
|
};
|
|
2138
3154
|
const interval = setInterval(updateStates, 200);
|
|
2139
3155
|
const handleFullscreenChange = () => {
|
|
3156
|
+
var _a;
|
|
2140
3157
|
setIsFullscreen(
|
|
2141
|
-
document.fullscreenElement === videoRef.current
|
|
3158
|
+
document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
|
|
2142
3159
|
);
|
|
2143
3160
|
};
|
|
2144
3161
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
@@ -3484,27 +4501,30 @@ var parseQuery = (url) => {
|
|
|
3484
4501
|
const query = {};
|
|
3485
4502
|
const queryString = url.split("?")[1] || "";
|
|
3486
4503
|
if (!queryString) return query;
|
|
4504
|
+
const manualParse = (qs) => {
|
|
4505
|
+
qs.split("&").forEach((param) => {
|
|
4506
|
+
const [key, value] = param.split("=");
|
|
4507
|
+
if (key) {
|
|
4508
|
+
try {
|
|
4509
|
+
query[decodeURIComponent(key)] = value ? decodeURIComponent(value.replace(/\+/g, " ")) : "";
|
|
4510
|
+
} catch (e) {
|
|
4511
|
+
query[key] = value || "";
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
});
|
|
4515
|
+
};
|
|
3487
4516
|
if (typeof URLSearchParams !== "undefined") {
|
|
3488
4517
|
try {
|
|
3489
4518
|
const params = new URLSearchParams(queryString);
|
|
3490
4519
|
params.forEach((value, key) => {
|
|
3491
4520
|
query[key] = value;
|
|
3492
4521
|
});
|
|
4522
|
+
return query;
|
|
3493
4523
|
} catch (e) {
|
|
3494
|
-
queryString
|
|
3495
|
-
const [key, value] = param.split("=");
|
|
3496
|
-
if (key) {
|
|
3497
|
-
query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
|
|
3498
|
-
}
|
|
3499
|
-
});
|
|
4524
|
+
manualParse(queryString);
|
|
3500
4525
|
}
|
|
3501
4526
|
} else {
|
|
3502
|
-
queryString
|
|
3503
|
-
const [key, value] = param.split("=");
|
|
3504
|
-
if (key) {
|
|
3505
|
-
query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
|
|
3506
|
-
}
|
|
3507
|
-
});
|
|
4527
|
+
manualParse(queryString);
|
|
3508
4528
|
}
|
|
3509
4529
|
return query;
|
|
3510
4530
|
};
|
|
@@ -3578,6 +4598,7 @@ var HlsPlayer = class extends Component {
|
|
|
3578
4598
|
this.player = null;
|
|
3579
4599
|
this.mounted = false;
|
|
3580
4600
|
this.load = async () => {
|
|
4601
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3581
4602
|
if (!this.props.videoElement || !this.props.src) return;
|
|
3582
4603
|
try {
|
|
3583
4604
|
if (this.player) {
|
|
@@ -3614,27 +4635,29 @@ var HlsPlayer = class extends Component {
|
|
|
3614
4635
|
if (this.props.adFailsafeTimeoutMs !== void 0)
|
|
3615
4636
|
config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
|
|
3616
4637
|
this.player = new StormcloudVideoPlayer(config);
|
|
3617
|
-
this.props.onMount
|
|
4638
|
+
(_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
|
|
3618
4639
|
await this.player.load();
|
|
3619
4640
|
if (this.mounted) {
|
|
3620
|
-
this.props.onReady
|
|
4641
|
+
(_d = (_c = this.props).onReady) == null ? void 0 : _d.call(_c);
|
|
3621
4642
|
}
|
|
3622
4643
|
} catch (error) {
|
|
3623
4644
|
if (this.mounted) {
|
|
3624
|
-
this.props.onError
|
|
4645
|
+
(_f = (_e = this.props).onError) == null ? void 0 : _f.call(_e, error);
|
|
3625
4646
|
}
|
|
3626
4647
|
}
|
|
3627
4648
|
};
|
|
3628
4649
|
this.play = () => {
|
|
4650
|
+
var _a, _b;
|
|
3629
4651
|
if (this.props.videoElement) {
|
|
3630
4652
|
this.props.videoElement.play();
|
|
3631
|
-
this.props.onPlay
|
|
4653
|
+
(_b = (_a = this.props).onPlay) == null ? void 0 : _b.call(_a);
|
|
3632
4654
|
}
|
|
3633
4655
|
};
|
|
3634
4656
|
this.pause = () => {
|
|
4657
|
+
var _a, _b;
|
|
3635
4658
|
if (this.props.videoElement) {
|
|
3636
4659
|
this.props.videoElement.pause();
|
|
3637
|
-
this.props.onPause
|
|
4660
|
+
(_b = (_a = this.props).onPause) == null ? void 0 : _b.call(_a);
|
|
3638
4661
|
}
|
|
3639
4662
|
};
|
|
3640
4663
|
this.stop = () => {
|
|
@@ -3729,37 +4752,44 @@ var FilePlayer = class extends Component2 {
|
|
|
3729
4752
|
this.mounted = false;
|
|
3730
4753
|
this.ready = false;
|
|
3731
4754
|
this.load = () => {
|
|
4755
|
+
var _a, _b;
|
|
3732
4756
|
if (!this.props.videoElement || !this.props.src) return;
|
|
3733
4757
|
const video = this.props.videoElement;
|
|
3734
4758
|
const handleLoadedMetadata = () => {
|
|
4759
|
+
var _a2, _b2;
|
|
3735
4760
|
if (this.mounted && !this.ready) {
|
|
3736
4761
|
this.ready = true;
|
|
3737
|
-
this.props.onReady
|
|
4762
|
+
(_b2 = (_a2 = this.props).onReady) == null ? void 0 : _b2.call(_a2);
|
|
3738
4763
|
}
|
|
3739
4764
|
};
|
|
3740
4765
|
const handlePlay = () => {
|
|
4766
|
+
var _a2, _b2;
|
|
3741
4767
|
if (this.mounted) {
|
|
3742
|
-
this.props.onPlay
|
|
4768
|
+
(_b2 = (_a2 = this.props).onPlay) == null ? void 0 : _b2.call(_a2);
|
|
3743
4769
|
}
|
|
3744
4770
|
};
|
|
3745
4771
|
const handlePause = () => {
|
|
4772
|
+
var _a2, _b2;
|
|
3746
4773
|
if (this.mounted) {
|
|
3747
|
-
this.props.onPause
|
|
4774
|
+
(_b2 = (_a2 = this.props).onPause) == null ? void 0 : _b2.call(_a2);
|
|
3748
4775
|
}
|
|
3749
4776
|
};
|
|
3750
4777
|
const handleEnded = () => {
|
|
4778
|
+
var _a2, _b2;
|
|
3751
4779
|
if (this.mounted) {
|
|
3752
|
-
this.props.onEnded
|
|
4780
|
+
(_b2 = (_a2 = this.props).onEnded) == null ? void 0 : _b2.call(_a2);
|
|
3753
4781
|
}
|
|
3754
4782
|
};
|
|
3755
4783
|
const handleError = (error) => {
|
|
4784
|
+
var _a2, _b2;
|
|
3756
4785
|
if (this.mounted) {
|
|
3757
|
-
this.props.onError
|
|
4786
|
+
(_b2 = (_a2 = this.props).onError) == null ? void 0 : _b2.call(_a2, error);
|
|
3758
4787
|
}
|
|
3759
4788
|
};
|
|
3760
4789
|
const handleLoadedData = () => {
|
|
4790
|
+
var _a2, _b2;
|
|
3761
4791
|
if (this.mounted) {
|
|
3762
|
-
this.props.onLoaded
|
|
4792
|
+
(_b2 = (_a2 = this.props).onLoaded) == null ? void 0 : _b2.call(_a2);
|
|
3763
4793
|
}
|
|
3764
4794
|
};
|
|
3765
4795
|
video.addEventListener("loadedmetadata", handleLoadedMetadata);
|
|
@@ -3778,7 +4808,7 @@ var FilePlayer = class extends Component2 {
|
|
|
3778
4808
|
if (this.props.preload !== void 0)
|
|
3779
4809
|
video.preload = this.props.preload;
|
|
3780
4810
|
if (this.props.poster !== void 0) video.poster = this.props.poster;
|
|
3781
|
-
this.props.onMount
|
|
4811
|
+
(_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
|
|
3782
4812
|
return () => {
|
|
3783
4813
|
video.removeEventListener("loadedmetadata", handleLoadedMetadata);
|
|
3784
4814
|
video.removeEventListener("play", handlePlay);
|
|
@@ -3947,6 +4977,7 @@ var Player = class extends Component3 {
|
|
|
3947
4977
|
return this.player.getInternalPlayer(key);
|
|
3948
4978
|
};
|
|
3949
4979
|
this.progress = () => {
|
|
4980
|
+
var _a, _b;
|
|
3950
4981
|
if (this.props.src && this.player && this.isReady) {
|
|
3951
4982
|
const playedSeconds = this.getCurrentTime() || 0;
|
|
3952
4983
|
const loadedSeconds = this.getSecondsLoaded();
|
|
@@ -3963,7 +4994,7 @@ var Player = class extends Component3 {
|
|
|
3963
4994
|
progress.loaded = loadedSeconds / duration;
|
|
3964
4995
|
}
|
|
3965
4996
|
if (progress.playedSeconds !== this.prevPlayed || progress.loadedSeconds !== this.prevLoaded) {
|
|
3966
|
-
this.props.onProgress
|
|
4997
|
+
(_b = (_a = this.props).onProgress) == null ? void 0 : _b.call(_a, progress);
|
|
3967
4998
|
}
|
|
3968
4999
|
this.prevPlayed = progress.playedSeconds;
|
|
3969
5000
|
this.prevLoaded = progress.loadedSeconds;
|
|
@@ -3999,10 +5030,10 @@ var Player = class extends Component3 {
|
|
|
3999
5030
|
if (this.player.setPlaybackRate && playbackRate !== 1) {
|
|
4000
5031
|
this.player.setPlaybackRate(playbackRate);
|
|
4001
5032
|
}
|
|
4002
|
-
onStart
|
|
5033
|
+
onStart == null ? void 0 : onStart();
|
|
4003
5034
|
this.startOnPlay = false;
|
|
4004
5035
|
}
|
|
4005
|
-
onPlay
|
|
5036
|
+
onPlay == null ? void 0 : onPlay();
|
|
4006
5037
|
if (this.seekOnPlay) {
|
|
4007
5038
|
this.seekTo(this.seekOnPlay);
|
|
4008
5039
|
this.seekOnPlay = null;
|
|
@@ -4010,9 +5041,10 @@ var Player = class extends Component3 {
|
|
|
4010
5041
|
this.handleDurationCheck();
|
|
4011
5042
|
};
|
|
4012
5043
|
this.handlePause = (e) => {
|
|
5044
|
+
var _a, _b;
|
|
4013
5045
|
this.isPlaying = false;
|
|
4014
5046
|
if (!this.isLoading) {
|
|
4015
|
-
this.props.onPause
|
|
5047
|
+
(_b = (_a = this.props).onPause) == null ? void 0 : _b.call(_a, e);
|
|
4016
5048
|
}
|
|
4017
5049
|
};
|
|
4018
5050
|
this.handleEnded = () => {
|
|
@@ -4022,19 +5054,21 @@ var Player = class extends Component3 {
|
|
|
4022
5054
|
}
|
|
4023
5055
|
if (!loop) {
|
|
4024
5056
|
this.isPlaying = false;
|
|
4025
|
-
onEnded
|
|
5057
|
+
onEnded == null ? void 0 : onEnded();
|
|
4026
5058
|
}
|
|
4027
5059
|
};
|
|
4028
5060
|
this.handleError = (...args) => {
|
|
5061
|
+
var _a, _b;
|
|
4029
5062
|
this.isLoading = false;
|
|
4030
|
-
this.props.onError
|
|
5063
|
+
(_b = (_a = this.props).onError) == null ? void 0 : _b.call(_a, args[0], args[1], args[2], args[3]);
|
|
4031
5064
|
};
|
|
4032
5065
|
this.handleDurationCheck = () => {
|
|
5066
|
+
var _a, _b;
|
|
4033
5067
|
clearTimeout(this.durationCheckTimeout);
|
|
4034
5068
|
const duration = this.getDuration();
|
|
4035
5069
|
if (duration) {
|
|
4036
5070
|
if (!this.onDurationCalled) {
|
|
4037
|
-
this.props.onDuration
|
|
5071
|
+
(_b = (_a = this.props).onDuration) == null ? void 0 : _b.call(_a, duration);
|
|
4038
5072
|
this.onDurationCalled = true;
|
|
4039
5073
|
}
|
|
4040
5074
|
} else {
|
|
@@ -4233,7 +5267,8 @@ var createStormcloudPlayer = (playerList, fallback) => {
|
|
|
4233
5267
|
return omit(this.props, SUPPORTED_PROPS);
|
|
4234
5268
|
};
|
|
4235
5269
|
this.handleReady = () => {
|
|
4236
|
-
|
|
5270
|
+
var _a2, _b;
|
|
5271
|
+
(_b = (_a2 = this.props).onReady) == null ? void 0 : _b.call(_a2, this);
|
|
4237
5272
|
};
|
|
4238
5273
|
this.seekTo = (fraction, type, keepPlaying) => {
|
|
4239
5274
|
if (!this.player) return null;
|
|
@@ -4333,12 +5368,19 @@ export {
|
|
|
4333
5368
|
StormcloudVideoPlayer,
|
|
4334
5369
|
StormcloudVideoPlayerComponent,
|
|
4335
5370
|
canPlay,
|
|
5371
|
+
createHlsAdPlayer,
|
|
5372
|
+
createImaController,
|
|
4336
5373
|
createStormcloudPlayer,
|
|
4337
5374
|
StormcloudVideoPlayerComponent as default,
|
|
5375
|
+
detectBrowser,
|
|
5376
|
+
getBrowserConfigOverrides,
|
|
4338
5377
|
getBrowserID,
|
|
4339
5378
|
getClientInfo,
|
|
5379
|
+
getRecommendedAdPlayer,
|
|
5380
|
+
initializePolyfills,
|
|
4340
5381
|
isMediaStream,
|
|
4341
5382
|
lazy,
|
|
5383
|
+
logBrowserInfo,
|
|
4342
5384
|
merge,
|
|
4343
5385
|
omit,
|
|
4344
5386
|
parseQuery,
|
|
@@ -4346,6 +5388,9 @@ export {
|
|
|
4346
5388
|
randomString,
|
|
4347
5389
|
sendHeartbeat,
|
|
4348
5390
|
sendInitialTracking,
|
|
5391
|
+
supportsFeature,
|
|
5392
|
+
supportsGoogleIMA,
|
|
5393
|
+
supportsModernJS,
|
|
4349
5394
|
supportsWebKitPresentationMode
|
|
4350
5395
|
};
|
|
4351
5396
|
//# sourceMappingURL=index.js.map
|