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.
Files changed (38) hide show
  1. package/README.md +100 -1
  2. package/dist/stormcloud-vp.min.js +2 -2
  3. package/lib/index.cjs +1206 -151
  4. package/lib/index.cjs.map +1 -1
  5. package/lib/index.d.cts +77 -2
  6. package/lib/index.d.ts +77 -2
  7. package/lib/index.js +1196 -151
  8. package/lib/index.js.map +1 -1
  9. package/lib/player/StormcloudVideoPlayer.cjs +1100 -114
  10. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  11. package/lib/player/StormcloudVideoPlayer.d.cts +2 -2
  12. package/lib/players/FilePlayer.cjs +14 -7
  13. package/lib/players/FilePlayer.cjs.map +1 -1
  14. package/lib/players/HlsPlayer.cjs +1108 -119
  15. package/lib/players/HlsPlayer.cjs.map +1 -1
  16. package/lib/players/HlsPlayer.d.cts +1 -1
  17. package/lib/players/index.cjs +1122 -126
  18. package/lib/players/index.cjs.map +1 -1
  19. package/lib/sdk/hlsAdPlayer.cjs +561 -0
  20. package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
  21. package/lib/sdk/hlsAdPlayer.d.cts +10 -0
  22. package/lib/sdk/ima.cjs +176 -23
  23. package/lib/sdk/ima.cjs.map +1 -1
  24. package/lib/sdk/ima.d.cts +1 -1
  25. package/lib/{types-GpA_hKek.d.cts → types-mVgmKmzM.d.cts} +3 -0
  26. package/lib/ui/StormcloudVideoPlayer.cjs +1107 -119
  27. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  28. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  29. package/lib/utils/browserCompat.cjs +229 -0
  30. package/lib/utils/browserCompat.cjs.map +1 -0
  31. package/lib/utils/browserCompat.d.cts +36 -0
  32. package/lib/utils/polyfills.cjs +253 -0
  33. package/lib/utils/polyfills.cjs.map +1 -0
  34. package/lib/utils/polyfills.d.cts +11 -0
  35. package/lib/utils/tracking.cjs +10 -9
  36. package/lib/utils/tracking.cjs.map +1 -1
  37. package/lib/utils/tracking.d.cts +1 -1
  38. 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 Hls from "hls.js";
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?.getAttribute?.("sandbox") || "";
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?.ima)
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?.ima) {
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?.appendChild(container);
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?.continueLiveStreamDuringAds) {
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()?.catch(() => {
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?.continueLiveStreamDuringAds) {
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?.continueLiveStreamDuringAds) {
289
- video.play()?.catch(() => {
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?.continueLiveStreamDuringAds) {
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?.continueLiveStreamDuringAds) {
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?.continueLiveStreamDuringAds) {
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?.(error);
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
- if (!window.google?.ima || !adDisplayContainer) {
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?.continueLiveStreamDuringAds) {
419
- video.play()?.catch(() => {
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?.stop?.();
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?.continueLiveStreamDuringAds) {
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?.destroy?.();
659
+ (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
456
660
  } catch {
457
661
  }
458
- if (adContainerEl?.parentElement) {
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
- if (!adsManager || !window.google?.ima) {
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
- listeners.get(event)?.delete(listener);
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?.width,
529
- height: screen?.height,
530
- availWidth: screen?.availWidth,
531
- availHeight: screen?.availHeight,
532
- orientation: screen?.orientation?.type || "",
533
- pixelDepth: screen?.pixelDepth
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?.outerHeight === 0 && window?.outerWidth === 0) {
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?.orientation?.angle !== void 0;
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?.join(",") || "",
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
- this.config = config;
1718
+ initializePolyfills();
1719
+ const browserOverrides = getBrowserConfigOverrides();
1720
+ this.config = { ...config, ...browserOverrides };
780
1721
  this.video = config.videoElement;
781
- this.ima = createImaController(this.video, {
782
- continueLiveStreamDuringAds: false
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 ?? false;
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 = createImaController(this.video, {
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()?.catch(() => {
820
- });
1787
+ await ((_b = this.video.play()) == null ? void 0 : _b.catch(() => {
1788
+ }));
821
1789
  }
822
1790
  return;
823
1791
  }
824
- this.hls = new Hls({
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(Hls.Events.MEDIA_ATTACHED, () => {
833
- this.hls?.loadSource(this.config.src);
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(Hls.Events.MANIFEST_PARSED, async (_, data) => {
836
- this.isLiveStream = this.hls?.levels?.some(
837
- (level) => level?.details?.live === true || level?.details?.type === "LIVE"
838
- ) ?? false;
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 = createImaController(this.video, {
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()?.catch(() => {
854
- });
1824
+ await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
1825
+ }));
855
1826
  }
856
1827
  });
857
- this.hls.on(Hls.Events.FRAG_PARSING_METADATA, (_evt, data) => {
858
- const id3Tags = (data?.samples || []).map((s) => ({
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?.data,
861
- ptsSeconds: s?.pts
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(Hls.Events.FRAG_CHANGED, (_evt, data) => {
866
- const frag = data?.frag;
867
- const tagList = frag?.tagList;
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?.duration !== void 0 ? { durationSeconds: prog.duration } : {},
899
- ...prog?.elapsed !== void 0 ? { ptsSeconds: prog.elapsed } : {},
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(Hls.Events.ERROR, (_evt, data) => {
926
- if (data?.fatal) {
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 Hls.ErrorTypes.NETWORK_ERROR:
929
- this.hls?.startLoad();
1901
+ case Hls2.ErrorTypes.NETWORK_ERROR:
1902
+ (_a2 = this.hls) == null ? void 0 : _a2.startLoad();
930
1903
  break;
931
- case Hls.ErrorTypes.MEDIA_ERROR:
932
- this.hls?.recoverMediaError();
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] ?? "").trim();
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] ?? "").trim();
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?.duration !== void 0 ? { durationSeconds: cont.duration } : {},
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 ?? true;
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 ?? 1e3;
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] ?? match[4] ?? "";
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
- throw new Error(`Failed to fetch ad configuration: ${response.status}`);
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?.ima?.["publisherdesk.ima"]?.payload;
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
- this.vastConfig = data.response?.options?.vast;
1469
- if (this.config.debugAdTiming) {
1470
- console.log("[StormcloudVideoPlayer] Ad configuration loaded:", {
1471
- vastTagUrl: this.apiVastTagUrl,
1472
- vastConfig: this.vastConfig
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 ?? false);
2516
+ return !((_a = this.config.showCustomControls) != null ? _a : false);
1499
2517
  }
1500
- return !!(this.config.allowNativeHls && !(this.config.showCustomControls ?? false));
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
- if (this.vastConfig) {
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 = adsNumber;
2540
+ this.totalAdsInBreak = 1;
1531
2541
  if (this.config.debugAdTiming) {
1532
2542
  console.log(
1533
- `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
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?.durationMs != null) {
2581
+ if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
1572
2582
  this.expectedAdBreakDurationMs = scheduled.durationMs;
1573
- this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
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 ?? 1e3;
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
- const durationMs = adBreak.durationMs ?? 0;
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()?.catch((error) => {
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 ?? 1e4;
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?.destroy();
1890
- this.ima?.destroy();
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?.toLowerCase().includes(".m3u8") || src?.toLowerCase().includes("/hls/");
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?.(player);
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?.(player);
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?.parentElement
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?.parentElement
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.split("&").forEach((param) => {
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.split("&").forEach((param) => {
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?.(this);
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?.(error);
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?.(error);
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?.(this);
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?.(progress);
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?.(e);
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?.(args[0], args[1], args[2], args[3]);
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?.(duration);
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
- this.props.onReady?.(this);
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