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.
@@ -256,7 +256,11 @@ function createImaController(video, options) {
256
256
  }
257
257
  function hideContentVideo() {
258
258
  if (!contentVideoHidden) {
259
- video.style.visibility = "hidden";
259
+ video.style.transition = "opacity 0.3s ease-in-out";
260
+ video.style.opacity = "0";
261
+ setTimeout(() => {
262
+ video.style.visibility = "hidden";
263
+ }, 300);
260
264
  video.muted = true;
261
265
  video.volume = 0;
262
266
  contentVideoHidden = true;
@@ -266,6 +270,9 @@ function createImaController(video, options) {
266
270
  function showContentVideo() {
267
271
  if (contentVideoHidden) {
268
272
  video.style.visibility = "visible";
273
+ video.style.transition = "opacity 0.3s ease-in-out";
274
+ video.offsetHeight;
275
+ video.style.opacity = "1";
269
276
  video.muted = originalMutedState;
270
277
  video.volume = originalVolume;
271
278
  contentVideoHidden = false;
@@ -438,7 +445,9 @@ function createImaController(video, options) {
438
445
  container.style.justifyContent = "center";
439
446
  container.style.pointerEvents = "none";
440
447
  container.style.zIndex = "10";
441
- container.style.backgroundColor = "#000";
448
+ container.style.backgroundColor = "transparent";
449
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
450
+ container.style.opacity = "0";
442
451
  (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
443
452
  adContainerEl = container;
444
453
  }
@@ -548,7 +557,9 @@ function createImaController(video, options) {
548
557
  container.style.justifyContent = "center";
549
558
  container.style.pointerEvents = "none";
550
559
  container.style.zIndex = "10";
551
- container.style.backgroundColor = "#000";
560
+ container.style.backgroundColor = "transparent";
561
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
562
+ container.style.opacity = "0";
552
563
  if (!video.parentElement) {
553
564
  throw new Error("Video element has no parent for ad container");
554
565
  }
@@ -612,13 +623,19 @@ function createImaController(video, options) {
612
623
  console.error("[IMA] Ad error:", errorEvent.getError());
613
624
  destroyAdsManager();
614
625
  adPlaying = false;
615
- showContentVideo();
616
626
  setAdPlayingFlag(false);
617
627
  if (adContainerEl) {
618
- adContainerEl.style.pointerEvents = "none";
619
- adContainerEl.style.display = "none";
620
- console.log("[IMA] Ad container hidden after error");
628
+ adContainerEl.style.opacity = "0";
629
+ adContainerEl.style.backgroundColor = "transparent";
630
+ setTimeout(() => {
631
+ if (adContainerEl) {
632
+ adContainerEl.style.pointerEvents = "none";
633
+ adContainerEl.style.display = "none";
634
+ console.log("[IMA] Ad container hidden after error");
635
+ }
636
+ }, 300);
621
637
  }
638
+ showContentVideo();
622
639
  if (adsLoadedReject) {
623
640
  adsLoadedReject(new Error("Ad playback error"));
624
641
  adsLoadedReject = void 0;
@@ -664,6 +681,15 @@ function createImaController(video, options) {
664
681
  "[IMA] Content video continues in background (Live mode)"
665
682
  );
666
683
  }
684
+ hideContentVideo();
685
+ if (adContainerEl) {
686
+ adContainerEl.style.pointerEvents = "auto";
687
+ adContainerEl.style.display = "flex";
688
+ adContainerEl.style.backgroundColor = "#000";
689
+ adContainerEl.offsetHeight;
690
+ adContainerEl.style.opacity = "1";
691
+ console.log("[IMA] Ad container shown on content pause");
692
+ }
667
693
  adPlaying = true;
668
694
  setAdPlayingFlag(true);
669
695
  emit("content_pause");
@@ -683,6 +709,9 @@ function createImaController(video, options) {
683
709
  if (adContainerEl) {
684
710
  adContainerEl.style.pointerEvents = "auto";
685
711
  adContainerEl.style.display = "flex";
712
+ adContainerEl.style.backgroundColor = "#000";
713
+ adContainerEl.offsetHeight;
714
+ adContainerEl.style.opacity = "1";
686
715
  console.log("[IMA] Ad container now visible");
687
716
  }
688
717
  });
@@ -699,12 +728,18 @@ function createImaController(video, options) {
699
728
  console.log("[IMA] All ads completed - restoring content");
700
729
  adPlaying = false;
701
730
  setAdPlayingFlag(false);
702
- showContentVideo();
703
731
  if (adContainerEl) {
704
- adContainerEl.style.pointerEvents = "none";
705
- adContainerEl.style.display = "none";
706
- console.log("[IMA] Ad container hidden");
732
+ adContainerEl.style.opacity = "0";
733
+ adContainerEl.style.backgroundColor = "transparent";
734
+ setTimeout(() => {
735
+ if (adContainerEl) {
736
+ adContainerEl.style.pointerEvents = "none";
737
+ adContainerEl.style.display = "none";
738
+ console.log("[IMA] Ad container hidden");
739
+ }
740
+ }, 300);
707
741
  }
742
+ showContentVideo();
708
743
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds) && video.paused) {
709
744
  console.log("[IMA] Resuming content video playback");
710
745
  video.play().catch((e) => {
@@ -722,13 +757,21 @@ function createImaController(video, options) {
722
757
  } catch (e) {
723
758
  console.error("[IMA] Error setting up ads manager:", e);
724
759
  adPlaying = false;
725
- showContentVideo();
726
760
  setAdPlayingFlag(false);
727
761
  if (adContainerEl) {
728
- adContainerEl.style.pointerEvents = "none";
729
- adContainerEl.style.display = "none";
730
- console.log("[IMA] Ad container hidden after setup error");
762
+ adContainerEl.style.opacity = "0";
763
+ adContainerEl.style.backgroundColor = "transparent";
764
+ setTimeout(() => {
765
+ if (adContainerEl) {
766
+ adContainerEl.style.pointerEvents = "none";
767
+ adContainerEl.style.display = "none";
768
+ console.log(
769
+ "[IMA] Ad container hidden after setup error"
770
+ );
771
+ }
772
+ }, 300);
731
773
  }
774
+ showContentVideo();
732
775
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
733
776
  if (video.paused) {
734
777
  console.log(
@@ -753,13 +796,19 @@ function createImaController(video, options) {
753
796
  (adErrorEvent) => {
754
797
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
755
798
  adPlaying = false;
756
- showContentVideo();
757
799
  setAdPlayingFlag(false);
758
800
  if (adContainerEl) {
759
- adContainerEl.style.pointerEvents = "none";
760
- adContainerEl.style.display = "none";
761
- console.log("[IMA] Ad container hidden after loader error");
801
+ adContainerEl.style.opacity = "0";
802
+ adContainerEl.style.backgroundColor = "transparent";
803
+ setTimeout(() => {
804
+ if (adContainerEl) {
805
+ adContainerEl.style.pointerEvents = "none";
806
+ adContainerEl.style.display = "none";
807
+ console.log("[IMA] Ad container hidden after loader error");
808
+ }
809
+ }, 300);
762
810
  }
811
+ showContentVideo();
763
812
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
764
813
  if (video.paused) {
765
814
  console.log("[IMA] Resuming paused video after loader error");
@@ -865,12 +914,18 @@ function createImaController(video, options) {
865
914
  console.log("[IMA] Stopping ad playback");
866
915
  adPlaying = false;
867
916
  setAdPlayingFlag(false);
868
- showContentVideo();
869
917
  if (adContainerEl) {
870
- adContainerEl.style.pointerEvents = "none";
871
- adContainerEl.style.display = "none";
872
- console.log("[IMA] Ad container hidden after stop");
918
+ adContainerEl.style.opacity = "0";
919
+ adContainerEl.style.backgroundColor = "transparent";
920
+ setTimeout(() => {
921
+ if (adContainerEl) {
922
+ adContainerEl.style.pointerEvents = "none";
923
+ adContainerEl.style.display = "none";
924
+ console.log("[IMA] Ad container hidden after stop");
925
+ }
926
+ }, 300);
873
927
  }
928
+ showContentVideo();
874
929
  try {
875
930
  (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
876
931
  } catch {
@@ -881,23 +936,29 @@ function createImaController(video, options) {
881
936
  var _a;
882
937
  destroyAdsManager();
883
938
  adPlaying = false;
884
- showContentVideo();
885
939
  setAdPlayingFlag(false);
886
940
  if (adContainerEl) {
887
- adContainerEl.style.pointerEvents = "none";
888
- adContainerEl.style.display = "none";
941
+ adContainerEl.style.opacity = "0";
942
+ adContainerEl.style.backgroundColor = "transparent";
943
+ setTimeout(() => {
944
+ if (adContainerEl) {
945
+ adContainerEl.style.pointerEvents = "none";
946
+ adContainerEl.style.display = "none";
947
+ if (adContainerEl.parentElement) {
948
+ adContainerEl.parentElement.removeChild(adContainerEl);
949
+ }
950
+ adContainerEl = void 0;
951
+ adVideoElement = void 0;
952
+ }
953
+ }, 300);
889
954
  }
955
+ showContentVideo();
890
956
  try {
891
957
  (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
892
958
  } catch {
893
959
  }
894
- if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
895
- adContainerEl.parentElement.removeChild(adContainerEl);
896
- }
897
- adContainerEl = void 0;
898
960
  adDisplayContainer = void 0;
899
961
  adsLoader = void 0;
900
- adVideoElement = void 0;
901
962
  contentVideoHidden = false;
902
963
  preloadedVast.clear();
903
964
  preloadingVast.clear();
@@ -977,13 +1038,22 @@ function createImaController(video, options) {
977
1038
  ensurePlaceholderContainer();
978
1039
  if (adContainerEl) {
979
1040
  adContainerEl.style.display = "flex";
1041
+ adContainerEl.style.backgroundColor = "#000";
1042
+ adContainerEl.offsetHeight;
1043
+ adContainerEl.style.opacity = "1";
980
1044
  adContainerEl.style.pointerEvents = "auto";
981
1045
  }
982
1046
  },
983
1047
  hidePlaceholder() {
984
1048
  if (adContainerEl) {
985
- adContainerEl.style.display = "none";
986
- adContainerEl.style.pointerEvents = "none";
1049
+ adContainerEl.style.opacity = "0";
1050
+ adContainerEl.style.backgroundColor = "transparent";
1051
+ setTimeout(() => {
1052
+ if (adContainerEl) {
1053
+ adContainerEl.style.display = "none";
1054
+ adContainerEl.style.pointerEvents = "none";
1055
+ }
1056
+ }, 300);
987
1057
  }
988
1058
  }
989
1059
  };
@@ -2154,6 +2224,9 @@ var StormcloudVideoPlayer = class {
2154
2224
  this.hasInitialBufferCompleted = false;
2155
2225
  this.adPodAllUrls = [];
2156
2226
  this.preloadingAdUrls = /* @__PURE__ */ new Set();
2227
+ this.vastToMediaUrlMap = /* @__PURE__ */ new Map();
2228
+ this.preloadedMediaUrls = /* @__PURE__ */ new Set();
2229
+ this.preloadingMediaUrls = /* @__PURE__ */ new Set();
2157
2230
  initializePolyfills();
2158
2231
  const browserOverrides = getBrowserConfigOverrides();
2159
2232
  this.config = { ...config, ...browserOverrides };
@@ -2426,27 +2499,44 @@ var StormcloudVideoPlayer = class {
2426
2499
  });
2427
2500
  this.ima.on("ad_error", () => {
2428
2501
  if (this.config.debugAdTiming) {
2429
- console.log("[StormcloudVideoPlayer] IMA ad_error event received");
2502
+ console.log("[StormcloudVideoPlayer] IMA ad_error event received", {
2503
+ showAds: this.showAds,
2504
+ inAdBreak: this.inAdBreak,
2505
+ remainingAds: this.adPodQueue.length
2506
+ });
2430
2507
  }
2431
- if (this.showAds) {
2432
- if (this.inAdBreak) {
2433
- const remaining = this.getRemainingAdMs();
2434
- if (remaining > 500 && this.adPodQueue.length > 0) {
2435
- const next = this.adPodQueue.shift();
2508
+ if (this.inAdBreak) {
2509
+ const remaining = this.getRemainingAdMs();
2510
+ if (remaining > 500 && this.adPodQueue.length > 0) {
2511
+ const nextPreloaded = this.findNextPreloadedAd();
2512
+ if (nextPreloaded) {
2436
2513
  this.currentAdIndex++;
2437
- this.playSingleAd(next).catch(() => {
2514
+ if (this.config.debugAdTiming) {
2515
+ console.log(
2516
+ `[StormcloudVideoPlayer] Skipping to next preloaded ad after error`
2517
+ );
2518
+ }
2519
+ this.playSingleAd(nextPreloaded).catch(() => {
2520
+ this.handleAdFailure();
2438
2521
  });
2439
2522
  } else {
2523
+ if (this.config.debugAdTiming) {
2524
+ console.log(
2525
+ "[StormcloudVideoPlayer] No preloaded ads available, ending ad break"
2526
+ );
2527
+ }
2440
2528
  this.handleAdFailure();
2441
2529
  }
2442
2530
  } else {
2443
- if (this.config.debugAdTiming) {
2444
- console.log(
2445
- "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2446
- );
2447
- }
2448
2531
  this.handleAdFailure();
2449
2532
  }
2533
+ } else {
2534
+ if (this.config.debugAdTiming) {
2535
+ console.log(
2536
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2537
+ );
2538
+ }
2539
+ this.handleAdFailure();
2450
2540
  }
2451
2541
  });
2452
2542
  this.ima.on("content_pause", () => {
@@ -2477,22 +2567,31 @@ var StormcloudVideoPlayer = class {
2477
2567
  }
2478
2568
  const remaining = this.getRemainingAdMs();
2479
2569
  if (remaining > 500 && this.adPodQueue.length > 0) {
2480
- const next = this.adPodQueue.shift();
2481
- this.currentAdIndex++;
2482
- this.enforceAdHoldState();
2483
- if (this.config.debugAdTiming) {
2484
- console.log(
2485
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
2486
- );
2487
- }
2488
- this.playSingleAd(next).catch(() => {
2570
+ const nextPreloaded = this.findNextPreloadedAd();
2571
+ if (nextPreloaded) {
2572
+ this.currentAdIndex++;
2573
+ this.enforceAdHoldState();
2489
2574
  if (this.config.debugAdTiming) {
2490
- console.error(
2491
- "[StormcloudVideoPlayer] Failed to play next ad in pod"
2575
+ console.log(
2576
+ `[StormcloudVideoPlayer] Playing next preloaded ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
2577
+ );
2578
+ }
2579
+ this.playSingleAd(nextPreloaded).catch(() => {
2580
+ if (this.config.debugAdTiming) {
2581
+ console.error(
2582
+ "[StormcloudVideoPlayer] Failed to play next ad in pod"
2583
+ );
2584
+ }
2585
+ this.handleAdPodComplete();
2586
+ });
2587
+ } else {
2588
+ if (this.config.debugAdTiming) {
2589
+ console.log(
2590
+ "[StormcloudVideoPlayer] No preloaded ads available - completing ad break"
2492
2591
  );
2493
2592
  }
2494
2593
  this.handleAdPodComplete();
2495
- });
2594
+ }
2496
2595
  } else {
2497
2596
  if (this.config.debugAdTiming) {
2498
2597
  console.log(
@@ -2737,6 +2836,9 @@ var StormcloudVideoPlayer = class {
2737
2836
  const first = tags[0];
2738
2837
  const rest = tags.slice(1);
2739
2838
  this.adPodQueue = rest;
2839
+ if (!this.showAds) {
2840
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
2841
+ }
2740
2842
  this.playSingleAd(first).catch(() => {
2741
2843
  });
2742
2844
  }
@@ -3093,19 +3195,38 @@ var StormcloudVideoPlayer = class {
3093
3195
  if (vastTagUrls.length > 0) {
3094
3196
  this.adPodAllUrls = [...vastTagUrls];
3095
3197
  this.preloadingAdUrls.clear();
3198
+ this.vastToMediaUrlMap.clear();
3199
+ this.preloadedMediaUrls.clear();
3200
+ this.preloadingMediaUrls.clear();
3096
3201
  this.logQueuedAdUrls(this.adPodAllUrls);
3202
+ if (this.config.debugAdTiming) {
3203
+ console.log(
3204
+ `[StormcloudVideoPlayer] Capturing original audio state before ad break:`,
3205
+ {
3206
+ videoMuted: this.video.muted,
3207
+ videoVolume: this.video.volume
3208
+ }
3209
+ );
3210
+ }
3211
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3097
3212
  this.inAdBreak = true;
3098
- this.showAds = true;
3099
3213
  this.currentAdIndex = 0;
3100
3214
  this.totalAdsInBreak = vastTagUrls.length;
3101
3215
  this.adPodQueue = [...vastTagUrls];
3102
3216
  this.enforceAdHoldState();
3103
- this.preloadUpcomingAds();
3104
3217
  if (this.config.debugAdTiming) {
3105
3218
  console.log(
3106
- `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
3219
+ `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - preloading all ads in parallel`
3107
3220
  );
3108
3221
  }
3222
+ this.preloadAllAdsInBackground().catch((error) => {
3223
+ if (this.config.debugAdTiming) {
3224
+ console.warn(
3225
+ "[StormcloudVideoPlayer] Error in background preloading:",
3226
+ error
3227
+ );
3228
+ }
3229
+ });
3109
3230
  try {
3110
3231
  await this.playAdPod();
3111
3232
  } catch (error) {
@@ -3131,14 +3252,28 @@ var StormcloudVideoPlayer = class {
3131
3252
  }
3132
3253
  return;
3133
3254
  }
3134
- const firstAd = this.adPodQueue.shift();
3255
+ await new Promise((resolve) => setTimeout(resolve, 500));
3256
+ const firstPreloaded = this.findNextPreloadedAd();
3257
+ if (!firstPreloaded) {
3258
+ if (this.config.debugAdTiming) {
3259
+ console.log(
3260
+ "[StormcloudVideoPlayer] No preloaded ads available after waiting, trying first ad anyway"
3261
+ );
3262
+ }
3263
+ const firstAd = this.adPodQueue.shift();
3264
+ if (firstAd) {
3265
+ this.currentAdIndex++;
3266
+ await this.playSingleAd(firstAd);
3267
+ }
3268
+ return;
3269
+ }
3135
3270
  this.currentAdIndex++;
3136
3271
  if (this.config.debugAdTiming) {
3137
3272
  console.log(
3138
- `[StormcloudVideoPlayer] Playing ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3273
+ `[StormcloudVideoPlayer] Playing first preloaded ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3139
3274
  );
3140
3275
  }
3141
- await this.playSingleAd(firstAd);
3276
+ await this.playSingleAd(firstPreloaded);
3142
3277
  }
3143
3278
  findCurrentOrNextBreak(nowMs) {
3144
3279
  var _a;
@@ -3171,6 +3306,7 @@ var StormcloudVideoPlayer = class {
3171
3306
  const first = tags[0];
3172
3307
  const rest = tags.slice(1);
3173
3308
  this.adPodQueue = rest;
3309
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3174
3310
  this.enforceAdHoldState();
3175
3311
  await this.playSingleAd(first);
3176
3312
  this.inAdBreak = true;
@@ -3291,27 +3427,9 @@ var StormcloudVideoPlayer = class {
3291
3427
  `[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
3292
3428
  );
3293
3429
  }
3294
- if (!this.showAds) {
3295
- if (this.config.debugAdTiming) {
3296
- console.log(
3297
- `[StormcloudVideoPlayer] Capturing original state before ad request:`,
3298
- {
3299
- videoMuted: this.video.muted,
3300
- videoVolume: this.video.volume,
3301
- showAds: this.showAds
3302
- }
3303
- );
3304
- }
3305
- this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3306
- } else if (this.config.debugAdTiming) {
3307
- console.log(
3308
- `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
3309
- );
3310
- }
3311
3430
  this.startAdFailsafeTimer();
3312
3431
  try {
3313
3432
  await this.ima.requestAds(vastTagUrl);
3314
- this.preloadUpcomingAds();
3315
3433
  try {
3316
3434
  if (this.config.debugAdTiming) {
3317
3435
  console.log(
@@ -3320,9 +3438,10 @@ var StormcloudVideoPlayer = class {
3320
3438
  }
3321
3439
  this.enforceAdHoldState();
3322
3440
  await this.ima.play();
3441
+ this.showAds = true;
3323
3442
  if (this.config.debugAdTiming) {
3324
3443
  console.log(
3325
- "[StormcloudVideoPlayer] Ad playback started successfully"
3444
+ "[StormcloudVideoPlayer] Ad playback started successfully, showAds = true"
3326
3445
  );
3327
3446
  }
3328
3447
  } catch (playError) {
@@ -3350,6 +3469,9 @@ var StormcloudVideoPlayer = class {
3350
3469
  }
3351
3470
  this.releaseAdHoldState();
3352
3471
  this.preloadingAdUrls.clear();
3472
+ this.vastToMediaUrlMap.clear();
3473
+ this.preloadedMediaUrls.clear();
3474
+ this.preloadingMediaUrls.clear();
3353
3475
  this.inAdBreak = false;
3354
3476
  this.expectedAdBreakDurationMs = void 0;
3355
3477
  this.currentAdBreakStartWallClockMs = void 0;
@@ -3459,44 +3581,204 @@ var StormcloudVideoPlayer = class {
3459
3581
  this.ima.hidePlaceholder();
3460
3582
  }
3461
3583
  }
3462
- preloadUpcomingAds() {
3463
- if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
3464
- return;
3584
+ async fetchAndParseVastXml(vastTagUrl) {
3585
+ try {
3586
+ const response = await fetch(vastTagUrl, { mode: "cors" });
3587
+ if (!response.ok) {
3588
+ throw new Error(`Failed to fetch VAST: ${response.status}`);
3589
+ }
3590
+ const xmlText = await response.text();
3591
+ return this.extractMediaUrlsFromVast(xmlText);
3592
+ } catch (error) {
3593
+ if (this.config.debugAdTiming) {
3594
+ console.warn(
3595
+ `[StormcloudVideoPlayer] Failed to fetch/parse VAST XML: ${vastTagUrl}`,
3596
+ error
3597
+ );
3598
+ }
3599
+ return [];
3465
3600
  }
3466
- const upcoming = this.adPodQueue.slice(0, 2);
3467
- for (const url of upcoming) {
3468
- if (!url) continue;
3469
- if (this.ima.hasPreloadedAd(url)) {
3470
- this.preloadingAdUrls.delete(url);
3471
- continue;
3601
+ }
3602
+ extractMediaUrlsFromVast(xmlText) {
3603
+ var _a;
3604
+ const mediaUrls = [];
3605
+ try {
3606
+ const parser = new DOMParser();
3607
+ const xmlDoc = parser.parseFromString(xmlText, "text/xml");
3608
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
3609
+ for (const mediaFile of Array.from(mediaFileElements)) {
3610
+ const url = (_a = mediaFile.textContent) == null ? void 0 : _a.trim();
3611
+ if (url) {
3612
+ const lowerUrl = url.toLowerCase();
3613
+ 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")) {
3614
+ mediaUrls.push(url);
3615
+ }
3616
+ }
3472
3617
  }
3473
- if (this.preloadingAdUrls.has(url)) {
3474
- continue;
3618
+ if (this.config.debugAdTiming && mediaUrls.length > 0) {
3619
+ console.log(
3620
+ `[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs from VAST:`,
3621
+ mediaUrls
3622
+ );
3623
+ }
3624
+ } catch (error) {
3625
+ if (this.config.debugAdTiming) {
3626
+ console.warn(
3627
+ "[StormcloudVideoPlayer] Failed to parse VAST XML:",
3628
+ error
3629
+ );
3475
3630
  }
3631
+ }
3632
+ return mediaUrls;
3633
+ }
3634
+ async preloadMediaFile(mediaUrl) {
3635
+ if (this.preloadedMediaUrls.has(mediaUrl)) {
3636
+ return;
3637
+ }
3638
+ if (this.preloadingMediaUrls.has(mediaUrl)) {
3639
+ return;
3640
+ }
3641
+ this.preloadingMediaUrls.add(mediaUrl);
3642
+ try {
3476
3643
  if (this.config.debugAdTiming) {
3477
3644
  console.log(
3478
- `[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
3645
+ `[StormcloudVideoPlayer] Preloading video file: ${mediaUrl}`
3479
3646
  );
3480
3647
  }
3481
- this.preloadingAdUrls.add(url);
3482
- this.ima.preloadAds(url).then(() => {
3648
+ const response = await fetch(mediaUrl, {
3649
+ mode: "cors",
3650
+ method: "GET",
3651
+ headers: {
3652
+ Range: "bytes=0-1048576"
3653
+ }
3654
+ });
3655
+ if (response.ok || response.status === 206) {
3656
+ this.preloadedMediaUrls.add(mediaUrl);
3483
3657
  if (this.config.debugAdTiming) {
3484
3658
  console.log(
3485
- `[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
3659
+ `[StormcloudVideoPlayer] Successfully preloaded video file: ${mediaUrl}`
3486
3660
  );
3487
3661
  }
3488
- }).catch((error) => {
3662
+ }
3663
+ } catch (error) {
3664
+ if (this.config.debugAdTiming) {
3665
+ console.warn(
3666
+ `[StormcloudVideoPlayer] Failed to preload video file: ${mediaUrl}`,
3667
+ error
3668
+ );
3669
+ }
3670
+ } finally {
3671
+ this.preloadingMediaUrls.delete(mediaUrl);
3672
+ }
3673
+ }
3674
+ async preloadAllAdsInBackground() {
3675
+ if (this.adPodAllUrls.length === 0) {
3676
+ return;
3677
+ }
3678
+ if (this.config.debugAdTiming) {
3679
+ console.log(
3680
+ `[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
3681
+ );
3682
+ }
3683
+ const preloadPromises = this.adPodAllUrls.map(
3684
+ (vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
3489
3685
  if (this.config.debugAdTiming) {
3490
3686
  console.warn(
3491
- `[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
3687
+ `[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
3492
3688
  error
3493
3689
  );
3494
3690
  }
3495
- }).finally(() => {
3496
- this.preloadingAdUrls.delete(url);
3497
- });
3691
+ })
3692
+ );
3693
+ await Promise.all(preloadPromises);
3694
+ if (this.config.debugAdTiming) {
3695
+ console.log(
3696
+ `[StormcloudVideoPlayer] Background preloading completed for all ads`
3697
+ );
3498
3698
  }
3499
3699
  }
3700
+ async preloadSingleAd(vastTagUrl) {
3701
+ if (!vastTagUrl) return;
3702
+ try {
3703
+ if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
3704
+ if (!this.preloadingAdUrls.has(vastTagUrl)) {
3705
+ if (this.config.debugAdTiming) {
3706
+ console.log(
3707
+ `[StormcloudVideoPlayer] Preloading VAST: ${vastTagUrl}`
3708
+ );
3709
+ }
3710
+ this.preloadingAdUrls.add(vastTagUrl);
3711
+ await this.ima.preloadAds(vastTagUrl).then(() => {
3712
+ if (this.config.debugAdTiming) {
3713
+ console.log(
3714
+ `[StormcloudVideoPlayer] IMA VAST preload complete: ${vastTagUrl}`
3715
+ );
3716
+ }
3717
+ }).catch((error) => {
3718
+ if (this.config.debugAdTiming) {
3719
+ console.warn(
3720
+ `[StormcloudVideoPlayer] IMA VAST preload failed: ${vastTagUrl}`,
3721
+ error
3722
+ );
3723
+ }
3724
+ }).finally(() => {
3725
+ this.preloadingAdUrls.delete(vastTagUrl);
3726
+ });
3727
+ }
3728
+ }
3729
+ let mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3730
+ if (!mediaUrls) {
3731
+ if (this.config.debugAdTiming) {
3732
+ console.log(
3733
+ `[StormcloudVideoPlayer] Fetching and parsing VAST to extract media URLs: ${vastTagUrl}`
3734
+ );
3735
+ }
3736
+ mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
3737
+ if (mediaUrls.length > 0) {
3738
+ this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
3739
+ }
3740
+ }
3741
+ if (mediaUrls && mediaUrls.length > 0) {
3742
+ const primaryMediaUrl = mediaUrls[0];
3743
+ if (primaryMediaUrl && !this.preloadedMediaUrls.has(primaryMediaUrl)) {
3744
+ await this.preloadMediaFile(primaryMediaUrl);
3745
+ }
3746
+ }
3747
+ } catch (error) {
3748
+ if (this.config.debugAdTiming) {
3749
+ console.warn(
3750
+ `[StormcloudVideoPlayer] Failed to preload ad: ${vastTagUrl}`,
3751
+ error
3752
+ );
3753
+ }
3754
+ }
3755
+ }
3756
+ findNextPreloadedAd() {
3757
+ var _a, _b, _c;
3758
+ for (let i = 0; i < this.adPodQueue.length; i++) {
3759
+ const vastTagUrl = this.adPodQueue[i];
3760
+ if (!vastTagUrl) continue;
3761
+ const hasImaPreload = (_c = (_b = (_a = this.ima).hasPreloadedAd) == null ? void 0 : _b.call(_a, vastTagUrl)) != null ? _c : false;
3762
+ const mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3763
+ const hasMediaPreload = mediaUrls && mediaUrls.length > 0 ? this.preloadedMediaUrls.has(mediaUrls[0]) : false;
3764
+ if (hasImaPreload || hasMediaPreload) {
3765
+ if (this.config.debugAdTiming) {
3766
+ console.log(
3767
+ `[StormcloudVideoPlayer] Found preloaded ad at index ${i}: ${vastTagUrl}`,
3768
+ { hasImaPreload, hasMediaPreload }
3769
+ );
3770
+ }
3771
+ this.adPodQueue.splice(0, i + 1);
3772
+ return vastTagUrl;
3773
+ }
3774
+ }
3775
+ if (this.config.debugAdTiming) {
3776
+ console.log(
3777
+ "[StormcloudVideoPlayer] No preloaded ads found in queue"
3778
+ );
3779
+ }
3780
+ return void 0;
3781
+ }
3500
3782
  getRemainingAdMs() {
3501
3783
  if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
3502
3784
  return 0;
@@ -3665,6 +3947,9 @@ var StormcloudVideoPlayer = class {
3665
3947
  (_b = this.ima) == null ? void 0 : _b.destroy();
3666
3948
  this.releaseAdHoldState();
3667
3949
  this.preloadingAdUrls.clear();
3950
+ this.vastToMediaUrlMap.clear();
3951
+ this.preloadedMediaUrls.clear();
3952
+ this.preloadingMediaUrls.clear();
3668
3953
  this.adPodAllUrls = [];
3669
3954
  }
3670
3955
  };