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.
@@ -115,9 +115,22 @@ function createImaController(video, options) {
115
115
  'script[data-ima="true"]'
116
116
  );
117
117
  if (existing) {
118
- return new Promise(
119
- (resolve) => existing.addEventListener("load", () => resolve())
120
- );
118
+ if (window.google?.ima) {
119
+ return Promise.resolve();
120
+ }
121
+ return new Promise((resolve, reject) => {
122
+ const timeout = setTimeout(() => {
123
+ reject(new Error("IMA SDK load timeout"));
124
+ }, 1e4);
125
+ existing.addEventListener("load", () => {
126
+ clearTimeout(timeout);
127
+ resolve();
128
+ });
129
+ existing.addEventListener("error", () => {
130
+ clearTimeout(timeout);
131
+ reject(new Error("IMA SDK load failed"));
132
+ });
133
+ });
121
134
  }
122
135
  return new Promise((resolve, reject) => {
123
136
  const script = document.createElement("script");
@@ -146,6 +159,17 @@ function createImaController(video, options) {
146
159
  adsRequest.adTagUrl = vastTagUrl;
147
160
  adsLoader.requestAds(adsRequest);
148
161
  }
162
+ function destroyAdsManager() {
163
+ if (adsManager) {
164
+ try {
165
+ console.log("[IMA] Destroying existing ads manager");
166
+ adsManager.destroy();
167
+ } catch (error) {
168
+ console.warn("[IMA] Error destroying ads manager:", error);
169
+ }
170
+ adsManager = void 0;
171
+ }
172
+ }
149
173
  return {
150
174
  initialize() {
151
175
  ensureImaLoaded().then(() => {
@@ -178,9 +202,22 @@ function createImaController(video, options) {
178
202
  },
179
203
  async requestAds(vastTagUrl) {
180
204
  console.log("[IMA] Requesting ads:", vastTagUrl);
205
+ if (adPlaying) {
206
+ console.warn(
207
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
208
+ );
209
+ return Promise.reject(
210
+ new Error("Ad already playing - cannot request new ads")
211
+ );
212
+ }
213
+ destroyAdsManager();
214
+ adsLoadedReject = void 0;
215
+ adsLoadedResolve = void 0;
216
+ let currentReject;
181
217
  adsLoadedPromise = new Promise((resolve, reject) => {
182
218
  adsLoadedResolve = resolve;
183
219
  adsLoadedReject = reject;
220
+ currentReject = reject;
184
221
  setTimeout(() => {
185
222
  if (adsLoadedReject) {
186
223
  adsLoadedReject(new Error("Ad request timeout"));
@@ -202,7 +239,7 @@ function createImaController(video, options) {
202
239
  container.style.top = "0";
203
240
  container.style.right = "0";
204
241
  container.style.bottom = "0";
205
- container.style.display = "flex";
242
+ container.style.display = "none";
206
243
  container.style.alignItems = "center";
207
244
  container.style.justifyContent = "center";
208
245
  container.style.pointerEvents = "none";
@@ -242,14 +279,14 @@ function createImaController(video, options) {
242
279
  AdErrorEvent.AD_ERROR,
243
280
  (errorEvent) => {
244
281
  console.error("[IMA] Ad error:", errorEvent.getError());
245
- try {
246
- adsManager?.destroy?.();
247
- } catch {
248
- }
282
+ destroyAdsManager();
249
283
  adPlaying = false;
250
284
  video.muted = originalMutedState;
251
- if (adContainerEl)
285
+ if (adContainerEl) {
252
286
  adContainerEl.style.pointerEvents = "none";
287
+ adContainerEl.style.display = "none";
288
+ console.log("[IMA] Ad container hidden after error");
289
+ }
253
290
  if (adsLoadedReject) {
254
291
  adsLoadedReject(new Error("Ad playback error"));
255
292
  adsLoadedReject = void 0;
@@ -282,10 +319,6 @@ function createImaController(video, options) {
282
319
  AdEvent.CONTENT_PAUSE_REQUESTED,
283
320
  () => {
284
321
  console.log("[IMA] Content pause requested");
285
- if (!adPlaying) {
286
- originalMutedState = video.muted;
287
- }
288
- video.muted = true;
289
322
  if (!options?.continueLiveStreamDuringAds) {
290
323
  video.pause();
291
324
  console.log("[IMA] Video paused (VOD mode)");
@@ -294,20 +327,34 @@ function createImaController(video, options) {
294
327
  "[IMA] Video continues playing but muted (Live mode)"
295
328
  );
296
329
  }
330
+ video.muted = true;
297
331
  adPlaying = true;
298
- if (adContainerEl)
299
- adContainerEl.style.pointerEvents = "auto";
300
332
  emit("content_pause");
301
333
  }
302
334
  );
335
+ adsManager.addEventListener(AdEvent.STARTED, () => {
336
+ console.log("[IMA] Ad started playing");
337
+ if (adContainerEl) {
338
+ adContainerEl.style.pointerEvents = "auto";
339
+ adContainerEl.style.display = "flex";
340
+ console.log(
341
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
342
+ );
343
+ }
344
+ });
303
345
  adsManager.addEventListener(
304
346
  AdEvent.CONTENT_RESUME_REQUESTED,
305
347
  () => {
306
348
  console.log("[IMA] Content resume requested");
307
349
  adPlaying = false;
308
350
  video.muted = originalMutedState;
309
- if (adContainerEl)
351
+ if (adContainerEl) {
310
352
  adContainerEl.style.pointerEvents = "none";
353
+ adContainerEl.style.display = "none";
354
+ console.log(
355
+ "[IMA] Ad container hidden - pointer events disabled"
356
+ );
357
+ }
311
358
  if (!options?.continueLiveStreamDuringAds) {
312
359
  video.play()?.catch(() => {
313
360
  });
@@ -324,7 +371,13 @@ function createImaController(video, options) {
324
371
  console.log("[IMA] All ads completed");
325
372
  adPlaying = false;
326
373
  video.muted = originalMutedState;
327
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
374
+ if (adContainerEl) {
375
+ adContainerEl.style.pointerEvents = "none";
376
+ adContainerEl.style.display = "none";
377
+ console.log(
378
+ "[IMA] Ad container hidden after all ads completed"
379
+ );
380
+ }
328
381
  if (!options?.continueLiveStreamDuringAds) {
329
382
  video.play().catch(() => {
330
383
  });
@@ -367,6 +420,13 @@ function createImaController(video, options) {
367
420
  google.ima.AdErrorEvent.Type.AD_ERROR,
368
421
  (adErrorEvent) => {
369
422
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
423
+ adPlaying = false;
424
+ video.muted = originalMutedState;
425
+ if (adContainerEl) adContainerEl.style.pointerEvents = "none";
426
+ if (!options?.continueLiveStreamDuringAds) {
427
+ video.play().catch(() => {
428
+ });
429
+ }
370
430
  if (adsLoadedReject) {
371
431
  adsLoadedReject(new Error("Ads loader error"));
372
432
  adsLoadedReject = void 0;
@@ -382,11 +442,9 @@ function createImaController(video, options) {
382
442
  return adsLoadedPromise;
383
443
  } catch (error) {
384
444
  console.error("[IMA] Failed to request ads:", error);
385
- if (adsLoadedReject) {
386
- adsLoadedReject(error);
387
- adsLoadedReject = void 0;
388
- adsLoadedResolve = void 0;
389
- }
445
+ currentReject?.(error);
446
+ adsLoadedReject = void 0;
447
+ adsLoadedResolve = void 0;
390
448
  return Promise.reject(error);
391
449
  }
392
450
  },
@@ -423,10 +481,16 @@ function createImaController(video, options) {
423
481
  async stop() {
424
482
  adPlaying = false;
425
483
  video.muted = originalMutedState;
484
+ if (adContainerEl) {
485
+ adContainerEl.style.pointerEvents = "none";
486
+ adContainerEl.style.display = "none";
487
+ console.log("[IMA] Ad container hidden after stop");
488
+ }
426
489
  try {
427
490
  adsManager?.stop?.();
428
491
  } catch {
429
492
  }
493
+ destroyAdsManager();
430
494
  if (!options?.continueLiveStreamDuringAds) {
431
495
  video.play().catch(() => {
432
496
  });
@@ -436,12 +500,13 @@ function createImaController(video, options) {
436
500
  }
437
501
  },
438
502
  destroy() {
439
- try {
440
- adsManager?.destroy?.();
441
- } catch {
442
- }
503
+ destroyAdsManager();
443
504
  adPlaying = false;
444
505
  video.muted = originalMutedState;
506
+ if (adContainerEl) {
507
+ adContainerEl.style.pointerEvents = "none";
508
+ adContainerEl.style.display = "none";
509
+ }
445
510
  try {
446
511
  adsLoader?.destroy?.();
447
512
  } catch {
@@ -449,6 +514,9 @@ function createImaController(video, options) {
449
514
  if (adContainerEl?.parentElement) {
450
515
  adContainerEl.parentElement.removeChild(adContainerEl);
451
516
  }
517
+ adContainerEl = void 0;
518
+ adDisplayContainer = void 0;
519
+ adsLoader = void 0;
452
520
  },
453
521
  isAdPlaying() {
454
522
  return adPlaying;
@@ -926,17 +994,8 @@ var StormcloudVideoPlayer = class {
926
994
  this.video.muted = !!this.config.muted;
927
995
  this.ima.initialize();
928
996
  this.ima.on("all_ads_completed", () => {
929
- if (!this.inAdBreak) return;
930
- const remaining = this.getRemainingAdMs();
931
- if (remaining > 500 && this.adPodQueue.length > 0) {
932
- const next = this.adPodQueue.shift();
933
- this.currentAdIndex++;
934
- this.playSingleAd(next).catch(() => {
935
- });
936
- } else {
937
- this.currentAdIndex = 0;
938
- this.totalAdsInBreak = 0;
939
- this.showAds = false;
997
+ if (this.config.debugAdTiming) {
998
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
940
999
  }
941
1000
  });
942
1001
  this.ima.on("ad_error", () => {
@@ -967,6 +1026,26 @@ var StormcloudVideoPlayer = class {
967
1026
  );
968
1027
  }
969
1028
  this.clearAdFailsafeTimer();
1029
+ if (!this.inAdBreak) return;
1030
+ const remaining = this.getRemainingAdMs();
1031
+ if (remaining > 500 && this.adPodQueue.length > 0) {
1032
+ const next = this.adPodQueue.shift();
1033
+ this.currentAdIndex++;
1034
+ if (this.config.debugAdTiming) {
1035
+ console.log(
1036
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
1037
+ );
1038
+ }
1039
+ this.playSingleAd(next).catch(() => {
1040
+ });
1041
+ } else {
1042
+ if (this.config.debugAdTiming) {
1043
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1044
+ }
1045
+ this.currentAdIndex = 0;
1046
+ this.totalAdsInBreak = 0;
1047
+ this.showAds = false;
1048
+ }
970
1049
  });
971
1050
  this.video.addEventListener("timeupdate", () => {
972
1051
  this.onTimeUpdate(this.video.currentTime);
@@ -1616,21 +1695,21 @@ var StormcloudVideoPlayer = class {
1616
1695
  if (this.config.debugAdTiming) {
1617
1696
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1618
1697
  }
1619
- this.ima.updateOriginalMutedState(this.video.muted);
1620
- if (!this.shouldContinueLiveStreamDuringAds()) {
1621
- if (this.config.debugAdTiming) {
1622
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1623
- }
1624
- this.video.pause();
1625
- } else {
1698
+ if (this.ima.isAdPlaying()) {
1626
1699
  if (this.config.debugAdTiming) {
1627
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1700
+ console.warn(
1701
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1702
+ );
1628
1703
  }
1629
- this.video.muted = true;
1704
+ return;
1630
1705
  }
1706
+ this.ima.updateOriginalMutedState(this.video.muted);
1631
1707
  this.startAdFailsafeTimer();
1632
1708
  try {
1633
1709
  await this.ima.requestAds(vastTagUrl);
1710
+ if (this.config.debugAdTiming) {
1711
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1712
+ }
1634
1713
  await this.ima.play();
1635
1714
  if (this.config.debugAdTiming) {
1636
1715
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1658,6 +1737,13 @@ var StormcloudVideoPlayer = class {
1658
1737
  this.showAds = false;
1659
1738
  this.currentAdIndex = 0;
1660
1739
  this.totalAdsInBreak = 0;
1740
+ const originalMutedState = this.ima.getOriginalMutedState();
1741
+ this.video.muted = originalMutedState;
1742
+ if (this.config.debugAdTiming) {
1743
+ console.log(
1744
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1745
+ );
1746
+ }
1661
1747
  if (this.video.paused) {
1662
1748
  this.video.play()?.catch(() => {
1663
1749
  if (this.config.debugAdTiming) {