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
@@ -36,7 +36,167 @@ module.exports = __toCommonJS(StormcloudVideoPlayer_exports);
36
36
  var import_react = __toESM(require("react"), 1);
37
37
 
38
38
  // src/player/StormcloudVideoPlayer.ts
39
- var import_hls = __toESM(require("hls.js"), 1);
39
+ var import_hls2 = __toESM(require("hls.js"), 1);
40
+
41
+ // src/utils/browserCompat.ts
42
+ function getChromeVersion(ua) {
43
+ const match = ua.match(/Chrome\/(\d+)/);
44
+ return match && match[1] ? parseInt(match[1], 10) : 0;
45
+ }
46
+ function getWebKitVersion(ua) {
47
+ const match = ua.match(/AppleWebKit\/(\d+)/);
48
+ return match && match[1] ? parseInt(match[1], 10) : 0;
49
+ }
50
+ function getPlatform() {
51
+ var _a;
52
+ if ("userAgentData" in navigator && ((_a = navigator.userAgentData) == null ? void 0 : _a.platform)) {
53
+ return navigator.userAgentData.platform;
54
+ }
55
+ const ua = navigator.userAgent;
56
+ if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
57
+ return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
58
+ }
59
+ if (/Win/i.test(ua)) {
60
+ return "Win32";
61
+ }
62
+ if (/Linux/i.test(ua)) {
63
+ return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
64
+ }
65
+ if (/CrOS/i.test(ua)) {
66
+ return "CrOS";
67
+ }
68
+ return navigator.platform || "Unknown";
69
+ }
70
+ function detectBrowser() {
71
+ const ua = navigator.userAgent;
72
+ const platform = getPlatform();
73
+ let name = "Unknown";
74
+ let version = "0";
75
+ let majorVersion = 0;
76
+ let isSmartTV = false;
77
+ let isLegacyTV = false;
78
+ let supportsIMA = true;
79
+ let supportsModernJS = true;
80
+ let recommendedAdPlayer = "ima";
81
+ if (/Web0S|webOS/i.test(ua)) {
82
+ name = "LG WebOS";
83
+ isSmartTV = true;
84
+ const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
85
+ version = match && match[1] ? match[1] : "Unknown";
86
+ if (version !== "Unknown") {
87
+ const parts = version.split(".");
88
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
89
+ }
90
+ } else if (/Tizen/i.test(ua)) {
91
+ name = "Samsung Tizen";
92
+ isSmartTV = true;
93
+ const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
94
+ version = match && match[1] ? match[1] : "Unknown";
95
+ if (version !== "Unknown") {
96
+ const parts = version.split(".");
97
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
98
+ }
99
+ } else if (/SMART-TV|SmartTV/i.test(ua)) {
100
+ name = "Smart TV";
101
+ isSmartTV = true;
102
+ } else if (/NetCast/i.test(ua)) {
103
+ name = "LG NetCast";
104
+ isSmartTV = true;
105
+ isLegacyTV = true;
106
+ } else if (/BRAVIA/i.test(ua)) {
107
+ name = "Sony BRAVIA";
108
+ isSmartTV = true;
109
+ }
110
+ const chromeVersion = getChromeVersion(ua);
111
+ const webkitVersion = getWebKitVersion(ua);
112
+ if (chromeVersion > 0) {
113
+ if (!isSmartTV) {
114
+ name = "Chrome";
115
+ version = chromeVersion.toString();
116
+ majorVersion = chromeVersion;
117
+ }
118
+ if (chromeVersion < 50) {
119
+ supportsIMA = false;
120
+ supportsModernJS = false;
121
+ isLegacyTV = true;
122
+ recommendedAdPlayer = "hls";
123
+ }
124
+ }
125
+ if (webkitVersion > 0 && webkitVersion < 600) {
126
+ supportsModernJS = false;
127
+ if (isSmartTV) {
128
+ isLegacyTV = true;
129
+ supportsIMA = false;
130
+ recommendedAdPlayer = "hls";
131
+ }
132
+ }
133
+ if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
134
+ supportsModernJS = false;
135
+ supportsIMA = false;
136
+ recommendedAdPlayer = "hls";
137
+ }
138
+ if (typeof URLSearchParams === "undefined") {
139
+ supportsModernJS = false;
140
+ }
141
+ return {
142
+ name,
143
+ version,
144
+ majorVersion,
145
+ isSmartTV,
146
+ isLegacyTV,
147
+ platform,
148
+ supportsIMA,
149
+ supportsModernJS,
150
+ recommendedAdPlayer
151
+ };
152
+ }
153
+ function supportsGoogleIMA() {
154
+ const browser = detectBrowser();
155
+ if (browser.isLegacyTV) {
156
+ return false;
157
+ }
158
+ if (typeof document === "undefined" || typeof document.createElement !== "function") {
159
+ return false;
160
+ }
161
+ try {
162
+ const video = document.createElement("video");
163
+ if (!video) {
164
+ return false;
165
+ }
166
+ } catch (e) {
167
+ return false;
168
+ }
169
+ if (typeof Promise === "undefined") {
170
+ return false;
171
+ }
172
+ return browser.supportsIMA;
173
+ }
174
+ function logBrowserInfo(debug = false) {
175
+ if (!debug) return;
176
+ const browser = detectBrowser();
177
+ const imaSupport = supportsGoogleIMA();
178
+ console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
179
+ browser: `${browser.name} ${browser.version}`,
180
+ platform: browser.platform,
181
+ isSmartTV: browser.isSmartTV,
182
+ isLegacyTV: browser.isLegacyTV,
183
+ supportsIMA: imaSupport,
184
+ supportsModernJS: browser.supportsModernJS,
185
+ recommendedAdPlayer: browser.recommendedAdPlayer,
186
+ userAgent: navigator.userAgent
187
+ });
188
+ }
189
+ function getBrowserConfigOverrides() {
190
+ const browser = detectBrowser();
191
+ const overrides = {};
192
+ if (browser.isLegacyTV || !browser.supportsIMA) {
193
+ overrides.adPlayerType = "hls";
194
+ }
195
+ if (browser.isSmartTV) {
196
+ overrides.allowNativeHls = true;
197
+ }
198
+ return overrides;
199
+ }
40
200
 
41
201
  // src/sdk/ima.ts
42
202
  function createImaController(video, options) {
@@ -54,9 +214,18 @@ function createImaController(video, options) {
54
214
  }
55
215
  }
