stormcloud-video-player 0.2.31 → 0.2.33

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.
@@ -390,15 +390,17 @@ function createImaController(video, options) {
390
390
  let adsLoadedResolve;
391
391
  let adsLoadedReject;
392
392
  function makeAdsRequest(google, vastTagUrl) {
393
+ console.log("[IMA] \u{1F4CB} === makeAdsRequest() - Building IMA request ===");
393
394
  const adsRequest = new google.ima.AdsRequest();
394
395
  const preloadedResponse = preloadedVast.get(vastTagUrl);
395
396
  if (preloadedResponse) {
396
397
  adsRequest.adsResponse = preloadedResponse;
397
398
  console.log(
398
- "[IMA] Using preloaded VAST response for immediate ad request"
399
+ "[IMA] \u26A1 Using preloaded VAST response for immediate ad request"
399
400
  );
400
401
  } else {
401
402
  adsRequest.adTagUrl = vastTagUrl;
403
+ console.log("[IMA] \u{1F310} Will fetch VAST from URL:", vastTagUrl);
402
404
  }
403
405
  const videoWidth = video.offsetWidth || video.clientWidth || 640;
404
406
  const videoHeight = video.offsetHeight || video.clientHeight || 360;
@@ -410,6 +412,7 @@ function createImaController(video, options) {
410
412
  try {
411
413
  const willAutoPlay = !video.paused || video.autoplay;
412
414
  adsRequest.setAdWillAutoPlay(willAutoPlay);
415
+ console.log(`[IMA] Ad will autoplay: ${willAutoPlay}`);
413
416
  } catch (error) {
414
417
  console.warn("[IMA] Failed to call setAdWillAutoPlay:", error);
415
418
  }
@@ -418,13 +421,17 @@ function createImaController(video, options) {
418
421
  try {
419
422
  const willPlayMuted = video.muted || video.volume === 0;
420
423
  adsRequest.setAdWillPlayMuted(willPlayMuted);
424
+ console.log(`[IMA] Ad will play muted: ${willPlayMuted}`);
421
425
  } catch (error) {
422
426
  console.warn("[IMA] Failed to call setAdWillPlayMuted:", error);
423
427
  }
424
428
  }
425
429
  adsRequest.vastLoadTimeout = 5e3;
426
- console.log(`[IMA] Ads request dimensions: ${videoWidth}x${videoHeight}`);
430
+ console.log(`[IMA] \u{1F4D0} Ads request dimensions: ${videoWidth}x${videoHeight}`);
431
+ console.log("[IMA] \u23F1\uFE0F VAST load timeout: 5000ms");
432
+ console.log("[IMA] \u{1F680} Calling adsLoader.requestAds()...");
427
433
  adsLoader.requestAds(adsRequest);
434
+ console.log("[IMA] \u23F3 Waiting for ADS_MANAGER_LOADED or AD_ERROR event...");
428
435
  if (preloadedResponse) {
429
436
  preloadedVast.delete(vastTagUrl);
430
437
  }
@@ -502,22 +509,24 @@ function createImaController(video, options) {
502
509
  });
503
510
  },
504
511
  async requestAds(vastTagUrl) {
505
- console.log("[IMA] Requesting ads:", vastTagUrl);
512
+ console.log("[IMA] \u{1F4E1} === requestAds() called ===");
513
+ console.log("[IMA] VAST URL:", vastTagUrl);
514
+ console.log("[IMA] This will fetch the ad from the server - no visual change yet");
506
515
  if (!vastTagUrl || vastTagUrl.trim() === "") {
507
516
  const error = new Error("VAST tag URL is empty or undefined");
508
- console.warn("[IMA]", error.message);
517
+ console.warn("[IMA] \u274C", error.message);
509
518
  return Promise.reject(error);
510
519
  }
511
520
  try {
512
521
  new URL(vastTagUrl);
513
522
  } catch (e) {
514
523
  const error = new Error(`Invalid VAST tag URL format: ${vastTagUrl}`);
515
- console.warn("[IMA]", error.message);
524
+ console.warn("[IMA] \u274C", error.message);
516
525
  return Promise.reject(error);
517
526
  }
518
527
  if (adPlaying) {
519
528
  console.warn(
520
- "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
529
+ "[IMA] \u26A0\uFE0F Cannot request new ads while an ad is playing. Call stop() first."
521
530
  );
522
531
  return Promise.reject(
523
532
  new Error("Ad already playing - cannot request new ads")
@@ -607,20 +616,85 @@ function createImaController(video, options) {
607
616
  adsLoader.addEventListener(
608
617
  google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
609
618
  (evt) => {
610
- console.log(
611
- "[IMA] Ads manager loaded - enabling preloading for continuous playback"
612
- );
619
+ console.log("[IMA] \u2705 ADS_MANAGER_LOADED - Ads fetched successfully!");
620
+ console.log("[IMA] Setting up ads manager with preloading enabled");
621
+ console.log("[IMA] ========================================");
622
+ console.log("[IMA] EXPECTED EVENT FLOW:");
623
+ console.log("[IMA] 1. requestAds() \u2192 fetch VAST");
624
+ console.log("[IMA] 2. ADS_MANAGER_LOADED \u2192 ads ready");
625
+ console.log("[IMA] 3. play() \u2192 start playback");
626
+ console.log("[IMA] 4. CONTENT_PAUSE_REQUESTED \u2192 show ad layer \u2728");
627
+ console.log("[IMA] 5. STARTED \u2192 ad is playing");
628
+ console.log("[IMA] 6. CONTENT_RESUME_REQUESTED \u2192 ad done");
629
+ console.log("[IMA] 7. ALL_ADS_COMPLETED \u2192 hide ad layer");
630
+ console.log("[IMA] ========================================");
613
631
  try {
614
632
  const adsRenderingSettings = new google.ima.AdsRenderingSettings();
615
633
  adsRenderingSettings.enablePreloading = true;
616
634
  adsManager = evt.getAdsManager(video, adsRenderingSettings);
617
635
  const AdEvent = google.ima.AdEvent.Type;
618
636
  const AdErrorEvent = google.ima.AdErrorEvent.Type;
637
+ console.log("[IMA] ========== IMA EVENT LOGGING ENABLED ==========");
638
+ console.log("[IMA] All IMA SDK events will be logged below");
639
+ const allAdEvents = [
640
+ "AD_BREAK_READY",
641
+ "AD_METADATA",
642
+ "ALL_ADS_COMPLETED",
643
+ "CLICK",
644
+ "COMPLETE",
645
+ "CONTENT_PAUSE_REQUESTED",
646
+ "CONTENT_RESUME_REQUESTED",
647
+ "DURATION_CHANGE",
648
+ "FIRST_QUARTILE",
649
+ "IMPRESSION",
650
+ "INTERACTION",
651
+ "LINEAR_CHANGED",
652
+ "LOADED",
653
+ "LOG",
654
+ "MIDPOINT",
655
+ "PAUSED",
656
+ "RESUMED",
657
+ "SKIPPABLE_STATE_CHANGED",
658
+ "SKIPPED",
659
+ "STARTED",
660
+ "THIRD_QUARTILE",
661
+ "USER_CLOSE",
662
+ "VOLUME_CHANGED",
663
+ "VOLUME_MUTED"
664
+ ];
665
+ allAdEvents.forEach((eventType) => {
666
+ if (AdEvent[eventType]) {
667
+ adsManager.addEventListener(AdEvent[eventType], (e) => {
668
+ var _a, _b, _c, _d, _e, _f;
669
+ const ad = (_a = e.getAd) == null ? void 0 : _a.call(e);
670
+ const adData = ad ? {
671
+ adId: (_b = ad.getAdId) == null ? void 0 : _b.call(ad),
672
+ title: (_c = ad.getTitle) == null ? void 0 : _c.call(ad),
673
+ duration: (_d = ad.getDuration) == null ? void 0 : _d.call(ad),
674
+ isLinear: (_e = ad.isLinear) == null ? void 0 : _e.call(ad),
675
+ contentType: (_f = ad.getContentType) == null ? void 0 : _f.call(ad)
676
+ } : null;
677
+ console.log(`[IMA EVENT] ${eventType}`, {
678
+ eventType,
679
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
680
+ adData
681
+ });
682
+ });
683
+ }
684
+ });
685
+ console.log("[IMA] ========== EVENT LISTENERS ATTACHED ==========");
619
686
  adsManager.addEventListener(
620
687
  AdErrorEvent.AD_ERROR,
621
688
  (errorEvent) => {
622
- var _a;
623
- console.error("[IMA] Ad error:", errorEvent.getError());
689
+ var _a, _b, _c, _d, _e, _f;
690
+ const error = errorEvent.getError();
691
+ console.error("[IMA] \u274C AD_ERROR Event:", {
692
+ message: (_a = error.getMessage) == null ? void 0 : _a.call(error),
693
+ errorCode: (_b = error.getErrorCode) == null ? void 0 : _b.call(error),
694
+ type: (_c = error.getType) == null ? void 0 : _c.call(error),
695
+ vastErrorCode: (_d = error.getVastErrorCode) == null ? void 0 : _d.call(error),
696
+ innerError: (_e = error.getInnerError) == null ? void 0 : _e.call(error)
697
+ });
624
698
  destroyAdsManager();
625
699
  adPlaying = false;
626
700
  setAdPlayingFlag(false);
@@ -662,7 +736,7 @@ function createImaController(video, options) {
662
736
  console.log(
663
737
  "[IMA] Resuming paused video after ad error"
664
738
  );
665
- (_a = video.play()) == null ? void 0 : _a.catch(() => {
739
+ (_f = video.play()) == null ? void 0 : _f.catch(() => {
666
740
  });
667
741
  }
668
742
  }
@@ -672,7 +746,8 @@ function createImaController(video, options) {
672
746
  adsManager.addEventListener(
673
747
  AdEvent.CONTENT_PAUSE_REQUESTED,
674
748
  () => {
675
- console.log("[IMA] Content pause requested");
749
+ console.log("[IMA] \u2705 CONTENT_PAUSE_REQUESTED - Ad is ready to play!");
750
+ console.log("[IMA] This is the event that triggers the ad layer to appear");
676
751
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
677
752
  video.pause();
678
753
  console.log("[IMA] Content video paused (VOD mode)");
@@ -681,13 +756,23 @@ function createImaController(video, options) {
681
756
  "[IMA] Content video continues in background (Live mode)"
682
757
  );
683
758
  }
759
+ hideContentVideo();
760
+ if (adContainerEl) {
761
+ adContainerEl.style.pointerEvents = "auto";
762
+ adContainerEl.style.display = "flex";
763
+ adContainerEl.style.backgroundColor = "#000";
764
+ adContainerEl.offsetHeight;
765
+ adContainerEl.style.opacity = "1";
766
+ console.log("[IMA] \u2728 Ad container NOW VISIBLE (after content pause)");
767
+ }
684
768
  adPlaying = true;
685
769
  setAdPlayingFlag(true);
770
+ console.log("[IMA] Emitting 'content_pause' event to player");
686
771
  emit("content_pause");
687
772
  }
688
773
  );
689
774
  adsManager.addEventListener(AdEvent.STARTED, () => {
690
- console.log("[IMA] Ad started - showing ad video");
775
+ console.log("[IMA] \u25B6\uFE0F STARTED - Ad playback has begun");
691
776
  setAdPlayingFlag(true);
692
777
  hideContentVideo();
693
778
  if (adVideoElement) {
@@ -703,20 +788,21 @@ function createImaController(video, options) {
703
788
  adContainerEl.style.backgroundColor = "#000";
704
789
  adContainerEl.offsetHeight;
705
790
  adContainerEl.style.opacity = "1";
706
- console.log("[IMA] Ad container now visible");
791
+ console.log("[IMA] Ad container now visible (STARTED event)");
707
792
  }
708
793
  });
709
794
  adsManager.addEventListener(
710
795
  AdEvent.CONTENT_RESUME_REQUESTED,
711
796
  () => {
712
- console.log("[IMA] Content resume requested");
797
+ console.log("[IMA] \u23F8\uFE0F CONTENT_RESUME_REQUESTED - Single ad completed");
713
798
  adPlaying = false;
714
799
  setAdPlayingFlag(false);
800
+ console.log("[IMA] Emitting 'content_resume' event to player");
715
801
  emit("content_resume");
716
802
  }
717
803
  );
718
804
  adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
719
- console.log("[IMA] All ads completed - restoring content");
805
+ console.log("[IMA] \u{1F3C1} ALL_ADS_COMPLETED - All ads in break finished");
720
806
  adPlaying = false;
721
807
  setAdPlayingFlag(false);
722
808
  if (adContainerEl) {
@@ -726,7 +812,7 @@ function createImaController(video, options) {
726
812
  if (adContainerEl) {
727
813
  adContainerEl.style.pointerEvents = "none";
728
814
  adContainerEl.style.display = "none";
729
- console.log("[IMA] Ad container hidden");
815
+ console.log("[IMA] \u{1F648} Ad container hidden (ALL_ADS_COMPLETED)");
730
816
  }
731
817
  }, 300);
732
818
  }
@@ -737,6 +823,7 @@ function createImaController(video, options) {
737
823
  console.warn("[IMA] Failed to resume content video:", e);
738
824
  });
739
825
  }
826
+ console.log("[IMA] Emitting 'all_ads_completed' event to player");
740
827
  emit("all_ads_completed");
741
828
  });
742
829
  console.log("[IMA] Ads manager event listeners attached");
@@ -785,7 +872,17 @@ function createImaController(video, options) {
785
872
  adsLoader.addEventListener(
786
873
  google.ima.AdErrorEvent.Type.AD_ERROR,
787
874
  (adErrorEvent) => {
788
- console.error("[IMA] Ads loader error:", adErrorEvent.getError());
875
+ var _a, _b, _c, _d;
876
+ const error = adErrorEvent.getError();
877
+ console.error("[IMA] \u274C ADS_LOADER ERROR - Ad request failed!", {
878
+ message: (_a = error.getMessage) == null ? void 0 : _a.call(error),
879
+ errorCode: (_b = error.getErrorCode) == null ? void 0 : _b.call(error),
880
+ type: (_c = error.getType) == null ? void 0 : _c.call(error),
881
+ vastErrorCode: (_d = error.getVastErrorCode) == null ? void 0 : _d.call(error)
882
+ });
883
+ console.error("[IMA] This means the ad server didn't return valid ads");
884
+ console.error("[IMA] Ad layer will NOT appear (no flicker)");
885
+ console.error("[IMA] Full error object:", adErrorEvent.getError());
789
886
  adPlaying = false;
790
887
  setAdPlayingFlag(false);
791
888
  if (adContainerEl) {
@@ -817,8 +914,10 @@ function createImaController(video, options) {
817
914
  false
818
915
  );
819
916
  }
820
- console.log("[IMA] Making ads request");
917
+ console.log("[IMA] \u{1F680} Making ads request to IMA SDK");
918
+ console.log("[IMA] Waiting for IMA SDK response (LOADED or ERROR event)...");
821
919
  makeAdsRequest(google, vastTagUrl);
920
+ console.log("[IMA] \u23F3 Returning promise that will resolve when ads are loaded");
822
921
  return adsLoadedPromise;
823
922
  } catch (error) {
824
923
  console.error("[IMA] Failed to request ads:", error);
@@ -856,20 +955,23 @@ function createImaController(video, options) {
856
955
  },
857
956
  async play() {
858
957
  var _a, _b;
958
+ console.log("[IMA] \u25B6\uFE0F === play() called ===");
959
+ console.log("[IMA] This initializes and starts the ad");
960
+ console.log("[IMA] Ad layer will appear when CONTENT_PAUSE_REQUESTED fires");
859
961
  if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
860
962
  console.warn(
861
- "[IMA] Cannot play ad: IMA SDK or ad container not available"
963
+ "[IMA] \u274C Cannot play ad: IMA SDK or ad container not available"
862
964
  );
863
965
  return Promise.reject(new Error("IMA SDK not available"));
864
966
  }
865
967
  if (!adsManager) {
866
- console.warn("[IMA] Cannot play ad: No ads manager available");
968
+ console.warn("[IMA] \u274C Cannot play ad: No ads manager available");
867
969
  return Promise.reject(new Error("No ads manager"));
868
970
  }
869
971
  try {
870
972
  const width = video.clientWidth || 640;
871
973
  const height = video.clientHeight || 360;
872
- console.log(`[IMA] Initializing ads manager (${width}x${height})`);
974
+ console.log(`[IMA] \u{1F3AC} Initializing ads manager (${width}x${height})`);
873
975
  adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
874
976
  adPlaying = true;
875
977
  const adVolume = originalMutedState ? 0 : originalVolume;
@@ -877,7 +979,7 @@ function createImaController(video, options) {
877
979
  adVideoElement.volume = adVolume;
878
980
  adVideoElement.muted = originalMutedState;
879
981
  console.log(
880
- `[IMA] Set dedicated ad video volume to ${adVolume}, muted: ${originalMutedState}`
982
+ `[IMA] \u{1F50A} Set dedicated ad video volume to ${adVolume}, muted: ${originalMutedState}`
881
983
  );
882
984
  }
883
985
  try {
@@ -886,11 +988,13 @@ function createImaController(video, options) {
886
988
  } catch (error) {
887
989
  console.warn("[IMA] Failed to set IMA manager volume:", error);
888
990
  }
889
- console.log("[IMA] Starting ad playback");
991
+ console.log("[IMA] \u{1F3AF} Calling adsManager.start()");
992
+ console.log("[IMA] If successful, IMA will fire CONTENT_PAUSE_REQUESTED");
890
993
  adsManager.start();
994
+ console.log("[IMA] \u2705 play() completed successfully");
891
995
  return Promise.resolve();
892
996
  } catch (error) {
893
- console.error("[IMA] Error starting ad playback:", error);
997
+ console.error("[IMA] \u274C Error starting ad playback:", error);
894
998
  adPlaying = false;
895
999
  setAdPlayingFlag(false);
896
1000
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -2215,6 +2319,9 @@ var StormcloudVideoPlayer = class {
2215
2319
  this.hasInitialBufferCompleted = false;
2216
2320
  this.adPodAllUrls = [];
2217
2321
  this.preloadingAdUrls = /* @__PURE__ */ new Set();
2322
+ this.vastToMediaUrlMap = /* @__PURE__ */ new Map();
2323
+ this.preloadedMediaUrls = /* @__PURE__ */ new Set();
2324
+ this.preloadingMediaUrls = /* @__PURE__ */ new Set();
2218
2325
  initializePolyfills();
2219
2326
  const browserOverrides = getBrowserConfigOverrides();
2220
2327
  this.config = { ...config, ...browserOverrides };
@@ -2487,27 +2594,44 @@ var StormcloudVideoPlayer = class {
2487
2594
  });
2488
2595
  this.ima.on("ad_error", () => {
2489
2596
  if (this.config.debugAdTiming) {
2490
- console.log("[StormcloudVideoPlayer] IMA ad_error event received");
2597
+ console.log("[StormcloudVideoPlayer] IMA ad_error event received", {
2598
+ showAds: this.showAds,
2599
+ inAdBreak: this.inAdBreak,
2600
+ remainingAds: this.adPodQueue.length
2601
+ });
2491
2602
  }
2492
- if (this.showAds) {
2493
- if (this.inAdBreak) {
2494
- const remaining = this.getRemainingAdMs();
2495
- if (remaining > 500 && this.adPodQueue.length > 0) {
2496
- const next = this.adPodQueue.shift();
2603
+ if (this.inAdBreak) {
2604
+ const remaining = this.getRemainingAdMs();
2605
+ if (remaining > 500 && this.adPodQueue.length > 0) {
2606
+ const nextPreloaded = this.findNextPreloadedAd();
2607
+ if (nextPreloaded) {
2497
2608
  this.currentAdIndex++;
2498
- this.playSingleAd(next).catch(() => {
2609
+ if (this.config.debugAdTiming) {
2610
+ console.log(
2611
+ `[StormcloudVideoPlayer] Skipping to next preloaded ad after error`
2612
+ );
2613
+ }
2614
+ this.playSingleAd(nextPreloaded).catch(() => {
2615
+ this.handleAdFailure();
2499
2616
  });
2500
2617
  } else {
2618
+ if (this.config.debugAdTiming) {
2619
+ console.log(
2620
+ "[StormcloudVideoPlayer] No preloaded ads available, ending ad break"
2621
+ );
2622
+ }
2501
2623
  this.handleAdFailure();
2502
2624
  }
2503
2625
  } else {
2504
- if (this.config.debugAdTiming) {
2505
- console.log(
2506
- "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2507
- );
2508
- }
2509
2626
  this.handleAdFailure();
2510
2627
  }
2628
+ } else {
2629
+ if (this.config.debugAdTiming) {
2630
+ console.log(
2631
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2632
+ );
2633
+ }
2634
+ this.handleAdFailure();
2511
2635
  }
2512
2636
  });
2513
2637
  this.ima.on("content_pause", () => {
@@ -2538,22 +2662,30 @@ var StormcloudVideoPlayer = class {
2538
2662
  }
2539
2663
  const remaining = this.getRemainingAdMs();
2540
2664
  if (remaining > 500 && this.adPodQueue.length > 0) {
2541
- const next = this.adPodQueue.shift();
2542
- this.currentAdIndex++;
2543
- this.enforceAdHoldState();
2544
- if (this.config.debugAdTiming) {
2545
- console.log(
2546
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
2547
- );
2548
- }
2549
- this.playSingleAd(next).catch(() => {
2665
+ const nextPreloaded = this.findNextPreloadedAd();
2666
+ if (nextPreloaded) {
2667
+ this.currentAdIndex++;
2550
2668
  if (this.config.debugAdTiming) {
2551
- console.error(
2552
- "[StormcloudVideoPlayer] Failed to play next ad in pod"
2669
+ console.log(
2670
+ `[StormcloudVideoPlayer] Playing next preloaded ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - layer will stay visible if ad loads`
2671
+ );
2672
+ }
2673
+ this.playSingleAd(nextPreloaded).catch(() => {
2674
+ if (this.config.debugAdTiming) {
2675
+ console.error(
2676
+ "[StormcloudVideoPlayer] Failed to play next ad in pod"
2677
+ );
2678
+ }
2679
+ this.handleAdPodComplete();
2680
+ });
2681
+ } else {
2682
+ if (this.config.debugAdTiming) {
2683
+ console.log(
2684
+ "[StormcloudVideoPlayer] No preloaded ads available - completing ad break"
2553
2685
  );
2554
2686
  }
2555
2687
  this.handleAdPodComplete();
2556
- });
2688
+ }
2557
2689
  } else {
2558
2690
  if (this.config.debugAdTiming) {
2559
2691
  console.log(
@@ -2798,6 +2930,9 @@ var StormcloudVideoPlayer = class {
2798
2930
  const first = tags[0];
2799
2931
  const rest = tags.slice(1);
2800
2932
  this.adPodQueue = rest;
2933
+ if (!this.showAds) {
2934
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
2935
+ }
2801
2936
  this.playSingleAd(first).catch(() => {
2802
2937
  });
2803
2938
  }
@@ -3154,19 +3289,37 @@ var StormcloudVideoPlayer = class {
3154
3289
  if (vastTagUrls.length > 0) {
3155
3290
  this.adPodAllUrls = [...vastTagUrls];
3156
3291
  this.preloadingAdUrls.clear();
3292
+ this.vastToMediaUrlMap.clear();
3293
+ this.preloadedMediaUrls.clear();
3294
+ this.preloadingMediaUrls.clear();
3157
3295
  this.logQueuedAdUrls(this.adPodAllUrls);
3296
+ if (this.config.debugAdTiming) {
3297
+ console.log(
3298
+ `[StormcloudVideoPlayer] Capturing original audio state before ad break:`,
3299
+ {
3300
+ videoMuted: this.video.muted,
3301
+ videoVolume: this.video.volume
3302
+ }
3303
+ );
3304
+ }
3305
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3158
3306
  this.inAdBreak = true;
3159
- this.showAds = true;
3160
3307
  this.currentAdIndex = 0;
3161
3308
  this.totalAdsInBreak = vastTagUrls.length;
3162
3309
  this.adPodQueue = [...vastTagUrls];
3163
- this.enforceAdHoldState();
3164
- this.preloadUpcomingAds();
3165
3310
  if (this.config.debugAdTiming) {
3166
3311
  console.log(
3167
- `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
3312
+ `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - preloading all ads in parallel (ad layer will show after ad loads)`
3168
3313
  );
3169
3314
  }
3315
+ this.preloadAllAdsInBackground().catch((error) => {
3316
+ if (this.config.debugAdTiming) {
3317
+ console.warn(
3318
+ "[StormcloudVideoPlayer] Error in background preloading:",
3319
+ error
3320
+ );
3321
+ }
3322
+ });
3170
3323
  try {
3171
3324
  await this.playAdPod();
3172
3325
  } catch (error) {
@@ -3192,14 +3345,28 @@ var StormcloudVideoPlayer = class {
3192
3345
  }
3193
3346
  return;
3194
3347
  }
3195
- const firstAd = this.adPodQueue.shift();
3348
+ await new Promise((resolve) => setTimeout(resolve, 500));
3349
+ const firstPreloaded = this.findNextPreloadedAd();
3350
+ if (!firstPreloaded) {
3351
+ if (this.config.debugAdTiming) {
3352
+ console.log(
3353
+ "[StormcloudVideoPlayer] No preloaded ads available after waiting, trying first ad anyway"
3354
+ );
3355
+ }
3356
+ const firstAd = this.adPodQueue.shift();
3357
+ if (firstAd) {
3358
+ this.currentAdIndex++;
3359
+ await this.playSingleAd(firstAd);
3360
+ }
3361
+ return;
3362
+ }
3196
3363
  this.currentAdIndex++;
3197
3364
  if (this.config.debugAdTiming) {
3198
3365
  console.log(
3199
- `[StormcloudVideoPlayer] Playing ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3366
+ `[StormcloudVideoPlayer] Playing first preloaded ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3200
3367
  );
3201
3368
  }
3202
- await this.playSingleAd(firstAd);
3369
+ await this.playSingleAd(firstPreloaded);
3203
3370
  }
3204
3371
  findCurrentOrNextBreak(nowMs) {
3205
3372
  var _a;
@@ -3232,7 +3399,7 @@ var StormcloudVideoPlayer = class {
3232
3399
  const first = tags[0];
3233
3400
  const rest = tags.slice(1);
3234
3401
  this.adPodQueue = rest;
3235
- this.enforceAdHoldState();
3402
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3236
3403
  await this.playSingleAd(first);
3237
3404
  this.inAdBreak = true;
3238
3405
  this.expectedAdBreakDurationMs = remainingMs;
@@ -3352,38 +3519,20 @@ var StormcloudVideoPlayer = class {
3352
3519
  `[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
3353
3520
  );
3354
3521
  }
3355
- if (!this.showAds) {
3356
- if (this.config.debugAdTiming) {
3357
- console.log(
3358
- `[StormcloudVideoPlayer] Capturing original state before ad request:`,
3359
- {
3360
- videoMuted: this.video.muted,
3361
- videoVolume: this.video.volume,
3362
- showAds: this.showAds
3363
- }
3364
- );
3365
- }
3366
- this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3367
- } else if (this.config.debugAdTiming) {
3368
- console.log(
3369
- `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
3370
- );
3371
- }
3372
3522
  this.startAdFailsafeTimer();
3373
3523
  try {
3374
3524
  await this.ima.requestAds(vastTagUrl);
3375
- this.preloadUpcomingAds();
3376
3525
  try {
3377
3526
  if (this.config.debugAdTiming) {
3378
3527
  console.log(
3379
- "[StormcloudVideoPlayer] Ad request completed, attempting playback"
3528
+ "[StormcloudVideoPlayer] Ad request completed, attempting playback (ad layer will show when IMA triggers content pause)"
3380
3529
  );
3381
3530
  }
3382
- this.enforceAdHoldState();
3383
3531
  await this.ima.play();
3532
+ this.showAds = true;
3384
3533
  if (this.config.debugAdTiming) {
3385
3534
  console.log(
3386
- "[StormcloudVideoPlayer] Ad playback started successfully"
3535
+ "[StormcloudVideoPlayer] Ad playback started successfully, showAds = true"
3387
3536
  );
3388
3537
  }
3389
3538
  } catch (playError) {
@@ -3411,6 +3560,9 @@ var StormcloudVideoPlayer = class {
3411
3560
  }
3412
3561
  this.releaseAdHoldState();
3413
3562
  this.preloadingAdUrls.clear();
3563
+ this.vastToMediaUrlMap.clear();
3564
+ this.preloadedMediaUrls.clear();
3565
+ this.preloadingMediaUrls.clear();
3414
3566
  this.inAdBreak = false;
3415
3567
  this.expectedAdBreakDurationMs = void 0;
3416
3568
  this.currentAdBreakStartWallClockMs = void 0;
@@ -3520,44 +3672,204 @@ var StormcloudVideoPlayer = class {
3520
3672
  this.ima.hidePlaceholder();
3521
3673
  }
3522
3674
  }
3523
- preloadUpcomingAds() {
3524
- if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
3525
- return;
3675
+ async fetchAndParseVastXml(vastTagUrl) {
3676
+ try {
3677
+ const response = await fetch(vastTagUrl, { mode: "cors" });
3678
+ if (!response.ok) {
3679
+ throw new Error(`Failed to fetch VAST: ${response.status}`);
3680
+ }
3681
+ const xmlText = await response.text();
3682
+ return this.extractMediaUrlsFromVast(xmlText);
3683
+ } catch (error) {
3684
+ if (this.config.debugAdTiming) {
3685
+ console.warn(
3686
+ `[StormcloudVideoPlayer] Failed to fetch/parse VAST XML: ${vastTagUrl}`,
3687
+ error
3688
+ );
3689
+ }
3690
+ return [];
3526
3691
  }
3527
- const upcoming = this.adPodQueue.slice(0, 2);
3528
- for (const url of upcoming) {
3529
- if (!url) continue;
3530
- if (this.ima.hasPreloadedAd(url)) {
3531
- this.preloadingAdUrls.delete(url);
3532
- continue;
3692
+ }
3693
+ extractMediaUrlsFromVast(xmlText) {
3694
+ var _a;
3695
+ const mediaUrls = [];
3696
+ try {
3697
+ const parser = new DOMParser();
3698
+ const xmlDoc = parser.parseFromString(xmlText, "text/xml");
3699
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
3700
+ for (const mediaFile of Array.from(mediaFileElements)) {
3701
+ const url = (_a = mediaFile.textContent) == null ? void 0 : _a.trim();
3702
+ if (url) {
3703
+ const lowerUrl = url.toLowerCase();
3704
+ if (lowerUrl.endsWith(".mp4") || lowerUrl.endsWith(".webm") || lowerUrl.endsWith(".mov") || lowerUrl.endsWith(".avi") || lowerUrl.includes(".mp4?") || lowerUrl.includes(".webm?") || lowerUrl.includes("/mp4/") || lowerUrl.includes("type=video")) {
3705
+ mediaUrls.push(url);
3706
+ }
3707
+ }
3533
3708
  }
3534
- if (this.preloadingAdUrls.has(url)) {
3535
- continue;
3709
+ if (this.config.debugAdTiming && mediaUrls.length > 0) {
3710
+ console.log(
3711
+ `[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs from VAST:`,
3712
+ mediaUrls
3713
+ );
3536
3714
  }
3715
+ } catch (error) {
3716
+ if (this.config.debugAdTiming) {
3717
+ console.warn(
3718
+ "[StormcloudVideoPlayer] Failed to parse VAST XML:",
3719
+ error
3720
+ );
3721
+ }
3722
+ }
3723
+ return mediaUrls;
3724
+ }
3725
+ async preloadMediaFile(mediaUrl) {
3726
+ if (this.preloadedMediaUrls.has(mediaUrl)) {
3727
+ return;
3728
+ }
3729
+ if (this.preloadingMediaUrls.has(mediaUrl)) {
3730
+ return;
3731
+ }
3732
+ this.preloadingMediaUrls.add(mediaUrl);
3733
+ try {
3537
3734
  if (this.config.debugAdTiming) {
3538
3735
  console.log(
3539
- `[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
3736
+ `[StormcloudVideoPlayer] Preloading video file: ${mediaUrl}`
3540
3737
  );
3541
3738
  }
3542
- this.preloadingAdUrls.add(url);
3543
- this.ima.preloadAds(url).then(() => {
3739
+ const response = await fetch(mediaUrl, {
3740
+ mode: "cors",
3741
+ method: "GET",
3742
+ headers: {
3743
+ Range: "bytes=0-1048576"
3744
+ }
3745
+ });
3746
+ if (response.ok || response.status === 206) {
3747
+ this.preloadedMediaUrls.add(mediaUrl);
3544
3748
  if (this.config.debugAdTiming) {
3545
3749
  console.log(
3546
- `[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
3750
+ `[StormcloudVideoPlayer] Successfully preloaded video file: ${mediaUrl}`
3547
3751
  );
3548
3752
  }
3549
- }).catch((error) => {
3753
+ }
3754
+ } catch (error) {
3755
+ if (this.config.debugAdTiming) {
3756
+ console.warn(
3757
+ `[StormcloudVideoPlayer] Failed to preload video file: ${mediaUrl}`,
3758
+ error
3759
+ );
3760
+ }
3761
+ } finally {
3762
+ this.preloadingMediaUrls.delete(mediaUrl);
3763
+ }
3764
+ }
3765
+ async preloadAllAdsInBackground() {
3766
+ if (this.adPodAllUrls.length === 0) {
3767
+ return;
3768
+ }
3769
+ if (this.config.debugAdTiming) {
3770
+ console.log(
3771
+ `[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
3772
+ );
3773
+ }
3774
+ const preloadPromises = this.adPodAllUrls.map(
3775
+ (vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
3550
3776
  if (this.config.debugAdTiming) {
3551
3777
  console.warn(
3552
- `[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
3778
+ `[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
3553
3779
  error
3554
3780
  );
3555
3781
  }
3556
- }).finally(() => {
3557
- this.preloadingAdUrls.delete(url);
3558
- });
3782
+ })
3783
+ );
3784
+ await Promise.all(preloadPromises);
3785
+ if (this.config.debugAdTiming) {
3786
+ console.log(
3787
+ `[StormcloudVideoPlayer] Background preloading completed for all ads`
3788
+ );
3559
3789
  }
3560
3790
  }
3791
+ async preloadSingleAd(vastTagUrl) {
3792
+ if (!vastTagUrl) return;
3793
+ try {
3794
+ if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
3795
+ if (!this.preloadingAdUrls.has(vastTagUrl)) {
3796
+ if (this.config.debugAdTiming) {
3797
+ console.log(
3798
+ `[StormcloudVideoPlayer] Preloading VAST: ${vastTagUrl}`
3799
+ );
3800
+ }
3801
+ this.preloadingAdUrls.add(vastTagUrl);
3802
+ await this.ima.preloadAds(vastTagUrl).then(() => {
3803
+ if (this.config.debugAdTiming) {
3804
+ console.log(
3805
+ `[StormcloudVideoPlayer] IMA VAST preload complete: ${vastTagUrl}`
3806
+ );
3807
+ }
3808
+ }).catch((error) => {
3809
+ if (this.config.debugAdTiming) {
3810
+ console.warn(
3811
+ `[StormcloudVideoPlayer] IMA VAST preload failed: ${vastTagUrl}`,
3812
+ error
3813
+ );
3814
+ }
3815
+ }).finally(() => {
3816
+ this.preloadingAdUrls.delete(vastTagUrl);
3817
+ });
3818
+ }
3819
+ }
3820
+ let mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3821
+ if (!mediaUrls) {
3822
+ if (this.config.debugAdTiming) {
3823
+ console.log(
3824
+ `[StormcloudVideoPlayer] Fetching and parsing VAST to extract media URLs: ${vastTagUrl}`
3825
+ );
3826
+ }
3827
+ mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
3828
+ if (mediaUrls.length > 0) {
3829
+ this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
3830
+ }
3831
+ }
3832
+ if (mediaUrls && mediaUrls.length > 0) {
3833
+ const primaryMediaUrl = mediaUrls[0];
3834
+ if (primaryMediaUrl && !this.preloadedMediaUrls.has(primaryMediaUrl)) {
3835
+ await this.preloadMediaFile(primaryMediaUrl);
3836
+ }
3837
+ }
3838
+ } catch (error) {
3839
+ if (this.config.debugAdTiming) {
3840
+ console.warn(
3841
+ `[StormcloudVideoPlayer] Failed to preload ad: ${vastTagUrl}`,
3842
+ error
3843
+ );
3844
+ }
3845
+ }
3846
+ }
3847
+ findNextPreloadedAd() {
3848
+ var _a, _b, _c;
3849
+ for (let i = 0; i < this.adPodQueue.length; i++) {
3850
+ const vastTagUrl = this.adPodQueue[i];
3851
+ if (!vastTagUrl) continue;
3852
+ const hasImaPreload = (_c = (_b = (_a = this.ima).hasPreloadedAd) == null ? void 0 : _b.call(_a, vastTagUrl)) != null ? _c : false;
3853
+ const mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3854
+ const hasMediaPreload = mediaUrls && mediaUrls.length > 0 ? this.preloadedMediaUrls.has(mediaUrls[0]) : false;
3855
+ if (hasImaPreload || hasMediaPreload) {
3856
+ if (this.config.debugAdTiming) {
3857
+ console.log(
3858
+ `[StormcloudVideoPlayer] Found preloaded ad at index ${i}: ${vastTagUrl}`,
3859
+ { hasImaPreload, hasMediaPreload }
3860
+ );
3861
+ }
3862
+ this.adPodQueue.splice(0, i + 1);
3863
+ return vastTagUrl;
3864
+ }
3865
+ }
3866
+ if (this.config.debugAdTiming) {
3867
+ console.log(
3868
+ "[StormcloudVideoPlayer] No preloaded ads found in queue"
3869
+ );
3870
+ }
3871
+ return void 0;
3872
+ }
3561
3873
  getRemainingAdMs() {
3562
3874
  if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
3563
3875
  return 0;
@@ -3726,6 +4038,9 @@ var StormcloudVideoPlayer = class {
3726
4038
  (_b = this.ima) == null ? void 0 : _b.destroy();
3727
4039
  this.releaseAdHoldState();
3728
4040
  this.preloadingAdUrls.clear();
4041
+ this.vastToMediaUrlMap.clear();
4042
+ this.preloadedMediaUrls.clear();
4043
+ this.preloadingMediaUrls.clear();
3729
4044
  this.adPodAllUrls = [];
3730
4045
  }
3731
4046
  };