stormcloud-video-player 0.2.11 → 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/README.md +100 -1
- package/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +1010 -48
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +75 -2
- package/lib/index.d.ts +75 -2
- package/lib/index.js +1000 -48
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +934 -44
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +2 -2
- package/lib/players/HlsPlayer.cjs +934 -44
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +934 -44
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +488 -0
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
- package/lib/sdk/hlsAdPlayer.d.cts +8 -0
- package/lib/sdk/ima.cjs +143 -0
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/sdk/ima.d.cts +1 -1
- package/lib/{types-GpA_hKek.d.cts → types-mVgmKmzM.d.cts} +3 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +934 -44
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/browserCompat.cjs +228 -0
- package/lib/utils/browserCompat.cjs.map +1 -0
- package/lib/utils/browserCompat.d.cts +36 -0
- package/lib/utils/polyfills.cjs +253 -0
- package/lib/utils/polyfills.cjs.map +1 -0
- package/lib/utils/polyfills.d.cts +11 -0
- package/lib/utils/tracking.cjs +12 -4
- package/lib/utils/tracking.cjs.map +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +1 -1
package/lib/players/index.cjs
CHANGED
|
@@ -75,7 +75,166 @@ var canPlay = {
|
|
|
75
75
|
var import_react2 = require("react");
|
|
76
76
|
|
|
77
77
|
// src/player/StormcloudVideoPlayer.ts
|
|
78
|
-
var
|
|
78
|
+
var import_hls2 = __toESM(require("hls.js"), 1);
|
|
79
|
+
|
|
80
|
+
// src/utils/browserCompat.ts
|
|
81
|
+
function getChromeVersion(ua) {
|
|
82
|
+
const match = ua.match(/Chrome\/(\d+)/);
|
|
83
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
84
|
+
}
|
|
85
|
+
function getWebKitVersion(ua) {
|
|
86
|
+
const match = ua.match(/AppleWebKit\/(\d+)/);
|
|
87
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
88
|
+
}
|
|
89
|
+
function getPlatform() {
|
|
90
|
+
if ("userAgentData" in navigator && navigator.userAgentData?.platform) {
|
|
91
|
+
return navigator.userAgentData.platform;
|
|
92
|
+
}
|
|
93
|
+
const ua = navigator.userAgent;
|
|
94
|
+
if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
|
|
95
|
+
return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
|
|
96
|
+
}
|
|
97
|
+
if (/Win/i.test(ua)) {
|
|
98
|
+
return "Win32";
|
|
99
|
+
}
|
|
100
|
+
if (/Linux/i.test(ua)) {
|
|
101
|
+
return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
|
|
102
|
+
}
|
|
103
|
+
if (/CrOS/i.test(ua)) {
|
|
104
|
+
return "CrOS";
|
|
105
|
+
}
|
|
106
|
+
return navigator.platform || "Unknown";
|
|
107
|
+
}
|
|
108
|
+
function detectBrowser() {
|
|
109
|
+
const ua = navigator.userAgent;
|
|
110
|
+
const platform = getPlatform();
|
|
111
|
+
let name = "Unknown";
|
|
112
|
+
let version = "0";
|
|
113
|
+
let majorVersion = 0;
|
|
114
|
+
let isSmartTV = false;
|
|
115
|
+
let isLegacyTV = false;
|
|
116
|
+
let supportsIMA = true;
|
|
117
|
+
let supportsModernJS = true;
|
|
118
|
+
let recommendedAdPlayer = "ima";
|
|
119
|
+
if (/Web0S|webOS/i.test(ua)) {
|
|
120
|
+
name = "LG WebOS";
|
|
121
|
+
isSmartTV = true;
|
|
122
|
+
const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
|
|
123
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
124
|
+
if (version !== "Unknown") {
|
|
125
|
+
const parts = version.split(".");
|
|
126
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
127
|
+
}
|
|
128
|
+
} else if (/Tizen/i.test(ua)) {
|
|
129
|
+
name = "Samsung Tizen";
|
|
130
|
+
isSmartTV = true;
|
|
131
|
+
const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
|
|
132
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
133
|
+
if (version !== "Unknown") {
|
|
134
|
+
const parts = version.split(".");
|
|
135
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
136
|
+
}
|
|
137
|
+
} else if (/SMART-TV|SmartTV/i.test(ua)) {
|
|
138
|
+
name = "Smart TV";
|
|
139
|
+
isSmartTV = true;
|
|
140
|
+
} else if (/NetCast/i.test(ua)) {
|
|
141
|
+
name = "LG NetCast";
|
|
142
|
+
isSmartTV = true;
|
|
143
|
+
isLegacyTV = true;
|
|
144
|
+
} else if (/BRAVIA/i.test(ua)) {
|
|
145
|
+
name = "Sony BRAVIA";
|
|
146
|
+
isSmartTV = true;
|
|
147
|
+
}
|
|
148
|
+
const chromeVersion = getChromeVersion(ua);
|
|
149
|
+
const webkitVersion = getWebKitVersion(ua);
|
|
150
|
+
if (chromeVersion > 0) {
|
|
151
|
+
if (!isSmartTV) {
|
|
152
|
+
name = "Chrome";
|
|
153
|
+
version = chromeVersion.toString();
|
|
154
|
+
majorVersion = chromeVersion;
|
|
155
|
+
}
|
|
156
|
+
if (chromeVersion < 50) {
|
|
157
|
+
supportsIMA = false;
|
|
158
|
+
supportsModernJS = false;
|
|
159
|
+
isLegacyTV = true;
|
|
160
|
+
recommendedAdPlayer = "hls";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (webkitVersion > 0 && webkitVersion < 600) {
|
|
164
|
+
supportsModernJS = false;
|
|
165
|
+
if (isSmartTV) {
|
|
166
|
+
isLegacyTV = true;
|
|
167
|
+
supportsIMA = false;
|
|
168
|
+
recommendedAdPlayer = "hls";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
|
|
172
|
+
supportsModernJS = false;
|
|
173
|
+
supportsIMA = false;
|
|
174
|
+
recommendedAdPlayer = "hls";
|
|
175
|
+
}
|
|
176
|
+
if (typeof URLSearchParams === "undefined") {
|
|
177
|
+
supportsModernJS = false;
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
name,
|
|
181
|
+
version,
|
|
182
|
+
majorVersion,
|
|
183
|
+
isSmartTV,
|
|
184
|
+
isLegacyTV,
|
|
185
|
+
platform,
|
|
186
|
+
supportsIMA,
|
|
187
|
+
supportsModernJS,
|
|
188
|
+
recommendedAdPlayer
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function supportsGoogleIMA() {
|
|
192
|
+
const browser = detectBrowser();
|
|
193
|
+
if (browser.isLegacyTV) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (typeof document === "undefined" || typeof document.createElement !== "function") {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const video = document.createElement("video");
|
|
201
|
+
if (!video) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
if (typeof Promise === "undefined") {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
return browser.supportsIMA;
|
|
211
|
+
}
|
|
212
|
+
function logBrowserInfo(debug = false) {
|
|
213
|
+
if (!debug) return;
|
|
214
|
+
const browser = detectBrowser();
|
|
215
|
+
const imaSupport = supportsGoogleIMA();
|
|
216
|
+
console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
|
|
217
|
+
browser: `${browser.name} ${browser.version}`,
|
|
218
|
+
platform: browser.platform,
|
|
219
|
+
isSmartTV: browser.isSmartTV,
|
|
220
|
+
isLegacyTV: browser.isLegacyTV,
|
|
221
|
+
supportsIMA: imaSupport,
|
|
222
|
+
supportsModernJS: browser.supportsModernJS,
|
|
223
|
+
recommendedAdPlayer: browser.recommendedAdPlayer,
|
|
224
|
+
userAgent: navigator.userAgent
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function getBrowserConfigOverrides() {
|
|
228
|
+
const browser = detectBrowser();
|
|
229
|
+
const overrides = {};
|
|
230
|
+
if (browser.isLegacyTV || !browser.supportsIMA) {
|
|
231
|
+
overrides.adPlayerType = "hls";
|
|
232
|
+
}
|
|
233
|
+
if (browser.isSmartTV) {
|
|
234
|
+
overrides.allowNativeHls = true;
|
|
235
|
+
}
|
|
236
|
+
return overrides;
|
|
237
|
+
}
|
|
79
238
|
|
|
80
239
|
// src/sdk/ima.ts
|
|
81
240
|
function createImaController(video, options) {
|
|
@@ -93,6 +252,14 @@ function createImaController(video, options) {
|
|
|
93
252
|
}
|
|
94
253
|
}
|
|
95
254
|
function ensureImaLoaded() {
|
|
255
|
+
if (!supportsGoogleIMA()) {
|
|
256
|
+
console.warn(
|
|
257
|
+
"[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
|
|
258
|
+
);
|
|
259
|
+
return Promise.reject(
|
|
260
|
+
new Error("Google IMA SDK not supported on this browser")
|
|
261
|
+
);
|
|
262
|
+
}
|
|
96
263
|
try {
|
|
97
264
|
const frameEl = window.frameElement;
|
|
98
265
|
const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
|
|
@@ -588,6 +755,456 @@ function createImaController(video, options) {
|
|
|
588
755
|
};
|
|
589
756
|
}
|
|
590
757
|
|
|
758
|
+
// src/sdk/hlsAdPlayer.ts
|
|
759
|
+
var import_hls = __toESM(require("hls.js"), 1);
|
|
760
|
+
function createHlsAdPlayer(contentVideo, options) {
|
|
761
|
+
let adPlaying = false;
|
|
762
|
+
let originalMutedState = false;
|
|
763
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
764
|
+
const licenseKey = options?.licenseKey;
|
|
765
|
+
let adVideoElement;
|
|
766
|
+
let adHls;
|
|
767
|
+
let adContainerEl;
|
|
768
|
+
let currentAd;
|
|
769
|
+
let sessionId;
|
|
770
|
+
let trackingFired = {
|
|
771
|
+
impression: false,
|
|
772
|
+
start: false,
|
|
773
|
+
firstQuartile: false,
|
|
774
|
+
midpoint: false,
|
|
775
|
+
thirdQuartile: false,
|
|
776
|
+
complete: false
|
|
777
|
+
};
|
|
778
|
+
function emit(event, payload) {
|
|
779
|
+
const set = listeners.get(event);
|
|
780
|
+
if (!set) return;
|
|
781
|
+
for (const fn of Array.from(set)) {
|
|
782
|
+
try {
|
|
783
|
+
fn(payload);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function generateSessionId() {
|
|
790
|
+
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
791
|
+
}
|
|
792
|
+
function fireTrackingPixels(urls) {
|
|
793
|
+
if (!urls || urls.length === 0) return;
|
|
794
|
+
urls.forEach((url) => {
|
|
795
|
+
try {
|
|
796
|
+
let trackingUrl = url;
|
|
797
|
+
if (sessionId) {
|
|
798
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
|
|
799
|
+
}
|
|
800
|
+
if (licenseKey) {
|
|
801
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
|
|
802
|
+
}
|
|
803
|
+
const img = new Image(1, 1);
|
|
804
|
+
img.src = trackingUrl;
|
|
805
|
+
console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
function parseVastXml(xmlString) {
|
|
812
|
+
try {
|
|
813
|
+
const parser = new DOMParser();
|
|
814
|
+
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
|
815
|
+
const parserError = xmlDoc.querySelector("parsererror");
|
|
816
|
+
if (parserError) {
|
|
817
|
+
console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
const adElement = xmlDoc.querySelector("Ad");
|
|
821
|
+
if (!adElement) {
|
|
822
|
+
console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
const adId = adElement.getAttribute("id") || "unknown";
|
|
826
|
+
const title = xmlDoc.querySelector("AdTitle")?.textContent || "Ad";
|
|
827
|
+
const durationText = xmlDoc.querySelector("Duration")?.textContent || "00:00:30";
|
|
828
|
+
const durationParts = durationText.split(":");
|
|
829
|
+
const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
830
|
+
const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
|
|
831
|
+
const mediaFiles = [];
|
|
832
|
+
mediaFileElements.forEach((mf) => {
|
|
833
|
+
const type = mf.getAttribute("type") || "";
|
|
834
|
+
if (type === "application/x-mpegURL" || type.includes("m3u8")) {
|
|
835
|
+
const bitrateAttr = mf.getAttribute("bitrate");
|
|
836
|
+
const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
837
|
+
mediaFiles.push({
|
|
838
|
+
url: mf.textContent?.trim() || "",
|
|
839
|
+
type,
|
|
840
|
+
width: parseInt(mf.getAttribute("width") || "1920", 10),
|
|
841
|
+
height: parseInt(mf.getAttribute("height") || "1080", 10),
|
|
842
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
if (mediaFiles.length === 0) {
|
|
847
|
+
console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
const trackingUrls = {
|
|
851
|
+
impression: [],
|
|
852
|
+
start: [],
|
|
853
|
+
firstQuartile: [],
|
|
854
|
+
midpoint: [],
|
|
855
|
+
thirdQuartile: [],
|
|
856
|
+
complete: [],
|
|
857
|
+
mute: [],
|
|
858
|
+
unmute: [],
|
|
859
|
+
pause: [],
|
|
860
|
+
resume: [],
|
|
861
|
+
fullscreen: [],
|
|
862
|
+
exitFullscreen: [],
|
|
863
|
+
skip: [],
|
|
864
|
+
error: []
|
|
865
|
+
};
|
|
866
|
+
xmlDoc.querySelectorAll("Impression").forEach((el) => {
|
|
867
|
+
const url = el.textContent?.trim();
|
|
868
|
+
if (url) trackingUrls.impression.push(url);
|
|
869
|
+
});
|
|
870
|
+
xmlDoc.querySelectorAll("Tracking").forEach((el) => {
|
|
871
|
+
const event = el.getAttribute("event");
|
|
872
|
+
const url = el.textContent?.trim();
|
|
873
|
+
if (event && url) {
|
|
874
|
+
const eventKey = event;
|
|
875
|
+
if (trackingUrls[eventKey]) {
|
|
876
|
+
trackingUrls[eventKey].push(url);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
const clickThrough = xmlDoc.querySelector("ClickThrough")?.textContent?.trim();
|
|
881
|
+
return {
|
|
882
|
+
id: adId,
|
|
883
|
+
title,
|
|
884
|
+
duration,
|
|
885
|
+
mediaFiles,
|
|
886
|
+
trackingUrls,
|
|
887
|
+
clickThrough
|
|
888
|
+
};
|
|
889
|
+
} catch (error) {
|
|
890
|
+
console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function createAdVideoElement() {
|
|
895
|
+
const video = document.createElement("video");
|
|
896
|
+
video.style.position = "absolute";
|
|
897
|
+
video.style.left = "0";
|
|
898
|
+
video.style.top = "0";
|
|
899
|
+
video.style.width = "100%";
|
|
900
|
+
video.style.height = "100%";
|
|
901
|
+
video.style.objectFit = "contain";
|
|
902
|
+
video.style.backgroundColor = "#000";
|
|
903
|
+
video.playsInline = true;
|
|
904
|
+
video.muted = false;
|
|
905
|
+
return video;
|
|
906
|
+
}
|
|
907
|
+
function setupAdEventListeners() {
|
|
908
|
+
if (!adVideoElement || !currentAd) return;
|
|
909
|
+
adVideoElement.addEventListener("timeupdate", () => {
|
|
910
|
+
if (!currentAd || !adVideoElement) return;
|
|
911
|
+
const progress = adVideoElement.currentTime / currentAd.duration;
|
|
912
|
+
if (progress >= 0.25 && !trackingFired.firstQuartile) {
|
|
913
|
+
trackingFired.firstQuartile = true;
|
|
914
|
+
fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
|
|
915
|
+
}
|
|
916
|
+
if (progress >= 0.5 && !trackingFired.midpoint) {
|
|
917
|
+
trackingFired.midpoint = true;
|
|
918
|
+
fireTrackingPixels(currentAd.trackingUrls.midpoint);
|
|
919
|
+
}
|
|
920
|
+
if (progress >= 0.75 && !trackingFired.thirdQuartile) {
|
|
921
|
+
trackingFired.thirdQuartile = true;
|
|
922
|
+
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
adVideoElement.addEventListener("playing", () => {
|
|
926
|
+
if (!currentAd || trackingFired.start) return;
|
|
927
|
+
trackingFired.start = true;
|
|
928
|
+
fireTrackingPixels(currentAd.trackingUrls.start);
|
|
929
|
+
console.log("[HlsAdPlayer] Ad started playing");
|
|
930
|
+
});
|
|
931
|
+
adVideoElement.addEventListener("ended", () => {
|
|
932
|
+
if (!currentAd || trackingFired.complete) return;
|
|
933
|
+
trackingFired.complete = true;
|
|
934
|
+
fireTrackingPixels(currentAd.trackingUrls.complete);
|
|
935
|
+
console.log("[HlsAdPlayer] Ad completed");
|
|
936
|
+
handleAdComplete();
|
|
937
|
+
});
|
|
938
|
+
adVideoElement.addEventListener("error", (e) => {
|
|
939
|
+
console.error("[HlsAdPlayer] Ad video error:", e);
|
|
940
|
+
if (currentAd) {
|
|
941
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
942
|
+
}
|
|
943
|
+
handleAdError();
|
|
944
|
+
});
|
|
945
|
+
adVideoElement.addEventListener("volumechange", () => {
|
|
946
|
+
if (!currentAd) return;
|
|
947
|
+
if (adVideoElement.muted) {
|
|
948
|
+
fireTrackingPixels(currentAd.trackingUrls.mute);
|
|
949
|
+
} else {
|
|
950
|
+
fireTrackingPixels(currentAd.trackingUrls.unmute);
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
adVideoElement.addEventListener("pause", () => {
|
|
954
|
+
if (currentAd && !adVideoElement.ended) {
|
|
955
|
+
fireTrackingPixels(currentAd.trackingUrls.pause);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
adVideoElement.addEventListener("play", () => {
|
|
959
|
+
if (currentAd && adVideoElement.currentTime > 0) {
|
|
960
|
+
fireTrackingPixels(currentAd.trackingUrls.resume);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
function handleAdComplete() {
|
|
965
|
+
console.log("[HlsAdPlayer] Handling ad completion");
|
|
966
|
+
adPlaying = false;
|
|
967
|
+
contentVideo.muted = originalMutedState;
|
|
968
|
+
if (adContainerEl) {
|
|
969
|
+
adContainerEl.style.display = "none";
|
|
970
|
+
adContainerEl.style.pointerEvents = "none";
|
|
971
|
+
}
|
|
972
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
973
|
+
contentVideo.play().catch(() => {
|
|
974
|
+
});
|
|
975
|
+
console.log("[HlsAdPlayer] Content resumed (VOD mode)");
|
|
976
|
+
} else {
|
|
977
|
+
console.log("[HlsAdPlayer] Content unmuted (Live mode)");
|
|
978
|
+
}
|
|
979
|
+
emit("content_resume");
|
|
980
|
+
emit("all_ads_completed");
|
|
981
|
+
}
|
|
982
|
+
function handleAdError() {
|
|
983
|
+
console.log("[HlsAdPlayer] Handling ad error");
|
|
984
|
+
adPlaying = false;
|
|
985
|
+
contentVideo.muted = originalMutedState;
|
|
986
|
+
if (adContainerEl) {
|
|
987
|
+
adContainerEl.style.display = "none";
|
|
988
|
+
adContainerEl.style.pointerEvents = "none";
|
|
989
|
+
}
|
|
990
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
991
|
+
if (contentVideo.paused) {
|
|
992
|
+
contentVideo.play().catch(() => {
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
emit("ad_error");
|
|
997
|
+
}
|
|
998
|
+
return {
|
|
999
|
+
initialize() {
|
|
1000
|
+
console.log("[HlsAdPlayer] Initializing");
|
|
1001
|
+
if (!adContainerEl) {
|
|
1002
|
+
const container = document.createElement("div");
|
|
1003
|
+
container.style.position = "absolute";
|
|
1004
|
+
container.style.left = "0";
|
|
1005
|
+
container.style.top = "0";
|
|
1006
|
+
container.style.right = "0";
|
|
1007
|
+
container.style.bottom = "0";
|
|
1008
|
+
container.style.display = "none";
|
|
1009
|
+
container.style.alignItems = "center";
|
|
1010
|
+
container.style.justifyContent = "center";
|
|
1011
|
+
container.style.pointerEvents = "none";
|
|
1012
|
+
container.style.zIndex = "2";
|
|
1013
|
+
container.style.backgroundColor = "#000";
|
|
1014
|
+
contentVideo.parentElement?.appendChild(container);
|
|
1015
|
+
adContainerEl = container;
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
async requestAds(vastTagUrl) {
|
|
1019
|
+
console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
|
|
1020
|
+
if (adPlaying) {
|
|
1021
|
+
console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
|
|
1022
|
+
return Promise.reject(new Error("Ad already playing"));
|
|
1023
|
+
}
|
|
1024
|
+
try {
|
|
1025
|
+
sessionId = generateSessionId();
|
|
1026
|
+
const response = await fetch(vastTagUrl);
|
|
1027
|
+
if (!response.ok) {
|
|
1028
|
+
throw new Error(`Failed to fetch VAST: ${response.statusText}`);
|
|
1029
|
+
}
|
|
1030
|
+
const vastXml = await response.text();
|
|
1031
|
+
console.log("[HlsAdPlayer] VAST XML received");
|
|
1032
|
+
const ad = parseVastXml(vastXml);
|
|
1033
|
+
if (!ad) {
|
|
1034
|
+
throw new Error("Failed to parse VAST XML or no ads available");
|
|
1035
|
+
}
|
|
1036
|
+
currentAd = ad;
|
|
1037
|
+
console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
|
|
1038
|
+
fireTrackingPixels(ad.trackingUrls.impression);
|
|
1039
|
+
trackingFired.impression = true;
|
|
1040
|
+
return Promise.resolve();
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
console.error("[HlsAdPlayer] Error requesting ads:", error);
|
|
1043
|
+
emit("ad_error");
|
|
1044
|
+
return Promise.reject(error);
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
async play() {
|
|
1048
|
+
if (!currentAd) {
|
|
1049
|
+
console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
|
|
1050
|
+
return Promise.reject(new Error("No ad loaded"));
|
|
1051
|
+
}
|
|
1052
|
+
console.log("[HlsAdPlayer] Starting ad playback");
|
|
1053
|
+
try {
|
|
1054
|
+
if (!adVideoElement) {
|
|
1055
|
+
adVideoElement = createAdVideoElement();
|
|
1056
|
+
adContainerEl?.appendChild(adVideoElement);
|
|
1057
|
+
setupAdEventListeners();
|
|
1058
|
+
}
|
|
1059
|
+
trackingFired = {
|
|
1060
|
+
impression: trackingFired.impression,
|
|
1061
|
+
start: false,
|
|
1062
|
+
firstQuartile: false,
|
|
1063
|
+
midpoint: false,
|
|
1064
|
+
thirdQuartile: false,
|
|
1065
|
+
complete: false
|
|
1066
|
+
};
|
|
1067
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
1068
|
+
contentVideo.pause();
|
|
1069
|
+
console.log("[HlsAdPlayer] Content paused (VOD mode)");
|
|
1070
|
+
} else {
|
|
1071
|
+
console.log("[HlsAdPlayer] Content continues (Live mode)");
|
|
1072
|
+
}
|
|
1073
|
+
contentVideo.muted = true;
|
|
1074
|
+
adPlaying = true;
|
|
1075
|
+
if (adContainerEl) {
|
|
1076
|
+
adContainerEl.style.display = "flex";
|
|
1077
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
1078
|
+
}
|
|
1079
|
+
emit("content_pause");
|
|
1080
|
+
const mediaFile = currentAd.mediaFiles[0];
|
|
1081
|
+
if (!mediaFile) {
|
|
1082
|
+
throw new Error("No media file available for ad");
|
|
1083
|
+
}
|
|
1084
|
+
console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
|
|
1085
|
+
if (import_hls.default.isSupported()) {
|
|
1086
|
+
if (adHls) {
|
|
1087
|
+
adHls.destroy();
|
|
1088
|
+
}
|
|
1089
|
+
adHls = new import_hls.default({
|
|
1090
|
+
enableWorker: true,
|
|
1091
|
+
lowLatencyMode: false
|
|
1092
|
+
});
|
|
1093
|
+
adHls.loadSource(mediaFile.url);
|
|
1094
|
+
adHls.attachMedia(adVideoElement);
|
|
1095
|
+
adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
|
|
1096
|
+
console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
|
|
1097
|
+
adVideoElement.play().catch((error) => {
|
|
1098
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1099
|
+
handleAdError();
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
adHls.on(import_hls.default.Events.ERROR, (event, data) => {
|
|
1103
|
+
console.error("[HlsAdPlayer] HLS error:", data);
|
|
1104
|
+
if (data.fatal) {
|
|
1105
|
+
handleAdError();
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
} else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
|
|
1109
|
+
adVideoElement.src = mediaFile.url;
|
|
1110
|
+
adVideoElement.play().catch((error) => {
|
|
1111
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1112
|
+
handleAdError();
|
|
1113
|
+
});
|
|
1114
|
+
} else {
|
|
1115
|
+
throw new Error("HLS not supported");
|
|
1116
|
+
}
|
|
1117
|
+
return Promise.resolve();
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
console.error("[HlsAdPlayer] Error playing ad:", error);
|
|
1120
|
+
handleAdError();
|
|
1121
|
+
return Promise.reject(error);
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
async stop() {
|
|
1125
|
+
console.log("[HlsAdPlayer] Stopping ad");
|
|
1126
|
+
adPlaying = false;
|
|
1127
|
+
contentVideo.muted = originalMutedState;
|
|
1128
|
+
if (adContainerEl) {
|
|
1129
|
+
adContainerEl.style.display = "none";
|
|
1130
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1131
|
+
}
|
|
1132
|
+
if (adHls) {
|
|
1133
|
+
adHls.destroy();
|
|
1134
|
+
adHls = void 0;
|
|
1135
|
+
}
|
|
1136
|
+
if (adVideoElement) {
|
|
1137
|
+
adVideoElement.pause();
|
|
1138
|
+
adVideoElement.src = "";
|
|
1139
|
+
}
|
|
1140
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
1141
|
+
contentVideo.play().catch(() => {
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
currentAd = void 0;
|
|
1145
|
+
},
|
|
1146
|
+
destroy() {
|
|
1147
|
+
console.log("[HlsAdPlayer] Destroying");
|
|
1148
|
+
adPlaying = false;
|
|
1149
|
+
contentVideo.muted = originalMutedState;
|
|
1150
|
+
if (adHls) {
|
|
1151
|
+
adHls.destroy();
|
|
1152
|
+
adHls = void 0;
|
|
1153
|
+
}
|
|
1154
|
+
if (adVideoElement) {
|
|
1155
|
+
adVideoElement.pause();
|
|
1156
|
+
adVideoElement.src = "";
|
|
1157
|
+
adVideoElement.remove();
|
|
1158
|
+
adVideoElement = void 0;
|
|
1159
|
+
}
|
|
1160
|
+
if (adContainerEl?.parentElement) {
|
|
1161
|
+
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
1162
|
+
}
|
|
1163
|
+
adContainerEl = void 0;
|
|
1164
|
+
currentAd = void 0;
|
|
1165
|
+
listeners.clear();
|
|
1166
|
+
},
|
|
1167
|
+
isAdPlaying() {
|
|
1168
|
+
return adPlaying;
|
|
1169
|
+
},
|
|
1170
|
+
resize(width, height) {
|
|
1171
|
+
console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
|
|
1172
|
+
if (adContainerEl) {
|
|
1173
|
+
adContainerEl.style.width = `${width}px`;
|
|
1174
|
+
adContainerEl.style.height = `${height}px`;
|
|
1175
|
+
}
|
|
1176
|
+
if (adVideoElement) {
|
|
1177
|
+
adVideoElement.style.width = `${width}px`;
|
|
1178
|
+
adVideoElement.style.height = `${height}px`;
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
on(event, listener) {
|
|
1182
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
1183
|
+
listeners.get(event).add(listener);
|
|
1184
|
+
},
|
|
1185
|
+
off(event, listener) {
|
|
1186
|
+
listeners.get(event)?.delete(listener);
|
|
1187
|
+
},
|
|
1188
|
+
updateOriginalMutedState(muted) {
|
|
1189
|
+
originalMutedState = muted;
|
|
1190
|
+
},
|
|
1191
|
+
getOriginalMutedState() {
|
|
1192
|
+
return originalMutedState;
|
|
1193
|
+
},
|
|
1194
|
+
setAdVolume(volume) {
|
|
1195
|
+
if (adVideoElement && adPlaying) {
|
|
1196
|
+
adVideoElement.volume = Math.max(0, Math.min(1, volume));
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
getAdVolume() {
|
|
1200
|
+
if (adVideoElement && adPlaying) {
|
|
1201
|
+
return adVideoElement.volume;
|
|
1202
|
+
}
|
|
1203
|
+
return 1;
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
|
|
591
1208
|
// src/utils/tracking.ts
|
|
592
1209
|
var cachedBrowserId = null;
|
|
593
1210
|
function getClientInfo() {
|
|
@@ -739,10 +1356,18 @@ async function getBrowserID(clientInfo) {
|
|
|
739
1356
|
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
740
1357
|
try {
|
|
741
1358
|
await crypto.subtle.digest("SHA-256", new Uint8Array([1, 2, 3]));
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
new TextEncoder().encode(fingerprintString)
|
|
745
|
-
|
|
1359
|
+
let encodedData;
|
|
1360
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1361
|
+
encodedData = new TextEncoder().encode(fingerprintString);
|
|
1362
|
+
} else {
|
|
1363
|
+
const utf8 = unescape(encodeURIComponent(fingerprintString));
|
|
1364
|
+
const buffer = new Uint8Array(utf8.length);
|
|
1365
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
1366
|
+
buffer[i] = utf8.charCodeAt(i);
|
|
1367
|
+
}
|
|
1368
|
+
encodedData = buffer;
|
|
1369
|
+
}
|
|
1370
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encodedData);
|
|
746
1371
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
747
1372
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
748
1373
|
cachedBrowserId = hashHex;
|
|
@@ -829,6 +1454,215 @@ async function sendHeartbeat(licenseKey) {
|
|
|
829
1454
|
}
|
|
830
1455
|
}
|
|
831
1456
|
|
|
1457
|
+
// src/utils/polyfills.ts
|
|
1458
|
+
function polyfillURLSearchParams() {
|
|
1459
|
+
if (typeof URLSearchParams !== "undefined") {
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
class URLSearchParamsPolyfill {
|
|
1463
|
+
constructor(init) {
|
|
1464
|
+
this.params = /* @__PURE__ */ new Map();
|
|
1465
|
+
if (typeof init === "string") {
|
|
1466
|
+
this.parseQueryString(init);
|
|
1467
|
+
} else if (init instanceof URLSearchParamsPolyfill) {
|
|
1468
|
+
init.forEach((value, key) => {
|
|
1469
|
+
this.append(key, value);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
parseQueryString(query) {
|
|
1474
|
+
const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
|
|
1475
|
+
if (!cleanQuery) return;
|
|
1476
|
+
cleanQuery.split("&").forEach((param) => {
|
|
1477
|
+
const [key, value] = param.split("=");
|
|
1478
|
+
if (key) {
|
|
1479
|
+
const decodedKey = this.safeDecodeURIComponent(key);
|
|
1480
|
+
const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
|
|
1481
|
+
this.append(decodedKey, decodedValue);
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
safeDecodeURIComponent(str) {
|
|
1486
|
+
try {
|
|
1487
|
+
return decodeURIComponent(str.replace(/\+/g, " "));
|
|
1488
|
+
} catch (e) {
|
|
1489
|
+
return str;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
append(name, value) {
|
|
1493
|
+
const values = this.params.get(name) || [];
|
|
1494
|
+
values.push(String(value));
|
|
1495
|
+
this.params.set(name, values);
|
|
1496
|
+
}
|
|
1497
|
+
delete(name) {
|
|
1498
|
+
this.params.delete(name);
|
|
1499
|
+
}
|
|
1500
|
+
get(name) {
|
|
1501
|
+
const values = this.params.get(name);
|
|
1502
|
+
return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
|
|
1503
|
+
}
|
|
1504
|
+
getAll(name) {
|
|
1505
|
+
return this.params.get(name) || [];
|
|
1506
|
+
}
|
|
1507
|
+
has(name) {
|
|
1508
|
+
return this.params.has(name);
|
|
1509
|
+
}
|
|
1510
|
+
set(name, value) {
|
|
1511
|
+
this.params.set(name, [String(value)]);
|
|
1512
|
+
}
|
|
1513
|
+
forEach(callback) {
|
|
1514
|
+
this.params.forEach((values, key) => {
|
|
1515
|
+
values.forEach((value) => {
|
|
1516
|
+
callback(value, key, this);
|
|
1517
|
+
});
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
toString() {
|
|
1521
|
+
const parts = [];
|
|
1522
|
+
this.params.forEach((values, key) => {
|
|
1523
|
+
values.forEach((value) => {
|
|
1524
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
1525
|
+
});
|
|
1526
|
+
});
|
|
1527
|
+
return parts.join("&");
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
window.URLSearchParams = URLSearchParamsPolyfill;
|
|
1531
|
+
}
|
|
1532
|
+
function polyfillTextEncoder() {
|
|
1533
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
class TextEncoderPolyfill {
|
|
1537
|
+
constructor() {
|
|
1538
|
+
this.encoding = "utf-8";
|
|
1539
|
+
}
|
|
1540
|
+
encode(str) {
|
|
1541
|
+
const utf8 = [];
|
|
1542
|
+
for (let i = 0; i < str.length; i++) {
|
|
1543
|
+
let charcode = str.charCodeAt(i);
|
|
1544
|
+
if (charcode < 128) {
|
|
1545
|
+
utf8.push(charcode);
|
|
1546
|
+
} else if (charcode < 2048) {
|
|
1547
|
+
utf8.push(192 | charcode >> 6, 128 | charcode & 63);
|
|
1548
|
+
} else if (charcode < 55296 || charcode >= 57344) {
|
|
1549
|
+
utf8.push(
|
|
1550
|
+
224 | charcode >> 12,
|
|
1551
|
+
128 | charcode >> 6 & 63,
|
|
1552
|
+
128 | charcode & 63
|
|
1553
|
+
);
|
|
1554
|
+
} else {
|
|
1555
|
+
i++;
|
|
1556
|
+
charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
|
|
1557
|
+
utf8.push(
|
|
1558
|
+
240 | charcode >> 18,
|
|
1559
|
+
128 | charcode >> 12 & 63,
|
|
1560
|
+
128 | charcode >> 6 & 63,
|
|
1561
|
+
128 | charcode & 63
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return new Uint8Array(utf8);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
window.TextEncoder = TextEncoderPolyfill;
|
|
1569
|
+
}
|
|
1570
|
+
function polyfillPromiseFinally() {
|
|
1571
|
+
if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
|
|
1572
|
+
Promise.prototype.finally = function(callback) {
|
|
1573
|
+
const constructor = this.constructor;
|
|
1574
|
+
return this.then(
|
|
1575
|
+
(value) => constructor.resolve(callback()).then(() => value),
|
|
1576
|
+
(reason) => constructor.resolve(callback()).then(() => {
|
|
1577
|
+
throw reason;
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
function polyfillObjectAssign() {
|
|
1584
|
+
if (typeof Object.assign !== "function") {
|
|
1585
|
+
Object.assign = function(target, ...sources) {
|
|
1586
|
+
if (target == null) {
|
|
1587
|
+
throw new TypeError("Cannot convert undefined or null to object");
|
|
1588
|
+
}
|
|
1589
|
+
const to = Object(target);
|
|
1590
|
+
for (let i = 0; i < sources.length; i++) {
|
|
1591
|
+
const nextSource = sources[i];
|
|
1592
|
+
if (nextSource != null) {
|
|
1593
|
+
for (const nextKey in nextSource) {
|
|
1594
|
+
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
|
1595
|
+
to[nextKey] = nextSource[nextKey];
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return to;
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
function polyfillArrayFrom() {
|
|
1605
|
+
if (!Array.from) {
|
|
1606
|
+
Array.from = function(arrayLike, mapFn, thisArg) {
|
|
1607
|
+
const items = Object(arrayLike);
|
|
1608
|
+
if (arrayLike == null) {
|
|
1609
|
+
throw new TypeError("Array.from requires an array-like object");
|
|
1610
|
+
}
|
|
1611
|
+
const len = items.length >>> 0;
|
|
1612
|
+
const result = new Array(len);
|
|
1613
|
+
for (let i = 0; i < len; i++) {
|
|
1614
|
+
if (mapFn) {
|
|
1615
|
+
result[i] = mapFn.call(thisArg, items[i], i);
|
|
1616
|
+
} else {
|
|
1617
|
+
result[i] = items[i];
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return result;
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
function polyfillStringStartsWith() {
|
|
1625
|
+
if (!String.prototype.startsWith) {
|
|
1626
|
+
String.prototype.startsWith = function(search, pos) {
|
|
1627
|
+
pos = !pos || pos < 0 ? 0 : +pos;
|
|
1628
|
+
return this.substring(pos, pos + search.length) === search;
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
function polyfillStringEndsWith() {
|
|
1633
|
+
if (!String.prototype.endsWith) {
|
|
1634
|
+
String.prototype.endsWith = function(search, length) {
|
|
1635
|
+
if (length === void 0 || length > this.length) {
|
|
1636
|
+
length = this.length;
|
|
1637
|
+
}
|
|
1638
|
+
return this.substring(length - search.length, length) === search;
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
function polyfillStringIncludes() {
|
|
1643
|
+
if (!String.prototype.includes) {
|
|
1644
|
+
String.prototype.includes = function(search, start) {
|
|
1645
|
+
if (typeof start !== "number") {
|
|
1646
|
+
start = 0;
|
|
1647
|
+
}
|
|
1648
|
+
if (start + search.length > this.length) {
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
return this.indexOf(search, start) !== -1;
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
function initializePolyfills() {
|
|
1656
|
+
polyfillObjectAssign();
|
|
1657
|
+
polyfillArrayFrom();
|
|
1658
|
+
polyfillStringStartsWith();
|
|
1659
|
+
polyfillStringEndsWith();
|
|
1660
|
+
polyfillStringIncludes();
|
|
1661
|
+
polyfillURLSearchParams();
|
|
1662
|
+
polyfillTextEncoder();
|
|
1663
|
+
polyfillPromiseFinally();
|
|
1664
|
+
}
|
|
1665
|
+
|
|
832
1666
|
// src/player/StormcloudVideoPlayer.ts
|
|
833
1667
|
var StormcloudVideoPlayer = class {
|
|
834
1668
|
constructor(config) {
|
|
@@ -841,11 +1675,40 @@ var StormcloudVideoPlayer = class {
|
|
|
841
1675
|
this.totalAdsInBreak = 0;
|
|
842
1676
|
this.showAds = false;
|
|
843
1677
|
this.isLiveStream = false;
|
|
844
|
-
|
|
1678
|
+
initializePolyfills();
|
|
1679
|
+
const browserOverrides = getBrowserConfigOverrides();
|
|
1680
|
+
this.config = { ...config, ...browserOverrides };
|
|
845
1681
|
this.video = config.videoElement;
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1682
|
+
logBrowserInfo(config.debugAdTiming);
|
|
1683
|
+
this.ima = this.createAdPlayer(false);
|
|
1684
|
+
}
|
|
1685
|
+
createAdPlayer(continueLiveStreamDuringAds) {
|
|
1686
|
+
const vastMode = this.config.vastMode || "default";
|
|
1687
|
+
let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
|
|
1688
|
+
if (adPlayerType === "ima" && !supportsGoogleIMA()) {
|
|
1689
|
+
if (this.config.debugAdTiming) {
|
|
1690
|
+
console.warn(
|
|
1691
|
+
"[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
adPlayerType = "hls";
|
|
1695
|
+
}
|
|
1696
|
+
if (adPlayerType === "hls") {
|
|
1697
|
+
if (this.config.debugAdTiming) {
|
|
1698
|
+
console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
|
|
1699
|
+
}
|
|
1700
|
+
return createHlsAdPlayer(this.video, {
|
|
1701
|
+
continueLiveStreamDuringAds,
|
|
1702
|
+
...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {}
|
|
1703
|
+
});
|
|
1704
|
+
} else {
|
|
1705
|
+
if (this.config.debugAdTiming) {
|
|
1706
|
+
console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
|
|
1707
|
+
}
|
|
1708
|
+
return createImaController(this.video, {
|
|
1709
|
+
continueLiveStreamDuringAds
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
849
1712
|
}
|
|
850
1713
|
async load() {
|
|
851
1714
|
if (!this.attached) {
|
|
@@ -876,9 +1739,7 @@ var StormcloudVideoPlayer = class {
|
|
|
876
1739
|
);
|
|
877
1740
|
}
|
|
878
1741
|
this.ima.destroy();
|
|
879
|
-
this.ima =
|
|
880
|
-
continueLiveStreamDuringAds: false
|
|
881
|
-
});
|
|
1742
|
+
this.ima = this.createAdPlayer(false);
|
|
882
1743
|
this.ima.initialize();
|
|
883
1744
|
if (this.config.autoplay) {
|
|
884
1745
|
await this.video.play()?.catch(() => {
|
|
@@ -886,7 +1747,7 @@ var StormcloudVideoPlayer = class {
|
|
|
886
1747
|
}
|
|
887
1748
|
return;
|
|
888
1749
|
}
|
|
889
|
-
this.hls = new
|
|
1750
|
+
this.hls = new import_hls2.default({
|
|
890
1751
|
enableWorker: true,
|
|
891
1752
|
backBufferLength: 30,
|
|
892
1753
|
liveDurationInfinity: true,
|
|
@@ -894,10 +1755,10 @@ var StormcloudVideoPlayer = class {
|
|
|
894
1755
|
maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
|
|
895
1756
|
...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
|
|
896
1757
|
});
|
|
897
|
-
this.hls.on(
|
|
1758
|
+
this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
|
|
898
1759
|
this.hls?.loadSource(this.config.src);
|
|
899
1760
|
});
|
|
900
|
-
this.hls.on(
|
|
1761
|
+
this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
|
|
901
1762
|
this.isLiveStream = this.hls?.levels?.some(
|
|
902
1763
|
(level) => level?.details?.live === true || level?.details?.type === "LIVE"
|
|
903
1764
|
) ?? false;
|
|
@@ -910,16 +1771,14 @@ var StormcloudVideoPlayer = class {
|
|
|
910
1771
|
});
|
|
911
1772
|
}
|
|
912
1773
|
this.ima.destroy();
|
|
913
|
-
this.ima =
|
|
914
|
-
continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
|
|
915
|
-
});
|
|
1774
|
+
this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
|
|
916
1775
|
this.ima.initialize();
|
|
917
1776
|
if (this.config.autoplay) {
|
|
918
1777
|
await this.video.play()?.catch(() => {
|
|
919
1778
|
});
|
|
920
1779
|
}
|
|
921
1780
|
});
|
|
922
|
-
this.hls.on(
|
|
1781
|
+
this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
|
|
923
1782
|
const id3Tags = (data?.samples || []).map((s) => ({
|
|
924
1783
|
key: "ID3",
|
|
925
1784
|
value: s?.data,
|
|
@@ -927,7 +1786,7 @@ var StormcloudVideoPlayer = class {
|
|
|
927
1786
|
}));
|
|
928
1787
|
id3Tags.forEach((tag) => this.onId3Tag(tag));
|
|
929
1788
|
});
|
|
930
|
-
this.hls.on(
|
|
1789
|
+
this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
|
|
931
1790
|
const frag = data?.frag;
|
|
932
1791
|
const tagList = frag?.tagList;
|
|
933
1792
|
if (!Array.isArray(tagList)) return;
|
|
@@ -987,13 +1846,13 @@ var StormcloudVideoPlayer = class {
|
|
|
987
1846
|
}
|
|
988
1847
|
}
|
|
989
1848
|
});
|
|
990
|
-
this.hls.on(
|
|
1849
|
+
this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
|
|
991
1850
|
if (data?.fatal) {
|
|
992
1851
|
switch (data.type) {
|
|
993
|
-
case
|
|
1852
|
+
case import_hls2.default.ErrorTypes.NETWORK_ERROR:
|
|
994
1853
|
this.hls?.startLoad();
|
|
995
1854
|
break;
|
|
996
|
-
case
|
|
1855
|
+
case import_hls2.default.ErrorTypes.MEDIA_ERROR:
|
|
997
1856
|
this.hls?.recoverMediaError();
|
|
998
1857
|
break;
|
|
999
1858
|
default:
|
|
@@ -1504,6 +2363,42 @@ var StormcloudVideoPlayer = class {
|
|
|
1504
2363
|
}
|
|
1505
2364
|
}
|
|
1506
2365
|
async fetchAdConfiguration() {
|
|
2366
|
+
const vastMode = this.config.vastMode || "default";
|
|
2367
|
+
if (this.config.debugAdTiming) {
|
|
2368
|
+
console.log(
|
|
2369
|
+
"[StormcloudVideoPlayer] VAST mode:",
|
|
2370
|
+
vastMode
|
|
2371
|
+
);
|
|
2372
|
+
}
|
|
2373
|
+
if (vastMode === "adstorm") {
|
|
2374
|
+
if (!this.config.licenseKey) {
|
|
2375
|
+
if (this.config.debugAdTiming) {
|
|
2376
|
+
console.warn(
|
|
2377
|
+
"[StormcloudVideoPlayer] AdStorm mode requires a license key"
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
|
|
2383
|
+
this.apiVastTagUrl = vastEndpoint;
|
|
2384
|
+
if (this.config.debugAdTiming) {
|
|
2385
|
+
console.log(
|
|
2386
|
+
"[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
|
|
2387
|
+
vastEndpoint
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
if (this.config.vastTagUrl) {
|
|
2393
|
+
this.apiVastTagUrl = this.config.vastTagUrl;
|
|
2394
|
+
if (this.config.debugAdTiming) {
|
|
2395
|
+
console.log(
|
|
2396
|
+
"[StormcloudVideoPlayer] Using custom VAST tag URL:",
|
|
2397
|
+
this.apiVastTagUrl
|
|
2398
|
+
);
|
|
2399
|
+
}
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
1507
2402
|
const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
1508
2403
|
if (this.config.debugAdTiming) {
|
|
1509
2404
|
console.log(
|
|
@@ -1517,7 +2412,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1517
2412
|
}
|
|
1518
2413
|
const response = await fetch(apiUrl, { headers });
|
|
1519
2414
|
if (!response.ok) {
|
|
1520
|
-
|
|
2415
|
+
if (this.config.debugAdTiming) {
|
|
2416
|
+
console.warn(
|
|
2417
|
+
`[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
return;
|
|
1521
2421
|
}
|
|
1522
2422
|
const data = await response.json();
|
|
1523
2423
|
const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
|
|
@@ -1525,17 +2425,16 @@ var StormcloudVideoPlayer = class {
|
|
|
1525
2425
|
this.apiVastTagUrl = decodeURIComponent(imaPayload);
|
|
1526
2426
|
if (this.config.debugAdTiming) {
|
|
1527
2427
|
console.log(
|
|
1528
|
-
"[StormcloudVideoPlayer] Extracted VAST tag URL:",
|
|
2428
|
+
"[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
|
|
1529
2429
|
this.apiVastTagUrl
|
|
1530
2430
|
);
|
|
1531
2431
|
}
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
});
|
|
2432
|
+
} else {
|
|
2433
|
+
if (this.config.debugAdTiming) {
|
|
2434
|
+
console.warn(
|
|
2435
|
+
"[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
1539
2438
|
}
|
|
1540
2439
|
}
|
|
1541
2440
|
getCurrentAdIndex() {
|
|
@@ -1579,23 +2478,14 @@ var StormcloudVideoPlayer = class {
|
|
|
1579
2478
|
);
|
|
1580
2479
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1581
2480
|
let vastTagUrl;
|
|
1582
|
-
let adsNumber = 1;
|
|
1583
2481
|
if (this.apiVastTagUrl) {
|
|
1584
2482
|
vastTagUrl = this.apiVastTagUrl;
|
|
1585
|
-
|
|
1586
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1587
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1588
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1589
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1590
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
2483
|
+
this.adPodQueue = [];
|
|
1594
2484
|
this.currentAdIndex = 0;
|
|
1595
|
-
this.totalAdsInBreak =
|
|
2485
|
+
this.totalAdsInBreak = 1;
|
|
1596
2486
|
if (this.config.debugAdTiming) {
|
|
1597
2487
|
console.log(
|
|
1598
|
-
|
|
2488
|
+
"[StormcloudVideoPlayer] Using VAST endpoint:",
|
|
1599
2489
|
vastTagUrl
|
|
1600
2490
|
);
|
|
1601
2491
|
}
|