stormcloud-video-player 0.2.16 → 0.2.18

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.
@@ -200,6 +200,13 @@ function createImaController(video, options) {
200
200
  let adPlaying = false;
201
201
  let originalMutedState = false;
202
202
  const listeners = /* @__PURE__ */ new Map();
203
+ function setAdPlayingFlag(isPlaying) {
204
+ if (isPlaying) {
205
+ video.dataset.stormcloudAdPlaying = "true";
206
+ } else {
207
+ delete video.dataset.stormcloudAdPlaying;
208
+ }
209
+ }
203
210
  function emit(event, payload) {
204
211
  const set = listeners.get(event);
205
212
  if (!set) return;
@@ -284,6 +291,14 @@ function createImaController(video, options) {
284
291
  function makeAdsRequest(google, vastTagUrl) {
285
292
  const adsRequest = new google.ima.AdsRequest();
286
293
  adsRequest.adTagUrl = vastTagUrl;
294
+ const videoWidth = video.offsetWidth || video.clientWidth || 640;
295
+ const videoHeight = video.offsetHeight || video.clientHeight || 360;
296
+ adsRequest.linearAdSlotWidth = videoWidth;
297
+ adsRequest.linearAdSlotHeight = videoHeight;
298
+ adsRequest.nonLinearAdSlotWidth = videoWidth;
299
+ adsRequest.nonLinearAdSlotHeight = videoHeight;
300
+ adsRequest.vastLoadTimeout = 5e3;
301
+ console.log(`[IMA] Ads request dimensions: ${videoWidth}x${videoHeight}`);
287
302
  adsLoader.requestAds(adsRequest);
288
303
  }
289
304
  function destroyAdsManager() {
@@ -330,6 +345,18 @@ function createImaController(video, options) {
330
345
  },
331
346
  async requestAds(vastTagUrl) {
332
347
  console.log("[IMA] Requesting ads:", vastTagUrl);
348
+ if (!vastTagUrl || vastTagUrl.trim() === "") {
349
+ const error = new Error("VAST tag URL is empty or undefined");
350
+ console.warn("[IMA]", error.message);
351
+ return Promise.reject(error);
352
+ }
353
+ try {
354
+ new URL(vastTagUrl);
355
+ } catch (e) {
356
+ const error = new Error(`Invalid VAST tag URL format: ${vastTagUrl}`);
357
+ console.warn("[IMA]", error.message);
358
+ return Promise.reject(error);
359
+ }
333
360
  if (adPlaying) {
334
361
  console.warn(
335
362
  "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
@@ -391,6 +418,18 @@ function createImaController(video, options) {
391
418
  );
392
419
  }
393
420
  }
421
+ const videoWidth = video.offsetWidth || video.clientWidth;
422
+ const videoHeight = video.offsetHeight || video.clientHeight;
423
+ if (!videoWidth || !videoHeight || videoWidth === 0 || videoHeight === 0) {
424
+ const error = new Error(
425
+ `Invalid video dimensions: ${videoWidth}x${videoHeight}. Cannot initialize ads.`
426
+ );
427
+ console.warn("[IMA]", error.message);
428
+ currentReject == null ? void 0 : currentReject(error);
429
+ adsLoadedReject = void 0;
430
+ adsLoadedResolve = void 0;
431
+ return Promise.reject(error);
432
+ }
394
433
  if (!adsLoader) {
395
434
  console.log("[IMA] Creating ads loader");
396
435
  const adsLoaderCls = new google.ima.AdsLoader(adDisplayContainer);
@@ -410,7 +449,12 @@ function createImaController(video, options) {
410
449
  console.error("[IMA] Ad error:", errorEvent.getError());
411
450
  destroyAdsManager();
412
451
  adPlaying = false;
452
+ const previousMutedState = video.muted;
413
453
  video.muted = originalMutedState;
454
+ setAdPlayingFlag(false);
455
+ console.log(
456
+ `[IMA] Restored mute state after ad error: ${previousMutedState} -> ${originalMutedState}`
457
+ );
414
458
  if (adContainerEl) {
415
459
  adContainerEl.style.pointerEvents = "none";
416
460
  adContainerEl.style.display = "none";
@@ -439,7 +483,9 @@ function createImaController(video, options) {
439
483
  emit("ad_error");
440
484
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
441
485
  if (video.paused) {
442
- console.log("[IMA] Resuming paused video after ad error");
486
+ console.log(
487
+ "[IMA] Resuming paused video after ad error"
488
+ );
443
489
  (_a = video.play()) == null ? void 0 : _a.catch(() => {
444
490
  });
445
491
  }
@@ -461,11 +507,13 @@ function createImaController(video, options) {
461
507
  }
462
508
  video.muted = true;
463
509
  adPlaying = true;
510
+ setAdPlayingFlag(true);
464
511
  emit("content_pause");
465
512
  }
466
513
  );
467
514
  adsManager.addEventListener(AdEvent.STARTED, () => {
468
515
  console.log("[IMA] Ad started playing");
516
+ setAdPlayingFlag(true);
469
517
  if (adContainerEl) {
470
518
  adContainerEl.style.pointerEvents = "auto";
471
519
  adContainerEl.style.display = "flex";
@@ -481,6 +529,7 @@ function createImaController(video, options) {
481
529
  console.log("[IMA] Content resume requested");
482
530
  adPlaying = false;
483
531
  video.muted = originalMutedState;
532
+ setAdPlayingFlag(false);
484
533
  if (adContainerEl) {
485
534
  adContainerEl.style.pointerEvents = "none";
486
535
  adContainerEl.style.display = "none";
@@ -504,6 +553,7 @@ function createImaController(video, options) {
504
553
  console.log("[IMA] All ads completed");
505
554
  adPlaying = false;
506
555
  video.muted = originalMutedState;
556
+ setAdPlayingFlag(false);
507
557
  if (adContainerEl) {
508
558
  adContainerEl.style.pointerEvents = "none";
509
559
  adContainerEl.style.display = "none";
@@ -534,6 +584,7 @@ function createImaController(video, options) {
534
584
  console.error("[IMA] Error setting up ads manager:", e);
535
585
  adPlaying = false;
536
586
  video.muted = originalMutedState;
587
+ setAdPlayingFlag(false);
537
588
  if (adContainerEl) {
538
589
  adContainerEl.style.pointerEvents = "none";
539
590
  adContainerEl.style.display = "none";
@@ -541,7 +592,9 @@ function createImaController(video, options) {
541
592
  }
542
593
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
543
594
  if (video.paused) {
544
- console.log("[IMA] Resuming paused video after setup error");
595
+ console.log(
596
+ "[IMA] Resuming paused video after setup error"
597
+ );
545
598
  video.play().catch(() => {
546
599
  });
547
600
  }
@@ -561,7 +614,12 @@ function createImaController(video, options) {
561
614
  (adErrorEvent) => {
562
615
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
563
616
  adPlaying = false;
617
+ const previousMutedState = video.muted;
564
618
  video.muted = originalMutedState;
619
+ setAdPlayingFlag(false);
620
+ console.log(
621
+ `[IMA] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
622
+ );
565
623
  if (adContainerEl) {
566
624
  adContainerEl.style.pointerEvents = "none";
567
625
  adContainerEl.style.display = "none";
@@ -613,12 +671,20 @@ function createImaController(video, options) {
613
671
  console.log(`[IMA] Initializing ads manager (${width}x${height})`);
614
672
  adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
615
673
  adPlaying = true;
674
+ const adVolume = originalMutedState ? 0 : video.volume;
675
+ try {
676
+ adsManager.setVolume(adVolume);
677
+ console.log(`[IMA] Set ad volume to ${adVolume}`);
678
+ } catch (error) {
679
+ console.warn("[IMA] Failed to set ad volume:", error);
680
+ }
616
681
  console.log("[IMA] Starting ad playback");
617
682
  adsManager.start();
618
683
  return Promise.resolve();
619
684
  } catch (error) {
620
685
  console.error("[IMA] Error starting ad playback:", error);
621
686
  adPlaying = false;
687
+ setAdPlayingFlag(false);
622
688
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
623
689
  (_b = video.play()) == null ? void 0 : _b.catch(() => {
624
690
  });
@@ -630,6 +696,7 @@ function createImaController(video, options) {
630
696
  var _a;
631
697
  adPlaying = false;
632
698
  video.muted = originalMutedState;
699
+ setAdPlayingFlag(false);
633
700
  if (adContainerEl) {
634
701
  adContainerEl.style.pointerEvents = "none";
635
702
  adContainerEl.style.display = "none";
@@ -653,6 +720,7 @@ function createImaController(video, options) {
653
720
  destroyAdsManager();
654
721
  adPlaying = false;
655
722
  video.muted = originalMutedState;
723
+ setAdPlayingFlag(false);
656
724
  if (adContainerEl) {
657
725
  adContainerEl.style.pointerEvents = "none";
658
726
  adContainerEl.style.display = "none";
@@ -695,6 +763,9 @@ function createImaController(video, options) {
695
763
  (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
696
764
  },
697
765
  updateOriginalMutedState(muted) {
766
+ console.log(
767
+ `[IMA] updateOriginalMutedState called: ${originalMutedState} -> ${muted}`
768
+ );
698
769
  originalMutedState = muted;
699
770
  },
700
771
  getOriginalMutedState() {
@@ -751,7 +822,10 @@ function createHlsAdPlayer(contentVideo, options) {
751
822
  try {
752
823
  fn(payload);
753
824
  } catch (error) {
754
- console.warn(`[HlsAdPlayer] Error in event listener for ${event}:`, error);
825
+ console.warn(
826
+ `[HlsAdPlayer] Error in event listener for ${event}:`,
827
+ error
828
+ );
755
829
  }
756
830
  }
757
831
  }
@@ -814,7 +888,9 @@ function createHlsAdPlayer(contentVideo, options) {
814
888
  }
815
889
  const mainQuality = getMainStreamQuality();
816
890
  if (!mainQuality) {
817
- console.log("[HlsAdPlayer] No main stream quality info, using first media file");
891
+ console.log(
892
+ "[HlsAdPlayer] No main stream quality info, using first media file"
893
+ );
818
894
  return firstFile;
819
895
  }
820
896
  console.log("[HlsAdPlayer] Main stream quality:", mainQuality);
@@ -850,7 +926,10 @@ function createHlsAdPlayer(contentVideo, options) {
850
926
  const xmlDoc = parser.parseFromString(xmlString, "text/xml");
851
927
  const parserError = xmlDoc.querySelector("parsererror");
852
928
  if (parserError) {
853
- console.error("[HlsAdPlayer] XML parsing error (malformed VAST XML):", parserError.textContent);
929
+ console.error(
930
+ "[HlsAdPlayer] XML parsing error (malformed VAST XML):",
931
+ parserError.textContent
932
+ );
854
933
  return null;
855
934
  }
856
935
  const adElement = xmlDoc.querySelector("Ad");
@@ -866,17 +945,23 @@ function createHlsAdPlayer(contentVideo, options) {
866
945
  const duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
867
946
  const mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
868
947
  const mediaFiles = [];
869
- console.log(`[HlsAdPlayer] Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`);
948
+ console.log(
949
+ `[HlsAdPlayer] Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`
950
+ );
870
951
  mediaFileElements.forEach((mf, index) => {
871
952
  var _a2;
872
953
  const type = mf.getAttribute("type") || "";
873
954
  const url = ((_a2 = mf.textContent) == null ? void 0 : _a2.trim()) || "";
874
955
  const width = mf.getAttribute("width") || "";
875
956
  const height = mf.getAttribute("height") || "";
876
- console.log(`[HlsAdPlayer] MediaFile ${index}: type="${type}", url="${url}", width="${width}", height="${height}"`);
957
+ console.log(
958
+ `[HlsAdPlayer] MediaFile ${index}: type="${type}", url="${url}", width="${width}", height="${height}"`
959
+ );
877
960
  if (type === "application/x-mpegURL" || type.includes("m3u8")) {
878
961
  if (!url) {
879
- console.warn(`[HlsAdPlayer] MediaFile ${index} has HLS type but empty URL`);
962
+ console.warn(
963
+ `[HlsAdPlayer] MediaFile ${index} has HLS type but empty URL`
964
+ );
880
965
  return;
881
966
  }
882
967
  const bitrateAttr = mf.getAttribute("bitrate");
@@ -890,12 +975,16 @@ function createHlsAdPlayer(contentVideo, options) {
890
975
  });
891
976
  console.log(`[HlsAdPlayer] Added HLS MediaFile: ${url}`);
892
977
  } else {
893
- console.log(`[HlsAdPlayer] MediaFile ${index} ignored (type="${type}" is not HLS)`);
978
+ console.log(
979
+ `[HlsAdPlayer] MediaFile ${index} ignored (type="${type}" is not HLS)`
980
+ );
894
981
  }
895
982
  });
896
983
  if (mediaFiles.length === 0) {
897
984
  if (isNoAdAvailable) {
898
- console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
985
+ console.warn(
986
+ "[HlsAdPlayer] No ads available (VAST response indicates no ads)"
987
+ );
899
988
  } else {
900
989
  console.warn("[HlsAdPlayer] No HLS media files found in VAST XML");
901
990
  }
@@ -958,6 +1047,10 @@ function createHlsAdPlayer(contentVideo, options) {
958
1047
  video.style.backgroundColor = "#000";
959
1048
  video.playsInline = true;
960
1049
  video.muted = false;
1050
+ video.volume = 1;
1051
+ console.log(
1052
+ `[HlsAdPlayer] Created ad video element with volume ${video.volume}`
1053
+ );
961
1054
  return video;
962
1055
  }
963
1056
  function setupAdEventListeners() {
@@ -1017,10 +1110,22 @@ function createHlsAdPlayer(contentVideo, options) {
1017
1110
  }
1018
1111
  });
1019
1112
  }
1113
+ function setAdPlayingFlag(isPlaying) {
1114
+ if (isPlaying) {
1115
+ contentVideo.dataset.stormcloudAdPlaying = "true";
1116
+ } else {
1117
+ delete contentVideo.dataset.stormcloudAdPlaying;
1118
+ }
1119
+ }
1020
1120
  function handleAdComplete() {
1021
1121
  console.log("[HlsAdPlayer] Handling ad completion");
1022
1122
  adPlaying = false;
1123
+ setAdPlayingFlag(false);
1124
+ const previousMutedState = contentVideo.muted;
1023
1125
  contentVideo.muted = originalMutedState;
1126
+ console.log(
1127
+ `[HlsAdPlayer] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
1128
+ );
1024
1129
  if (adContainerEl) {
1025
1130
  adContainerEl.style.display = "none";
1026
1131
  adContainerEl.style.pointerEvents = "none";
@@ -1038,7 +1143,12 @@ function createHlsAdPlayer(contentVideo, options) {
1038
1143
  function handleAdError() {
1039
1144
  console.log("[HlsAdPlayer] Handling ad error");
1040
1145
  adPlaying = false;
1146
+ setAdPlayingFlag(false);
1147
+ const previousMutedState = contentVideo.muted;
1041
1148
  contentVideo.muted = originalMutedState;
1149
+ console.log(
1150
+ `[HlsAdPlayer] Restored mute state: ${previousMutedState} -> ${originalMutedState}`
1151
+ );
1042
1152
  if (adContainerEl) {
1043
1153
  adContainerEl.style.display = "none";
1044
1154
  adContainerEl.style.pointerEvents = "none";
@@ -1075,7 +1185,9 @@ function createHlsAdPlayer(contentVideo, options) {
1075
1185
  async requestAds(vastTagUrl) {
1076
1186
  console.log("[HlsAdPlayer] Requesting ads:", vastTagUrl);
1077
1187
  if (adPlaying) {
1078
- console.warn("[HlsAdPlayer] Cannot request new ads while an ad is playing");
1188
+ console.warn(
1189
+ "[HlsAdPlayer] Cannot request new ads while an ad is playing"
1190
+ );
1079
1191
  return Promise.reject(new Error("Ad already playing"));
1080
1192
  }
1081
1193
  try {
@@ -1086,14 +1198,20 @@ function createHlsAdPlayer(contentVideo, options) {
1086
1198
  }
1087
1199
  const vastXml = await response.text();
1088
1200
  console.log("[HlsAdPlayer] VAST XML received");
1089
- console.log("[HlsAdPlayer] VAST XML content (first 2000 chars):", vastXml.substring(0, 2e3));
1201
+ console.log(
1202
+ "[HlsAdPlayer] VAST XML content (first 2000 chars):",
1203
+ vastXml.substring(0, 2e3)
1204
+ );
1090
1205
  const ad = parseVastXml(vastXml);
1091
1206
  if (!ad) {
1092
1207
  console.warn("[HlsAdPlayer] No ads available from VAST response");
1208
+ emit("ad_error");
1093
1209
  return Promise.resolve();
1094
1210
  }
1095
1211
  currentAd = ad;
1096
- console.log(`[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`);
1212
+ console.log(
1213
+ `[HlsAdPlayer] Ad parsed: ${ad.title}, duration: ${ad.duration}s`
1214
+ );
1097
1215
  fireTrackingPixels(ad.trackingUrls.impression);
1098
1216
  trackingFired.impression = true;
1099
1217
  return Promise.resolve();
@@ -1105,7 +1223,9 @@ function createHlsAdPlayer(contentVideo, options) {
1105
1223
  },
1106
1224
  async play() {
1107
1225
  if (!currentAd) {
1108
- console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
1226
+ console.warn(
1227
+ "[HlsAdPlayer] Cannot play: No ad loaded (no ads available)"
1228
+ );
1109
1229
  return Promise.reject(new Error("No ad loaded"));
1110
1230
  }
1111
1231
  console.log("[HlsAdPlayer] Starting ad playback");
@@ -1123,6 +1243,7 @@ function createHlsAdPlayer(contentVideo, options) {
1123
1243
  thirdQuartile: false,
1124
1244
  complete: false
1125
1245
  };
1246
+ const contentVolume = contentVideo.volume;
1126
1247
  if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
1127
1248
  contentVideo.pause();
1128
1249
  console.log("[HlsAdPlayer] Content paused (VOD mode)");
@@ -1131,6 +1252,15 @@ function createHlsAdPlayer(contentVideo, options) {
1131
1252
  }
1132
1253
  contentVideo.muted = true;
1133
1254
  adPlaying = true;
1255
+ setAdPlayingFlag(true);
1256
+ if (adVideoElement) {
1257
+ const adVolume = originalMutedState ? 0 : contentVolume;
1258
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1259
+ adVideoElement.muted = false;
1260
+ console.log(
1261
+ `[HlsAdPlayer] Set ad video volume to ${adVideoElement.volume}, muted: ${adVideoElement.muted}, originalMutedState: ${originalMutedState}, contentVolume: ${contentVolume}`
1262
+ );
1263
+ }
1134
1264
  if (adContainerEl) {
1135
1265
  adContainerEl.style.display = "flex";
1136
1266
  adContainerEl.style.pointerEvents = "auto";
@@ -1183,6 +1313,7 @@ function createHlsAdPlayer(contentVideo, options) {
1183
1313
  async stop() {
1184
1314
  console.log("[HlsAdPlayer] Stopping ad");
1185
1315
  adPlaying = false;
1316
+ setAdPlayingFlag(false);
1186
1317
  contentVideo.muted = originalMutedState;
1187
1318
  if (adContainerEl) {
1188
1319
  adContainerEl.style.display = "none";
@@ -1205,6 +1336,7 @@ function createHlsAdPlayer(contentVideo, options) {
1205
1336
  destroy() {
1206
1337
  console.log("[HlsAdPlayer] Destroying");
1207
1338
  adPlaying = false;
1339
+ setAdPlayingFlag(false);
1208
1340
  contentVideo.muted = originalMutedState;
1209
1341
  if (adHls) {
1210
1342
  adHls.destroy();
@@ -1246,6 +1378,9 @@ function createHlsAdPlayer(contentVideo, options) {
1246
1378
  (_a = listeners.get(event)) == null ? void 0 : _a.delete(listener);
1247
1379
  },
1248
1380
  updateOriginalMutedState(muted) {
1381
+ console.log(
1382
+ `[HlsAdPlayer] updateOriginalMutedState called: ${originalMutedState} -> ${muted}`
1383
+ );
1249
1384
  originalMutedState = muted;
1250
1385
  },
1251
1386
  getOriginalMutedState() {
@@ -1738,6 +1873,9 @@ var StormcloudVideoPlayer = class {
1738
1873
  this.isLiveStream = false;
1739
1874
  this.nativeHlsMode = false;
1740
1875
  this.videoSrcProtection = null;
1876
+ this.bufferedSegmentsCount = 0;
1877
+ this.shouldAutoplayAfterBuffering = false;
1878
+ this.hasInitialBufferCompleted = false;
1741
1879
  initializePolyfills();
1742
1880
  const browserOverrides = getBrowserConfigOverrides();
1743
1881
  this.config = { ...config, ...browserOverrides };
@@ -1824,14 +1962,22 @@ var StormcloudVideoPlayer = class {
1824
1962
  liveDurationInfinity: true,
1825
1963
  lowLatencyMode: !!this.config.lowLatencyMode,
1826
1964
  maxLiveSyncPlaybackRate: this.config.lowLatencyMode ? 1.5 : 1,
1827
- ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {}
1965
+ ...this.config.lowLatencyMode ? { liveSyncDuration: 2 } : {},
1966
+ maxBufferLength: 30,
1967
+ maxMaxBufferLength: 600,
1968
+ maxBufferSize: 60 * 1e3 * 1e3,
1969
+ maxBufferHole: 0.5,
1970
+ highBufferWatchdogPeriod: 2,
1971
+ nudgeOffset: 0.1,
1972
+ nudgeMaxRetry: 3,
1973
+ startPosition: -1
1828
1974
  });
1829
1975
  this.hls.on(import_hls2.default.Events.MEDIA_ATTACHED, () => {
1830
1976
  var _a2;
1831
1977
  (_a2 = this.hls) == null ? void 0 : _a2.loadSource(this.config.src);
1832
1978
  });
1833
1979
  this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, async (_, data) => {
1834
- var _a2, _b2, _c, _d;
1980
+ var _a2, _b2, _c, _d, _e;
1835
1981
  this.isLiveStream = (_c = (_b2 = (_a2 = this.hls) == null ? void 0 : _a2.levels) == null ? void 0 : _b2.some(
1836
1982
  (level) => {
1837
1983
  var _a3, _b3;
@@ -1849,9 +1995,51 @@ var StormcloudVideoPlayer = class {
1849
1995
  this.ima.destroy();
1850
1996
  this.ima = this.createAdPlayer(this.shouldContinueLiveStreamDuringAds());
1851
1997
  this.ima.initialize();
1852
- if (this.config.autoplay) {
1853
- await ((_d = this.video.play()) == null ? void 0 : _d.catch(() => {
1854
- }));
1998
+ this.bufferedSegmentsCount = 0;
1999
+ this.hasInitialBufferCompleted = false;
2000
+ this.shouldAutoplayAfterBuffering = !!this.config.autoplay;
2001
+ const minSegments = (_d = this.config.minSegmentsBeforePlay) != null ? _d : 2;
2002
+ if (this.config.debugAdTiming) {
2003
+ console.log(
2004
+ "[StormcloudVideoPlayer] Waiting for",
2005
+ minSegments,
2006
+ "segments to buffer before playback"
2007
+ );
2008
+ }
2009
+ if (minSegments === 0 || !this.config.autoplay) {
2010
+ this.hasInitialBufferCompleted = true;
2011
+ if (this.config.autoplay) {
2012
+ await ((_e = this.video.play()) == null ? void 0 : _e.catch(() => {
2013
+ }));
2014
+ }
2015
+ }
2016
+ });
2017
+ this.hls.on(import_hls2.default.Events.FRAG_BUFFERED, async (_evt, data) => {
2018
+ var _a2, _b2;
2019
+ if (this.hasInitialBufferCompleted) {
2020
+ return;
2021
+ }
2022
+ this.bufferedSegmentsCount++;
2023
+ const minSegments = (_a2 = this.config.minSegmentsBeforePlay) != null ? _a2 : 2;
2024
+ if (this.config.debugAdTiming) {
2025
+ console.log(
2026
+ `[StormcloudVideoPlayer] Buffered segment ${this.bufferedSegmentsCount}/${minSegments}`
2027
+ );
2028
+ }
2029
+ if (this.bufferedSegmentsCount >= minSegments) {
2030
+ this.hasInitialBufferCompleted = true;
2031
+ if (this.shouldAutoplayAfterBuffering) {
2032
+ if (this.config.debugAdTiming) {
2033
+ console.log(
2034
+ `[StormcloudVideoPlayer] Initial buffer complete (${this.bufferedSegmentsCount} segments). Starting playback.`
2035
+ );
2036
+ }
2037
+ await ((_b2 = this.video.play()) == null ? void 0 : _b2.catch((err) => {
2038
+ if (this.config.debugAdTiming) {
2039
+ console.warn("[StormcloudVideoPlayer] Autoplay failed:", err);
2040
+ }
2041
+ }));
2042
+ }
1855
2043
  }
1856
2044
  });
1857
2045
  this.hls.on(import_hls2.default.Events.FRAG_PARSING_METADATA, (_evt, data) => {
@@ -1947,6 +2135,7 @@ var StormcloudVideoPlayer = class {
1947
2135
  this.video.autoplay = !!this.config.autoplay;
1948
2136
  this.video.muted = !!this.config.muted;
1949
2137
  this.ima.initialize();
2138
+ this.ima.updateOriginalMutedState(this.video.muted);
1950
2139
  this.ima.on("all_ads_completed", () => {
1951
2140
  if (this.config.debugAdTiming) {
1952
2141
  console.log(
@@ -2731,7 +2920,23 @@ var StormcloudVideoPlayer = class {
2731
2920
  }
2732
2921
  return;
2733
2922
  }
2734
- this.ima.updateOriginalMutedState(this.video.muted);
2923
+ if (!this.showAds) {
2924
+ if (this.config.debugAdTiming) {
2925
+ console.log(
2926
+ `[StormcloudVideoPlayer] Capturing original state before ad request:`,
2927
+ {
2928
+ videoMuted: this.video.muted,
2929
+ videoVolume: this.video.volume,
2930
+ showAds: this.showAds
2931
+ }
2932
+ );
2933
+ }
2934
+ this.ima.updateOriginalMutedState(this.video.muted);
2935
+ } else if (this.config.debugAdTiming) {
2936
+ console.log(
2937
+ `[StormcloudVideoPlayer] Keeping existing original mute state (currently showing ads)`
2938
+ );
2939
+ }
2735
2940
  this.startAdFailsafeTimer();
2736
2941
  try {
2737
2942
  await this.ima.requestAds(vastTagUrl);
@@ -2786,11 +2991,12 @@ var StormcloudVideoPlayer = class {
2786
2991
  this.showAds = false;
2787
2992
  this.currentAdIndex = 0;
2788
2993
  this.totalAdsInBreak = 0;
2994
+ const currentMutedState = this.video.muted;
2789
2995
  const originalMutedState = this.ima.getOriginalMutedState();
2790
2996
  this.video.muted = originalMutedState;
2791
2997
  if (this.config.debugAdTiming) {
2792
2998
  console.log(
2793
- `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
2999
+ `[StormcloudVideoPlayer] Restored mute state: ${currentMutedState} -> ${originalMutedState}`
2794
3000
  );
2795
3001
  }
2796
3002
  if (this.video.paused) {
@@ -2882,6 +3088,7 @@ var StormcloudVideoPlayer = class {
2882
3088
  }
2883
3089
  } else {
2884
3090
  this.video.muted = !this.video.muted;
3091
+ this.ima.updateOriginalMutedState(this.video.muted);
2885
3092
  if (this.config.debugAdTiming) {
2886
3093
  console.log("[StormcloudVideoPlayer] Muted:", this.video.muted);
2887
3094
  }
@@ -2926,11 +3133,47 @@ var StormcloudVideoPlayer = class {
2926
3133
  }
2927
3134
  isMuted() {
2928
3135
  if (this.ima.isAdPlaying()) {
2929
- const adVolume = this.ima.getAdVolume();
2930
- return adVolume === 0;
3136
+ if (this.config.debugAdTiming) {
3137
+ console.log(
3138
+ "[StormcloudVideoPlayer] isMuted() override during ad playback -> false"
3139
+ );
3140
+ }
3141
+ return false;
3142
+ }
3143
+ if (this.config.debugAdTiming) {
3144
+ console.log(
3145
+ `[StormcloudVideoPlayer] isMuted() no ad playing: video.muted=${this.video.muted}`
3146
+ );
2931
3147
  }
2932
3148
  return this.video.muted;
2933
3149
  }
3150
+ setMuted(muted) {
3151
+ const adPlaying = this.ima.isAdPlaying();
3152
+ if (adPlaying && muted === this.video.muted) {
3153
+ if (this.config.debugAdTiming) {
3154
+ console.log(
3155
+ "[StormcloudVideoPlayer] setMuted reflective update during ad ignored",
3156
+ { muted }
3157
+ );
3158
+ }
3159
+ return;
3160
+ }
3161
+ this.video.muted = muted;
3162
+ if (adPlaying) {
3163
+ this.ima.updateOriginalMutedState(muted);
3164
+ this.ima.setAdVolume(muted ? 0 : 1);
3165
+ if (this.config.debugAdTiming) {
3166
+ console.log("[StormcloudVideoPlayer] setMuted applied during ad", {
3167
+ muted
3168
+ });
3169
+ }
3170
+ return;
3171
+ }
3172
+ this.ima.updateOriginalMutedState(muted);
3173
+ if (this.config.debugAdTiming) {
3174
+ console.log("[StormcloudVideoPlayer] setMuted called:", muted);
3175
+ }
3176
+ }
2934
3177
  isFullscreen() {
2935
3178
  return !!document.fullscreenElement;
2936
3179
  }