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
@@ -75,7 +75,167 @@ var canPlay = {
75
75
  var import_react2 = require("react");
76
76
 
77
77
  // src/player/StormcloudVideoPlayer.ts
78
- var import_hls = __toESM(require("hls.js"), 1);
78
+ var import_hls2 = __toESM(require("hls.js"), 1);
79
+
80
+ // src/utils/browserCompat.ts
81
+ function getChromeVersion(ua) {
82
+ const match = ua.match(/Chrome\/(\d+)/);
83
+ return match && match[1] ? parseInt(match[1], 10) : 0;
84
+ }
85
+ function getWebKitVersion(ua) {
86
+ const match = ua.match(/AppleWebKit\/(\d+)/);
87
+ return match && match[1] ? parseInt(match[1], 10) : 0;
88
+ }
89
+ function getPlatform() {
90
+ var _a;
91
+ if ("userAgentData" in navigator && ((_a = navigator.userAgentData) == null ? void 0 : _a.platform)) {
92
+ return navigator.userAgentData.platform;
93
+ }
94
+ const ua = navigator.userAgent;
95
+ if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
96
+ return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
97
+ }
98
+ if (/Win/i.test(ua)) {
99
+ return "Win32";
100
+ }
101
+ if (/Linux/i.test(ua)) {
102
+ return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
103
+ }
104
+ if (/CrOS/i.test(ua)) {
105
+ return "CrOS";
106
+ }
107
+ return navigator.platform || "Unknown";
108
+ }
109
+ function detectBrowser() {
110
+ const ua = navigator.userAgent;
111
+ const platform = getPlatform();
112
+ let name = "Unknown";
113
+ let version = "0";
114
+ let majorVersion = 0;
115
+ let isSmartTV = false;
116
+ let isLegacyTV = false;
117
+ let supportsIMA = true;
118
+ let supportsModernJS = true;
119
+ let recommendedAdPlayer = "ima";
120
+ if (/Web0S|webOS/i.test(ua)) {
121
+ name = "LG WebOS";
122
+ isSmartTV = true;
123
+ const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
124
+ version = match && match[1] ? match[1] : "Unknown";
125
+ if (version !== "Unknown") {
126
+ const parts = version.split(".");
127
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
128
+ }
129
+ } else if (/Tizen/i.test(ua)) {
130
+ name = "Samsung Tizen";
131
+ isSmartTV = true;
132
+ const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
133
+ version = match && match[1] ? match[1] : "Unknown";
134
+ if (version !== "Unknown") {
135
+ const parts = version.split(".");
136
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
137
+ }
138
+ } else if (/SMART-TV|SmartTV/i.test(ua)) {
139
+ name = "Smart TV";
140
+ isSmartTV = true;
141
+ } else if (/NetCast/i.test(ua)) {
142
+ name = "LG NetCast";
143
+ isSmartTV = true;
144
+ isLegacyTV = true;
145
+ } else if (/BRAVIA/i.test(ua)) {
146
+ name = "Sony BRAVIA";
147
+ isSmartTV = true;
148
+ }
149
+ const chromeVersion = getChromeVersion(ua);
150
+ const webkitVersion = getWebKitVersion(ua);
151
+ if (chromeVersion > 0) {
152
+ if (!isSmartTV) {
153
+ name = "Chrome";
154
+ version = chromeVersion.toString();
155
+ majorVersion = chromeVersion;
156
+ }
157
+ if (chromeVersion < 50) {
158
+ supportsIMA = false;
159
+ supportsModernJS = false;
160
+ isLegacyTV = true;
161
+ recommendedAdPlayer = "hls";
162
+ }
163
+ }
164
+ if (webkitVersion > 0 && webkitVersion < 600) {
165
+ supportsModernJS = false;
166
+ if (isSmartTV) {
167
+ isLegacyTV = true;
168
+ supportsIMA = false;
169
+ recommendedAdPlayer = "hls";
170
+ }
171
+ }
172
+ if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
173
+ supportsModernJS = false;
174
+ supportsIMA = false;
175
+ recommendedAdPlayer = "hls";
176
+ }
177
+ if (typeof URLSearchParams === "undefined") {
178
+ supportsModernJS = false;
179
+ }
180
+ return {
181
+ name,
182
+ version,
183
+ majorVersion,
184
+ isSmartTV,
185
+ isLegacyTV,
186
+ platform,
187
+ supportsIMA,
188
+ supportsModernJS,
189
+ recommendedAdPlayer
190
+ };
191
+ }
192
+ function supportsGoogleIMA() {
193
+ const browser = detectBrowser();
194
+ if (browser.isLegacyTV) {
195
+ return false;
196
+ }
197
+ if (typeof document === "undefined" || typeof document.createElement !== "function") {
198
+ return false;
199
+ }
200
+ try {
201
+ const video = document.createElement("video");
202
+ if (!video) {
203
+ return false;
204
+ }
205
+ } catch (e) {
206
+ return false;
207
+ }
208
+ if (typeof Promise === "undefined") {
209
+ return false;
210
+ }
211
+ return browser.supportsIMA;
212
+ }
213
+ function logBrowserInfo(debug = false) {
214
+ if (!debug) return;
215
+ const browser = detectBrowser();
216
+ const imaSupport = supportsGoogleIMA();
217
+ console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
218
+ browser: `${browser.name} ${browser.version}`,
219
+ platform: browser.platform,
220
+ isSmartTV: browser.isSmartTV,
221
+ isLegacyTV: browser.isLegacyTV,
222
+ supportsIMA: imaSupport,
223
+ supportsModernJS: browser.supportsModernJS,
224
+ recommendedAdPlayer: browser.recommendedAdPlayer,
225
+ userAgent: navigator.userAgent
226
+ });
227
+ }
228
+ function getBrowserConfigOverrides() {
229
+ const browser = detectBrowser();
230
+ const overrides = {};
231
+ if (browser.isLegacyTV || !browser.supportsIMA) {
232
+ overrides.adPlayerType = "hls";
233
+ }
234
+ if (browser.isSmartTV) {
235
+ overrides.allowNativeHls = true;
236
+ }
237
+ return overrides;
238
+ }
79
239
 
80
240
  // src/sdk/ima.ts
81
241
  function createImaController(video, options) {
@@ -93,9 +253,18 @@ function createImaController(video, options) {
93
253
  }
94
254
  }
