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/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
|
|
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() {
|
|
@@ -725,10 +1381,18 @@ async function getBrowserID(clientInfo) {
|
|
|
725
1381
|
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
726
1382
|
try {
|
|
727
1383
|
await crypto.subtle.digest("SHA-256", new Uint8Array([1, 2, 3]));
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
new TextEncoder().encode(fingerprintString)
|
|
731
|
-
|
|
1384
|
+
let encodedData;
|
|
1385
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1386
|
+
encodedData = new TextEncoder().encode(fingerprintString);
|
|
1387
|
+
} else {
|
|
1388
|
+
const utf8 = unescape(encodeURIComponent(fingerprintString));
|
|
1389
|
+
const buffer = new Uint8Array(utf8.length);
|
|
1390
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
1391
|
+
buffer[i] = utf8.charCodeAt(i);
|
|
1392
|
+
}
|
|
1393
|
+
encodedData = buffer;
|
|
1394
|
+
}
|
|
1395
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encodedData);
|
|
732
1396
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
733
1397
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
734
1398
|
cachedBrowserId = hashHex;
|
|
@@ -815,6 +1479,215 @@ async function sendHeartbeat(licenseKey) {
|
|
|
815
1479
|
}
|
|
816
1480
|
}
|
|
817
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
|
+
|
|
818
1691
|
// src/player/StormcloudVideoPlayer.ts
|
|
819
1692
|
var StormcloudVideoPlayer = class {
|
|
820
1693
|
constructor(config) {
|
|
@@ -827,11 +1700,40 @@ var StormcloudVideoPlayer = class {
|
|
|
827
1700
|
this.totalAdsInBreak = 0;
|
|
828
1701
|
this.showAds = false;
|
|
829
1702
|
this.isLiveStream = false;
|
|
830
|
-
|
|
1703
|
+
initializePolyfills();
|
|
1704
|
+
const browserOverrides = getBrowserConfigOverrides();
|
|
1705
|
+
this.config = { ...config, ...browserOverrides };
|
|
831
1706
|
this.video = config.videoElement;
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
+
}
|
|
835
1737
|
}
|
|
836
1738
|
async load() {
|
|
837
1739
|
if (!this.attached) {
|
|
@@ -862,9 +1764,7 @@ var StormcloudVideoPlayer = class {
|
|
|
862
1764
|
);
|
|
863
1765
|
}
|
|
864
1766
|
this.ima.destroy();
|
|
865
|
-
this.ima =
|
|
866
|
-
continueLiveStreamDuringAds: false
|
|
867
|
-
});
|
|
1767
|
+
this.ima = this.createAdPlayer(false);
|
|
868
1768
|
this.ima.initialize();
|
|
869
1769
|
if (this.config.autoplay) {
|
|
870
1770
|
await this.video.play()?.catch(() => {
|
|
@@ -872,7 +1772,7 @@ var StormcloudVideoPlayer = class {
|
|
|
872
1772
|
}
|
|
873
1773
|
return;
|
|
874
1774
|
}
|
|
875
|
-
this.hls = new
|
|
1775
|
+
this.hls = new import_hls2.default({
|
|
876
1776
|
enableWorker: true,
|
|
877
1777
|
backBufferLength: 30,
|
|
878
1778
|
liveDurationInfinity: true,
|
|
@@ -880,10 +1780,10 @@ var StormcloudVideoPlayer = class {
|
|
|
880
1780
|
maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
|
|
881
1781
|
...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
|
|
882
1782
|
});
|
|
883
|
-
this.hls.on(
|
|
1783
|
+
this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
|
|
884
1784
|
this.hls?.loadSource(this.config.src);
|
|
885
1785
|
});
|
|
886
|
-
this.hls.on(
|
|
1786
|
+
this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
|
|
887
1787
|
this.isLiveStream = this.hls?.levels?.some(
|
|
888
1788
|
(level) => level?.details?.live === true || level?.details?.type === "LIVE"
|
|
889
1789
|
) ?? false;
|
|
@@ -896,16 +1796,14 @@ var StormcloudVideoPlayer = class {
|
|
|
896
1796
|
});
|
|
897
1797
|
}
|
|
898
1798
|
this.ima.destroy();
|
|
899
|
-
this.ima =
|
|
900
|
-
continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
|
|
901
|
-
});
|
|
1799
|
+
this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
|
|
902
1800
|
this.ima.initialize();
|
|
903
1801
|
if (this.config.autoplay) {
|
|
904
1802
|
await this.video.play()?.catch(() => {
|
|
905
1803
|
});
|
|
906
1804
|
}
|
|
907
1805
|
});
|
|
908
|
-
this.hls.on(
|
|
1806
|
+
this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
|
|
909
1807
|
const id3Tags = (data?.samples || []).map((s) => ({
|
|
910
1808
|
key: "ID3",
|
|
911
1809
|
value: s?.data,
|
|
@@ -913,7 +1811,7 @@ var StormcloudVideoPlayer = class {
|
|
|
913
1811
|
}));
|
|
914
1812
|
id3Tags.forEach((tag) => this.onId3Tag(tag));
|
|
915
1813
|
});
|
|
916
|
-
this.hls.on(
|
|
1814
|
+
this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
|
|
917
1815
|
const frag = data?.frag;
|
|
918
1816
|
const tagList = frag?.tagList;
|
|
919
1817
|
if (!Array.isArray(tagList)) return;
|
|
@@ -973,13 +1871,13 @@ var StormcloudVideoPlayer = class {
|
|
|
973
1871
|
}
|
|
974
1872
|
}
|
|
975
1873
|
});
|
|
976
|
-
this.hls.on(
|
|
1874
|
+
this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
|
|
977
1875
|
if (data?.fatal) {
|
|
978
1876
|
switch (data.type) {
|
|
979
|
-
case
|
|
1877
|
+
case import_hls2.default.ErrorTypes.NETWORK_ERROR:
|
|
980
1878
|
this.hls?.startLoad();
|
|
981
1879
|
break;
|
|
982
|
-
case
|
|
1880
|
+
case import_hls2.default.ErrorTypes.MEDIA_ERROR:
|
|
983
1881
|
this.hls?.recoverMediaError();
|
|
984
1882
|
break;
|
|
985
1883
|
default:
|
|
@@ -1490,6 +2388,42 @@ var StormcloudVideoPlayer = class {
|
|
|
1490
2388
|
}
|
|
1491
2389
|
}
|
|
1492
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
|
+
}
|
|
1493
2427
|
const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
1494
2428
|
if (this.config.debugAdTiming) {
|
|
1495
2429
|
console.log(
|
|
@@ -1503,7 +2437,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1503
2437
|
}
|
|
1504
2438
|
const response = await fetch(apiUrl, { headers });
|
|
1505
2439
|
if (!response.ok) {
|
|
1506
|
-
|
|
2440
|
+
if (this.config.debugAdTiming) {
|
|
2441
|
+
console.warn(
|
|
2442
|
+
`[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
2445
|
+
return;
|
|
1507
2446
|
}
|
|
1508
2447
|
const data = await response.json();
|
|
1509
2448
|
const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
|
|
@@ -1511,17 +2450,16 @@ var StormcloudVideoPlayer = class {
|
|
|
1511
2450
|
this.apiVastTagUrl = decodeURIComponent(imaPayload);
|
|
1512
2451
|
if (this.config.debugAdTiming) {
|
|
1513
2452
|
console.log(
|
|
1514
|
-
"[StormcloudVideoPlayer] Extracted VAST tag URL:",
|
|
2453
|
+
"[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
|
|
1515
2454
|
this.apiVastTagUrl
|
|
1516
2455
|
);
|
|
1517
2456
|
}
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
});
|
|
2457
|
+
} else {
|
|
2458
|
+
if (this.config.debugAdTiming) {
|
|
2459
|
+
console.warn(
|
|
2460
|
+
"[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
1525
2463
|
}
|
|
1526
2464
|
}
|
|
1527
2465
|
getCurrentAdIndex() {
|
|
@@ -1565,23 +2503,14 @@ var StormcloudVideoPlayer = class {
|
|
|
1565
2503
|
);
|
|
1566
2504
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1567
2505
|
let vastTagUrl;
|
|
1568
|
-
let adsNumber = 1;
|
|
1569
2506
|
if (this.apiVastTagUrl) {
|
|
1570
2507
|
vastTagUrl = this.apiVastTagUrl;
|
|
1571
|
-
|
|
1572
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1573
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1574
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1575
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1576
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
2508
|
+
this.adPodQueue = [];
|
|
1580
2509
|
this.currentAdIndex = 0;
|
|
1581
|
-
this.totalAdsInBreak =
|
|
2510
|
+
this.totalAdsInBreak = 1;
|
|
1582
2511
|
if (this.config.debugAdTiming) {
|
|
1583
2512
|
console.log(
|
|
1584
|
-
|
|
2513
|
+
"[StormcloudVideoPlayer] Using VAST endpoint:",
|
|
1585
2514
|
vastTagUrl
|
|
1586
2515
|
);
|
|
1587
2516
|
}
|
|
@@ -3524,10 +4453,33 @@ var randomString = () => {
|
|
|
3524
4453
|
};
|
|
3525
4454
|
var parseQuery = (url) => {
|
|
3526
4455
|
const query = {};
|
|
3527
|
-
const
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
4456
|
+
const queryString = url.split("?")[1] || "";
|
|
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
|
+
};
|
|
4470
|
+
if (typeof URLSearchParams !== "undefined") {
|
|
4471
|
+
try {
|
|
4472
|
+
const params = new URLSearchParams(queryString);
|
|
4473
|
+
params.forEach((value, key) => {
|
|
4474
|
+
query[key] = value;
|
|
4475
|
+
});
|
|
4476
|
+
return query;
|
|
4477
|
+
} catch (e) {
|
|
4478
|
+
manualParse(queryString);
|
|
4479
|
+
}
|
|
4480
|
+
} else {
|
|
4481
|
+
manualParse(queryString);
|
|
4482
|
+
}
|
|
3531
4483
|
return query;
|
|
3532
4484
|
};
|
|
3533
4485
|
var merge = (target, ...sources) => {
|
|
@@ -4356,11 +5308,18 @@ var StormcloudPlayer_default = StormcloudPlayer;
|
|
|
4356
5308
|
StormcloudVideoPlayer,
|
|
4357
5309
|
StormcloudVideoPlayerComponent,
|
|
4358
5310
|
canPlay,
|
|
5311
|
+
createHlsAdPlayer,
|
|
5312
|
+
createImaController,
|
|
4359
5313
|
createStormcloudPlayer,
|
|
5314
|
+
detectBrowser,
|
|
5315
|
+
getBrowserConfigOverrides,
|
|
4360
5316
|
getBrowserID,
|
|
4361
5317
|
getClientInfo,
|
|
5318
|
+
getRecommendedAdPlayer,
|
|
5319
|
+
initializePolyfills,
|
|
4362
5320
|
isMediaStream,
|
|
4363
5321
|
lazy,
|
|
5322
|
+
logBrowserInfo,
|
|
4364
5323
|
merge,
|
|
4365
5324
|
omit,
|
|
4366
5325
|
parseQuery,
|
|
@@ -4368,6 +5327,9 @@ var StormcloudPlayer_default = StormcloudPlayer;
|
|
|
4368
5327
|
randomString,
|
|
4369
5328
|
sendHeartbeat,
|
|
4370
5329
|
sendInitialTracking,
|
|
5330
|
+
supportsFeature,
|
|
5331
|
+
supportsGoogleIMA,
|
|
5332
|
+
supportsModernJS,
|
|
4371
5333
|
supportsWebKitPresentationMode
|
|
4372
5334
|
});
|
|
4373
5335
|
//# sourceMappingURL=index.cjs.map
|