stormcloud-video-player 0.2.12 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,195 @@ 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
+ if ("userAgentData" in navigator && navigator.userAgentData?.platform) {
87
+ return navigator.userAgentData.platform;
88
+ }
89
+ const ua = navigator.userAgent;
90
+ if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
91
+ return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
92
+ }
93
+ if (/Win/i.test(ua)) {
94
+ return "Win32";
95
+ }
96
+ if (/Linux/i.test(ua)) {
97
+ return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
98
+ }
99
+ if (/CrOS/i.test(ua)) {
100
+ return "CrOS";
101
+ }
102
+ return navigator.platform || "Unknown";
103
+ }
104
+ function detectBrowser() {
105
+ const ua = navigator.userAgent;
106
+ const platform = getPlatform();
107
+ let name = "Unknown";
108
+ let version = "0";
109
+ let majorVersion = 0;
110
+ let isSmartTV = false;
111
+ let isLegacyTV = false;
112
+ let supportsIMA = true;
113
+ let supportsModernJS2 = true;
114
+ let recommendedAdPlayer = "ima";
115
+ if (/Web0S|webOS/i.test(ua)) {
116
+ name = "LG WebOS";
117
+ isSmartTV = true;
118
+ const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
119
+ version = match && match[1] ? match[1] : "Unknown";
120
+ if (version !== "Unknown") {
121
+ const parts = version.split(".");
122
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
123
+ }
124
+ } else if (/Tizen/i.test(ua)) {
125
+ name = "Samsung Tizen";
126
+ isSmartTV = true;
127
+ const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
128
+ version = match && match[1] ? match[1] : "Unknown";
129
+ if (version !== "Unknown") {
130
+ const parts = version.split(".");
131
+ majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
132
+ }
133
+ } else if (/SMART-TV|SmartTV/i.test(ua)) {
134
+ name = "Smart TV";
135
+ isSmartTV = true;
136
+ } else if (/NetCast/i.test(ua)) {
137
+ name = "LG NetCast";
138
+ isSmartTV = true;
139
+ isLegacyTV = true;
140
+ } else if (/BRAVIA/i.test(ua)) {
141
+ name = "Sony BRAVIA";
142
+ isSmartTV = true;
143
+ }
144
+ const chromeVersion = getChromeVersion(ua);
145
+ const webkitVersion = getWebKitVersion(ua);
146
+ if (chromeVersion > 0) {
147
+ if (!isSmartTV) {
148
+ name = "Chrome";
149
+ version = chromeVersion.toString();
150
+ majorVersion = chromeVersion;
151
+ }
152
+ if (chromeVersion < 50) {
153
+ supportsIMA = false;
154
+ supportsModernJS2 = false;
155
+ isLegacyTV = true;
156
+ recommendedAdPlayer = "hls";
157
+ }
158
+ }
159
+ if (webkitVersion > 0 && webkitVersion < 600) {
160
+ supportsModernJS2 = false;
161
+ if (isSmartTV) {
162
+ isLegacyTV = true;
163
+ supportsIMA = false;
164
+ recommendedAdPlayer = "hls";
165
+ }
166
+ }
167
+ if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
168
+ supportsModernJS2 = false;
169
+ supportsIMA = false;
170
+ recommendedAdPlayer = "hls";
171
+ }
172
+ if (typeof URLSearchParams === "undefined") {
173
+ supportsModernJS2 = false;
174
+ }
175
+ return {
176
+ name,
177
+ version,
178
+ majorVersion,
179
+ isSmartTV,
180
+ isLegacyTV,
181
+ platform,
182
+ supportsIMA,
183
+ supportsModernJS: supportsModernJS2,
184
+ recommendedAdPlayer
185
+ };
186
+ }
187
+ function supportsGoogleIMA() {
188
+ const browser = detectBrowser();
189
+ if (browser.isLegacyTV) {
190
+ return false;
191
+ }
192
+ if (typeof document === "undefined" || typeof document.createElement !== "function") {
193
+ return false;
194
+ }
195
+ try {
196
+ const video = document.createElement("video");
197
+ if (!video) {
198
+ return false;
199
+ }
200
+ } catch (e) {
201
+ return false;
202
+ }
203
+ if (typeof Promise === "undefined") {
204
+ return false;
205
+ }
206
+ return browser.supportsIMA;
207
+ }
208
+ function getRecommendedAdPlayer() {
209
+ const browser = detectBrowser();
210
+ return browser.recommendedAdPlayer;
211
+ }
212
+ function supportsModernJS() {
213
+ try {
214
+ 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";
215
+ } catch (e) {
216
+ return false;
217
+ }
218
+ }
219
+ function logBrowserInfo(debug = false) {
220
+ if (!debug) return;
221
+ const browser = detectBrowser();
222
+ const imaSupport = supportsGoogleIMA();
223
+ console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
224
+ browser: `${browser.name} ${browser.version}`,
225
+ platform: browser.platform,
226
+ isSmartTV: browser.isSmartTV,
227
+ isLegacyTV: browser.isLegacyTV,
228
+ supportsIMA: imaSupport,
229
+ supportsModernJS: browser.supportsModernJS,
230
+ recommendedAdPlayer: browser.recommendedAdPlayer,
231
+ userAgent: navigator.userAgent
232
+ });
233
+ }
234
+ function getBrowserConfigOverrides() {
235
+ const browser = detectBrowser();
236
+ const overrides = {};
237
+ if (browser.isLegacyTV || !browser.supportsIMA) {
238
+ overrides.adPlayerType = "hls";
239
+ }
240
+ if (browser.isSmartTV) {
241
+ overrides.allowNativeHls = true;
242
+ }
243
+ return overrides;
244
+ }
245
+ function supportsFeature(feature) {
246
+ switch (feature) {
247
+ case "ima":
248
+ return supportsGoogleIMA();
249
+ case "urlsearchparams":
250
+ return typeof URLSearchParams !== "undefined";
251
+ case "textencoder":
252
+ return typeof TextEncoder !== "undefined";
253
+ case "promises":
254
+ return typeof Promise !== "undefined";
255
+ case "fetch":
256
+ return typeof fetch !== "undefined";
257
+ case "crypto":
258
+ return typeof crypto !== "undefined" && typeof crypto.subtle !== "undefined";
259
+ default:
260
+ return false;
261
+ }
262
+ }
65
263
 
