stormcloud-video-player 0.2.12 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +100 -1
  2. package/dist/stormcloud-vp.min.js +2 -2
  3. package/lib/index.cjs +1206 -151
  4. package/lib/index.cjs.map +1 -1
  5. package/lib/index.d.cts +77 -2
  6. package/lib/index.d.ts +77 -2
  7. package/lib/index.js +1196 -151
  8. package/lib/index.js.map +1 -1
  9. package/lib/player/StormcloudVideoPlayer.cjs +1100 -114
  10. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  11. package/lib/player/StormcloudVideoPlayer.d.cts +2 -2
  12. package/lib/players/FilePlayer.cjs +14 -7
  13. package/lib/players/FilePlayer.cjs.map +1 -1
  14. package/lib/players/HlsPlayer.cjs +1108 -119
  15. package/lib/players/HlsPlayer.cjs.map +1 -1
  16. package/lib/players/HlsPlayer.d.cts +1 -1
  17. package/lib/players/index.cjs +1122 -126
  18. package/lib/players/index.cjs.map +1 -1
  19. package/lib/sdk/hlsAdPlayer.cjs +561 -0
  20. package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
  21. package/lib/sdk/hlsAdPlayer.d.cts +10 -0
  22. package/lib/sdk/ima.cjs +176 -23
  23. package/lib/sdk/ima.cjs.map +1 -1
  24. package/lib/sdk/ima.d.cts +1 -1
  25. package/lib/{types-GpA_hKek.d.cts → types-mVgmKmzM.d.cts} +3 -0
  26. package/lib/ui/StormcloudVideoPlayer.cjs +1107 -119
  27. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  28. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  29. package/lib/utils/browserCompat.cjs +229 -0
  30. package/lib/utils/browserCompat.cjs.map +1 -0
  31. package/lib/utils/browserCompat.d.cts +36 -0
  32. package/lib/utils/polyfills.cjs +253 -0
  33. package/lib/utils/polyfills.cjs.map +1 -0
  34. package/lib/utils/polyfills.d.cts +11 -0
  35. package/lib/utils/tracking.cjs +10 -9
  36. package/lib/utils/tracking.cjs.map +1 -1
  37. package/lib/utils/tracking.d.cts +1 -1
  38. package/package.json +1 -1
package/lib/index.cjs CHANGED
@@ -40,12 +40,19 @@ __export(index_exports, {
40
40
  StormcloudVideoPlayer: () => StormcloudVideoPlayer,
41
41
  StormcloudVideoPlayerComponent: () => StormcloudVideoPlayerComponent,
42
42
  canPlay: () => canPlay,
43
+ createHlsAdPlayer: () => createHlsAdPlayer,
44
+ createImaController: () => createImaController,
43
45
  createStormcloudPlayer: () => createStormcloudPlayer,
44
46
  default: () => StormcloudVideoPlayerComponent,
47
+ detectBrowser: () => detectBrowser,
48
+ getBrowserConfigOverrides: () => getBrowserConfigOverrides,
45
49
  getBrowserID: () => getBrowserID,
46
50
  getClientInfo: () => getClientInfo,
51
+ getRecommendedAdPlayer: () => getRecommendedAdPlayer,
52
+ initializePolyfills: () => initializePolyfills,
47
53
  isMediaStream: () => isMediaStream,
48
54
  lazy: () => lazy,
55
+ logBrowserInfo: () => logBrowserInfo,
49
56
  merge: () => merge,
50
57
  omit: () => omit,
51
58
  parseQuery: () => parseQuery,
@@ -53,6 +60,9 @@ __export(index_exports, {
53
60
  randomString: () => randomString,
54
61
  sendHeartbeat: () => sendHeartbeat,
55
62
  sendInitialTracking: () => sendInitialTracking,
63
+ supportsFeature: () => supportsFeature,
64
+ supportsGoogleIMA: () => supportsGoogleIMA,
65
+ supportsModernJS: () => supportsModernJS,
56
66
  supportsWebKitPresentationMode: () => supportsWebKitPresentationMode
57
67
  });
58
68
  module.exports = __toCommonJS(index_exports);
@@ -61,7 +71,196 @@ module.exports = __toCommonJS(index_exports);
61
71
  var import_react = __toESM(require("react"), 1);
62
72
 
63
73
  // src/player/StormcloudVideoPlayer.ts
64
- var import_hls = __toESM(require("hls.js"), 1);
74
+ var import_hls2 = __toESM(require("hls.js"), 1);
75
+
76
+ // src/utils/browserCompat.ts
77
+ function getChromeVersion(ua) {
78
+ const match = ua.match(/Chrome\/(\d+)/);
79
+ return match && match[1] ? parseInt(match[1], 10) : 0;
80
+ }
81
+ function getWebKitVersion(ua) {
82
+ const match = ua.match(/AppleWebKit\/(\d+)/);
83
+ return match && match[1] ? parseInt(match[1], 10) : 0;
84
+ }
85
+ function getPlatform() {
86
+ var _a;
87
+ if ("userAgentData" in navigator && ((_a = navigator.userAgentData) == null ? void 0 : _a.platform)) {
88
+ return navigator.userAgentData.platform;
89
+ }
90
+ const ua = navigator.userAgent;
91
+ if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
92
+ return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
93
+ }
94
+ if (/Win/i.test(ua)) {
95
+ return "Win32";
96
+ }
97
+ if (/Linux/i.test(ua)) {
98
+ return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
99
+ }
100
+ if (/CrOS/i.test(ua)) {
101
+ return "CrOS";
102
+ }
103
+ return navigator.platform || "Unknown";
104
+ }
105
+ function detectBrowser() {
106
+ const ua = navigator.userAgent;
107
+ const platform = getPlatform();
108
+ let name = "Unknown";
109
+ let version = "0";
110
+ let majorVersion = 0;
111
+ let isSmartTV = false;
112
+ let isLegacyTV = false;
113
+ let supportsIMA = true;
114
+ let supportsModernJS2 = true;
115
+ let recommendedAdPlayer = "ima";
116
+ if (/Web0S|webOS/i.test(ua)) {
117
+ name = "LG WebOS";
118
+ isSmartTV = true;
119
+ const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
120
+ version = match && match[1] ? match[1] : "Unknown";
121
+ if (version !== "Unknown") {
122
+ const parts = version.split(".");
123
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
124
+ }
125
+ } else if (/Tizen/i.test(ua)) {
126
+ name = "Samsung Tizen";
127
+ isSmartTV = true;
128
+ const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
129
+ version = match && match[1] ? match[1] : "Unknown";
130
+ if (version !== "Unknown") {
131
+ const parts = version.split(".");
132
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
133
+ }
134
+ } else if (/SMART-TV|SmartTV/i.test(ua)) {
135
+ name = "Smart TV";
136
+ isSmartTV = true;
137
+ } else if (/NetCast/i.test(ua)) {
138
+ name = "LG NetCast";
139
+ isSmartTV = true;
140
+ isLegacyTV = true;
141
+ } else if (/BRAVIA/i.test(ua)) {
142
+ name = "Sony BRAVIA";
143
+ isSmartTV = true;
144
+ }
145
+ const chromeVersion = getChromeVersion(ua);
146
+ const webkitVersion = getWebKitVersion(ua);
147
+ if (chromeVersion > 0) {
148
+ if (!isSmartTV) {
149
+ name = "Chrome";
150
+ version = chromeVersion.toString();
151
+ majorVersion = chromeVersion;
152
+ }
153
+ if (chromeVersion < 50) {
154
+ supportsIMA = false;
155
+ supportsModernJS2 = false;
156
+ isLegacyTV = true;
157
+ recommendedAdPlayer = "hls";
158
+ }
159
+ }
160
+ if (webkitVersion > 0 && webkitVersion < 600) {
161
+ supportsModernJS2 = false;
162
+ if (isSmartTV) {
163
+ isLegacyTV = true;
164
+ supportsIMA = false;
165
+ recommendedAdPlayer = "hls";
166
+ }
167
+ }
168
+ if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
169
+ supportsModernJS2 = false;
170
+ supportsIMA = false;
171
+ recommendedAdPlayer = "hls";
172
+ }
173
+ if (typeof URLSearchParams === "undefined") {
174
+ supportsModernJS2 = false;
175
+ }
176
+ return {
177
+ name,
178
+ version,
179
+ majorVersion,
180
+ isSmartTV,
181
+ isLegacyTV,
182
+ platform,
183
+ supportsIMA,
184
+ supportsModernJS: supportsModernJS2,
185
+ recommendedAdPlayer
186
+ };
187
+ }
188
+ function supportsGoogleIMA() {
189
+ const browser = detectBrowser();
190
+ if (browser.isLegacyTV) {
191
+ return false;
192
+ }
193
+ if (typeof document === "undefined" || typeof document.createElement !== "function") {
194
+ return false;
195
+ }
196
+ try {
197
+ const video = document.createElement("video");
198
+ if (!video) {
199
+ return false;
200
+ }
201
+ } catch (e) {
202
+ return false;
203
+ }
204
+ if (typeof Promise === "undefined") {
205
+ return false;
206
+ }
207
+ return browser.supportsIMA;
208
+ }
209
+ function getRecommendedAdPlayer() {
210
+ const browser = detectBrowser();
211
+ return browser.recommendedAdPlayer;
212
+ }
213
+ function supportsModernJS() {
214
+ try {
215
+ return typeof Promise !== "undefined" && typeof Map !== "undefined" && typeof Set !== "undefined" && typeof Array.from !== "undefined" && typeof Object.assign !== "undefined" && typeof Array.prototype.forEach !== "undefined" && typeof String.prototype.includes !== "undefined";
216
+ } catch (e) {
217
+ return false;
218
+ }
219
+ }
220
+ function logBrowserInfo(debug = false) {
221
+ if (!debug) return;
222
+ const browser = detectBrowser();
223
+ const imaSupport = supportsGoogleIMA();
224
+ console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
225
+ browser: `${browser.name} ${browser.version}`,
226
+ platform: browser.platform,
227
+ isSmartTV: browser.isSmartTV,
228
+ isLegacyTV: browser.isLegacyTV,
229
+ supportsIMA: imaSupport,
230
+ supportsModernJS: browser.supportsModernJS,
231
+ recommendedAdPlayer: browser.recommendedAdPlayer,
232
+ userAgent: navigator.userAgent
233
+ });
234
+ }
235
+ function getBrowserConfigOverrides() {
236
+ const browser = detectBrowser();
237
+ const overrides = {};
238
+ if (browser.isLegacyTV || !browser.supportsIMA) {
239
+ overrides.adPlayerType = "hls";
240
+ }
241
+ if (browser.isSmartTV) {
242
+ overrides.allowNativeHls = true;
243
+ }
244
+ return overrides;
245
+ }
246
+ function supportsFeature(feature) {
247
+ switch (feature) {
248
+ case "ima":
249
+ return supportsGoogleIMA();
250
+ case "urlsearchparams":
251
+ return typeof URLSearchParams !== "undefined";
252
+ case "textencoder":
253
+ return typeof TextEncoder !== "undefined";
254
+ case "promises":
255
+ return typeof Promise !== "undefined";
256
+ case "fetch":
257
+ return typeof fetch !== "undefined";
258
+ case "crypto":
259
+ return typeof crypto !== "undefined" && typeof crypto.subtle !== "undefined";
260
+ default:
261
+ return false;
262
+ }
263
+ }
65
264
 
