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.
package/lib/index.js CHANGED
@@ -346,15 +346,17 @@ function createImaController(video, options) {
346
346
  let adsLoadedResolve;
347
347
  let adsLoadedReject;
348
348
  function makeAdsRequest(google, vastTagUrl) {
349
+ console.log("[IMA] \u{1F4CB} === makeAdsRequest() - Building IMA request ===");
349
350
  const adsRequest = new google.ima.AdsRequest();
350
351
  const preloadedResponse = preloadedVast.get(vastTagUrl);
351
352
  if (preloadedResponse) {
352
353
  adsRequest.adsResponse = preloadedResponse;
353
354
  console.log(
354
- "[IMA] Using preloaded VAST response for immediate ad request"
355
+ "[IMA] \u26A1 Using preloaded VAST response for immediate ad request"
355
356
  );
356
357
  } else {
357
358
  adsRequest.adTagUrl = vastTagUrl;
359
+ console.log("[IMA] \u{1F310} Will fetch VAST from URL:", vastTagUrl);
358
360
  }
359
361
  const videoWidth = video.offsetWidth || video.clientWidth || 640;
360
362
  const videoHeight = video.offsetHeight || video.clientHeight || 360;
@@ -366,6 +368,7 @@ function createImaController(video, options) {
366
368
  try {
367
369
  const willAutoPlay = !video.paused || video.autoplay;
368
370
  adsRequest.setAdWillAutoPlay(willAutoPlay);
371
+ console.log(`[IMA] Ad will autoplay: ${willAutoPlay}`);
369
372
  } catch (error) {
370
373
  console.warn("[IMA] Failed to call setAdWillAutoPlay:", error);
371
374
  }
@@ -374,13 +377,17 @@ function createImaController(video, options) {
374
377
  try {
375
378
  const willPlayMuted = video.muted || video.volume === 0;
376
379
  adsRequest.setAdWillPlayMuted(willPlayMuted);
380
+ console.log(`[IMA] Ad will play muted: ${willPlayMuted}`);
377
381
  } catch (error) {
378
382
  console.warn("[IMA] Failed to call setAdWillPlayMuted:", error);
379
383
  }
380
384
  }
381
385
  adsRequest.vastLoadTimeout = 5e3;
382
- console.log(`[IMA] Ads request dimensions: ${videoWidth}x${videoHeight}`);
386
+ console.log(`[IMA] \u{1F4D0} Ads request dimensions: ${videoWidth}x${videoHeight}`);
387
+ console.log("[IMA] \u23F1\uFE0F VAST load timeout: 5000ms");
388
+ console.log("[IMA] \u{1F680} Calling adsLoader.requestAds()...");
383
389
  adsLoader.requestAds(adsRequest);
390
+ console.log("[IMA] \u23F3 Waiting for ADS_MANAGER_LOADED or AD_ERROR event...");
384
391
  if (preloadedResponse) {
385
392
  preloadedVast.delete(vastTagUrl);
386
393
  }
@@ -458,22 +465,24 @@ function createImaController(video, options) {
458
465
  });
459
466
  },
460
467
  async requestAds(vastTagUrl) {
461
- console.log("[IMA] Requesting ads:", vastTagUrl);
468
+ console.log("[IMA] \u{1F4E1} === requestAds() called ===");
469
+ console.log("[IMA] VAST URL:", vastTagUrl);
470
+ console.log("[IMA] This will fetch the ad from the server - no visual change yet");
462
471
  if (!vastTagUrl || vastTagUrl.trim() === "") {
463
472
  const error = new Error("VAST tag URL is empty or undefined");
464
- console.warn("[IMA]", error.message);
473
+ console.warn("[IMA] \u274C", error.message);
465
474
  return Promise.reject(error);
466
475
  }
467
476
  try {
468
477
  new URL(vastTagUrl);
469
478
  } catch (e) {
470
479
  const error = new Error(`Invalid VAST tag URL format: ${vastTagUrl}`);
471
- console.warn("[IMA]", error.message);
480
+ console.warn("[IMA] \u274C", error.message);
472
481
  return Promise.reject(error);
473
482
  }
474
483
  if (adPlaying) {
475
484
  console.warn(
476
- "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
485
+ "[IMA] \u26A0\uFE0F Cannot request new ads while an ad is playing. Call stop() first."
477
486
  );
478
487
  return Promise.reject(
479
488
  new Error("Ad already playing - cannot request new ads")
@@ -563,20 +572,85 @@ function createImaController(video, options) {
563
572
  adsLoader.addEventListener(
564
573
  google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
565
574
  (evt) => {
566
- console.log(
567
- "[IMA] Ads manager loaded - enabling preloading for continuous playback"
568
- );
575
+ console.log("[IMA] \u2705 ADS_MANAGER_LOADED - Ads fetched successfully!");
576
+ console.log("[IMA] Setting up ads manager with preloading enabled");
577
+ console.log("[IMA] ========================================");
578
+ console.log("[IMA] EXPECTED EVENT FLOW:");
579
+ console.log("[IMA] 1. requestAds() \u2192 fetch VAST");
580
+ console.log("[IMA] 2. ADS_MANAGER_LOADED \u2192 ads ready");
581
+ console.log("[IMA] 3. play() \u2192 start playback");
582
+ console.log("[IMA] 4. CONTENT_PAUSE_REQUESTED \u2192 show ad layer \u2728");
583
+ console.log("[IMA] 5. STARTED \u2192 ad is playing");
584
+ console.log("[IMA] 6. CONTENT_RESUME_REQUESTED \u2192 ad done");
585
+ console.log("[IMA] 7. ALL_ADS_COMPLETED \u2192 hide ad layer");
586
+ console.log("[IMA] ========================================");
569
587
  try {
570
588
  const adsRenderingSettings = new google.ima.AdsRenderingSettings();
571
589
  adsRenderingSettings.enablePreloading = true;
572
590
  adsManager = evt.getAdsManager(video, adsRenderingSettings);
573
591
  const AdEvent = google.ima.AdEvent.Type;
574
592
  const AdErrorEvent = google.ima.AdErrorEvent.Type;
593
+ console.log("[IMA] ========== IMA EVENT LOGGING ENABLED ==========");
594
+ console.log("[IMA] All IMA SDK events will be logged below");
595
+ const allAdEvents = [
596
+ "AD_BREAK_READY",
597
+ "AD_METADATA",
598
+ "ALL_ADS_COMPLETED",
599
+ "CLICK",
600
+ "COMPLETE",
601
+ "CONTENT_PAUSE_REQUESTED",
602
+ "CONTENT_RESUME_REQUESTED",
603
+ "DURATION_CHANGE",
604
+ "FIRST_QUARTILE",
605
+ "IMPRESSION",
606
+ "INTERACTION",
607
+ "LINEAR_CHANGED",
608
+ "LOADED",
609
+ "LOG",
610
+ "MIDPOINT",
611
+ "PAUSED",
612
+ "RESUMED",
613
+ "SKIPPABLE_STATE_CHANGED",
614
+ "SKIPPED",
615
+ "STARTED",
616
+ "THIRD_QUARTILE",
617
+ "USER_CLOSE",
618
+ "VOLUME_CHANGED",
619
+ "VOLUME_MUTED"
620
+ ];
621
+ allAdEvents.forEach((eventType) => {
622
+ if (AdEvent[eventType]) {
623
+ adsManager.addEventListener(AdEvent[eventType], (e) => {
624
+ var _a, _b, _c, _d, _e, _f;
625
+ const ad = (_a = e.getAd) == null ? void 0 : _a.call(e);
626
+ const adData = ad ? {
627
+ adId: (_b = ad.getAdId) == null ? void 0 : _b.call(ad),
628
+ title: (_c = ad.getTitle) == null ? void 0 : _c.call(ad),
629
+ duration: (_d = ad.getDuration) == null ? void 0 : _d.call(ad),
630
+ isLinear: (_e = ad.isLinear) == null ? void 0 : _e.call(ad),
631
+ contentType: (_f = ad.getContentType) == null ? void 0 : _f.call(ad)
632
+ } : null;
633
+ console.log(`[IMA EVENT] ${eventType}`, {
634
+ eventType,
635
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
636
+ adData
637
+ });
638
+ });
639
+ }
640
+ });
641
+ console.log("[IMA] ========== EVENT LISTENERS ATTACHED ==========");
575
642
  adsManager.addEventListener(
576
643
  AdErrorEvent.AD_ERROR,
577
644
  (errorEvent) => {
578
- var _a;
579
- console.error("[IMA] Ad error:", errorEvent.getError());
645
+ var _a, _b, _c, _d, _e, _f;
646
+ const error = errorEvent.getError();
647
+ console.error("[IMA] \u274C AD_ERROR Event:", {
648
+ message: (_a = error.getMessage) == null ? void 0 : _a.call(error),
649
+ errorCode: (_b = error.getErrorCode) == null ? void 0 : _b.call(error),
650
+ type: (_c = error.getType) == null ? void 0 : _c.call(error),
651
+ vastErrorCode: (_d = error.getVastErrorCode) == null ? void 0 : _d.call(error),
652
+ innerError: (_e = error.getInnerError) == null ? void 0 : _e.call(error)
653
+ });
580
654
  destroyAdsManager();
581
655
  adPlaying = false;
582
656
  setAdPlayingFlag(false);
@@ -618,7 +692,7 @@ function createImaController(video, options) {
618
692
  console.log(
619
693
  "[IMA] Resuming paused video after ad error"
620
694
  );
621
- (_a = video.play()) == null ? void 0 : _a.catch(() => {
695
+ (_f = video.play()) == null ? void 0 : _f.catch(() => {
622
696
  });
623
697
  }
624
698
  }
@@ -628,7 +702,8 @@ function createImaController(video, options) {
628
702
  adsManager.addEventListener(
629
703
  AdEvent.CONTENT_PAUSE_REQUESTED,
630
704
  () => {
631
- console.log("[IMA] Content pause requested");
705
+ console.log("[IMA] \u2705 CONTENT_PAUSE_REQUESTED - Ad is ready to play!");
706
+ console.log("[IMA] This is the event that triggers the ad layer to appear");
632
707
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
633
708
  video.pause();
634
709
  console.log("[IMA] Content video paused (VOD mode)");
@@ -637,13 +712,23 @@ function createImaController(video, options) {
637
712
  "[IMA] Content video continues in background (Live mode)"
638
713
  );
639
714
  }
715
+ hideContentVideo();
716
+ if (adContainerEl) {
717
+ adContainerEl.style.pointerEvents = "auto";
718
+ adContainerEl.style.display = "flex";
719
+ adContainerEl.style.backgroundColor = "#000";
720
+ adContainerEl.offsetHeight;
721
+ adContainerEl.style.opacity = "1";
722
+ console.log("[IMA] \u2728 Ad container NOW VISIBLE (after content pause)");
723
+ }
640
724
  adPlaying = true;
641
725
  setAdPlayingFlag(true);
726
+ console.log("[IMA] Emitting 'content_pause' event to player");
642
727
  emit("content_pause");
643
728
  }
644
729
  );
645
730
  adsManager.addEventListener(AdEvent.STARTED, () => {
646
- console.log("[IMA] Ad started - showing ad video");
731
+ console.log("[IMA] \u25B6\uFE0F STARTED - Ad playback has begun");
647
732
  setAdPlayingFlag(true);
648
733
  hideContentVideo();
649
734
  if (adVideoElement) {
@@ -659,20 +744,21 @@ function createImaController(video, options) {
659
744
  adContainerEl.style.backgroundColor = "#000";
660
745
  adContainerEl.offsetHeight;
661
746
  adContainerEl.style.opacity = "1";
662
- console.log("[IMA] Ad container now visible");
747
+ console.log("[IMA] Ad container now visible (STARTED event)");
663
748
  }
664
749
  });
665
750
  adsManager.addEventListener(
666
751
  AdEvent.CONTENT_RESUME_REQUESTED,
667
752
  () => {
668
- console.log("[IMA] Content resume requested");
753
+ console.log("[IMA] \u23F8\uFE0F CONTENT_RESUME_REQUESTED - Single ad completed");
669
754
  adPlaying = false;
670
755
  setAdPlayingFlag(false);
756
+ console.log("[IMA] Emitting 'content_resume' event to player");
671
757
  emit("content_resume");
672
758
  }
673
759
  );
674
760
  adsManager.addEventListener(AdEvent.ALL_ADS_COMPLETED, () => {
675
- console.log("[IMA] All ads completed - restoring content");
761
+ console.log("[IMA] \u{1F3C1} ALL_ADS_COMPLETED - All ads in break finished");
676
762
  adPlaying = false;
677
763
  setAdPlayingFlag(false);
678
764
  if (adContainerEl) {
@@ -682,7 +768,7 @@ function createImaController(video, options) {
682
768
  if (adContainerEl) {
683
769
  adContainerEl.style.pointerEvents = "none";
684
770
  adContainerEl.style.display = "none";
685
- console.log("[IMA] Ad container hidden");
771
+ console.log("[IMA] \u{1F648} Ad container hidden (ALL_ADS_COMPLETED)");
686
772
  }
687
773
  }, 300);
688
774
  }
@@ -693,6 +779,7 @@ function createImaController(video, options) {
693
779
  console.warn("[IMA] Failed to resume content video:", e);
694
780
  });
695
781
  }
782
+ console.log("[IMA] Emitting 'all_ads_completed' event to player");
696
783
  emit("all_ads_completed");
697
784
  });
698
785
  console.log("[IMA] Ads manager event listeners attached");
@@ -741,7 +828,17 @@ function createImaController(video, options) {
741
828
  adsLoader.addEventListener(
742
829
  google.ima.AdErrorEvent.Type.AD_ERROR,
743
830
  (adErrorEvent) => {
744
- console.error("[IMA] Ads loader error:", adErrorEvent.getError());
831
+ var _a, _b, _c, _d;
832
+ const error = adErrorEvent.getError();
833
+ console.error("[IMA] \u274C ADS_LOADER ERROR - Ad request failed!", {
834
+ message: (_a = error.getMessage) == null ? void 0 : _a.call(error),
835
+ errorCode: (_b = error.getErrorCode) == null ? void 0 : _b.call(error),
836
+ type: (_c = error.getType) == null ? void 0 : _c.call(error),
837
+ vastErrorCode: (_d = error.getVastErrorCode) == null ? void 0 : _d.call(error)
838
+ });
839
+ console.error("[IMA] This means the ad server didn't return valid ads");
840
+ console.error("[IMA] Ad layer will NOT appear (no flicker)");
841
+ console.error("[IMA] Full error object:", adErrorEvent.getError());
745
842
  adPlaying = false;
746
843
  setAdPlayingFlag(false);
747
844
  if (adContainerEl) {
@@ -773,8 +870,10 @@ function createImaController(video, options) {
773
870
  false
774
871
  );
775
872
  }
776
- console.log("[IMA] Making ads request");
873
+ console.log("[IMA] \u{1F680} Making ads request to IMA SDK");
874
+ console.log("[IMA] Waiting for IMA SDK response (LOADED or ERROR event)...");
777
875
  makeAdsRequest(google, vastTagUrl);
876
+ console.log("[IMA] \u23F3 Returning promise that will resolve when ads are loaded");
778
877
  return adsLoadedPromise;
779
878
  } catch (error) {
780
879
  console.error("[IMA] Failed to request ads:", error);
@@ -812,20 +911,23 @@ function createImaController(video, options) {
812
911
  },
813
912
  async play() {
814
913
  var _a, _b;
914
+ console.log("[IMA] \u25B6\uFE0F === play() called ===");
915
+ console.log("[IMA] This initializes and starts the ad");
916
+ console.log("[IMA] Ad layer will appear when CONTENT_PAUSE_REQUESTED fires");
815
917
  if (!((_a = window.google) == null ? void 0 : _a.ima) || !adDisplayContainer) {
816
918
  console.warn(
817
- "[IMA] Cannot play ad: IMA SDK or ad container not available"
919
+ "[IMA] \u274C Cannot play ad: IMA SDK or ad container not available"
818
920
  );
819
921
  return Promise.reject(new Error("IMA SDK not available"));
820
922
  }
821
923
  if (!adsManager) {
822
- console.warn("[IMA] Cannot play ad: No ads manager available");
924
+ console.warn("[IMA] \u274C Cannot play ad: No ads manager available");
823
925
  return Promise.reject(new Error("No ads manager"));
824
926
  }
825
927
  try {
826
928
  const width = video.clientWidth || 640;
827
929
  const height = video.clientHeight || 360;
828
- console.log(`[IMA] Initializing ads manager (${width}x${height})`);
930
+ console.log(`[IMA] \u{1F3AC} Initializing ads manager (${width}x${height})`);
829
931
  adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
830
932
  adPlaying = true;
831
933
  const adVolume = originalMutedState ? 0 : originalVolume;
@@ -833,7 +935,7 @@ function createImaController(video, options) {
833
935
  adVideoElement.volume = adVolume;
834
936
  adVideoElement.muted = originalMutedState;
835
937
  console.log(
836
- `[IMA] Set dedicated ad video volume to ${adVolume}, muted: ${originalMutedState}`
938
+ `[IMA] \u{1F50A} Set dedicated ad video volume to ${adVolume}, muted: ${originalMutedState}`
837
939
  );
838
940
  }
839
941
  try {
@@ -842,11 +944,13 @@ function createImaController(video, options) {
842
944
  } catch (error) {
843
945
  console.warn("[IMA] Failed to set IMA manager volume:", error);
844
946
  }
845
- console.log("[IMA] Starting ad playback");
947
+ console.log("[IMA] \u{1F3AF} Calling adsManager.start()");
948
+ console.log("[IMA] If successful, IMA will fire CONTENT_PAUSE_REQUESTED");
846
949
  adsManager.start();
950
+ console.log("[IMA] \u2705 play() completed successfully");
847
951
  return Promise.resolve();
848
952
  } catch (error) {
849
- console.error("[IMA] Error starting ad playback:", error);
953
+ console.error("[IMA] \u274C Error starting ad playback:", error);
850
954
  adPlaying = false;
851
955
  setAdPlayingFlag(false);
852
956
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -2171,6 +2275,9 @@ var StormcloudVideoPlayer = class {
2171
2275
  this.hasInitialBufferCompleted = false;
2172
2276
  this.adPodAllUrls = [];
2173
2277
  this.preloadingAdUrls = /* @__PURE__ */ new Set();
2278
+ this.vastToMediaUrlMap = /* @__PURE__ */ new Map();
2279
+ this.preloadedMediaUrls = /* @__PURE__ */ new Set();
2280
+ this.preloadingMediaUrls = /* @__PURE__ */ new Set();
2174
2281
  initializePolyfills();
2175
2282
  const browserOverrides = getBrowserConfigOverrides();
2176
2283
  this.config = { ...config, ...browserOverrides };
@@ -2443,27 +2550,44 @@ var StormcloudVideoPlayer = class {
2443
2550
  });
2444
2551
  this.ima.on("ad_error", () => {
2445
2552
  if (this.config.debugAdTiming) {
2446
- console.log("[StormcloudVideoPlayer] IMA ad_error event received");
2553
+ console.log("[StormcloudVideoPlayer] IMA ad_error event received", {
2554
+ showAds: this.showAds,
2555
+ inAdBreak: this.inAdBreak,
2556
+ remainingAds: this.adPodQueue.length
2557
+ });
2447
2558
  }
2448
- if (this.showAds) {
2449
- if (this.inAdBreak) {
2450
- const remaining = this.getRemainingAdMs();
2451
- if (remaining > 500 && this.adPodQueue.length > 0) {
2452
- const next = this.adPodQueue.shift();
2559
+ if (this.inAdBreak) {
2560
+ const remaining = this.getRemainingAdMs();
2561
+ if (remaining > 500 && this.adPodQueue.length > 0) {
2562
+ const nextPreloaded = this.findNextPreloadedAd();
2563
+ if (nextPreloaded) {
2453
2564
  this.currentAdIndex++;
2454
- this.playSingleAd(next).catch(() => {
2565
+ if (this.config.debugAdTiming) {
2566
+ console.log(
2567
+ `[StormcloudVideoPlayer] Skipping to next preloaded ad after error`
2568
+ );
2569
+ }
2570
+ this.playSingleAd(nextPreloaded).catch(() => {
2571
+ this.handleAdFailure();
2455
2572
  });
2456
2573
  } else {
2574
+ if (this.config.debugAdTiming) {
2575
+ console.log(
2576
+ "[StormcloudVideoPlayer] No preloaded ads available, ending ad break"
2577
+ );
2578
+ }
2457
2579
  this.handleAdFailure();
2458
2580
  }
2459
2581
  } else {
2460
- if (this.config.debugAdTiming) {
2461
- console.log(
2462
- "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2463
- );
2464
- }
2465
2582
  this.handleAdFailure();
2466
2583
  }
2584
+ } else {
2585
+ if (this.config.debugAdTiming) {
2586
+ console.log(
2587
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2588
+ );
2589
+ }
2590
+ this.handleAdFailure();
2467
2591
  }
2468
2592
  });
2469
2593
  this.ima.on("content_pause", () => {
@@ -2494,22 +2618,30 @@ var StormcloudVideoPlayer = class {
2494
2618
  }
2495
2619
  const remaining = this.getRemainingAdMs();
2496
2620
  if (remaining > 500 && this.adPodQueue.length > 0) {
2497
- const next = this.adPodQueue.shift();
2498
- this.currentAdIndex++;
2499
- this.enforceAdHoldState();
2500
- if (this.config.debugAdTiming) {
2501
- console.log(
2502
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
2503
- );
2504
- }
2505
- this.playSingleAd(next).catch(() => {
2621
+ const nextPreloaded = this.findNextPreloadedAd();
2622
+ if (nextPreloaded) {
2623
+ this.currentAdIndex++;
2506
2624
  if (this.config.debugAdTiming) {
2507
- console.error(
2508
- "[StormcloudVideoPlayer] Failed to play next ad in pod"
2625
+ console.log(
2626
+ `[StormcloudVideoPlayer] Playing next preloaded ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - layer will stay visible if ad loads`
2627
+ );
2628
+ }
2629
+ this.playSingleAd(nextPreloaded).catch(() => {
2630
+ if (this.config.debugAdTiming) {
2631
+ console.error(
2632
+ "[StormcloudVideoPlayer] Failed to play next ad in pod"
2633
+ );
2634
+ }
2635
+ this.handleAdPodComplete();
2636
+ });
2637
+ } else {
2638
+ if (this.config.debugAdTiming) {
2639
+ console.log(
2640
+ "[StormcloudVideoPlayer] No preloaded ads available - completing ad break"
2509
2641
  );
2510
2642
  }
2511
2643
  this.handleAdPodComplete();
2512
- });
2644
+ }
2513
2645
  } else {
2514
2646
  if (this.config.debugAdTiming) {
2515
2647
  console.log(
@@ -2754,6 +2886,9 @@ var StormcloudVideoPlayer = class {
2754
2886
  const first = tags[0];
2755
2887
  const rest = tags.slice(1);
2756
2888
  this.adPodQueue = rest;
2889
+ if (!this.showAds) {
2890
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
2891
+ }
2757
2892
  this.playSingleAd(first).catch(() => {
2758
2893
  });
2759
2894
  }
@@ -3110,19 +3245,37 @@ var StormcloudVideoPlayer = class {
3110
3245
  if (vastTagUrls.length > 0) {
3111
3246
  this.adPodAllUrls = [...vastTagUrls];
3112
3247
  this.preloadingAdUrls.clear();
3248
+ this.vastToMediaUrlMap.clear();
3249
+ this.preloadedMediaUrls.clear();
3250
+ this.preloadingMediaUrls.clear();
3113
3251
  this.logQueuedAdUrls(this.adPodAllUrls);
3252
+ if (this.config.debugAdTiming) {
3253
+ console.log(
3254
+ `[StormcloudVideoPlayer] Capturing original audio state before ad break:`,
3255
+ {
3256
+ videoMuted: this.video.muted,
3257
+ videoVolume: this.video.volume
3258
+ }
3259
+ );
3260
+ }
3261
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3114
3262
  this.inAdBreak = true;
3115
- this.showAds = true;
3116
3263
  this.currentAdIndex = 0;
3117
3264
  this.totalAdsInBreak = vastTagUrls.length;
3118
3265
  this.adPodQueue = [...vastTagUrls];
3119
- this.enforceAdHoldState();
3120
- this.preloadUpcomingAds();
3121
3266
  if (this.config.debugAdTiming) {
3122
3267
  console.log(
3123
- `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
3268
+ `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - preloading all ads in parallel (ad layer will show after ad loads)`
3124
3269
  );
3125
3270
  }
3271
+ this.preloadAllAdsInBackground().catch((error) => {
3272
+ if (this.config.debugAdTiming) {
3273
+ console.warn(
3274
+ "[StormcloudVideoPlayer] Error in background preloading:",
3275
+ error
3276
+ );
3277
+ }
3278
+ });
3126
3279
  try {
3127
3280
  await this.playAdPod();
3128
3281
  } catch (error) {
@@ -3148,14 +3301,28 @@ var StormcloudVideoPlayer = class {
3148
3301
  }
3149
3302
  return;
3150
3303
  }
3151
- const firstAd = this.adPodQueue.shift();
3304
+ await new Promise((resolve) => setTimeout(resolve, 500));
3305
+ const firstPreloaded = this.findNextPreloadedAd();
3306
+ if (!firstPreloaded) {
3307
+ if (this.config.debugAdTiming) {
3308
+ console.log(
3309
+ "[StormcloudVideoPlayer] No preloaded ads available after waiting, trying first ad anyway"
3310
+ );
3311
+ }
3312
+ const firstAd = this.adPodQueue.shift();
3313
+ if (firstAd) {
3314
+ this.currentAdIndex++;
3315
+ await this.playSingleAd(firstAd);
3316
+ }
3317
+ return;
3318
+ }
3152
3319
  this.currentAdIndex++;
3153
3320
  if (this.config.debugAdTiming) {
3154
3321
  console.log(
3155
- `[StormcloudVideoPlayer] Playing ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3322
+ `[StormcloudVideoPlayer] Playing first preloaded ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3156
3323
  );
3157
3324
  }
3158
- await this.playSingleAd(firstAd);
3325
+ await this.playSingleAd(firstPreloaded);
3159
3326
  }
3160
3327
  findCurrentOrNextBreak(nowMs) {
3161
3328
  var _a;
@@ -3188,7 +3355,7 @@ var StormcloudVideoPlayer = class {
3188
3355
  const first = tags[0];
3189
3356
  const rest = tags.slice(1);
3190
3357
  this.adPodQueue = rest;
3191
- this.enforceAdHoldState();
3358
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3192
3359
  await this.playSingleAd(first);
3193
3360
  this.inAdBreak = true;
3194
3361
  this.expectedAdBreakDurationMs = remainingMs;
@@ -3308,38 +3475,20 @@ var StormcloudVideoPlayer = class {
3308
3475
  `[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
3309
3476
  );
3310
3477
  }
3311
- if (!this.showAds) {
3312
- if (this.config.debugAdTiming) {
3313
- console.log(
3314
- `[StormcloudVideoPlayer] Capturing original state before ad request:`,
3315
- {
3316
- videoMuted: this.video.muted,
3317
- videoVolume: this.video.volume,
3318
- showAds: this.showAds
3319
- }
3320
- );
3321
- }
3322
- this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3323
- } else if (this.config.debugAdTiming) {
3324
- console.log(
3325
- `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
3326
- );
3327
- }
3328
3478
  this.startAdFailsafeTimer();
3329
3479
  try {
3330
3480
  await this.ima.requestAds(vastTagUrl);
3331
- this.preloadUpcomingAds();
3332
3481
  try {
3333
3482
  if (this.config.debugAdTiming) {
3334
3483
  console.log(
3335
- "[StormcloudVideoPlayer] Ad request completed, attempting playback"
3484
+ "[StormcloudVideoPlayer] Ad request completed, attempting playback (ad layer will show when IMA triggers content pause)"
3336
3485
  );
3337
3486
  }
3338
- this.enforceAdHoldState();
3339
3487
  await this.ima.play();
3488
+ this.showAds = true;
3340
3489
  if (this.config.debugAdTiming) {
3341
3490
  console.log(
3342
- "[StormcloudVideoPlayer] Ad playback started successfully"
3491
+ "[StormcloudVideoPlayer] Ad playback started successfully, showAds = true"
3343
3492
  );
3344
3493
  }
3345
3494
  } catch (playError) {
@@ -3367,6 +3516,9 @@ var StormcloudVideoPlayer = class {
3367
3516
  }
3368
3517
  this.releaseAdHoldState();
3369
3518
  this.preloadingAdUrls.clear();
3519
+ this.vastToMediaUrlMap.clear();
3520
+ this.preloadedMediaUrls.clear();
3521
+ this.preloadingMediaUrls.clear();
3370
3522
  this.inAdBreak = false;
3371
3523
  this.expectedAdBreakDurationMs = void 0;
3372
3524
  this.currentAdBreakStartWallClockMs = void 0;
@@ -3476,44 +3628,204 @@ var StormcloudVideoPlayer = class {
3476
3628
  this.ima.hidePlaceholder();
3477
3629
  }
3478
3630
  }
3479
- preloadUpcomingAds() {
3480
- if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
3481
- return;
3631
+ async fetchAndParseVastXml(vastTagUrl) {
3632
+ try {
3633
+ const response = await fetch(vastTagUrl, { mode: "cors" });
3634
+ if (!response.ok) {
3635
+ throw new Error(`Failed to fetch VAST: ${response.status}`);
3636
+ }
3637
+ const xmlText = await response.text();
3638
+ return this.extractMediaUrlsFromVast(xmlText);
3639
+ } catch (error) {
3640
+ if (this.config.debugAdTiming) {
3641
+ console.warn(
3642
+ `[StormcloudVideoPlayer] Failed to fetch/parse VAST XML: ${vastTagUrl}`,
3643
+ error
3644
+ );
3645
+ }
3646
+ return [];
3482
3647
  }
3483
- const upcoming = this.adPodQueue.slice(0, 2);
3484
- for (const url of upcoming) {
3485
- if (!url) continue;
3486
- if (this.ima.hasPreloadedAd(url)) {
3487
- this.preloadingAdUrls.delete(url);
3488
- continue;
3648
+ }
3649
+ extractMediaUrlsFromVast(xmlText) {
3650
+ var _a;
3651
+ const mediaUrls = [];
3652
+ try {
3653
+ const parser = new DOMParser();
3654
+ const xmlDoc = parser.parseFromString(xmlText, "text/xml");
3655
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
3656
+ for (const mediaFile of Array.from(mediaFileElements)) {
3657
+ const url = (_a = mediaFile.textContent) == null ? void 0 : _a.trim();
3658
+ if (url) {
3659
+ const lowerUrl = url.toLowerCase();
3660
+ 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")) {
3661
+ mediaUrls.push(url);
3662
+ }
3663
+ }
3489
3664
  }
3490
- if (this.preloadingAdUrls.has(url)) {
3491
- continue;
3665
+ if (this.config.debugAdTiming && mediaUrls.length > 0) {
3666
+ console.log(
3667
+ `[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs from VAST:`,
3668
+ mediaUrls
3669
+ );
3492
3670
  }
3671
+ } catch (error) {
3672
+ if (this.config.debugAdTiming) {
3673
+ console.warn(
3674
+ "[StormcloudVideoPlayer] Failed to parse VAST XML:",
3675
+ error
3676
+ );
3677
+ }
3678
+ }
3679
+ return mediaUrls;
3680
+ }
3681
+ async preloadMediaFile(mediaUrl) {
3682
+ if (this.preloadedMediaUrls.has(mediaUrl)) {
3683
+ return;
3684
+ }
3685
+ if (this.preloadingMediaUrls.has(mediaUrl)) {
3686
+ return;
3687
+ }
3688
+ this.preloadingMediaUrls.add(mediaUrl);
3689
+ try {
3493
3690
  if (this.config.debugAdTiming) {
3494
3691
  console.log(
3495
- `[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
3692
+ `[StormcloudVideoPlayer] Preloading video file: ${mediaUrl}`
3496
3693
  );
3497
3694
  }
3498
- this.preloadingAdUrls.add(url);
3499
- this.ima.preloadAds(url).then(() => {
3695
+ const response = await fetch(mediaUrl, {
3696
+ mode: "cors",
3697
+ method: "GET",
3698
+ headers: {
3699
+ Range: "bytes=0-1048576"
3700
+ }
3701
+ });
3702
+ if (response.ok || response.status === 206) {
3703
+ this.preloadedMediaUrls.add(mediaUrl);
3500
3704
  if (this.config.debugAdTiming) {
3501
3705
  console.log(
3502
- `[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
3706
+ `[StormcloudVideoPlayer] Successfully preloaded video file: ${mediaUrl}`
3503
3707
  );
3504
3708
  }
3505
- }).catch((error) => {
3709
+ }
3710
+ } catch (error) {
3711
+ if (this.config.debugAdTiming) {
3712
+ console.warn(
3713
+ `[StormcloudVideoPlayer] Failed to preload video file: ${mediaUrl}`,
3714
+ error
3715
+ );
3716
+ }
3717
+ } finally {
3718
+ this.preloadingMediaUrls.delete(mediaUrl);
3719
+ }
3720
+ }
3721
+ async preloadAllAdsInBackground() {
3722
+ if (this.adPodAllUrls.length === 0) {
3723
+ return;
3724
+ }
3725
+ if (this.config.debugAdTiming) {
3726
+ console.log(
3727
+ `[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
3728
+ );
3729
+ }
3730
+ const preloadPromises = this.adPodAllUrls.map(
3731
+ (vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
3506
3732
  if (this.config.debugAdTiming) {
3507
3733
  console.warn(
3508
- `[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
3734
+ `[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
3509
3735
  error
3510
3736
  );
3511
3737
  }
3512
- }).finally(() => {
3513
- this.preloadingAdUrls.delete(url);
3514
- });
3738
+ })
3739
+ );
3740
+ await Promise.all(preloadPromises);
3741
+ if (this.config.debugAdTiming) {
3742
+ console.log(
3743
+ `[StormcloudVideoPlayer] Background preloading completed for all ads`
3744
+ );
3515
3745
  }
3516
3746
  }
3747
+ async preloadSingleAd(vastTagUrl) {
3748
+ if (!vastTagUrl) return;
3749
+ try {
3750
+ if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
3751
+ if (!this.preloadingAdUrls.has(vastTagUrl)) {
3752
+ if (this.config.debugAdTiming) {
3753
+ console.log(
3754
+ `[StormcloudVideoPlayer] Preloading VAST: ${vastTagUrl}`
3755
+ );
3756
+ }
3757
+ this.preloadingAdUrls.add(vastTagUrl);
3758
+ await this.ima.preloadAds(vastTagUrl).then(() => {
3759
+ if (this.config.debugAdTiming) {
3760
+ console.log(
3761
+ `[StormcloudVideoPlayer] IMA VAST preload complete: ${vastTagUrl}`
3762
+ );
3763
+ }
3764
+ }).catch((error) => {
3765
+ if (this.config.debugAdTiming) {
3766
+ console.warn(
3767
+ `[StormcloudVideoPlayer] IMA VAST preload failed: ${vastTagUrl}`,
3768
+ error
3769
+ );
3770
+ }
3771
+ }).finally(() => {
3772
+ this.preloadingAdUrls.delete(vastTagUrl);
3773
+ });
3774
+ }
3775
+ }
3776
+ let mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3777
+ if (!mediaUrls) {
3778
+ if (this.config.debugAdTiming) {
3779
+ console.log(
3780
+ `[StormcloudVideoPlayer] Fetching and parsing VAST to extract media URLs: ${vastTagUrl}`
3781
+ );
3782
+ }
3783
+ mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
3784
+ if (mediaUrls.length > 0) {
3785
+ this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
3786
+ }
3787
+ }
3788
+ if (mediaUrls && mediaUrls.length > 0) {
3789
+ const primaryMediaUrl = mediaUrls[0];
3790
+ if (primaryMediaUrl && !this.preloadedMediaUrls.has(primaryMediaUrl)) {
3791
+ await this.preloadMediaFile(primaryMediaUrl);
3792
+ }
3793
+ }
3794
+ } catch (error) {
3795
+ if (this.config.debugAdTiming) {
3796
+ console.warn(
3797
+ `[StormcloudVideoPlayer] Failed to preload ad: ${vastTagUrl}`,
3798
+ error
3799
+ );
3800
+ }
3801
+ }
3802
+ }
3803
+ findNextPreloadedAd() {
3804
+ var _a, _b, _c;
3805
+ for (let i = 0; i < this.adPodQueue.length; i++) {
3806
+ const vastTagUrl = this.adPodQueue[i];
3807
+ if (!vastTagUrl) continue;
3808
+ const hasImaPreload = (_c = (_b = (_a = this.ima).hasPreloadedAd) == null ? void 0 : _b.call(_a, vastTagUrl)) != null ? _c : false;
3809
+ const mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3810
+ const hasMediaPreload = mediaUrls && mediaUrls.length > 0 ? this.preloadedMediaUrls.has(mediaUrls[0]) : false;
3811
+ if (hasImaPreload || hasMediaPreload) {
3812
+ if (this.config.debugAdTiming) {
3813
+ console.log(
3814
+ `[StormcloudVideoPlayer] Found preloaded ad at index ${i}: ${vastTagUrl}`,
3815
+ { hasImaPreload, hasMediaPreload }
3816
+ );
3817
+ }
3818
+ this.adPodQueue.splice(0, i + 1);
3819
+ return vastTagUrl;
3820
+ }
3821
+ }
3822
+ if (this.config.debugAdTiming) {
3823
+ console.log(
3824
+ "[StormcloudVideoPlayer] No preloaded ads found in queue"
3825
+ );
3826
+ }
3827
+ return void 0;
3828
+ }
3517
3829
  getRemainingAdMs() {
3518
3830
  if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
3519
3831
  return 0;
@@ -3682,6 +3994,9 @@ var StormcloudVideoPlayer = class {
3682
3994
  (_b = this.ima) == null ? void 0 : _b.destroy();
3683
3995
  this.releaseAdHoldState();
3684
3996
  this.preloadingAdUrls.clear();
3997
+ this.vastToMediaUrlMap.clear();
3998
+ this.preloadedMediaUrls.clear();
3999
+ this.preloadingMediaUrls.clear();
3685
4000
  this.adPodAllUrls = [];
3686
4001
  }
3687
4002
  };