66
264
  // src/sdk/ima.ts
67
265
  function createImaController(video, options) {
@@ -79,6 +277,14 @@ function createImaController(video, options) {
79
277
  }
80
278
  }
81
279
  function ensureImaLoaded() {
280
+ if (!supportsGoogleIMA()) {
281
+ console.warn(
282
+ "[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
283
+ );
284
+ return Promise.reject(
285
+ new Error("Google IMA SDK not supported on this browser")
286
+ );
287
+ }
82
288
  try {
83
289
  const frameEl = window.frameElement;
84
290
  const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
@@ -574,6 +780,456 @@ function createImaController(video, options) {
574
780
  };
575
781
  }
576
782
 
783
+ // src/sdk/hlsAdPlayer.ts
784
+ var import_hls = __toESM(require("hls.js"), 1);
785
+ function createHlsAdPlayer(contentVideo, options) {
786
+ let adPlaying = false;
787
+ let originalMutedState = false;
788
+ const listeners = /* @__PURE__ */ new Map();
789
+ const licenseKey = options?.licenseKey;
790
+ let adVideoElement;
791
+ let adHls;
792
+ let adContainerEl;
793
+ let currentAd;
794
+ let sessionId;
795
+ let trackingFired = {
796
+ impression: false,
797
+ start: false,
798
+ firstQuartile: false,
799
+ midpoint: false,
800
+ thirdQuartile: false,
801
+ complete: false
802
+ };
803
+ function emit(event, payload) {
804
+ const set = listeners.get(event);
805
+ if (!set) return;
806
+ for (const fn of Array.from(set)) {
807
+ try {
808
+ fn(payload);
809
+ } catch (error) {
810
+ console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
811
+ }
812
+ }
813
+ }
814
+ function generateSessionId() {
815
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
816
+ }
817
+ function fireTrackingPixels(urls) {
818
+ if (!urls || urls.length === 0) return;
819
+ urls.forEach((url) => {
820
+ try {
821
+ let trackingUrl = url;
822
+ if (sessionId) {
823
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
824
+ }
825
+ if (licenseKey) {
826
+ trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
827
+ }
828
+ const img = new Image(1, 1);
829
+ img.src = trackingUrl;
830
+ console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
831
+ } catch (error) {
832
+ console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
833
+ }
834
+ });
835
+ }
836
+ function parseVastXml(xmlString) {
837
+ try {
838
+ const parser = new DOMParser();
839
+ const xmlDoc = parser.parseFromString(xmlString, "text/xml");
840
+ const parserError = xmlDoc.querySelector("parsererror");
841
+ if (parserError) {
842
+ console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
843
+ return null;
844
+ }
845
+ const adElement = xmlDoc.querySelector("Ad");
846
+ if (!adElement) {
847
+ console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
848
+ return null;
849
+ }
850
+ const adId = adElement.getAttribute("id") || "unknown";
851
+ const title = xmlDoc.querySelector("AdTitle")?.textContent || "Ad";
852
+ const durationText = xmlDoc.querySelector("Duration")?.textContent || "00:00:30";
853
+ const durationParts = durationText.split(":");
854
+ const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
855
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
856
+ const mediaFiles = [];
857
+ mediaFileElements.forEach((mf) => {
858
+ const type = mf.getAttribute("type") || "";
859
+ if (type === "application/x-mpegURL" || type.includes("m3u8")) {
860
+ const bitrateAttr = mf.getAttribute("bitrate");
861
+ const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
862
+ mediaFiles.push({
863
+ url: mf.textContent?.trim() || "",
864
+ type,
865
+ width: parseInt(mf.getAttribute("width") || "1920", 10),
866
+ height: parseInt(mf.getAttribute("height") || "1080", 10),
867
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
868
+ });
869
+ }
870
+ });
871
+ if (mediaFiles.length === 0) {
872
+ console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
873
+ return null;
874
+ }
875
+ const trackingUrls = {
876
+ impression: [],
877
+ start: [],
878
+ firstQuartile: [],
879
+ midpoint: [],
880
+ thirdQuartile: [],
881
+ complete: [],
882
+ mute: [],
883
+ unmute: [],
884
+ pause: [],
885
+ resume: [],
886
+ fullscreen: [],
887
+ exitFullscreen: [],
888
+ skip: [],
889
+ error: []
890
+ };
891
+ xmlDoc.querySelectorAll("Impression").forEach((el) => {
892
+ const url = el.textContent?.trim();
893
+ if (url) trackingUrls.impression.push(url);
894
+ });
895
+ xmlDoc.querySelectorAll("Tracking").forEach((el) => {
896
+ const event = el.getAttribute("event");
897
+ const url = el.textContent?.trim();
898
+ if (event && url) {
899
+ const eventKey = event;
900
+ if (trackingUrls[eventKey]) {
901
+ trackingUrls[eventKey].push(url);
902
+ }
903
+ }
904
+ });
905
+ const clickThrough = xmlDoc.querySelector("ClickThrough")?.textContent?.trim();
906
+ return {
907
+ id: adId,
908
+ title,
909
+ duration,
910
+ mediaFiles,
911
+ trackingUrls,
912
+ clickThrough
913
+ };
914
+ } catch (error) {
915
+ console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
916
+ return null;
917
+ }
918
+ }
919
+ function createAdVideoElement() {
920
+ const video = document.createElement("video");
921
+ video.style.position = "absolute";
922
+ video.style.left = "0";
923
+ video.style.top = "0";
924
+ video.style.width = "100%";
925
+ video.style.height = "100%";
926
+ video.style.objectFit = "contain";
927
+ video.style.backgroundColor = "#000";
928
+ video.playsInline = true;
929
+ video.muted = false;
930
+ return video;
931
+ }
932
+ function setupAdEventListeners() {
933
+ if (!adVideoElement || !currentAd) return;
934
+ adVideoElement.addEventListener("timeupdate", () => {
935
+ if (!currentAd || !adVideoElement) return;
936
+ const progress = adVideoElement.currentTime / currentAd.duration;
937
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
938
+ trackingFired.firstQuartile = true;
939
+ fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
940
+ }
941
+ if (progress >= 0.5 && !trackingFired.midpoint) {
942
+ trackingFired.midpoint = true;
943
+ fireTrackingPixels(currentAd.trackingUrls.midpoint);
944
+ }
945
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
946
+ trackingFired.thirdQuartile = true;
947
+ fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
948
+ }
949
+ });
950
+ adVideoElement.addEventListener("playing", () => {
951
+ if (!currentAd || trackingFired.start) return;
952
+ trackingFired.start = true;
953
+ fireTrackingPixels(currentAd.trackingUrls.start);
954
+ console.log("[HlsAdPlayer] Ad started playing");
955
+ });
956
+ adVideoElement.addEventListener("ended", () => {
957
+ if (!currentAd || trackingFired.complete) return;
958
+ trackingFired.complete = true;
959
+ fireTrackingPixels(currentAd.trackingUrls.complete);
960
+ console.log("[HlsAdPlayer] Ad completed");
961
+ handleAdComplete();
962
+ });
963
+ adVideoElement.addEventListener("error", (e) => {
964
+ console.error("[HlsAdPlayer] Ad video error:", e);
965
+ if (currentAd) {
966
+ fireTrackingPixels(currentAd.trackingUrls.error);
967
+ }
968
+ handleAdError();
969
+ });
970
+ adVideoElement.addEventListener("volumechange", () => {
971
+ if (!currentAd) return;
972
+ if (adVideoElement.muted) {
973
+ fireTrackingPixels(currentAd.trackingUrls.mute);
974
+ } else {
975
+ fireTrackingPixels(currentAd.trackingUrls.unmute);
976
+ }
977
+ });
978
+ adVideoElement.addEventListener("pause", () => {
979
+ if (currentAd && !adVideoElement.ended) {
980
+ fireTrackingPixels(currentAd.trackingUrls.pause);
981
+ }
982
+ });
983
+ adVideoElement.addEventListener("play", () => {
984
+ if (currentAd && adVideoElement.currentTime > 0) {
985
+ fireTrackingPixels(currentAd.trackingUrls.resume);
986
+ }
987
+ });
988
+ }
989
+ function handleAdComplete() {
990
+ console.log("[HlsAdPlayer] Handling ad completion");
991
+ adPlaying = false;
992
+ contentVideo.muted = originalMutedState;
993
+ if (adContainerEl) {
994
+ adContainerEl.style.display = "none";
995
+ adContainerEl.style.pointerEvents = "none";
996
+ }
997
+ if (!options?.continueLiveStreamDuringAds) {
998
+ contentVideo.play().catch(() => {
999
+ });
1000
+ console.log("[HlsAdPlayer] Content resumed (VOD mode)");
1001
+ } else {
1002
+ console.log("[HlsAdPlayer] Content unmuted (Live mode)");
1003
+ }
1004
+ emit("content_resume");
1005
+ emit("all_ads_completed");
1006
+ }
1007
+ function handleAdError() {
1008
+ console.log("[HlsAdPlayer] Handling ad error");
1009
+ adPlaying = false;
1010
+ contentVideo.muted = originalMutedState;
1011
+ if (adContainerEl) {
1012
+ adContainerEl.style.display = "none";
1013
+ adContainerEl.style.pointerEvents = "none";
1014
+ }
1015
+ if (!options?.continueLiveStreamDuringAds) {
1016
+ if (contentVideo.paused) {
1017
+ contentVideo.play().catch(() => {
1018
+ });
1019
+ }
1020
+ }
1021
+ emit("ad_error");
1022
+ }
1023
+ return {
1024
+ initialize() {
1025
+ console.log("[HlsAdPlayer] Initializing");
1026
+ if (!adContainerEl) {
1027
+ const container = document.createElement("div");
1028
+ container.style.position = "absolute";
1029
+ container.style.left = "0";
1030
+ container.style.top = "0";
1031
+ container.style.right = "0";
1032
+ container.style.bottom = "0";
1033
+ container.style.display = "none";
1034
+ container.style.alignItems = "center";
1035
+ container.style.justifyContent = "center";
1036
+ container.style.pointerEvents = "none";
1037
+ container.style.zIndex = "2";
1038
+ container.style.backgroundColor = "#000";
1039
+ contentVideo.parentElement?.appendChild(container);
1040
+ adContainerEl = container;
1041
+ }
1042
+ },
1043
+ async requestAds(vastTagUrl) {
1044
+ console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
1045
+ if (adPlaying) {
1046
+ console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
1047
+ return Promise.reject(new Error("Ad already playing"));
1048
+ }
1049
+ try {
1050
+ sessionId = generateSessionId();
1051
+ const response = await fetch(vastTagUrl);
1052
+ if (!response.ok) {
1053
+ throw new Error(`Failed to fetch VAST: ${response.statusText}`);
1054
+ }
1055
+ const vastXml = await response.text();
1056
+ console.log("[HlsAdPlayer] VAST XML received");
1057
+ const ad = parseVastXml(vastXml);
1058
+ if (!ad) {
1059
+ throw new Error("Failed to parse VAST XML or no ads available");
1060
+ }
1061
+ currentAd = ad;
1062
+ console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
1063
+ fireTrackingPixels(ad.trackingUrls.impression);
1064
+ trackingFired.impression = true;
1065
+ return Promise.resolve();
1066
+ } catch (error) {
1067
+ console.error("[HlsAdPlayer] Error requesting ads:", error);
1068
+ emit("ad_error");
1069
+ return Promise.reject(error);
1070
+ }
1071
+ },
1072
+ async play() {
1073
+ if (!currentAd) {
1074
+ console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
1075
+ return Promise.reject(new Error("No ad loaded"));
1076
+ }
1077
+ console.log("[HlsAdPlayer] Starting ad playback");
1078
+ try {
1079
+ if (!adVideoElement) {
1080
+ adVideoElement = createAdVideoElement();
1081
+ adContainerEl?.appendChild(adVideoElement);
1082
+ setupAdEventListeners();
1083
+ }
1084
+ trackingFired = {
1085
+ impression: trackingFired.impression,
1086
+ start: false,
1087
+ firstQuartile: false,
1088
+ midpoint: false,
1089
+ thirdQuartile: false,
1090
+ complete: false
1091
+ };
1092
+ if (!options?.continueLiveStreamDuringAds) {
1093
+ contentVideo.pause();
1094
+ console.log("[HlsAdPlayer] Content paused (VOD mode)");
1095
+ } else {
1096
+ console.log("[HlsAdPlayer] Content continues (Live mode)");
1097
+ }
1098
+ contentVideo.muted = true;
1099
+ adPlaying = true;
1100
+ if (adContainerEl) {
1101
+ adContainerEl.style.display = "flex";
1102
+ adContainerEl.style.pointerEvents = "auto";
1103
+ }
1104
+ emit("content_pause");
1105
+ const mediaFile = currentAd.mediaFiles[0];
1106
+ if (!mediaFile) {
1107
+ throw new Error("No media file available for ad");
1108
+ }
1109
+ console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
1110
+ if (import_hls.default.isSupported()) {
1111
+ if (adHls) {
1112
+ adHls.destroy();
1113
+ }
1114
+ adHls = new import_hls.default({
1115
+ enableWorker: true,
1116
+ lowLatencyMode: false
1117
+ });
1118
+ adHls.loadSource(mediaFile.url);
1119
+ adHls.attachMedia(adVideoElement);
1120
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
1121
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1122
+ adVideoElement.play().catch((error) => {
1123
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1124
+ handleAdError();
1125
+ });
1126
+ });
1127
+ adHls.on(import_hls.default.Events.ERROR, (event, data) => {
1128
+ console.error("[HlsAdPlayer] HLS error:", data);
1129
+ if (data.fatal) {
1130
+ handleAdError();
1131
+ }
1132
+ });
1133
+ } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1134
+ adVideoElement.src = mediaFile.url;
1135
+ adVideoElement.play().catch((error) => {
1136
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1137
+ handleAdError();
1138
+ });
1139
+ } else {
1140
+ throw new Error("HLS not supported");
1141
+ }
1142
+ return Promise.resolve();
1143
+ } catch (error) {
1144
+ console.error("[HlsAdPlayer] Error playing ad:", error);
1145
+ handleAdError();
1146
+ return Promise.reject(error);
1147
+ }
1148
+ },
1149
+ async stop() {
1150
+ console.log("[HlsAdPlayer] Stopping ad");
1151
+ adPlaying = false;
1152
+ contentVideo.muted = originalMutedState;
1153
+ if (adContainerEl) {
1154
+ adContainerEl.style.display = "none";
1155
+ adContainerEl.style.pointerEvents = "none";
1156
+ }
1157
+ if (adHls) {
1158
+ adHls.destroy();
1159
+ adHls = void 0;
1160
+ }
1161
+ if (adVideoElement) {
1162
+ adVideoElement.pause();
1163
+ adVideoElement.src = "";
1164
+ }
1165
+ if (!options?.continueLiveStreamDuringAds) {
1166
+ contentVideo.play().catch(() => {
1167
+ });
1168
+ }
1169
+ currentAd = void 0;
1170
+ },
1171
+ destroy() {
1172
+ console.log("[HlsAdPlayer] Destroying");
1173
+ adPlaying = false;
1174
+ contentVideo.muted = originalMutedState;
1175
+ if (adHls) {
1176
+ adHls.destroy();
1177
+ adHls = void 0;
1178
+ }
1179
+ if (adVideoElement) {
1180
+ adVideoElement.pause();
1181
+ adVideoElement.src = "";
1182
+ adVideoElement.remove();
1183
+ adVideoElement = void 0;
1184
+ }
1185
+ if (adContainerEl?.parentElement) {
1186
+ adContainerEl.parentElement.removeChild(adContainerEl);
1187
+ }
1188
+ adContainerEl = void 0;
1189
+ currentAd = void 0;
1190
+ listeners.clear();
1191
+ },
1192
+ isAdPlaying() {
1193
+ return adPlaying;
1194
+ },
1195
+ resize(width, height) {
1196
+ console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
1197
+ if (adContainerEl) {
1198
+ adContainerEl.style.width = `${width}px`;
1199
+ adContainerEl.style.height = `${height}px`;
1200
+ }
1201
+ if (adVideoElement) {
1202
+ adVideoElement.style.width = `${width}px`;
1203
+ adVideoElement.style.height = `${height}px`;
1204
+ }
1205
+ },
1206
+ on(event, listener) {
1207
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
1208
+ listeners.get(event).add(listener);
1209
+ },
1210
+ off(event, listener) {
1211
+ listeners.get(event)?.delete(listener);
1212
+ },
1213
+ updateOriginalMutedState(muted) {
1214
+ originalMutedState = muted;
1215
+ },
1216
+ getOriginalMutedState() {
1217
+ return originalMutedState;
1218
+ },
1219
+ setAdVolume(volume) {
1220
+ if (adVideoElement && adPlaying) {
1221
+ adVideoElement.volume = Math.max(0, Math.min(1, volume));
1222
+ }
1223
+ },
1224
+ getAdVolume() {
1225
+ if (adVideoElement && adPlaying) {
1226
+ return adVideoElement.volume;
1227
+ }
1228
+ return 1;
1229
+ }
1230
+ };
1231
+ }
1232
+
577
1233
  // src/utils/tracking.ts
