stormcloud-video-player 0.2.24 → 0.2.25
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/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +256 -32
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +10 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +256 -32
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +256 -32
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +7 -1
- package/lib/players/HlsPlayer.cjs +256 -32
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +256 -32
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +87 -10
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.d.cts +1 -1
- package/lib/sdk/ima.cjs +87 -19
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/sdk/ima.d.cts +1 -1
- package/lib/{types-D1xfSdLP.d.cts → types-9_2sbHCg.d.cts} +4 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +256 -32
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +1 -1
package/lib/index.d.cts
CHANGED
|
@@ -39,6 +39,8 @@ interface StormcloudVideoPlayerConfig {
|
|
|
39
39
|
interface ImaController {
|
|
40
40
|
initialize: () => void;
|
|
41
41
|
requestAds: (vastTagUrl: string) => Promise<void>;
|
|
42
|
+
preloadAds: (vastTagUrl: string) => Promise<void>;
|
|
43
|
+
hasPreloadedAd: (vastTagUrl: string) => boolean;
|
|
42
44
|
play: () => Promise<void>;
|
|
43
45
|
stop: () => Promise<void>;
|
|
44
46
|
destroy: () => void;
|
|
@@ -50,6 +52,8 @@ interface ImaController {
|
|
|
50
52
|
getOriginalMutedState: () => boolean;
|
|
51
53
|
setAdVolume: (volume: number) => void;
|
|
52
54
|
getAdVolume: () => number;
|
|
55
|
+
showPlaceholder: () => void;
|
|
56
|
+
hidePlaceholder: () => void;
|
|
53
57
|
}
|
|
54
58
|
interface ImaControllerOptions {
|
|
55
59
|
maxRetries?: number;
|
|
@@ -122,6 +126,8 @@ declare class StormcloudVideoPlayer {
|
|
|
122
126
|
private bufferedSegmentsCount;
|
|
123
127
|
private shouldAutoplayAfterBuffering;
|
|
124
128
|
private hasInitialBufferCompleted;
|
|
129
|
+
private adPodAllUrls;
|
|
130
|
+
private preloadingAdUrls;
|
|
125
131
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
126
132
|
private createAdPlayer;
|
|
127
133
|
load(): Promise<void>;
|
|
@@ -164,6 +170,10 @@ declare class StormcloudVideoPlayer {
|
|
|
164
170
|
private startAdFailsafeTimer;
|
|
165
171
|
private clearAdFailsafeTimer;
|
|
166
172
|
private selectVastTagsForBreak;
|
|
173
|
+
private logQueuedAdUrls;
|
|
174
|
+
private enforceAdHoldState;
|
|
175
|
+
private releaseAdHoldState;
|
|
176
|
+
private preloadUpcomingAds;
|
|
167
177
|
private getRemainingAdMs;
|
|
168
178
|
private findBreakForTime;
|
|
169
179
|
toggleMute(): void;
|
package/lib/index.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ interface StormcloudVideoPlayerConfig {
|
|
|
39
39
|
interface ImaController {
|
|
40
40
|
initialize: () => void;
|
|
41
41
|
requestAds: (vastTagUrl: string) => Promise<void>;
|
|
42
|
+
preloadAds: (vastTagUrl: string) => Promise<void>;
|
|
43
|
+
hasPreloadedAd: (vastTagUrl: string) => boolean;
|
|
42
44
|
play: () => Promise<void>;
|
|
43
45
|
stop: () => Promise<void>;
|
|
44
46
|
destroy: () => void;
|
|
@@ -50,6 +52,8 @@ interface ImaController {
|
|
|
50
52
|
getOriginalMutedState: () => boolean;
|
|
51
53
|
setAdVolume: (volume: number) => void;
|
|
52
54
|
getAdVolume: () => number;
|
|
55
|
+
showPlaceholder: () => void;
|
|
56
|
+
hidePlaceholder: () => void;
|
|
53
57
|
}
|
|
54
58
|
interface ImaControllerOptions {
|
|
55
59
|
maxRetries?: number;
|
|
@@ -122,6 +126,8 @@ declare class StormcloudVideoPlayer {
|
|
|
122
126
|
private bufferedSegmentsCount;
|
|
123
127
|
private shouldAutoplayAfterBuffering;
|
|
124
128
|
private hasInitialBufferCompleted;
|
|
129
|
+
private adPodAllUrls;
|
|
130
|
+
private preloadingAdUrls;
|
|
125
131
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
126
132
|
private createAdPlayer;
|
|
127
133
|
load(): Promise<void>;
|
|
@@ -164,6 +170,10 @@ declare class StormcloudVideoPlayer {
|
|
|
164
170
|
private startAdFailsafeTimer;
|
|
165
171
|
private clearAdFailsafeTimer;
|
|
166
172
|
private selectVastTagsForBreak;
|
|
173
|
+
private logQueuedAdUrls;
|
|
174
|
+
private enforceAdHoldState;
|
|
175
|
+
private releaseAdHoldState;
|
|
176
|
+
private preloadUpcomingAds;
|
|
167
177
|
private getRemainingAdMs;
|
|
168
178
|
private findBreakForTime;
|
|
169
179
|
toggleMute(): void;
|
package/lib/index.js
CHANGED
|
@@ -198,6 +198,8 @@ function createImaController(video, options) {
|
|
|
198
198
|
let adPlaying = false;
|
|
199
199
|
let originalMutedState = false;
|
|
200
200
|
const listeners = /* @__PURE__ */ new Map();
|
|
201
|
+
const preloadedVast = /* @__PURE__ */ new Map();
|
|
202
|
+
const preloadingVast = /* @__PURE__ */ new Map();
|
|
201
203
|
function setAdPlayingFlag(isPlaying) {
|
|
202
204
|
if (isPlaying) {
|
|
203
205
|
video.dataset.stormcloudAdPlaying = "true";
|
|
@@ -288,7 +290,15 @@ function createImaController(video, options) {
|
|
|
288
290
|
let adsLoadedReject;
|
|
289
291
|
function makeAdsRequest(google, vastTagUrl) {
|
|
290
292
|
const adsRequest = new google.ima.AdsRequest();
|
|
291
|
-
|
|
293
|
+
const preloadedResponse = preloadedVast.get(vastTagUrl);
|
|
294
|
+
if (preloadedResponse) {
|
|
295
|
+
adsRequest.adsResponse = preloadedResponse;
|
|
296
|
+
console.log(
|
|
297
|
+
"[IMA] Using preloaded VAST response for immediate ad request"
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
adsRequest.adTagUrl = vastTagUrl;
|
|
301
|
+
}
|
|
292
302
|
const videoWidth = video.offsetWidth || video.clientWidth || 640;
|
|
293
303
|
const videoHeight = video.offsetHeight || video.clientHeight || 360;
|
|
294
304
|
adsRequest.linearAdSlotWidth = videoWidth;
|
|
@@ -298,6 +308,36 @@ function createImaController(video, options) {
|
|
|
298
308
|
adsRequest.vastLoadTimeout = 5e3;
|
|
299
309
|
console.log(`[IMA] Ads request dimensions: ${videoWidth}x${videoHeight}`);
|
|
300
310
|
adsLoader.requestAds(adsRequest);
|
|
311
|
+
if (preloadedResponse) {
|
|
312
|
+
preloadedVast.delete(vastTagUrl);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function ensurePlaceholderContainer() {
|
|
316
|
+
var _a;
|
|
317
|
+
if (adContainerEl) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const container = document.createElement("div");
|
|
321
|
+
container.style.position = "absolute";
|
|
322
|
+
container.style.left = "0";
|
|
323
|
+
container.style.top = "0";
|
|
324
|
+
container.style.right = "0";
|
|
325
|
+
container.style.bottom = "0";
|
|
326
|
+
container.style.display = "none";
|
|
327
|
+
container.style.alignItems = "center";
|
|
328
|
+
container.style.justifyContent = "center";
|
|
329
|
+
container.style.pointerEvents = "none";
|
|
330
|
+
container.style.zIndex = "10";
|
|
331
|
+
container.style.backgroundColor = "#000";
|
|
332
|
+
(_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
|
|
333
|
+
adContainerEl = container;
|
|
334
|
+
}
|
|
335
|
+
async function fetchVastDocument(vastTagUrl) {
|
|
336
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
throw new Error(`Failed to preload VAST: ${response.status}`);
|
|
339
|
+
}
|
|
340
|
+
return response.text();
|
|
301
341
|
}
|
|
302
342
|
function destroyAdsManager() {
|
|
303
343
|
if (adsManager) {
|
|
@@ -313,29 +353,16 @@ function createImaController(video, options) {
|
|
|
313
353
|
return {
|
|
314
354
|
initialize() {
|
|
315
355
|
ensureImaLoaded().then(() => {
|
|
316
|
-
var _a
|
|
356
|
+
var _a;
|
|
317
357
|
const google = window.google;
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
container.style.position = "absolute";
|
|
321
|
-
container.style.left = "0";
|
|
322
|
-
container.style.top = "0";
|
|
323
|
-
container.style.right = "0";
|
|
324
|
-
container.style.bottom = "0";
|
|
325
|
-
container.style.display = "none";
|
|
326
|
-
container.style.alignItems = "center";
|
|
327
|
-
container.style.justifyContent = "center";
|
|
328
|
-
container.style.pointerEvents = "none";
|
|
329
|
-
container.style.zIndex = "10";
|
|
330
|
-
container.style.backgroundColor = "#000";
|
|
331
|
-
(_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
|
|
332
|
-
adContainerEl = container;
|
|
358
|
+
ensurePlaceholderContainer();
|
|
359
|
+
if (!adDisplayContainer && adContainerEl) {
|
|
333
360
|
adDisplayContainer = new google.ima.AdDisplayContainer(
|
|
334
|
-
|
|
361
|
+
adContainerEl,
|
|
335
362
|
video
|
|
336
363
|
);
|
|
337
364
|
try {
|
|
338
|
-
(
|
|
365
|
+
(_a = adDisplayContainer.initialize) == null ? void 0 : _a.call(adDisplayContainer);
|
|
339
366
|
} catch {
|
|
340
367
|
}
|
|
341
368
|
}
|
|
@@ -639,6 +666,32 @@ function createImaController(video, options) {
|
|
|
639
666
|
return Promise.reject(error);
|
|
640
667
|
}
|
|
641
668
|
},
|
|
669
|
+
async preloadAds(vastTagUrl) {
|
|
670
|
+
if (!vastTagUrl || vastTagUrl.trim() === "") {
|
|
671
|
+
return Promise.resolve();
|
|
672
|
+
}
|
|
673
|
+
if (preloadedVast.has(vastTagUrl)) {
|
|
674
|
+
return Promise.resolve();
|
|
675
|
+
}
|
|
676
|
+
const inflight = preloadingVast.get(vastTagUrl);
|
|
677
|
+
if (inflight) {
|
|
678
|
+
return inflight;
|
|
679
|
+
}
|
|
680
|
+
const preloadPromise = fetchVastDocument(vastTagUrl).then((xml) => {
|
|
681
|
+
preloadedVast.set(vastTagUrl, xml);
|
|
682
|
+
console.log("[IMA] Cached VAST response for preloading:", vastTagUrl);
|
|
683
|
+
}).catch((error) => {
|
|
684
|
+
console.warn("[IMA] Failed to preload VAST response:", error);
|
|
685
|
+
preloadedVast.delete(vastTagUrl);
|
|
686
|
+
}).finally(() => {
|
|
687
|
+
preloadingVast.delete(vastTagUrl);
|
|
688
|
+
});
|
|
689
|
+
preloadingVast.set(vastTagUrl, preloadPromise);
|
|
690
|
+
return preloadPromise;
|
|
691
|
+
},
|
|
692
|
+
hasPreloadedAd(vastTagUrl) {
|
|
693
|
+
return preloadedVast.has(vastTagUrl);
|
|
694
|
+
},
|
|
642
695
|
async play() {
|
|
643
696
|
var _a, _b;
|
|
644
697
|
if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
|
|
@@ -714,6 +767,8 @@ function createImaController(video, options) {
|
|
|
714
767
|
adContainerEl = void 0;
|
|
715
768
|
adDisplayContainer = void 0;
|
|
716
769
|
adsLoader = void 0;
|
|
770
|
+
preloadedVast.clear();
|
|
771
|
+
preloadingVast.clear();
|
|
717
772
|
},
|
|
718
773
|
isAdPlaying() {
|
|
719
774
|
return adPlaying;
|
|
@@ -769,6 +824,19 @@ function createImaController(video, options) {
|
|
|
769
824
|
}
|
|
770
825
|
}
|
|
771
826
|
return 1;
|
|
827
|
+
},
|
|
828
|
+
showPlaceholder() {
|
|
829
|
+
ensurePlaceholderContainer();
|
|
830
|
+
if (adContainerEl) {
|
|
831
|
+
adContainerEl.style.display = "flex";
|
|
832
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
hidePlaceholder() {
|
|
836
|
+
if (adContainerEl) {
|
|
837
|
+
adContainerEl.style.display = "none";
|
|
838
|
+
adContainerEl.style.pointerEvents = "none";
|
|
839
|
+
}
|
|
772
840
|
}
|
|
773
841
|
};
|
|
774
842
|
}
|
|
@@ -786,6 +854,8 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
786
854
|
let adContainerEl;
|
|
787
855
|
let currentAd;
|
|
788
856
|
let sessionId;
|
|
857
|
+
const preloadedAds = /* @__PURE__ */ new Map();
|
|
858
|
+
const preloadingAds = /* @__PURE__ */ new Map();
|
|
789
859
|
let trackingFired = {
|
|
790
860
|
impression: false,
|
|
791
861
|
start: false,
|
|
@@ -1015,6 +1085,19 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1015
1085
|
return null;
|
|
1016
1086
|
}
|
|
1017
1087
|
}
|
|
1088
|
+
async function fetchAndParseVastAd(vastTagUrl) {
|
|
1089
|
+
const response = await fetch(vastTagUrl);
|
|
1090
|
+
if (!response.ok) {
|
|
1091
|
+
throw new Error(`Failed to fetch VAST: ${response.statusText}`);
|
|
1092
|
+
}
|
|
1093
|
+
const vastXml = await response.text();
|
|
1094
|
+
console.log("[HlsAdPlayer] VAST XML received");
|
|
1095
|
+
console.log(
|
|
1096
|
+
"[HlsAdPlayer] VAST XML content (first 2000 chars):",
|
|
1097
|
+
vastXml.substring(0, 2e3)
|
|
1098
|
+
);
|
|
1099
|
+
return parseVastXml(vastXml);
|
|
1100
|
+
}
|
|
1018
1101
|
function createAdVideoElement() {
|
|
1019
1102
|
const video = document.createElement("video");
|
|
1020
1103
|
video.style.position = "absolute";
|
|
@@ -1166,17 +1249,17 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1166
1249
|
}
|
|
1167
1250
|
try {
|
|
1168
1251
|
sessionId = generateSessionId();
|
|
1169
|
-
|
|
1170
|
-
if (
|
|
1171
|
-
|
|
1252
|
+
let ad;
|
|
1253
|
+
if (preloadedAds.has(vastTagUrl)) {
|
|
1254
|
+
ad = preloadedAds.get(vastTagUrl);
|
|
1255
|
+
preloadedAds.delete(vastTagUrl);
|
|
1256
|
+
console.log(
|
|
1257
|
+
"[HlsAdPlayer] Using preloaded VAST response:",
|
|
1258
|
+
vastTagUrl
|
|
1259
|
+
);
|
|
1260
|
+
} else {
|
|
1261
|
+
ad = await fetchAndParseVastAd(vastTagUrl);
|
|
1172
1262
|
}
|
|
1173
|
-
const vastXml = await response.text();
|
|
1174
|
-
console.log("[HlsAdPlayer] VAST XML received");
|
|
1175
|
-
console.log(
|
|
1176
|
-
"[HlsAdPlayer] VAST XML content (first 2000 chars):",
|
|
1177
|
-
vastXml.substring(0, 2e3)
|
|
1178
|
-
);
|
|
1179
|
-
const ad = parseVastXml(vastXml);
|
|
1180
1263
|
if (!ad) {
|
|
1181
1264
|
console.warn("[HlsAdPlayer] No ads available from VAST response");
|
|
1182
1265
|
emit("ad_error");
|
|
@@ -1195,6 +1278,37 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1195
1278
|
return Promise.reject(error);
|
|
1196
1279
|
}
|
|
1197
1280
|
},
|
|
1281
|
+
async preloadAds(vastTagUrl) {
|
|
1282
|
+
if (!vastTagUrl || vastTagUrl.trim() === "") {
|
|
1283
|
+
return Promise.resolve();
|
|
1284
|
+
}
|
|
1285
|
+
if (preloadedAds.has(vastTagUrl)) {
|
|
1286
|
+
return Promise.resolve();
|
|
1287
|
+
}
|
|
1288
|
+
const inflight = preloadingAds.get(vastTagUrl);
|
|
1289
|
+
if (inflight) {
|
|
1290
|
+
return inflight;
|
|
1291
|
+
}
|
|
1292
|
+
const preloadPromise = fetchAndParseVastAd(vastTagUrl).then((ad) => {
|
|
1293
|
+
if (ad) {
|
|
1294
|
+
preloadedAds.set(vastTagUrl, ad);
|
|
1295
|
+
console.log(
|
|
1296
|
+
"[HlsAdPlayer] Cached VAST response for preloading:",
|
|
1297
|
+
vastTagUrl
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
}).catch((error) => {
|
|
1301
|
+
console.warn("[HlsAdPlayer] Failed to preload VAST response:", error);
|
|
1302
|
+
preloadedAds.delete(vastTagUrl);
|
|
1303
|
+
}).finally(() => {
|
|
1304
|
+
preloadingAds.delete(vastTagUrl);
|
|
1305
|
+
});
|
|
1306
|
+
preloadingAds.set(vastTagUrl, preloadPromise);
|
|
1307
|
+
return preloadPromise;
|
|
1308
|
+
},
|
|
1309
|
+
hasPreloadedAd(vastTagUrl) {
|
|
1310
|
+
return preloadedAds.has(vastTagUrl);
|
|
1311
|
+
},
|
|
1198
1312
|
async play() {
|
|
1199
1313
|
if (!currentAd) {
|
|
1200
1314
|
console.warn(
|
|
@@ -1325,6 +1439,8 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1325
1439
|
adContainerEl = void 0;
|
|
1326
1440
|
currentAd = void 0;
|
|
1327
1441
|
listeners.clear();
|
|
1442
|
+
preloadedAds.clear();
|
|
1443
|
+
preloadingAds.clear();
|
|
1328
1444
|
},
|
|
1329
1445
|
isAdPlaying() {
|
|
1330
1446
|
return adPlaying;
|
|
@@ -1367,6 +1483,35 @@ function createHlsAdPlayer(contentVideo, options) {
|
|
|
1367
1483
|
return adVideoElement.volume;
|
|
1368
1484
|
}
|
|
1369
1485
|
return 1;
|
|
1486
|
+
},
|
|
1487
|
+
showPlaceholder() {
|
|
1488
|
+
var _a;
|
|
1489
|
+
if (!adContainerEl) {
|
|
1490
|
+
const container = document.createElement("div");
|
|
1491
|
+
container.style.position = "absolute";
|
|
1492
|
+
container.style.left = "0";
|
|
1493
|
+
container.style.top = "0";
|
|
1494
|
+
container.style.right = "0";
|
|
1495
|
+
container.style.bottom = "0";
|
|
1496
|
+
container.style.display = "none";
|
|
1497
|
+
container.style.alignItems = "center";
|
|
1498
|
+
container.style.justifyContent = "center";
|
|
1499
|
+
container.style.pointerEvents = "none";
|
|
1500
|
+
container.style.zIndex = "10";
|
|
1501
|
+
container.style.backgroundColor = "#000";
|
|
1502
|
+
(_a = contentVideo.parentElement) == null ? void 0 : _a.appendChild(container);
|
|
1503
|
+
adContainerEl = container;
|
|
1504
|
+
}
|
|
1505
|
+
if (adContainerEl) {
|
|
1506
|
+
adContainerEl.style.display = "flex";
|
|
1507
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
1508
|
+
}
|
|
1509
|
+
},
|
|
1510
|
+
hidePlaceholder() {
|
|
1511
|
+
if (adContainerEl) {
|
|
1512
|
+
adContainerEl.style.display = "none";
|
|
1513
|
+
adContainerEl.style.pointerEvents = "none";
|
|
1514
|
+
}
|
|
1370
1515
|
}
|
|
1371
1516
|
};
|
|
1372
1517
|
}
|
|
@@ -1847,6 +1992,8 @@ var StormcloudVideoPlayer = class {
|
|
|
1847
1992
|
this.bufferedSegmentsCount = 0;
|
|
1848
1993
|
this.shouldAutoplayAfterBuffering = false;
|
|
1849
1994
|
this.hasInitialBufferCompleted = false;
|
|
1995
|
+
this.adPodAllUrls = [];
|
|
1996
|
+
this.preloadingAdUrls = /* @__PURE__ */ new Set();
|
|
1850
1997
|
initializePolyfills();
|
|
1851
1998
|
const browserOverrides = getBrowserConfigOverrides();
|
|
1852
1999
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2147,6 +2294,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2147
2294
|
console.log("[StormcloudVideoPlayer] IMA content_pause event received");
|
|
2148
2295
|
}
|
|
2149
2296
|
this.clearAdFailsafeTimer();
|
|
2297
|
+
this.enforceAdHoldState();
|
|
2150
2298
|
});
|
|
2151
2299
|
this.ima.on("content_resume", () => {
|
|
2152
2300
|
if (this.config.debugAdTiming) {
|
|
@@ -2171,9 +2319,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2171
2319
|
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
2172
2320
|
const next = this.adPodQueue.shift();
|
|
2173
2321
|
this.currentAdIndex++;
|
|
2174
|
-
this.
|
|
2175
|
-
this.video.muted = true;
|
|
2176
|
-
this.video.volume = 0;
|
|
2322
|
+
this.enforceAdHoldState();
|
|
2177
2323
|
if (this.config.debugAdTiming) {
|
|
2178
2324
|
console.log(
|
|
2179
2325
|
`[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
|
|
@@ -2785,11 +2931,16 @@ var StormcloudVideoPlayer = class {
|
|
|
2785
2931
|
return;
|
|
2786
2932
|
}
|
|
2787
2933
|
if (vastTagUrls.length > 0) {
|
|
2934
|
+
this.adPodAllUrls = [...vastTagUrls];
|
|
2935
|
+
this.preloadingAdUrls.clear();
|
|
2936
|
+
this.logQueuedAdUrls(this.adPodAllUrls);
|
|
2788
2937
|
this.inAdBreak = true;
|
|
2789
2938
|
this.showAds = true;
|
|
2790
2939
|
this.currentAdIndex = 0;
|
|
2791
2940
|
this.totalAdsInBreak = vastTagUrls.length;
|
|
2792
2941
|
this.adPodQueue = [...vastTagUrls];
|
|
2942
|
+
this.enforceAdHoldState();
|
|
2943
|
+
this.preloadUpcomingAds();
|
|
2793
2944
|
if (this.config.debugAdTiming) {
|
|
2794
2945
|
console.log(
|
|
2795
2946
|
`[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
|
|
@@ -2860,6 +3011,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2860
3011
|
const first = tags[0];
|
|
2861
3012
|
const rest = tags.slice(1);
|
|
2862
3013
|
this.adPodQueue = rest;
|
|
3014
|
+
this.enforceAdHoldState();
|
|
2863
3015
|
await this.playSingleAd(first);
|
|
2864
3016
|
this.inAdBreak = true;
|
|
2865
3017
|
this.expectedAdBreakDurationMs = remainingMs;
|
|
@@ -2973,6 +3125,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2973
3125
|
}
|
|
2974
3126
|
return;
|
|
2975
3127
|
}
|
|
3128
|
+
const wasPreloaded = this.ima.hasPreloadedAd(vastTagUrl);
|
|
3129
|
+
if (wasPreloaded && this.config.debugAdTiming) {
|
|
3130
|
+
console.log(
|
|
3131
|
+
`[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
2976
3134
|
if (!this.showAds) {
|
|
2977
3135
|
if (this.config.debugAdTiming) {
|
|
2978
3136
|
console.log(
|
|
@@ -2993,12 +3151,14 @@ var StormcloudVideoPlayer = class {
|
|
|
2993
3151
|
this.startAdFailsafeTimer();
|
|
2994
3152
|
try {
|
|
2995
3153
|
await this.ima.requestAds(vastTagUrl);
|
|
3154
|
+
this.preloadUpcomingAds();
|
|
2996
3155
|
try {
|
|
2997
3156
|
if (this.config.debugAdTiming) {
|
|
2998
3157
|
console.log(
|
|
2999
3158
|
"[StormcloudVideoPlayer] Ad request completed, attempting playback"
|
|
3000
3159
|
);
|
|
3001
3160
|
}
|
|
3161
|
+
this.enforceAdHoldState();
|
|
3002
3162
|
await this.ima.play();
|
|
3003
3163
|
if (this.config.debugAdTiming) {
|
|
3004
3164
|
console.log(
|
|
@@ -3028,6 +3188,8 @@ var StormcloudVideoPlayer = class {
|
|
|
3028
3188
|
"[StormcloudVideoPlayer] Handling ad pod completion - resuming content and hiding ad layer"
|
|
3029
3189
|
);
|
|
3030
3190
|
}
|
|
3191
|
+
this.releaseAdHoldState();
|
|
3192
|
+
this.preloadingAdUrls.clear();
|
|
3031
3193
|
this.inAdBreak = false;
|
|
3032
3194
|
this.expectedAdBreakDurationMs = void 0;
|
|
3033
3195
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
@@ -3035,6 +3197,7 @@ var StormcloudVideoPlayer = class {
|
|
|
3035
3197
|
this.clearAdStopTimer();
|
|
3036
3198
|
this.clearAdFailsafeTimer();
|
|
3037
3199
|
this.adPodQueue = [];
|
|
3200
|
+
this.adPodAllUrls = [];
|
|
3038
3201
|
this.showAds = false;
|
|
3039
3202
|
this.currentAdIndex = 0;
|
|
3040
3203
|
this.totalAdsInBreak = 0;
|
|
@@ -3115,6 +3278,64 @@ var StormcloudVideoPlayer = class {
|
|
|
3115
3278
|
}
|
|
3116
3279
|
return [b.vastTagUrl];
|
|
3117
3280
|
}
|
|
3281
|
+
logQueuedAdUrls(urls) {
|
|
3282
|
+
if (!this.config.debugAdTiming) {
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
console.log("[StormcloudVideoPlayer] ALL ad URLs queued:", urls);
|
|
3286
|
+
}
|
|
3287
|
+
enforceAdHoldState() {
|
|
3288
|
+
this.video.dataset.stormcloudAdPlaying = "true";
|
|
3289
|
+
this.video.muted = true;
|
|
3290
|
+
this.video.volume = 0;
|
|
3291
|
+
if (typeof this.ima.showPlaceholder === "function") {
|
|
3292
|
+
this.ima.showPlaceholder();
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
releaseAdHoldState() {
|
|
3296
|
+
delete this.video.dataset.stormcloudAdPlaying;
|
|
3297
|
+
if (typeof this.ima.hidePlaceholder === "function") {
|
|
3298
|
+
this.ima.hidePlaceholder();
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
preloadUpcomingAds() {
|
|
3302
|
+
if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
|
|
3303
|
+
return;
|
|
3304
|
+
}
|
|
3305
|
+
const upcoming = this.adPodQueue.slice(0, 2);
|
|
3306
|
+
for (const url of upcoming) {
|
|
3307
|
+
if (!url) continue;
|
|
3308
|
+
if (this.ima.hasPreloadedAd(url)) {
|
|
3309
|
+
this.preloadingAdUrls.delete(url);
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
3312
|
+
if (this.preloadingAdUrls.has(url)) {
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
if (this.config.debugAdTiming) {
|
|
3316
|
+
console.log(
|
|
3317
|
+
`[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
this.preloadingAdUrls.add(url);
|
|
3321
|
+
this.ima.preloadAds(url).then(() => {
|
|
3322
|
+
if (this.config.debugAdTiming) {
|
|
3323
|
+
console.log(
|
|
3324
|
+
`[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
|
|
3325
|
+
);
|
|
3326
|
+
}
|
|
3327
|
+
}).catch((error) => {
|
|
3328
|
+
if (this.config.debugAdTiming) {
|
|
3329
|
+
console.warn(
|
|
3330
|
+
`[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
|
|
3331
|
+
error
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
}).finally(() => {
|
|
3335
|
+
this.preloadingAdUrls.delete(url);
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3118
3339
|
getRemainingAdMs() {
|
|
3119
3340
|
if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
|
|
3120
3341
|
return 0;
|
|
@@ -3266,6 +3487,9 @@ var StormcloudVideoPlayer = class {
|
|
|
3266
3487
|
}
|
|
3267
3488
|
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
3268
3489
|
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
3490
|
+
this.releaseAdHoldState();
|
|
3491
|
+
this.preloadingAdUrls.clear();
|
|
3492
|
+
this.adPodAllUrls = [];
|
|
3269
3493
|
}
|
|
3270
3494
|
};
|
|
3271
3495
|
|