stormcloud-video-player 0.2.26 → 0.2.28

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.d.cts CHANGED
@@ -181,6 +181,7 @@ declare class StormcloudVideoPlayer {
181
181
  toggleFullscreen(): Promise<void>;
182
182
  isMuted(): boolean;
183
183
  setMuted(muted: boolean): void;
184
+ setVolume(volume: number): void;
184
185
  isFullscreen(): boolean;
185
186
  isLive(): boolean;
186
187
  get videoElement(): HTMLVideoElement;
package/lib/index.d.ts CHANGED
@@ -181,6 +181,7 @@ declare class StormcloudVideoPlayer {
181
181
  toggleFullscreen(): Promise<void>;
182
182
  isMuted(): boolean;
183
183
  setMuted(muted: boolean): void;
184
+ setVolume(volume: number): void;
184
185
  isFullscreen(): boolean;
185
186
  isLive(): boolean;
186
187
  get videoElement(): HTMLVideoElement;
package/lib/index.js CHANGED
@@ -201,6 +201,7 @@ function createImaController(video, options) {
201
201
  const listeners = /* @__PURE__ */ new Map();
202
202
  const preloadedVast = /* @__PURE__ */ new Map();
203
203
  const preloadingVast = /* @__PURE__ */ new Map();
204
+ let adVideoElement;
204
205
  function setAdPlayingFlag(isPlaying) {
205
206
  if (isPlaying) {
206
207
  video.dataset.stormcloudAdPlaying = "true";
@@ -208,6 +209,24 @@ function createImaController(video, options) {
208
209
  delete video.dataset.stormcloudAdPlaying;
209
210
  }
210
211
  }
212
+ function createAdVideoElement() {
213
+ const adVideo = document.createElement("video");
214
+ adVideo.style.position = "absolute";
215
+ adVideo.style.top = "0";
216
+ adVideo.style.left = "0";
217
+ adVideo.style.width = "100%";
218
+ adVideo.style.height = "100%";
219
+ adVideo.style.objectFit = "contain";
220
+ adVideo.style.backgroundColor = "#000";
221
+ adVideo.style.zIndex = "15";
222
+ adVideo.playsInline = true;
223
+ adVideo.muted = false;
224
+ adVideo.volume = originalMutedState ? 0 : originalVolume;
225
+ console.log(
226
+ `[IMA] Created dedicated ad video element with volume: ${adVideo.volume}, muted: ${adVideo.muted}`
227
+ );
228
+ return adVideo;
229
+ }
211
230
  function emit(event, payload) {
212
231
  const set = listeners.get(event);
213
232
  if (!set) return;
@@ -374,12 +393,22 @@ function createImaController(video, options) {
374
393
  const google = window.google;
375
394
  ensurePlaceholderContainer();
376
395
  if (!adDisplayContainer && adContainerEl) {
396
+ if (!adVideoElement) {
397
+ adVideoElement = createAdVideoElement();
398
+ adContainerEl.appendChild(adVideoElement);
399
+ console.log(
400
+ "[IMA] Dedicated ad video element added to container"
401
+ );
402
+ }
377
403
  adDisplayContainer = new google.ima.AdDisplayContainer(
378
404
  adContainerEl,
379
- video
405
+ adVideoElement
380
406
  );
381
407
  try {
382
408
  (_a = adDisplayContainer.initialize) == null ? void 0 : _a.call(adDisplayContainer);
409
+ console.log(
410
+ "[IMA] AdDisplayContainer initialized with dedicated ad video"
411
+ );
383
412
  } catch {
384
413
  }
385
414
  }
@@ -448,13 +477,22 @@ function createImaController(video, options) {
448
477
  }
449
478
  video.parentElement.appendChild(container);
450
479
  adContainerEl = container;
480
+ if (!adVideoElement) {
481
+ adVideoElement = createAdVideoElement();
482
+ adContainerEl.appendChild(adVideoElement);
483
+ console.log(
484
+ "[IMA] Dedicated ad video element created and added to container"
485
+ );
486
+ }
451
487
  adDisplayContainer = new google.ima.AdDisplayContainer(
452
488
  container,
453
- video
489
+ adVideoElement
454
490
  );
455
491
  try {
456
492
  adDisplayContainer.initialize();
457
- console.log("[IMA] Ad display container initialized");
493
+ console.log(
494
+ "[IMA] Ad display container initialized with dedicated ad video"
495
+ );
458
496
  } catch (error) {
459
497
  console.warn(
460
498
  "[IMA] Failed to initialize ad display container:",
@@ -497,11 +535,10 @@ function createImaController(video, options) {
497
535
  console.error("[IMA] Ad error:", errorEvent.getError());
498
536
  destroyAdsManager();
499
537
  adPlaying = false;
500
- const previousMutedState = video.muted;
501
- video.muted = originalMutedState;
538
+ video.style.visibility = "visible";
502
539
  setAdPlayingFlag(false);
503
540
  console.log(
504
- `[IMA] Restored mute state after ad error: ${previousMutedState} -> ${originalMutedState}`
541
+ "[IMA] Restored main video visibility after ad error"
505
542
  );
506
543
  if (adContainerEl) {
507
544
  adContainerEl.style.pointerEvents = "none";
@@ -545,18 +582,17 @@ function createImaController(video, options) {
545
582
  AdEvent.CONTENT_PAUSE_REQUESTED,
546
583
  () => {
547
584
  console.log(
548
- "[IMA] Content pause requested - FORCE MUTING main video"
585
+ "[IMA] Content pause requested - hiding main video, showing ad video"
549
586
  );
550
587
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
551
588
  video.pause();
552
- console.log("[IMA] Video paused (VOD mode)");
589
+ console.log("[IMA] Content video paused (VOD mode)");
553
590
  } else {
554
591
  console.log(
555
- "[IMA] Video continues playing but muted (Live mode)"
592
+ "[IMA] Content video continues playing in background (Live mode)"
556
593
  );
557
594
  }
558
- video.muted = true;
559
- video.volume = 0;
595
+ video.style.visibility = "hidden";
560
596
  adPlaying = true;
561
597
  setAdPlayingFlag(true);
562
598
  emit("content_pause");
@@ -564,36 +600,46 @@ function createImaController(video, options) {
564
600
  );
565
601
  adsManager.addEventListener(AdEvent.STARTED, () => {
566
602
  console.log(
567
- "[IMA] Ad started playing - FORCE MUTING main video"
603
+ "[IMA] Ad started playing - showing dedicated ad video"
568
604
  );
569
605
  setAdPlayingFlag(true);
570
- video.muted = true;
571
- video.volume = 0;
606
+ video.style.visibility = "hidden";
607
+ if (adVideoElement) {
608
+ adVideoElement.volume = originalMutedState ? 0 : originalVolume;
609
+ adVideoElement.muted = originalMutedState;
610
+ console.log(
611
+ `[IMA] Ad video volume: ${adVideoElement.volume}, muted: ${adVideoElement.muted}`
612
+ );
613
+ }
572
614
  if (adContainerEl) {
573
615
  adContainerEl.style.pointerEvents = "auto";
574
616
  adContainerEl.style.display = "flex";
575
617
  console.log(
576
- "[IMA] Ad container visibility set to flex with pointer events enabled"
618
+ "[IMA] Ad container visible with dedicated ad video"
577
619
  );
578
620
  }
579
621
  });
580
622
  adsManager.addEventListener(
581
623
  AdEvent.CONTENT_RESUME_REQUESTED,
582
624
  () => {
583
- console.log("[IMA] Content resume requested");
625
+ console.log(
626
+ "[IMA] Content resume requested - showing main video"
627
+ );
584
628
  adPlaying = false;
585
629
  setAdPlayingFlag(false);
630
+ video.style.visibility = "visible";
586
631
  emit("content_resume");
587
632
  setTimeout(() => {
588
633
  const stillInPod = video.dataset.stormcloudAdPlaying === "true";
589
634
  if (stillInPod) {
590
635
  console.log(
591
- "[IMA] Still in ad pod - keeping ad container visible (black screen)"
636
+ "[IMA] Still in ad pod - keeping ad video visible"
592
637
  );
593
638
  if (adContainerEl) {
594
639
  adContainerEl.style.display = "flex";
595
640
  adContainerEl.style.pointerEvents = "auto";
596
641
  }
642
+ video.style.visibility = "hidden";
597
643
  }
598
644
  }, 50);
599
645
  }
@@ -613,7 +659,7 @@ function createImaController(video, options) {
613
659
  } catch (e) {
614
660
  console.error("[IMA] Error setting up ads manager:", e);
615
661
  adPlaying = false;
616
- video.muted = originalMutedState;
662
+ video.style.visibility = "visible";
617
663
  setAdPlayingFlag(false);
618
664
  if (adContainerEl) {
619
665
  adContainerEl.style.pointerEvents = "none";
@@ -644,11 +690,10 @@ function createImaController(video, options) {
644
690
  (adErrorEvent) => {
645
691
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
646
692
  adPlaying = false;
647
- const previousMutedState = video.muted;
648
- video.muted = originalMutedState;
693
+ video.style.visibility = "visible";
649
694
  setAdPlayingFlag(false);
650
695
  console.log(
651
- `[IMA] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
696
+ "[IMA] Restored main video visibility after loader error"
652
697
  );
653
698
  if (adContainerEl) {
654
699
  adContainerEl.style.pointerEvents = "none";
@@ -728,11 +773,18 @@ function createImaController(video, options) {
728
773
  adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
729
774
  adPlaying = true;
730
775
  const adVolume = originalMutedState ? 0 : originalVolume;
776
+ if (adVideoElement) {
777
+ adVideoElement.volume = adVolume;
778
+ adVideoElement.muted = originalMutedState;
779
+ console.log(
780
+ `[IMA] Set dedicated ad video volume to ${adVolume}, muted: ${originalMutedState}`
781
+ );
782
+ }
731
783
  try {
732
784
  adsManager.setVolume(adVolume);
733
- console.log(`[IMA] Set ad volume to ${adVolume}`);
785
+ console.log(`[IMA] Set IMA manager volume to ${adVolume}`);
734
786
  } catch (error) {
735
- console.warn("[IMA] Failed to set ad volume:", error);
787
+ console.warn("[IMA] Failed to set IMA manager volume:", error);
736
788
  }
737
789
  console.log("[IMA] Starting ad playback");
738
790
  adsManager.start();
@@ -753,6 +805,7 @@ function createImaController(video, options) {
753
805
  console.log("[IMA] Stopping ad playback");
754
806
  adPlaying = false;
755
807
  setAdPlayingFlag(false);
808
+ video.style.visibility = "visible";
756
809
  if (adContainerEl) {
757
810
  adContainerEl.style.pointerEvents = "none";
758
811
  adContainerEl.style.display = "none";
@@ -768,8 +821,7 @@ function createImaController(video, options) {
768
821
  var _a;
769
822
  destroyAdsManager();
770
823
  adPlaying = false;
771
- video.muted = originalMutedState;
772
- video.volume = originalVolume;
824
+ video.style.visibility = "visible";
773
825
  setAdPlayingFlag(false);
774
826
  if (adContainerEl) {
775
827
  adContainerEl.style.pointerEvents = "none";
@@ -785,6 +837,7 @@ function createImaController(video, options) {
785
837
  adContainerEl = void 0;
786
838
  adDisplayContainer = void 0;
787
839
  adsLoader = void 0;
840
+ adVideoElement = void 0;
788
841
  preloadedVast.clear();
789
842
  preloadingVast.clear();
790
843
  },
@@ -829,15 +882,26 @@ function createImaController(video, options) {
829
882
  return originalVolume;
830
883
  },
831
884
  setAdVolume(volume) {
885
+ const clampedVolume = Math.max(0, Math.min(1, volume));
886
+ if (adVideoElement && adPlaying) {
887
+ adVideoElement.volume = clampedVolume;
888
+ adVideoElement.muted = clampedVolume === 0;
889
+ console.log(
890
+ `[IMA] Set dedicated ad video volume to ${clampedVolume}, muted: ${clampedVolume === 0}`
891
+ );
892
+ }
832
893
  if (adsManager && adPlaying) {
833
894
  try {
834
- adsManager.setVolume(Math.max(0, Math.min(1, volume)));
895
+ adsManager.setVolume(clampedVolume);
835
896
  } catch (error) {
836
- console.warn("[IMA] Failed to set ad volume:", error);
897
+ console.warn("[IMA] Failed to set IMA manager volume:", error);
837
898
  }
838
899
  }
839
900
  },
840
901
  getAdVolume() {
902
+ if (adVideoElement && adPlaying) {
903
+ return adVideoElement.volume;
904
+ }
841
905
  if (adsManager && adPlaying) {
842
906
  try {
843
907
  return adsManager.getVolume();
@@ -3454,11 +3518,6 @@ var StormcloudVideoPlayer = class {
3454
3518
  }
3455
3519
  return false;
3456
3520
  }
3457
- if (this.config.debugAdTiming) {
3458
- console.log(
3459
- `[StormcloudVideoPlayer] isMuted() no ad playing: video.muted=${this.video.muted}`
3460
- );
3461
- }
3462
3521
  return this.video.muted;
3463
3522
  }
3464
3523
  setMuted(muted) {
@@ -3488,6 +3547,26 @@ var StormcloudVideoPlayer = class {
3488
3547
  console.log("[StormcloudVideoPlayer] setMuted called:", muted);
3489
3548
  }
3490
3549
  }
3550
+ setVolume(volume) {
3551
+ const clampedVolume = Math.max(0, Math.min(1, volume));
3552
+ const adPlaying = this.ima.isAdPlaying();
3553
+ if (adPlaying) {
3554
+ this.ima.setAdVolume(clampedVolume);
3555
+ this.ima.updateOriginalMutedState(clampedVolume === 0, clampedVolume);
3556
+ if (this.config.debugAdTiming) {
3557
+ console.log("[StormcloudVideoPlayer] setVolume applied during ad", {
3558
+ volume: clampedVolume
3559
+ });
3560
+ }
3561
+ } else {
3562
+ this.video.volume = clampedVolume;
3563
+ this.video.muted = clampedVolume === 0;
3564
+ this.ima.updateOriginalMutedState(clampedVolume === 0, clampedVolume);
3565
+ if (this.config.debugAdTiming) {
3566
+ console.log("[StormcloudVideoPlayer] setVolume called:", clampedVolume);
3567
+ }
3568
+ }
3569
+ }
3491
3570
  isFullscreen() {
3492
3571
  return !!document.fullscreenElement;
3493
3572
  }
@@ -3668,10 +3747,9 @@ var StormcloudVideoPlayerComponent = React.memo(
3668
3747
  }
3669
3748
  };
3670
3749
  const handleVolumeChange = (newVolume) => {
3671
- if (videoRef.current && isFinite(newVolume)) {
3750
+ if (playerRef.current && isFinite(newVolume)) {
3672
3751
  const clampedVolume = Math.max(0, Math.min(1, newVolume));
3673
- videoRef.current.volume = clampedVolume;
3674
- videoRef.current.muted = clampedVolume === 0;
3752
+ playerRef.current.setVolume(clampedVolume);
3675
3753
  }
3676
3754
  };
3677
3755
  const handlePlaybackRateChange = (rate) => {
@@ -3773,7 +3851,7 @@ var StormcloudVideoPlayerComponent = React.memo(
3773
3851
  if (autoplay !== void 0 && playerRef.current.videoElement) {
3774
3852
  playerRef.current.videoElement.autoplay = autoplay;
3775
3853
  }
3776
- if (muted !== void 0) {
3854
+ if (muted !== void 0 && !playerRef.current.isShowingAds()) {
3777
3855
  playerRef.current.setMuted(muted);
3778
3856
  }
3779
3857
  } catch (error) {