578
1234
  var cachedBrowserId = null;
579
1235
  function getClientInfo() {
@@ -823,6 +1479,215 @@ async function sendHeartbeat(licenseKey) {
823
1479
  }
824
1480
  }
825
1481
 
1482
+ // src/utils/polyfills.ts
1483
+ function polyfillURLSearchParams() {
1484
+ if (typeof URLSearchParams !== "undefined") {
1485
+ return;
1486
+ }
1487
+ class URLSearchParamsPolyfill {
1488
+ constructor(init) {
1489
+ this.params = /* @__PURE__ */ new Map();
1490
+ if (typeof init === "string") {
1491
+ this.parseQueryString(init);
1492
+ } else if (init instanceof URLSearchParamsPolyfill) {
1493
+ init.forEach((value, key) => {
1494
+ this.append(key, value);
1495
+ });
1496
+ }
1497
+ }
1498
+ parseQueryString(query) {
1499
+ const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
1500
+ if (!cleanQuery) return;
1501
+ cleanQuery.split("&").forEach((param) => {
1502
+ const [key, value] = param.split("=");
1503
+ if (key) {
1504
+ const decodedKey = this.safeDecodeURIComponent(key);
1505
+ const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
1506
+ this.append(decodedKey, decodedValue);
1507
+ }
1508
+ });
1509
+ }
1510
+ safeDecodeURIComponent(str) {
1511
+ try {
1512
+ return decodeURIComponent(str.replace(/\+/g, " "));
1513
+ } catch (e) {
1514
+ return str;
1515
+ }
1516
+ }
1517
+ append(name, value) {
1518
+ const values = this.params.get(name) || [];
1519
+ values.push(String(value));
1520
+ this.params.set(name, values);
1521
+ }
1522
+ delete(name) {
1523
+ this.params.delete(name);
1524
+ }
1525
+ get(name) {
1526
+ const values = this.params.get(name);
1527
+ return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
1528
+ }
1529
+ getAll(name) {
1530
+ return this.params.get(name) || [];
1531
+ }
1532
+ has(name) {
1533
+ return this.params.has(name);
1534
+ }
1535
+ set(name, value) {
1536
+ this.params.set(name, [String(value)]);
1537
+ }
1538
+ forEach(callback) {
1539
+ this.params.forEach((values, key) => {
1540
+ values.forEach((value) => {
1541
+ callback(value, key, this);
1542
+ });
1543
+ });
1544
+ }
1545
+ toString() {
1546
+ const parts = [];
1547
+ this.params.forEach((values, key) => {
1548
+ values.forEach((value) => {
1549
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
1550
+ });
1551
+ });
1552
+ return parts.join("&");
1553
+ }
1554
+ }
1555
+ window.URLSearchParams = URLSearchParamsPolyfill;
1556
+ }
1557
+ function polyfillTextEncoder() {
1558
+ if (typeof TextEncoder !== "undefined") {
1559
+ return;
1560
+ }
1561
+ class TextEncoderPolyfill {
1562
+ constructor() {
1563
+ this.encoding = "utf-8";
1564
+ }
1565
+ encode(str) {
1566
+ const utf8 = [];
1567
+ for (let i = 0; i < str.length; i++) {
1568
+ let charcode = str.charCodeAt(i);
1569
+ if (charcode < 128) {
1570
+ utf8.push(charcode);
1571
+ } else if (charcode < 2048) {
1572
+ utf8.push(192 | charcode >> 6, 128 | charcode & 63);
1573
+ } else if (charcode < 55296 || charcode >= 57344) {
1574
+ utf8.push(
1575
+ 224 | charcode >> 12,
1576
+ 128 | charcode >> 6 & 63,
1577
+ 128 | charcode & 63
1578
+ );
1579
+ } else {
1580
+ i++;
1581
+ charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
1582
+ utf8.push(
1583
+ 240 | charcode >> 18,
1584
+ 128 | charcode >> 12 & 63,
1585
+ 128 | charcode >> 6 & 63,
1586
+ 128 | charcode & 63
1587
+ );
1588
+ }
1589
+ }
1590
+ return new Uint8Array(utf8);
1591
+ }
1592
+ }
1593
+ window.TextEncoder = TextEncoderPolyfill;
1594
+ }
1595
+ function polyfillPromiseFinally() {
1596
+ if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
1597
+ Promise.prototype.finally = function(callback) {
1598
+ const constructor = this.constructor;
1599
+ return this.then(
1600
+ (value) => constructor.resolve(callback()).then(() => value),
1601
+ (reason) => constructor.resolve(callback()).then(() => {
1602
+ throw reason;
1603
+ })
1604
+ );
1605
+ };
1606
+ }
1607
+ }
1608
+ function polyfillObjectAssign() {
1609
+ if (typeof Object.assign !== "function") {
1610
+ Object.assign = function(target, ...sources) {
1611
+ if (target == null) {
1612
+ throw new TypeError("Cannot convert undefined or null to object");
1613
+ }
1614
+ const to = Object(target);
1615
+ for (let i = 0; i < sources.length; i++) {
1616
+ const nextSource = sources[i];
1617
+ if (nextSource != null) {
1618
+ for (const nextKey in nextSource) {
1619
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
1620
+ to[nextKey] = nextSource[nextKey];
1621
+ }
1622
+ }
1623
+ }
1624
+ }
1625
+ return to;
1626
+ };
1627
+ }
1628
+ }
1629
+ function polyfillArrayFrom() {
1630
+ if (!Array.from) {
1631
+ Array.from = function(arrayLike, mapFn, thisArg) {
1632
+ const items = Object(arrayLike);
1633
+ if (arrayLike == null) {
1634
+ throw new TypeError("Array.from requires an array-like object");
1635
+ }
1636
+ const len = items.length >>> 0;
1637
+ const result = new Array(len);
1638
+ for (let i = 0; i < len; i++) {
1639
+ if (mapFn) {
1640
+ result[i] = mapFn.call(thisArg, items[i], i);
1641
+ } else {
1642
+ result[i] = items[i];
1643
+ }
1644
+ }
1645
+ return result;
1646
+ };
1647
+ }
1648
+ }
1649
+ function polyfillStringStartsWith() {
1650
+ if (!String.prototype.startsWith) {
1651
+ String.prototype.startsWith = function(search, pos) {
1652
+ pos = !pos || pos < 0 ? 0 : +pos;
1653
+ return this.substring(pos, pos + search.length) === search;
1654
+ };
1655
+ }
1656
+ }
1657
+ function polyfillStringEndsWith() {
1658
+ if (!String.prototype.endsWith) {
1659
+ String.prototype.endsWith = function(search, length) {
1660
+ if (length === void 0 || length > this.length) {
1661
+ length = this.length;
1662
+ }
1663
+ return this.substring(length - search.length, length) === search;
1664
+ };
1665
+ }
1666
+ }
1667
+ function polyfillStringIncludes() {
1668
+ if (!String.prototype.includes) {
1669
+ String.prototype.includes = function(search, start) {
1670
+ if (typeof start !== "number") {
1671
+ start = 0;
1672
+ }
1673
+ if (start + search.length > this.length) {
1674
+ return false;
1675
+ }
1676
+ return this.indexOf(search, start) !== -1;
1677
+ };
1678
+ }
1679
+ }
1680
+ function initializePolyfills() {
1681
+ polyfillObjectAssign();
1682
+ polyfillArrayFrom();
1683
+ polyfillStringStartsWith();
1684
+ polyfillStringEndsWith();
1685
+ polyfillStringIncludes();
1686
+ polyfillURLSearchParams();
1687
+ polyfillTextEncoder();
1688
+ polyfillPromiseFinally();
1689
+ }
1690
+
826
1691
  // src/player/StormcloudVideoPlayer.ts
