stormcloud-video-player 0.2.9 → 0.2.10

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.
@@ -73,9 +73,22 @@ function createImaController(video, options) {
73
73
  'script[data-ima="true"]'
74
74
  );
75
75
  if (existing) {
76
- return new Promise(
77
- (resolve) => existing.addEventListener("load", () => resolve())
78
- );
76
+ if (window.google?.ima) {
77
+ return Promise.resolve();
78
+ }
79
+ return new Promise((resolve, reject) => {
80
+ const timeout = setTimeout(() => {
81
+ reject(new Error("IMA SDK load timeout"));
82
+ }, 1e4);
83
+ existing.addEventListener("load", () => {
84
+ clearTimeout(timeout);
85
+ resolve();
86
+ });
87
+ existing.addEventListener("error", () => {
88
+ clearTimeout(timeout);
89
+ reject(new Error("IMA SDK load failed"));
90
+ });
91
+ });
79
92
  }
80
93
  return new Promise((resolve, reject) => {
81
94
  const script = document.createElement("script");
@@ -104,6 +117,17 @@ function createImaController(video, options) {
104
117
  adsRequest.adTagUrl = vastTagUrl;
105
118
  adsLoader.requestAds(adsRequest);
106
119
  }
120
+ function destroyAdsManager() {
121
+ if (adsManager) {
122
+ try {
123
+ console.log("[IMA] Destroying existing ads manager");
124
+ adsManager.destroy();
125
+ } catch (error) {
126
+ console.warn("[IMA] Error destroying ads manager:", error);
127
+ }
128
+ adsManager = void 0;
129
+ }
130
+ }
107
131
  return {
108
132
  initialize() {
109
133
  ensureImaLoaded().then(() => {
@@ -136,9 +160,22 @@ function createImaController(video, options) {
136
160
  },
137
161
  async requestAds(vastTagUrl) {
138
162
  console.log("[IMA] Requesting ads:", vastTagUrl);
163
+ if (adPlaying) {
164
+ console.warn(
165
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
166
+ );
167
+ return Promise.reject(
168
+ new Error("Ad already playing - cannot request new ads")
169
+ );
170
+ }
171
+ destroyAdsManager();
172
+ adsLoadedReject = void 0;
173
+ adsLoadedResolve = void 0;
174
+ let currentReject;
139
175
  adsLoadedPromise = new Promise((resolve, reject) => {
140
176
  adsLoadedResolve = resolve;
141
177
  adsLoadedReject = reject;
178
+ currentReject = reject;
142
179
  setTimeout(() => {
143
180
  if (adsLoadedReject) {
144
181
  adsLoadedReject(new Error("Ad request timeout"));
@@ -160,7 +197,7 @@ function createImaController(video, options) {
160
197
  container.style.top = "0";
161
198
  container.style.right = "0";
162
199
  container.style.bottom = "0";
163
- container.style.display = "flex";
200
+ container.style.display = "none";
164
201
  container.style.alignItems = "center";
165
202
  container.style.justifyContent = "center";
166
203
  container.style.pointerEvents = "none";
@@ -200,14 +237,14 @@ function createImaController(video, options) {
200
237
  AdErrorEvent.AD_ERROR,
201
238
  (errorEvent) => {
202
239
  console.error("[IMA] Ad error:", errorEvent.getError());
203
- try {
204
- adsManager?.destroy?.();
205
- } catch {
206
- }
240
+ destroyAdsManager();
207
241
  adPlaying = false;
208
242
  video.muted = originalMutedState;
209
- if (adContainerEl)
243
+ if (adContainerEl) {
210
244
  adContainerEl.style.pointerEvents = "none";
245
+ adContainerEl.style.display = "none";
246
+ console.log("[IMA] Ad container hidden after error");
247
+ }
211
248
  if (adsLoadedReject) {
212
249
  adsLoadedReject(new Error("Ad playback error"));
213
250
  adsLoadedReject = void 0;
@@ -240,10 +277,6 @@ function createImaController(video, options) {
240
277
  AdEvent.CONTENT_PAUSE_REQUESTED,
241
278
  () => {
242
279
  console.log("[IMA] Content pause requested");
243
- if (!adPlaying) {
244
- originalMutedState = video.muted;
245
- }
246
- video.muted = true;
247
280
  if (!options?.continueLiveStreamDuringAds) {
248
281
  video.pause();
249
282
  console.log("[IMA] Video paused (VOD mode)");
@@ -252,20 +285,34 @@ function createImaController(video, options) {
252
285
  "[IMA] Video continues playing but muted (Live mode)"
253
286
  );
254
287
  }
288
+ video.muted = true;
255
289
  adPlaying = true;
256
- if (adContainerEl)
257
- adContainerEl.style.pointerEvents = "auto";
258
290
  emit("content_pause");
259
291
  }
260
292
  );
293
+ adsManager.addEventListener(AdEvent.STARTED, () => {
294
+ console.log("[IMA] Ad started playing");
295
+ if (adContainerEl) {
296
+ adContainerEl.style.pointerEvents = "auto";
297
+ adContainerEl.style.display = "flex";
298
+ console.log(
299
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
300
+ );
301
+ }
302
+ });
261
303
  adsManager.addEventListener(
262
304
  AdEvent.CONTENT_RESUME_REQUESTED,
263
305
  () => {
264
306
  console.log("[IMA] Content resume requested");
265
307
  adPlaying = false;
266
308
  video.muted = originalMutedState;
267
- if (adContainerEl)
309
+ if (adContainerEl) {
268
310
  adContainerEl.style.pointerEvents = "none";
311
+ adContainerEl.style.display = "none";
312
+ console.log(
313
+ "[IMA] Ad container hidden - pointer events disabled"
314
+ );
315
+ }
269
316
  if (!options?.continueLiveStreamDuringAds) {
270
317
  video.play()?.catch(() => {
271
318
  });
@@ -282,7 +329,13 @@ function createImaController(video, options) {
282
329
  console.log("[IMA] All ads completed");
283
330
  adPlaying = false;
284
331
  video.muted = originalMutedState;
285
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
332
+ if (adContainerEl) {
333
+ adContainerEl.style.pointerEvents = "none";
334
+ adContainerEl.style.display = "none";
335
+ console.log(
336
+ "[IMA] Ad container hidden after all ads completed"
337
+ );
338
+ }
286
339
  if (!options?.continueLiveStreamDuringAds) {
287
340
  video.play().catch(() => {
288
341
  });
@@ -325,6 +378,13 @@ function createImaController(video, options) {
325
378
  google.ima.AdErrorEvent.Type.AD_ERROR,
326
379
  (adErrorEvent) => {
327
380
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
381
+ adPlaying = false;
382
+ video.muted = originalMutedState;
383
+ if (adContainerEl) adContainerEl.style.pointerEvents = "none";
384
+ if (!options?.continueLiveStreamDuringAds) {
385
+ video.play().catch(() => {
386
+ });
387
+ }
328
388
  if (adsLoadedReject) {
329
389
  adsLoadedReject(new Error("Ads loader error"));
330
390
  adsLoadedReject = void 0;
@@ -340,11 +400,9 @@ function createImaController(video, options) {
340
400
  return adsLoadedPromise;
341
401
  } catch (error) {
342
402
  console.error("[IMA] Failed to request ads:", error);
343
- if (adsLoadedReject) {
344
- adsLoadedReject(error);
345
- adsLoadedReject = void 0;
346
- adsLoadedResolve = void 0;
347
- }
403
+ currentReject?.(error);
404
+ adsLoadedReject = void 0;
405
+ adsLoadedResolve = void 0;
348
406
  return Promise.reject(error);
349
407
  }
350
408
  },
@@ -381,10 +439,16 @@ function createImaController(video, options) {
381
439
  async stop() {
382
440
  adPlaying = false;
383
441
  video.muted = originalMutedState;
442
+ if (adContainerEl) {
443
+ adContainerEl.style.pointerEvents = "none";
444
+ adContainerEl.style.display = "none";
445
+ console.log("[IMA] Ad container hidden after stop");
446
+ }
384
447
  try {
385
448
  adsManager?.stop?.();
386
449
  } catch {
387
450
  }
451
+ destroyAdsManager();
388
452
  if (!options?.continueLiveStreamDuringAds) {
389
453
  video.play().catch(() => {
390
454
  });
@@ -394,12 +458,13 @@ function createImaController(video, options) {
394
458
  }
395
459
  },
396
460
  destroy() {
397
- try {
398
- adsManager?.destroy?.();
399
- } catch {
400
- }
461
+ destroyAdsManager();
401
462
  adPlaying = false;
402
463
  video.muted = originalMutedState;
464
+ if (adContainerEl) {
465
+ adContainerEl.style.pointerEvents = "none";
466
+ adContainerEl.style.display = "none";
467
+ }
403
468
  try {
404
469
  adsLoader?.destroy?.();
405
470
  } catch {
@@ -407,6 +472,9 @@ function createImaController(video, options) {
407
472
  if (adContainerEl?.parentElement) {
408
473
  adContainerEl.parentElement.removeChild(adContainerEl);
409
474
  }
475
+ adContainerEl = void 0;
476
+ adDisplayContainer = void 0;
477
+ adsLoader = void 0;
410
478
  },
411
479
  isAdPlaying() {
412
480
  return adPlaying;
@@ -884,17 +952,8 @@ var StormcloudVideoPlayer = class {
884
952
  this.video.muted = !!this.config.muted;
885
953
  this.ima.initialize();
886
954
  this.ima.on("all_ads_completed", () => {
887
- if (!this.inAdBreak) return;
888
- const remaining = this.getRemainingAdMs();
889
- if (remaining > 500 && this.adPodQueue.length > 0) {
890
- const next = this.adPodQueue.shift();
891
- this.currentAdIndex++;
892
- this.playSingleAd(next).catch(() => {
893
- });
894
- } else {
895
- this.currentAdIndex = 0;
896
- this.totalAdsInBreak = 0;
897
- this.showAds = false;
955
+ if (this.config.debugAdTiming) {
956
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
898
957
  }
899
958
  });
900
959
  this.ima.on("ad_error", () => {
@@ -925,6 +984,26 @@ var StormcloudVideoPlayer = class {
925
984
  );
926
985
  }
927
986
  this.clearAdFailsafeTimer();
987
+ if (!this.inAdBreak) return;
988
+ const remaining = this.getRemainingAdMs();
989
+ if (remaining > 500 && this.adPodQueue.length > 0) {
990
+ const next = this.adPodQueue.shift();
991
+ this.currentAdIndex++;
992
+ if (this.config.debugAdTiming) {
993
+ console.log(
994
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
995
+ );
996
+ }
997
+ this.playSingleAd(next).catch(() => {
998
+ });
999
+ } else {
1000
+ if (this.config.debugAdTiming) {
1001
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1002
+ }
1003
+ this.currentAdIndex = 0;
1004
+ this.totalAdsInBreak = 0;
1005
+ this.showAds = false;
1006
+ }
928
1007
  });
929
1008
  this.video.addEventListener("timeupdate", () => {
930
1009
  this.onTimeUpdate(this.video.currentTime);
@@ -1574,21 +1653,21 @@ var StormcloudVideoPlayer = class {
1574
1653
  if (this.config.debugAdTiming) {
1575
1654
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1576
1655
  }
1577
- this.ima.updateOriginalMutedState(this.video.muted);
1578
- if (!this.shouldContinueLiveStreamDuringAds()) {
1579
- if (this.config.debugAdTiming) {
1580
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1581
- }
1582
- this.video.pause();
1583
- } else {
1656
+ if (this.ima.isAdPlaying()) {
1584
1657
  if (this.config.debugAdTiming) {
1585
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1658
+ console.warn(
1659
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1660
+ );
1586
1661
  }
1587
- this.video.muted = true;
1662
+ return;
1588
1663
  }
1664
+ this.ima.updateOriginalMutedState(this.video.muted);
1589
1665
  this.startAdFailsafeTimer();
1590
1666
  try {
1591
1667
  await this.ima.requestAds(vastTagUrl);
1668
+ if (this.config.debugAdTiming) {
1669
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1670
+ }
1592
1671
  await this.ima.play();
1593
1672
  if (this.config.debugAdTiming) {
1594
1673
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1616,6 +1695,13 @@ var StormcloudVideoPlayer = class {
1616
1695
  this.showAds = false;
1617
1696
  this.currentAdIndex = 0;
1618
1697
  this.totalAdsInBreak = 0;
1698
+ const originalMutedState = this.ima.getOriginalMutedState();
1699
+ this.video.muted = originalMutedState;
1700
+ if (this.config.debugAdTiming) {
1701
+ console.log(
1702
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1703
+ );
1704
+ }
1619
1705
  if (this.video.paused) {
1620
1706
  this.video.play()?.catch(() => {
1621
1707
  if (this.config.debugAdTiming) {