56
216
  function ensureImaLoaded() {
217
+ var _a, _b, _c;
218
+ if (!supportsGoogleIMA()) {
219
+ console.warn(
220
+ "[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
221
+ );
222
+ return Promise.reject(
223
+ new Error("Google IMA SDK not supported on this browser")
224
+ );
225
+ }
57
226
  try {
58
227
  const frameEl = window.frameElement;
59
- const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
228
+ const sandboxAttr = ((_a = frameEl == null ? void 0 : frameEl.getAttribute) == null ? void 0 : _a.call(frameEl, "sandbox")) || "";
60
229
  if (sandboxAttr) {
61
230
  const tokens = new Set(
62
231
  sandboxAttr.split(/\s+/).map((t) => t.trim()).filter((t) => t.length > 0)
@@ -70,13 +239,13 @@ function createImaController(video, options) {
70
239
  }
71
240
  } catch {
72
241
  }
73
- if (typeof window !== "undefined" && window.google?.ima)
242
+ if (typeof window !== "undefined" && ((_b = window.google) == null ? void 0 : _b.ima))
74
243
  return Promise.resolve();
75
244
  const existing = document.querySelector(
76
245
  'script[data-ima="true"]'
77
246
  );
78
247
  if (existing) {
79
- if (window.google?.ima) {
248
+ if ((_c = window.google) == null ? void 0 : _c.ima) {
80
249
  return Promise.resolve();
81
250
  }
82
251
  return new Promise((resolve, reject) => {
@@ -134,6 +303,7 @@ function createImaController(video, options) {
134
303
  return {
135
304
  initialize() {
136
305
  ensureImaLoaded().then(() => {
306
+ var _a, _b;
137
307
  const google = window.google;
138
308
  if (!adDisplayContainer) {
139
309
  const container = document.createElement("div");
@@ -147,14 +317,14 @@ function createImaController(video, options) {
147
317
  container.style.justifyContent = "center";
148
318
  container.style.pointerEvents = "none";
149
319
  container.style.zIndex = "2";
150
- video.parentElement?.appendChild(container);
320
+ (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
151
321
  adContainerEl = container;
152
322
  adDisplayContainer = new google.ima.AdDisplayContainer(
153
323
  container,
154
324
  video
155
325
  );
156
326
  try {
157
- adDisplayContainer.initialize?.();
327
+ (_b = adDisplayContainer.initialize) == null ? void 0 : _b.call(adDisplayContainer);
158
328
  } catch {
159
329
  }
160
330
  }
@@ -239,6 +409,7 @@ function createImaController(video, options) {
239
409
  adsManager.addEventListener(
240
410
  AdErrorEvent.AD_ERROR,
241
411
  (errorEvent) => {
412
+ var _a;
242
413
  console.error("[IMA] Ad error:", errorEvent.getError());
243
414
  destroyAdsManager();
244
415
  adPlaying = false;
@@ -269,10 +440,10 @@ function createImaController(video, options) {
269
440
  "[IMA] Max retries reached, emitting ad_error"
270
441
  );
271
442
  emit("ad_error");
272
- if (!options?.continueLiveStreamDuringAds) {
443
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
273
444
  if (video.paused) {
274
445
  console.log("[IMA] Resuming paused video after ad error");
275
- video.play()?.catch(() => {
446
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
276
447
  });
277
448
  }
278
449
  }
@@ -283,7 +454,7 @@ function createImaController(video, options) {
283
454
  AdEvent.CONTENT_PAUSE_REQUESTED,
284
455
  () => {
285
456
  console.log("[IMA] Content pause requested");
286
- if (!options?.continueLiveStreamDuringAds) {
457
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
287
458
  video.pause();
288
459
  console.log("[IMA] Video paused (VOD mode)");
289
460
  } else {
@@ -309,6 +480,7 @@ function createImaController(video, options) {
309
480
  adsManager.addEventListener(
310
481
  AdEvent.CONTENT_RESUME_REQUESTED,
311
482
  () => {
483
+ var _a;
312
484
  console.log("[IMA] Content resume requested");
313
485
  adPlaying = false;
314
486
  video.muted = originalMutedState;
@@ -319,8 +491,8 @@ function createImaController(video, options) {
319
491
  "[IMA] Ad container hidden - pointer events disabled"
320
492
  );
321
493
  }
322
- if (!options?.continueLiveStreamDuringAds) {
323
- video.play()?.catch(() => {
494
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
495
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
324
496
  });
325
497
  console.log("[IMA] Video resumed (VOD mode)");
326
498
  } else {
@@ -342,7 +514,7 @@ function createImaController(video, options) {
342
514
  "[IMA] Ad container hidden after all ads completed"
343
515
  );
344
516
  }
345
- if (!options?.continueLiveStreamDuringAds) {
517
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
346
518
  video.play().catch(() => {
347
519
  });
348
520
  console.log(
@@ -370,7 +542,7 @@ function createImaController(video, options) {
370
542
  adContainerEl.style.display = "none";
371
543
  console.log("[IMA] Ad container hidden after setup error");
372
544
  }
373
- if (!options?.continueLiveStreamDuringAds) {
545
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
374
546
  if (video.paused) {
375
547
  console.log("[IMA] Resuming paused video after setup error");
376
548
  video.play().catch(() => {
@@ -398,7 +570,7 @@ function createImaController(video, options) {
398
570
  adContainerEl.style.display = "none";
399
571
  console.log("[IMA] Ad container hidden after loader error");
400
572
  }
401
- if (!options?.continueLiveStreamDuringAds) {
573
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
402
574
  if (video.paused) {
403
575
  console.log("[IMA] Resuming paused video after loader error");
404
576
  video.play().catch(() => {
@@ -420,14 +592,15 @@ function createImaController(video, options) {
420
592
  return adsLoadedPromise;
421
593
  } catch (error) {
422
594
  console.error("[IMA] Failed to request ads:", error);
423
- currentReject?.(error);
595
+ currentReject == null ? void 0 : currentReject(error);
424
596
  adsLoadedReject = void 0;
425
597
  adsLoadedResolve = void 0;
426
598
  return Promise.reject(error);
427
599
  }
428
600
  },
429
601
  async play() {
430
- if (!window.google?.ima || !adDisplayContainer) {
602
+ var _a, _b;
603
+ if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
431
604
  console.warn(
432
605
  "[IMA] Cannot play ad: IMA SDK or ad container not available"
433
606
  );
@@ -449,14 +622,15 @@ function createImaController(video, options) {
449
622
  } catch (error) {
450
623
  console.error("[IMA] Error starting ad playback:", error);
451
624
  adPlaying = false;
452
- if (!options?.continueLiveStreamDuringAds) {
453
- video.play()?.catch(() => {
625
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
626
+ (_b = video.play()) == null ? void 0 : _b.catch(() => {
454
627
  });
455
628
  }
456
629
  return Promise.reject(error);
457
630
  }
458
631
  },
459
632
  async stop() {
633
+ var _a;
460
634
  adPlaying = false;
461
635
  video.muted = originalMutedState;
462
636
  if (adContainerEl) {
@@ -465,11 +639,11 @@ function createImaController(video, options) {
465
639
  console.log("[IMA] Ad container hidden after stop");
466
640
  }
467
641
  try {
468
- adsManager?.stop?.();
642
+ (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
469
643
  } catch {
470
644
  }
471
645
  destroyAdsManager();
472
- if (!options?.continueLiveStreamDuringAds) {
646
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
473
647
  video.play().catch(() => {
474
648
  });
475
649
  console.log("[IMA] Video resumed after stop (VOD mode)");
@@ -478,6 +652,7 @@ function createImaController(video, options) {
478
652
  }
479
653
  },
480
654
  destroy() {
655
+ var _a;
481
656
  destroyAdsManager();
482
657
  adPlaying = false;
483
658
  video.muted = originalMutedState;
@@ -486,10 +661,10 @@ function createImaController(video, options) {
486
661
  adContainerEl.style.display = "none";
487
662
  }
488
663
  try {
489
- adsLoader?.destroy?.();
664
+ (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
490
665
  } catch {
491
666
  }
492
- if (adContainerEl?.parentElement) {
667
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
493
668
  adContainerEl.parentElement.removeChild(adContainerEl);
494
669
  }
495
670
  adContainerEl = void 0;
@@ -500,7 +675,8 @@ function createImaController(video, options) {
500
675
  return adPlaying;
501
676
  },
502
677
  resize(width, height) {
503
- if (!adsManager || !window.google?.ima) {
678
+ var _a;
679
+ if (!adsManager || !((_a = window.google) == null ? void 0 : _a.ima)) {
504
680
  console.warn(
505
681
  "[IMA] Cannot resize: No ads manager or IMA SDK available"
506
682
  );
@@ -518,7 +694,8 @@ function createImaController(video, options) {
518
694
  listeners.get(event).add(listener);
519
695
  },
520
696
  off(event, listener) {
521
- listeners.get(event)?.delete(listener);
697
+ var _a;
698
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
522
699
  },
523
700
  updateOriginalMutedState(muted) {
524
701
  originalMutedState = muted;
@@ -549,9 +726,533 @@ function createImaController(video, options) {
549
726
  };
550
727
  }
551
728
 
729
+ // src/sdk/hlsAdPlayer.ts
730
+ var import_hls = __toESM(require("hls.js"), 1);
731
+ function createHlsAdPlayer(contentVideo, options) {
732
+ let adPlaying = false;
733
+ let originalMutedState = false;
734
+ const listeners = /* @__PURE__ */ new Map();
735
+ const licenseKey = options == null ? void 0 : options.licenseKey;
736
+ const mainHlsInstance = options == null ? void 0 : options.mainHlsInstance;
737
+ let adVideoElement;
738
+ let adHls;
739
+ let adContainerEl;
740
+ let currentAd;
741
+ let sessionId;
742
+ let trackingFired = {
743
+ impression: false,
744
+ start: false,
745
+ firstQuartile: false,
746
+ midpoint: false,
747
+ thirdQuartile: false,
748
+ complete: false
749
+ };
750
+ function emit(event, payload) {
751
+ const set = listeners.get(event);
752
+ if (!set) return;
753
+ for (const fn of Array.from(set)) {
754
+ try {
755
+ fn(payload);
756
+ } catch (error) {
757
+ console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
758
+ }
759
+ }
760
+ }
761
+ function generateSessionId() {
762
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
763
+ }
764
+ function fireTrackingPixels(urls) {
765
+ if (!urls || urls.length === 0) return;
766
+ urls.forEach((url) => {
767
+ try {
768
+ let trackingUrl = url;
769
+ if (sessionId) {
770
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
771
+ }
772
+ if (licenseKey) {
773
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
774
+ }
775
+ const img = new Image(1, 1);
776
+ img.src = trackingUrl;
777
+ console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
778
+ } catch (error) {
779
+ console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
780
+ }
781
+ });
782
+ }
783
+ function getMainStreamQuality() {
784
+ if (!mainHlsInstance || !mainHlsInstance.levels) {
785
+ return null;
786
+ }
787
+ const currentLevel = mainHlsInstance.currentLevel;
788
+ if (currentLevel === -1 || !mainHlsInstance.levels[currentLevel]) {
789
+ const autoLevel = mainHlsInstance.loadLevel;
790
+ if (autoLevel !== -1 && mainHlsInstance.levels[autoLevel]) {
791
+ const level2 = mainHlsInstance.levels[autoLevel];
792
+ return {
793
+ width: level2.width || 1920,
794
+ height: level2.height || 1080,
795
+ bitrate: level2.bitrate || 5e6
796
+ };
797
+ }
798
+ return null;
799
+ }
800
+ const level = mainHlsInstance.levels[currentLevel];
801
+ return {
802
+ width: level.width || 1920,
803
+ height: level.height || 1080,
804
+ bitrate: level.bitrate || 5e6
805
+ };
806
+ }
807
+ function selectBestMediaFile(mediaFiles) {
808
+ if (mediaFiles.length === 0) {
809
+ throw new Error("No media files available");
810
+ }
811
+ const firstFile = mediaFiles[0];
812
+ if (!firstFile) {
813
+ throw new Error("No media files available");
814
+ }
815
+ if (mediaFiles.length === 1) {
816
+ return firstFile;
817
+ }
818
+ const mainQuality = getMainStreamQuality();
819
+ if (!mainQuality) {
820
+ console.log("[HlsAdPlayer] No main stream quality info, using first media file");
821
+ return firstFile;
822
+ }
823
+ console.log("[HlsAdPlayer] Main stream quality:", mainQuality);
824
+ const scoredFiles = mediaFiles.map((file) => {
825
+ const widthDiff = Math.abs(file.width - mainQuality.width);
826
+ const heightDiff = Math.abs(file.height - mainQuality.height);
827
+ const resolutionDiff = widthDiff + heightDiff;
828
+ const fileBitrate = (file.bitrate || 5e3) * 1e3;
829
+ const bitrateDiff = Math.abs(fileBitrate - mainQuality.bitrate);
830
+ const score = resolutionDiff * 2 + bitrateDiff / 1e3;
831
+ return { file, score, resolutionDiff, bitrateDiff };
832
+ });
833
+ scoredFiles.sort((a, b) => a.score - b.score);
834
+ const bestMatch = scoredFiles[0];
835
+ if (!bestMatch) {
836
+ console.log("[HlsAdPlayer] No best match found, using first media file");
837
+ return firstFile;
838
+ }
839
+ console.log("[HlsAdPlayer] Selected media file:", {
840
+ url: bestMatch.file.url,
841
+ resolution: `${bestMatch.file.width}x${bestMatch.file.height}`,
842
+ bitrate: bestMatch.file.bitrate,
843
+ score: bestMatch.score,
844
+ resolutionDiff: bestMatch.resolutionDiff,
845
+ bitrateDiff: bestMatch.bitrateDiff
846
+ });
847
+ return bestMatch.file;
848
+ }
849
+ function parseVastXml(xmlString) {
850
+ var _a, _b, _c, _d;
851
+ try {
852
+ const parser = new DOMParser();
853
+ const xmlDoc = parser.parseFromString(xmlString, "text/xml");
854
+ const parserError = xmlDoc.querySelector("parsererror");
855
+ if (parserError) {
856
+ console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
857
+ return null;
858
+ }
859
+ const adElement = xmlDoc.querySelector("Ad");
860
+ if (!adElement) {
861
+ console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
862
+ return null;
863
+ }
864
+ const adId = adElement.getAttribute("id") || "unknown";
865
+ const title = ((_a = xmlDoc.querySelector("AdTitle")) == null ? void 0 : _a.textContent) || "Ad";
866
+ const durationText = ((_b = xmlDoc.querySelector("Duration")) == null ? void 0 : _b.textContent) || "00:00:30";
867
+ const durationParts = durationText.split(":");
868
+ const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
869
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
870
+ const mediaFiles = [];
871
+ mediaFileElements.forEach((mf) => {
872
+ var _a2;
873
+ const type = mf.getAttribute("type") || "";
874
+ if (type === "application/x-mpegURL" || type.includes("m3u8")) {
875
+ const bitrateAttr = mf.getAttribute("bitrate");
876
+ const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
877
+ mediaFiles.push({
878
+ url: ((_a2 = mf.textContent) == null ? void 0 : _a2.trim()) || "",
879
+ type,
880
+ width: parseInt(mf.getAttribute("width") || "1920", 10),
881
+ height: parseInt(mf.getAttribute("height") || "1080", 10),
882
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
883
+ });
884
+ }
885
+ });
886
+ if (mediaFiles.length === 0) {
887
+ console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
888
+ return null;
889
+ }
890
+ const trackingUrls = {
891
+ impression: [],
892
+ start: [],
893
+ firstQuartile: [],
894
+ midpoint: [],
895
+ thirdQuartile: [],
896
+ complete: [],
897
+ mute: [],
898
+ unmute: [],
899
+ pause: [],
900
+ resume: [],
901
+ fullscreen: [],
902
+ exitFullscreen: [],
903
+ skip: [],
904
+ error: []
905
+ };
906
+ xmlDoc.querySelectorAll("Impression").forEach((el) => {
907
+ var _a2;
908
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
909
+ if (url) trackingUrls.impression.push(url);
910
+ });
911
+ xmlDoc.querySelectorAll("Tracking").forEach((el) => {
912
+ var _a2;
913
+ const event = el.getAttribute("event");
914
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
915
+ if (event && url) {
916
+ const eventKey = event;
917
+ if (trackingUrls[eventKey]) {
918
+ trackingUrls[eventKey].push(url);
919
+ }
920
+ }
921
+ });
922
+ const clickThrough = (_d = (_c = xmlDoc.querySelector("ClickThrough")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim();
923
+ return {
924
+ id: adId,
925
+ title,
926
+ duration,
927
+ mediaFiles,
928
+ trackingUrls,
929
+ clickThrough
930
+ };
931
+ } catch (error) {
932
+ console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
933
+ return null;
934
+ }
935
+ }
936
+ function createAdVideoElement() {
937
+ const video = document.createElement("video");
938
+ video.style.position = "absolute";
939
+ video.style.left = "0";
940
+ video.style.top = "0";
941
+ video.style.width = "100%";
942
+ video.style.height = "100%";
943
+ video.style.objectFit = "contain";
944
+ video.style.backgroundColor = "#000";
945
+ video.playsInline = true;
946
+ video.muted = false;
947
+ return video;
948
+ }
949
+ function setupAdEventListeners() {
950
+ if (!adVideoElement || !currentAd) return;
951
+ adVideoElement.addEventListener("timeupdate", () => {
952
+ if (!currentAd || !adVideoElement) return;
953
+ const progress = adVideoElement.currentTime / currentAd.duration;
954
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
955
+ trackingFired.firstQuartile = true;
956
+ fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
957
+ }
958
+ if (progress >= 0.5 && !trackingFired.midpoint) {
959
+ trackingFired.midpoint = true;
960
+ fireTrackingPixels(currentAd.trackingUrls.midpoint);
961
+ }
962
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
963
+ trackingFired.thirdQuartile = true;
964
+ fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
965
+ }
966
+ });
967
+ adVideoElement.addEventListener("playing", () => {
968
+ if (!currentAd || trackingFired.start) return;
969
+ trackingFired.start = true;
970
+ fireTrackingPixels(currentAd.trackingUrls.start);
971
+ console.log("[HlsAdPlayer] Ad started playing");
972
+ });
973
+ adVideoElement.addEventListener("ended", () => {
974
+ if (!currentAd || trackingFired.complete) return;
975
+ trackingFired.complete = true;
976
+ fireTrackingPixels(currentAd.trackingUrls.complete);
977
+ console.log("[HlsAdPlayer] Ad completed");
978
+ handleAdComplete();
979
+ });
980
+ adVideoElement.addEventListener("error", (e) => {
981
+ console.error("[HlsAdPlayer] Ad video error:", e);
982
+ if (currentAd) {
983
+ fireTrackingPixels(currentAd.trackingUrls.error);
984
+ }
985
+ handleAdError();
986
+ });
987
+ adVideoElement.addEventListener("volumechange", () => {
988
+ if (!currentAd) return;
989
+ if (adVideoElement.muted) {
990
+ fireTrackingPixels(currentAd.trackingUrls.mute);
991
+ } else {
992
+ fireTrackingPixels(currentAd.trackingUrls.unmute);
993
+ }
994
+ });
995
+ adVideoElement.addEventListener("pause", () => {
996
+ if (currentAd && !adVideoElement.ended) {
997
+ fireTrackingPixels(currentAd.trackingUrls.pause);
998
+ }
999
+ });
1000
+ adVideoElement.addEventListener("play", () => {
1001
+ if (currentAd && adVideoElement.currentTime > 0) {
1002
+ fireTrackingPixels(currentAd.trackingUrls.resume);
1003
+ }
1004
+ });
1005
+ }
1006
+ function handleAdComplete() {
1007
+ console.log("[HlsAdPlayer] Handling ad completion");
1008
+ adPlaying = false;
1009
+ contentVideo.muted = originalMutedState;
1010
+ if (adContainerEl) {
1011
+ adContainerEl.style.display = "none";
1012
+ adContainerEl.style.pointerEvents = "none";
1013
+ }
1014
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1015
+ contentVideo.play().catch(() => {
1016
+ });
1017
+ console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1018
+ } else {
1019
+ console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1020
+ }
1021
+ emit("content_resume");
1022
+ emit("all_ads_completed");
1023
+ }
1024
+ function handleAdError() {
1025
+ console.log("[HlsAdPlayer] Handling ad error");
1026
+ adPlaying = false;
1027
+ contentVideo.muted = originalMutedState;
1028
+ if (adContainerEl) {
1029
+ adContainerEl.style.display = "none";
1030
+ adContainerEl.style.pointerEvents = "none";
1031
+ }
1032
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1033
+ if (contentVideo.paused) {
1034
+ contentVideo.play().catch(() => {
1035
+ });
1036
+ }
1037
+ }
1038
+ emit("ad_error");
1039
+ }
1040
+ return {
1041
+ initialize() {
1042
+ var _a;
1043
+ console.log("[HlsAdPlayer] Initializing");
1044
+ if (!adContainerEl) {
1045
+ const container = document.createElement("div");
1046
+ container.style.position = "absolute";
1047
+ container.style.left = "0";
1048
+ container.style.top = "0";
1049
+ container.style.right = "0";
1050
+ container.style.bottom = "0";
1051
+ container.style.display = "none";
1052
+ container.style.alignItems = "center";
1053
+ container.style.justifyContent = "center";
1054
+ container.style.pointerEvents = "none";
1055
+ container.style.zIndex = "2";
1056
+ container.style.backgroundColor = "#000";
1057
+ (_a = contentVideo.parentElement) == null ? void 0 : _a.appendChild(container);
1058
+ adContainerEl = container;
1059
+ }
1060
+ },
1061
+ async requestAds(vastTagUrl) {
1062
+ console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
1063
+ if (adPlaying) {
1064
+ console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
1065
+ return Promise.reject(new Error("Ad already playing"));
1066
+ }
1067
+ try {
1068
+ sessionId = generateSessionId();
1069
+ const response = await fetch(vastTagUrl);
1070
+ if (!response.ok) {
1071
+ throw new Error(`Failed to fetch VAST: ${response.statusText}`);
1072
+ }
1073
+ const vastXml = await response.text();
1074
+ console.log("[HlsAdPlayer] VAST XML received");
1075
+ const ad = parseVastXml(vastXml);
1076
+ if (!ad) {
1077
+ throw new Error("Failed to parse VAST XML or no ads available");
1078
+ }
1079
+ currentAd = ad;
1080
+ console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
1081
+ fireTrackingPixels(ad.trackingUrls.impression);
1082
+ trackingFired.impression = true;
1083
+ return Promise.resolve();
1084
+ } catch (error) {
1085
+ console.error("[HlsAdPlayer] Error requesting ads:", error);
1086
+ emit("ad_error");
1087
+ return Promise.reject(error);
1088
+ }
1089
+ },
1090
+ async play() {
1091
+ if (!currentAd) {
1092
+ console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
1093
+ return Promise.reject(new Error("No ad loaded"));
1094
+ }
1095
+ console.log("[HlsAdPlayer] Starting ad playback");
1096
+ try {
1097
+ if (!adVideoElement) {
1098
+ adVideoElement = createAdVideoElement();
1099
+ adContainerEl == null ? void 0 : adContainerEl.appendChild(adVideoElement);
1100
+ setupAdEventListeners();
1101
+ }
1102
+ trackingFired = {
1103
+ impression: trackingFired.impression,
1104
+ start: false,
1105
+ firstQuartile: false,
1106
+ midpoint: false,
1107
+ thirdQuartile: false,
1108
+ complete: false
1109
+ };
1110
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1111
+ contentVideo.pause();
1112
+ console.log("[HlsAdPlayer] Content paused (VOD mode)");
1113
+ } else {
1114
+ console.log("[HlsAdPlayer] Content continues (Live mode)");
1115
+ }
1116
+ contentVideo.muted = true;
1117
+ adPlaying = true;
1118
+ if (adContainerEl) {
1119
+ adContainerEl.style.display = "flex";
1120
+ adContainerEl.style.pointerEvents = "auto";
1121
+ }
1122
+ emit("content_pause");
1123
+ const mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1124
+ if (!mediaFile) {
1125
+ throw new Error("No media file available for ad");
1126
+ }
1127
+ console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
1128
+ if (import_hls.default.isSupported()) {
1129
+ if (adHls) {
1130
+ adHls.destroy();
1131
+ }
1132
+ adHls = new import_hls.default({
1133
+ enableWorker: true,
1134
+ lowLatencyMode: false
1135
+ });
1136
+ adHls.loadSource(mediaFile.url);
1137
+ adHls.attachMedia(adVideoElement);
1138
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
1139
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1140
+ adVideoElement.play().catch((error) => {
1141
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1142
+ handleAdError();
1143
+ });
1144
+ });
1145
+ adHls.on(import_hls.default.Events.ERROR, (event, data) => {
1146
+ console.error("[HlsAdPlayer] HLS error:", data);
1147
+ if (data.fatal) {
1148
+ handleAdError();
1149
+ }
1150
+ });
1151
+ } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1152
+ adVideoElement.src = mediaFile.url;
1153
+ adVideoElement.play().catch((error) => {
1154
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1155
+ handleAdError();
1156
+ });
1157
+ } else {
1158
+ throw new Error("HLS not supported");
1159
+ }
1160
+ return Promise.resolve();
1161
+ } catch (error) {
1162
+ console.error("[HlsAdPlayer] Error playing ad:", error);
1163
+ handleAdError();
1164
+ return Promise.reject(error);
1165
+ }
1166
+ },
1167
+ async stop() {
1168
+ console.log("[HlsAdPlayer] Stopping ad");
1169
+ adPlaying = false;
1170
+ contentVideo.muted = originalMutedState;
1171
+ if (adContainerEl) {
1172
+ adContainerEl.style.display = "none";
1173
+ adContainerEl.style.pointerEvents = "none";
1174
+ }
1175
+ if (adHls) {
1176
+ adHls.destroy();
1177
+ adHls = void 0;
1178
+ }
1179
+ if (adVideoElement) {
1180
+ adVideoElement.pause();
1181
+ adVideoElement.src = "";
1182
+ }
1183
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1184
+ contentVideo.play().catch(() => {
1185
+ });
1186
+ }
1187
+ currentAd = void 0;
1188
+ },
1189
+ destroy() {
1190
+ console.log("[HlsAdPlayer] Destroying");
1191
+ adPlaying = false;
1192
+ contentVideo.muted = originalMutedState;
1193
+ if (adHls) {
1194
+ adHls.destroy();
1195
+ adHls = void 0;
1196
+ }
1197
+ if (adVideoElement) {
1198
+ adVideoElement.pause();
1199
+ adVideoElement.src = "";
1200
+ adVideoElement.remove();
1201
+ adVideoElement = void 0;
1202
+ }
1203
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
1204
+ adContainerEl.parentElement.removeChild(adContainerEl);
1205
+ }
1206
+ adContainerEl = void 0;
1207
+ currentAd = void 0;
1208
+ listeners.clear();
1209
+ },
1210
+ isAdPlaying() {
1211
+ return adPlaying;
1212
+ },
1213
+ resize(width, height) {
1214
+ console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
1215
+ if (adContainerEl) {
1216
+ adContainerEl.style.width = `${width}px`;
1217
+ adContainerEl.style.height = `${height}px`;
1218
+ }
1219
+ if (adVideoElement) {
1220
+ adVideoElement.style.width = `${width}px`;
1221
+ adVideoElement.style.height = `${height}px`;
1222
+ }
1223
+ },
1224
+ on(event, listener) {
1225
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
1226
+ listeners.get(event).add(listener);
1227
+ },
1228
+ off(event, listener) {
1229
+ var _a;
1230
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
1231
+ },
1232
+ updateOriginalMutedState(muted) {
1233
+ originalMutedState = muted;
1234
+ },
1235
+ getOriginalMutedState() {
1236
+ return originalMutedState;
1237
+ },
1238
+ setAdVolume(volume) {
1239
+ if (adVideoElement && adPlaying) {
1240
+ adVideoElement.volume = Math.max(0, Math.min(1, volume));
1241
+ }
1242
+ },
1243
+ getAdVolume() {
1244
+ if (adVideoElement && adPlaying) {
1245
+ return adVideoElement.volume;
1246
+ }
1247
+ return 1;
1248
+ }
1249
+ };
1250
+ }
1251
+
552
1252
  // src/utils/tracking.ts
553
1253
  var cachedBrowserId = null;
554
1254
  function getClientInfo() {
1255
+ var _a, _b, _c, _d;
555
1256
  const ua = navigator.userAgent;
556
1257
  const platform = navigator.platform;
557
1258
  const vendor = navigator.vendor || "";
@@ -559,12 +1260,12 @@ function getClientInfo() {
559
1260
  const memory = navigator.deviceMemory || null;
560
1261
  const hardwareConcurrency = navigator.hardwareConcurrency || 1;
561
1262
  const screenInfo = {
562
- width: screen?.width,
563
- height: screen?.height,
564
- availWidth: screen?.availWidth,
565
- availHeight: screen?.availHeight,
566
- orientation: screen?.orientation?.type || "",
567
- pixelDepth: screen?.pixelDepth
1263
+ width: screen == null ? void 0 : screen.width,
1264
+ height: screen == null ? void 0 : screen.height,
1265
+ availWidth: screen == null ? void 0 : screen.availWidth,
1266
+ availHeight: screen == null ? void 0 : screen.availHeight,
1267
+ orientation: ((_a = screen == null ? void 0 : screen.orientation) == null ? void 0 : _a.type) || "",
1268
+ pixelDepth: screen == null ? void 0 : screen.pixelDepth
568
1269
  };
569
1270
  let deviceType = "desktop";
570
1271
  let brand = "Unknown";
@@ -661,10 +1362,10 @@ function getClientInfo() {
661
1362
  if (vendor.includes("Samsung") || ua.includes("SM-")) brand = "Samsung";
662
1363
  }
663
1364
  isWebView = /wv|WebView|Linux; U;/.test(ua);
664
- if (window?.outerHeight === 0 && window?.outerWidth === 0) {
1365
+ if ((window == null ? void 0 : window.outerHeight) === 0 && (window == null ? void 0 : window.outerWidth) === 0) {
665
1366
  isWebView = true;
666
1367
  }
667
- isWebApp = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;
1368
+ 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;
668
1369
  return {
669
1370
  brand,
670
1371
  os,
@@ -685,7 +1386,7 @@ function getClientInfo() {
685
1386
  deviceMemory: memory,
686
1387
  maxTouchPoints,
687
1388
  language: navigator.language,
688
- languages: navigator.languages?.join(",") || "",
1389
+ languages: ((_d = navigator.languages) == null ? void 0 : _d.join(",")) || "",
689
1390
  cookieEnabled: navigator.cookieEnabled,
690
1391
  doNotTrack: navigator.doNotTrack || "",
691
1392
  referrer: document.referrer,
@@ -798,6 +1499,215 @@ async function sendHeartbeat(licenseKey) {
798
1499
  }
799
1500
  }
800
1501
 
1502
+ // src/utils/polyfills.ts
1503
+ function polyfillURLSearchParams() {
1504
+ if (typeof URLSearchParams !== "undefined") {
1505
+ return;
1506
+ }
1507
+ class URLSearchParamsPolyfill {
1508
+ constructor(init) {
1509
+ this.params = /* @__PURE__ */ new Map();
1510
+ if (typeof init === "string") {
1511
+ this.parseQueryString(init);
1512
+ } else if (init instanceof URLSearchParamsPolyfill) {
1513
+ init.forEach((value, key) => {
1514
+ this.append(key, value);
1515
+ });
1516
+ }
1517
+ }
1518
+ parseQueryString(query) {
1519
+ const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
1520
+ if (!cleanQuery) return;
1521
+ cleanQuery.split("&").forEach((param) => {
1522
+ const [key, value] = param.split("=");
1523
+ if (key) {
1524
+ const decodedKey = this.safeDecodeURIComponent(key);
1525
+ const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
1526
+ this.append(decodedKey, decodedValue);
1527
+ }
1528
+ });
1529
+ }
1530
+ safeDecodeURIComponent(str) {
1531
+ try {
1532
+ return decodeURIComponent(str.replace(/\+/g, " "));
1533
+ } catch (e) {
1534
+ return str;
1535
+ }
1536
+ }
1537
+ append(name, value) {
1538
+ const values = this.params.get(name) || [];
1539
+ values.push(String(value));
1540
+ this.params.set(name, values);
1541
+ }
1542
+ delete(name) {
1543
+ this.params.delete(name);
1544
+ }
1545
+ get(name) {
1546
+ const values = this.params.get(name);
1547
+ return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
1548
+ }
1549
+ getAll(name) {
1550
+ return this.params.get(name) || [];
1551
+ }
1552
+ has(name) {
1553
+ return this.params.has(name);
1554
+ }
1555
+ set(name, value) {
1556
+ this.params.set(name, [String(value)]);
1557
+ }
1558
+ forEach(callback) {
1559
+ this.params.forEach((values, key) => {
1560
+ values.forEach((value) => {
1561
+ callback(value, key, this);
1562
+ });
1563
+ });
1564
+ }
1565
+ toString() {
1566
+ const parts = [];
1567
+ this.params.forEach((values, key) => {
1568
+ values.forEach((value) => {
1569
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
1570
+ });
1571
+ });
1572
+ return parts.join("&");
1573
+ }
1574
+ }
1575
+ window.URLSearchParams = URLSearchParamsPolyfill;
1576
+ }
1577
+ function polyfillTextEncoder() {
1578
+ if (typeof TextEncoder !== "undefined") {
1579
+ return;
1580
+ }
1581
+ class TextEncoderPolyfill {
1582
+ constructor() {
1583
+ this.encoding = "utf-8";
1584
+ }
1585
+ encode(str) {
1586
+ const utf8 = [];
1587
+ for (let i = 0; i < str.length; i++) {
1588
+ let charcode = str.charCodeAt(i);
1589
+ if (charcode < 128) {
1590
+ utf8.push(charcode);
1591
+ } else if (charcode < 2048) {
1592
+ utf8.push(192 | charcode >> 6, 128 | charcode & 63);
1593
+ } else if (charcode < 55296 || charcode >= 57344) {
1594
+ utf8.push(
1595
+ 224 | charcode >> 12,
1596
+ 128 | charcode >> 6 & 63,
1597
+ 128 | charcode & 63
1598
+ );
1599
+ } else {
1600
+ i++;
1601
+ charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
1602
+ utf8.push(
1603
+ 240 | charcode >> 18,
1604
+ 128 | charcode >> 12 & 63,
1605
+ 128 | charcode >> 6 & 63,
1606
+ 128 | charcode & 63
1607
+ );
1608
+ }
1609
+ }
1610
+ return new Uint8Array(utf8);
1611
+ }
1612
+ }
1613
+ window.TextEncoder = TextEncoderPolyfill;
1614
+ }
1615
+ function polyfillPromiseFinally() {
1616
+ if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
1617
+ Promise.prototype.finally = function(callback) {
1618
+ const constructor = this.constructor;
1619
+ return this.then(
1620
+ (value) => constructor.resolve(callback()).then(() => value),
1621
+ (reason) => constructor.resolve(callback()).then(() => {
1622
+ throw reason;
1623
+ })
1624
+ );
1625
+ };
1626
+ }
1627
+ }
1628
+ function polyfillObjectAssign() {
1629
+ if (typeof Object.assign !== "function") {
1630
+ Object.assign = function(target, ...sources) {
1631
+ if (target == null) {
1632
+ throw new TypeError("Cannot convert undefined or null to object");
1633
+ }
1634
+ const to = Object(target);
1635
+ for (let i = 0; i < sources.length; i++) {
1636
+ const nextSource = sources[i];
1637
+ if (nextSource != null) {
1638
+ for (const nextKey in nextSource) {
1639
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
1640
+ to[nextKey] = nextSource[nextKey];
1641
+ }
1642
+ }
1643
+ }
1644
+ }
1645
+ return to;
1646
+ };
1647
+ }
1648
+ }
1649
+ function polyfillArrayFrom() {
1650
+ if (!Array.from) {
1651
+ Array.from = function(arrayLike, mapFn, thisArg) {
1652
+ const items = Object(arrayLike);
1653
+ if (arrayLike == null) {
1654
+ throw new TypeError("Array.from requires an array-like object");
1655
+ }
1656
+ const len = items.length >>> 0;
1657
+ const result = new Array(len);
1658
+ for (let i = 0; i < len; i++) {
1659
+ if (mapFn) {
1660
+ result[i] = mapFn.call(thisArg, items[i], i);
1661
+ } else {
1662
+ result[i] = items[i];
1663
+ }
1664
+ }
1665
+ return result;
1666
+ };
1667
+ }
1668
+ }
1669
+ function polyfillStringStartsWith() {
1670
+ if (!String.prototype.startsWith) {
1671
+ String.prototype.startsWith = function(search, pos) {
1672
+ pos = !pos || pos < 0 ? 0 : +pos;
1673
+ return this.substring(pos, pos + search.length) === search;
1674
+ };
1675
+ }
1676
+ }
1677
+ function polyfillStringEndsWith() {
1678
+ if (!String.prototype.endsWith) {
1679
+ String.prototype.endsWith = function(search, length) {
1680
+ if (length === void 0 || length > this.length) {
1681
+ length = this.length;
1682
+ }
1683
+ return this.substring(length - search.length, length) === search;
1684
+ };
1685
+ }
1686
+ }
1687
+ function polyfillStringIncludes() {
1688
+ if (!String.prototype.includes) {
1689
+ String.prototype.includes = function(search, start) {
1690
+ if (typeof start !== "number") {
1691
+ start = 0;
1692
+ }
1693
+ if (start + search.length > this.length) {
1694
+ return false;
1695
+ }
1696
+ return this.indexOf(search, start) !== -1;
1697
+ };
1698
+ }
1699
+ }
1700
+ function initializePolyfills() {
1701
+ polyfillObjectAssign();
1702
+ polyfillArrayFrom();
1703
+ polyfillStringStartsWith();
1704
+ polyfillStringEndsWith();
1705
+ polyfillStringIncludes();
1706
+ polyfillURLSearchParams();
1707
+ polyfillTextEncoder();
1708
+ polyfillPromiseFinally();
1709
+ }
1710
+
801
1711
  // src/player/StormcloudVideoPlayer.ts
802
1712
  var StormcloudVideoPlayer = class {
803
1713
  constructor(config) {
@@ -810,13 +1720,44 @@ var StormcloudVideoPlayer = class {
810
1720
  this.totalAdsInBreak = 0;
811
1721
  this.showAds = false;
812
1722
  this.isLiveStream = false;
813
- this.config = config;
1723
+ initializePolyfills();
1724
+ const browserOverrides = getBrowserConfigOverrides();
1725
+ this.config = { ...config, ...browserOverrides };
814
1726
  this.video = config.videoElement;
815
- this.ima = createImaController(this.video, {
816
- continueLiveStreamDuringAds: false
817
- });
1727
+ logBrowserInfo(config.debugAdTiming);
1728
+ this.ima = this.createAdPlayer(false);
1729
+ }
1730
+ createAdPlayer(continueLiveStreamDuringAds) {
1731
+ const vastMode = this.config.vastMode || "default";
1732
+ let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
1733
+ if (adPlayerType === "ima" && !supportsGoogleIMA()) {
1734
+ if (this.config.debugAdTiming) {
1735
+ console.warn(
1736
+ "[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
1737
+ );
1738
+ }
1739
+ adPlayerType = "hls";
1740
+ }
1741
+ if (adPlayerType === "hls") {
1742
+ if (this.config.debugAdTiming) {
1743
+ console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
1744
+ }
1745
+ return createHlsAdPlayer(this.video, {
1746
+ continueLiveStreamDuringAds,
1747
+ ...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {},
1748
+ ...this.hls ? { mainHlsInstance: this.hls } : {}
1749
+ });
1750
+ } else {
1751
+ if (this.config.debugAdTiming) {
1752
+ console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
1753
+ }
1754
+ return createImaController(this.video, {
1755
+ continueLiveStreamDuringAds
1756
+ });
1757
+ }
818
1758
  }
819
1759
  async load() {
1760
+ var _a, _b;
820
1761
  if (!this.attached) {
821
1762
  this.attach();
822
1763
  }
@@ -833,7 +1774,7 @@ var StormcloudVideoPlayer = class {
833
1774
  this.initializeTracking();
834
1775
  if (this.shouldUseNativeHls()) {
835
1776
  this.video.src = this.config.src;
836
- this.isLiveStream = this.config.lowLatencyMode ?? false;
1777
+ this.isLiveStream = (_a = this.config.lowLatencyMode) != null ? _a : false;
837
1778
  if (this.config.debugAdTiming) {
838
1779
  console.log(
839
1780
  "[StormcloudVideoPlayer] allowNativeHls: true - VOD mode detected:",
@@ -845,17 +1786,15 @@ var StormcloudVideoPlayer = class {
845
1786
  );
846
1787
  }
847
1788
  this.ima.destroy();
848
- this.ima = createImaController(this.video, {
849
- continueLiveStreamDuringAds: false
850
- });
1789
+ this.ima = this.createAdPlayer(false);
851
1790
  this.ima.initialize();
852
1791
  if (this.config.autoplay) {
853
- await this.video.play()?.catch(() => {
854
- });
1792
+ await ((_b = this.video.play()) == null ? void 0 : _b.catch(() => {
1793
+ }));
855
1794
  }
856
1795
  return;
857
1796
  }
858
- this.hls = new import_hls.default({
1797
+ this.hls = new import_hls2.default({
859
1798
  enableWorker: true,
860
1799
  backBufferLength: 30,
861
1800
  liveDurationInfinity: true,
@@ -863,13 +1802,18 @@ var StormcloudVideoPlayer = class {
863
1802
  maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
864
1803
  ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
865
1804
  });
866
- this.hls.on(import_hls.default.Events.MEDIA_ATTACHED, () => {
867
- this.hls?.loadSource(this.config.src);
1805
+ this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
1806
+ var _a2;
1807
+ (_a2 = this.hls) == null ? void 0 : _a2.loadSource(this.config.src);
868
1808
  });
869
- this.hls.on(import_hls.default.Events.MANIFEST_PARSED, async (_, data) => {
870
- this.isLiveStream = this.hls?.levels?.some(
871
- (level) => level?.details?.live === true || level?.details?.type === "LIVE"
872
- ) ?? false;
1809
+ this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
1810
+ var _a2, _b2, _c, _d;
1811
+ this.isLiveStream = (_c = (_b2 = (_a2 = this.hls) == null ? void 0 : _a2.levels) == null ? void 0 : _b2.some(
1812
+ (level) => {
1813
+ var _a3, _b3;
1814
+ 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";
1815
+ }
1816
+ )) != null ? _c : false;
873
1817
  if (this.config.debugAdTiming) {
874
1818
  const adBehavior = this.shouldContinueLiveStreamDuringAds() ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
875
1819
  console.log("[StormcloudVideoPlayer] Stream type detected:", {
@@ -879,33 +1823,32 @@ var StormcloudVideoPlayer = class {
879
1823
  });
880
1824
  }
881
1825
  this.ima.destroy();
882
- this.ima = createImaController(this.video, {
883
- continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
884
- });
1826
+ this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
885
1827
  this.ima.initialize();
886
1828
  if (this.config.autoplay) {
887
- await this.video.play()?.catch(() => {
888
- });
1829
+ await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
1830
+ }));
889
1831
  }
890
1832
  });
891
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
892
- const id3Tags = (data?.samples || []).map((s) => ({
1833
+ this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
1834
+ const id3Tags = ((data == null ? void 0 : data.samples) || []).map((s) => ({
893
1835
  key: "ID3",
894
- value: s?.data,
895
- ptsSeconds: s?.pts
1836
+ value: s == null ? void 0 : s.data,
1837
+ ptsSeconds: s == null ? void 0 : s.pts
896
1838
  }));
897
1839
  id3Tags.forEach((tag) => this.onId3Tag(tag));
898
1840
  });
899
- this.hls.on(import_hls.default.Events.FRAG_CHANGED, (_evt, data) => {
900
- const frag = data?.frag;
901
- const tagList = frag?.tagList;
1841
+ this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
1842
+ var _a2, _b2, _c;
1843
+ const frag = data == null ? void 0 : data.frag;
1844
+ const tagList = frag == null ? void 0 : frag.tagList;
902
1845
  if (!Array.isArray(tagList)) return;
903
1846
  for (const entry of tagList) {
904
1847
  let tag = "";
905
1848
  let value = "";
906
1849
  if (Array.isArray(entry)) {
907
- tag = String(entry[0] ?? "");
908
- value = String(entry[1] ?? "");
1850
+ tag = String((_a2 = entry[0]) != null ? _a2 : "");
1851
+ value = String((_b2 = entry[1]) != null ? _b2 : "");
909
1852
  } else if (typeof entry === "string") {
910
1853
  const idx = entry.indexOf(":");
911
1854
  if (idx >= 0) {
@@ -929,8 +1872,8 @@ var StormcloudVideoPlayer = class {
929
1872
  const prog = this.parseCueOutCont(value);
930
1873
  const marker = {
931
1874
  type: "progress",
932
- ...prog?.duration !== void 0 ? { durationSeconds: prog.duration } : {},
933
- ...prog?.elapsed !== void 0 ? { ptsSeconds: prog.elapsed } : {},
1875
+ ...(prog == null ? void 0 : prog.duration) !== void 0 ? { durationSeconds: prog.duration } : {},
1876
+ ...(prog == null ? void 0 : prog.elapsed) !== void 0 ? { ptsSeconds: prog.elapsed } : {},
934
1877
  raw: { tag, value }
935
1878
  };
936
1879
  this.onScte35Marker(marker);
@@ -940,7 +1883,7 @@ var StormcloudVideoPlayer = class {
940
1883
  const attrs = this.parseAttributeList(value);
941
1884
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
942
1885
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
943
- const klass = String(attrs["CLASS"] ?? "");
1886
+ const klass = String((_c = attrs["CLASS"]) != null ? _c : "");
944
1887
  const duration = this.toNumber(attrs["DURATION"]);
945
1888
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
946
1889
  const marker = {
@@ -956,14 +1899,15 @@ var StormcloudVideoPlayer = class {
956
1899
  }
957
1900
  }
958
1901
  });
959
- this.hls.on(import_hls.default.Events.ERROR, (_evt, data) => {
960
- if (data?.fatal) {
1902
+ this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
1903
+ var _a2, _b2;
1904
+ if (data == null ? void 0 : data.fatal) {
961
1905
  switch (data.type) {
962
- case import_hls.default.ErrorTypes.NETWORK_ERROR:
963
- this.hls?.startLoad();
1906
+ case import_hls2.default.ErrorTypes.NETWORK_ERROR:
1907
+ (_a2 = this.hls) == null ? void 0 : _a2.startLoad();
964
1908
  break;
965
- case import_hls.default.ErrorTypes.MEDIA_ERROR:
966
- this.hls?.recoverMediaError();
1909
+ case import_hls2.default.ErrorTypes.MEDIA_ERROR:
1910
+ (_b2 = this.hls) == null ? void 0 : _b2.recoverMediaError();
967
1911
  break;
968
1912
  default:
969
1913
  this.destroy();
@@ -1065,11 +2009,12 @@ var StormcloudVideoPlayer = class {
1065
2009
  }
1066
2010
  }
1067
2011
  parseScte35FromId3(tag) {
2012
+ var _a, _b, _c, _d;
1068
2013
  const text = this.decodeId3ValueToText(tag.value);
1069
2014
  if (!text) return void 0;
1070
2015
  const cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
1071
2016
  if (cueOutMatch) {
1072
- const arg = (cueOutMatch[1] ?? "").trim();
2017
+ const arg = ((_a = cueOutMatch[1]) != null ? _a : "").trim();
1073
2018
  const dur = this.parseCueOutDuration(arg);
1074
2019
  const marker = {
1075
2020
  type: "start",
@@ -1081,12 +2026,12 @@ var StormcloudVideoPlayer = class {
1081
2026
  }
1082
2027
  const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
1083
2028
  if (cueOutContMatch) {
1084
- const arg = (cueOutContMatch[1] ?? "").trim();
2029
+ const arg = ((_b = cueOutContMatch[1]) != null ? _b : "").trim();
1085
2030
  const cont = this.parseCueOutCont(arg);
1086
2031
  const marker = {
1087
2032
  type: "progress",
1088
2033
  ...tag.ptsSeconds !== void 0 ? { ptsSeconds: tag.ptsSeconds } : {},
1089
- ...cont?.duration !== void 0 ? { durationSeconds: cont.duration } : {},
2034
+ ...(cont == null ? void 0 : cont.duration) !== void 0 ? { durationSeconds: cont.duration } : {},
1090
2035
  raw: { id3: text }
1091
2036
  };
1092
2037
  return marker;
@@ -1102,10 +2047,10 @@ var StormcloudVideoPlayer = class {
1102
2047
  }
1103
2048
  const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
1104
2049
  if (daterangeMatch) {
1105
- const attrs = this.parseAttributeList(daterangeMatch[1] ?? "");
2050
+ const attrs = this.parseAttributeList((_c = daterangeMatch[1]) != null ? _c : "");
1106
2051
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
1107
2052
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
1108
- const klass = String(attrs["CLASS"] ?? "");
2053
+ const klass = String((_d = attrs["CLASS"]) != null ? _d : "");
1109
2054
  const duration = this.toNumber(attrs["DURATION"]);
1110
2055
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
1111
2056
  const marker = {
@@ -1162,6 +2107,7 @@ var StormcloudVideoPlayer = class {
1162
2107
  }
1163
2108
  }
1164
2109
  onScte35Marker(marker) {
2110
+ var _a, _b;
1165
2111
  if (this.config.debugAdTiming) {
1166
2112
  console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
1167
2113
  type: marker.type,
@@ -1177,7 +2123,7 @@ var StormcloudVideoPlayer = class {
1177
2123
  this.expectedAdBreakDurationMs = durationMs;
1178
2124
  this.currentAdBreakStartWallClockMs = Date.now();
1179
2125
  const isManifestMarker = this.isManifestBasedMarker(marker);
1180
- const forceImmediate = this.config.immediateManifestAds ?? true;
2126
+ const forceImmediate = (_a = this.config.immediateManifestAds) != null ? _a : true;
1181
2127
  if (this.config.debugAdTiming) {
1182
2128
  console.log("[StormcloudVideoPlayer] Ad start decision:", {
1183
2129
  isManifestMarker,
@@ -1194,7 +2140,7 @@ var StormcloudVideoPlayer = class {
1194
2140
  this.clearAdStartTimer();
1195
2141
  this.handleAdStart(marker);
1196
2142
  } else if (typeof marker.ptsSeconds === "number") {
1197
- const tol = this.config.driftToleranceMs ?? 1e3;
2143
+ const tol = (_b = this.config.driftToleranceMs) != null ? _b : 1e3;
1198
2144
  const nowMs = this.video.currentTime * 1e3;
1199
2145
  const estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
1200
2146
  const deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
@@ -1304,12 +2250,13 @@ var StormcloudVideoPlayer = class {
1304
2250
  return void 0;
1305
2251
  }
1306
2252
  parseAttributeList(value) {
2253
+ var _a, _b, _c;
1307
2254
  const attrs = {};
1308
2255
  const regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
1309
2256
  let match;
1310
2257
  while ((match = regex.exec(value)) !== null) {
1311
- const key = match[1] ?? "";
1312
- let rawVal = match[3] ?? match[4] ?? "";
2258
+ const key = (_a = match[1]) != null ? _a : "";
2259
+ let rawVal = (_c = (_b = match[3]) != null ? _b : match[4]) != null ? _c : "";
1313
2260
  if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
1314
2261
  rawVal = rawVal.slice(1, -1);
1315
2262
  }
@@ -1473,6 +2420,43 @@ var StormcloudVideoPlayer = class {
1473
2420
  }
1474
2421
  }
1475
2422
  async fetchAdConfiguration() {
2423
+ var _a, _b, _c;
2424
+ const vastMode = this.config.vastMode || "default";
2425
+ if (this.config.debugAdTiming) {
2426
+ console.log(
2427
+ "[StormcloudVideoPlayer] VAST mode:",
2428
+ vastMode
2429
+ );
2430
+ }
2431
+ if (vastMode === "adstorm") {
2432
+ if (!this.config.licenseKey) {
2433
+ if (this.config.debugAdTiming) {
2434
+ console.warn(
2435
+ "[StormcloudVideoPlayer] AdStorm mode requires a license key"
2436
+ );
2437
+ }
2438
+ return;
2439
+ }
2440
+ const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
2441
+ this.apiVastTagUrl = vastEndpoint;
2442
+ if (this.config.debugAdTiming) {
2443
+ console.log(
2444
+ "[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
2445
+ vastEndpoint
2446
+ );
2447
+ }
2448
+ return;
2449
+ }
2450
+ if (this.config.vastTagUrl) {
2451
+ this.apiVastTagUrl = this.config.vastTagUrl;
2452
+ if (this.config.debugAdTiming) {
2453
+ console.log(
2454
+ "[StormcloudVideoPlayer] Using custom VAST tag URL:",
2455
+ this.apiVastTagUrl
2456
+ );
2457
+ }
2458
+ return;
2459
+ }
1476
2460
  const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
1477
2461
  if (this.config.debugAdTiming) {
1478
2462
  console.log(
@@ -1486,25 +2470,29 @@ var StormcloudVideoPlayer = class {
1486
2470
  }
1487
2471
  const response = await fetch(apiUrl, { headers });
1488
2472
  if (!response.ok) {
1489
- throw new Error(`Failed to fetch ad configuration: ${response.status}`);
2473
+ if (this.config.debugAdTiming) {
2474
+ console.warn(
2475
+ `[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
2476
+ );
2477
+ }
2478
+ return;
1490
2479
  }
1491
2480
  const data = await response.json();
1492
- const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
2481
+ const imaPayload = (_c = (_b = (_a = data.response) == null ? void 0 : _a.ima) == null ? void 0 : _b["publisherdesk.ima"]) == null ? void 0 : _c.payload;
1493
2482
  if (imaPayload) {
1494
2483
  this.apiVastTagUrl = decodeURIComponent(imaPayload);
1495
2484
  if (this.config.debugAdTiming) {
1496
2485
  console.log(
1497
- "[StormcloudVideoPlayer] Extracted VAST tag URL:",
2486
+ "[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
1498
2487
  this.apiVastTagUrl
1499
2488
  );
1500
2489
  }
1501
- }
1502
- this.vastConfig = data.response?.options?.vast;
1503
- if (this.config.debugAdTiming) {
1504
- console.log("[StormcloudVideoPlayer] Ad configuration loaded:", {
1505
- vastTagUrl: this.apiVastTagUrl,
1506
- vastConfig: this.vastConfig
1507
- });
2490
+ } else {
2491
+ if (this.config.debugAdTiming) {
2492
+ console.warn(
2493
+ "[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
2494
+ );
2495
+ }
1508
2496
  }
1509
2497
  }
1510
2498
  getCurrentAdIndex() {
@@ -1527,11 +2515,12 @@ var StormcloudVideoPlayer = class {
1527
2515
  return "other";
1528
2516
  }
1529
2517
  shouldShowNativeControls() {
2518
+ var _a, _b;
1530
2519
  const streamType = this.getStreamType();
1531
2520
  if (streamType === "other") {
1532
- return !(this.config.showCustomControls ?? false);
2521
+ return !((_a = this.config.showCustomControls) != null ? _a : false);
1533
2522
  }
1534
- return !!(this.config.allowNativeHls && !(this.config.showCustomControls ?? false));
2523
+ return !!(this.config.allowNativeHls && !((_b = this.config.showCustomControls) != null ? _b : false));
1535
2524
  }
1536
2525
  shouldContinueLiveStreamDuringAds() {
1537
2526
  if (this.config.allowNativeHls) {
@@ -1543,28 +2532,20 @@ var StormcloudVideoPlayer = class {
1543
2532
  return true;
1544
2533
  }
1545
2534
  async handleAdStart(_marker) {
2535
+ var _a;
1546
2536
  const scheduled = this.findCurrentOrNextBreak(
1547
2537
  this.video.currentTime * 1e3
1548
2538
  );
1549
2539
  const tags = this.selectVastTagsForBreak(scheduled);
1550
2540
  let vastTagUrl;
1551
- let adsNumber = 1;
1552
2541
  if (this.apiVastTagUrl) {
1553
2542
  vastTagUrl = this.apiVastTagUrl;
1554
- if (this.vastConfig) {
1555
- const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
1556
- if (isHls && this.vastConfig.cue_tones?.number_ads) {
1557
- adsNumber = this.vastConfig.cue_tones.number_ads;
1558
- } else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
1559
- adsNumber = this.vastConfig.timer_vod.number_ads;
1560
- }
1561
- }
1562
- this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
2543
+ this.adPodQueue = [];
1563
2544
  this.currentAdIndex = 0;
1564
- this.totalAdsInBreak = adsNumber;
2545
+ this.totalAdsInBreak = 1;
1565
2546
  if (this.config.debugAdTiming) {
1566
2547
  console.log(
1567
- `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
2548
+ "[StormcloudVideoPlayer] Using VAST endpoint:",
1568
2549
  vastTagUrl
1569
2550
  );
1570
2551
  }
@@ -1602,17 +2583,18 @@ var StormcloudVideoPlayer = class {
1602
2583
  this.handleAdFailure();
1603
2584
  }
1604
2585
  }
1605
- if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
2586
+ if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
1606
2587
  this.expectedAdBreakDurationMs = scheduled.durationMs;
1607
- this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
2588
+ this.currentAdBreakStartWallClockMs = (_a = this.currentAdBreakStartWallClockMs) != null ? _a : Date.now();
1608
2589
  this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
1609
2590
  }
1610
2591
  }
1611
2592
  findCurrentOrNextBreak(nowMs) {
2593
+ var _a;
1612
2594
  const schedule = [];
1613
2595
  let candidate;
1614
2596
  for (const b of schedule) {
1615
- const tol = this.config.driftToleranceMs ?? 1e3;
2597
+ const tol = (_a = this.config.driftToleranceMs) != null ? _a : 1e3;
1616
2598
  if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
1617
2599
  candidate = b;
1618
2600
  }
@@ -1628,7 +2610,8 @@ var StormcloudVideoPlayer = class {
1628
2610
  }
1629
2611
  }
1630
2612
  async handleMidAdJoin(adBreak, nowMs) {
1631
- const durationMs = adBreak.durationMs ?? 0;
2613
+ var _a;
2614
+ const durationMs = (_a = adBreak.durationMs) != null ? _a : 0;
1632
2615
  const endMs = adBreak.startTimeMs + durationMs;
1633
2616
  if (durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs) {
1634
2617
  const remainingMs = endMs - nowMs;
@@ -1729,6 +2712,7 @@ var StormcloudVideoPlayer = class {
1729
2712
  }
1730
2713
  }
1731
2714
  handleAdFailure() {
2715
+ var _a;
1732
2716
  if (this.config.debugAdTiming) {
1733
2717
  console.log(
1734
2718
  "[StormcloudVideoPlayer] Handling ad failure - resuming content",
@@ -1761,7 +2745,7 @@ var StormcloudVideoPlayer = class {
1761
2745
  if (this.config.debugAdTiming) {
1762
2746
  console.log("[StormcloudVideoPlayer] Resuming paused video");
1763
2747
  }
1764
- this.video.play()?.catch((error) => {
2748
+ (_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
1765
2749
  if (this.config.debugAdTiming) {
1766
2750
  console.error(
1767
2751
  "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
@@ -1776,8 +2760,9 @@ var StormcloudVideoPlayer = class {
1776
2760
  }
1777
2761
  }
1778
2762
  startAdFailsafeTimer() {
2763
+ var _a;
1779
2764
  this.clearAdFailsafeTimer();
1780
- const failsafeMs = this.config.adFailsafeTimeoutMs ?? 1e4;
2765
+ const failsafeMs = (_a = this.config.adFailsafeTimeoutMs) != null ? _a : 1e4;
1781
2766
  if (this.config.debugAdTiming) {
1782
2767
  console.log(
1783
2768
  `[StormcloudVideoPlayer] Starting failsafe timer (${failsafeMs}ms)`
@@ -1913,6 +2898,7 @@ var StormcloudVideoPlayer = class {
1913
2898
  }
1914
2899
  }
1915
2900
  destroy() {
2901
+ var _a, _b;
1916
2902
  this.clearAdStartTimer();
1917
2903
  this.clearAdStopTimer();
1918
2904
  this.clearAdFailsafeTimer();
@@ -1920,8 +2906,8 @@ var StormcloudVideoPlayer = class {
1920
2906
  clearInterval(this.heartbeatInterval);
1921
2907
  this.heartbeatInterval = void 0;
1922
2908
  }
1923
- this.hls?.destroy();
1924
- this.ima?.destroy();
2909
+ (_a = this.hls) == null ? void 0 : _a.destroy();
2910
+ (_b = this.ima) == null ? void 0 : _b.destroy();
1925
2911
  }
1926
2912
  };
1927
2913
 
@@ -2028,7 +3014,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2028
3014
  }
2029
3015
  setShowSpeedMenu(false);
2030
3016
  };
2031
- const isHlsStream = src?.toLowerCase().includes(".m3u8") || src?.toLowerCase().includes("/hls/");
3017
+ const isHlsStream = (src == null ? void 0 : src.toLowerCase().includes(".m3u8")) || (src == null ? void 0 : src.toLowerCase().includes("/hls/"));
2032
3018
  const shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
2033
3019
  const criticalPropsKey = (0, import_react.useMemo)(() => {
2034
3020
  return CRITICAL_PROPS.map((prop) => `${prop}:${props[prop]}`).join("|");
@@ -2078,13 +3064,13 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2078
3064
  player.load().then(() => {
2079
3065
  const showNative = player.shouldShowNativeControls();
2080
3066
  setShouldShowNativeControls(showNative);
2081
- onReady?.(player);
3067
+ onReady == null ? void 0 : onReady(player);
2082
3068
  }).catch((error) => {
2083
3069
  console.error(
2084
3070
  "StormcloudVideoPlayer: Failed to load player:",
2085
3071
  error
2086
3072
  );
2087
- onReady?.(player);
3073
+ onReady == null ? void 0 : onReady(player);
2088
3074
  });
2089
3075
  return () => {
2090
3076
  try {
@@ -2140,6 +3126,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2140
3126
  (0, import_react.useEffect)(() => {
2141
3127
  if (!playerRef.current || !videoRef.current) return;
2142
3128
  const updateStates = () => {
3129
+ var _a;
2143
3130
  if (playerRef.current && videoRef.current) {
2144
3131
  setIsMuted(playerRef.current.isMuted());
2145
3132
  setIsPlaying(!videoRef.current.paused);
@@ -2157,13 +3144,14 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2157
3144
  );
2158
3145
  }
2159
3146
  setIsFullscreen(
2160
- document.fullscreenElement === videoRef.current?.parentElement
3147
+ document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
2161
3148
  );
2162
3149
  };
2163
3150
  const interval = setInterval(updateStates, 200);
2164
3151
  const handleFullscreenChange = () => {
3152
+ var _a;
2165
3153
  setIsFullscreen(
2166
- document.fullscreenElement === videoRef.current?.parentElement
3154
+ document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
2167
3155
  );
2168
3156
  };
2169
3157
  document.addEventListener("fullscreenchange", handleFullscreenChange);