827
1692
  var StormcloudVideoPlayer = class {
828
1693
  constructor(config) {
@@ -835,11 +1700,40 @@ var StormcloudVideoPlayer = class {
835
1700
  this.totalAdsInBreak = 0;
836
1701
  this.showAds = false;
837
1702
  this.isLiveStream = false;
838
- this.config = config;
1703
+ initializePolyfills();
1704
+ const browserOverrides = getBrowserConfigOverrides();
1705
+ this.config = { ...config, ...browserOverrides };
839
1706
  this.video = config.videoElement;
840
- this.ima = createImaController(this.video, {
841
- continueLiveStreamDuringAds: false
842
- });
1707
+ logBrowserInfo(config.debugAdTiming);
1708
+ this.ima = this.createAdPlayer(false);
1709
+ }
1710
+ createAdPlayer(continueLiveStreamDuringAds) {
1711
+ const vastMode = this.config.vastMode || "default";
1712
+ let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
1713
+ if (adPlayerType === "ima" && !supportsGoogleIMA()) {
1714
+ if (this.config.debugAdTiming) {
1715
+ console.warn(
1716
+ "[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
1717
+ );
1718
+ }
1719
+ adPlayerType = "hls";
1720
+ }
1721
+ if (adPlayerType === "hls") {
1722
+ if (this.config.debugAdTiming) {
1723
+ console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
1724
+ }
1725
+ return createHlsAdPlayer(this.video, {
1726
+ continueLiveStreamDuringAds,
1727
+ ...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {}
1728
+ });
1729
+ } else {
1730
+ if (this.config.debugAdTiming) {
1731
+ console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
1732
+ }
1733
+ return createImaController(this.video, {
1734
+ continueLiveStreamDuringAds
1735
+ });
1736
+ }
843
1737
  }
844
1738
  async load() {
845
1739
  if (!this.attached) {
@@ -870,9 +1764,7 @@ var StormcloudVideoPlayer = class {
870
1764
  );
871
1765
  }
872
1766
  this.ima.destroy();
873
- this.ima = createImaController(this.video, {
874
- continueLiveStreamDuringAds: false
875
- });
1767
+ this.ima = this.createAdPlayer(false);
876
1768
  this.ima.initialize();
877
1769
  if (this.config.autoplay) {
878
1770
  await this.video.play()?.catch(() => {
@@ -880,7 +1772,7 @@ var StormcloudVideoPlayer = class {
880
1772
  }
881
1773
  return;
882
1774
  }
883
- this.hls = new import_hls.default({
1775
+ this.hls = new import_hls2.default({
884
1776
  enableWorker: true,
885
1777
  backBufferLength: 30,
886
1778
  liveDurationInfinity: true,
@@ -888,10 +1780,10 @@ var StormcloudVideoPlayer = class {
888
1780
  maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
889
1781
  ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
890
1782
  });
891
- this.hls.on(import_hls.default.Events.MEDIA_ATTACHED, () => {
1783
+ this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
892
1784
  this.hls?.loadSource(this.config.src);
893
1785
  });
894
- this.hls.on(import_hls.default.Events.MANIFEST_PARSED, async (_, data) => {
1786
+ this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
895
1787
  this.isLiveStream = this.hls?.levels?.some(
896
1788
  (level) => level?.details?.live === true || level?.details?.type === "LIVE"
897
1789
  ) ?? false;
@@ -904,16 +1796,14 @@ var StormcloudVideoPlayer = class {
904
1796
  });
905
1797
  }
906
1798
  this.ima.destroy();
907
- this.ima = createImaController(this.video, {
908
- continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
909
- });
1799
+ this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
910
1800
  this.ima.initialize();
911
1801
  if (this.config.autoplay) {
912
1802
  await this.video.play()?.catch(() => {
913
1803
  });
914
1804
  }
915
1805
  });
916
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
1806
+ this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
917
1807
  const id3Tags = (data?.samples || []).map((s) => ({
918
1808
  key: "ID3",
919
1809
  value: s?.data,
@@ -921,7 +1811,7 @@ var StormcloudVideoPlayer = class {
921
1811
  }));
922
1812
  id3Tags.forEach((tag) => this.onId3Tag(tag));
923
1813
  });
924
- this.hls.on(import_hls.default.Events.FRAG_CHANGED, (_evt, data) => {
1814
+ this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
925
1815
  const frag = data?.frag;
926
1816
  const tagList = frag?.tagList;
927
1817
  if (!Array.isArray(tagList)) return;
@@ -981,13 +1871,13 @@ var StormcloudVideoPlayer = class {
981
1871
  }
982
1872
  }
983
1873
  });
984
- this.hls.on(import_hls.default.Events.ERROR, (_evt, data) => {
1874
+ this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
985
1875
  if (data?.fatal) {
986
1876
  switch (data.type) {
987
- case import_hls.default.ErrorTypes.NETWORK_ERROR:
1877
+ case import_hls2.default.ErrorTypes.NETWORK_ERROR:
988
1878
  this.hls?.startLoad();
989
1879
  break;
990
- case import_hls.default.ErrorTypes.MEDIA_ERROR:
1880
+ case import_hls2.default.ErrorTypes.MEDIA_ERROR:
991
1881
  this.hls?.recoverMediaError();
992
1882
  break;
993
1883
  default:
@@ -1498,6 +2388,42 @@ var StormcloudVideoPlayer = class {
1498
2388
  }
1499
2389
  }
1500
2390
  async fetchAdConfiguration() {
2391
+ const vastMode = this.config.vastMode || "default";
2392
+ if (this.config.debugAdTiming) {
2393
+ console.log(
2394
+ "[StormcloudVideoPlayer] VAST mode:",
2395
+ vastMode
2396
+ );
2397
+ }
2398
+ if (vastMode === "adstorm") {
2399
+ if (!this.config.licenseKey) {
2400
+ if (this.config.debugAdTiming) {
2401
+ console.warn(
2402
+ "[StormcloudVideoPlayer] AdStorm mode requires a license key"
2403
+ );
2404
+ }
2405
+ return;
2406
+ }
2407
+ const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
2408
+ this.apiVastTagUrl = vastEndpoint;
2409
+ if (this.config.debugAdTiming) {
2410
+ console.log(
2411
+ "[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
2412
+ vastEndpoint
2413
+ );
2414
+ }
2415
+ return;
2416
+ }
2417
+ if (this.config.vastTagUrl) {
2418
+ this.apiVastTagUrl = this.config.vastTagUrl;
2419
+ if (this.config.debugAdTiming) {
2420
+ console.log(
2421
+ "[StormcloudVideoPlayer] Using custom VAST tag URL:",
2422
+ this.apiVastTagUrl
2423
+ );
2424
+ }
2425
+ return;
2426
+ }
1501
2427
  const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
1502
2428
  if (this.config.debugAdTiming) {
1503
2429
  console.log(
@@ -1511,7 +2437,12 @@ var StormcloudVideoPlayer = class {
1511
2437
  }
1512
2438
  const response = await fetch(apiUrl, { headers });
1513
2439
  if (!response.ok) {
1514
- throw new Error(`Failed to fetch ad configuration: ${response.status}`);
2440
+ if (this.config.debugAdTiming) {
2441
+ console.warn(
2442
+ `[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
2443
+ );
2444
+ }
2445
+ return;
1515
2446
  }
1516
2447
  const data = await response.json();
1517
2448
  const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
@@ -1519,17 +2450,16 @@ var StormcloudVideoPlayer = class {
1519
2450
  this.apiVastTagUrl = decodeURIComponent(imaPayload);
1520
2451
  if (this.config.debugAdTiming) {
1521
2452
  console.log(
1522
- "[StormcloudVideoPlayer] Extracted VAST tag URL:",
2453
+ "[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
1523
2454
  this.apiVastTagUrl
1524
2455
  );
1525
2456
  }
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
- });
2457
+ } else {
2458
+ if (this.config.debugAdTiming) {
2459
+ console.warn(
2460
+ "[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
2461
+ );
2462
+ }
1533
2463
  }
1534
2464
  }
1535
2465
  getCurrentAdIndex() {
@@ -1573,23 +2503,14 @@ var StormcloudVideoPlayer = class {
1573
2503
  );
1574
2504
  const tags = this.selectVastTagsForBreak(scheduled);
1575
2505
  let vastTagUrl;
1576
- let adsNumber = 1;
1577
2506
  if (this.apiVastTagUrl) {
1578
2507
  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);
2508
+ this.adPodQueue = [];
1588
2509
  this.currentAdIndex = 0;
1589
- this.totalAdsInBreak = adsNumber;
2510
+ this.totalAdsInBreak = 1;
1590
2511
  if (this.config.debugAdTiming) {
1591
2512
  console.log(
1592
- `[StormcloudVideoPlayer] Using API VAST tag with ${adsNumber} ads:`,
2513
+ "[StormcloudVideoPlayer] Using VAST endpoint:",
1593
2514
  vastTagUrl
1594
2515
  );
1595
2516
  }
@@ -3534,27 +4455,30 @@ var parseQuery = (url) => {
3534
4455
  const query = {};
3535
4456
  const queryString = url.split("?")[1] || "";
3536
4457
  if (!queryString) return query;
4458
+ const manualParse = (qs) => {
4459
+ qs.split("&").forEach((param) => {
4460
+ const [key, value] = param.split("=");
4461
+ if (key) {
4462
+ try {
4463
+ query[decodeURIComponent(key)] = value ? decodeURIComponent(value.replace(/\+/g, " ")) : "";
4464
+ } catch (e) {
4465
+ query[key] = value || "";
4466
+ }
4467
+ }
4468
+ });
4469
+ };
3537
4470
  if (typeof URLSearchParams !== "undefined") {
3538
4471
  try {
3539
4472
  const params = new URLSearchParams(queryString);
3540
4473
  params.forEach((value, key) => {
3541
4474
  query[key] = value;
3542
4475
  });
4476
+ return query;
3543
4477
  } 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
- });
4478
+ manualParse(queryString);
3550
4479
  }
3551
4480
  } else {
3552
- queryString.split("&").forEach((param) => {
3553
- const [key, value] = param.split("=");
3554
- if (key) {
3555
- query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
3556
- }
3557
- });
4481
+ manualParse(queryString);
3558
4482
  }
3559
4483
  return query;
3560
4484
  };
@@ -4384,11 +5308,18 @@ var StormcloudPlayer_default = StormcloudPlayer;
4384
5308
  StormcloudVideoPlayer,
4385
5309
  StormcloudVideoPlayerComponent,
4386
5310
  canPlay,
5311
+ createHlsAdPlayer,
5312
+ createImaController,
4387
5313
  createStormcloudPlayer,
5314
+ detectBrowser,
5315
+ getBrowserConfigOverrides,
4388
5316
  getBrowserID,
4389
5317
  getClientInfo,
5318
+ getRecommendedAdPlayer,
5319
+ initializePolyfills,
4390
5320
  isMediaStream,
4391
5321
  lazy,
5322
+ logBrowserInfo,
4392
5323
  merge,
4393
5324
  omit,
4394
5325
  parseQuery,
@@ -4396,6 +5327,9 @@ var StormcloudPlayer_default = StormcloudPlayer;
4396
5327
  randomString,
4397
5328
  sendHeartbeat,
4398
5329
  sendInitialTracking,
5330
+ supportsFeature,
5331
+ supportsGoogleIMA,
5332
+ supportsModernJS,
4399
5333
  supportsWebKitPresentationMode
4400
5334
  });
4401
5335
  //# sourceMappingURL=index.cjs.map