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.cjs CHANGED
@@ -281,7 +281,11 @@ function createImaController(video, options) {
281
281
  }
282
282
  function hideContentVideo() {
283
283
  if (!contentVideoHidden) {
284
- video.style.visibility = "hidden";
284
+ video.style.transition = "opacity 0.3s ease-in-out";
285
+ video.style.opacity = "0";
286
+ setTimeout(() => {
287
+ video.style.visibility = "hidden";
288
+ }, 300);
285
289
  video.muted = true;
286
290
  video.volume = 0;
287
291
  contentVideoHidden = true;
@@ -291,6 +295,9 @@ function createImaController(video, options) {
291
295
  function showContentVideo() {
292
296
  if (contentVideoHidden) {
293
297
  video.style.visibility = "visible";
298
+ video.style.transition = "opacity 0.3s ease-in-out";
299
+ video.offsetHeight;
300
+ video.style.opacity = "1";
294
301
  video.muted = originalMutedState;
295
302
  video.volume = originalVolume;
296
303
  contentVideoHidden = false;
@@ -463,7 +470,9 @@ function createImaController(video, options) {
463
470
  container.style.justifyContent = "center";
464
471
  container.style.pointerEvents = "none";
465
472
  container.style.zIndex = "10";
466
- container.style.backgroundColor = "#000";
473
+ container.style.backgroundColor = "transparent";
474
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
475
+ container.style.opacity = "0";
467
476
  (_a = video.parentElement) == null ? void 0 : _a.appendChild(container);
468
477
  adContainerEl = container;
469
478
  }
@@ -573,7 +582,9 @@ function createImaController(video, options) {
573
582
  container.style.justifyContent = "center";
574
583
  container.style.pointerEvents = "none";
575
584
  container.style.zIndex = "10";
576
- container.style.backgroundColor = "#000";
585
+ container.style.backgroundColor = "transparent";
586
+ container.style.transition = "opacity 0.3s ease-in-out, background-color 0.3s ease-in-out";
587
+ container.style.opacity = "0";
577
588
  if (!video.parentElement) {
578
589
  throw new Error("Video element has no parent for ad container");
579
590
  }
@@ -637,13 +648,19 @@ function createImaController(video, options) {
637
648
  console.error("[IMA] Ad error:", errorEvent.getError());
638
649
  destroyAdsManager();
639
650
  adPlaying = false;
640
- showContentVideo();
641
651
  setAdPlayingFlag(false);
642
652
  if (adContainerEl) {
643
- adContainerEl.style.pointerEvents = "none";
644
- adContainerEl.style.display = "none";
645
- console.log("[IMA] Ad container hidden after error");
653
+ adContainerEl.style.opacity = "0";
654
+ adContainerEl.style.backgroundColor = "transparent";
655
+ setTimeout(() => {
656
+ if (adContainerEl) {
657
+ adContainerEl.style.pointerEvents = "none";
658
+ adContainerEl.style.display = "none";
659
+ console.log("[IMA] Ad container hidden after error");
660
+ }
661
+ }, 300);
646
662
  }
663
+ showContentVideo();
647
664
  if (adsLoadedReject) {
648
665
  adsLoadedReject(new Error("Ad playback error"));
649
666
  adsLoadedReject = void 0;
@@ -689,6 +706,15 @@ function createImaController(video, options) {
689
706
  "[IMA] Content video continues in background (Live mode)"
690
707
  );
691
708
  }
709
+ hideContentVideo();
710
+ if (adContainerEl) {
711
+ adContainerEl.style.pointerEvents = "auto";
712
+ adContainerEl.style.display = "flex";
713
+ adContainerEl.style.backgroundColor = "#000";
714
+ adContainerEl.offsetHeight;
715
+ adContainerEl.style.opacity = "1";
716
+ console.log("[IMA] Ad container shown on content pause");
717
+ }
692
718
  adPlaying = true;
693
719
  setAdPlayingFlag(true);
694
720
  emit("content_pause");
@@ -708,6 +734,9 @@ function createImaController(video, options) {
708
734
  if (adContainerEl) {
709
735
  adContainerEl.style.pointerEvents = "auto";
710
736
  adContainerEl.style.display = "flex";
737
+ adContainerEl.style.backgroundColor = "#000";
738
+ adContainerEl.offsetHeight;
739
+ adContainerEl.style.opacity = "1";
711
740
  console.log("[IMA] Ad container now visible");
712
741
  }
713
742
  });
@@ -724,12 +753,18 @@ function createImaController(video, options) {
724
753
  console.log("[IMA] All ads completed - restoring content");
725
754
  adPlaying = false;
726
755
  setAdPlayingFlag(false);
727
- showContentVideo();
728
756
  if (adContainerEl) {
729
- adContainerEl.style.pointerEvents = "none";
730
- adContainerEl.style.display = "none";
731
- console.log("[IMA] Ad container hidden");
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");
764
+ }
765
+ }, 300);
732
766
  }
767
+ showContentVideo();
733
768
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds) && video.paused) {
734
769
  console.log("[IMA] Resuming content video playback");
735
770
  video.play().catch((e) => {
@@ -747,13 +782,21 @@ function createImaController(video, options) {
747
782
  } catch (e) {
748
783
  console.error("[IMA] Error setting up ads manager:", e);
749
784
  adPlaying = false;
750
- showContentVideo();
751
785
  setAdPlayingFlag(false);
752
786
  if (adContainerEl) {
753
- adContainerEl.style.pointerEvents = "none";
754
- adContainerEl.style.display = "none";
755
- console.log("[IMA] Ad container hidden after setup error");
787
+ adContainerEl.style.opacity = "0";
788
+ adContainerEl.style.backgroundColor = "transparent";
789
+ setTimeout(() => {
790
+ if (adContainerEl) {
791
+ adContainerEl.style.pointerEvents = "none";
792
+ adContainerEl.style.display = "none";
793
+ console.log(
794
+ "[IMA] Ad container hidden after setup error"
795
+ );
796
+ }
797
+ }, 300);
756
798
  }
799
+ showContentVideo();
757
800
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
758
801
  if (video.paused) {
759
802
  console.log(
@@ -778,13 +821,19 @@ function createImaController(video, options) {
778
821
  (adErrorEvent) => {
779
822
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
780
823
  adPlaying = false;
781
- showContentVideo();
782
824
  setAdPlayingFlag(false);
783
825
  if (adContainerEl) {
784
- adContainerEl.style.pointerEvents = "none";
785
- adContainerEl.style.display = "none";
786
- console.log("[IMA] Ad container hidden after loader error");
826
+ adContainerEl.style.opacity = "0";
827
+ adContainerEl.style.backgroundColor = "transparent";
828
+ setTimeout(() => {
829
+ if (adContainerEl) {
830
+ adContainerEl.style.pointerEvents = "none";
831
+ adContainerEl.style.display = "none";
832
+ console.log("[IMA] Ad container hidden after loader error");
833
+ }
834
+ }, 300);
787
835
  }
836
+ showContentVideo();
788
837
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
789
838
  if (video.paused) {
790
839
  console.log("[IMA] Resuming paused video after loader error");
@@ -890,12 +939,18 @@ function createImaController(video, options) {
890
939
  console.log("[IMA] Stopping ad playback");
891
940
  adPlaying = false;
892
941
  setAdPlayingFlag(false);
893
- showContentVideo();
894
942
  if (adContainerEl) {
895
- adContainerEl.style.pointerEvents = "none";
896
- adContainerEl.style.display = "none";
897
- console.log("[IMA] Ad container hidden after stop");
943
+ adContainerEl.style.opacity = "0";
944
+ adContainerEl.style.backgroundColor = "transparent";
945
+ setTimeout(() => {
946
+ if (adContainerEl) {
947
+ adContainerEl.style.pointerEvents = "none";
948
+ adContainerEl.style.display = "none";
949
+ console.log("[IMA] Ad container hidden after stop");
950
+ }
951
+ }, 300);
898
952
  }
953
+ showContentVideo();
899
954
  try {
900
955
  (_a = adsManager == null ? void 0 : adsManager.stop) == null ? void 0 : _a.call(adsManager);
901
956
  } catch {
@@ -906,23 +961,29 @@ function createImaController(video, options) {
906
961
  var _a;
907
962
  destroyAdsManager();
908
963
  adPlaying = false;
909
- showContentVideo();
910
964
  setAdPlayingFlag(false);
911
965
  if (adContainerEl) {
912
- adContainerEl.style.pointerEvents = "none";
913
- adContainerEl.style.display = "none";
966
+ adContainerEl.style.opacity = "0";
967
+ adContainerEl.style.backgroundColor = "transparent";
968
+ setTimeout(() => {
969
+ if (adContainerEl) {
970
+ adContainerEl.style.pointerEvents = "none";
971
+ adContainerEl.style.display = "none";
972
+ if (adContainerEl.parentElement) {
973
+ adContainerEl.parentElement.removeChild(adContainerEl);
974
+ }
975
+ adContainerEl = void 0;
976
+ adVideoElement = void 0;
977
+ }
978
+ }, 300);
914
979
  }
980
+ showContentVideo();
915
981
  try {
916
982
  (_a = adsLoader == null ? void 0 : adsLoader.destroy) == null ? void 0 : _a.call(adsLoader);
917
983
  } catch {
918
984
  }
919
- if (adContainerEl == null ? void 0 : adContainerEl.parentElement) {
920
- adContainerEl.parentElement.removeChild(adContainerEl);
921
- }
922
- adContainerEl = void 0;
923
985
  adDisplayContainer = void 0;
924
986
  adsLoader = void 0;
925
- adVideoElement = void 0;
926
987
  contentVideoHidden = false;
927
988
  preloadedVast.clear();
928
989
  preloadingVast.clear();
@@ -1002,13 +1063,22 @@ function createImaController(video, options) {
1002
1063
  ensurePlaceholderContainer();
1003
1064
  if (adContainerEl) {
1004
1065
  adContainerEl.style.display = "flex";
1066
+ adContainerEl.style.backgroundColor = "#000";
1067
+ adContainerEl.offsetHeight;
1068
+ adContainerEl.style.opacity = "1";
1005
1069
  adContainerEl.style.pointerEvents = "auto";
1006
1070
  }
1007
1071
  },
1008
1072
  hidePlaceholder() {
1009
1073
  if (adContainerEl) {
1010
- adContainerEl.style.display = "none";
1011
- adContainerEl.style.pointerEvents = "none";
1074
+ adContainerEl.style.opacity = "0";
1075
+ adContainerEl.style.backgroundColor = "transparent";
1076
+ setTimeout(() => {
1077
+ if (adContainerEl) {
1078
+ adContainerEl.style.display = "none";
1079
+ adContainerEl.style.pointerEvents = "none";
1080
+ }
1081
+ }, 300);
1012
1082
  }
1013
1083
  }
1014
1084
  };
@@ -2179,6 +2249,9 @@ var StormcloudVideoPlayer = class {
2179
2249
  this.hasInitialBufferCompleted = false;
2180
2250
  this.adPodAllUrls = [];
2181
2251
  this.preloadingAdUrls = /* @__PURE__ */ new Set();
2252
+ this.vastToMediaUrlMap = /* @__PURE__ */ new Map();
2253
+ this.preloadedMediaUrls = /* @__PURE__ */ new Set();
2254
+ this.preloadingMediaUrls = /* @__PURE__ */ new Set();
2182
2255
  initializePolyfills();
2183
2256
  const browserOverrides = getBrowserConfigOverrides();
2184
2257
  this.config = { ...config, ...browserOverrides };
@@ -2451,27 +2524,44 @@ var StormcloudVideoPlayer = class {
2451
2524
  });
2452
2525
  this.ima.on("ad_error", () => {
2453
2526
  if (this.config.debugAdTiming) {
2454
- console.log("[StormcloudVideoPlayer] IMA ad_error event received");
2527
+ console.log("[StormcloudVideoPlayer] IMA ad_error event received", {
2528
+ showAds: this.showAds,
2529
+ inAdBreak: this.inAdBreak,
2530
+ remainingAds: this.adPodQueue.length
2531
+ });
2455
2532
  }
2456
- if (this.showAds) {
2457
- if (this.inAdBreak) {
2458
- const remaining = this.getRemainingAdMs();
2459
- if (remaining > 500 && this.adPodQueue.length > 0) {
2460
- const next = this.adPodQueue.shift();
2533
+ if (this.inAdBreak) {
2534
+ const remaining = this.getRemainingAdMs();
2535
+ if (remaining > 500 && this.adPodQueue.length > 0) {
2536
+ const nextPreloaded = this.findNextPreloadedAd();
2537
+ if (nextPreloaded) {
2461
2538
  this.currentAdIndex++;
2462
- this.playSingleAd(next).catch(() => {
2539
+ if (this.config.debugAdTiming) {
2540
+ console.log(
2541
+ `[StormcloudVideoPlayer] Skipping to next preloaded ad after error`
2542
+ );
2543
+ }
2544
+ this.playSingleAd(nextPreloaded).catch(() => {
2545
+ this.handleAdFailure();
2463
2546
  });
2464
2547
  } else {
2548
+ if (this.config.debugAdTiming) {
2549
+ console.log(
2550
+ "[StormcloudVideoPlayer] No preloaded ads available, ending ad break"
2551
+ );
2552
+ }
2465
2553
  this.handleAdFailure();
2466
2554
  }
2467
2555
  } else {
2468
- if (this.config.debugAdTiming) {
2469
- console.log(
2470
- "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2471
- );
2472
- }
2473
2556
  this.handleAdFailure();
2474
2557
  }
2558
+ } else {
2559
+ if (this.config.debugAdTiming) {
2560
+ console.log(
2561
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
2562
+ );
2563
+ }
2564
+ this.handleAdFailure();
2475
2565
  }
2476
2566
  });
2477
2567
  this.ima.on("content_pause", () => {
@@ -2502,22 +2592,31 @@ var StormcloudVideoPlayer = class {
2502
2592
  }
2503
2593
  const remaining = this.getRemainingAdMs();
2504
2594
  if (remaining > 500 && this.adPodQueue.length > 0) {
2505
- const next = this.adPodQueue.shift();
2506
- this.currentAdIndex++;
2507
- this.enforceAdHoldState();
2508
- if (this.config.debugAdTiming) {
2509
- console.log(
2510
- `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak}) - IMMEDIATELY starting next ad`
2511
- );
2512
- }
2513
- this.playSingleAd(next).catch(() => {
2595
+ const nextPreloaded = this.findNextPreloadedAd();
2596
+ if (nextPreloaded) {
2597
+ this.currentAdIndex++;
2598
+ this.enforceAdHoldState();
2514
2599
  if (this.config.debugAdTiming) {
2515
- console.error(
2516
- "[StormcloudVideoPlayer] Failed to play next ad in pod"
2600
+ console.log(
2601
+ `[StormcloudVideoPlayer] Playing next preloaded ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
2602
+ );
2603
+ }
2604
+ this.playSingleAd(nextPreloaded).catch(() => {
2605
+ if (this.config.debugAdTiming) {
2606
+ console.error(
2607
+ "[StormcloudVideoPlayer] Failed to play next ad in pod"
2608
+ );
2609
+ }
2610
+ this.handleAdPodComplete();
2611
+ });
2612
+ } else {
2613
+ if (this.config.debugAdTiming) {
2614
+ console.log(
2615
+ "[StormcloudVideoPlayer] No preloaded ads available - completing ad break"
2517
2616
  );
2518
2617
  }
2519
2618
  this.handleAdPodComplete();
2520
- });
2619
+ }
2521
2620
  } else {
2522
2621
  if (this.config.debugAdTiming) {
2523
2622
  console.log(
@@ -2762,6 +2861,9 @@ var StormcloudVideoPlayer = class {
2762
2861
  const first = tags[0];
2763
2862
  const rest = tags.slice(1);
2764
2863
  this.adPodQueue = rest;
2864
+ if (!this.showAds) {
2865
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
2866
+ }
2765
2867
  this.playSingleAd(first).catch(() => {
2766
2868
  });
2767
2869
  }
@@ -3118,19 +3220,38 @@ var StormcloudVideoPlayer = class {
3118
3220
  if (vastTagUrls.length > 0) {
3119
3221
  this.adPodAllUrls = [...vastTagUrls];
3120
3222
  this.preloadingAdUrls.clear();
3223
+ this.vastToMediaUrlMap.clear();
3224
+ this.preloadedMediaUrls.clear();
3225
+ this.preloadingMediaUrls.clear();
3121
3226
  this.logQueuedAdUrls(this.adPodAllUrls);
3227
+ if (this.config.debugAdTiming) {
3228
+ console.log(
3229
+ `[StormcloudVideoPlayer] Capturing original audio state before ad break:`,
3230
+ {
3231
+ videoMuted: this.video.muted,
3232
+ videoVolume: this.video.volume
3233
+ }
3234
+ );
3235
+ }
3236
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3122
3237
  this.inAdBreak = true;
3123
- this.showAds = true;
3124
3238
  this.currentAdIndex = 0;
3125
3239
  this.totalAdsInBreak = vastTagUrls.length;
3126
3240
  this.adPodQueue = [...vastTagUrls];
3127
3241
  this.enforceAdHoldState();
3128
- this.preloadUpcomingAds();
3129
3242
  if (this.config.debugAdTiming) {
3130
3243
  console.log(
3131
- `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - will play continuously`
3244
+ `[StormcloudVideoPlayer] Starting ad pod with ${vastTagUrls.length} ads - preloading all ads in parallel`
3132
3245
  );
3133
3246
  }
3247
+ this.preloadAllAdsInBackground().catch((error) => {
3248
+ if (this.config.debugAdTiming) {
3249
+ console.warn(
3250
+ "[StormcloudVideoPlayer] Error in background preloading:",
3251
+ error
3252
+ );
3253
+ }
3254
+ });
3134
3255
  try {
3135
3256
  await this.playAdPod();
3136
3257
  } catch (error) {
@@ -3156,14 +3277,28 @@ var StormcloudVideoPlayer = class {
3156
3277
  }
3157
3278
  return;
3158
3279
  }
3159
- const firstAd = this.adPodQueue.shift();
3280
+ await new Promise((resolve) => setTimeout(resolve, 500));
3281
+ const firstPreloaded = this.findNextPreloadedAd();
3282
+ if (!firstPreloaded) {
3283
+ if (this.config.debugAdTiming) {
3284
+ console.log(
3285
+ "[StormcloudVideoPlayer] No preloaded ads available after waiting, trying first ad anyway"
3286
+ );
3287
+ }
3288
+ const firstAd = this.adPodQueue.shift();
3289
+ if (firstAd) {
3290
+ this.currentAdIndex++;
3291
+ await this.playSingleAd(firstAd);
3292
+ }
3293
+ return;
3294
+ }
3160
3295
  this.currentAdIndex++;
3161
3296
  if (this.config.debugAdTiming) {
3162
3297
  console.log(
3163
- `[StormcloudVideoPlayer] Playing ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3298
+ `[StormcloudVideoPlayer] Playing first preloaded ad ${this.currentAdIndex}/${this.totalAdsInBreak}`
3164
3299
  );
3165
3300
  }
3166
- await this.playSingleAd(firstAd);
3301
+ await this.playSingleAd(firstPreloaded);
3167
3302
  }
3168
3303
  findCurrentOrNextBreak(nowMs) {
3169
3304
  var _a;
@@ -3196,6 +3331,7 @@ var StormcloudVideoPlayer = class {
3196
3331
  const first = tags[0];
3197
3332
  const rest = tags.slice(1);
3198
3333
  this.adPodQueue = rest;
3334
+ this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3199
3335
  this.enforceAdHoldState();
3200
3336
  await this.playSingleAd(first);
3201
3337
  this.inAdBreak = true;
@@ -3316,27 +3452,9 @@ var StormcloudVideoPlayer = class {
3316
3452
  `[StormcloudVideoPlayer] IMA SDK preloaded this ad already: ${vastTagUrl}`
3317
3453
  );
3318
3454
  }
3319
- if (!this.showAds) {
3320
- if (this.config.debugAdTiming) {
3321
- console.log(
3322
- `[StormcloudVideoPlayer] Capturing original state before ad request:`,
3323
- {
3324
- videoMuted: this.video.muted,
3325
- videoVolume: this.video.volume,
3326
- showAds: this.showAds
3327
- }
3328
- );
3329
- }
3330
- this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3331
- } else if (this.config.debugAdTiming) {
3332
- console.log(
3333
- `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
3334
- );
3335
- }
3336
3455
  this.startAdFailsafeTimer();
3337
3456
  try {
3338
3457
  await this.ima.requestAds(vastTagUrl);
3339
- this.preloadUpcomingAds();
3340
3458
  try {
3341
3459
  if (this.config.debugAdTiming) {
3342
3460
  console.log(
@@ -3345,9 +3463,10 @@ var StormcloudVideoPlayer = class {
3345
3463
  }
3346
3464
  this.enforceAdHoldState();
3347
3465
  await this.ima.play();
3466
+ this.showAds = true;
3348
3467
  if (this.config.debugAdTiming) {
3349
3468
  console.log(
3350
- "[StormcloudVideoPlayer] Ad playback started successfully"
3469
+ "[StormcloudVideoPlayer] Ad playback started successfully, showAds = true"
3351
3470
  );
3352
3471
  }
3353
3472
  } catch (playError) {
@@ -3375,6 +3494,9 @@ var StormcloudVideoPlayer = class {
3375
3494
  }
3376
3495
  this.releaseAdHoldState();
3377
3496
  this.preloadingAdUrls.clear();
3497
+ this.vastToMediaUrlMap.clear();
3498
+ this.preloadedMediaUrls.clear();
3499
+ this.preloadingMediaUrls.clear();
3378
3500
  this.inAdBreak = false;
3379
3501
  this.expectedAdBreakDurationMs = void 0;
3380
3502
  this.currentAdBreakStartWallClockMs = void 0;
@@ -3484,43 +3606,203 @@ var StormcloudVideoPlayer = class {
3484
3606
  this.ima.hidePlaceholder();
3485
3607
  }
3486
3608
  }
3487
- preloadUpcomingAds() {
3488
- if (!this.ima.preloadAds || this.adPodQueue.length === 0) {
3489
- return;
3609
+ async fetchAndParseVastXml(vastTagUrl) {
3610
+ try {
3611
+ const response = await fetch(vastTagUrl, { mode: "cors" });
3612
+ if (!response.ok) {
3613
+ throw new Error(`Failed to fetch VAST: ${response.status}`);
3614
+ }
3615
+ const xmlText = await response.text();
3616
+ return this.extractMediaUrlsFromVast(xmlText);
3617
+ } catch (error) {
3618
+ if (this.config.debugAdTiming) {
3619
+ console.warn(
3620
+ `[StormcloudVideoPlayer] Failed to fetch/parse VAST XML: ${vastTagUrl}`,
3621
+ error
3622
+ );
3623
+ }
3624
+ return [];
3490
3625
  }
3491
- const upcoming = this.adPodQueue.slice(0, 2);
3492
- for (const url of upcoming) {
3493
- if (!url) continue;
3494
- if (this.ima.hasPreloadedAd(url)) {
3495
- this.preloadingAdUrls.delete(url);
3496
- continue;
3626
+ }
3627
+ extractMediaUrlsFromVast(xmlText) {
3628
+ var _a;
3629
+ const mediaUrls = [];
3630
+ try {
3631
+ const parser = new DOMParser();
3632
+ const xmlDoc = parser.parseFromString(xmlText, "text/xml");
3633
+ const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
3634
+ for (const mediaFile of Array.from(mediaFileElements)) {
3635
+ const url = (_a = mediaFile.textContent) == null ? void 0 : _a.trim();
3636
+ if (url) {
3637
+ const lowerUrl = url.toLowerCase();
3638
+ 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")) {
3639
+ mediaUrls.push(url);
3640
+ }
3641
+ }
3642
+ }
3643
+ if (this.config.debugAdTiming && mediaUrls.length > 0) {
3644
+ console.log(
3645
+ `[StormcloudVideoPlayer] Extracted ${mediaUrls.length} media URLs from VAST:`,
3646
+ mediaUrls
3647
+ );
3497
3648
  }
3498
- if (this.preloadingAdUrls.has(url)) {
3499
- continue;
3649
+ } catch (error) {
3650
+ if (this.config.debugAdTiming) {
3651
+ console.warn(
3652
+ "[StormcloudVideoPlayer] Failed to parse VAST XML:",
3653
+ error
3654
+ );
3500
3655
  }
3656
+ }
3657
+ return mediaUrls;
3658
+ }
3659
+ async preloadMediaFile(mediaUrl) {
3660
+ if (this.preloadedMediaUrls.has(mediaUrl)) {
3661
+ return;
3662
+ }
3663
+ if (this.preloadingMediaUrls.has(mediaUrl)) {
3664
+ return;
3665
+ }
3666
+ this.preloadingMediaUrls.add(mediaUrl);
3667
+ try {
3501
3668
  if (this.config.debugAdTiming) {
3502
3669
  console.log(
3503
- `[StormcloudVideoPlayer] Scheduling IMA preload for upcoming ad: ${url}`
3670
+ `[StormcloudVideoPlayer] Preloading video file: ${mediaUrl}`
3504
3671
  );
3505
3672
  }
3506
- this.preloadingAdUrls.add(url);
3507
- this.ima.preloadAds(url).then(() => {
3673
+ const response = await fetch(mediaUrl, {
3674
+ mode: "cors",
3675
+ method: "GET",
3676
+ headers: {
3677
+ Range: "bytes=0-1048576"
3678
+ }
3679
+ });
3680
+ if (response.ok || response.status === 206) {
3681
+ this.preloadedMediaUrls.add(mediaUrl);
3508
3682
  if (this.config.debugAdTiming) {
3509
3683
  console.log(
3510
- `[StormcloudVideoPlayer] IMA preload complete for ad: ${url}`
3684
+ `[StormcloudVideoPlayer] Successfully preloaded video file: ${mediaUrl}`
3511
3685
  );
3512
3686
  }
3513
- }).catch((error) => {
3687
+ }
3688
+ } catch (error) {
3689
+ if (this.config.debugAdTiming) {
3690
+ console.warn(
3691
+ `[StormcloudVideoPlayer] Failed to preload video file: ${mediaUrl}`,
3692
+ error
3693
+ );
3694
+ }
3695
+ } finally {
3696
+ this.preloadingMediaUrls.delete(mediaUrl);
3697
+ }
3698
+ }
3699
+ async preloadAllAdsInBackground() {
3700
+ if (this.adPodAllUrls.length === 0) {
3701
+ return;
3702
+ }
3703
+ if (this.config.debugAdTiming) {
3704
+ console.log(
3705
+ `[StormcloudVideoPlayer] Starting parallel preload of ${this.adPodAllUrls.length} ads`
3706
+ );
3707
+ }
3708
+ const preloadPromises = this.adPodAllUrls.map(
3709
+ (vastTagUrl) => this.preloadSingleAd(vastTagUrl).catch((error) => {
3514
3710
  if (this.config.debugAdTiming) {
3515
3711
  console.warn(
3516
- `[StormcloudVideoPlayer] IMA preload failed for ad: ${url}`,
3712
+ `[StormcloudVideoPlayer] Preload failed for ${vastTagUrl}:`,
3517
3713
  error
3518
3714
  );
3519
3715
  }
3520
- }).finally(() => {
3521
- this.preloadingAdUrls.delete(url);
3522
- });
3716
+ })
3717
+ );
3718
+ await Promise.all(preloadPromises);
3719
+ if (this.config.debugAdTiming) {
3720
+ console.log(
3721
+ `[StormcloudVideoPlayer] Background preloading completed for all ads`
3722
+ );
3723
+ }
3724
+ }
3725
+ async preloadSingleAd(vastTagUrl) {
3726
+ if (!vastTagUrl) return;
3727
+ try {
3728
+ if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
3729
+ if (!this.preloadingAdUrls.has(vastTagUrl)) {
3730
+ if (this.config.debugAdTiming) {
3731
+ console.log(
3732
+ `[StormcloudVideoPlayer] Preloading VAST: ${vastTagUrl}`
3733
+ );
3734
+ }
3735
+ this.preloadingAdUrls.add(vastTagUrl);
3736
+ await this.ima.preloadAds(vastTagUrl).then(() => {
3737
+ if (this.config.debugAdTiming) {
3738
+ console.log(
3739
+ `[StormcloudVideoPlayer] IMA VAST preload complete: ${vastTagUrl}`
3740
+ );
3741
+ }
3742
+ }).catch((error) => {
3743
+ if (this.config.debugAdTiming) {
3744
+ console.warn(
3745
+ `[StormcloudVideoPlayer] IMA VAST preload failed: ${vastTagUrl}`,
3746
+ error
3747
+ );
3748
+ }
3749
+ }).finally(() => {
3750
+ this.preloadingAdUrls.delete(vastTagUrl);
3751
+ });
3752
+ }
3753
+ }
3754
+ let mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3755
+ if (!mediaUrls) {
3756
+ if (this.config.debugAdTiming) {
3757
+ console.log(
3758
+ `[StormcloudVideoPlayer] Fetching and parsing VAST to extract media URLs: ${vastTagUrl}`
3759
+ );
3760
+ }
3761
+ mediaUrls = await this.fetchAndParseVastXml(vastTagUrl);
3762
+ if (mediaUrls.length > 0) {
3763
+ this.vastToMediaUrlMap.set(vastTagUrl, mediaUrls);
3764
+ }
3765
+ }
3766
+ if (mediaUrls && mediaUrls.length > 0) {
3767
+ const primaryMediaUrl = mediaUrls[0];
3768
+ if (primaryMediaUrl && !this.preloadedMediaUrls.has(primaryMediaUrl)) {
3769
+ await this.preloadMediaFile(primaryMediaUrl);
3770
+ }
3771
+ }
3772
+ } catch (error) {
3773
+ if (this.config.debugAdTiming) {
3774
+ console.warn(
3775
+ `[StormcloudVideoPlayer] Failed to preload ad: ${vastTagUrl}`,
3776
+ error
3777
+ );
3778
+ }
3779
+ }
3780
+ }
3781
+ findNextPreloadedAd() {
3782
+ var _a, _b, _c;
3783
+ for (let i = 0; i < this.adPodQueue.length; i++) {
3784
+ const vastTagUrl = this.adPodQueue[i];
3785
+ if (!vastTagUrl) continue;
3786
+ const hasImaPreload = (_c = (_b = (_a = this.ima).hasPreloadedAd) == null ? void 0 : _b.call(_a, vastTagUrl)) != null ? _c : false;
3787
+ const mediaUrls = this.vastToMediaUrlMap.get(vastTagUrl);
3788
+ const hasMediaPreload = mediaUrls && mediaUrls.length > 0 ? this.preloadedMediaUrls.has(mediaUrls[0]) : false;
3789
+ if (hasImaPreload || hasMediaPreload) {
3790
+ if (this.config.debugAdTiming) {
3791
+ console.log(
3792
+ `[StormcloudVideoPlayer] Found preloaded ad at index ${i}: ${vastTagUrl}`,
3793
+ { hasImaPreload, hasMediaPreload }
3794
+ );
3795
+ }
3796
+ this.adPodQueue.splice(0, i + 1);
3797
+ return vastTagUrl;
3798
+ }
3523
3799
  }
3800
+ if (this.config.debugAdTiming) {
3801
+ console.log(
3802
+ "[StormcloudVideoPlayer] No preloaded ads found in queue"
3803
+ );
3804
+ }
3805
+ return void 0;
3524
3806
  }
3525
3807
  getRemainingAdMs() {
3526
3808
  if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null)
@@ -3690,6 +3972,9 @@ var StormcloudVideoPlayer = class {
3690
3972
  (_b = this.ima) == null ? void 0 : _b.destroy();
3691
3973
  this.releaseAdHoldState();
3692
3974
  this.preloadingAdUrls.clear();
3975
+ this.vastToMediaUrlMap.clear();
3976
+ this.preloadedMediaUrls.clear();
3977
+ this.preloadingMediaUrls.clear();
3693
3978
  this.adPodAllUrls = [];
3694
3979
  }
3695
3980
  };