95
255
  function ensureImaLoaded() {
256
+ var _a, _b, _c;
257
+ if (!supportsGoogleIMA()) {
258
+ console.warn(
259
+ "[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
260
+ );
261
+ return Promise.reject(
262
+ new Error("Google IMA SDK not supported on this browser")
263
+ );
264
+ }
96
265
  try {
97
266
  const frameEl = window.frameElement;
98
- const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
267
+ const sandboxAttr = ((_a = frameEl == null ? void 0 : frameEl.getAttribute) == null ? void 0 : _a.call(frameEl, "sandbox")) || "";
99
268
  if (sandboxAttr) {
100
269
  const tokens = new Set(
101
270
  sandboxAttr.split(/\s+/).map((t) => t.trim()).filter((t) => t.length > 0)
@@ -109,13 +278,13 @@ function createImaController(video, options) {
109
278
  }
110
279
  } catch {
111
280
  }
112
- if (typeof window !== "undefined" && window.google?.ima)
281
+ if (typeof window !== "undefined" && ((_b = window.google) == null ? void 0 : _b.ima))
113
282
  return Promise.resolve();
114
283
  const existing = document.querySelector(
115
284
  'script[data-ima="true"]'
116
285
  );
117
286
  if (existing) {
118
- if (window.google?.ima) {
287
+ if ((_c = window.google) == null ? void 0 : _c.ima) {
119
288
  return Promise.resolve();
120
289
  }
121
290
  return new Promise((resolve, reject) => {
@@ -173,6 +342,7 @@ function createImaController(video, options) {
173
342
  return {
174
343
  initialize() {
175
344
  ensureImaLoaded().then(() => {
345
+ var _a, _b;
176
346
  const google = window.google;
177
347
  if (!adDisplayContainer) {
178
348
  const container = document.createElement("div");
@@ -186,14 +356,14 @@ function createImaController(video, options) {
186
356
  container.style.justifyContent = "center";
187
357
  container.style.pointerEvents = "none";
188
358
  container.style.zIndex = "2";
189
- video.parentElement?.appendChild(container);
359
+ (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
190
360
  adContainerEl = container;
191
361
  adDisplayContainer = new google.ima.AdDisplayContainer(
192
362
  container,
193
363
  video
194
364
  );
195
365
  try {
196
- adDisplayContainer.initialize?.();
366
+ (_b = adDisplayContainer.initialize) == null ? void 0 : _b.call(adDisplayContainer);
197
367
  } catch {
198
368
  }
199
369
  }
@@ -278,6 +448,7 @@ function createImaController(video, options) {
278
448
  adsManager.addEventListener(
279
449
  AdErrorEvent.AD_ERROR,
280
450
  (errorEvent) => {
451
+ var _a;
281
452
  console.error("[IMA] Ad error:", errorEvent.getError());
282
453
  destroyAdsManager();
283
454
  adPlaying = false;
@@ -308,10 +479,10 @@ function createImaController(video, options) {
308
479
  "[IMA] Max retries reached, emitting ad_error"
309
480
  );
310
481
  emit("ad_error");
311
- if (!options?.continueLiveStreamDuringAds) {
482
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
312
483
  if (video.paused) {
313
484
  console.log("[IMA] Resuming paused video after ad error");
314
- video.play()?.catch(() => {
485
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
315
486
  });
316
487
  }
317
488
  }
@@ -322,7 +493,7 @@ function createImaController(video, options) {
322
493
  AdEvent.CONTENT_PAUSE_REQUESTED,
323
494
  () => {
324
495
  console.log("[IMA] Content pause requested");
325
- if (!options?.continueLiveStreamDuringAds) {
496
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
326
497
  video.pause();
327
498
  console.log("[IMA] Video paused (VOD mode)");
328
499
  } else {
@@ -348,6 +519,7 @@ function createImaController(video, options) {
348
519
  adsManager.addEventListener(
349
520
  AdEvent.CONTENT_RESUME_REQUESTED,
350
521
  () => {
522
+ var _a;
351
523
  console.log("[IMA] Content resume requested");
352
524
  adPlaying = false;
353
525
  video.muted = originalMutedState;
@@ -358,8 +530,8 @@ function createImaController(video, options) {
358
530
  "[IMA] Ad container hidden - pointer events disabled"
359
531
  );
360
532
  }
361
- if (!options?.continueLiveStreamDuringAds) {
362
- video.play()?.catch(() => {
533
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
534
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
363
535
  });
364
536
  console.log("[IMA] Video resumed (VOD mode)");
365
537
  } else {
@@ -381,7 +553,7 @@ function createImaController(video, options) {
381
553
  "[IMA] Ad container hidden after all ads completed"
382
554
  );
383
555
  }
384
- if (!options?.continueLiveStreamDuringAds) {
556
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
385
557
  video.play().catch(() => {
386
558
  });
387
559
  console.log(
@@ -409,7 +581,7 @@ function createImaController(video, options) {
409
581
  adContainerEl.style.display = "none";
410
582
  console.log("[IMA] Ad container hidden after setup error");
411
583
  }
412
- if (!options?.continueLiveStreamDuringAds) {
584
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
413
585
  if (video.paused) {
414
586
  console.log("[IMA] Resuming paused video after setup error");
415
587
  video.play().catch(() => {
@@ -437,7 +609,7 @@ function createImaController(video, options) {
437
609
  adContainerEl.style.display = "none";
438
610
  console.log("[IMA] Ad container hidden after loader error");
439
611
  }
440
- if (!options?.continueLiveStreamDuringAds) {
612
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
441
613
  if (video.paused) {
442
614
  console.log("[IMA] Resuming paused video after loader error");
443
615
  video.play().catch(() => {
@@ -459,14 +631,15 @@ function createImaController(video, options) {
459
631
  return adsLoadedPromise;
460
632
  } catch (error) {
461
633
  console.error("[IMA] Failed to request ads:", error);
462
- currentReject?.(error);
634
+ currentReject == null ? void 0 : currentReject(error);
463
635
  adsLoadedReject = void 0;
464
636
  adsLoadedResolve = void 0;
465
637
  return Promise.reject(error);
466
638
  }
467
639
  },
468
640
  async play() {
469
- if (!window.google?.ima || !adDisplayContainer) {
641
+ var _a, _b;
642
+ if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
470
643
  console.warn(
471
644
  "[IMA] Cannot play ad: IMA SDK or ad container not available"
472
645
  );
@@ -488,14 +661,15 @@ function createImaController(video, options) {
488
661
  } catch (error) {
489
662
  console.error("[IMA] Error starting ad playback:", error);
490
663
  adPlaying = false;
491
- if (!options?.continueLiveStreamDuringAds) {
492
- video.play()?.catch(() => {
664
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
665
+ (_b = video.play()) == null ? void 0 : _b.catch(() => {
493
666
  });
494
667
  }
495
668
  return Promise.reject(error);
496
669
  }
497
670
  },
498
671
  async stop() {
672
+ var _a;
499
673
  adPlaying = false;
500
674
  video.muted = originalMutedState;
501
675
  if (adContainerEl) {
@@ -504,11 +678,11 @@ function createImaController(video, options) {
504
678
  console.log("[IMA] Ad container hidden after stop");
505
679
  }
506
680
  try {
507
- adsManager?.stop?.();
681
+ (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
508
682
  } catch {
509
683
  }
510
684
  destroyAdsManager();
511
- if (!options?.continueLiveStreamDuringAds) {
685
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
512
686
  video.play().catch(() => {
513
687
  });
514
688
  console.log("[IMA] Video resumed after stop (VOD mode)");
@@ -517,6 +691,7 @@ function createImaController(video, options) {
517
691
  }
518
692
  },
519
693
  destroy() {
694
+ var _a;
520
695
  destroyAdsManager();
521
696
  adPlaying = false;
522
697
  video.muted = originalMutedState;
@@ -525,10 +700,10 @@ function createImaController(video, options) {
525
700
  adContainerEl.style.display = "none";
526
701
  }
527
702
  try {
528
- adsLoader?.destroy?.();
703
+ (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
529
704
  } catch {
530
705
  }
531
- if (adContainerEl?.parentElement) {
706
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
532
707
  adContainerEl.parentElement.removeChild(adContainerEl);
533
708
  }
534
709
  adContainerEl = void 0;
@@ -539,7 +714,8 @@ function createImaController(video, options) {
539
714
  return adPlaying;
540
715
  },
541
716
  resize(width, height) {
542
- if (!adsManager || !window.google?.ima) {
717
+ var _a;
718
+ if (!adsManager || !((_a = window.google) == null ? void 0 : _a.ima)) {
543
719
  console.warn(
544
720
  "[IMA] Cannot resize: No ads manager or IMA SDK available"
545
721
  );
@@ -557,7 +733,8 @@ function createImaController(video, options) {
557
733
  listeners.get(event).add(listener);
558
734
  },
559
735
  off(event, listener) {
560
- listeners.get(event)?.delete(listener);
736
+ var _a;
737
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
561
738
  },
562
739
  updateOriginalMutedState(muted) {
563
740
  originalMutedState = muted;
@@ -588,9 +765,533 @@ function createImaController(video, options) {
588
765
  };
589
766
  }
590
767
 
768
+ // src/sdk/hlsAdPlayer.ts
769
+ var import_hls = __toESM(require("hls.js"), 1);
770
+ function createHlsAdPlayer(contentVideo, options) {
771
+ let adPlaying = false;
772
+ let originalMutedState = false;
773
+ const listeners = /* @__PURE__ */ new Map();
774
+ const licenseKey = options == null ? void 0 : options.licenseKey;
775
+ const mainHlsInstance = options == null ? void 0 : options.mainHlsInstance;
776
+ let adVideoElement;
777
+ let adHls;
778
+ let adContainerEl;
779
+ let currentAd;
780
+ let sessionId;
781
+ let trackingFired = {
782
+ impression: false,
783
+ start: false,
784
+ firstQuartile: false,
785
+ midpoint: false,
786
+ thirdQuartile: false,
787
+ complete: false
788
+ };
789
+ function emit(event, payload) {
790
+ const set = listeners.get(event);
791
+ if (!set) return;
792
+ for (const fn of Array.from(set)) {
793
+ try {
794
+ fn(payload);
795
+ } catch (error) {
796
+ console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
797
+ }
798
+ }
799
+ }
800
+ function generateSessionId() {
801
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
802
+ }
803
+ function fireTrackingPixels(urls) {
804
+ if (!urls || urls.length === 0) return;
805
+ urls.forEach((url) => {
806
+ try {
807
+ let trackingUrl = url;
808
+ if (sessionId) {
809
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
810
+ }
811
+ if (licenseKey) {
812
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
813
+ }
814
+ const img = new Image(1, 1);
815
+ img.src = trackingUrl;
816
+ console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
817
+ } catch (error) {
818
+ console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
819
+ }
820
+ });
821
+ }
822
+ function getMainStreamQuality() {
823
+ if (!mainHlsInstance || !mainHlsInstance.levels) {
824
+ return null;
825
+ }
826
+ const currentLevel = mainHlsInstance.currentLevel;
827
+ if (currentLevel === -1 || !mainHlsInstance.levels[currentLevel]) {
828
+ const autoLevel = mainHlsInstance.loadLevel;
829
+ if (autoLevel !== -1 && mainHlsInstance.levels[autoLevel]) {
830
+ const level2 = mainHlsInstance.levels[autoLevel];
831
+ return {
832
+ width: level2.width || 1920,
833
+ height: level2.height || 1080,
834
+ bitrate: level2.bitrate || 5e6
835
+ };
836
+ }
837
+ return null;
838
+ }
839
+ const level = mainHlsInstance.levels[currentLevel];
840
+ return {
841
+ width: level.width || 1920,
842
+ height: level.height || 1080,
843
+ bitrate: level.bitrate || 5e6
844
+ };
845
+ }
846
+ function selectBestMediaFile(mediaFiles) {
847
+ if (mediaFiles.length === 0) {
848
+ throw new Error("No media files available");
849
+ }
850
+ const firstFile = mediaFiles[0];
851
+ if (!firstFile) {
852
+ throw new Error("No media files available");
853
+ }
854
+ if (mediaFiles.length === 1) {
855
+ return firstFile;
856
+ }
857
+ const mainQuality = getMainStreamQuality();
858
+ if (!mainQuality) {
859
+ console.log("[HlsAdPlayer] No main stream quality info, using first media file");
860
+ return firstFile;
861
+ }
862
+ console.log("[HlsAdPlayer] Main stream quality:", mainQuality);
863
+ const scoredFiles = mediaFiles.map((file) => {
864
+ const widthDiff = Math.abs(file.width - mainQuality.width);
865
+ const heightDiff = Math.abs(file.height - mainQuality.height);
866
+ const resolutionDiff = widthDiff + heightDiff;
867
+ const fileBitrate = (file.bitrate || 5e3) * 1e3;
868
+ const bitrateDiff = Math.abs(fileBitrate - mainQuality.bitrate);
869
+ const score = resolutionDiff * 2 + bitrateDiff / 1e3;
870
+ return { file, score, resolutionDiff, bitrateDiff };
871
+ });
872
+ scoredFiles.sort((a, b) => a.score - b.score);
873
+ const bestMatch = scoredFiles[0];
874
+ if (!bestMatch) {
875
+ console.log("[HlsAdPlayer] No best match found, using first media file");
876
+ return firstFile;
877
+ }
878
+ console.log("[HlsAdPlayer] Selected media file:", {
879
+ url: bestMatch.file.url,
880
+ resolution: `${bestMatch.file.width}x${bestMatch.file.height}`,
881
+ bitrate: bestMatch.file.bitrate,
882
+ score: bestMatch.score,
883
+ resolutionDiff: bestMatch.resolutionDiff,
884
+ bitrateDiff: bestMatch.bitrateDiff
885
+ });
886
+ return bestMatch.file;
887
+ }
888
+ function parseVastXml(xmlString) {
889
+ var _a, _b, _c, _d;
890
+ try {
891
+ const parser = new DOMParser();
892
+ const xmlDoc = parser.parseFromString(xmlString, "text/xml");
893
+ const parserError = xmlDoc.querySelector("parsererror");
894
+ if (parserError) {
895
+ console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
896
+ return null;
897
+ }
898
+ const adElement = xmlDoc.querySelector("Ad");
899
+ if (!adElement) {
900
+ console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
901
+ return null;
902
+ }
903
+ const adId = adElement.getAttribute("id") || "unknown";
904
+ const title = ((_a = xmlDoc.querySelector("AdTitle")) == null ? void 0 : _a.textContent) || "Ad";
905
+ const durationText = ((_b = xmlDoc.querySelector("Duration")) == null ? void 0 : _b.textContent) || "00:00:30";
906
+ const durationParts = durationText.split(":");
907
+ const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
908
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
909
+ const mediaFiles = [];
910
+ mediaFileElements.forEach((mf) => {
911
+ var _a2;
912
+ const type = mf.getAttribute("type") || "";
913
+ if (type === "application/x-mpegURL" || type.includes("m3u8")) {
914
+ const bitrateAttr = mf.getAttribute("bitrate");
915
+ const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
916
+ mediaFiles.push({
917
+ url: ((_a2 = mf.textContent) == null ? void 0 : _a2.trim()) || "",
918
+ type,
919
+ width: parseInt(mf.getAttribute("width") || "1920", 10),
920
+ height: parseInt(mf.getAttribute("height") || "1080", 10),
921
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
922
+ });
923
+ }
924
+ });
925
+ if (mediaFiles.length === 0) {
926
+ console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
927
+ return null;
928
+ }
929
+ const trackingUrls = {
930
+ impression: [],
931
+ start: [],
932
+ firstQuartile: [],
933
+ midpoint: [],
934
+ thirdQuartile: [],
935
+ complete: [],
936
+ mute: [],
937
+ unmute: [],
938
+ pause: [],
939
+ resume: [],
940
+ fullscreen: [],
941
+ exitFullscreen: [],
942
+ skip: [],
943
+ error: []
944
+ };
945
+ xmlDoc.querySelectorAll("Impression").forEach((el) => {
946
+ var _a2;
947
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
948
+ if (url) trackingUrls.impression.push(url);
949
+ });
950
+ xmlDoc.querySelectorAll("Tracking").forEach((el) => {
951
+ var _a2;
952
+ const event = el.getAttribute("event");
953
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
954
+ if (event && url) {
955
+ const eventKey = event;
956
+ if (trackingUrls[eventKey]) {
957
+ trackingUrls[eventKey].push(url);
958
+ }
959
+ }
960
+ });
961
+ const clickThrough = (_d = (_c = xmlDoc.querySelector("ClickThrough")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim();
962
+ return {
963
+ id: adId,
964
+ title,
965
+ duration,
966
+ mediaFiles,
967
+ trackingUrls,
968
+ clickThrough
969
+ };
970
+ } catch (error) {
971
+ console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
972
+ return null;
973
+ }
974
+ }
975
+ function createAdVideoElement() {
976
+ const video = document.createElement("video");
977
+ video.style.position = "absolute";
978
+ video.style.left = "0";
979
+ video.style.top = "0";
980
+ video.style.width = "100%";
981
+ video.style.height = "100%";
982
+ video.style.objectFit = "contain";
983
+ video.style.backgroundColor = "#000";
984
+ video.playsInline = true;
985
+ video.muted = false;
986
+ return video;
987
+ }
988
+ function setupAdEventListeners() {
989
+ if (!adVideoElement || !currentAd) return;
990
+ adVideoElement.addEventListener("timeupdate", () => {
991
+ if (!currentAd || !adVideoElement) return;
992
+ const progress = adVideoElement.currentTime / currentAd.duration;
993
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
994
+ trackingFired.firstQuartile = true;
995
+ fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
996
+ }
997
+ if (progress >= 0.5 && !trackingFired.midpoint) {
998
+ trackingFired.midpoint = true;
999
+ fireTrackingPixels(currentAd.trackingUrls.midpoint);
1000
+ }
1001
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1002
+ trackingFired.thirdQuartile = true;
1003
+ fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1004
+ }
1005
+ });
1006
+ adVideoElement.addEventListener("playing", () => {
1007
+ if (!currentAd || trackingFired.start) return;
1008
+ trackingFired.start = true;
1009
+ fireTrackingPixels(currentAd.trackingUrls.start);
1010
+ console.log("[HlsAdPlayer] Ad started playing");
1011
+ });
1012
+ adVideoElement.addEventListener("ended", () => {
1013
+ if (!currentAd || trackingFired.complete) return;
1014
+ trackingFired.complete = true;
1015
+ fireTrackingPixels(currentAd.trackingUrls.complete);
1016
+ console.log("[HlsAdPlayer] Ad completed");
1017
+ handleAdComplete();
1018
+ });
1019
+ adVideoElement.addEventListener("error", (e) => {
1020
+ console.error("[HlsAdPlayer] Ad video error:", e);
1021
+ if (currentAd) {
1022
+ fireTrackingPixels(currentAd.trackingUrls.error);
1023
+ }
1024
+ handleAdError();
1025
+ });
1026
+ adVideoElement.addEventListener("volumechange", () => {
1027
+ if (!currentAd) return;
1028
+ if (adVideoElement.muted) {
1029
+ fireTrackingPixels(currentAd.trackingUrls.mute);
1030
+ } else {
1031
+ fireTrackingPixels(currentAd.trackingUrls.unmute);
1032
+ }
1033
+ });
1034
+ adVideoElement.addEventListener("pause", () => {
1035
+ if (currentAd && !adVideoElement.ended) {
1036
+ fireTrackingPixels(currentAd.trackingUrls.pause);
1037
+ }
1038
+ });
1039
+ adVideoElement.addEventListener("play", () => {
1040
+ if (currentAd && adVideoElement.currentTime > 0) {
1041
+ fireTrackingPixels(currentAd.trackingUrls.resume);
1042
+ }
1043
+ });
1044
+ }
1045
+ function handleAdComplete() {
1046
+ console.log("[HlsAdPlayer] Handling ad completion");
1047
+ adPlaying = false;
1048
+ contentVideo.muted = originalMutedState;
1049
+ if (adContainerEl) {
1050
+ adContainerEl.style.display = "none";
1051
+ adContainerEl.style.pointerEvents = "none";
1052
+ }
1053
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1054
+ contentVideo.play().catch(() => {
1055
+ });
1056
+ console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1057
+ } else {
1058
+ console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1059
+ }
1060
+ emit("content_resume");
1061
+ emit("all_ads_completed");
1062
+ }
1063
+ function handleAdError() {
1064
+ console.log("[HlsAdPlayer] Handling ad error");
1065
+ adPlaying = false;
1066
+ contentVideo.muted = originalMutedState;
1067
+ if (adContainerEl) {
1068
+ adContainerEl.style.display = "none";
1069
+ adContainerEl.style.pointerEvents = "none";
1070
+ }
1071
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1072
+ if (contentVideo.paused) {
1073
+ contentVideo.play().catch(() => {
1074
+ });
1075
+ }
1076
+ }
1077
+ emit("ad_error");
1078
+ }
1079
+ return {
1080
+ initialize() {
1081
+ var _a;
1082
+ console.log("[HlsAdPlayer] Initializing");
1083
+ if (!adContainerEl) {
1084
+ const container = document.createElement("div");
1085
+ container.style.position = "absolute";
1086
+ container.style.left = "0";
1087
+ container.style.top = "0";
1088
+ container.style.right = "0";
1089
+ container.style.bottom = "0";
1090
+ container.style.display = "none";
1091
+ container.style.alignItems = "center";
1092
+ container.style.justifyContent = "center";
1093
+ container.style.pointerEvents = "none";
1094
+ container.style.zIndex = "2";
1095
+ container.style.backgroundColor = "#000";
1096
+ (_a = contentVideo.parentElement) == null ? void 0 : _a.appendChild(container);
1097
+ adContainerEl = container;
1098
+ }
1099
+ },
1100
+ async requestAds(vastTagUrl) {
1101
+ console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
1102
+ if (adPlaying) {
1103
+ console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
1104
+ return Promise.reject(new Error("Ad already playing"));
1105
+ }
1106
+ try {
1107
+ sessionId = generateSessionId();
1108
+ const response = await fetch(vastTagUrl);
1109
+ if (!response.ok) {
1110
+ throw new Error(`Failed to fetch VAST: ${response.statusText}`);
1111
+ }
1112
+ const vastXml = await response.text();
1113
+ console.log("[HlsAdPlayer] VAST XML received");
1114
+ const ad = parseVastXml(vastXml);
1115
+ if (!ad) {
1116
+ throw new Error("Failed to parse VAST XML or no ads available");
1117
+ }
1118
+ currentAd = ad;
1119
+ console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
1120
+ fireTrackingPixels(ad.trackingUrls.impression);
1121
+ trackingFired.impression = true;
1122
+ return Promise.resolve();
1123
+ } catch (error) {
1124
+ console.error("[HlsAdPlayer] Error requesting ads:", error);
1125
+ emit("ad_error");
1126
+ return Promise.reject(error);
1127
+ }
1128
+ },
1129
+ async play() {
1130
+ if (!currentAd) {
1131
+ console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
1132
+ return Promise.reject(new Error("No ad loaded"));
1133
+ }
1134
+ console.log("[HlsAdPlayer] Starting ad playback");
1135
+ try {
1136
+ if (!adVideoElement) {
1137
+ adVideoElement = createAdVideoElement();
1138
+ adContainerEl == null ? void 0 : adContainerEl.appendChild(adVideoElement);
1139
+ setupAdEventListeners();
1140
+ }
1141
+ trackingFired = {
1142
+ impression: trackingFired.impression,
1143
+ start: false,
1144
+ firstQuartile: false,
1145
+ midpoint: false,
1146
+ thirdQuartile: false,
1147
+ complete: false
1148
+ };
1149
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1150
+ contentVideo.pause();
1151
+ console.log("[HlsAdPlayer] Content paused (VOD mode)");
1152
+ } else {
1153
+ console.log("[HlsAdPlayer] Content continues (Live mode)");
1154
+ }
1155
+ contentVideo.muted = true;
1156
+ adPlaying = true;
1157
+ if (adContainerEl) {
1158
+ adContainerEl.style.display = "flex";
1159
+ adContainerEl.style.pointerEvents = "auto";
1160
+ }
1161
+ emit("content_pause");
1162
+ const mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1163
+ if (!mediaFile) {
1164
+ throw new Error("No media file available for ad");
1165
+ }
1166
+ console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
1167
+ if (import_hls.default.isSupported()) {
1168
+ if (adHls) {
1169
+ adHls.destroy();
1170
+ }
1171
+ adHls = new import_hls.default({
1172
+ enableWorker: true,
1173
+ lowLatencyMode: false
1174
+ });
1175
+ adHls.loadSource(mediaFile.url);
1176
+ adHls.attachMedia(adVideoElement);
1177
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
1178
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1179
+ adVideoElement.play().catch((error) => {
1180
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1181
+ handleAdError();
1182
+ });
1183
+ });
1184
+ adHls.on(import_hls.default.Events.ERROR, (event, data) => {
1185
+ console.error("[HlsAdPlayer] HLS error:", data);
1186
+ if (data.fatal) {
1187
+ handleAdError();
1188
+ }
1189
+ });
1190
+ } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1191
+ adVideoElement.src = mediaFile.url;
1192
+ adVideoElement.play().catch((error) => {
1193
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1194
+ handleAdError();
1195
+ });
1196
+ } else {
1197
+ throw new Error("HLS not supported");
1198
+ }
1199
+ return Promise.resolve();
1200
+ } catch (error) {
1201
+ console.error("[HlsAdPlayer] Error playing ad:", error);
1202
+ handleAdError();
1203
+ return Promise.reject(error);
1204
+ }
1205
+ },
1206
+ async stop() {
1207
+ console.log("[HlsAdPlayer] Stopping ad");
1208
+ adPlaying = false;
1209
+ contentVideo.muted = originalMutedState;
1210
+ if (adContainerEl) {
1211
+ adContainerEl.style.display = "none";
1212
+ adContainerEl.style.pointerEvents = "none";
1213
+ }
1214
+ if (adHls) {
1215
+ adHls.destroy();
1216
+ adHls = void 0;
1217
+ }
1218
+ if (adVideoElement) {
1219
+ adVideoElement.pause();
1220
+ adVideoElement.src = "";
1221
+ }
1222
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1223
+ contentVideo.play().catch(() => {
1224
+ });
1225
+ }
1226
+ currentAd = void 0;
1227
+ },
1228
+ destroy() {
1229
+ console.log("[HlsAdPlayer] Destroying");
1230
+ adPlaying = false;
1231
+ contentVideo.muted = originalMutedState;
1232
+ if (adHls) {
1233
+ adHls.destroy();
1234
+ adHls = void 0;
1235
+ }
1236
+ if (adVideoElement) {
1237
+ adVideoElement.pause();
1238
+ adVideoElement.src = "";
1239
+ adVideoElement.remove();
1240
+ adVideoElement = void 0;
1241
+ }
1242
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
1243
+ adContainerEl.parentElement.removeChild(adContainerEl);
1244
+ }
1245
+ adContainerEl = void 0;
1246
+ currentAd = void 0;
1247
+ listeners.clear();
1248
+ },
1249
+ isAdPlaying() {
1250
+ return adPlaying;
1251
+ },
1252
+ resize(width, height) {
1253
+ console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
1254
+ if (adContainerEl) {
1255
+ adContainerEl.style.width = `${width}px`;
1256
+ adContainerEl.style.height = `${height}px`;
1257
+ }
1258
+ if (adVideoElement) {
1259
+ adVideoElement.style.width = `${width}px`;
1260
+ adVideoElement.style.height = `${height}px`;
1261
+ }
1262
+ },
1263
+ on(event, listener) {
1264
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
1265
+ listeners.get(event).add(listener);
1266
+ },
1267
+ off(event, listener) {
1268
+ var _a;
1269
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
1270
+ },
1271
+ updateOriginalMutedState(muted) {
1272
+ originalMutedState = muted;
1273
+ },
1274
+ getOriginalMutedState() {
1275
+ return originalMutedState;
1276
+ },
1277
+ setAdVolume(volume) {
1278
+ if (adVideoElement && adPlaying) {
1279
+ adVideoElement.volume = Math.max(0, Math.min(1, volume));
1280
+ }
1281
+ },
1282
+ getAdVolume() {
1283
+ if (adVideoElement && adPlaying) {
1284
+ return adVideoElement.volume;
1285
+ }
1286
+ return 1;
1287
+ }
1288
+ };
1289
+ }
1290
+
591
1291
  // src/utils/tracking.ts
592
1292
  var cachedBrowserId = null;
593
1293
  function getClientInfo() {
1294
+ var _a, _b, _c, _d;
594
1295
  const ua = navigator.userAgent;
595
1296
  const platform = navigator.platform;
596
1297
  const vendor = navigator.vendor || "";
@@ -598,12 +1299,12 @@ function getClientInfo() {
598
1299
  const memory = navigator.deviceMemory || null;
599
1300
  const hardwareConcurrency = navigator.hardwareConcurrency || 1;
600
1301
  const screenInfo = {
601
- width: screen?.width,
602
- height: screen?.height,
603
- availWidth: screen?.availWidth,
604
- availHeight: screen?.availHeight,
605
- orientation: screen?.orientation?.type || "",
606
- pixelDepth: screen?.pixelDepth
1302
+ width: screen == null ? void 0 : screen.width,
1303
+ height: screen == null ? void 0 : screen.height,
1304
+ availWidth: screen == null ? void 0 : screen.availWidth,
1305
+ availHeight: screen == null ? void 0 : screen.availHeight,
1306
+ orientation: ((_a = screen == null ? void 0 : screen.orientation) == null ? void 0 : _a.type) || "",
1307
+ pixelDepth: screen == null ? void 0 : screen.pixelDepth
607
1308
  };
608
1309
  let deviceType = "desktop";
609
1310
  let brand = "Unknown";
@@ -700,10 +1401,10 @@ function getClientInfo() {
700
1401
  if (vendor.includes("Samsung") || ua.includes("SM-")) brand = "Samsung";
701
1402
  }
702
1403
  isWebView = /wv|WebView|Linux; U;/.test(ua);
703
- if (window?.outerHeight === 0 && window?.outerWidth === 0) {
1404
+ if ((window == null ? void 0 : window.outerHeight) === 0 && (window == null ? void 0 : window.outerWidth) === 0) {
704
1405
  isWebView = true;
705
1406
  }
706
- isWebApp = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;
1407
+ 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;
707
1408
  return {
708
1409
  brand,
709
1410
  os,
@@ -724,7 +1425,7 @@ function getClientInfo() {
724
1425
  deviceMemory: memory,
725
1426
  maxTouchPoints,
726
1427
  language: navigator.language,
727
- languages: navigator.languages?.join(",") || "",
1428
+ languages: ((_d = navigator.languages) == null ? void 0 : _d.join(",")) || "",
728
1429
  cookieEnabled: navigator.cookieEnabled,
729
1430
  doNotTrack: navigator.doNotTrack || "",
730
1431
  referrer: document.referrer,
@@ -837,6 +1538,215 @@ async function sendHeartbeat(licenseKey) {
837
1538
  }
838
1539
  }
839
1540
 
1541
+ // src/utils/polyfills.ts
1542
+ function polyfillURLSearchParams() {
1543
+ if (typeof URLSearchParams !== "undefined") {
1544
+ return;
1545
+ }
1546
+ class URLSearchParamsPolyfill {
1547
+ constructor(init) {
1548
+ this.params = /* @__PURE__ */ new Map();
1549
+ if (typeof init === "string") {
1550
+ this.parseQueryString(init);
1551
+ } else if (init instanceof URLSearchParamsPolyfill) {
1552
+ init.forEach((value, key) => {
1553
+ this.append(key, value);
1554
+ });
1555
+ }
1556
+ }
1557
+ parseQueryString(query) {
1558
+ const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
1559
+ if (!cleanQuery) return;
1560
+ cleanQuery.split("&").forEach((param) => {
1561
+ const [key, value] = param.split("=");
1562
+ if (key) {
1563
+ const decodedKey = this.safeDecodeURIComponent(key);
1564
+ const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
1565
+ this.append(decodedKey, decodedValue);
1566
+ }
1567
+ });
1568
+ }
1569
+ safeDecodeURIComponent(str) {
1570
+ try {
1571
+ return decodeURIComponent(str.replace(/\+/g, " "));
1572
+ } catch (e) {
1573
+ return str;
1574
+ }
1575
+ }
1576
+ append(name, value) {
1577
+ const values = this.params.get(name) || [];
1578
+ values.push(String(value));
1579
+ this.params.set(name, values);
1580
+ }
1581
+ delete(name) {
1582
+ this.params.delete(name);
1583
+ }
1584
+ get(name) {
1585
+ const values = this.params.get(name);
1586
+ return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
1587
+ }
1588
+ getAll(name) {
1589
+ return this.params.get(name) || [];
1590
+ }
1591
+ has(name) {
1592
+ return this.params.has(name);
1593
+ }
1594
+ set(name, value) {
1595
+ this.params.set(name, [String(value)]);
1596
+ }
1597
+ forEach(callback) {
1598
+ this.params.forEach((values, key) => {
1599
+ values.forEach((value) => {
1600
+ callback(value, key, this);
1601
+ });
1602
+ });
1603
+ }
1604
+ toString() {
1605
+ const parts = [];
1606
+ this.params.forEach((values, key) => {
1607
+ values.forEach((value) => {
1608
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
1609
+ });
1610
+ });
1611
+ return parts.join("&");
1612
+ }
1613
+ }
1614
+ window.URLSearchParams = URLSearchParamsPolyfill;
1615
+ }
1616
+ function polyfillTextEncoder() {
1617
+ if (typeof TextEncoder !== "undefined") {
1618
+ return;
1619
+ }
1620
+ class TextEncoderPolyfill {
1621
+ constructor() {
1622
+ this.encoding = "utf-8";
1623
+ }
1624
+ encode(str) {
1625
+ const utf8 = [];
1626
+ for (let i = 0; i < str.length; i++) {
1627
+ let charcode = str.charCodeAt(i);
1628
+ if (charcode < 128) {
1629
+ utf8.push(charcode);
1630
+ } else if (charcode < 2048) {
1631
+ utf8.push(192 | charcode >> 6, 128 | charcode & 63);
1632
+ } else if (charcode < 55296 || charcode >= 57344) {
1633
+ utf8.push(
1634
+ 224 | charcode >> 12,
1635
+ 128 | charcode >> 6 & 63,
1636
+ 128 | charcode & 63
1637
+ );
1638
+ } else {
1639
+ i++;
1640
+ charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
1641
+ utf8.push(
1642
+ 240 | charcode >> 18,
1643
+ 128 | charcode >> 12 & 63,
1644
+ 128 | charcode >> 6 & 63,
1645
+ 128 | charcode & 63
1646
+ );
1647
+ }
1648
+ }
1649
+ return new Uint8Array(utf8);
1650
+ }
1651
+ }
1652
+ window.TextEncoder = TextEncoderPolyfill;
1653
+ }
1654
+ function polyfillPromiseFinally() {
1655
+ if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
1656
+ Promise.prototype.finally = function(callback) {
1657
+ const constructor = this.constructor;
1658
+ return this.then(
1659
+ (value) => constructor.resolve(callback()).then(() => value),
1660
+ (reason) => constructor.resolve(callback()).then(() => {
1661
+ throw reason;
1662
+ })
1663
+ );
1664
+ };
1665
+ }
1666
+ }
1667
+ function polyfillObjectAssign() {
1668
+ if (typeof Object.assign !== "function") {
1669
+ Object.assign = function(target, ...sources) {
1670
+ if (target == null) {
1671
+ throw new TypeError("Cannot convert undefined or null to object");
1672
+ }
1673
+ const to = Object(target);
1674
+ for (let i = 0; i < sources.length; i++) {
1675
+ const nextSource = sources[i];
1676
+ if (nextSource != null) {
1677
+ for (const nextKey in nextSource) {
1678
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
1679
+ to[nextKey] = nextSource[nextKey];
1680
+ }
1681
+ }
1682
+ }
1683
+ }
1684
+ return to;
1685
+ };
1686
+ }
1687
+ }
1688
+ function polyfillArrayFrom() {
1689
+ if (!Array.from) {
1690
+ Array.from = function(arrayLike, mapFn, thisArg) {
1691
+ const items = Object(arrayLike);
1692
+ if (arrayLike == null) {
1693
+ throw new TypeError("Array.from requires an array-like object");
1694
+ }
1695
+ const len = items.length >>> 0;
1696
+ const result = new Array(len);
1697
+ for (let i = 0; i < len; i++) {
1698
+ if (mapFn) {
1699
+ result[i] = mapFn.call(thisArg, items[i], i);
1700
+ } else {
1701
+ result[i] = items[i];
1702
+ }
1703
+ }
1704
+ return result;
1705
+ };
1706
+ }
1707
+ }
1708
+ function polyfillStringStartsWith() {
1709
+ if (!String.prototype.startsWith) {
1710
+ String.prototype.startsWith = function(search, pos) {
1711
+ pos = !pos || pos < 0 ? 0 : +pos;
1712
+ return this.substring(pos, pos + search.length) === search;
1713
+ };
1714
+ }
1715
+ }
1716
+ function polyfillStringEndsWith() {
1717
+ if (!String.prototype.endsWith) {
1718
+ String.prototype.endsWith = function(search, length) {
1719
+ if (length === void 0 || length > this.length) {
1720
+ length = this.length;
1721
+ }
1722
+ return this.substring(length - search.length, length) === search;
1723
+ };
1724
+ }
1725
+ }
1726
+ function polyfillStringIncludes() {
1727
+ if (!String.prototype.includes) {
1728
+ String.prototype.includes = function(search, start) {
1729
+ if (typeof start !== "number") {
1730
+ start = 0;
1731
+ }
1732
+ if (start + search.length > this.length) {
1733
+ return false;
1734
+ }
1735
+ return this.indexOf(search, start) !== -1;
1736
+ };
1737
+ }
1738
+ }
1739
+ function initializePolyfills() {
1740
+ polyfillObjectAssign();
1741
+ polyfillArrayFrom();
1742
+ polyfillStringStartsWith();
1743
+ polyfillStringEndsWith();
1744
+ polyfillStringIncludes();
1745
+ polyfillURLSearchParams();
1746
+ polyfillTextEncoder();
1747
+ polyfillPromiseFinally();
1748
+ }
1749
+
840
1750
  // src/player/StormcloudVideoPlayer.ts
841
1751
  var StormcloudVideoPlayer = class {
842
1752
  constructor(config) {
@@ -849,13 +1759,44 @@ var StormcloudVideoPlayer = class {
849
1759
  this.totalAdsInBreak = 0;
850
1760
  this.showAds = false;
851
1761
  this.isLiveStream = false;
852
- this.config = config;
1762
+ initializePolyfills();
1763
+ const browserOverrides = getBrowserConfigOverrides();
1764
+ this.config = { ...config, ...browserOverrides };
853
1765
  this.video = config.videoElement;
854
- this.ima = createImaController(this.video, {
855
- continueLiveStreamDuringAds: false
856
- });
1766
+ logBrowserInfo(config.debugAdTiming);
1767
+ this.ima = this.createAdPlayer(false);
1768
+ }
1769
+ createAdPlayer(continueLiveStreamDuringAds) {
1770
+ const vastMode = this.config.vastMode || "default";
1771
+ let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
1772
+ if (adPlayerType === "ima" && !supportsGoogleIMA()) {
1773
+ if (this.config.debugAdTiming) {
1774
+ console.warn(
1775
+ "[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
1776
+ );
1777
+ }
1778
+ adPlayerType = "hls";
1779
+ }
1780
+ if (adPlayerType === "hls") {
1781
+ if (this.config.debugAdTiming) {
1782
+ console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
1783
+ }
1784
+ return createHlsAdPlayer(this.video, {
1785
+ continueLiveStreamDuringAds,
1786
+ ...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {},
1787
+ ...this.hls ? { mainHlsInstance: this.hls } : {}
1788
+ });
1789
+ } else {
1790
+ if (this.config.debugAdTiming) {
1791
+ console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
1792
+ }
1793
+ return createImaController(this.video, {
1794
+ continueLiveStreamDuringAds
1795
+ });
1796
+ }
857
1797
  }
858
1798
  async load() {
1799
+ var _a, _b;
859
1800
  if (!this.attached) {
860
1801
  this.attach();
861
1802
  }
@@ -872,7 +1813,7 @@ var StormcloudVideoPlayer = class {
872
1813
  this.initializeTracking();
873
1814
  if (this.shouldUseNativeHls()) {
874
1815
  this.video.src = this.config.src;
875
- this.isLiveStream = this.config.lowLatencyMode ?? false;
1816
+ this.isLiveStream = (_a = this.config.lowLatencyMode) != null ? _a : false;
876
1817
  if (this.config.debugAdTiming) {
877
1818
  console.log(
878
1819
  "[StormcloudVideoPlayer] allowNativeHls: true - VOD mode detected:",
@@ -884,17 +1825,15 @@ var StormcloudVideoPlayer = class {
884
1825
  );
885
1826
  }
886
1827
  this.ima.destroy();
887
- this.ima = createImaController(this.video, {
888
- continueLiveStreamDuringAds: false
889
- });
1828
+ this.ima = this.createAdPlayer(false);
890
1829
  this.ima.initialize();
891
1830
  if (this.config.autoplay) {
892
- await this.video.play()?.catch(() => {
893
- });
1831
+ await ((_b = this.video.play()) == null ? void 0 : _b.catch(() => {
1832
+ }));
894
1833
  }
895
1834
  return;
896
1835
  }
897
- this.hls = new import_hls.default({
1836
+ this.hls = new import_hls2.default({
898
1837
  enableWorker: true,
899
1838
  backBufferLength: 30,
900
1839
  liveDurationInfinity: true,
@@ -902,13 +1841,18 @@ var StormcloudVideoPlayer = class {
902
1841
  maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
903
1842
  ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
904
1843
  });
905
- this.hls.on(import_hls.default.Events.MEDIA_ATTACHED, () => {
906
- this.hls?.loadSource(this.config.src);
1844
+ this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
1845
+ var _a2;
1846
+ (_a2 = this.hls) == null ? void 0 : _a2.loadSource(this.config.src);
907
1847
  });
908
- this.hls.on(import_hls.default.Events.MANIFEST_PARSED, async (_, data) => {
909
- this.isLiveStream = this.hls?.levels?.some(
910
- (level) => level?.details?.live === true || level?.details?.type === "LIVE"
911
- ) ?? false;
1848
+ this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
1849
+ var _a2, _b2, _c, _d;
1850
+ this.isLiveStream = (_c = (_b2 = (_a2 = this.hls) == null ? void 0 : _a2.levels) == null ? void 0 : _b2.some(
1851
+ (level) => {
1852
+ var _a3, _b3;
1853
+ 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";
1854
+ }
1855
+ )) != null ? _c : false;
912
1856
  if (this.config.debugAdTiming) {
913
1857
  const adBehavior = this.shouldContinueLiveStreamDuringAds() ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
914
1858
  console.log("[StormcloudVideoPlayer] Stream type detected:", {
@@ -918,33 +1862,32 @@ var StormcloudVideoPlayer = class {
918
1862
  });
919
1863
  }
920
1864
  this.ima.destroy();
921
- this.ima = createImaController(this.video, {
922
- continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
923
- });
1865
+ this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
924
1866
  this.ima.initialize();
925
1867
  if (this.config.autoplay) {
926
- await this.video.play()?.catch(() => {
927
- });
1868
+ await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
1869
+ }));
928
1870
  }
929
1871
  });
930
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
931
- const id3Tags = (data?.samples || []).map((s) => ({
1872
+ this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
1873
+ const id3Tags = ((data == null ? void 0 : data.samples) || []).map((s) => ({
932
1874
  key: "ID3",
933
- value: s?.data,
934
- ptsSeconds: s?.pts
1875
+ value: s == null ? void 0 : s.data,
1876
+ ptsSeconds: s == null ? void 0 : s.pts
935
1877
  }));
936
1878
  id3Tags.forEach((tag) => this.onId3Tag(tag));
937
1879
  });
938
- this.hls.on(import_hls.default.Events.FRAG_CHANGED, (_evt, data) => {
939
- const frag = data?.frag;
940
- const tagList = frag?.tagList;
1880
+ this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
1881
+ var _a2, _b2, _c;
1882
+ const frag = data == null ? void 0 : data.frag;
1883
+ const tagList = frag == null ? void 0 : frag.tagList;
941
1884
  if (!Array.isArray(tagList)) return;
942
1885
  for (const entry of tagList) {
943
1886
  let tag = "";
944
1887
  let value = "";
945
1888
  if (Array.isArray(entry)) {
946
- tag = String(entry[0] ?? "");
947
- value = String(entry[1] ?? "");
1889
+ tag = String((_a2 = entry[0]) != null ? _a2 : "");
1890
+ value = String((_b2 = entry[1]) != null ? _b2 : "");
948
1891
  } else if (typeof entry === "string") {
949
1892
  const idx = entry.indexOf(":");
950
1893
  if (idx >= 0) {
@@ -968,8 +1911,8 @@ var StormcloudVideoPlayer = class {
968
1911
  const prog = this.parseCueOutCont(value);
969
1912
  const marker = {
970
1913
  type: "progress",
971
- ...prog?.duration !== void 0 ? { durationSeconds: prog.duration } : {},
972
- ...prog?.elapsed !== void 0 ? { ptsSeconds: prog.elapsed } : {},
1914
+ ...(prog == null ? void 0 : prog.duration) !== void 0 ? { durationSeconds: prog.duration } : {},
1915
+ ...(prog == null ? void 0 : prog.elapsed) !== void 0 ? { ptsSeconds: prog.elapsed } : {},
973
1916
  raw: { tag, value }
974
1917
  };
975
1918
  this.onScte35Marker(marker);
@@ -979,7 +1922,7 @@ var StormcloudVideoPlayer = class {
979
1922
  const attrs = this.parseAttributeList(value);
980
1923
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
981
1924
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
982
- const klass = String(attrs["CLASS"] ?? "");
1925
+ const klass = String((_c = attrs["CLASS"]) != null ? _c : "");
983
1926
  const duration = this.toNumber(attrs["DURATION"]);
984
1927
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
985
1928
  const marker = {
@@ -995,14 +1938,15 @@ var StormcloudVideoPlayer = class {
995
1938
  }
996
1939
  }
997
1940
  });
998
- this.hls.on(import_hls.default.Events.ERROR, (_evt, data) => {
999
- if (data?.fatal) {
1941
+ this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
1942
+ var _a2, _b2;
1943
+ if (data == null ? void 0 : data.fatal) {
1000
1944
  switch (data.type) {
1001
- case import_hls.default.ErrorTypes.NETWORK_ERROR:
1002
- this.hls?.startLoad();
1945
+ case import_hls2.default.ErrorTypes.NETWORK_ERROR:
1946
+ (_a2 = this.hls) == null ? void 0 : _a2.startLoad();
1003
1947
  break;
1004
- case import_hls.default.ErrorTypes.MEDIA_ERROR:
1005
- this.hls?.recoverMediaError();
1948
+ case import_hls2.default.ErrorTypes.MEDIA_ERROR:
1949
+ (_b2 = this.hls) == null ? void 0 : _b2.recoverMediaError();
1006
1950
  break;
1007
1951
  default:
1008
1952
  this.destroy();
@@ -1104,11 +2048,12 @@ var StormcloudVideoPlayer = class {
1104
2048
  }
1105
2049
  }
1106
2050
  parseScte35FromId3(tag) {
2051
+ var _a, _b, _c, _d;
1107
2052
  const text = this.decodeId3ValueToText(tag.value);
1108
2053
  if (!text) return void 0;
1109
2054
  const cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
1110
2055
  if (cueOutMatch) {
1111
- const arg = (cueOutMatch[1] ?? "").trim();
2056
+ const arg = ((_a = cueOutMatch[1]) != null ? _a : "").trim();
1112
2057
  const dur = this.parseCueOutDuration(arg);
1113
2058
  const marker = {
1114
2059
  type: "start",
@@ -1120,12 +2065,12 @@ var StormcloudVideoPlayer = class {
1120
2065
  }
1121
2066
  const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
1122
2067
  if (cueOutContMatch) {
1123
- const arg = (cueOutContMatch[1] ?? "").trim();
2068
+ const arg = ((_b = cueOutContMatch[1]) != null ? _b : "").trim();
1124
2069
  const cont = this.parseCueOutCont(arg);
1125
2070
  const marker = {
1126
2071
  type: "progress",
1127
2072
  ...tag.ptsSeconds !== void 0 ? { ptsSeconds: tag.ptsSeconds } : {},
1128
- ...cont?.duration !== void 0 ? { durationSeconds: cont.duration } : {},
2073
+ ...(cont == null ? void 0 : cont.duration) !== void 0 ? { durationSeconds: cont.duration } : {},
1129
2074
  raw: { id3: text }
1130
2075
  };
1131
2076
  return marker;
@@ -1141,10 +2086,10 @@ var StormcloudVideoPlayer = class {
1141
2086
  }
1142
2087
  const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
1143
2088
  if (daterangeMatch) {
1144
- const attrs = this.parseAttributeList(daterangeMatch[1] ?? "");
2089
+ const attrs = this.parseAttributeList((_c = daterangeMatch[1]) != null ? _c : "");
1145
2090
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
1146
2091
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
1147
- const klass = String(attrs["CLASS"] ?? "");
2092
+ const klass = String((_d = attrs["CLASS"]) != null ? _d : "");
1148
2093
  const duration = this.toNumber(attrs["DURATION"]);
1149
2094
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
1150
2095
  const marker = {
@@ -1201,6 +2146,7 @@ var StormcloudVideoPlayer = class {
1201
2146
  }
1202
2147
  }
1203
2148
  onScte35Marker(marker) {
2149
+ var _a, _b;
1204
2150
  if (this.config.debugAdTiming) {
1205
2151
  console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
1206
2152
  type: marker.type,
@@ -1216,7 +2162,7 @@ var StormcloudVideoPlayer = class {
1216
2162
  this.expectedAdBreakDurationMs = durationMs;
1217
2163
  this.currentAdBreakStartWallClockMs = Date.now();
1218
2164
  const isManifestMarker = this.isManifestBasedMarker(marker);
1219
- const forceImmediate = this.config.immediateManifestAds ?? true;
2165
+ const forceImmediate = (_a = this.config.immediateManifestAds) != null ? _a : true;
1220
2166
  if (this.config.debugAdTiming) {
1221
2167
  console.log("[StormcloudVideoPlayer] Ad start decision:", {
1222
2168
  isManifestMarker,
@@ -1233,7 +2179,7 @@ var StormcloudVideoPlayer = class {
1233
2179
  this.clearAdStartTimer();
1234
2180
  this.handleAdStart(marker);
1235
2181
  } else if (typeof marker.ptsSeconds === "number") {
1236
- const tol = this.config.driftToleranceMs ?? 1e3;
2182
+ const tol = (_b = this.config.driftToleranceMs) != null ? _b : 1e3;
1237
2183
  const nowMs = this.video.currentTime * 1e3;
1238
2184
  const estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
1239
2185
  const deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
@@ -1343,12 +2289,13 @@ var StormcloudVideoPlayer = class {
1343
2289
  return void 0;
1344
2290
  }
1345
2291
  parseAttributeList(value) {
2292
+ var _a, _b, _c;
1346
2293
  const attrs = {};
1347
2294
  const regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
1348
2295
  let match;
1349
2296
  while ((match = regex.exec(value)) !== null) {
1350
- const key = match[1] ?? "";
1351
- let rawVal = match[3] ?? match[4] ?? "";
2297
+ const key = (_a = match[1]) != null ? _a : "";
2298
+ let rawVal = (_c = (_b = match[3]) != null ? _b : match[4]) != null ? _c : "";
1352
2299
  if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
1353
2300
  rawVal = rawVal.slice(1, -1);
1354
2301
  }
@@ -1512,6 +2459,43 @@ var StormcloudVideoPlayer = class {
1512
2459
  }
1513
2460
  }
1514
2461
  async fetchAdConfiguration() {
2462
+ var _a, _b, _c;
2463
+ const vastMode = this.config.vastMode || "default";
2464
+ if (this.config.debugAdTiming) {
2465
+ console.log(
2466
+ "[StormcloudVideoPlayer] VAST mode:",
2467
+ vastMode
2468
+ );
2469
+ }
2470
+ if (vastMode === "adstorm") {
2471
+ if (!this.config.licenseKey) {
2472
+ if (this.config.debugAdTiming) {
2473
+ console.warn(
2474
+ "[StormcloudVideoPlayer] AdStorm mode requires a license key"
2475
+ );
2476
+ }
2477
+ return;
2478
+ }
2479
+ const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
2480
+ this.apiVastTagUrl = vastEndpoint;
2481
+ if (this.config.debugAdTiming) {
2482
+ console.log(
2483
+ "[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
2484
+ vastEndpoint
2485
+ );
2486
+ }
2487
+ return;
2488
+ }
2489
+ if (this.config.vastTagUrl) {
2490
+ this.apiVastTagUrl = this.config.vastTagUrl;
2491
+ if (this.config.debugAdTiming) {
2492
+ console.log(
2493
+ "[StormcloudVideoPlayer] Using custom VAST tag URL:",
2494
+ this.apiVastTagUrl
2495
+ );
2496
+ }
2497
+ return;
2498
+ }
1515
2499
  const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
1516
2500
  if (this.config.debugAdTiming) {
1517
2501
  console.log(
@@ -1525,25 +2509,29 @@ var StormcloudVideoPlayer = class {
1525
2509
  }
1526
2510
  const response = await fetch(apiUrl, { headers });
1527
2511
  if (!response.ok) {
1528
- throw new Error(`Failed to fetch ad configuration: ${response.status}`);
2512
+ if (this.config.debugAdTiming) {
2513
+ console.warn(
2514
+ `[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
2515
+ );
2516
+ }
2517
+ return;
1529
2518
  }
1530
2519
  const data = await response.json();
1531
- const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
2520
+ const imaPayload = (_c = (_b = (_a = data.response) == null ? void 0 : _a.ima) == null ? void 0 : _b["publisherdesk.ima"]) == null ? void 0 : _c.payload;
1532
2521
  if (imaPayload) {
1533
2522
  this.apiVastTagUrl = decodeURIComponent(imaPayload);
1534
2523
  if (this.config.debugAdTiming) {
1535
2524
  console.log(
1536
- "[StormcloudVideoPlayer] Extracted VAST tag URL:",
2525
+ "[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
1537
2526
  this.apiVastTagUrl
1538
2527
  );
1539
2528
  }
1540
- }
1541
- this.vastConfig = data.response?.options?.vast;
1542
- if (this.config.debugAdTiming) {
1543
- console.log("[StormcloudVideoPlayer] Ad configuration loaded:", {
1544
- vastTagUrl: this.apiVastTagUrl,
1545
- vastConfig: this.vastConfig
1546
- });
2529
+ } else {
2530
+ if (this.config.debugAdTiming) {
2531
+ console.warn(
2532
+ "[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
2533
+ );
2534
+ }
1547
2535
  }
1548
2536
  }
1549
2537
  getCurrentAdIndex() {
@@ -1566,11 +2554,12 @@ var StormcloudVideoPlayer = class {
1566
2554
  return "other";
1567
2555
  }
1568
2556
  shouldShowNativeControls() {
2557
+ var _a, _b;
1569
2558
  const streamType = this.getStreamType();
1570
2559
  if (streamType === "other") {
1571
- return !(this.config.showCustomControls ?? false);
2560
+ return !((_a = this.config.showCustomControls) != null ? _a : false);
1572
2561
  }
1573
- return !!(this.config.allowNativeHls && !(this.config.showCustomControls ?? false));
2562
+ return !!(this.config.allowNativeHls && !((_b = this.config.showCustomControls) != null ? _b : false));
1574
2563
  }
1575
2564
  shouldContinueLiveStreamDuringAds() {
1576
2565
  if (this.config.allowNativeHls) {
@@ -1582,28 +2571,20 @@ var StormcloudVideoPlayer = class {
1582
2571
  return true;
1583
2572
  }
1584
2573
  async handleAdStart(_marker) {
2574
+ var _a;
1585
2575
  const scheduled = this.findCurrentOrNextBreak(
1586
2576
  this.video.currentTime * 1e3
1587
2577
  );
1588
2578
  const tags = this.selectVastTagsForBreak(scheduled);
1589
2579
  let vastTagUrl;
1590
- let adsNumber = 1;
1591
2580
  if (this.apiVastTagUrl) {
1592
2581
  vastTagUrl = this.apiVastTagUrl;
1593
- if (this.vastConfig) {
1594
- const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
1595
- if (isHls && this.vastConfig.cue_tones?.number_ads) {
1596
- adsNumber = this.vastConfig.cue_tones.number_ads;
1597
- } else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
1598
- adsNumber = this.vastConfig.timer_vod.number_ads;
1599
- }
1600
- }
1601
- this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
2582
+ this.adPodQueue = [];
1602
2583
  this.currentAdIndex = 0;
1603
- this.totalAdsInBreak = adsNumber;
2584
+ this.totalAdsInBreak = 1;
1604
2585
  if (this.config.debugAdTiming) {
1605
2586
  console.log(
1606
- `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
2587
+ "[StormcloudVideoPlayer] Using VAST endpoint:",
1607
2588
  vastTagUrl
1608
2589
  );
1609
2590
  }
@@ -1641,17 +2622,18 @@ var StormcloudVideoPlayer = class {
1641
2622
  this.handleAdFailure();
1642
2623
  }
1643
2624
  }
1644
- if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
2625
+ if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
1645
2626
  this.expectedAdBreakDurationMs = scheduled.durationMs;
1646
- this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
2627
+ this.currentAdBreakStartWallClockMs = (_a = this.currentAdBreakStartWallClockMs) != null ? _a : Date.now();
1647
2628
  this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
1648
2629
  }
1649
2630
  }
1650
2631
  findCurrentOrNextBreak(nowMs) {
2632
+ var _a;
1651
2633
  const schedule = [];
1652
2634
  let candidate;
1653
2635
  for (const b of schedule) {
1654
- const tol = this.config.driftToleranceMs ?? 1e3;
2636
+ const tol = (_a = this.config.driftToleranceMs) != null ? _a : 1e3;
1655
2637
  if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
1656
2638
  candidate = b;
1657
2639
  }
@@ -1667,7 +2649,8 @@ var StormcloudVideoPlayer = class {
1667
2649
  }
1668
2650
  }
1669
2651
  async handleMidAdJoin(adBreak, nowMs) {
1670
- const durationMs = adBreak.durationMs ?? 0;
2652
+ var _a;
2653
+ const durationMs = (_a = adBreak.durationMs) != null ? _a : 0;
1671
2654
  const endMs = adBreak.startTimeMs + durationMs;
1672
2655
  if (durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs) {
1673
2656
  const remainingMs = endMs - nowMs;
@@ -1768,6 +2751,7 @@ var StormcloudVideoPlayer = class {
1768
2751
  }
1769
2752
  }
1770
2753
  handleAdFailure() {
2754
+ var _a;
1771
2755
  if (this.config.debugAdTiming) {
1772
2756
  console.log(
1773
2757
  "[StormcloudVideoPlayer] Handling ad failure - resuming content",
@@ -1800,7 +2784,7 @@ var StormcloudVideoPlayer = class {
1800
2784
  if (this.config.debugAdTiming) {
1801
2785
  console.log("[StormcloudVideoPlayer] Resuming paused video");
1802
2786
  }
1803
- this.video.play()?.catch((error) => {
2787
+ (_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
1804
2788
  if (this.config.debugAdTiming) {
1805
2789
  console.error(
1806
2790
  "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
@@ -1815,8 +2799,9 @@ var StormcloudVideoPlayer = class {
1815
2799
  }
1816
2800
  }
1817
2801
  startAdFailsafeTimer() {
2802
+ var _a;
1818
2803
  this.clearAdFailsafeTimer();
1819
- const failsafeMs = this.config.adFailsafeTimeoutMs ?? 1e4;
2804
+ const failsafeMs = (_a = this.config.adFailsafeTimeoutMs) != null ? _a : 1e4;
1820
2805
  if (this.config.debugAdTiming) {
1821
2806
  console.log(
1822
2807
  `[StormcloudVideoPlayer] Starting failsafe timer (${failsafeMs}ms)`
@@ -1952,6 +2937,7 @@ var StormcloudVideoPlayer = class {
1952
2937
  }
1953
2938
  }
1954
2939
  destroy() {
2940
+ var _a, _b;
1955
2941
  this.clearAdStartTimer();
1956
2942
  this.clearAdStopTimer();
1957
2943
  this.clearAdFailsafeTimer();
@@ -1959,8 +2945,8 @@ var StormcloudVideoPlayer = class {
1959
2945
  clearInterval(this.heartbeatInterval);
1960
2946
  this.heartbeatInterval = void 0;
1961
2947
  }
1962
- this.hls?.destroy();
1963
- this.ima?.destroy();
2948
+ (_a = this.hls) == null ? void 0 : _a.destroy();
2949
+ (_b = this.ima) == null ? void 0 : _b.destroy();
1964
2950
  }
1965
2951
  };
1966
2952
 
@@ -1971,6 +2957,7 @@ var HlsPlayer = class extends import_react2.Component {
1971
2957
  this.player = null;
1972
2958
  this.mounted = false;
1973
2959
  this.load = async () => {
2960
+ var _a, _b, _c, _d, _e, _f;
1974
2961
  if (!this.props.videoElement || !this.props.src) return;
1975
2962
  try {
1976
2963
  if (this.player) {
@@ -2007,27 +2994,29 @@ var HlsPlayer = class extends import_react2.Component {
2007
2994
  if (this.props.adFailsafeTimeoutMs !== void 0)
2008
2995
  config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
2009
2996
  this.player = new StormcloudVideoPlayer(config);
2010
- this.props.onMount?.(this);
2997
+ (_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
2011
2998
  await this.player.load();
2012
2999
  if (this.mounted) {
2013
- this.props.onReady?.();
3000
+ (_d = (_c = this.props).onReady) == null ? void 0 : _d.call(_c);
2014
3001
  }
2015
3002
  } catch (error) {
2016
3003
  if (this.mounted) {
2017
- this.props.onError?.(error);
3004
+ (_f = (_e = this.props).onError) == null ? void 0 : _f.call(_e, error);
2018
3005
  }
2019
3006
  }
2020
3007
  };
2021
3008
  this.play = () => {
3009
+ var _a, _b;
2022
3010
  if (this.props.videoElement) {
2023
3011
  this.props.videoElement.play();
2024
- this.props.onPlay?.();
3012
+ (_b = (_a = this.props).onPlay) == null ? void 0 : _b.call(_a);
2025
3013
  }
2026
3014
  };
2027
3015
  this.pause = () => {
3016
+ var _a, _b;
2028
3017
  if (this.props.videoElement) {
2029
3018
  this.props.videoElement.pause();
2030
- this.props.onPause?.();
3019
+ (_b = (_a = this.props).onPause) == null ? void 0 : _b.call(_a);
2031
3020
  }
2032
3021
  };
2033
3022
  this.stop = () => {
@@ -2122,37 +3111,44 @@ var FilePlayer = class extends import_react3.Component {
2122
3111
  this.mounted = false;
2123
3112
  this.ready = false;
2124
3113
  this.load = () => {
3114
+ var _a, _b;
2125
3115
  if (!this.props.videoElement || !this.props.src) return;
2126
3116
  const video = this.props.videoElement;
2127
3117
  const handleLoadedMetadata = () => {
3118
+ var _a2, _b2;
2128
3119
  if (this.mounted && !this.ready) {
2129
3120
  this.ready = true;
2130
- this.props.onReady?.();
3121
+ (_b2 = (_a2 = this.props).onReady) == null ? void 0 : _b2.call(_a2);
2131
3122
  }
2132
3123
  };
2133
3124
  const handlePlay = () => {
3125
+ var _a2, _b2;
2134
3126
  if (this.mounted) {
2135
- this.props.onPlay?.();
3127
+ (_b2 = (_a2 = this.props).onPlay) == null ? void 0 : _b2.call(_a2);
2136
3128
  }
2137
3129
  };
2138
3130
  const handlePause = () => {
3131
+ var _a2, _b2;
2139
3132
  if (this.mounted) {
2140
- this.props.onPause?.();
3133
+ (_b2 = (_a2 = this.props).onPause) == null ? void 0 : _b2.call(_a2);
2141
3134
  }
2142
3135
  };
2143
3136
  const handleEnded = () => {
3137
+ var _a2, _b2;
2144
3138
  if (this.mounted) {
2145
- this.props.onEnded?.();
3139
+ (_b2 = (_a2 = this.props).onEnded) == null ? void 0 : _b2.call(_a2);
2146
3140
  }
2147
3141
  };
2148
3142
  const handleError = (error) => {
3143
+ var _a2, _b2;
2149
3144
  if (this.mounted) {
2150
- this.props.onError?.(error);
3145
+ (_b2 = (_a2 = this.props).onError) == null ? void 0 : _b2.call(_a2, error);
2151
3146
  }
2152
3147
  };
2153
3148
  const handleLoadedData = () => {
3149
+ var _a2, _b2;
2154
3150
  if (this.mounted) {
2155
- this.props.onLoaded?.();
3151
+ (_b2 = (_a2 = this.props).onLoaded) == null ? void 0 : _b2.call(_a2);
2156
3152
  }
2157
3153
  };
2158
3154
  video.addEventListener("loadedmetadata", handleLoadedMetadata);
@@ -2171,7 +3167,7 @@ var FilePlayer = class extends import_react3.Component {
2171
3167
  if (this.props.preload !== void 0)
2172
3168
  video.preload = this.props.preload;
2173
3169
  if (this.props.poster !== void 0) video.poster = this.props.poster;
2174
- this.props.onMount?.(this);
3170
+ (_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
2175
3171
  return () => {
2176
3172
  video.removeEventListener("loadedmetadata", handleLoadedMetadata);
2177
3173
  video.removeEventListener("play", handlePlay);