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
|
@@ -33,7 +33,166 @@ __export(StormcloudVideoPlayer_exports, {
|
|
|
33
33
|
StormcloudVideoPlayer: () => StormcloudVideoPlayer
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(StormcloudVideoPlayer_exports);
|
|
36
|
-
var
|
|
36
|
+
var import_hls2 = __toESM(require("hls.js"), 1);
|
|
37
|
+
|
|
38
|
+
// src/utils/browserCompat.ts
|
|
39
|
+
function getChromeVersion(ua) {
|
|
40
|
+
const match = ua.match(/Chrome\/(\d+)/);
|
|
41
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
42
|
+
}
|
|
43
|
+
function getWebKitVersion(ua) {
|
|
44
|
+
const match = ua.match(/AppleWebKit\/(\d+)/);
|
|
45
|
+
return match && match[1] ? parseInt(match[1], 10) : 0;
|
|
46
|
+
}
|
|
47
|
+
function getPlatform() {
|
|
48
|
+
if ("userAgentData" in navigator && navigator.userAgentData?.platform) {
|
|
49
|
+
return navigator.userAgentData.platform;
|
|
50
|
+
}
|
|
51
|
+
const ua = navigator.userAgent;
|
|
52
|
+
if (/Mac|iPhone|iPad|iPod/i.test(ua)) {
|
|
53
|
+
return /iPhone|iPad|iPod/i.test(ua) ? "iPhone" : "MacIntel";
|
|
54
|
+
}
|
|
55
|
+
if (/Win/i.test(ua)) {
|
|
56
|
+
return "Win32";
|
|
57
|
+
}
|
|
58
|
+
if (/Linux/i.test(ua)) {
|
|
59
|
+
return /Android/i.test(ua) ? "Linux armv8l" : "Linux x86_64";
|
|
60
|
+
}
|
|
61
|
+
if (/CrOS/i.test(ua)) {
|
|
62
|
+
return "CrOS";
|
|
63
|
+
}
|
|
64
|
+
return navigator.platform || "Unknown";
|
|
65
|
+
}
|
|
66
|
+
function detectBrowser() {
|
|
67
|
+
const ua = navigator.userAgent;
|
|
68
|
+
const platform = getPlatform();
|
|
69
|
+
let name = "Unknown";
|
|
70
|
+
let version = "0";
|
|
71
|
+
let majorVersion = 0;
|
|
72
|
+
let isSmartTV = false;
|
|
73
|
+
let isLegacyTV = false;
|
|
74
|
+
let supportsIMA = true;
|
|
75
|
+
let supportsModernJS = true;
|
|
76
|
+
let recommendedAdPlayer = "ima";
|
|
77
|
+
if (/Web0S|webOS/i.test(ua)) {
|
|
78
|
+
name = "LG WebOS";
|
|
79
|
+
isSmartTV = true;
|
|
80
|
+
const match = ua.match(/Web0S[/\s]*([\d.]+)/i);
|
|
81
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
82
|
+
if (version !== "Unknown") {
|
|
83
|
+
const parts = version.split(".");
|
|
84
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
85
|
+
}
|
|
86
|
+
} else if (/Tizen/i.test(ua)) {
|
|
87
|
+
name = "Samsung Tizen";
|
|
88
|
+
isSmartTV = true;
|
|
89
|
+
const match = ua.match(/Tizen[/\s]*([\d.]+)/i);
|
|
90
|
+
version = match && match[1] ? match[1] : "Unknown";
|
|
91
|
+
if (version !== "Unknown") {
|
|
92
|
+
const parts = version.split(".");
|
|
93
|
+
majorVersion = parts[0] ? parseInt(parts[0], 10) : 0;
|
|
94
|
+
}
|
|
95
|
+
} else if (/SMART-TV|SmartTV/i.test(ua)) {
|
|
96
|
+
name = "Smart TV";
|
|
97
|
+
isSmartTV = true;
|
|
98
|
+
} else if (/NetCast/i.test(ua)) {
|
|
99
|
+
name = "LG NetCast";
|
|
100
|
+
isSmartTV = true;
|
|
101
|
+
isLegacyTV = true;
|
|
102
|
+
} else if (/BRAVIA/i.test(ua)) {
|
|
103
|
+
name = "Sony BRAVIA";
|
|
104
|
+
isSmartTV = true;
|
|
105
|
+
}
|
|
106
|
+
const chromeVersion = getChromeVersion(ua);
|
|
107
|
+
const webkitVersion = getWebKitVersion(ua);
|
|
108
|
+
if (chromeVersion > 0) {
|
|
109
|
+
if (!isSmartTV) {
|
|
110
|
+
name = "Chrome";
|
|
111
|
+
version = chromeVersion.toString();
|
|
112
|
+
majorVersion = chromeVersion;
|
|
113
|
+
}
|
|
114
|
+
if (chromeVersion < 50) {
|
|
115
|
+
supportsIMA = false;
|
|
116
|
+
supportsModernJS = false;
|
|
117
|
+
isLegacyTV = true;
|
|
118
|
+
recommendedAdPlayer = "hls";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (webkitVersion > 0 && webkitVersion < 600) {
|
|
122
|
+
supportsModernJS = false;
|
|
123
|
+
if (isSmartTV) {
|
|
124
|
+
isLegacyTV = true;
|
|
125
|
+
supportsIMA = false;
|
|
126
|
+
recommendedAdPlayer = "hls";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (typeof Promise === "undefined" || typeof Map === "undefined" || typeof Set === "undefined") {
|
|
130
|
+
supportsModernJS = false;
|
|
131
|
+
supportsIMA = false;
|
|
132
|
+
recommendedAdPlayer = "hls";
|
|
133
|
+
}
|
|
134
|
+
if (typeof URLSearchParams === "undefined") {
|
|
135
|
+
supportsModernJS = false;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
name,
|
|
139
|
+
version,
|
|
140
|
+
majorVersion,
|
|
141
|
+
isSmartTV,
|
|
142
|
+
isLegacyTV,
|
|
143
|
+
platform,
|
|
144
|
+
supportsIMA,
|
|
145
|
+
supportsModernJS,
|
|
146
|
+
recommendedAdPlayer
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function supportsGoogleIMA() {
|
|
150
|
+
const browser = detectBrowser();
|
|
151
|
+
if (browser.isLegacyTV) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
if (typeof document === "undefined" || typeof document.createElement !== "function") {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const video = document.createElement("video");
|
|
159
|
+
if (!video) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (typeof Promise === "undefined") {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return browser.supportsIMA;
|
|
169
|
+
}
|
|
170
|
+
function logBrowserInfo(debug = false) {
|
|
171
|
+
if (!debug) return;
|
|
172
|
+
const browser = detectBrowser();
|
|
173
|
+
const imaSupport = supportsGoogleIMA();
|
|
174
|
+
console.log("[StormcloudVideoPlayer] Browser Compatibility Info:", {
|
|
175
|
+
browser: `${browser.name} ${browser.version}`,
|
|
176
|
+
platform: browser.platform,
|
|
177
|
+
isSmartTV: browser.isSmartTV,
|
|
178
|
+
isLegacyTV: browser.isLegacyTV,
|
|
179
|
+
supportsIMA: imaSupport,
|
|
180
|
+
supportsModernJS: browser.supportsModernJS,
|
|
181
|
+
recommendedAdPlayer: browser.recommendedAdPlayer,
|
|
182
|
+
userAgent: navigator.userAgent
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function getBrowserConfigOverrides() {
|
|
186
|
+
const browser = detectBrowser();
|
|
187
|
+
const overrides = {};
|
|
188
|
+
if (browser.isLegacyTV || !browser.supportsIMA) {
|
|
189
|
+
overrides.adPlayerType = "hls";
|
|
190
|
+
}
|
|
191
|
+
if (browser.isSmartTV) {
|
|
192
|
+
overrides.allowNativeHls = true;
|
|
193
|
+
}
|
|
194
|
+
return overrides;
|
|
195
|
+
}
|
|
37
196
|
|
|
38
197
|
// src/sdk/ima.ts
|
|
39
198
|
function createImaController(video, options) {
|
|
@@ -51,6 +210,14 @@ function createImaController(video, options) {
|
|
|
51
210
|
}
|
|
52
211
|
}
|
|
53
212
|
function ensureImaLoaded() {
|
|
213
|
+
if (!supportsGoogleIMA()) {
|
|
214
|
+
console.warn(
|
|
215
|
+
"[IMA] Google IMA SDK is not supported on this browser. Please use HLS ad player instead."
|
|
216
|
+
);
|
|
217
|
+
return Promise.reject(
|
|
218
|
+
new Error("Google IMA SDK not supported on this browser")
|
|
219
|
+
);
|
|
220
|
+
}
|
|
54
221
|
try {
|
|
55
222
|
const frameEl = window.frameElement;
|
|
56
223
|
const sandboxAttr = frameEl?.getAttribute?.("sandbox") || "";
|
|
@@ -546,6 +713,456 @@ function createImaController(video, options) {
|
|
|
546
713
|
};
|
|
547
714
|
}
|
|
548
715
|
|
|
716
|
+
// src/sdk/hlsAdPlayer.ts
|
|
717
|
+
var import_hls = __toESM(require("hls.js"), 1);
|
|
718
|
+
function createHlsAdPlayer(contentVideo, options) {
|
|
719
|
+
let adPlaying = false;
|
|
720
|
+
let originalMutedState = false;
|
|
721
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
722
|
+
const licenseKey = options?.licenseKey;
|
|
723
|
+
let adVideoElement;
|
|
724
|
+
let adHls;
|
|
725
|
+
let adContainerEl;
|
|
726
|
+
let currentAd;
|
|
727
|
+
let sessionId;
|
|
728
|
+
let trackingFired = {
|
|
729
|
+
impression: false,
|
|
730
|
+
start: false,
|
|
731
|
+
firstQuartile: false,
|
|
732
|
+
midpoint: false,
|
|
733
|
+
thirdQuartile: false,
|
|
734
|
+
complete: false
|
|
735
|
+
};
|
|
736
|
+
function emit(event, payload) {
|
|
737
|
+
const set = listeners.get(event);
|
|
738
|
+
if (!set) return;
|
|
739
|
+
for (const fn of Array.from(set)) {
|
|
740
|
+
try {
|
|
741
|
+
fn(payload);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function generateSessionId() {
|
|
748
|
+
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
749
|
+
}
|
|
750
|
+
function fireTrackingPixels(urls) {
|
|
751
|
+
if (!urls || urls.length === 0) return;
|
|
752
|
+
urls.forEach((url) => {
|
|
753
|
+
try {
|
|
754
|
+
let trackingUrl = url;
|
|
755
|
+
if (sessionId) {
|
|
756
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}session_id=${sessionId}`;
|
|
757
|
+
}
|
|
758
|
+
if (licenseKey) {
|
|
759
|
+
trackingUrl = `${trackingUrl}${trackingUrl.includes("?") ? "&" : "?"}license_key=${licenseKey}`;
|
|
760
|
+
}
|
|
761
|
+
const img = new Image(1, 1);
|
|
762
|
+
img.src = trackingUrl;
|
|
763
|
+
console.log(`[HlsAdPlayer] Fired tracking pixel: ${trackingUrl}`);
|
|
764
|
+
} catch (error) {
|
|
765
|
+
console.warn(`[HlsAdPlayer] Error firing tracking pixel:`, error);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
function parseVastXml(xmlString) {
|
|
770
|
+
try {
|
|
771
|
+
const parser = new DOMParser();
|
|
772
|
+
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
|
773
|
+
const parserError = xmlDoc.querySelector("parsererror");
|
|
774
|
+
if (parserError) {
|
|
775
|
+
console.error("[HlsAdPlayer] XML parsing error:", parserError.textContent);
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
const adElement = xmlDoc.querySelector("Ad");
|
|
779
|
+
if (!adElement) {
|
|
780
|
+
console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
const adId = adElement.getAttribute("id") || "unknown";
|
|
784
|
+
const title = xmlDoc.querySelector("AdTitle")?.textContent || "Ad";
|
|
785
|
+
const durationText = xmlDoc.querySelector("Duration")?.textContent || "00:00:30";
|
|
786
|
+
const durationParts = durationText.split(":");
|
|
787
|
+
const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
788
|
+
const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
|
|
789
|
+
const mediaFiles = [];
|
|
790
|
+
mediaFileElements.forEach((mf) => {
|
|
791
|
+
const type = mf.getAttribute("type") || "";
|
|
792
|
+
if (type === "application/x-mpegURL" || type.includes("m3u8")) {
|
|
793
|
+
const bitrateAttr = mf.getAttribute("bitrate");
|
|
794
|
+
const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
795
|
+
mediaFiles.push({
|
|
796
|
+
url: mf.textContent?.trim() || "",
|
|
797
|
+
type,
|
|
798
|
+
width: parseInt(mf.getAttribute("width") || "1920", 10),
|
|
799
|
+
height: parseInt(mf.getAttribute("height") || "1080", 10),
|
|
800
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
if (mediaFiles.length === 0) {
|
|
805
|
+
console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
const trackingUrls = {
|
|
809
|
+
impression: [],
|
|
810
|
+
start: [],
|
|
811
|
+
firstQuartile: [],
|
|
812
|
+
midpoint: [],
|
|
813
|
+
thirdQuartile: [],
|
|
814
|
+
complete: [],
|
|
815
|
+
mute: [],
|
|
816
|
+
unmute: [],
|
|
817
|
+
pause: [],
|
|
818
|
+
resume: [],
|
|
819
|
+
fullscreen: [],
|
|
820
|
+
exitFullscreen: [],
|
|
821
|
+
skip: [],
|
|
822
|
+
error: []
|
|
823
|
+
};
|
|
824
|
+
xmlDoc.querySelectorAll("Impression").forEach((el) => {
|
|
825
|
+
const url = el.textContent?.trim();
|
|
826
|
+
if (url) trackingUrls.impression.push(url);
|
|
827
|
+
});
|
|
828
|
+
xmlDoc.querySelectorAll("Tracking").forEach((el) => {
|
|
829
|
+
const event = el.getAttribute("event");
|
|
830
|
+
const url = el.textContent?.trim();
|
|
831
|
+
if (event && url) {
|
|
832
|
+
const eventKey = event;
|
|
833
|
+
if (trackingUrls[eventKey]) {
|
|
834
|
+
trackingUrls[eventKey].push(url);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
const clickThrough = xmlDoc.querySelector("ClickThrough")?.textContent?.trim();
|
|
839
|
+
return {
|
|
840
|
+
id: adId,
|
|
841
|
+
title,
|
|
842
|
+
duration,
|
|
843
|
+
mediaFiles,
|
|
844
|
+
trackingUrls,
|
|
845
|
+
clickThrough
|
|
846
|
+
};
|
|
847
|
+
} catch (error) {
|
|
848
|
+
console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function createAdVideoElement() {
|
|
853
|
+
const video = document.createElement("video");
|
|
854
|
+
video.style.position = "absolute";
|
|
855
|
+
video.style.left = "0";
|
|
856
|
+
video.style.top = "0";
|
|
857
|
+
video.style.width = "100%";
|
|
858
|
+
video.style.height = "100%";
|
|
859
|
+
video.style.objectFit = "contain";
|
|
860
|
+
video.style.backgroundColor = "#000";
|
|
861
|
+
video.playsInline = true;
|
|
862
|
+
video.muted = false;
|
|
863
|
+
return video;
|
|
864
|
+
}
|
|
865
|
+
function setupAdEventListeners() {
|
|
866
|
+
if (!adVideoElement || !currentAd) return;
|
|
867
|
+
adVideoElement.addEventListener("timeupdate", () => {
|
|
868
|
+
if (!currentAd || !adVideoElement) return;
|
|
869
|
+
const progress = adVideoElement.currentTime / currentAd.duration;
|
|
870
|
+
if (progress >= 0.25 && !trackingFired.firstQuartile) {
|
|
871
|
+
trackingFired.firstQuartile = true;
|
|
872
|
+
fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
|
|
873
|
+
}
|
|
874
|
+
if (progress >= 0.5 && !trackingFired.midpoint) {
|
|
875
|
+
trackingFired.midpoint = true;
|
|
876
|
+
fireTrackingPixels(currentAd.trackingUrls.midpoint);
|
|
877
|
+
}
|
|
878
|
+
if (progress >= 0.75 && !trackingFired.thirdQuartile) {
|
|
879
|
+
trackingFired.thirdQuartile = true;
|
|
880
|
+
fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
adVideoElement.addEventListener("playing", () => {
|
|
884
|
+
if (!currentAd || trackingFired.start) return;
|
|
885
|
+
trackingFired.start = true;
|
|
886
|
+
fireTrackingPixels(currentAd.trackingUrls.start);
|
|
887
|
+
console.log("[HlsAdPlayer] Ad started playing");
|
|
888
|
+
});
|
|
889
|
+
adVideoElement.addEventListener("ended", () => {
|
|
890
|
+
if (!currentAd || trackingFired.complete) return;
|
|
891
|
+
trackingFired.complete = true;
|
|
892
|
+
fireTrackingPixels(currentAd.trackingUrls.complete);
|
|
893
|
+
console.log("[HlsAdPlayer] Ad completed");
|
|
894
|
+
handleAdComplete();
|
|
895
|
+
});
|
|
896
|
+
adVideoElement.addEventListener("error", (e) => {
|
|
897
|
+
console.error("[HlsAdPlayer] Ad video error:", e);
|
|
898
|
+
if (currentAd) {
|
|
899
|
+
fireTrackingPixels(currentAd.trackingUrls.error);
|
|
900
|
+
}
|
|
901
|
+
handleAdError();
|
|
902
|
+
});
|
|
903
|
+
adVideoElement.addEventListener("volumechange", () => {
|
|
904
|
+
if (!currentAd) return;
|
|
905
|
+
if (adVideoElement.muted) {
|
|
906
|
+
fireTrackingPixels(currentAd.trackingUrls.mute);
|
|
907
|
+
} else {
|
|
908
|
+
fireTrackingPixels(currentAd.trackingUrls.unmute);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
adVideoElement.addEventListener("pause", () => {
|
|
912
|
+
if (currentAd && !adVideoElement.ended) {
|
|
913
|
+
fireTrackingPixels(currentAd.trackingUrls.pause);
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
adVideoElement.addEventListener("play", () => {
|
|
917
|
+
if (currentAd && adVideoElement.currentTime > 0) {
|
|
918
|
+
fireTrackingPixels(currentAd.trackingUrls.resume);
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
function handleAdComplete() {
|
|
923
|
+
console.log("[HlsAdPlayer] Handling ad completion");
|
|
924
|
+
adPlaying = false;
|
|
925
|
+
contentVideo.muted = originalMutedState;
|
|
926
|
+
if (adContainerEl) {
|
|
927
|
+
adContainerEl.style.display = "none";
|
|
928
|
+
adContainerEl.style.pointerEvents = "none";
|
|
929
|
+
}
|
|
930
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
931
|
+
contentVideo.play().catch(() => {
|
|
932
|
+
});
|
|
933
|
+
console.log("[HlsAdPlayer] Content resumed (VOD mode)");
|
|
934
|
+
} else {
|
|
935
|
+
console.log("[HlsAdPlayer] Content unmuted (Live mode)");
|
|
936
|
+
}
|
|
937
|
+
emit("content_resume");
|
|
938
|
+
emit("all_ads_completed");
|
|
939
|
+
}
|
|
940
|
+
function handleAdError() {
|
|
941
|
+
console.log("[HlsAdPlayer] Handling ad error");
|
|
942
|
+
adPlaying = false;
|
|
943
|
+
contentVideo.muted = originalMutedState;
|
|
944
|
+
if (adContainerEl) {
|
|
945
|
+
adContainerEl.style.display = "none";
|
|
946
|
+
adContainerEl.style.pointerEvents = "none";
|
|
947
|
+
}
|
|
948
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
949
|
+
if (contentVideo.paused) {
|
|
950
|
+
contentVideo.play().catch(() => {
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
emit("ad_error");
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
initialize() {
|
|
958
|
+
console.log("[HlsAdPlayer] Initializing");
|
|
959
|
+
if (!adContainerEl) {
|
|
960
|
+
const container = document.createElement("div");
|
|
961
|
+
container.style.position = "absolute";
|
|
962
|
+
container.style.left = "0";
|
|
963
|
+
container.style.top = "0";
|
|
964
|
+
container.style.right = "0";
|
|
965
|
+
container.style.bottom = "0";
|
|
966
|
+
container.style.display = "none";
|
|
967
|
+
container.style.alignItems = "center";
|
|
968
|
+
container.style.justifyContent = "center";
|
|
969
|
+
container.style.pointerEvents = "none";
|
|
970
|
+
container.style.zIndex = "2";
|
|
971
|
+
container.style.backgroundColor = "#000";
|
|
972
|
+
contentVideo.parentElement?.appendChild(container);
|
|
973
|
+
adContainerEl = container;
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
async requestAds(vastTagUrl) {
|
|
977
|
+
console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
|
|
978
|
+
if (adPlaying) {
|
|
979
|
+
console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
|
|
980
|
+
return Promise.reject(new Error("Ad already playing"));
|
|
981
|
+
}
|
|
982
|
+
try {
|
|
983
|
+
sessionId = generateSessionId();
|
|
984
|
+
const response = await fetch(vastTagUrl);
|
|
985
|
+
if (!response.ok) {
|
|
986
|
+
throw new Error(`Failed to fetch VAST: ${response.statusText}`);
|
|
987
|
+
}
|
|
988
|
+
const vastXml = await response.text();
|
|
989
|
+
console.log("[HlsAdPlayer] VAST XML received");
|
|
990
|
+
const ad = parseVastXml(vastXml);
|
|
991
|
+
if (!ad) {
|
|
992
|
+
throw new Error("Failed to parse VAST XML or no ads available");
|
|
993
|
+
}
|
|
994
|
+
currentAd = ad;
|
|
995
|
+
console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
|
|
996
|
+
fireTrackingPixels(ad.trackingUrls.impression);
|
|
997
|
+
trackingFired.impression = true;
|
|
998
|
+
return Promise.resolve();
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
console.error("[HlsAdPlayer] Error requesting ads:", error);
|
|
1001
|
+
emit("ad_error");
|
|
1002
|
+
return Promise.reject(error);
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
async play() {
|
|
1006
|
+
if (!currentAd) {
|
|
1007
|
+
console.warn("[HlsAdPlayer] Cannot play: No ad loaded");
|
|
1008
|
+
return Promise.reject(new Error("No ad loaded"));
|
|
1009
|
+
}
|
|
1010
|
+
console.log("[HlsAdPlayer] Starting ad playback");
|
|
1011
|
+
try {
|
|
1012
|
+
if (!adVideoElement) {
|
|
1013
|
+
adVideoElement = createAdVideoElement();
|
|
1014
|
+
adContainerEl?.appendChild(adVideoElement);
|
|
1015
|
+
setupAdEventListeners();
|
|
1016
|
+
}
|
|
1017
|
+
trackingFired = {
|
|
1018
|
+
impression: trackingFired.impression,
|
|
1019
|
+
start: false,
|
|
1020
|
+
firstQuartile: false,
|
|
1021
|
+
midpoint: false,
|
|
1022
|
+
thirdQuartile: false,
|
|
1023
|
+
complete: false
|
|
1024
|
+
};
|
|
1025
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
1026
|
+
contentVideo.pause();
|
|
1027
|
+
console.log("[HlsAdPlayer] Content paused (VOD mode)");
|
|
1028
|
+
} else {
|
|
1029
|
+
console.log("[HlsAdPlayer] Content continues (Live mode)");
|
|
1030
|
+
}
|
|
1031
|
+
contentVideo.muted = true;
|
|
1032
|
+
adPlaying = true;
|
|
1033
|
+
if (adContainerEl) {
|
|
1034
|
+
adContainerEl.style.display = "flex";
|
|
1035
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
1036
|
+
}
|
|
1037
|
+
emit("content_pause");
|
|
1038
|
+
const mediaFile = currentAd.mediaFiles[0];
|
|
1039
|
+
if (!mediaFile) {
|
|
1040
|
+
throw new Error("No media file available for ad");
|
|
1041
|
+
}
|
|
1042
|
+
console.log(`[HlsAdPlayer] Loading ad from: ${mediaFile.url}`);
|
|
1043
|
+
if (import_hls.default.isSupported()) {
|
|
1044
|
+
if (adHls) {
|
|
1045
|
+
adHls.destroy();
|
|
1046
|
+
}
|
|
1047
|
+
adHls = new import_hls.default({
|
|
1048
|
+
enableWorker: true,
|
|
1049
|
+
lowLatencyMode: false
|
|
1050
|
+
});
|
|
1051
|
+
adHls.loadSource(mediaFile.url);
|
|
1052
|
+
adHls.attachMedia(adVideoElement);
|
|
1053
|
+
adHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
|
|
1054
|
+
console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
|
|
1055
|
+
adVideoElement.play().catch((error) => {
|
|
1056
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1057
|
+
handleAdError();
|
|
1058
|
+
});
|
|
1059
|
+
});
|
|
1060
|
+
adHls.on(import_hls.default.Events.ERROR, (event, data) => {
|
|
1061
|
+
console.error("[HlsAdPlayer] HLS error:", data);
|
|
1062
|
+
if (data.fatal) {
|
|
1063
|
+
handleAdError();
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
} else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
|
|
1067
|
+
adVideoElement.src = mediaFile.url;
|
|
1068
|
+
adVideoElement.play().catch((error) => {
|
|
1069
|
+
console.error("[HlsAdPlayer] Error starting ad playback:", error);
|
|
1070
|
+
handleAdError();
|
|
1071
|
+
});
|
|
1072
|
+
} else {
|
|
1073
|
+
throw new Error("HLS not supported");
|
|
1074
|
+
}
|
|
1075
|
+
return Promise.resolve();
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
console.error("[HlsAdPlayer] Error playing ad:", error);
|
|
1078
|
+
handleAdError();
|
|
1079
|
+
return Promise.reject(error);
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
async stop() {
|
|
1083
|
+
console.log("[HlsAdPlayer] Stopping ad");
|
|
1084
|
+
adPlaying = false;
|
|
1085
|
+
contentVideo.muted = originalMutedState;
|
|
1086
|
+
if (adContainerEl) {
|
|
1087
|
+
adContainerEl.style.display = "none";
|
|
1088
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1089
|
+
}
|
|
1090
|
+
if (adHls) {
|
|
1091
|
+
adHls.destroy();
|
|
1092
|
+
adHls = void 0;
|
|
1093
|
+
}
|
|
1094
|
+
if (adVideoElement) {
|
|
1095
|
+
adVideoElement.pause();
|
|
1096
|
+
adVideoElement.src = "";
|
|
1097
|
+
}
|
|
1098
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
1099
|
+
contentVideo.play().catch(() => {
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
currentAd = void 0;
|
|
1103
|
+
},
|
|
1104
|
+
destroy() {
|
|
1105
|
+
console.log("[HlsAdPlayer] Destroying");
|
|
1106
|
+
adPlaying = false;
|
|
1107
|
+
contentVideo.muted = originalMutedState;
|
|
1108
|
+
if (adHls) {
|
|
1109
|
+
adHls.destroy();
|
|
1110
|
+
adHls = void 0;
|
|
1111
|
+
}
|
|
1112
|
+
if (adVideoElement) {
|
|
1113
|
+
adVideoElement.pause();
|
|
1114
|
+
adVideoElement.src = "";
|
|
1115
|
+
adVideoElement.remove();
|
|
1116
|
+
adVideoElement = void 0;
|
|
1117
|
+
}
|
|
1118
|
+
if (adContainerEl?.parentElement) {
|
|
1119
|
+
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
1120
|
+
}
|
|
1121
|
+
adContainerEl = void 0;
|
|
1122
|
+
currentAd = void 0;
|
|
1123
|
+
listeners.clear();
|
|
1124
|
+
},
|
|
1125
|
+
isAdPlaying() {
|
|
1126
|
+
return adPlaying;
|
|
1127
|
+
},
|
|
1128
|
+
resize(width, height) {
|
|
1129
|
+
console.log(`[HlsAdPlayer] Resizing to ${width}x${height}`);
|
|
1130
|
+
if (adContainerEl) {
|
|
1131
|
+
adContainerEl.style.width = `${width}px`;
|
|
1132
|
+
adContainerEl.style.height = `${height}px`;
|
|
1133
|
+
}
|
|
1134
|
+
if (adVideoElement) {
|
|
1135
|
+
adVideoElement.style.width = `${width}px`;
|
|
1136
|
+
adVideoElement.style.height = `${height}px`;
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
on(event, listener) {
|
|
1140
|
+
if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
|
|
1141
|
+
listeners.get(event).add(listener);
|
|
1142
|
+
},
|
|
1143
|
+
off(event, listener) {
|
|
1144
|
+
listeners.get(event)?.delete(listener);
|
|
1145
|
+
},
|
|
1146
|
+
updateOriginalMutedState(muted) {
|
|
1147
|
+
originalMutedState = muted;
|
|
1148
|
+
},
|
|
1149
|
+
getOriginalMutedState() {
|
|
1150
|
+
return originalMutedState;
|
|
1151
|
+
},
|
|
1152
|
+
setAdVolume(volume) {
|
|
1153
|
+
if (adVideoElement && adPlaying) {
|
|
1154
|
+
adVideoElement.volume = Math.max(0, Math.min(1, volume));
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
getAdVolume() {
|
|
1158
|
+
if (adVideoElement && adPlaying) {
|
|
1159
|
+
return adVideoElement.volume;
|
|
1160
|
+
}
|
|
1161
|
+
return 1;
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
|
|
549
1166
|
// src/utils/tracking.ts
|
|
550
1167
|
var cachedBrowserId = null;
|
|
551
1168
|
function getClientInfo() {
|
|
@@ -697,10 +1314,18 @@ async function getBrowserID(clientInfo) {
|
|
|
697
1314
|
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
698
1315
|
try {
|
|
699
1316
|
await crypto.subtle.digest("SHA-256", new Uint8Array([1, 2, 3]));
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
new TextEncoder().encode(fingerprintString)
|
|
703
|
-
|
|
1317
|
+
let encodedData;
|
|
1318
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1319
|
+
encodedData = new TextEncoder().encode(fingerprintString);
|
|
1320
|
+
} else {
|
|
1321
|
+
const utf8 = unescape(encodeURIComponent(fingerprintString));
|
|
1322
|
+
const buffer = new Uint8Array(utf8.length);
|
|
1323
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
1324
|
+
buffer[i] = utf8.charCodeAt(i);
|
|
1325
|
+
}
|
|
1326
|
+
encodedData = buffer;
|
|
1327
|
+
}
|
|
1328
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encodedData);
|
|
704
1329
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
705
1330
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
706
1331
|
cachedBrowserId = hashHex;
|
|
@@ -787,6 +1412,215 @@ async function sendHeartbeat(licenseKey) {
|
|
|
787
1412
|
}
|
|
788
1413
|
}
|
|
789
1414
|
|
|
1415
|
+
// src/utils/polyfills.ts
|
|
1416
|
+
function polyfillURLSearchParams() {
|
|
1417
|
+
if (typeof URLSearchParams !== "undefined") {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
class URLSearchParamsPolyfill {
|
|
1421
|
+
constructor(init) {
|
|
1422
|
+
this.params = /* @__PURE__ */ new Map();
|
|
1423
|
+
if (typeof init === "string") {
|
|
1424
|
+
this.parseQueryString(init);
|
|
1425
|
+
} else if (init instanceof URLSearchParamsPolyfill) {
|
|
1426
|
+
init.forEach((value, key) => {
|
|
1427
|
+
this.append(key, value);
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
parseQueryString(query) {
|
|
1432
|
+
const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
|
|
1433
|
+
if (!cleanQuery) return;
|
|
1434
|
+
cleanQuery.split("&").forEach((param) => {
|
|
1435
|
+
const [key, value] = param.split("=");
|
|
1436
|
+
if (key) {
|
|
1437
|
+
const decodedKey = this.safeDecodeURIComponent(key);
|
|
1438
|
+
const decodedValue = value ? this.safeDecodeURIComponent(value) : "";
|
|
1439
|
+
this.append(decodedKey, decodedValue);
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
safeDecodeURIComponent(str) {
|
|
1444
|
+
try {
|
|
1445
|
+
return decodeURIComponent(str.replace(/\+/g, " "));
|
|
1446
|
+
} catch (e) {
|
|
1447
|
+
return str;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
append(name, value) {
|
|
1451
|
+
const values = this.params.get(name) || [];
|
|
1452
|
+
values.push(String(value));
|
|
1453
|
+
this.params.set(name, values);
|
|
1454
|
+
}
|
|
1455
|
+
delete(name) {
|
|
1456
|
+
this.params.delete(name);
|
|
1457
|
+
}
|
|
1458
|
+
get(name) {
|
|
1459
|
+
const values = this.params.get(name);
|
|
1460
|
+
return values && values.length > 0 && values[0] !== void 0 ? values[0] : null;
|
|
1461
|
+
}
|
|
1462
|
+
getAll(name) {
|
|
1463
|
+
return this.params.get(name) || [];
|
|
1464
|
+
}
|
|
1465
|
+
has(name) {
|
|
1466
|
+
return this.params.has(name);
|
|
1467
|
+
}
|
|
1468
|
+
set(name, value) {
|
|
1469
|
+
this.params.set(name, [String(value)]);
|
|
1470
|
+
}
|
|
1471
|
+
forEach(callback) {
|
|
1472
|
+
this.params.forEach((values, key) => {
|
|
1473
|
+
values.forEach((value) => {
|
|
1474
|
+
callback(value, key, this);
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
toString() {
|
|
1479
|
+
const parts = [];
|
|
1480
|
+
this.params.forEach((values, key) => {
|
|
1481
|
+
values.forEach((value) => {
|
|
1482
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
1483
|
+
});
|
|
1484
|
+
});
|
|
1485
|
+
return parts.join("&");
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
window.URLSearchParams = URLSearchParamsPolyfill;
|
|
1489
|
+
}
|
|
1490
|
+
function polyfillTextEncoder() {
|
|
1491
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
class TextEncoderPolyfill {
|
|
1495
|
+
constructor() {
|
|
1496
|
+
this.encoding = "utf-8";
|
|
1497
|
+
}
|
|
1498
|
+
encode(str) {
|
|
1499
|
+
const utf8 = [];
|
|
1500
|
+
for (let i = 0; i < str.length; i++) {
|
|
1501
|
+
let charcode = str.charCodeAt(i);
|
|
1502
|
+
if (charcode < 128) {
|
|
1503
|
+
utf8.push(charcode);
|
|
1504
|
+
} else if (charcode < 2048) {
|
|
1505
|
+
utf8.push(192 | charcode >> 6, 128 | charcode & 63);
|
|
1506
|
+
} else if (charcode < 55296 || charcode >= 57344) {
|
|
1507
|
+
utf8.push(
|
|
1508
|
+
224 | charcode >> 12,
|
|
1509
|
+
128 | charcode >> 6 & 63,
|
|
1510
|
+
128 | charcode & 63
|
|
1511
|
+
);
|
|
1512
|
+
} else {
|
|
1513
|
+
i++;
|
|
1514
|
+
charcode = 65536 + ((charcode & 1023) << 10 | str.charCodeAt(i) & 1023);
|
|
1515
|
+
utf8.push(
|
|
1516
|
+
240 | charcode >> 18,
|
|
1517
|
+
128 | charcode >> 12 & 63,
|
|
1518
|
+
128 | charcode >> 6 & 63,
|
|
1519
|
+
128 | charcode & 63
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return new Uint8Array(utf8);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
window.TextEncoder = TextEncoderPolyfill;
|
|
1527
|
+
}
|
|
1528
|
+
function polyfillPromiseFinally() {
|
|
1529
|
+
if (typeof Promise !== "undefined" && !Promise.prototype.finally) {
|
|
1530
|
+
Promise.prototype.finally = function(callback) {
|
|
1531
|
+
const constructor = this.constructor;
|
|
1532
|
+
return this.then(
|
|
1533
|
+
(value) => constructor.resolve(callback()).then(() => value),
|
|
1534
|
+
(reason) => constructor.resolve(callback()).then(() => {
|
|
1535
|
+
throw reason;
|
|
1536
|
+
})
|
|
1537
|
+
);
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function polyfillObjectAssign() {
|
|
1542
|
+
if (typeof Object.assign !== "function") {
|
|
1543
|
+
Object.assign = function(target, ...sources) {
|
|
1544
|
+
if (target == null) {
|
|
1545
|
+
throw new TypeError("Cannot convert undefined or null to object");
|
|
1546
|
+
}
|
|
1547
|
+
const to = Object(target);
|
|
1548
|
+
for (let i = 0; i < sources.length; i++) {
|
|
1549
|
+
const nextSource = sources[i];
|
|
1550
|
+
if (nextSource != null) {
|
|
1551
|
+
for (const nextKey in nextSource) {
|
|
1552
|
+
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
|
1553
|
+
to[nextKey] = nextSource[nextKey];
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
return to;
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
function polyfillArrayFrom() {
|
|
1563
|
+
if (!Array.from) {
|
|
1564
|
+
Array.from = function(arrayLike, mapFn, thisArg) {
|
|
1565
|
+
const items = Object(arrayLike);
|
|
1566
|
+
if (arrayLike == null) {
|
|
1567
|
+
throw new TypeError("Array.from requires an array-like object");
|
|
1568
|
+
}
|
|
1569
|
+
const len = items.length >>> 0;
|
|
1570
|
+
const result = new Array(len);
|
|
1571
|
+
for (let i = 0; i < len; i++) {
|
|
1572
|
+
if (mapFn) {
|
|
1573
|
+
result[i] = mapFn.call(thisArg, items[i], i);
|
|
1574
|
+
} else {
|
|
1575
|
+
result[i] = items[i];
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return result;
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
function polyfillStringStartsWith() {
|
|
1583
|
+
if (!String.prototype.startsWith) {
|
|
1584
|
+
String.prototype.startsWith = function(search, pos) {
|
|
1585
|
+
pos = !pos || pos < 0 ? 0 : +pos;
|
|
1586
|
+
return this.substring(pos, pos + search.length) === search;
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
function polyfillStringEndsWith() {
|
|
1591
|
+
if (!String.prototype.endsWith) {
|
|
1592
|
+
String.prototype.endsWith = function(search, length) {
|
|
1593
|
+
if (length === void 0 || length > this.length) {
|
|
1594
|
+
length = this.length;
|
|
1595
|
+
}
|
|
1596
|
+
return this.substring(length - search.length, length) === search;
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
function polyfillStringIncludes() {
|
|
1601
|
+
if (!String.prototype.includes) {
|
|
1602
|
+
String.prototype.includes = function(search, start) {
|
|
1603
|
+
if (typeof start !== "number") {
|
|
1604
|
+
start = 0;
|
|
1605
|
+
}
|
|
1606
|
+
if (start + search.length > this.length) {
|
|
1607
|
+
return false;
|
|
1608
|
+
}
|
|
1609
|
+
return this.indexOf(search, start) !== -1;
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
function initializePolyfills() {
|
|
1614
|
+
polyfillObjectAssign();
|
|
1615
|
+
polyfillArrayFrom();
|
|
1616
|
+
polyfillStringStartsWith();
|
|
1617
|
+
polyfillStringEndsWith();
|
|
1618
|
+
polyfillStringIncludes();
|
|
1619
|
+
polyfillURLSearchParams();
|
|
1620
|
+
polyfillTextEncoder();
|
|
1621
|
+
polyfillPromiseFinally();
|
|
1622
|
+
}
|
|
1623
|
+
|
|
790
1624
|
// src/player/StormcloudVideoPlayer.ts
|
|
791
1625
|
var StormcloudVideoPlayer = class {
|
|
792
1626
|
constructor(config) {
|
|
@@ -799,11 +1633,40 @@ var StormcloudVideoPlayer = class {
|
|
|
799
1633
|
this.totalAdsInBreak = 0;
|
|
800
1634
|
this.showAds = false;
|
|
801
1635
|
this.isLiveStream = false;
|
|
802
|
-
|
|
1636
|
+
initializePolyfills();
|
|
1637
|
+
const browserOverrides = getBrowserConfigOverrides();
|
|
1638
|
+
this.config = { ...config, ...browserOverrides };
|
|
803
1639
|
this.video = config.videoElement;
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1640
|
+
logBrowserInfo(config.debugAdTiming);
|
|
1641
|
+
this.ima = this.createAdPlayer(false);
|
|
1642
|
+
}
|
|
1643
|
+
createAdPlayer(continueLiveStreamDuringAds) {
|
|
1644
|
+
const vastMode = this.config.vastMode || "default";
|
|
1645
|
+
let adPlayerType = this.config.adPlayerType || (vastMode === "adstorm" ? "hls" : "ima");
|
|
1646
|
+
if (adPlayerType === "ima" && !supportsGoogleIMA()) {
|
|
1647
|
+
if (this.config.debugAdTiming) {
|
|
1648
|
+
console.warn(
|
|
1649
|
+
"[StormcloudVideoPlayer] Google IMA SDK not supported on this browser, falling back to HLS ad player"
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
adPlayerType = "hls";
|
|
1653
|
+
}
|
|
1654
|
+
if (adPlayerType === "hls") {
|
|
1655
|
+
if (this.config.debugAdTiming) {
|
|
1656
|
+
console.log("[StormcloudVideoPlayer] Creating HLS ad player (AdStorm mode)");
|
|
1657
|
+
}
|
|
1658
|
+
return createHlsAdPlayer(this.video, {
|
|
1659
|
+
continueLiveStreamDuringAds,
|
|
1660
|
+
...this.config.licenseKey ? { licenseKey: this.config.licenseKey } : {}
|
|
1661
|
+
});
|
|
1662
|
+
} else {
|
|
1663
|
+
if (this.config.debugAdTiming) {
|
|
1664
|
+
console.log("[StormcloudVideoPlayer] Creating Google IMA ad player (Default mode)");
|
|
1665
|
+
}
|
|
1666
|
+
return createImaController(this.video, {
|
|
1667
|
+
continueLiveStreamDuringAds
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
807
1670
|
}
|
|
808
1671
|
async load() {
|
|
809
1672
|
if (!this.attached) {
|
|
@@ -834,9 +1697,7 @@ var StormcloudVideoPlayer = class {
|
|
|
834
1697
|
);
|
|
835
1698
|
}
|
|
836
1699
|
this.ima.destroy();
|
|
837
|
-
this.ima =
|
|
838
|
-
continueLiveStreamDuringAds: false
|
|
839
|
-
});
|
|
1700
|
+
this.ima = this.createAdPlayer(false);
|
|
840
1701
|
this.ima.initialize();
|
|
841
1702
|
if (this.config.autoplay) {
|
|
842
1703
|
await this.video.play()?.catch(() => {
|
|
@@ -844,7 +1705,7 @@ var StormcloudVideoPlayer = class {
|
|
|
844
1705
|
}
|
|
845
1706
|
return;
|
|
846
1707
|
}
|
|
847
|
-
this.hls = new
|
|
1708
|
+
this.hls = new import_hls2.default({
|
|
848
1709
|
enableWorker: true,
|
|
849
1710
|
backBufferLength: 30,
|
|
850
1711
|
liveDurationInfinity: true,
|
|
@@ -852,10 +1713,10 @@ var StormcloudVideoPlayer = class {
|
|
|
852
1713
|
maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
|
|
853
1714
|
...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
|
|
854
1715
|
});
|
|
855
|
-
this.hls.on(
|
|
1716
|
+
this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
|
|
856
1717
|
this.hls?.loadSource(this.config.src);
|
|
857
1718
|
});
|
|
858
|
-
this.hls.on(
|
|
1719
|
+
this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
|
|
859
1720
|
this.isLiveStream = this.hls?.levels?.some(
|
|
860
1721
|
(level) => level?.details?.live === true || level?.details?.type === "LIVE"
|
|
861
1722
|
) ?? false;
|
|
@@ -868,16 +1729,14 @@ var StormcloudVideoPlayer = class {
|
|
|
868
1729
|
});
|
|
869
1730
|
}
|
|
870
1731
|
this.ima.destroy();
|
|
871
|
-
this.ima =
|
|
872
|
-
continueLiveStreamDuringAds: this.shouldContinueLiveStreamDuringAds()
|
|
873
|
-
});
|
|
1732
|
+
this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
|
|
874
1733
|
this.ima.initialize();
|
|
875
1734
|
if (this.config.autoplay) {
|
|
876
1735
|
await this.video.play()?.catch(() => {
|
|
877
1736
|
});
|
|
878
1737
|
}
|
|
879
1738
|
});
|
|
880
|
-
this.hls.on(
|
|
1739
|
+
this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
|
|
881
1740
|
const id3Tags = (data?.samples || []).map((s) => ({
|
|
882
1741
|
key: "ID3",
|
|
883
1742
|
value: s?.data,
|
|
@@ -885,7 +1744,7 @@ var StormcloudVideoPlayer = class {
|
|
|
885
1744
|
}));
|
|
886
1745
|
id3Tags.forEach((tag) => this.onId3Tag(tag));
|
|
887
1746
|
});
|
|
888
|
-
this.hls.on(
|
|
1747
|
+
this.hls.on(import_hls2.default.Events.FRAG_CHANGED, (_evt, data) => {
|
|
889
1748
|
const frag = data?.frag;
|
|
890
1749
|
const tagList = frag?.tagList;
|
|
891
1750
|
if (!Array.isArray(tagList)) return;
|
|
@@ -945,13 +1804,13 @@ var StormcloudVideoPlayer = class {
|
|
|
945
1804
|
}
|
|
946
1805
|
}
|
|
947
1806
|
});
|
|
948
|
-
this.hls.on(
|
|
1807
|
+
this.hls.on(import_hls2.default.Events.ERROR, (_evt, data) => {
|
|
949
1808
|
if (data?.fatal) {
|
|
950
1809
|
switch (data.type) {
|
|
951
|
-
case
|
|
1810
|
+
case import_hls2.default.ErrorTypes.NETWORK_ERROR:
|
|
952
1811
|
this.hls?.startLoad();
|
|
953
1812
|
break;
|
|
954
|
-
case
|
|
1813
|
+
case import_hls2.default.ErrorTypes.MEDIA_ERROR:
|
|
955
1814
|
this.hls?.recoverMediaError();
|
|
956
1815
|
break;
|
|
957
1816
|
default:
|
|
@@ -1462,6 +2321,42 @@ var StormcloudVideoPlayer = class {
|
|
|
1462
2321
|
}
|
|
1463
2322
|
}
|
|
1464
2323
|
async fetchAdConfiguration() {
|
|
2324
|
+
const vastMode = this.config.vastMode || "default";
|
|
2325
|
+
if (this.config.debugAdTiming) {
|
|
2326
|
+
console.log(
|
|
2327
|
+
"[StormcloudVideoPlayer] VAST mode:",
|
|
2328
|
+
vastMode
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
if (vastMode === "adstorm") {
|
|
2332
|
+
if (!this.config.licenseKey) {
|
|
2333
|
+
if (this.config.debugAdTiming) {
|
|
2334
|
+
console.warn(
|
|
2335
|
+
"[StormcloudVideoPlayer] AdStorm mode requires a license key"
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
const vastEndpoint = `https://adstorm.co/api-adstorm-dev/adstorm/vast/${this.config.licenseKey}`;
|
|
2341
|
+
this.apiVastTagUrl = vastEndpoint;
|
|
2342
|
+
if (this.config.debugAdTiming) {
|
|
2343
|
+
console.log(
|
|
2344
|
+
"[StormcloudVideoPlayer] Using AdStorm VAST endpoint:",
|
|
2345
|
+
vastEndpoint
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
if (this.config.vastTagUrl) {
|
|
2351
|
+
this.apiVastTagUrl = this.config.vastTagUrl;
|
|
2352
|
+
if (this.config.debugAdTiming) {
|
|
2353
|
+
console.log(
|
|
2354
|
+
"[StormcloudVideoPlayer] Using custom VAST tag URL:",
|
|
2355
|
+
this.apiVastTagUrl
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
1465
2360
|
const apiUrl = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
1466
2361
|
if (this.config.debugAdTiming) {
|
|
1467
2362
|
console.log(
|
|
@@ -1475,7 +2370,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1475
2370
|
}
|
|
1476
2371
|
const response = await fetch(apiUrl, { headers });
|
|
1477
2372
|
if (!response.ok) {
|
|
1478
|
-
|
|
2373
|
+
if (this.config.debugAdTiming) {
|
|
2374
|
+
console.warn(
|
|
2375
|
+
`[StormcloudVideoPlayer] Failed to fetch ad configuration: ${response.status}`
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2378
|
+
return;
|
|
1479
2379
|
}
|
|
1480
2380
|
const data = await response.json();
|
|
1481
2381
|
const imaPayload = data.response?.ima?.["publisherdesk.ima"]?.payload;
|
|
@@ -1483,17 +2383,16 @@ var StormcloudVideoPlayer = class {
|
|
|
1483
2383
|
this.apiVastTagUrl = decodeURIComponent(imaPayload);
|
|
1484
2384
|
if (this.config.debugAdTiming) {
|
|
1485
2385
|
console.log(
|
|
1486
|
-
"[StormcloudVideoPlayer] Extracted VAST tag URL:",
|
|
2386
|
+
"[StormcloudVideoPlayer] Extracted VAST tag URL from /ads/web:",
|
|
1487
2387
|
this.apiVastTagUrl
|
|
1488
2388
|
);
|
|
1489
2389
|
}
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
});
|
|
2390
|
+
} else {
|
|
2391
|
+
if (this.config.debugAdTiming) {
|
|
2392
|
+
console.warn(
|
|
2393
|
+
"[StormcloudVideoPlayer] No VAST tag URL found in /ads/web response"
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
1497
2396
|
}
|
|
1498
2397
|
}
|
|
1499
2398
|
getCurrentAdIndex() {
|
|
@@ -1537,23 +2436,14 @@ var StormcloudVideoPlayer = class {
|
|
|
1537
2436
|
);
|
|
1538
2437
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
1539
2438
|
let vastTagUrl;
|
|
1540
|
-
let adsNumber = 1;
|
|
1541
2439
|
if (this.apiVastTagUrl) {
|
|
1542
2440
|
vastTagUrl = this.apiVastTagUrl;
|
|
1543
|
-
|
|
1544
|
-
const isHls = this.config.src.includes(".m3u8") || this.config.src.includes("hls");
|
|
1545
|
-
if (isHls && this.vastConfig.cue_tones?.number_ads) {
|
|
1546
|
-
adsNumber = this.vastConfig.cue_tones.number_ads;
|
|
1547
|
-
} else if (!isHls && this.vastConfig.timer_vod?.number_ads) {
|
|
1548
|
-
adsNumber = this.vastConfig.timer_vod.number_ads;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
this.adPodQueue = new Array(adsNumber - 1).fill(vastTagUrl);
|
|
2441
|
+
this.adPodQueue = [];
|
|
1552
2442
|
this.currentAdIndex = 0;
|
|
1553
|
-
this.totalAdsInBreak =
|
|
2443
|
+
this.totalAdsInBreak = 1;
|
|
1554
2444
|
if (this.config.debugAdTiming) {
|
|
1555
2445
|
console.log(
|
|
1556
|
-
|
|
2446
|
+
"[StormcloudVideoPlayer] Using VAST endpoint:",
|
|
1557
2447
|
vastTagUrl
|
|
1558
2448
|
);
|
|
1559
2449
|
}
|