66
265
  // src/sdk/ima.ts
67
266
  function createImaController(video, options) {
@@ -79,9 +278,18 @@ function createImaController(video, options) {
79
278
  }
80
279
  }
81
280
  function ensureImaLoaded() {
281
+ var _a, _b, _c;
282
+ if (!supportsGoogleIMA()) {
283
+ console.warn(
284
+ "[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
285
+ );
286
+ return Promise.reject(
287
+ new Error("Google IMA SDK not supported on this browser")
288
+ );
289
+ }
82
290
  try {
83
291
  const frameEl = window.frameElement;
84
- const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
292
+ const sandboxAttr = ((_a = frameEl == null ? void 0 : frameEl.getAttribute) == null ? void 0 : _a.call(frameEl, "sandbox")) || "";
85
293
  if (sandboxAttr) {
86
294
  const tokens = new Set(
87
295
  sandboxAttr.split(/\s+/).map((t) => t.trim()).filter((t) => t.length > 0)
@@ -95,13 +303,13 @@ function createImaController(video, options) {
95
303
  }
96
304
  } catch {
97
305
  }
98
- if (typeof window !== "undefined" && window.google?.ima)
306
+ if (typeof window !== "undefined" && ((_b = window.google) == null ? void 0 : _b.ima))
99
307
  return Promise.resolve();
100
308
  const existing = document.querySelector(
101
309
  'script[data-ima="true"]'
102
310
  );
103
311
  if (existing) {
104
- if (window.google?.ima) {
312
+ if ((_c = window.google) == null ? void 0 : _c.ima) {
105
313
  return Promise.resolve();
106
314
  }
107
315
  return new Promise((resolve, reject) => {
@@ -159,6 +367,7 @@ function createImaController(video, options) {
159
367
  return {
160
368
  initialize() {
161
369
  ensureImaLoaded().then(() => {
370
+ var _a, _b;
162
371
  const google = window.google;
163
372
  if (!adDisplayContainer) {
164
373
  const container = document.createElement("div");
@@ -172,14 +381,14 @@ function createImaController(video, options) {
172
381
  container.style.justifyContent = "center";
173
382
  container.style.pointerEvents = "none";
174
383
  container.style.zIndex = "2";
175
- video.parentElement?.appendChild(container);
384
+ (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
176
385
  adContainerEl = container;
177
386
  adDisplayContainer = new google.ima.AdDisplayContainer(
178
387
  container,
179
388
  video
180
389
  );
181
390
  try {
182
- adDisplayContainer.initialize?.();
391
+ (_b = adDisplayContainer.initialize) == null ? void 0 : _b.call(adDisplayContainer);
183
392
  } catch {
184
393
  }
185
394
  }
@@ -264,6 +473,7 @@ function createImaController(video, options) {
264
473
  adsManager.addEventListener(
265
474
  AdErrorEvent.AD_ERROR,
266
475
  (errorEvent) => {
476
+ var _a;
267
477
  console.error("[IMA] Ad error:", errorEvent.getError());
268
478
  destroyAdsManager();
269
479
  adPlaying = false;
@@ -294,10 +504,10 @@ function createImaController(video, options) {
294
504
  "[IMA] Max retries reached, emitting ad_error"
295
505
  );
296
506
  emit("ad_error");
297
- if (!options?.continueLiveStreamDuringAds) {
507
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
298
508
  if (video.paused) {
299
509
  console.log("[IMA] Resuming paused video after ad error");
300
- video.play()?.catch(() => {
510
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
301
511
  });
302
512
  }
303
513
  }
@@ -308,7 +518,7 @@ function createImaController(video, options) {
308
518
  AdEvent.CONTENT_PAUSE_REQUESTED,
309
519
  () => {
310
520
  console.log("[IMA] Content pause requested");
311
- if (!options?.continueLiveStreamDuringAds) {
521
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
312
522
  video.pause();
313
523
  console.log("[IMA] Video paused (VOD mode)");
314
524
  } else {
@@ -334,6 +544,7 @@ function createImaController(video, options) {
334
544
  adsManager.addEventListener(
335
545
  AdEvent.CONTENT_RESUME_REQUESTED,
336
546
  () => {
547
+ var _a;
337
548
  console.log("[IMA] Content resume requested");
338
549
  adPlaying = false;
339
550
  video.muted = originalMutedState;
@@ -344,8 +555,8 @@ function createImaController(video, options) {
344
555
  "[IMA] Ad container hidden - pointer events disabled"
345
556
  );
346
557
  }
347
- if (!options?.continueLiveStreamDuringAds) {
348
- video.play()?.catch(() => {
558
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
559
+ (_a = video.play()) == null ? void 0 : _a.catch(() => {
349
560
  });
350
561
  console.log("[IMA] Video resumed (VOD mode)");
351
562
  } else {
@@ -367,7 +578,7 @@ function createImaController(video, options) {
367
578
  "[IMA] Ad container hidden after all ads completed"
368
579
  );
369
580
  }
370
- if (!options?.continueLiveStreamDuringAds) {
581
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
371
582
  video.play().catch(() => {
372
583
  });
373
584
  console.log(
@@ -395,7 +606,7 @@ function createImaController(video, options) {
395
606
  adContainerEl.style.display = "none";
396
607
  console.log("[IMA] Ad container hidden after setup error");
397
608
  }
398
- if (!options?.continueLiveStreamDuringAds) {
609
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
399
610
  if (video.paused) {
400
611
  console.log("[IMA] Resuming paused video after setup error");
401
612
  video.play().catch(() => {
@@ -423,7 +634,7 @@ function createImaController(video, options) {
423
634
  adContainerEl.style.display = "none";
424
635
  console.log("[IMA] Ad container hidden after loader error");
425
636
  }
426
- if (!options?.continueLiveStreamDuringAds) {
637
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
427
638
  if (video.paused) {
428
639
  console.log("[IMA] Resuming paused video after loader error");
429
640
  video.play().catch(() => {
@@ -445,14 +656,15 @@ function createImaController(video, options) {
445
656
  return adsLoadedPromise;
446
657
  } catch (error) {
447
658
  console.error("[IMA] Failed to request ads:", error);
448
- currentReject?.(error);
659
+ currentReject == null ? void 0 : currentReject(error);
449
660
  adsLoadedReject = void 0;
450
661
  adsLoadedResolve = void 0;
451
662
  return Promise.reject(error);
452
663
  }
453
664
  },
454
665
  async play() {
455
- if (!window.google?.ima || !adDisplayContainer) {
666
+ var _a, _b;
667
+ if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
456
668
  console.warn(
457
669
  "[IMA] Cannot play ad: IMA SDK or ad container not available"
458
670
  );
@@ -474,14 +686,15 @@ function createImaController(video, options) {
474
686
  } catch (error) {
475
687
  console.error("[IMA] Error starting ad playback:", error);
476
688
  adPlaying = false;
477
- if (!options?.continueLiveStreamDuringAds) {
478
- video.play()?.catch(() => {
689
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
690
+ (_b = video.play()) == null ? void 0 : _b.catch(() => {
479
691
  });
480
692
  }
481
693
  return Promise.reject(error);
482
694
  }
483
695
  },
484
696
  async stop() {
697
+ var _a;
485
698
  adPlaying = false;
486
699
  video.muted = originalMutedState;
487
700
  if (adContainerEl) {
@@ -490,11 +703,11 @@ function createImaController(video, options) {
490
703
  console.log("[IMA] Ad container hidden after stop");
491
704
  }
492
705
  try {
493
- adsManager?.stop?.();
706
+ (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
494
707
  } catch {
495
708
  }
496
709
  destroyAdsManager();
497
- if (!options?.continueLiveStreamDuringAds) {
710
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
498
711
  video.play().catch(() => {
499
712
  });
500
713
  console.log("[IMA] Video resumed after stop (VOD mode)");
@@ -503,6 +716,7 @@ function createImaController(video, options) {
503
716
  }
504
717
  },
505
718
  destroy() {
719
+ var _a;
506
720
  destroyAdsManager();
507
721
  adPlaying = false;
508
722
  video.muted = originalMutedState;
@@ -511,10 +725,10 @@ function createImaController(video, options) {
511
725
  adContainerEl.style.display = "none";
512
726
  }
513
727
  try {
514
- adsLoader?.destroy?.();
728
+ (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
515
729
  } catch {
516
730
  }
517
- if (adContainerEl?.parentElement) {
731
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
518
732
  adContainerEl.parentElement.removeChild(adContainerEl);
519
733
  }
520
734
  adContainerEl = void 0;
@@ -525,7 +739,8 @@ function createImaController(video, options) {
525
739
  return adPlaying;
526
740
  },
527
741
  resize(width, height) {
528
- if (!adsManager || !window.google?.ima) {
742
+ var _a;
743
+ if (!adsManager || !((_a = window.google) == null ? void 0 : _a.ima)) {
529
744
  console.warn(
530
745
  "[IMA] Cannot resize: No ads manager or IMA SDK available"
531
746
  );
@@ -543,7 +758,8 @@ function createImaController(video, options) {
543
758
  listeners.get(event).add(listener);
544
759
  },
545
760
  off(event, listener) {
546
- listeners.get(event)?.delete(listener);
761
+ var _a;
762
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
547
763
  },
548
764
  updateOriginalMutedState(muted) {
549
765
  originalMutedState = muted;
@@ -574,9 +790,533 @@ function createImaController(video, options) {
574
790
  };
575
791
  }
576
792
 
793
+ // src/sdk/hlsAdPlayer.ts
794
+ var import_hls = __toESM(require("hls.js"), 1);
795
+ function createHlsAdPlayer(contentVideo, options) {
796
+ let adPlaying = false;
797
+ let originalMutedState = false;
798
+ const listeners = /* @__PURE__ */ new Map();
799
+ const licenseKey = options == null ? void 0 : options.licenseKey;
800
+ const mainHlsInstance = options == null ? void 0 : options.mainHlsInstance;
801
+ let adVideoElement;
802
+ let adHls;
803
+ let adContainerEl;
804
+ let currentAd;
805
+ let sessionId;
806
+ let trackingFired = {
807
+ impression: false,
808
+ start: false,
809
+ firstQuartile: false,
810
+ midpoint: false,
811
+ thirdQuartile: false,
812
+ complete: false
813
+ };
814
+ function emit(event, payload) {
815
+ const set = listeners.get(event);
816
+ if (!set) return;
817
+ for (const fn of Array.from(set)) {
818
+ try {
819
+ fn(payload);
820
+ } catch (error) {
821
+ console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
822
+ }
823
+ }
824
+ }
825
+ function generateSessionId() {
826
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
827
+ }
828
+ function fireTrackingPixels(urls) {
829
+ if (!urls || urls.length === 0) return;
830
+ urls.forEach((url) => {
831
+ try {
832
+ let trackingUrl = url;
833
+ if (sessionId) {
834
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
835
+ }
836
+ if (licenseKey) {
837
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
838
+ }
839
+ const img = new Image(1, 1);
840
+ img.src = trackingUrl;
841
+ console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
842
+ } catch (error) {
843
+ console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
844
+ }
845
+ });
846
+ }
847
+ function getMainStreamQuality() {
848
+ if (!mainHlsInstance || !mainHlsInstance.levels) {
849
+ return null;
850
+ }
851
+ const currentLevel = mainHlsInstance.currentLevel;
852
+ if (currentLevel === -1 || !mainHlsInstance.levels[currentLevel]) {
853
+ const autoLevel = mainHlsInstance.loadLevel;
854
+ if (autoLevel !== -1 && mainHlsInstance.levels[autoLevel]) {
855
+ const level2 = mainHlsInstance.levels[autoLevel];
856
+ return {
857
+ width: level2.width || 1920,
858
+ height: level2.height || 1080,
859
+ bitrate: level2.bitrate || 5e6
860
+ };
861
+ }
862
+ return null;
863
+ }
864
+ const level = mainHlsInstance.levels[currentLevel];
865
+ return {
866
+ width: level.width || 1920,
867
+ height: level.height || 1080,
868
+ bitrate: level.bitrate || 5e6
869
+ };
870
+ }
871
+ function selectBestMediaFile(mediaFiles) {
872
+ if (mediaFiles.length === 0) {
873
+ throw new Error("No media files available");
874
+ }
875
+ const firstFile = mediaFiles[0];
876
+ if (!firstFile) {
877
+ throw new Error("No media files available");
878
+ }
879
+ if (mediaFiles.length === 1) {
880
+ return firstFile;
881
+ }
882
+ const mainQuality = getMainStreamQuality();
883
+ if (!mainQuality) {
884
+ console.log("[HlsAdPlayer] No main stream quality info, using first media file");
885
+ return firstFile;
886
+ }
887
+ console.log("[HlsAdPlayer] Main stream quality:", mainQuality);
888
+ const scoredFiles = mediaFiles.map((file) => {
889
+ const widthDiff = Math.abs(file.width - mainQuality.width);
890
+ const heightDiff = Math.abs(file.height - mainQuality.height);
891
+ const resolutionDiff = widthDiff + heightDiff;
892
+ const fileBitrate = (file.bitrate || 5e3) * 1e3;
893
+ const bitrateDiff = Math.abs(fileBitrate - mainQuality.bitrate);
894
+ const score = resolutionDiff * 2 + bitrateDiff / 1e3;
895
+ return { file, score, resolutionDiff, bitrateDiff };
896
+ });
897
+ scoredFiles.sort((a, b) => a.score - b.score);
898
+ const bestMatch = scoredFiles[0];
899
+ if (!bestMatch) {
900
+ console.log("[HlsAdPlayer] No best match found, using first media file");
901
+ return firstFile;
902
+ }
903
+ console.log("[HlsAdPlayer] Selected media file:", {
904
+ url: bestMatch.file.url,
905
+ resolution: `${bestMatch.file.width}x${bestMatch.file.height}`,
906
+ bitrate: bestMatch.file.bitrate,
907
+ score: bestMatch.score,
908
+ resolutionDiff: bestMatch.resolutionDiff,
909
+ bitrateDiff: bestMatch.bitrateDiff
910
+ });
911
+ return bestMatch.file;
912
+ }
913
+ function parseVastXml(xmlString) {
914
+ var _a, _b, _c, _d;
915
+ try {
916
+ const parser = new DOMParser();
917
+ const xmlDoc = parser.parseFromString(xmlString, "text/xml");
918
+ const parserError = xmlDoc.querySelector("parsererror");
919
+ if (parserError) {
920
+ console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
921
+ return null;
922
+ }
923
+ const adElement = xmlDoc.querySelector("Ad");
924
+ if (!adElement) {
925
+ console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
926
+ return null;
927
+ }
928
+ const adId = adElement.getAttribute("id") || "unknown";
929
+ const title = ((_a = xmlDoc.querySelector("AdTitle")) == null ? void 0 : _a.textContent) || "Ad";
930
+ const durationText = ((_b = xmlDoc.querySelector("Duration")) == null ? void 0 : _b.textContent) || "00:00:30";
931
+ const durationParts = durationText.split(":");
932
+ const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
933
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
934
+ const mediaFiles = [];
935
+ mediaFileElements.forEach((mf) => {
936
+ var _a2;
937
+ const type = mf.getAttribute("type") || "";
938
+ if (type === "application/x-mpegURL" || type.includes("m3u8")) {
939
+ const bitrateAttr = mf.getAttribute("bitrate");
940
+ const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
941
+ mediaFiles.push({
942
+ url: ((_a2 = mf.textContent) == null ? void 0 : _a2.trim()) || "",
943
+ type,
944
+ width: parseInt(mf.getAttribute("width") || "1920", 10),
945
+ height: parseInt(mf.getAttribute("height") || "1080", 10),
946
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
947
+ });
948
+ }
949
+ });
950
+ if (mediaFiles.length === 0) {
951
+ console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
952
+ return null;
953
+ }
954
+ const trackingUrls = {
955
+ impression: [],
956
+ start: [],
957
+ firstQuartile: [],
958
+ midpoint: [],
959
+ thirdQuartile: [],
960
+ complete: [],
961
+ mute: [],
962
+ unmute: [],
963
+ pause: [],
964
+ resume: [],
965
+ fullscreen: [],
966
+ exitFullscreen: [],
967
+ skip: [],
968
+ error: []
969
+ };
970
+ xmlDoc.querySelectorAll("Impression").forEach((el) => {
971
+ var _a2;
972
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
973
+ if (url) trackingUrls.impression.push(url);
974
+ });
975
+ xmlDoc.querySelectorAll("Tracking").forEach((el) => {
976
+ var _a2;
977
+ const event = el.getAttribute("event");
978
+ const url = (_a2 = el.textContent) == null ? void 0 : _a2.trim();
979
+ if (event && url) {
980
+ const eventKey = event;
981
+ if (trackingUrls[eventKey]) {
982
+ trackingUrls[eventKey].push(url);
983
+ }
984
+ }
985
+ });
986
+ const clickThrough = (_d = (_c = xmlDoc.querySelector("ClickThrough")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.trim();
987
+ return {
988
+ id: adId,
989
+ title,
990
+ duration,
991
+ mediaFiles,
992
+ trackingUrls,
993
+ clickThrough
994
+ };
995
+ } catch (error) {
996
+ console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
997
+ return null;
998
+ }
999
+ }
1000
+ function createAdVideoElement() {
1001
+ const video = document.createElement("video");
1002
+ video.style.position = "absolute";
1003
+ video.style.left = "0";
1004
+ video.style.top = "0";
1005
+ video.style.width = "100%";
1006
+ video.style.height = "100%";
1007
+ video.style.objectFit = "contain";
1008
+ video.style.backgroundColor = "#000";
1009
+ video.playsInline = true;
1010
+ video.muted = false;
1011
+ return video;
1012
+ }
1013
+ function setupAdEventListeners() {
1014
+ if (!adVideoElement || !currentAd) return;
1015
+ adVideoElement.addEventListener("timeupdate", () => {
1016
+ if (!currentAd || !adVideoElement) return;
1017
+ const progress = adVideoElement.currentTime / currentAd.duration;
1018
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
1019
+ trackingFired.firstQuartile = true;
1020
+ fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
1021
+ }
1022
+ if (progress >= 0.5 && !trackingFired.midpoint) {
1023
+ trackingFired.midpoint = true;
1024
+ fireTrackingPixels(currentAd.trackingUrls.midpoint);
1025
+ }
1026
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1027
+ trackingFired.thirdQuartile = true;
1028
+ fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1029
+ }
1030
+ });
1031
+ adVideoElement.addEventListener("playing", () => {
1032
+ if (!currentAd || trackingFired.start) return;
1033
+ trackingFired.start = true;
1034
+ fireTrackingPixels(currentAd.trackingUrls.start);
1035
+ console.log("[HlsAdPlayer] Ad started playing");
1036
+ });
1037
+ adVideoElement.addEventListener("ended", () => {
1038
+ if (!currentAd || trackingFired.complete) return;
1039
+ trackingFired.complete = true;
1040
+ fireTrackingPixels(currentAd.trackingUrls.complete);
1041
+ console.log("[HlsAdPlayer] Ad completed");
1042
+ handleAdComplete();
1043
+ });
1044
+ adVideoElement.addEventListener("error", (e) => {
1045
+ console.error("[HlsAdPlayer] Ad video error:", e);
1046
+ if (currentAd) {
1047
+ fireTrackingPixels(currentAd.trackingUrls.error);
1048
+ }
1049
+ handleAdError();
1050
+ });
1051
+ adVideoElement.addEventListener("volumechange", () => {
1052
+ if (!currentAd) return;
1053
+ if (adVideoElement.muted) {
1054
+ fireTrackingPixels(currentAd.trackingUrls.mute);
1055
+ } else {
1056
+ fireTrackingPixels(currentAd.trackingUrls.unmute);
1057
+ }
1058
+ });
1059
+ adVideoElement.addEventListener("pause", () => {
1060
+ if (currentAd && !adVideoElement.ended) {
1061
+ fireTrackingPixels(currentAd.trackingUrls.pause);
1062
+ }
1063
+ });
1064
+ adVideoElement.addEventListener("play", () => {
1065
+ if (currentAd && adVideoElement.currentTime > 0) {
1066
+ fireTrackingPixels(currentAd.trackingUrls.resume);
1067
+ }
1068
+ });
1069
+ }
1070
+ function handleAdComplete() {
1071
+ console.log("[HlsAdPlayer] Handling ad completion");
1072
+ adPlaying = false;
1073
+ contentVideo.muted = originalMutedState;
1074
+ if (adContainerEl) {
1075
+ adContainerEl.style.display = "none";
1076
+ adContainerEl.style.pointerEvents = "none";
1077
+ }
1078
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1079
+ contentVideo.play().catch(() => {
1080
+ });
1081
+ console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1082
+ } else {
1083
+ console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1084
+ }
1085
+ emit("content_resume");
1086
+ emit("all_ads_completed");
1087
+ }
1088
+ function handleAdError() {
1089
+ console.log("[HlsAdPlayer] Handling ad error");
1090
+ adPlaying = false;
1091
+ contentVideo.muted = originalMutedState;
1092
+ if (adContainerEl) {
1093
+ adContainerEl.style.display = "none";
1094
+ adContainerEl.style.pointerEvents = "none";
1095
+ }
1096
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1097
+ if (contentVideo.paused) {
1098
+ contentVideo.play().catch(() => {
1099
+ });
1100
+ }
1101
+ }
1102
+ emit("ad_error");
1103
+ }
1104
+ return {
1105
+ initialize() {
1106
+ var _a;
1107
+ console.log("[HlsAdPlayer] Initializing");
1108
+ if (!adContainerEl) {
1109
+ const container = document.createElement("div");
1110
+ container.style.position = "absolute";
1111
+ container.style.left = "0";
1112
+ container.style.top = "0";
1113
+ container.style.right = "0";
1114
+ container.style.bottom = "0";
1115
+ container.style.display = "none";
1116
+ container.style.alignItems = "center";
1117
+ container.style.justifyContent = "center";
1118
+ container.style.pointerEvents = "none";
1119
+ container.style.zIndex = "2";
1120
+ container.style.backgroundColor = "#000";
1121
+ (_a = contentVideo.parentElement) == null ? void 0 : _a.appendChild(container);
1122
+ adContainerEl = container;
1123
+ }
1124
+ },
1125
+ async requestAds(vastTagUrl) {
1126
+ console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
1127
+ if (adPlaying) {
1128
+ console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
1129
+ return Promise.reject(new Error("Ad already playing"));
1130
+ }
1131
+ try {
1132
+ sessionId = generateSessionId();
1133
+ const response = await fetch(vastTagUrl);
1134
+ if (!response.ok) {
1135
+ throw new Error(`Failed to fetch VAST: ${response.statusText}`);
1136
+ }
1137
+ const vastXml = await response.text();
1138
+ console.log("[HlsAdPlayer] VAST XML received");
1139
+ const ad = parseVastXml(vastXml);
1140
+ if (!ad) {
1141
+ throw new Error("Failed to parse VAST XML or no ads available");
1142
+ }
1143
+ currentAd = ad;
1144
+ console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
1145
+ fireTrackingPixels(ad.trackingUrls.impression);
1146
+ trackingFired.impression = true;
1147
+ return Promise.resolve();
1148
+ } catch (error) {
1149
+ console.error("[HlsAdPlayer] Error requesting ads:", error);
1150
+ emit("ad_error");
1151
+ return Promise.reject(error);
1152
+ }
1153
+ },
1154
+ async play() {
1155
+ if (!currentAd) {
1156
+ console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
1157
+ return Promise.reject(new Error("No ad loaded"));
1158
+ }
1159
+ console.log("[HlsAdPlayer] Starting ad playback");
1160
+ try {
1161
+ if (!adVideoElement) {
1162
+ adVideoElement = createAdVideoElement();
1163
+ adContainerEl == null ? void 0 : adContainerEl.appendChild(adVideoElement);
1164
+ setupAdEventListeners();
1165
+ }
1166
+ trackingFired = {
1167
+ impression: trackingFired.impression,
1168
+ start: false,
1169
+ firstQuartile: false,
1170
+ midpoint: false,
1171
+ thirdQuartile: false,
1172
+ complete: false
1173
+ };
1174
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1175
+ contentVideo.pause();
1176
+ console.log("[HlsAdPlayer] Content paused (VOD mode)");
1177
+ } else {
1178
+ console.log("[HlsAdPlayer] Content continues (Live mode)");
1179
+ }
1180
+ contentVideo.muted = true;
1181
+ adPlaying = true;
1182
+ if (adContainerEl) {
1183
+ adContainerEl.style.display = "flex";
1184
+ adContainerEl.style.pointerEvents = "auto";
1185
+ }
1186
+ emit("content_pause");
1187
+ const mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1188
+ if (!mediaFile) {
1189
+ throw new Error("No media file available for ad");
1190
+ }
1191
+ console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
1192
+ if (import_hls.default.isSupported()) {
1193
+ if (adHls) {
1194
+ adHls.destroy();
1195
+ }
1196
+ adHls = new import_hls.default({
1197
+ enableWorker: true,
1198
+ lowLatencyMode: false
1199
+ });
1200
+ adHls.loadSource(mediaFile.url);
1201
+ adHls.attachMedia(adVideoElement);
1202
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
1203
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1204
+ adVideoElement.play().catch((error) => {
1205
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1206
+ handleAdError();
1207
+ });
1208
+ });
1209
+ adHls.on(import_hls.default.Events.ERROR, (event, data) => {
1210
+ console.error("[HlsAdPlayer] HLS error:", data);
1211
+ if (data.fatal) {
1212
+ handleAdError();
1213
+ }
1214
+ });
1215
+ } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1216
+ adVideoElement.src = mediaFile.url;
1217
+ adVideoElement.play().catch((error) => {
1218
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1219
+ handleAdError();
1220
+ });
1221
+ } else {
1222
+ throw new Error("HLS not supported");
1223
+ }
1224
+ return Promise.resolve();
1225
+ } catch (error) {
1226
+ console.error("[HlsAdPlayer] Error playing ad:", error);
1227
+ handleAdError();
1228
+ return Promise.reject(error);
1229
+ }
1230
+ },
1231
+ async stop() {
1232
+ console.log("[HlsAdPlayer] Stopping ad");
1233
+ adPlaying = false;
1234
+ contentVideo.muted = originalMutedState;
1235
+ if (adContainerEl) {
1236
+ adContainerEl.style.display = "none";
1237
+ adContainerEl.style.pointerEvents = "none";
1238
+ }
1239
+ if (adHls) {
1240
+ adHls.destroy();
1241
+ adHls = void 0;
1242
+ }
1243
+ if (adVideoElement) {
1244
+ adVideoElement.pause();
1245
+ adVideoElement.src = "";
1246
+ }
1247
+ if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1248
+ contentVideo.play().catch(() => {
1249
+ });
1250
+ }
1251
+ currentAd = void 0;
1252
+ },
1253
+ destroy() {
1254
+ console.log("[HlsAdPlayer] Destroying");
1255
+ adPlaying = false;
1256
+ contentVideo.muted = originalMutedState;
1257
+ if (adHls) {
1258
+ adHls.destroy();
1259
+ adHls = void 0;
1260
+ }
1261
+ if (adVideoElement) {
1262
+ adVideoElement.pause();
1263
+ adVideoElement.src = "";
1264
+ adVideoElement.remove();
1265
+ adVideoElement = void 0;
1266
+ }
1267
+ if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
1268
+ adContainerEl.parentElement.removeChild(adContainerEl);
1269
+ }
1270
+ adContainerEl = void 0;
1271
+ currentAd = void 0;
1272
+ listeners.clear();
1273
+ },
1274
+ isAdPlaying() {
1275
+ return adPlaying;
1276
+ },
1277
+ resize(width, height) {
1278
+ console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
1279
+ if (adContainerEl) {
1280
+ adContainerEl.style.width = `${width}px`;
1281
+ adContainerEl.style.height = `${height}px`;
1282
+ }
1283
+ if (adVideoElement) {
1284
+ adVideoElement.style.width = `${width}px`;
1285
+ adVideoElement.style.height = `${height}px`;
1286
+ }
1287
+ },
1288
+ on(event, listener) {
1289
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
1290
+ listeners.get(event).add(listener);
1291
+ },
1292
+ off(event, listener) {
1293
+ var _a;
1294
+ (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
1295
+ },
1296
+ updateOriginalMutedState(muted) {
1297
+ originalMutedState = muted;
1298
+ },
1299
+ getOriginalMutedState() {
1300
+ return originalMutedState;
1301
+ },
1302
+ setAdVolume(volume) {
1303
+ if (adVideoElement && adPlaying) {
1304
+ adVideoElement.volume = Math.max(0, Math.min(1, volume));
1305
+ }
1306
+ },
1307
+ getAdVolume() {
1308
+ if (adVideoElement && adPlaying) {
1309
+ return adVideoElement.volume;
1310
+ }
1311
+ return 1;
1312
+ }
1313
+ };
1314
+ }
1315
+
577
1316
  // src/utils/tracking.ts
578
1317
  var cachedBrowserId = null;
579
1318
  function getClientInfo() {
1319
+ var _a, _b, _c, _d;
580
1320
  const ua = navigator.userAgent;
581
1321
  const platform = navigator.platform;
582
1322
  const vendor = navigator.vendor || "";
@@ -584,12 +1324,12 @@ function getClientInfo() {
584
1324
  const memory = navigator.deviceMemory || null;
585
1325
  const hardwareConcurrency = navigator.hardwareConcurrency || 1;
586
1326
  const screenInfo = {
587
- width: screen?.width,
588
- height: screen?.height,
589
- availWidth: screen?.availWidth,
590
- availHeight: screen?.availHeight,
591
- orientation: screen?.orientation?.type || "",
592
- pixelDepth: screen?.pixelDepth
1327
+ width: screen == null ? void 0 : screen.width,
1328
+ height: screen == null ? void 0 : screen.height,
1329
+ availWidth: screen == null ? void 0 : screen.availWidth,
1330
+ availHeight: screen == null ? void 0 : screen.availHeight,
1331
+ orientation: ((_a = screen == null ? void 0 : screen.orientation) == null ? void 0 : _a.type) || "",
1332
+ pixelDepth: screen == null ? void 0 : screen.pixelDepth
593
1333
  };
594
1334
  let deviceType = "desktop";
595
1335
  let brand = "Unknown";
@@ -686,10 +1426,10 @@ function getClientInfo() {
686
1426
  if (vendor.includes("Samsung") || ua.includes("SM-")) brand = "Samsung";
687
1427
  }
688
1428
  isWebView = /wv|WebView|Linux; U;/.test(ua);
689
- if (window?.outerHeight === 0 && window?.outerWidth === 0) {
1429
+ if ((window == null ? void 0 : window.outerHeight) === 0 && (window == null ? void 0 : window.outerWidth) === 0) {
690
1430
  isWebView = true;
691
1431
  }
692
- isWebApp = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;
1432
+ 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;
693
1433
  return {
694
1434
  brand,
695
1435
  os,
@@ -710,7 +1450,7 @@ function getClientInfo() {
710
1450
  deviceMemory: memory,
711
1451
  maxTouchPoints,
712
1452
  language: navigator.language,
713
- languages: navigator.languages?.join(",") || "",
1453
+ languages: ((_d = navigator.languages) == null ? void 0 : _d.join(",")) || "",
714
1454
  cookieEnabled: navigator.cookieEnabled,
715
1455
  doNotTrack: navigator.doNotTrack || "",
716
1456
  referrer: document.referrer,
@@ -823,6 +1563,215 @@ async function sendHeartbeat(licenseKey) {
823
1563
  }
824
1564
  }
825
1565
 
1566
+ // src/utils/polyfills.ts
1567
+ function polyfillURLSearchParams() {
1568
+ if (typeof URLSearchParams !== "undefined") {
1569
+ return;
1570
+ }
1571
+ class URLSearchParamsPolyfill {
1572
+ constructor(init) {
1573
+ this.params = /* @__PURE__ */ new Map();
1574
+ if (typeof init === "string") {
1575
+ this.parseQueryString(init);
1576
+ } else if (init instanceof URLSearchParamsPolyfill) {
1577
+ init.forEach((value, key) => {
1578
+ this.append(key, value);
1579
+ });
1580
+ }
1581
+ }
1582
+ parseQueryString(query) {
1583
+ const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
1584
+ if (!cleanQuery) return;
1585
+ cleanQuery.split("&").forEach((param) => {
1586
+ const [key, value] = param.split("=");
1587
+ if (key) {
1588
+ const decodedKey = this.safeDecodeURIComponent(key);
1589
+ const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
1590
+ this.append(decodedKey, decodedValue);
1591
+ }
1592
+ });
1593
+ }
1594
+ safeDecodeURIComponent(str) {
1595
+ try {
1596
+ return decodeURIComponent(str.replace(/\+/g, " "));
1597
+ } catch (e) {
1598
+ return str;
1599
+ }
1600
+ }
1601
+ append(name, value) {
1602
+ const values = this.params.get(name) || [];
1603
+ values.push(String(value));
1604
+ this.params.set(name, values);
1605
+ }
1606
+ delete(name) {
1607
+ this.params.delete(name);
1608
+ }
1609
+ get(name) {
1610
+ const values = this.params.get(name);
1611
+ return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
1612
+ }
1613
+ getAll(name) {
1614
+ return this.params.get(name) || [];
1615
+ }
1616
+ has(name) {
1617
+ return this.params.has(name);
1618
+ }
1619
+ set(name, value) {
1620
+ this.params.set(name, [String(value)]);
1621
+ }
1622
+ forEach(callback) {
1623
+ this.params.forEach((values, key) => {
1624
+ values.forEach((value) => {
1625
+ callback(value, key, this);
1626
+ });
1627
+ });
1628
+ }
1629
+ toString() {
1630
+ const parts = [];
1631
+ this.params.forEach((values, key) => {
1632
+ values.forEach((value) => {
1633
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
1634
+ });
1635
+ });
1636
+ return parts.join("&");
1637
+ }
1638
+ }
1639
+ window.URLSearchParams = URLSearchParamsPolyfill;
1640
+ }
1641
+ function polyfillTextEncoder() {
1642
+ if (typeof TextEncoder !== "undefined") {
1643
+ return;
1644
+ }
1645
+ class TextEncoderPolyfill {
1646
+ constructor() {
1647
+ this.encoding = "utf-8";
1648
+ }
1649
+ encode(str) {
1650
+ const utf8 = [];
1651
+ for (let i = 0; i < str.length; i++) {
1652
+ let charcode = str.charCodeAt(i);
1653
+ if (charcode < 128) {
1654
+ utf8.push(charcode);
1655
+ } else if (charcode < 2048) {
1656
+ utf8.push(192 | charcode >> 6, 128 | charcode & 63);
1657
+ } else if (charcode < 55296 || charcode >= 57344) {
1658
+ utf8.push(
1659
+ 224 | charcode >> 12,
1660
+ 128 | charcode >> 6 & 63,
1661
+ 128 | charcode & 63
1662
+ );
1663
+ } else {
1664
+ i++;
1665
+ charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
1666
+ utf8.push(
1667
+ 240 | charcode >> 18,
1668
+ 128 | charcode >> 12 & 63,
1669
+ 128 | charcode >> 6 & 63,
1670
+ 128 | charcode & 63
1671
+ );
1672
+ }
1673
+ }
1674
+ return new Uint8Array(utf8);
1675
+ }
1676
+ }
1677
+ window.TextEncoder = TextEncoderPolyfill;
1678
+ }
1679
+ function polyfillPromiseFinally() {
1680
+ if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
1681
+ Promise.prototype.finally = function(callback) {
1682
+ const constructor = this.constructor;
1683
+ return this.then(
1684
+ (value) => constructor.resolve(callback()).then(() => value),
1685
+ (reason) => constructor.resolve(callback()).then(() => {
1686
+ throw reason;
1687
+ })
1688
+ );
1689
+ };
1690
+ }
1691
+ }
1692
+ function polyfillObjectAssign() {
1693
+ if (typeof Object.assign !== "function") {
1694
+ Object.assign = function(target, ...sources) {
1695
+ if (target == null) {
1696
+ throw new TypeError("Cannot convert undefined or null to object");
1697
+ }
1698
+ const to = Object(target);
1699
+ for (let i = 0; i < sources.length; i++) {
1700
+ const nextSource = sources[i];
1701
+ if (nextSource != null) {
1702
+ for (const nextKey in nextSource) {
1703
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
1704
+ to[nextKey] = nextSource[nextKey];
1705
+ }
1706
+ }
1707
+ }
1708
+ }
1709
+ return to;
1710
+ };
1711
+ }
1712
+ }
1713
+ function polyfillArrayFrom() {
1714
+ if (!Array.from) {
1715
+ Array.from = function(arrayLike, mapFn, thisArg) {
1716
+ const items = Object(arrayLike);
1717
+ if (arrayLike == null) {
1718
+ throw new TypeError("Array.from requires an array-like object");
1719
+ }
1720
+ const len = items.length >>> 0;
1721
+ const result = new Array(len);
1722
+ for (let i = 0; i < len; i++) {
1723
+ if (mapFn) {
1724
+ result[i] = mapFn.call(thisArg, items[i], i);
1725
+ } else {
1726
+ result[i] = items[i];
1727
+ }
1728
+ }
1729
+ return result;
1730
+ };
1731
+ }
1732
+ }
1733
+ function polyfillStringStartsWith() {
1734
+ if (!String.prototype.startsWith) {
1735
+ String.prototype.startsWith = function(search, pos) {
1736
+ pos = !pos || pos < 0 ? 0 : +pos;
1737
+ return this.substring(pos, pos + search.length) === search;
1738
+ };
1739
+ }
1740
+ }
1741
+ function polyfillStringEndsWith() {
1742
+ if (!String.prototype.endsWith) {
1743
+ String.prototype.endsWith = function(search, length) {
1744
+ if (length === void 0 || length > this.length) {
1745
+ length = this.length;
1746
+ }
1747
+ return this.substring(length - search.length, length) === search;
1748
+ };
1749
+ }
1750
+ }
1751
+ function polyfillStringIncludes() {
1752
+ if (!String.prototype.includes) {
1753
+ String.prototype.includes = function(search, start) {
1754
+ if (typeof start !== "number") {
1755
+ start = 0;
1756
+ }
1757
+ if (start + search.length > this.length) {
1758
+ return false;
1759
+ }
1760
+ return this.indexOf(search, start) !== -1;
1761
+ };
1762
+ }
1763
+ }
1764
+ function initializePolyfills() {
1765
+ polyfillObjectAssign();
1766
+ polyfillArrayFrom();
1767
+ polyfillStringStartsWith();
1768
+ polyfillStringEndsWith();
1769
+ polyfillStringIncludes();
1770
+ polyfillURLSearchParams();
1771
+ polyfillTextEncoder();
1772
+ polyfillPromiseFinally();
1773
+ }
1774
+
826
1775
  // src/player/StormcloudVideoPlayer.ts
827
1776
  var StormcloudVideoPlayer = class {
828
1777
  constructor(config) {
@@ -835,13 +1784,44 @@ var StormcloudVideoPlayer = class {
835
1784
  this.totalAdsInBreak = 0;
836
1785
  this.showAds = false;
837
1786
  this.isLiveStream = false;
838
- this.config = config;
1787
+ initializePolyfills();
1788
+ const browserOverrides = getBrowserConfigOverrides();
1789
+ this.config = { ...config, ...browserOverrides };
839
1790
  this.video = config.videoElement;
840
- this.ima = createImaController(this.video, {
841
- continueLiveStreamDuringAds: false
842
- });
1791
+ logBrowserInfo(config.debugAdTiming);
1792
+ this.ima = this.createAdPlayer(false);
1793
+ }
1794
+ createAdPlayer(continueLiveStreamDuringAds) {
1795
+ const vastMode = this.config.vastMode || "default";
1796
+ let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
1797
+ if (adPlayerType === "ima" && !supportsGoogleIMA()) {
1798
+ if (this.config.debugAdTiming) {
1799
+ console.warn(
1800
+ "[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
1801
+ );
1802
+ }
1803
+ adPlayerType = "hls";
1804
+ }
1805
+ if (adPlayerType === "hls") {
1806
+ if (this.config.debugAdTiming) {
1807
+ console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
1808
+ }
1809
+ return createHlsAdPlayer(this.video, {
1810
+ continueLiveStreamDuringAds,
1811
+ ...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {},
1812
+ ...this.hls ? { mainHlsInstance: this.hls } : {}
1813
+ });
1814
+ } else {
1815
+ if (this.config.debugAdTiming) {
1816
+ console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
1817
+ }
1818
+ return createImaController(this.video, {
1819
+ continueLiveStreamDuringAds
1820
+ });
1821
+ }
843
1822
  }
844
1823
  async load() {
1824
+ var _a, _b;
845
1825
  if (!this.attached) {
846
1826
  this.attach();
847
1827
  }
@@ -858,7 +1838,7 @@ var StormcloudVideoPlayer = class {
858
1838
  this.initializeTracking();
859
1839
  if (this.shouldUseNativeHls()) {
860
1840
  this.video.src = this.config.src;
861
- this.isLiveStream = this.config.lowLatencyMode ?? false;
1841
+ this.isLiveStream = (_a = this.config.lowLatencyMode) != null ? _a : false;
862
1842
  if (this.config.debugAdTiming) {
863
1843
  console.log(
864
1844
  "[StormcloudVideoPlayer] allowNativeHls: true - VOD mode detected:",
@@ -870,17 +1850,15 @@ var StormcloudVideoPlayer = class {
870
1850
  );
871
1851
  }
872
1852
  this.ima.destroy();
873
- this.ima = createImaController(this.video, {
874
- continueLiveStreamDuringAds: false
875
- });
1853
+ this.ima = this.createAdPlayer(false);
876
1854
  this.ima.initialize();
877
1855
  if (this.config.autoplay) {
878
- await this.video.play()?.catch(() => {
879
- });
1856
+ await ((_b = this.video.play()) == null ? void 0 : _b.catch(() => {
1857
+ }));
880
1858
  }
881
1859
  return;
882
1860
  }
883
- this.hls = new import_hls.default({
1861
+ this.hls = new import_hls2.default({
884
1862
  enableWorker: true,
885
1863
  backBufferLength: 30,
886
1864
  liveDurationInfinity: true,
@@ -888,13 +1866,18 @@ var StormcloudVideoPlayer = class {
888
1866
  maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
889
1867
  ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
890
1868
  });
891
- this.hls.on(import_hls.default.Events.MEDIA_ATTACHED, () => {
892
- this.hls?.loadSource(this.config.src);
1869
+ this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
1870
+ var _a2;
1871
+ (_a2 = this.hls) == null ? void 0 : _a2.loadSource(this.config.src);
893
1872
  });
894
- this.hls.on(import_hls.default.Events.MANIFEST_PARSED, async (_, data) => {
895
- this.isLiveStream = this.hls?.levels?.some(
896
- (level) => level?.details?.live === true || level?.details?.type === "LIVE"
897
- ) ?? false;
1873
+ this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
1874
+ var _a2, _b2, _c, _d;
1875
+ this.isLiveStream = (_c = (_b2 = (_a2 = this.hls) == null ? void 0 : _a2.levels) == null ? void 0 : _b2.some(
1876
+ (level) => {
1877
+ var _a3, _b3;
1878
+ 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";
1879
+ }
1880
+ )) != null ? _c : false;
898
1881
  if (this.config.debugAdTiming) {
899
1882
  const adBehavior = this.shouldContinueLiveStreamDuringAds() ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
900
1883
  console.log("[StormcloudVideoPlayer] Stream type detected:", {
@@ -904,33 +1887,32 @@ var StormcloudVideoPlayer = class {
904
1887
  });
905
1888
  }
906
1889
  this.ima.destroy();
907
- this.ima = createImaController(this.video, {
908
- continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
909
- });
1890
+ this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
910
1891
  this.ima.initialize();
911
1892
  if (this.config.autoplay) {
912
- await this.video.play()?.catch(() => {
913
- });
1893
+ await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
1894
+ }));
914
1895
  }
915
1896
  });
916
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
917
- const id3Tags = (data?.samples || []).map((s) => ({
1897
+ this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
1898
+ const id3Tags = ((data == null ? void 0 : data.samples) || []).map((s) => ({
918
1899
  key: "ID3",
919
- value: s?.data,
920
- ptsSeconds: s?.pts
1900
+ value: s == null ? void 0 : s.data,
1901
+ ptsSeconds: s == null ? void 0 : s.pts
921
1902
  }));
922
1903
  id3Tags.forEach((tag) => this.onId3Tag(tag));
923
1904
  });
924
- this.hls.on(import_hls.default.Events.FRAG_CHANGED, (_evt, data) => {
925
- const frag = data?.frag;
926
- const tagList = frag?.tagList;
1905
+ this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
1906
+ var _a2, _b2, _c;
1907
+ const frag = data == null ? void 0 : data.frag;
1908
+ const tagList = frag == null ? void 0 : frag.tagList;
927
1909
  if (!Array.isArray(tagList)) return;
928
1910
  for (const entry of tagList) {
929
1911
  let tag = "";
930
1912
  let value = "";
931
1913
  if (Array.isArray(entry)) {
932
- tag = String(entry[0] ?? "");
933
- value = String(entry[1] ?? "");
1914
+ tag = String((_a2 = entry[0]) != null ? _a2 : "");
1915
+ value = String((_b2 = entry[1]) != null ? _b2 : "");
934
1916
  } else if (typeof entry === "string") {
935
1917
  const idx = entry.indexOf(":");
936
1918
  if (idx >= 0) {
@@ -954,8 +1936,8 @@ var StormcloudVideoPlayer = class {
954
1936
  const prog = this.parseCueOutCont(value);
955
1937
  const marker = {
956
1938
  type: "progress",
957
- ...prog?.duration !== void 0 ? { durationSeconds: prog.duration } : {},
958
- ...prog?.elapsed !== void 0 ? { ptsSeconds: prog.elapsed } : {},
1939
+ ...(prog == null ? void 0 : prog.duration) !== void 0 ? { durationSeconds: prog.duration } : {},
1940
+ ...(prog == null ? void 0 : prog.elapsed) !== void 0 ? { ptsSeconds: prog.elapsed } : {},
959
1941
  raw: { tag, value }
960
1942
  };
961
1943
  this.onScte35Marker(marker);
@@ -965,7 +1947,7 @@ var StormcloudVideoPlayer = class {
965
1947
  const attrs = this.parseAttributeList(value);
966
1948
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
967
1949
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
968
- const klass = String(attrs["CLASS"] ?? "");
1950
+ const klass = String((_c = attrs["CLASS"]) != null ? _c : "");
969
1951
  const duration = this.toNumber(attrs["DURATION"]);
970
1952
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
971
1953
  const marker = {
@@ -981,14 +1963,15 @@ var StormcloudVideoPlayer = class {
981
1963
  }
982
1964
  }
983
1965
  });
984
- this.hls.on(import_hls.default.Events.ERROR, (_evt, data) => {
985
- if (data?.fatal) {
1966
+ this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
1967
+ var _a2, _b2;
1968
+ if (data == null ? void 0 : data.fatal) {
986
1969
  switch (data.type) {
987
- case import_hls.default.ErrorTypes.NETWORK_ERROR:
988
- this.hls?.startLoad();
1970
+ case import_hls2.default.ErrorTypes.NETWORK_ERROR:
1971
+ (_a2 = this.hls) == null ? void 0 : _a2.startLoad();
989
1972
  break;
990
- case import_hls.default.ErrorTypes.MEDIA_ERROR:
991
- this.hls?.recoverMediaError();
1973
+ case import_hls2.default.ErrorTypes.MEDIA_ERROR:
1974
+ (_b2 = this.hls) == null ? void 0 : _b2.recoverMediaError();
992
1975
  break;
993
1976
  default:
994
1977
  this.destroy();
@@ -1090,11 +2073,12 @@ var StormcloudVideoPlayer = class {
1090
2073
  }
1091
2074
  }
1092
2075
  parseScte35FromId3(tag) {
2076
+ var _a, _b, _c, _d;
1093
2077
  const text = this.decodeId3ValueToText(tag.value);
1094
2078
  if (!text) return void 0;
1095
2079
  const cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
1096
2080
  if (cueOutMatch) {
1097
- const arg = (cueOutMatch[1] ?? "").trim();
2081
+ const arg = ((_a = cueOutMatch[1]) != null ? _a : "").trim();
1098
2082
  const dur = this.parseCueOutDuration(arg);
1099
2083
  const marker = {
1100
2084
  type: "start",
@@ -1106,12 +2090,12 @@ var StormcloudVideoPlayer = class {
1106
2090
  }
1107
2091
  const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
1108
2092
  if (cueOutContMatch) {
1109
- const arg = (cueOutContMatch[1] ?? "").trim();
2093
+ const arg = ((_b = cueOutContMatch[1]) != null ? _b : "").trim();
1110
2094
  const cont = this.parseCueOutCont(arg);
1111
2095
  const marker = {
1112
2096
  type: "progress",
1113
2097
  ...tag.ptsSeconds !== void 0 ? { ptsSeconds: tag.ptsSeconds } : {},
1114
- ...cont?.duration !== void 0 ? { durationSeconds: cont.duration } : {},
2098
+ ...(cont == null ? void 0 : cont.duration) !== void 0 ? { durationSeconds: cont.duration } : {},
1115
2099
  raw: { id3: text }
1116
2100
  };
1117
2101
  return marker;
@@ -1127,10 +2111,10 @@ var StormcloudVideoPlayer = class {
1127
2111
  }
1128
2112
  const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
1129
2113
  if (daterangeMatch) {
1130
- const attrs = this.parseAttributeList(daterangeMatch[1] ?? "");
2114
+ const attrs = this.parseAttributeList((_c = daterangeMatch[1]) != null ? _c : "");
1131
2115
  const hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
1132
2116
  const hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
1133
- const klass = String(attrs["CLASS"] ?? "");
2117
+ const klass = String((_d = attrs["CLASS"]) != null ? _d : "");
1134
2118
  const duration = this.toNumber(attrs["DURATION"]);
1135
2119
  if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
1136
2120
  const marker = {
@@ -1187,6 +2171,7 @@ var StormcloudVideoPlayer = class {
1187
2171
  }
1188
2172
  }
1189
2173
  onScte35Marker(marker) {
2174
+ var _a, _b;
1190
2175
  if (this.config.debugAdTiming) {
1191
2176
  console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
1192
2177
  type: marker.type,
@@ -1202,7 +2187,7 @@ var StormcloudVideoPlayer = class {
1202
2187
  this.expectedAdBreakDurationMs = durationMs;
1203
2188
  this.currentAdBreakStartWallClockMs = Date.now();
1204
2189
  const isManifestMarker = this.isManifestBasedMarker(marker);
1205
- const forceImmediate = this.config.immediateManifestAds ?? true;
2190
+ const forceImmediate = (_a = this.config.immediateManifestAds) != null ? _a : true;
1206
2191
  if (this.config.debugAdTiming) {
1207
2192
  console.log("[StormcloudVideoPlayer] Ad start decision:", {
1208
2193
  isManifestMarker,
@@ -1219,7 +2204,7 @@ var StormcloudVideoPlayer = class {
1219
2204
  this.clearAdStartTimer();
1220
2205
  this.handleAdStart(marker);
1221
2206
  } else if (typeof marker.ptsSeconds === "number") {
1222
- const tol = this.config.driftToleranceMs ?? 1e3;
2207
+ const tol = (_b = this.config.driftToleranceMs) != null ? _b : 1e3;
1223
2208
  const nowMs = this.video.currentTime * 1e3;
1224
2209
  const estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
1225
2210
  const deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
@@ -1329,12 +2314,13 @@ var StormcloudVideoPlayer = class {
1329
2314
  return void 0;
1330
2315
  }
1331
2316
  parseAttributeList(value) {
2317
+ var _a, _b, _c;
1332
2318
  const attrs = {};
1333
2319
  const regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
1334
2320
  let match;
1335
2321
  while ((match = regex.exec(value)) !== null) {
1336
- const key = match[1] ?? "";
1337
- let rawVal = match[3] ?? match[4] ?? "";
2322
+ const key = (_a = match[1]) != null ? _a : "";
2323
+ let rawVal = (_c = (_b = match[3]) != null ? _b : match[4]) != null ? _c : "";
1338
2324
  if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
1339
2325
  rawVal = rawVal.slice(1, -1);
1340
2326
  }
@@ -1498,6 +2484,43 @@ var StormcloudVideoPlayer = class {
1498
2484
  }
1499
2485
  }
1500
2486
  async fetchAdConfiguration() {
2487
+ var _a, _b, _c;
2488
+ const vastMode = this.config.vastMode || "default";
2489
+ if (this.config.debugAdTiming) {
2490
+ console.log(
2491
+ "[StormcloudVideoPlayer] VAST mode:",
2492
+ vastMode
2493
+ );
2494
+ }
2495
+ if (vastMode === "adstorm") {
2496
+ if (!this.config.licenseKey) {
2497
+ if (this.config.debugAdTiming) {
2498
+ console.warn(
2499
+ "[StormcloudVideoPlayer] AdStorm mode requires a license key"
2500
+ );
2501
+ }
2502
+ return;
2503
+ }
2504
+ const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
2505
+ this.apiVastTagUrl = vastEndpoint;
2506
+ if (this.config.debugAdTiming) {
2507
+ console.log(
2508
+ "[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
2509
+ vastEndpoint
2510
+ );
2511
+ }
2512
+ return;
2513
+ }
2514
+ if (this.config.vastTagUrl) {
2515
+ this.apiVastTagUrl = this.config.vastTagUrl;
2516
+ if (this.config.debugAdTiming) {
2517
+ console.log(
2518
+ "[StormcloudVideoPlayer] Using custom VAST tag URL:",
2519
+ this.apiVastTagUrl
2520
+ );
2521
+ }
2522
+ return;
2523
+ }
1501
2524
  const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
1502
2525
  if (this.config.debugAdTiming) {
1503
2526
  console.log(
@@ -1511,25 +2534,29 @@ var StormcloudVideoPlayer = class {
1511
2534
  }
1512
2535
  const response = await fetch(apiUrl, { headers });
1513
2536
  if (!response.ok) {
1514
- throw new Error(`Failed to fetch ad configuration: ${response.status}`);
2537
+ if (this.config.debugAdTiming) {
2538
+ console.warn(
2539
+ `[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
2540
+ );
2541
+ }
2542
+ return;
1515
2543
  }
1516
2544
  const data = await response.json();
1517
- const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
2545
+ const imaPayload = (_c = (_b = (_a = data.response) == null ? void 0 : _a.ima) == null ? void 0 : _b["publisherdesk.ima"]) == null ? void 0 : _c.payload;
1518
2546
  if (imaPayload) {
1519
2547
  this.apiVastTagUrl = decodeURIComponent(imaPayload);
1520
2548
  if (this.config.debugAdTiming) {
1521
2549
  console.log(
1522
- "[StormcloudVideoPlayer] Extracted VAST tag URL:",
2550
+ "[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
1523
2551
  this.apiVastTagUrl
1524
2552
  );
1525
2553
  }
1526
- }
1527
- this.vastConfig = data.response?.options?.vast;
1528
- if (this.config.debugAdTiming) {
1529
- console.log("[StormcloudVideoPlayer] Ad configuration loaded:", {
1530
- vastTagUrl: this.apiVastTagUrl,
1531
- vastConfig: this.vastConfig
1532
- });
2554
+ } else {
2555
+ if (this.config.debugAdTiming) {
2556
+ console.warn(
2557
+ "[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
2558
+ );
2559
+ }
1533
2560
  }
1534
2561
  }
1535
2562
  getCurrentAdIndex() {
@@ -1552,11 +2579,12 @@ var StormcloudVideoPlayer = class {
1552
2579
  return "other";
1553
2580
  }
1554
2581
  shouldShowNativeControls() {
2582
+ var _a, _b;
1555
2583
  const streamType = this.getStreamType();
1556
2584
  if (streamType === "other") {
1557
- return !(this.config.showCustomControls ?? false);
2585
+ return !((_a = this.config.showCustomControls) != null ? _a : false);
1558
2586
  }
1559
- return !!(this.config.allowNativeHls && !(this.config.showCustomControls ?? false));
2587
+ return !!(this.config.allowNativeHls && !((_b = this.config.showCustomControls) != null ? _b : false));
1560
2588
  }
1561
2589
  shouldContinueLiveStreamDuringAds() {
1562
2590
  if (this.config.allowNativeHls) {
@@ -1568,28 +2596,20 @@ var StormcloudVideoPlayer = class {
1568
2596
  return true;
1569
2597
  }
1570
2598
  async handleAdStart(_marker) {
2599
+ var _a;
1571
2600
  const scheduled = this.findCurrentOrNextBreak(
1572
2601
  this.video.currentTime * 1e3
1573
2602
  );
1574
2603
  const tags = this.selectVastTagsForBreak(scheduled);
1575
2604
  let vastTagUrl;
1576
- let adsNumber = 1;
1577
2605
  if (this.apiVastTagUrl) {
1578
2606
  vastTagUrl = this.apiVastTagUrl;
1579
- if (this.vastConfig) {
1580
- const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
1581
- if (isHls && this.vastConfig.cue_tones?.number_ads) {
1582
- adsNumber = this.vastConfig.cue_tones.number_ads;
1583
- } else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
1584
- adsNumber = this.vastConfig.timer_vod.number_ads;
1585
- }
1586
- }
1587
- this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
2607
+ this.adPodQueue = [];
1588
2608
  this.currentAdIndex = 0;
1589
- this.totalAdsInBreak = adsNumber;
2609
+ this.totalAdsInBreak = 1;
1590
2610
  if (this.config.debugAdTiming) {
1591
2611
  console.log(
1592
- `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
2612
+ "[StormcloudVideoPlayer] Using VAST endpoint:",
1593
2613
  vastTagUrl
1594
2614
  );
1595
2615
  }
@@ -1627,17 +2647,18 @@ var StormcloudVideoPlayer = class {
1627
2647
  this.handleAdFailure();
1628
2648
  }
1629
2649
  }
1630
- if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
2650
+ if (this.expectedAdBreakDurationMs == null && (scheduled == null ? void 0 : scheduled.durationMs) != null) {
1631
2651
  this.expectedAdBreakDurationMs = scheduled.durationMs;
1632
- this.currentAdBreakStartWallClockMs = this.currentAdBreakStartWallClockMs ?? Date.now();
2652
+ this.currentAdBreakStartWallClockMs = (_a = this.currentAdBreakStartWallClockMs) != null ? _a : Date.now();
1633
2653
  this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
1634
2654
  }
1635
2655
  }
1636
2656
  findCurrentOrNextBreak(nowMs) {
2657
+ var _a;
1637
2658
  const schedule = [];
1638
2659
  let candidate;
1639
2660
  for (const b of schedule) {
1640
- const tol = this.config.driftToleranceMs ?? 1e3;
2661
+ const tol = (_a = this.config.driftToleranceMs) != null ? _a : 1e3;
1641
2662
  if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
1642
2663
  candidate = b;
1643
2664
  }
@@ -1653,7 +2674,8 @@ var StormcloudVideoPlayer = class {
1653
2674
  }
1654
2675
  }
1655
2676
  async handleMidAdJoin(adBreak, nowMs) {
1656
- const durationMs = adBreak.durationMs ?? 0;
2677
+ var _a;
2678
+ const durationMs = (_a = adBreak.durationMs) != null ? _a : 0;
1657
2679
  const endMs = adBreak.startTimeMs + durationMs;
1658
2680
  if (durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs) {
1659
2681
  const remainingMs = endMs - nowMs;
@@ -1754,6 +2776,7 @@ var StormcloudVideoPlayer = class {
1754
2776
  }
1755
2777
  }
1756
2778
  handleAdFailure() {
2779
+ var _a;
1757
2780
  if (this.config.debugAdTiming) {
1758
2781
  console.log(
1759
2782
  "[StormcloudVideoPlayer] Handling ad failure - resuming content",
@@ -1786,7 +2809,7 @@ var StormcloudVideoPlayer = class {
1786
2809
  if (this.config.debugAdTiming) {
1787
2810
  console.log("[StormcloudVideoPlayer] Resuming paused video");
1788
2811
  }
1789
- this.video.play()?.catch((error) => {
2812
+ (_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
1790
2813
  if (this.config.debugAdTiming) {
1791
2814
  console.error(
1792
2815
  "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
@@ -1801,8 +2824,9 @@ var StormcloudVideoPlayer = class {
1801
2824
  }
1802
2825
  }
1803
2826
  startAdFailsafeTimer() {
2827
+ var _a;
1804
2828
  this.clearAdFailsafeTimer();
1805
- const failsafeMs = this.config.adFailsafeTimeoutMs ?? 1e4;
2829
+ const failsafeMs = (_a = this.config.adFailsafeTimeoutMs) != null ? _a : 1e4;
1806
2830
  if (this.config.debugAdTiming) {
1807
2831
  console.log(
1808
2832
  `[StormcloudVideoPlayer] Starting failsafe timer (${failsafeMs}ms)`
@@ -1938,6 +2962,7 @@ var StormcloudVideoPlayer = class {
1938
2962
  }
1939
2963
  }
1940
2964
  destroy() {
2965
+ var _a, _b;
1941
2966
  this.clearAdStartTimer();
1942
2967
  this.clearAdStopTimer();
1943
2968
  this.clearAdFailsafeTimer();
@@ -1945,8 +2970,8 @@ var StormcloudVideoPlayer = class {
1945
2970
  clearInterval(this.heartbeatInterval);
1946
2971
  this.heartbeatInterval = void 0;
1947
2972
  }
1948
- this.hls?.destroy();
1949
- this.ima?.destroy();
2973
+ (_a = this.hls) == null ? void 0 : _a.destroy();
2974
+ (_b = this.ima) == null ? void 0 : _b.destroy();
1950
2975
  }
1951
2976
  };
1952
2977
 
@@ -2053,7 +3078,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2053
3078
  }
2054
3079
  setShowSpeedMenu(false);
2055
3080
  };
2056
- const isHlsStream = src?.toLowerCase().includes(".m3u8") || src?.toLowerCase().includes("/hls/");
3081
+ const isHlsStream = (src == null ? void 0 : src.toLowerCase().includes(".m3u8")) || (src == null ? void 0 : src.toLowerCase().includes("/hls/"));
2057
3082
  const shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
2058
3083
  const criticalPropsKey = (0, import_react.useMemo)(() => {
2059
3084
  return CRITICAL_PROPS.map((prop) => `${prop}:${props[prop]}`).join("|");
@@ -2103,13 +3128,13 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2103
3128
  player.load().then(() => {
2104
3129
  const showNative = player.shouldShowNativeControls();
2105
3130
  setShouldShowNativeControls(showNative);
2106
- onReady?.(player);
3131
+ onReady == null ? void 0 : onReady(player);
2107
3132
  }).catch((error) => {
2108
3133
  console.error(
2109
3134
  "StormcloudVideoPlayer: Failed to load player:",
2110
3135
  error
2111
3136
  );
2112
- onReady?.(player);
3137
+ onReady == null ? void 0 : onReady(player);
2113
3138
  });
2114
3139
  return () => {
2115
3140
  try {
@@ -2165,6 +3190,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2165
3190
  (0, import_react.useEffect)(() => {
2166
3191
  if (!playerRef.current || !videoRef.current) return;
2167
3192
  const updateStates = () => {
3193
+ var _a;
2168
3194
  if (playerRef.current && videoRef.current) {
2169
3195
  setIsMuted(playerRef.current.isMuted());
2170
3196
  setIsPlaying(!videoRef.current.paused);
@@ -2182,13 +3208,14 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2182
3208
  );
2183
3209
  }
2184
3210
  setIsFullscreen(
2185
- document.fullscreenElement === videoRef.current?.parentElement
3211
+ document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
2186
3212
  );
2187
3213
  };
2188
3214
  const interval = setInterval(updateStates, 200);
2189
3215
  const handleFullscreenChange = () => {
3216
+ var _a;
2190
3217
  setIsFullscreen(
2191
- document.fullscreenElement === videoRef.current?.parentElement
3218
+ document.fullscreenElement === ((_a = videoRef.current) == null ? void 0 : _a.parentElement)
2192
3219
  );
2193
3220
  };
2194
3221
  document.addEventListener("fullscreenchange", handleFullscreenChange);
@@ -3534,27 +4561,30 @@ var parseQuery = (url) => {
3534
4561
  const query = {};
3535
4562
  const queryString = url.split("?")[1] || "";
3536
4563
  if (!queryString) return query;
4564
+ const manualParse = (qs) => {
4565
+ qs.split("&").forEach((param) => {
4566
+ const [key, value] = param.split("=");
4567
+ if (key) {
4568
+ try {
4569
+ query[decodeURIComponent(key)] = value ? decodeURIComponent(value.replace(/\+/g, " ")) : "";
4570
+ } catch (e) {
4571
+ query[key] = value || "";
4572
+ }
4573
+ }
4574
+ });
4575
+ };
3537
4576
  if (typeof URLSearchParams !== "undefined") {
3538
4577
  try {
3539
4578
  const params = new URLSearchParams(queryString);
3540
4579
  params.forEach((value, key) => {
3541
4580
  query[key] = value;
3542
4581
  });
4582
+ return query;
3543
4583
  } catch (e) {
3544
- queryString.split("&").forEach((param) => {
3545
- const [key, value] = param.split("=");
3546
- if (key) {
3547
- query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
3548
- }
3549
- });
4584
+ manualParse(queryString);
3550
4585
  }
3551
4586
  } else {
3552
- queryString.split("&").forEach((param) => {
3553
- const [key, value] = param.split("=");
3554
- if (key) {
3555
- query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
3556
- }
3557
- });
4587
+ manualParse(queryString);
3558
4588
  }
3559
4589
  return query;
3560
4590
  };
@@ -3628,6 +4658,7 @@ var HlsPlayer = class extends import_react3.Component {
3628
4658
  this.player = null;
3629
4659
  this.mounted = false;
3630
4660
  this.load = async () => {
4661
+ var _a, _b, _c, _d, _e, _f;
3631
4662
  if (!this.props.videoElement || !this.props.src) return;
3632
4663
  try {
3633
4664
  if (this.player) {
@@ -3664,27 +4695,29 @@ var HlsPlayer = class extends import_react3.Component {
3664
4695
  if (this.props.adFailsafeTimeoutMs !== void 0)
3665
4696
  config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
3666
4697
  this.player = new StormcloudVideoPlayer(config);
3667
- this.props.onMount?.(this);
4698
+ (_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
3668
4699
  await this.player.load();
3669
4700
  if (this.mounted) {
3670
- this.props.onReady?.();
4701
+ (_d = (_c = this.props).onReady) == null ? void 0 : _d.call(_c);
3671
4702
  }
3672
4703
  } catch (error) {
3673
4704
  if (this.mounted) {
3674
- this.props.onError?.(error);
4705
+ (_f = (_e = this.props).onError) == null ? void 0 : _f.call(_e, error);
3675
4706
  }
3676
4707
  }
3677
4708
  };
3678
4709
  this.play = () => {
4710
+ var _a, _b;
3679
4711
  if (this.props.videoElement) {
3680
4712
  this.props.videoElement.play();
3681
- this.props.onPlay?.();
4713
+ (_b = (_a = this.props).onPlay) == null ? void 0 : _b.call(_a);
3682
4714
  }
3683
4715
  };
3684
4716
  this.pause = () => {
4717
+ var _a, _b;
3685
4718
  if (this.props.videoElement) {
3686
4719
  this.props.videoElement.pause();
3687
- this.props.onPause?.();
4720
+ (_b = (_a = this.props).onPause) == null ? void 0 : _b.call(_a);
3688
4721
  }
3689
4722
  };
3690
4723
  this.stop = () => {
@@ -3779,37 +4812,44 @@ var FilePlayer = class extends import_react4.Component {
3779
4812
  this.mounted = false;
3780
4813
  this.ready = false;
3781
4814
  this.load = () => {
4815
+ var _a, _b;
3782
4816
  if (!this.props.videoElement || !this.props.src) return;
3783
4817
  const video = this.props.videoElement;
3784
4818
  const handleLoadedMetadata = () => {
4819
+ var _a2, _b2;
3785
4820
  if (this.mounted && !this.ready) {
3786
4821
  this.ready = true;
3787
- this.props.onReady?.();
4822
+ (_b2 = (_a2 = this.props).onReady) == null ? void 0 : _b2.call(_a2);
3788
4823
  }
3789
4824
  };
3790
4825
  const handlePlay = () => {
4826
+ var _a2, _b2;
3791
4827
  if (this.mounted) {
3792
- this.props.onPlay?.();
4828
+ (_b2 = (_a2 = this.props).onPlay) == null ? void 0 : _b2.call(_a2);
3793
4829
  }
3794
4830
  };
3795
4831
  const handlePause = () => {
4832
+ var _a2, _b2;
3796
4833
  if (this.mounted) {
3797
- this.props.onPause?.();
4834
+ (_b2 = (_a2 = this.props).onPause) == null ? void 0 : _b2.call(_a2);
3798
4835
  }
3799
4836
  };
3800
4837
  const handleEnded = () => {
4838
+ var _a2, _b2;
3801
4839
  if (this.mounted) {
3802
- this.props.onEnded?.();
4840
+ (_b2 = (_a2 = this.props).onEnded) == null ? void 0 : _b2.call(_a2);
3803
4841
  }
3804
4842
  };
3805
4843
  const handleError = (error) => {
4844
+ var _a2, _b2;
3806
4845
  if (this.mounted) {
3807
- this.props.onError?.(error);
4846
+ (_b2 = (_a2 = this.props).onError) == null ? void 0 : _b2.call(_a2, error);
3808
4847
  }
3809
4848
  };
3810
4849
  const handleLoadedData = () => {
4850
+ var _a2, _b2;
3811
4851
  if (this.mounted) {
3812
- this.props.onLoaded?.();
4852
+ (_b2 = (_a2 = this.props).onLoaded) == null ? void 0 : _b2.call(_a2);
3813
4853
  }
3814
4854
  };
3815
4855
  video.addEventListener("loadedmetadata", handleLoadedMetadata);
@@ -3828,7 +4868,7 @@ var FilePlayer = class extends import_react4.Component {
3828
4868
  if (this.props.preload !== void 0)
3829
4869
  video.preload = this.props.preload;
3830
4870
  if (this.props.poster !== void 0) video.poster = this.props.poster;
3831
- this.props.onMount?.(this);
4871
+ (_b = (_a = this.props).onMount) == null ? void 0 : _b.call(_a, this);
3832
4872
  return () => {
3833
4873
  video.removeEventListener("loadedmetadata", handleLoadedMetadata);
3834
4874
  video.removeEventListener("play", handlePlay);
@@ -3997,6 +5037,7 @@ var Player = class extends import_react5.Component {
3997
5037
  return this.player.getInternalPlayer(key);
3998
5038
  };
3999
5039
  this.progress = () => {
5040
+ var _a, _b;
4000
5041
  if (this.props.src && this.player && this.isReady) {
4001
5042
  const playedSeconds = this.getCurrentTime() || 0;
4002
5043
  const loadedSeconds = this.getSecondsLoaded();
@@ -4013,7 +5054,7 @@ var Player = class extends import_react5.Component {
4013
5054
  progress.loaded = loadedSeconds / duration;
4014
5055
  }
4015
5056
  if (progress.playedSeconds !== this.prevPlayed || progress.loadedSeconds !== this.prevLoaded) {
4016
- this.props.onProgress?.(progress);
5057
+ (_b = (_a = this.props).onProgress) == null ? void 0 : _b.call(_a, progress);
4017
5058
  }
4018
5059
  this.prevPlayed = progress.playedSeconds;
4019
5060
  this.prevLoaded = progress.loadedSeconds;
@@ -4049,10 +5090,10 @@ var Player = class extends import_react5.Component {
4049
5090
  if (this.player.setPlaybackRate && playbackRate !== 1) {
4050
5091
  this.player.setPlaybackRate(playbackRate);
4051
5092
  }
4052
- onStart?.();
5093
+ onStart == null ? void 0 : onStart();
4053
5094
  this.startOnPlay = false;
4054
5095
  }
4055
- onPlay?.();
5096
+ onPlay == null ? void 0 : onPlay();
4056
5097
  if (this.seekOnPlay) {
4057
5098
  this.seekTo(this.seekOnPlay);
4058
5099
  this.seekOnPlay = null;
@@ -4060,9 +5101,10 @@ var Player = class extends import_react5.Component {
4060
5101
  this.handleDurationCheck();
4061
5102
  };
4062
5103
  this.handlePause = (e) => {
5104
+ var _a, _b;
4063
5105
  this.isPlaying = false;
4064
5106
  if (!this.isLoading) {
4065
- this.props.onPause?.(e);
5107
+ (_b = (_a = this.props).onPause) == null ? void 0 : _b.call(_a, e);
4066
5108
  }
4067
5109
  };
4068
5110
  this.handleEnded = () => {
@@ -4072,19 +5114,21 @@ var Player = class extends import_react5.Component {
4072
5114
  }
4073
5115
  if (!loop) {
4074
5116
  this.isPlaying = false;
4075
- onEnded?.();
5117
+ onEnded == null ? void 0 : onEnded();
4076
5118
  }
4077
5119
  };
4078
5120
  this.handleError = (...args) => {
5121
+ var _a, _b;
4079
5122
  this.isLoading = false;
4080
- this.props.onError?.(args[0], args[1], args[2], args[3]);
5123
+ (_b = (_a = this.props).onError) == null ? void 0 : _b.call(_a, args[0], args[1], args[2], args[3]);
4081
5124
  };
4082
5125
  this.handleDurationCheck = () => {
5126
+ var _a, _b;
4083
5127
  clearTimeout(this.durationCheckTimeout);
4084
5128
  const duration = this.getDuration();
4085
5129
  if (duration) {
4086
5130
  if (!this.onDurationCalled) {
4087
- this.props.onDuration?.(duration);
5131
+ (_b = (_a = this.props).onDuration) == null ? void 0 : _b.call(_a, duration);
4088
5132
  this.onDurationCalled = true;
4089
5133
  }
4090
5134
  } else {
@@ -4283,7 +5327,8 @@ var createStormcloudPlayer = (playerList, fallback) => {
4283
5327
  return omit(this.props, SUPPORTED_PROPS);
4284
5328
  };
4285
5329
  this.handleReady = () => {
4286
- this.props.onReady?.(this);
5330
+ var _a2, _b;
5331
+ (_b = (_a2 = this.props).onReady) == null ? void 0 : _b.call(_a2, this);
4287
5332
  };
4288
5333
  this.seekTo = (fraction, type, keepPlaying) => {
4289
5334
  if (!this.player) return null;
@@ -4384,11 +5429,18 @@ var StormcloudPlayer_default = StormcloudPlayer;
4384
5429
  StormcloudVideoPlayer,
4385
5430
  StormcloudVideoPlayerComponent,
4386
5431
  canPlay,
5432
+ createHlsAdPlayer,
5433
+ createImaController,
4387
5434
  createStormcloudPlayer,
5435
+ detectBrowser,
5436
+ getBrowserConfigOverrides,
4388
5437
  getBrowserID,
4389
5438
  getClientInfo,
5439
+ getRecommendedAdPlayer,
5440
+ initializePolyfills,
4390
5441
  isMediaStream,
4391
5442
  lazy,
5443
+ logBrowserInfo,
4392
5444
  merge,
4393
5445
  omit,
4394
5446
  parseQuery,
@@ -4396,6 +5448,9 @@ var StormcloudPlayer_default = StormcloudPlayer;
4396
5448
  randomString,
4397
5449
  sendHeartbeat,
4398
5450
  sendInitialTracking,
5451
+ supportsFeature,
5452
+ supportsGoogleIMA,
5453
+ supportsModernJS,
4399
5454
  supportsWebKitPresentationMode
4400
5455
  });
4401
5456
  //# sourceMappingURL=index.cjs.map