stormcloud-video-player 0.2.30 → 0.2.32

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
@@ -212,7 +212,11 @@ function createImaController(video, options) {
212
212
  }
213
213
  function hideContentVideo() {
214
214
  if (!contentVideoHidden) {
215
- video.style.visibility = "hidden";
215
+ video.style.transition = "opacity 0.3s ease-in-out";
216
+ video.style.opacity = "0";
217
+ setTimeout(() => {
218
+ video.style.visibility = "hidden";
219
+ }, 300);
216
220
  video.muted = true;
217
221
  video.volume = 0;
218
222
  contentVideoHidden = true;
@@ -222,6 +226,9 @@ function createImaController(video, options) {
222
226
  function showContentVideo() {
223
227
  if (contentVideoHidden) {
224
228
  video.style.visibility = "visible";
229
+ video.style.transition = "opacity 0.3s ease-in-out";
230
+ video.offsetHeight;
231
+ video.style.opacity = "1";
225
232
  video.muted = originalMutedState;
226
233
  video.volume = originalVolume;
227
234
  contentVideoHidden = false;
@@ -394,7 +401,9 @@ function createImaController(video, options) {
394
401
  container.style.justifyContent = "center";
395
402
  container.style.pointerEvents = "none";
396
403
  container.style.zIndex = "10";
397
- container.style.backgroundColor = "#000";
404
+ container.style.backgroundColor = "transparent";
405
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
406
+ container.style.opacity = "0";
398
407
  (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
399
408
  adContainerEl = container;
400
409
  }
@@ -504,7 +513,9 @@ function createImaController(video, options) {
504
513
  container.style.justifyContent = "center";
505
514
  container.style.pointerEvents = "none";
506
515
  container.style.zIndex = "10";
507
- container.style.backgroundColor = "#000";
516
+ container.style.backgroundColor = "transparent";
517
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
518
+ container.style.opacity = "0";
508
519
  if (!video.parentElement) {
509
520
  throw new Error("Video element has no parent for ad container");
510
521
  }
@@ -568,13 +579,19 @@ function createImaController(video, options) {
568
579
  console.error("[IMA] Ad error:", errorEvent.getError());
569
580
  destroyAdsManager();
570
581
  adPlaying = false;
571
- showContentVideo();
572
582
  setAdPlayingFlag(false);
573
583
  if (adContainerEl) {
574
- adContainerEl.style.pointerEvents = "none";
575
- adContainerEl.style.display = "none";
576
- console.log("[IMA] Ad container hidden after error");
584
+ adContainerEl.style.opacity = "0";
585
+ adContainerEl.style.backgroundColor = "transparent";
586
+ setTimeout(() => {
587
+ if (adContainerEl) {
588
+ adContainerEl.style.pointerEvents = "none";
589
+ adContainerEl.style.display = "none";
590
+ console.log("[IMA] Ad container hidden after error");
591
+ }
592
+ }, 300);
577
593
  }
594
+ showContentVideo();
578
595
  if (adsLoadedReject) {
579
596
  adsLoadedReject(new Error("Ad playback error"));
580
597
  adsLoadedReject = void 0;
@@ -620,6 +637,15 @@ function createImaController(video, options) {
620
637
  "[IMA] Content video continues in background (Live mode)"
621
638
  );
622
639
  }
640
+ hideContentVideo();
641
+ if (adContainerEl) {
642
+ adContainerEl.style.pointerEvents = "auto";
643
+ adContainerEl.style.display = "flex";
644
+ adContainerEl.style.backgroundColor = "#000";
645
+ adContainerEl.offsetHeight;
646
+ adContainerEl.style.opacity = "1";
647
+ console.log("[IMA] Ad container shown on content pause");
648
+ }
623
649
  adPlaying = true;
624
650
  setAdPlayingFlag(true);
625
651
  emit("content_pause");
@@ -639,6 +665,9 @@ function createImaController(video, options) {
639
665
  if (adContainerEl) {
640
666
  adContainerEl.style.pointerEvents = "auto";
641
667
  adContainerEl.style.display = "flex";
668
+ adContainerEl.style.backgroundColor = "#000";
669
+ adContainerEl.offsetHeight;
670
+ adContainerEl.style.opacity = "1";
642
671
  console.log("[IMA] Ad container now visible");
643
672
  }
644
673
  });
@@ -655,12 +684,18 @@ function createImaController(video, options) {
655
684
  console.log("[IMA] All ads completed - restoring content");
656
685
  adPlaying = false;
657
686
  setAdPlayingFlag(false);
658
- showContentVideo();
659
687
  if (adContainerEl) {
660
- adContainerEl.style.pointerEvents = "none";
661
- adContainerEl.style.display = "none";
662
- console.log("[IMA] Ad container hidden");
688
+ adContainerEl.style.opacity = "0";
689
+ adContainerEl.style.backgroundColor = "transparent";
690
+ setTimeout(() => {
691
+ if (adContainerEl) {
692
+ adContainerEl.style.pointerEvents = "none";
693
+ adContainerEl.style.display = "none";
694
+ console.log("[IMA] Ad container hidden");
695
+ }
696
+ }, 300);
663
697
  }
698
+ showContentVideo();
664
699
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds) && video.paused) {
665
700
  console.log("[IMA] Resuming content video playback");
666
701
  video.play().catch((e) => {
@@ -678,13 +713,21 @@ function createImaController(video, options) {
678
713
  } catch (e) {
679
714
  console.error("[IMA] Error setting up ads manager:", e);
680
715
  adPlaying = false;
681
- showContentVideo();
682
716
  setAdPlayingFlag(false);
683
717
  if (adContainerEl) {
684
- adContainerEl.style.pointerEvents = "none";
685
- adContainerEl.style.display = "none";
686
- console.log("[IMA] Ad container hidden after setup error");
718
+ adContainerEl.style.opacity = "0";
719
+ adContainerEl.style.backgroundColor = "transparent";
720
+ setTimeout(() => {
721
+ if (adContainerEl) {
722
+ adContainerEl.style.pointerEvents = "none";
723
+ adContainerEl.style.display = "none";
724
+ console.log(
725
+ "[IMA] Ad container hidden after setup error"
726
+ );
727
+ }
728
+ }, 300);
687
729
  }
730
+ showContentVideo();
688
731
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
689
732
  if (video.paused) {
690
733
  console.log(
@@ -709,13 +752,19 @@ function createImaController(video, options) {
709
752
  (adErrorEvent) => {
710
753
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
711
754
  adPlaying = false;
712
- showContentVideo();
713
755
  setAdPlayingFlag(false);
714
756
  if (adContainerEl) {
715
- adContainerEl.style.pointerEvents = "none";
716
- adContainerEl.style.display = "none";
717
- console.log("[IMA] Ad container hidden after loader error");
757
+ adContainerEl.style.opacity = "0";
758
+ adContainerEl.style.backgroundColor = "transparent";
759
+ setTimeout(() => {
760
+ if (adContainerEl) {
761
+ adContainerEl.style.pointerEvents = "none";
762
+ adContainerEl.style.display = "none";
763
+ console.log("[IMA] Ad container hidden after loader error");
764
+ }
765
+ }, 300);
718
766
  }
767
+ showContentVideo();
719
768
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
720
769
  if (video.paused) {
721
770
  console.log("[IMA] Resuming paused video after loader error");
@@ -821,12 +870,18 @@ function createImaController(video, options) {
821
870
  console.log("[IMA] Stopping ad playback");
822
871
  adPlaying = false;
823
872
  setAdPlayingFlag(false);
824
- showContentVideo();
825
873
  if (adContainerEl) {
826
- adContainerEl.style.pointerEvents = "none";
827
- adContainerEl.style.display = "none";
828
- console.log("[IMA] Ad container hidden after stop");
874
+ adContainerEl.style.opacity = "0";
875
+ adContainerEl.style.backgroundColor = "transparent";
876
+ setTimeout(() => {
877
+ if (adContainerEl) {
878
+ adContainerEl.style.pointerEvents = "none";
879
+ adContainerEl.style.display = "none";
880
+ console.log("[IMA] Ad container hidden after stop");
881
+ }
882
+ }, 300);
829
883
  }
884
+ showContentVideo();
830
885
  try {
831
886
  (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
832
887
  } catch {
@@ -837,23 +892,29 @@ function createImaController(video, options) {
837
892
  var _a;
838
893
  destroyAdsManager();
839
894
  adPlaying = false;
840
- showContentVideo();
841
895
  setAdPlayingFlag(false);
842
896
  if (adContainerEl) {
843
- adContainerEl.style.pointerEvents = "none";
844
- adContainerEl.style.display = "none";
897
+ adContainerEl.style.opacity = "0";
898
+ adContainerEl.style.backgroundColor = "transparent";
899
+ setTimeout(() => {
900
+ if (adContainerEl) {
901
+ adContainerEl.style.pointerEvents = "none";
902
+ adContainerEl.style.display = "none";
903
+ if (adContainerEl.parentElement) {
904
+ adContainerEl.parentElement.removeChild(adContainerEl);
905
+ }
906
+ adContainerEl = void 0;
907
+ adVideoElement = void 0;
908
+ }
909
+ }, 300);
845
910
  }
911
+ showContentVideo();
846
912
  try {
847
913
  (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
848
914
  } catch {
849
915
  }
850
- if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
851
- adContainerEl.parentElement.removeChild(adContainerEl);
852
- }
853
- adContainerEl = void 0;
854
916
  adDisplayContainer = void 0;
855
917
  adsLoader = void 0;
856
- adVideoElement = void 0;
857
918
  contentVideoHidden = false;
858
919
  preloadedVast.clear();
859
920
  preloadingVast.clear();
@@ -933,13 +994,22 @@ function createImaController(video, options) {
933
994
  ensurePlaceholderContainer();
934
995
  if (adContainerEl) {
935
996
  adContainerEl.style.display = "flex";
997
+ adContainerEl.style.backgroundColor = "#000";
998
+ adContainerEl.offsetHeight;
999
+ adContainerEl.style.opacity = "1";
936
1000
  adContainerEl.style.pointerEvents = "auto";
937
1001
  }
938
1002
  },
939
1003
  hidePlaceholder() {
940
1004
  if (adContainerEl) {
941
- adContainerEl.style.display = "none";
942
- adContainerEl.style.pointerEvents = "none";
1005
+ adContainerEl.style.opacity = "0";
1006
+ adContainerEl.style.backgroundColor = "transparent";
1007
+ setTimeout(() => {
1008
+ if (adContainerEl) {
1009
+ adContainerEl.style.display = "none";
1010
+ adContainerEl.style.pointerEvents = "none";
1011
+ }
1012
+ }, 300);
943
1013
  }
944
1014
  }
945
1015
  };
@@ -2110,6 +2180,9 @@ var StormcloudVideoPlayer = class {
2110
2180
  this.hasInitialBufferCompleted = false;
2111
2181
  this.adPodAllUrls = [];
2112
2182
  this.preloadingAdUrls = /* @__PURE__ */ new Set();
2183
+ this.vastToMediaUrlMap = /* @__PURE__ */ new Map();
2184
+ this.preloadedMediaUrls = /* @__PURE__ */ new Set();
2185
+ this.preloadingMediaUrls = /* @__PURE__ */ new Set();
2113
2186
  initializePolyfills();
2114
2187
  const browserOverrides = getBrowserConfigOverrides();
2115
2188
  this.config = { ...config, ...browserOverrides };
@@ -2382,27 +2455,44 @@ var StormcloudVideoPlayer = class {
2382
2455
  });
2383
2456
  this.ima.on("ad_error", () => {
2384
2457
  if (this.config.debugAdTiming) {
2385
- console.log("[StormcloudVideoPlayer] IMA ad_error event received");
2458
+ console.log("[StormcloudVideoPlayer] IMA ad_error event received", {
2459
+ showAds: this.showAds,
2460
+ inAdBreak: this.inAdBreak,
2461
+ remainingAds: this.adPodQueue.length
2462
+ });
2386
2463
  }
2387
- if (this.showAds) {
2388
- if (this.inAdBreak) {
2389
- const remaining = this.getRemainingAdMs();
2390
- if (remaining > 500 && this.adPodQueue.length > 0) {
2391
- const next = this.adPodQueue.shift();
2464
+ if (this.inAdBreak) {
2465
+ const remaining = this.getRemainingAdMs();
2466
+ if (remaining > 500 && this.adPodQueue.length > 0) {
2467
+ const nextPreloaded = this.findNextPreloadedAd();
2468
+ if (nextPreloaded) {
2392
2469
  this.currentAdIndex++;
2393
- this.playSingleAd(next).catch(() => {
2470
+ if (this.config.debugAdTiming) {
2471
+ console.log(
2472
+ `[StormcloudVideoPlayer] Skipping to next preloaded ad after error`
2473
+ );
2474
+ }
2475
+ this.playSingleAd(nextPreloaded).catch(() => {
2476
+ this.handleAdFailure();
2394
2477
  });
2395
2478
  } else {
2479
+ if (this.config.debugAdTiming) {
2480
+ console.log(
2481
+ "[StormcloudVideoPlayer] No preloaded ads available, ending ad break"
2482
+ );
2483
+ }
2396
2484
  this.handleAdFailure();
2397
2485
  }
2398
2486
  } else {
2399
- if (this.config.debugAdTiming) {
2400
- console.log(
2401
- "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2402
- );
2403
- }
2404
2487
  this.handleAdFailure();
2405
2488
  }
2489
+ } else {
2490
+ if (this.config.debugAdTiming) {
2491
+ console.log(
2492
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2493
+ );
2494
+ }
2495
+ this.handleAdFailure();
2406
2496
  }
2407
2497
  });
2408
2498
  this.ima.on("content_pause", () => {
@@ -2433,22 +2523,31 @@ var StormcloudVideoPlayer = class {
2433
2523
  }
2434
2524
  const remaining = this.getRemainingAdMs();
2435
2525
  if (remaining > 500 && this.adPodQueue.length > 0) {
2436
- const next = this.adPodQueue.shift();
2437
- this.currentAdIndex++;
2438
- this.enforceAdHoldState();
2439
- if (this.config.debugAdTiming) {
2440
- console.log(
2441
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
2442
- );
2443
- }
2444
- this.playSingleAd(next).catch(() => {
2526
+ const nextPreloaded = this.findNextPreloadedAd();
2527
+ if (nextPreloaded) {
2528
+ this.currentAdIndex++;
2529
+ this.enforceAdHoldState();
2445
2530
  if (this.config.debugAdTiming) {
2446
- console.error(
2447
- "[StormcloudVideoPlayer] Failed to play next ad in pod"
2531
+ console.log(
2532
+ `[StormcloudVideoPlayer] Playing next preloaded ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
2533
+ );
2534
+ }
2535
+ this.playSingleAd(nextPreloaded).catch(() => {
2536
+ if (this.config.debugAdTiming) {
2537
+ console.error(
2538
+ "[StormcloudVideoPlayer] Failed to play next ad in pod"
2539
+ );
2540
+ }
2541
+ this.handleAdPodComplete();
2542
+ });
2543
+ } else {
2544
+ if (this.config.debugAdTiming) {
2545
+ console.log(
2546
+ "[StormcloudVideoPlayer] No preloaded ads available - completing ad break"
2448
2547
  );
2449
2548
  }
2450
2549
  this.handleAdPodComplete();
2451
- });
2550
+ }
2452
2551
  } else {
2453
2552
  if (this.config.debugAdTiming) {
2454
2553
  console.log(
@@ -2693,6 +2792,9 @@ var StormcloudVideoPlayer = class {
2693
2792
  const first = tags[0];
2694
2793
  const rest = tags.slice(1);
2695
2794
  this.adPodQueue = rest;
2795
+ if (!this.showAds) {
2796
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
2797
+ }
2696
2798
  this.playSingleAd(first).catch(() => {
2697
2799
  });
2698
2800
  }
@@ -3049,19 +3151,38 @@ var StormcloudVideoPlayer = class {
3049
3151
  if (vastTagUrls.length > 0) {
3050
3152
  this.adPodAllUrls = [...vastTagUrls];
3051
3153
  this.preloadingAdUrls.clear();
3154
+ this.vastToMediaUrlMap.clear();
3155
+ this.preloadedMediaUrls.clear();
3156
+ this.preloadingMediaUrls.clear();
3052
3157
  this.logQueuedAdUrls(this.adPodAllUrls);
3158
+ if (this.config.debugAdTiming) {
3159
+ console.log(
3160
+ `[StormcloudVideoPlayer] Capturing original audio state before ad break:`,
3161
+ {
3162
+ videoMuted: this.video.muted,
3163
+ videoVolume: this.video.volume
3164
+ }
3165
+ );
3166
+ }
3167
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3053
3168
  this.inAdBreak = true;
3054
- this.showAds = true;
3055
3169
  this.currentAdIndex = 0;
3056
3170
  this.totalAdsInBreak = vastTagUrls.length;
3057
3171
  this.adPodQueue = [...vastTagUrls];
3058
3172
  this.enforceAdHoldState();
3059
- this.preloadUpcomingAds();
3060
3173
  if (this.config.debugAdTiming) {
3061
3174
  console.log(
3062
- `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
3175
+ `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - preloading all ads in parallel`
3063
3176
  );
3064
3177
  }
3178
+ this.preloadAllAdsInBackground().catch((error) => {
3179
+ if (this.config.debugAdTiming) {
3180
+ console.warn(
3181
+ "[StormcloudVideoPlayer] Error in background preloading:",
3182
+ error
3183
+ );
3184
+ }
3185
+ });
3065
3186
  try {
3066
3187
  await this.playAdPod();
3067
3188
  } catch (error) {
@@ -3087,14 +3208,28 @@ var StormcloudVideoPlayer = class {
3087
3208
  }
3088
3209
  return;
3089
3210
  }
3090
- const firstAd = this.adPodQueue.shift();
3211
+ await new Promise((resolve) => setTimeout(resolve, 500));
3212
+ const firstPreloaded = this.findNextPreloadedAd();
3213
+ if (!firstPreloaded) {
3214
+ if (this.config.debugAdTiming) {
3215
+ console.log(
3216
+ "[StormcloudVideoPlayer] No preloaded ads available after waiting, trying first ad anyway"
3217
+ );
3218
+ }
3219
+ const firstAd = this.adPodQueue.shift();
3220
+ if (firstAd) {
3221
+ this.currentAdIndex++;
3222
+ await this.playSingleAd(firstAd);
3223
+ }
3224
+ return;
3225
+ }
3091
3226
  this.currentAdIndex++;
3092
3227
  if (this.config.debugAdTiming) {
3093
3228
  console.log(
3094
- `[StormcloudVideoPlayer] Playing ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3229
+ `[StormcloudVideoPlayer] Playing first preloaded ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3095
3230
  );
3096
3231
  }
3097
- await this.playSingleAd(firstAd);
3232
+ await this.playSingleAd(firstPreloaded);
3098
3233
  }
3099
3234
  findCurrentOrNextBreak(nowMs) {
3100
3235
  var _a;
@@ -3127,6 +3262,7 @@ var StormcloudVideoPlayer = class {
3127
3262
  const first = tags[0];
3128
3263
  const rest = tags.slice(1);
3129
3264
  this.adPodQueue = rest;
3265
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3130
3266
  this.enforceAdHoldState();
3131
3267
  await this.playSingleAd(first);
3132
3268
  this.inAdBreak = true;
@@ -3247,27 +3383,9 @@ var StormcloudVideoPlayer = class {
3247
3383
  `[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
3248
3384
  );
3249
3385
  }
3250
- if (!this.showAds) {
3251
- if (this.config.debugAdTiming) {
3252
- console.log(
3253
- `[StormcloudVideoPlayer] Capturing original state before ad request:`,
3254
- {
3255
- videoMuted: this.video.muted,
3256
- videoVolume: this.video.volume,
3257
- showAds: this.showAds
3258
- }
3259
- );
3260
- }
3261
- this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3262
- } else if (this.config.debugAdTiming) {
3263
- console.log(
3264
- `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
3265
- );
3266
- }
3267
3386
  this.startAdFailsafeTimer();
3268
3387
  try {
3269
3388
  await this.ima.requestAds(vastTagUrl);
3270
- this.preloadUpcomingAds();
3271
3389
  try {
3272
3390
  if (this.config.debugAdTiming) {
3273
3391
  console.log(
@@ -3276,9 +3394,10 @@ var StormcloudVideoPlayer = class {
3276
3394
  }
3277
3395
  this.enforceAdHoldState();
3278
3396
  await this.ima.play();
3397
+ this.showAds = true;
3279
3398
  if (this.config.debugAdTiming) {
3280
3399
  console.log(
3281
- "[StormcloudVideoPlayer] Ad playback started successfully"
3400
+ "[StormcloudVideoPlayer] Ad playback started successfully, showAds = true"
3282
3401
  );
3283
3402
  }
3284
3403
  } catch (playError) {
@@ -3306,6 +3425,9 @@ var StormcloudVideoPlayer = class {
3306
3425
  }
3307
3426
  this.releaseAdHoldState();
3308
3427
  this.preloadingAdUrls.clear();
3428
+ this.vastToMediaUrlMap.clear();
3429
+ this.preloadedMediaUrls.clear();
3430
+ this.preloadingMediaUrls.clear();
3309
3431
  this.inAdBreak = false;
3310
3432
  this.expectedAdBreakDurationMs = void 0;
3311
3433
  this.currentAdBreakStartWallClockMs = void 0;
@@ -3415,43 +3537,203 @@ var StormcloudVideoPlayer = class {
3415
3537
  this.ima.hidePlaceholder();
3416
3538
  }
3417
3539
  }
3418
- preloadUpcomingAds() {
3419
- if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
3420
- return;
3540
+ async fetchAndParseVastXml(vastTagUrl) {
3541
+ try {
3542
+ const response = await fetch(vastTagUrl, { mode: "cors" });
3543
+ if (!response.ok) {
3544
+ throw new Error(`Failed to fetch VAST: ${response.status}`);
3545
+ }
3546
+ const xmlText = await response.text();
3547
+ return this.extractMediaUrlsFromVast(xmlText);
3548
+ } catch (error) {
3549
+ if (this.config.debugAdTiming) {
3550
+ console.warn(
3551
+ `[StormcloudVideoPlayer] Failed to fetch/parse VAST XML: ${vastTagUrl}`,
3552
+ error
3553
+ );
3554
+ }
3555
+ return [];
3421
3556
  }
3422
- const upcoming = this.adPodQueue.slice(0, 2);
3423
- for (const url of upcoming) {
3424
- if (!url) continue;
3425
- if (this.ima.hasPreloadedAd(url)) {
3426
- this.preloadingAdUrls.delete(url);
3427
- continue;
3557
+ }
3558
+ extractMediaUrlsFromVast(xmlText) {
3559
+ var _a;
3560
+ const mediaUrls = [];
3561
+ try {
3562
+ const parser = new DOMParser();
3563
+ const xmlDoc = parser.parseFromString(xmlText, "text/xml");
3564
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
3565
+ for (const mediaFile of Array.from(mediaFileElements)) {
3566
+ const url = (_a = mediaFile.textContent) == null ? void 0 : _a.trim();
3567
+ if (url) {
3568
+ const lowerUrl = url.toLowerCase();
3569
+ 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")) {
3570
+ mediaUrls.push(url);
3571
+ }
3572
+ }
3573
+ }
3574
+ if (this.config.debugAdTiming && mediaUrls.length > 0) {
3575
+ console.log(
3576
+ `[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs from VAST:`,
3577
+ mediaUrls
3578
+ );
3428
3579
  }
3429
- if (this.preloadingAdUrls.has(url)) {
3430
- continue;
3580
+ } catch (error) {
3581
+ if (this.config.debugAdTiming) {
3582
+ console.warn(
3583
+ "[StormcloudVideoPlayer] Failed to parse VAST XML:",
3584
+ error
3585
+ );
3431
3586
  }
3587
+ }
3588
+ return mediaUrls;
3589
+ }
3590
+ async preloadMediaFile(mediaUrl) {
3591
+ if (this.preloadedMediaUrls.has(mediaUrl)) {
3592
+ return;
3593
+ }
3594
+ if (this.preloadingMediaUrls.has(mediaUrl)) {
3595
+ return;
3596
+ }
3597
+ this.preloadingMediaUrls.add(mediaUrl);
3598
+ try {
3432
3599
  if (this.config.debugAdTiming) {
3433
3600
  console.log(
3434
- `[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
3601
+ `[StormcloudVideoPlayer] Preloading video file: ${mediaUrl}`
3435
3602
  );
3436
3603
  }
3437
- this.preloadingAdUrls.add(url);
3438
- this.ima.preloadAds(url).then(() => {
3604
+ const response = await fetch(mediaUrl, {
3605
+ mode: "cors",
3606
+ method: "GET",
3607
+ headers: {
3608
+ Range: "bytes=0-1048576"
3609
+ }
3610
+ });
3611
+ if (response.ok || response.status === 206) {
3612
+ this.preloadedMediaUrls.add(mediaUrl);
3439
3613
  if (this.config.debugAdTiming) {
3440
3614
  console.log(
3441
- `[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
3615
+ `[StormcloudVideoPlayer] Successfully preloaded video file: ${mediaUrl}`
3442
3616
  );
3443
3617
  }
3444
- }).catch((error) => {
3618
+ }
3619
+ } catch (error) {
3620
+ if (this.config.debugAdTiming) {
3621
+ console.warn(
3622
+ `[StormcloudVideoPlayer] Failed to preload video file: ${mediaUrl}`,
3623
+ error
3624
+ );
3625
+ }
3626
+ } finally {
3627
+ this.preloadingMediaUrls.delete(mediaUrl);
3628
+ }
3629
+ }
3630
+ async preloadAllAdsInBackground() {
3631
+ if (this.adPodAllUrls.length === 0) {
3632
+ return;
3633
+ }
3634
+ if (this.config.debugAdTiming) {
3635
+ console.log(
3636
+ `[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
3637
+ );
3638
+ }
3639
+ const preloadPromises = this.adPodAllUrls.map(
3640
+ (vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
3445
3641
  if (this.config.debugAdTiming) {
3446
3642
  console.warn(
3447
- `[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
3643
+ `[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
3448
3644
  error
3449
3645
  );
3450
3646
  }
3451
- }).finally(() => {
3452
- this.preloadingAdUrls.delete(url);
3453
- });
3647
+ })
3648
+ );
3649
+ await Promise.all(preloadPromises);
3650
+ if (this.config.debugAdTiming) {
3651
+ console.log(
3652
+ `[StormcloudVideoPlayer] Background preloading completed for all ads`
3653
+ );
3654
+ }
3655
+ }
3656
+ async preloadSingleAd(vastTagUrl) {
3657
+ if (!vastTagUrl) return;
3658
+ try {
3659
+ if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
3660
+ if (!this.preloadingAdUrls.has(vastTagUrl)) {
3661
+ if (this.config.debugAdTiming) {
3662
+ console.log(
3663
+ `[StormcloudVideoPlayer] Preloading VAST: ${vastTagUrl}`
3664
+ );
3665
+ }
3666
+ this.preloadingAdUrls.add(vastTagUrl);
3667
+ await this.ima.preloadAds(vastTagUrl).then(() => {
3668
+ if (this.config.debugAdTiming) {
3669
+ console.log(
3670
+ `[StormcloudVideoPlayer] IMA VAST preload complete: ${vastTagUrl}`
3671
+ );
3672
+ }
3673
+ }).catch((error) => {
3674
+ if (this.config.debugAdTiming) {
3675
+ console.warn(
3676
+ `[StormcloudVideoPlayer] IMA VAST preload failed: ${vastTagUrl}`,
3677
+ error
3678
+ );
3679
+ }
3680
+ }).finally(() => {
3681
+ this.preloadingAdUrls.delete(vastTagUrl);
3682
+ });
3683
+ }
3684
+ }
3685
+ let mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3686
+ if (!mediaUrls) {
3687
+ if (this.config.debugAdTiming) {
3688
+ console.log(
3689
+ `[StormcloudVideoPlayer] Fetching and parsing VAST to extract media URLs: ${vastTagUrl}`
3690
+ );
3691
+ }
3692
+ mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
3693
+ if (mediaUrls.length > 0) {
3694
+ this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
3695
+ }
3696
+ }
3697
+ if (mediaUrls && mediaUrls.length > 0) {
3698
+ const primaryMediaUrl = mediaUrls[0];
3699
+ if (primaryMediaUrl && !this.preloadedMediaUrls.has(primaryMediaUrl)) {
3700
+ await this.preloadMediaFile(primaryMediaUrl);
3701
+ }
3702
+ }
3703
+ } catch (error) {
3704
+ if (this.config.debugAdTiming) {
3705
+ console.warn(
3706
+ `[StormcloudVideoPlayer] Failed to preload ad: ${vastTagUrl}`,
3707
+ error
3708
+ );
3709
+ }
3710
+ }
3711
+ }
3712
+ findNextPreloadedAd() {
3713
+ var _a, _b, _c;
3714
+ for (let i = 0; i < this.adPodQueue.length; i++) {
3715
+ const vastTagUrl = this.adPodQueue[i];
3716
+ if (!vastTagUrl) continue;
3717
+ const hasImaPreload = (_c = (_b = (_a = this.ima).hasPreloadedAd) == null ? void 0 : _b.call(_a, vastTagUrl)) != null ? _c : false;
3718
+ const mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3719
+ const hasMediaPreload = mediaUrls && mediaUrls.length > 0 ? this.preloadedMediaUrls.has(mediaUrls[0]) : false;
3720
+ if (hasImaPreload || hasMediaPreload) {
3721
+ if (this.config.debugAdTiming) {
3722
+ console.log(
3723
+ `[StormcloudVideoPlayer] Found preloaded ad at index ${i}: ${vastTagUrl}`,
3724
+ { hasImaPreload, hasMediaPreload }
3725
+ );
3726
+ }
3727
+ this.adPodQueue.splice(0, i + 1);
3728
+ return vastTagUrl;
3729
+ }
3454
3730
  }
3731
+ if (this.config.debugAdTiming) {
3732
+ console.log(
3733
+ "[StormcloudVideoPlayer] No preloaded ads found in queue"
3734
+ );
3735
+ }
3736
+ return void 0;
3455
3737
  }
3456
3738
  getRemainingAdMs() {
3457
3739
  if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
@@ -3621,6 +3903,9 @@ var StormcloudVideoPlayer = class {
3621
3903
  (_b = this.ima) == null ? void 0 : _b.destroy();
3622
3904
  this.releaseAdHoldState();
3623
3905
  this.preloadingAdUrls.clear();
3906
+ this.vastToMediaUrlMap.clear();
3907
+ this.preloadedMediaUrls.clear();
3908
+ this.preloadingMediaUrls.clear();
3624
3909
  this.adPodAllUrls = [];
3625
3910
  }
3626
3911
  };