stormcloud-video-player 0.7.11 → 0.7.13

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.
@@ -1479,7 +1479,6 @@ function createAdStormPlayer(contentVideo, options) {
1479
1479
  contentVideo.muted = originalMutedState;
1480
1480
  contentVideo.volume = originalVolume;
1481
1481
  currentAd = void 0;
1482
- tornDown = false;
1483
1482
  return [
1484
1483
  2,
1485
1484
  Promise.resolve()
@@ -1594,6 +1593,15 @@ function createAdStormPlayer(contentVideo, options) {
1594
1593
  if (!token) return [
1595
1594
  2
1596
1595
  ];
1596
+ if (currentAd) {
1597
+ preloadSlots.set(token, {
1598
+ ad: currentAd
1599
+ });
1600
+ currentAd = void 0;
1601
+ return [
1602
+ 2
1603
+ ];
1604
+ }
1597
1605
  requestContext = typeof arg1 === "string" ? arg2 : arg1;
1598
1606
  return [
1599
1607
  4,
@@ -2087,56 +2095,6 @@ function sendInitialTracking(licenseKey) {
2087
2095
  });
2088
2096
  })();
2089
2097
  }
2090
- function sendAdDetectTracking(licenseKey, adDetectInfo) {
2091
- return _async_to_generator(function() {
2092
- var clientInfo, browserId, trackingData, error;
2093
- return _ts_generator(this, function(_state) {
2094
- switch(_state.label){
2095
- case 0:
2096
- _state.trys.push([
2097
- 0,
2098
- 3,
2099
- ,
2100
- 4
2101
- ]);
2102
- clientInfo = getClientInfo();
2103
- return [
2104
- 4,
2105
- getBrowserID(clientInfo)
2106
- ];
2107
- case 1:
2108
- browserId = _state.sent();
2109
- trackingData = _object_spread({
2110
- browserId: browserId
2111
- }, clientInfo);
2112
- return [
2113
- 4,
2114
- sendTrackRequest(licenseKey, _object_spread_props(_object_spread({}, trackingData), {
2115
- licenseKey: licenseKey,
2116
- adDetectInfo: adDetectInfo
2117
- }))
2118
- ];
2119
- case 2:
2120
- _state.sent();
2121
- return [
2122
- 3,
2123
- 4
2124
- ];
2125
- case 3:
2126
- error = _state.sent();
2127
- console.error("[StormcloudVideoPlayer] Error sending ad detect tracking:", error);
2128
- return [
2129
- 3,
2130
- 4
2131
- ];
2132
- case 4:
2133
- return [
2134
- 2
2135
- ];
2136
- }
2137
- });
2138
- })();
2139
- }
2140
2098
  function sendAdLoadedTracking(licenseKey, adLoadedInfo) {
2141
2099
  return _async_to_generator(function() {
2142
2100
  var clientInfo, browserId, trackingData, error;
@@ -2821,7 +2779,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2821
2779
  this.continuousFetchLoopPromise = null;
2822
2780
  this.attached = false;
2823
2781
  this.inAdBreak = false;
2824
- this.ptsDriftEmaMs = 0;
2825
2782
  this.adPodQueue = [];
2826
2783
  this.lastHeartbeatTime = 0;
2827
2784
  this.currentAdIndex = 0;
@@ -2845,7 +2802,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2845
2802
  this.totalAdRequestsInBreak = 0;
2846
2803
  this.maxTotalAdRequestsPerBreak = 20;
2847
2804
  this.pendingAdBreak = null;
2848
- this.savedMutedStateBeforeScte = null;
2805
+ this.savedMutedStateBeforeAd = null;
2849
2806
  this.consecutiveFailures = 0;
2850
2807
  this.maxConsecutiveFailures = 5;
2851
2808
  this.lastAdRequestTime = 0;
@@ -2858,7 +2815,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2858
2815
  this.adRequestRetryBackoffMs = 1500;
2859
2816
  this.preloadedTokens = [];
2860
2817
  this.debugLogEntries = [];
2861
- this.scteMarkerHistory = [];
2862
2818
  this.adInsertionDebugHistory = [];
2863
2819
  initializePolyfills();
2864
2820
  var browserOverrides = getBrowserConfigOverrides();
@@ -3147,18 +3103,44 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3147
3103
  _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3148
3104
  detail: "ad break started (60s)"
3149
3105
  });
3150
- var marker = {
3151
- type: "start",
3152
- durationSeconds: 60,
3153
- raw: {
3154
- apiInsertionPoint: _this.lastAdInsertionPoint
3155
- }
3156
- };
3157
- _this.onScte35Marker(marker);
3106
+ void _this.handleAdStart(60);
3158
3107
  }, offsetMs);
3159
3108
  }
3160
3109
  }
3161
3110
  });
3111
+ this.hls.on(import_hls.default.Events.FRAG_PARSING_USERDATA, function(_evt, data) {
3112
+ var _ref;
3113
+ var samples = (_ref = data === null || data === void 0 ? void 0 : data.samples) !== null && _ref !== void 0 ? _ref : [];
3114
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3115
+ try {
3116
+ for(var _iterator = samples[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3117
+ var sample = _step.value;
3118
+ var _ref1, _ref2;
3119
+ var _data_frag, _data_frag1;
3120
+ var bytes = sample === null || sample === void 0 ? void 0 : sample.data;
3121
+ if (!bytes || bytes.length < 5) continue;
3122
+ var isSCTE35 = bytes[0] === 252;
3123
+ if (!isSCTE35) continue;
3124
+ var segName = (_ref1 = (_ref2 = data === null || data === void 0 ? void 0 : (_data_frag = data.frag) === null || _data_frag === void 0 ? void 0 : _data_frag.relurl) !== null && _ref2 !== void 0 ? _ref2 : data === null || data === void 0 ? void 0 : (_data_frag1 = data.frag) === null || _data_frag1 === void 0 ? void 0 : _data_frag1.url) !== null && _ref1 !== void 0 ? _ref1 : "";
3125
+ _this.pushAdInsertionDebug("scte35_inserted", segName, {
3126
+ detail: "len=".concat(bytes.length, "B")
3127
+ });
3128
+ }
3129
+ } catch (err) {
3130
+ _didIteratorError = true;
3131
+ _iteratorError = err;
3132
+ } finally{
3133
+ try {
3134
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3135
+ _iterator.return();
3136
+ }
3137
+ } finally{
3138
+ if (_didIteratorError) {
3139
+ throw _iteratorError;
3140
+ }
3141
+ }
3142
+ }
3143
+ });
3162
3144
  this.hls.on(import_hls.default.Events.ERROR, function(_evt, data) {
3163
3145
  if (data === null || data === void 0 ? void 0 : data.fatal) {
3164
3146
  switch(data.type){
@@ -3233,8 +3215,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3233
3215
  _this.activeAdRequestToken = null;
3234
3216
  _this.showAds = true;
3235
3217
  if (_this.config.disableFiller) {
3236
- if (_this.savedMutedStateBeforeScte == null) {
3237
- _this.savedMutedStateBeforeScte = {
3218
+ if (_this.savedMutedStateBeforeAd == null) {
3219
+ _this.savedMutedStateBeforeAd = {
3238
3220
  muted: _this.video.muted,
3239
3221
  volume: _this.video.volume
3240
3222
  };
@@ -3262,10 +3244,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3262
3244
  });
3263
3245
  this.adLayer.on("content_resume", function() {
3264
3246
  var _ref, _ref1;
3265
- var _this_savedMutedStateBeforeScte, _this_savedMutedStateBeforeScte1;
3247
+ var _this_savedMutedStateBeforeAd, _this_savedMutedStateBeforeAd1;
3266
3248
  var remaining = _this.getRemainingAdMs();
3267
- var breakMuted = (_ref = (_this_savedMutedStateBeforeScte = _this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte === void 0 ? void 0 : _this_savedMutedStateBeforeScte.muted) !== null && _ref !== void 0 ? _ref : _this.adLayer.getOriginalMutedState();
3268
- var breakVolume = (_ref1 = (_this_savedMutedStateBeforeScte1 = _this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte1 === void 0 ? void 0 : _this_savedMutedStateBeforeScte1.volume) !== null && _ref1 !== void 0 ? _ref1 : _this.adLayer.getOriginalVolume();
3249
+ var breakMuted = (_ref = (_this_savedMutedStateBeforeAd = _this.savedMutedStateBeforeAd) === null || _this_savedMutedStateBeforeAd === void 0 ? void 0 : _this_savedMutedStateBeforeAd.muted) !== null && _ref !== void 0 ? _ref : _this.adLayer.getOriginalMutedState();
3250
+ var breakVolume = (_ref1 = (_this_savedMutedStateBeforeAd1 = _this.savedMutedStateBeforeAd) === null || _this_savedMutedStateBeforeAd1 === void 0 ? void 0 : _this_savedMutedStateBeforeAd1.volume) !== null && _ref1 !== void 0 ? _ref1 : _this.adLayer.getOriginalVolume();
3269
3251
  if (_this.config.debugAdTiming) {
3270
3252
  console.log("[StormcloudVideoPlayer] content_resume received, inAdBreak=%s, remaining=%s, preloadedTokens=%d, pendingNext=%s", _this.inAdBreak, remaining, _this.preloadedTokens.length, !!_this.pendingNextAdBids);
3271
3253
  }
@@ -3342,7 +3324,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3342
3324
  var remainingFinal = _this.getRemainingAdMs();
3343
3325
  if (_this.inAdBreak && remainingFinal > _this.MIN_AD_REMAINING_MS) {
3344
3326
  if (_this.config.debugAdTiming) {
3345
- console.log("[StormcloudVideoPlayer] content_resume: no more ads, showing filler for", remainingFinal, "ms");
3327
+ console.log("[StormcloudVideoPlayer] content_resume: ad ended/failed with time remaining, showing filler and continuing fetch for", remainingFinal, "ms");
3346
3328
  }
3347
3329
  if (!_this.config.disableFiller) {
3348
3330
  _this.showPlaceholderLayer();
@@ -3360,7 +3342,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3360
3342
  (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3361
3343
  }
3362
3344
  }
3363
- _this.stopContinuousFetching();
3345
+ _this.continuousFetchingActive = true;
3346
+ _this.startContinuousFetchLoop();
3364
3347
  return;
3365
3348
  }
3366
3349
  if (_this.config.debugAdTiming) {
@@ -3507,547 +3490,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3507
3490
  return !!(this.config.allowNativeHls && canNative);
3508
3491
  }
3509
3492
  },
3510
- {
3511
- key: "onId3Tag",
3512
- value: function onId3Tag(tag) {
3513
- if (typeof tag.ptsSeconds === "number") {
3514
- this.updatePtsDrift(tag.ptsSeconds);
3515
- }
3516
- var marker = this.parseScte35FromId3(tag);
3517
- if (marker) {
3518
- this.onScte35Marker(marker);
3519
- }
3520
- }
3521
- },
3522
- {
3523
- key: "parseScte35FromId3",
3524
- value: function parseScte35FromId3(tag) {
3525
- var text = this.decodeId3ValueToText(tag.value);
3526
- if (!text) return void 0;
3527
- var cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
3528
- if (cueOutMatch) {
3529
- var _cueOutMatch_;
3530
- var arg = ((_cueOutMatch_ = cueOutMatch[1]) !== null && _cueOutMatch_ !== void 0 ? _cueOutMatch_ : "").trim();
3531
- var dur = this.parseCueOutDuration(arg);
3532
- var marker = _object_spread_props(_object_spread({
3533
- type: "start"
3534
- }, tag.ptsSeconds !== void 0 ? {
3535
- ptsSeconds: tag.ptsSeconds
3536
- } : {}, dur !== void 0 ? {
3537
- durationSeconds: dur
3538
- } : {}), {
3539
- raw: {
3540
- id3: text
3541
- }
3542
- });
3543
- return marker;
3544
- }
3545
- var cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
3546
- if (cueOutContMatch) {
3547
- var _cueOutContMatch_;
3548
- var arg1 = ((_cueOutContMatch_ = cueOutContMatch[1]) !== null && _cueOutContMatch_ !== void 0 ? _cueOutContMatch_ : "").trim();
3549
- var cont = this.parseCueOutCont(arg1);
3550
- var marker1 = _object_spread_props(_object_spread({
3551
- type: "progress"
3552
- }, tag.ptsSeconds !== void 0 ? {
3553
- ptsSeconds: tag.ptsSeconds
3554
- } : {}, (cont === null || cont === void 0 ? void 0 : cont.duration) !== void 0 ? {
3555
- durationSeconds: cont.duration
3556
- } : {}), {
3557
- raw: {
3558
- id3: text
3559
- }
3560
- });
3561
- return marker1;
3562
- }
3563
- var cueInMatch = text.match(/EXT-X-CUE-IN\b/i) || text.match(/CUE-IN\b/i);
3564
- if (cueInMatch) {
3565
- var marker2 = _object_spread_props(_object_spread({
3566
- type: "end"
3567
- }, tag.ptsSeconds !== void 0 ? {
3568
- ptsSeconds: tag.ptsSeconds
3569
- } : {}), {
3570
- raw: {
3571
- id3: text
3572
- }
3573
- });
3574
- return marker2;
3575
- }
3576
- var daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
3577
- if (daterangeMatch) {
3578
- var _daterangeMatch_, _attrs_CLASS;
3579
- var attrs = this.parseAttributeList((_daterangeMatch_ = daterangeMatch[1]) !== null && _daterangeMatch_ !== void 0 ? _daterangeMatch_ : "");
3580
- var hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3581
- var hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
3582
- var klass = String((_attrs_CLASS = attrs["CLASS"]) !== null && _attrs_CLASS !== void 0 ? _attrs_CLASS : "");
3583
- var duration = this.toNumber(attrs["DURATION"]);
3584
- if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
3585
- var marker3 = _object_spread_props(_object_spread({
3586
- type: "start"
3587
- }, tag.ptsSeconds !== void 0 ? {
3588
- ptsSeconds: tag.ptsSeconds
3589
- } : {}, duration !== void 0 ? {
3590
- durationSeconds: duration
3591
- } : {}), {
3592
- raw: {
3593
- id3: text,
3594
- attrs: attrs
3595
- }
3596
- });
3597
- return marker3;
3598
- }
3599
- if (hasScteIn) {
3600
- var marker4 = _object_spread_props(_object_spread({
3601
- type: "end"
3602
- }, tag.ptsSeconds !== void 0 ? {
3603
- ptsSeconds: tag.ptsSeconds
3604
- } : {}), {
3605
- raw: {
3606
- id3: text,
3607
- attrs: attrs
3608
- }
3609
- });
3610
- return marker4;
3611
- }
3612
- }
3613
- if (/SCTE35-OUT/i.test(text)) {
3614
- var marker5 = _object_spread_props(_object_spread({
3615
- type: "start"
3616
- }, tag.ptsSeconds !== void 0 ? {
3617
- ptsSeconds: tag.ptsSeconds
3618
- } : {}), {
3619
- raw: {
3620
- id3: text
3621
- }
3622
- });
3623
- return marker5;
3624
- }
3625
- if (/SCTE35-IN/i.test(text)) {
3626
- var marker6 = _object_spread_props(_object_spread({
3627
- type: "end"
3628
- }, tag.ptsSeconds !== void 0 ? {
3629
- ptsSeconds: tag.ptsSeconds
3630
- } : {}), {
3631
- raw: {
3632
- id3: text
3633
- }
3634
- });
3635
- return marker6;
3636
- }
3637
- if (_instanceof(tag.value, Uint8Array)) {
3638
- var bin = this.parseScte35Binary(tag.value);
3639
- if (bin) return bin;
3640
- }
3641
- return void 0;
3642
- }
3643
- },
3644
- {
3645
- key: "decodeId3ValueToText",
3646
- value: function decodeId3ValueToText(value) {
3647
- try {
3648
- if (typeof value === "string") return value;
3649
- var decoder = new TextDecoder("utf-8", {
3650
- fatal: false
3651
- });
3652
- var text = decoder.decode(value);
3653
- if (text && /[\x20-\x7E]/.test(text)) return text;
3654
- var out = "";
3655
- for(var i = 0; i < value.length; i++)out += String.fromCharCode(value[i]);
3656
- return out;
3657
- } catch (unused) {
3658
- return void 0;
3659
- }
3660
- }
3661
- },
3662
- {
3663
- key: "onScte35Marker",
3664
- value: function onScte35Marker(marker) {
3665
- var _this = this;
3666
- if (this.config.disableAds) return;
3667
- this.pushScteMarker(marker);
3668
- this.pushDebugLog("info", "scte35", "SCTE-35 marker detected", {
3669
- type: marker.type,
3670
- ptsSeconds: marker.ptsSeconds,
3671
- durationSeconds: marker.durationSeconds,
3672
- currentTime: this.video.currentTime
3673
- });
3674
- if (this.config.debugAdTiming) {
3675
- console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
3676
- type: marker.type,
3677
- ptsSeconds: marker.ptsSeconds,
3678
- durationSeconds: marker.durationSeconds,
3679
- currentTime: this.video.currentTime,
3680
- raw: marker.raw,
3681
- hasPendingAdBreak: !!this.pendingAdBreak
3682
- });
3683
- }
3684
- if (marker.type === "start") {
3685
- var _this_config_immediateManifestAds;
3686
- var _this_pendingAdBreak;
3687
- if (this.savedMutedStateBeforeScte == null) {
3688
- this.savedMutedStateBeforeScte = {
3689
- muted: this.video.muted,
3690
- volume: this.video.volume
3691
- };
3692
- this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
3693
- }
3694
- if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
3695
- this.video.muted = true;
3696
- this.video.volume = 0;
3697
- if (this.config.debugAdTiming) {
3698
- console.log("[StormcloudVideoPlayer] Muted video on SCTE start marker");
3699
- }
3700
- }
3701
- if (this.inAdBreak) {
3702
- if (marker.durationSeconds != null) {
3703
- var newDurationMs = marker.durationSeconds * 1e3;
3704
- if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
3705
- this.expectedAdBreakDurationMs = newDurationMs;
3706
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
3707
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
3708
- this.scheduleAdStopCountdown(remainingMs);
3709
- if (this.config.debugAdTiming) {
3710
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
3711
- }
3712
- }
3713
- }
3714
- return;
3715
- }
3716
- this.inAdBreak = true;
3717
- var durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : ((_this_pendingAdBreak = this.pendingAdBreak) === null || _this_pendingAdBreak === void 0 ? void 0 : _this_pendingAdBreak.marker.durationSeconds) != null ? this.pendingAdBreak.marker.durationSeconds * 1e3 : void 0;
3718
- this.expectedAdBreakDurationMs = durationMs;
3719
- this.currentAdBreakStartWallClockMs = Date.now();
3720
- if (this.config.licenseKey) {
3721
- var _this_pendingAdBreak1;
3722
- var adDetectInfo = _object_spread({
3723
- source: "scte35",
3724
- timestamp: /* @__PURE__ */ new Date().toISOString()
3725
- }, marker.durationSeconds != null && {
3726
- durationSeconds: marker.durationSeconds
3727
- }, marker.ptsSeconds != null && {
3728
- ptsSeconds: marker.ptsSeconds
3729
- }, ((_this_pendingAdBreak1 = this.pendingAdBreak) === null || _this_pendingAdBreak1 === void 0 ? void 0 : _this_pendingAdBreak1.detectedAtFragmentSn) != null && {
3730
- detectedAtFragmentSn: this.pendingAdBreak.detectedAtFragmentSn
3731
- });
3732
- sendAdDetectTracking(this.config.licenseKey, adDetectInfo);
3733
- }
3734
- var isManifestMarker = this.isManifestBasedMarker(marker);
3735
- var forceImmediate = (_this_config_immediateManifestAds = this.config.immediateManifestAds) !== null && _this_config_immediateManifestAds !== void 0 ? _this_config_immediateManifestAds : true;
3736
- if (this.config.debugAdTiming) {
3737
- console.log("[StormcloudVideoPlayer] Ad start decision:", {
3738
- isManifestMarker: isManifestMarker,
3739
- forceImmediate: forceImmediate,
3740
- hasPts: typeof marker.ptsSeconds === "number"
3741
- });
3742
- }
3743
- if (isManifestMarker && forceImmediate) {
3744
- if (this.config.debugAdTiming) {
3745
- console.log("[StormcloudVideoPlayer] Starting ad immediately (manifest-based)");
3746
- }
3747
- this.clearAdStartTimer();
3748
- this.handleAdStart(marker);
3749
- } else if (typeof marker.ptsSeconds === "number") {
3750
- var _this_config_driftToleranceMs;
3751
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
3752
- var nowMs = this.video.currentTime * 1e3;
3753
- var estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
3754
- var deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
3755
- if (this.config.debugAdTiming) {
3756
- console.log("[StormcloudVideoPlayer] PTS-based timing calculation:", {
3757
- nowMs: nowMs,
3758
- estCurrentPtsMs: estCurrentPtsMs,
3759
- markerPtsMs: marker.ptsSeconds * 1e3,
3760
- deltaMs: deltaMs,
3761
- tolerance: tol
3762
- });
3763
- }
3764
- if (deltaMs > tol) {
3765
- if (this.config.debugAdTiming) {
3766
- console.log("[StormcloudVideoPlayer] Scheduling ad start in ".concat(deltaMs, "ms"));
3767
- }
3768
- this.scheduleAdStartIn(deltaMs);
3769
- } else {
3770
- if (this.config.debugAdTiming) {
3771
- console.log("[StormcloudVideoPlayer] Starting ad immediately (within tolerance)");
3772
- }
3773
- this.clearAdStartTimer();
3774
- this.handleAdStart(marker);
3775
- }
3776
- } else {
3777
- if (this.config.debugAdTiming) {
3778
- console.log("[StormcloudVideoPlayer] Starting ad immediately (fallback)");
3779
- }
3780
- this.clearAdStartTimer();
3781
- this.handleAdStart(marker);
3782
- }
3783
- if (this.expectedAdBreakDurationMs != null) {
3784
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
3785
- }
3786
- return;
3787
- }
3788
- if (marker.type === "progress" && this.inAdBreak) {
3789
- if (marker.durationSeconds != null) {
3790
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
3791
- }
3792
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
3793
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
3794
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
3795
- this.scheduleAdStopCountdown(remainingMs1);
3796
- }
3797
- if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
3798
- var bids = this.pendingNextAdBids;
3799
- this.pendingNextAdBids = null;
3800
- this.currentAdIndex++;
3801
- this.adLayer.playAd(bids).catch(function() {
3802
- return _this.handleAdFailure();
3803
- });
3804
- }
3805
- return;
3806
- }
3807
- if (marker.type === "end") {
3808
- var remaining = this.getRemainingAdMs();
3809
- var adPlaying = this.adLayer.isAdPlaying();
3810
- var hasQueuedAds = this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0;
3811
- if (this.config.debugAdTiming) {
3812
- console.log("[StormcloudVideoPlayer] SCTE-35 end marker received:", {
3813
- inAdBreak: this.inAdBreak,
3814
- remaining: remaining,
3815
- adPlaying: adPlaying,
3816
- hasQueuedAds: hasQueuedAds,
3817
- activeAdRequest: this.activeAdRequestToken !== null
3818
- });
3819
- }
3820
- if (!this.inAdBreak) {
3821
- if (this.config.debugAdTiming) {
3822
- console.log("[StormcloudVideoPlayer] Ignoring SCTE-35 end marker - not in ad break");
3823
- }
3824
- return;
3825
- }
3826
- if (adPlaying || remaining > 500) {
3827
- if (this.config.debugAdTiming) {
3828
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
3829
- }
3830
- return;
3831
- }
3832
- this.inAdBreak = false;
3833
- this.expectedAdBreakDurationMs = void 0;
3834
- this.currentAdBreakStartWallClockMs = void 0;
3835
- this.clearAdStartTimer();
3836
- this.clearAdStopTimer();
3837
- if (adPlaying) {
3838
- this.adLayer.stop().catch(function() {});
3839
- }
3840
- this.handleAdPodComplete();
3841
- return;
3842
- }
3843
- }
3844
- },
3845
- {
3846
- key: "parseCueOutDuration",
3847
- value: function parseCueOutDuration(value) {
3848
- var num = parseFloat(value.trim());
3849
- if (!Number.isNaN(num)) return num;
3850
- var match = value.match(/(?:^|[,\s])DURATION\s*=\s*([0-9.]+)/i) || value.match(/Duration\s*=\s*([0-9.]+)/i);
3851
- if (match && match[1] != null) {
3852
- var dStr = match[1];
3853
- var d = parseFloat(dStr);
3854
- return Number.isNaN(d) ? void 0 : d;
3855
- }
3856
- return void 0;
3857
- }
3858
- },
3859
- {
3860
- key: "parseCueOutCont",
3861
- value: function parseCueOutCont(value) {
3862
- var res = {};
3863
- var elapsedMatch = value.match(/Elapsed\s*=\s*([0-9.]+)/i);
3864
- var durationMatch = value.match(/Duration\s*=\s*([0-9.]+)/i);
3865
- if (elapsedMatch && elapsedMatch[1] != null) {
3866
- var e = parseFloat(elapsedMatch[1]);
3867
- if (!Number.isNaN(e)) res.elapsed = e;
3868
- }
3869
- if (durationMatch && durationMatch[1] != null) {
3870
- var d = parseFloat(durationMatch[1]);
3871
- if (!Number.isNaN(d)) res.duration = d;
3872
- }
3873
- if (!("elapsed" in res) || !("duration" in res)) {
3874
- var slashMatch = value.match(/([0-9.]+)\s*\/\s*([0-9.]+)/);
3875
- if (slashMatch && slashMatch[1] && slashMatch[2]) {
3876
- var elapsed = parseFloat(slashMatch[1]);
3877
- var duration = parseFloat(slashMatch[2]);
3878
- if (!Number.isNaN(elapsed) && !("elapsed" in res)) res.elapsed = elapsed;
3879
- if (!Number.isNaN(duration) && !("duration" in res)) res.duration = duration;
3880
- }
3881
- }
3882
- if ("elapsed" in res || "duration" in res) return res;
3883
- return void 0;
3884
- }
3885
- },
3886
- {
3887
- key: "parseAttributeList",
3888
- value: function parseAttributeList(value) {
3889
- var attrs = {};
3890
- var regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
3891
- var match;
3892
- while((match = regex.exec(value)) !== null){
3893
- var _match_, _ref, _match_1;
3894
- var key = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : "";
3895
- var rawVal = (_ref = (_match_1 = match[3]) !== null && _match_1 !== void 0 ? _match_1 : match[4]) !== null && _ref !== void 0 ? _ref : "";
3896
- if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
3897
- rawVal = rawVal.slice(1, -1);
3898
- }
3899
- if (key) {
3900
- attrs[key] = rawVal;
3901
- }
3902
- }
3903
- return attrs;
3904
- }
3905
- },
3906
- {
3907
- key: "toNumber",
3908
- value: function toNumber(val) {
3909
- if (val == null) return void 0;
3910
- var n = typeof val === "string" ? parseFloat(val) : Number(val);
3911
- return Number.isNaN(n) ? void 0 : n;
3912
- }
3913
- },
3914
- {
3915
- key: "isManifestBasedMarker",
3916
- value: function isManifestBasedMarker(marker) {
3917
- var raw = marker.raw;
3918
- if (!raw) return false;
3919
- if (raw.tag) {
3920
- var tag = String(raw.tag);
3921
- return tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-CUE-IN") || tag.includes("EXT-X-DATERANGE");
3922
- }
3923
- if (raw.id3) return false;
3924
- if (raw.splice_command_type) return false;
3925
- return false;
3926
- }
3927
- },
3928
- {
3929
- key: "parseScte35Binary",
3930
- value: function parseScte35Binary(data) {
3931
- var BitReader = /*#__PURE__*/ function() {
3932
- function BitReader(buf) {
3933
- _class_call_check(this, BitReader);
3934
- this.buf = buf;
3935
- this.bytePos = 0;
3936
- this.bitPos = 0;
3937
- }
3938
- _create_class(BitReader, [
3939
- {
3940
- key: "readBits",
3941
- value: function readBits(numBits) {
3942
- var result = 0;
3943
- while(numBits > 0){
3944
- if (this.bytePos >= this.buf.length) return result;
3945
- var remainingInByte = 8 - this.bitPos;
3946
- var toRead = Math.min(numBits, remainingInByte);
3947
- var currentByte = this.buf[this.bytePos];
3948
- var shift = remainingInByte - toRead;
3949
- var mask = (1 << toRead) - 1 & 255;
3950
- var bits = currentByte >> shift & mask;
3951
- result = result << toRead | bits;
3952
- this.bitPos += toRead;
3953
- if (this.bitPos >= 8) {
3954
- this.bitPos = 0;
3955
- this.bytePos += 1;
3956
- }
3957
- numBits -= toRead;
3958
- }
3959
- return result >>> 0;
3960
- }
3961
- },
3962
- {
3963
- key: "skipBits",
3964
- value: function skipBits(n) {
3965
- this.readBits(n);
3966
- }
3967
- }
3968
- ]);
3969
- return BitReader;
3970
- }();
3971
- var r = new BitReader(data);
3972
- var tableId = r.readBits(8);
3973
- if (tableId !== 252) return void 0;
3974
- r.readBits(1);
3975
- r.readBits(1);
3976
- r.readBits(2);
3977
- var sectionLength = r.readBits(12);
3978
- r.readBits(8);
3979
- r.readBits(1);
3980
- r.readBits(6);
3981
- var ptsAdjHigh = r.readBits(1);
3982
- var ptsAdjLow = r.readBits(32);
3983
- void ptsAdjHigh;
3984
- void ptsAdjLow;
3985
- r.readBits(8);
3986
- r.readBits(12);
3987
- var spliceCommandLength = r.readBits(12);
3988
- var spliceCommandType = r.readBits(8);
3989
- if (spliceCommandType !== 5) {
3990
- return void 0;
3991
- }
3992
- r.readBits(32);
3993
- var cancel = r.readBits(1) === 1;
3994
- r.readBits(7);
3995
- if (cancel) return void 0;
3996
- var outOfNetwork = r.readBits(1) === 1;
3997
- var programSpliceFlag = r.readBits(1) === 1;
3998
- var durationFlag = r.readBits(1) === 1;
3999
- var spliceImmediateFlag = r.readBits(1) === 1;
4000
- r.readBits(4);
4001
- if (programSpliceFlag && !spliceImmediateFlag) {
4002
- var timeSpecifiedFlag = r.readBits(1) === 1;
4003
- if (timeSpecifiedFlag) {
4004
- r.readBits(6);
4005
- r.readBits(33);
4006
- } else {
4007
- r.readBits(7);
4008
- }
4009
- } else if (!programSpliceFlag) {
4010
- var componentCount = r.readBits(8);
4011
- for(var i = 0; i < componentCount; i++){
4012
- r.readBits(8);
4013
- if (!spliceImmediateFlag) {
4014
- var timeSpecifiedFlag1 = r.readBits(1) === 1;
4015
- if (timeSpecifiedFlag1) {
4016
- r.readBits(6);
4017
- r.readBits(33);
4018
- } else {
4019
- r.readBits(7);
4020
- }
4021
- }
4022
- }
4023
- }
4024
- var durationSeconds = void 0;
4025
- if (durationFlag) {
4026
- r.readBits(6);
4027
- r.readBits(1);
4028
- var high = r.readBits(1);
4029
- var low = r.readBits(32);
4030
- var durationTicks = high * 4294967296 + low;
4031
- durationSeconds = durationTicks / 9e4;
4032
- }
4033
- r.readBits(16);
4034
- r.readBits(8);
4035
- r.readBits(8);
4036
- if (outOfNetwork) {
4037
- var marker = _object_spread_props(_object_spread({
4038
- type: "start"
4039
- }, durationSeconds !== void 0 ? {
4040
- durationSeconds: durationSeconds
4041
- } : {}), {
4042
- raw: {
4043
- splice_command_type: 5
4044
- }
4045
- });
4046
- return marker;
4047
- }
4048
- return void 0;
4049
- }
4050
- },
4051
3493
  {
4052
3494
  key: "initializeTracking",
4053
3495
  value: function initializeTracking() {
@@ -4169,35 +3611,35 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4169
3611
  },
4170
3612
  {
4171
3613
  key: "startAdPrefetch",
4172
- value: function startAdPrefetch(marker, fragmentSn) {
3614
+ value: function startAdPrefetch(durationSeconds, fragmentSn) {
4173
3615
  if (this.config.disableAds) return;
4174
3616
  if (this.pendingAdBreak || this.inAdBreak) {
4175
3617
  return;
4176
3618
  }
4177
- this.pendingAdBreak = _object_spread_props(_object_spread({
4178
- marker: marker
4179
- }, fragmentSn !== void 0 ? {
3619
+ this.pendingAdBreak = _object_spread_props(_object_spread({}, durationSeconds !== void 0 ? {
3620
+ durationSeconds: durationSeconds
3621
+ } : {}, fragmentSn !== void 0 ? {
4180
3622
  detectedAtFragmentSn: fragmentSn
4181
3623
  } : {}), {
4182
3624
  isFetching: false,
4183
3625
  fetchStartTime: Date.now()
4184
3626
  });
4185
- void this.runAdPrefetch(marker);
3627
+ void this.runAdPrefetch(durationSeconds);
4186
3628
  if (this.config.debugAdTiming) {
4187
- console.log("[PREFETCH] Ad break marker registered, multi-ad prefetch started");
3629
+ console.log("[PREFETCH] Ad break registered, multi-ad prefetch started");
4188
3630
  }
4189
3631
  }
4190
3632
  },
4191
3633
  {
4192
3634
  key: "runAdPrefetch",
4193
- value: function runAdPrefetch(marker) {
3635
+ value: function runAdPrefetch(durationSeconds) {
4194
3636
  return _async_to_generator(function() {
4195
- var _this, _marker_durationSeconds, _ref, _firstBids_, durSec, context, firstBids, unused, adDurationSec, estimatedCount, firstToken, remaining, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, result, token;
3637
+ var _this, _ref, _firstBids_, durSec, context, firstBids, unused, adDurationSec, estimatedCount, firstToken, unused1, remaining, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, result, token, unused2, err;
4196
3638
  return _ts_generator(this, function(_state) {
4197
3639
  switch(_state.label){
4198
3640
  case 0:
4199
3641
  _this = this;
4200
- durSec = (_marker_durationSeconds = marker.durationSeconds) !== null && _marker_durationSeconds !== void 0 ? _marker_durationSeconds : 60;
3642
+ durSec = durationSeconds !== null && durationSeconds !== void 0 ? durationSeconds : 60;
4201
3643
  context = {
4202
3644
  breakDurationSec: durSec,
4203
3645
  remainingBreakSec: durSec
@@ -4237,6 +3679,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4237
3679
  if (this.config.debugAdTiming) {
4238
3680
  console.log("[PREFETCH] First VAST request returned no ad, aborting prefetch");
4239
3681
  }
3682
+ this.clearPendingAdBreak();
4240
3683
  return [
4241
3684
  2
4242
3685
  ];
@@ -4247,14 +3690,43 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4247
3690
  console.log("[PREFETCH] Ad duration=".concat(adDurationSec, "s, break=").concat(durSec, "s → ").concat(estimatedCount, " ad(s) needed"));
4248
3691
  }
4249
3692
  firstToken = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
4250
- this.preloadedTokens.push(firstToken);
4251
- void this.adLayer.preloadAd(firstBids, firstToken);
3693
+ _state.label = 5;
3694
+ case 5:
3695
+ _state.trys.push([
3696
+ 5,
3697
+ 7,
3698
+ ,
3699
+ 8
3700
+ ]);
3701
+ return [
3702
+ 4,
3703
+ this.adLayer.preloadAd(firstBids, firstToken)
3704
+ ];
3705
+ case 6:
3706
+ _state.sent();
3707
+ if (!this.inAdBreak) {
3708
+ this.preloadedTokens.push(firstToken);
3709
+ if (this.config.debugAdTiming) {
3710
+ console.log("[PREFETCH] First ad preloaded and queued, token=".concat(firstToken));
3711
+ }
3712
+ }
3713
+ return [
3714
+ 3,
3715
+ 8
3716
+ ];
3717
+ case 7:
3718
+ unused1 = _state.sent();
4252
3719
  if (this.config.debugAdTiming) {
4253
- console.log("[PREFETCH] First ad preloading, token=".concat(firstToken));
3720
+ console.warn("[PREFETCH] First ad preload failed, token=".concat(firstToken));
4254
3721
  }
3722
+ return [
3723
+ 3,
3724
+ 8
3725
+ ];
3726
+ case 8:
4255
3727
  if (!(estimatedCount > 1)) return [
4256
3728
  3,
4257
- 6
3729
+ 19
4258
3730
  ];
4259
3731
  remaining = Array.from({
4260
3732
  length: estimatedCount - 1
@@ -4276,38 +3748,100 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4276
3748
  4,
4277
3749
  Promise.all(remaining)
4278
3750
  ];
4279
- case 5:
3751
+ case 9:
4280
3752
  results = _state.sent();
4281
3753
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3754
+ _state.label = 10;
3755
+ case 10:
3756
+ _state.trys.push([
3757
+ 10,
3758
+ 17,
3759
+ 18,
3760
+ 19
3761
+ ]);
3762
+ _iterator = results[Symbol.iterator]();
3763
+ _state.label = 11;
3764
+ case 11:
3765
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
3766
+ 3,
3767
+ 16
3768
+ ];
3769
+ result = _step.value;
3770
+ if (this.inAdBreak) return [
3771
+ 3,
3772
+ 16
3773
+ ];
3774
+ if (!(result.ok && result.value.length > 0)) return [
3775
+ 3,
3776
+ 15
3777
+ ];
3778
+ token = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
3779
+ _state.label = 12;
3780
+ case 12:
3781
+ _state.trys.push([
3782
+ 12,
3783
+ 14,
3784
+ ,
3785
+ 15
3786
+ ]);
3787
+ return [
3788
+ 4,
3789
+ this.adLayer.preloadAd(result.value, token)
3790
+ ];
3791
+ case 13:
3792
+ _state.sent();
3793
+ if (!this.inAdBreak) {
3794
+ this.preloadedTokens.push(token);
3795
+ if (this.config.debugAdTiming) {
3796
+ console.log("[PREFETCH] Additional ad preloaded and queued, token=".concat(token));
3797
+ }
3798
+ }
3799
+ return [
3800
+ 3,
3801
+ 15
3802
+ ];
3803
+ case 14:
3804
+ unused2 = _state.sent();
3805
+ if (this.config.debugAdTiming) {
3806
+ console.warn("[PREFETCH] Additional ad preload failed, token=".concat(token));
3807
+ }
3808
+ return [
3809
+ 3,
3810
+ 15
3811
+ ];
3812
+ case 15:
3813
+ _iteratorNormalCompletion = true;
3814
+ return [
3815
+ 3,
3816
+ 11
3817
+ ];
3818
+ case 16:
3819
+ return [
3820
+ 3,
3821
+ 19
3822
+ ];
3823
+ case 17:
3824
+ err = _state.sent();
3825
+ _didIteratorError = true;
3826
+ _iteratorError = err;
3827
+ return [
3828
+ 3,
3829
+ 19
3830
+ ];
3831
+ case 18:
4282
3832
  try {
4283
- for(_iterator = results[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4284
- result = _step.value;
4285
- if (this.inAdBreak) break;
4286
- if (result.ok && result.value.length > 0) {
4287
- token = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
4288
- this.preloadedTokens.push(token);
4289
- void this.adLayer.preloadAd(result.value, token);
4290
- if (this.config.debugAdTiming) {
4291
- console.log("[PREFETCH] Additional ad preloading, token=".concat(token));
4292
- }
4293
- }
3833
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3834
+ _iterator.return();
4294
3835
  }
4295
- } catch (err) {
4296
- _didIteratorError = true;
4297
- _iteratorError = err;
4298
3836
  } finally{
4299
- try {
4300
- if (!_iteratorNormalCompletion && _iterator.return != null) {
4301
- _iterator.return();
4302
- }
4303
- } finally{
4304
- if (_didIteratorError) {
4305
- throw _iteratorError;
4306
- }
3837
+ if (_didIteratorError) {
3838
+ throw _iteratorError;
4307
3839
  }
4308
3840
  }
4309
- _state.label = 6;
4310
- case 6:
3841
+ return [
3842
+ 7
3843
+ ];
3844
+ case 19:
4311
3845
  if (this.config.debugAdTiming) {
4312
3846
  console.log("[PREFETCH] Pre-fetch complete: ".concat(this.preloadedTokens.length, " ad(s) queued"));
4313
3847
  }
@@ -4379,7 +3913,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4379
3913
  key: "fetchAdInsertionPoint",
4380
3914
  value: function fetchAdInsertionPoint() {
4381
3915
  return _async_to_generator(function() {
4382
- var _this_lastAdInsertionPoint, resp, data, isNew, unused;
3916
+ var _this_lastAdInsertionPoint, _this_lastAdInsertionPoint1, resp, data, prevSegment, isNew, segmentChanged, unused;
4383
3917
  return _ts_generator(this, function(_state) {
4384
3918
  switch(_state.label){
4385
3919
  case 0:
@@ -4409,7 +3943,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4409
3943
  ];
4410
3944
  case 3:
4411
3945
  data = _state.sent();
4412
- isNew = data.updated_at !== ((_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.updated_at);
3946
+ prevSegment = (_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.segment_ts_name;
3947
+ isNew = data.updated_at !== ((_this_lastAdInsertionPoint1 = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint1 === void 0 ? void 0 : _this_lastAdInsertionPoint1.updated_at);
3948
+ segmentChanged = prevSegment !== data.segment_ts_name;
4413
3949
  this.lastAdInsertionPoint = data;
4414
3950
  if (isNew) {
4415
3951
  this.pushAdInsertionDebug("api_response", data.segment_ts_name, {
@@ -4417,6 +3953,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4417
3953
  updatedAt: data.updated_at,
4418
3954
  detail: "project=".concat(data.project_id)
4419
3955
  });
3956
+ if (segmentChanged && (this.pendingAdBreak || this.adInsertionOffsetTimerId != null)) {
3957
+ this.clearAdInsertionOffsetTimer();
3958
+ this.clearPendingAdBreak();
3959
+ this.cancelAndClearPreloadedTokens();
3960
+ if (this.config.debugAdTiming) {
3961
+ console.log("[StormcloudVideoPlayer] Insertion segment changed (".concat(prevSegment, " → ").concat(data.segment_ts_name, "), stale pending state cleared"));
3962
+ }
3963
+ }
3964
+ this.checkAdInsertionInManifest();
4420
3965
  }
4421
3966
  if (this.config.debugAdTiming) {
4422
3967
  console.log("[StormcloudVideoPlayer] Ad insertion point API response:", data);
@@ -4446,6 +3991,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4446
3991
  {
4447
3992
  key: "checkAdInsertionInManifest",
4448
3993
  value: function checkAdInsertionInManifest() {
3994
+ var _this = this;
4449
3995
  var _this_hls;
4450
3996
  if (!this.lastAdInsertionPoint) return;
4451
3997
  if (this.inAdBreak || this.pendingAdBreak) return;
@@ -4465,22 +4011,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4465
4011
  for(var _iterator1 = fragments[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
4466
4012
  var frag = _step1.value;
4467
4013
  if (this.fragmentMatchesSegment(frag, segmentName)) {
4468
- var _ref;
4014
+ var _frag_start, _frag_duration, _ref;
4015
+ var currentTime = this.video.currentTime;
4016
+ var fragStart = (_frag_start = frag.start) !== null && _frag_start !== void 0 ? _frag_start : 0;
4017
+ var fragDuration = (_frag_duration = frag.duration) !== null && _frag_duration !== void 0 ? _frag_duration : 0;
4018
+ var isCurrentlyPlaying = fragDuration > 0 && currentTime >= fragStart && currentTime < fragStart + fragDuration;
4469
4019
  this.pushAdInsertionDebug("segment_found", segmentName, {
4470
- detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
4020
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?", " ").concat(isCurrentlyPlaying ? "(playing)" : "(future)")
4471
4021
  });
4472
4022
  if (this.config.debugAdTiming) {
4473
- console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — starting pre-fetch'));
4023
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — ') + (isCurrentlyPlaying ? "currently playing, scheduling directly" : "starting pre-fetch"));
4024
+ }
4025
+ this.startAdPrefetch(60, frag === null || frag === void 0 ? void 0 : frag.sn);
4026
+ if (isCurrentlyPlaying && !this.inAdBreak) {
4027
+ var _this_lastAdInsertionPoint_offset_seconds, _ref1;
4028
+ this.processedAdInsertionUpdatedAt = this.lastAdInsertionPoint.updated_at;
4029
+ var rawOffsetMs = ((_this_lastAdInsertionPoint_offset_seconds = this.lastAdInsertionPoint.offset_seconds) !== null && _this_lastAdInsertionPoint_offset_seconds !== void 0 ? _this_lastAdInsertionPoint_offset_seconds : 0) * 1e3;
4030
+ var elapsedInFragMs = (currentTime - fragStart) * 1e3;
4031
+ var remainingOffsetMs = Math.max(0, rawOffsetMs - elapsedInFragMs);
4032
+ this.pushAdInsertionDebug("segment_playing", segmentName, {
4033
+ offsetSeconds: this.lastAdInsertionPoint.offset_seconds,
4034
+ detail: "sn=".concat((_ref1 = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref1 !== void 0 ? _ref1 : "?", " (caught in manifest check)")
4035
+ });
4036
+ this.pushAdInsertionDebug("ad_scheduled", segmentName, {
4037
+ offsetSeconds: this.lastAdInsertionPoint.offset_seconds,
4038
+ detail: "in ".concat(remainingOffsetMs, "ms (adjusted for elapsed ").concat(Math.round(elapsedInFragMs), "ms)")
4039
+ });
4040
+ this.clearAdInsertionOffsetTimer();
4041
+ this.adInsertionOffsetTimerId = window.setTimeout(function() {
4042
+ _this.adInsertionOffsetTimerId = void 0;
4043
+ if (_this.inAdBreak) return;
4044
+ _this.pushAdInsertionDebug("ad_triggered", segmentName, {
4045
+ detail: "ad break started (60s, via manifest-check path)"
4046
+ });
4047
+ void _this.handleAdStart(60);
4048
+ }, remainingOffsetMs);
4474
4049
  }
4475
- var marker = {
4476
- type: "start",
4477
- durationSeconds: 60,
4478
- raw: {
4479
- apiInsertionPoint: this.lastAdInsertionPoint,
4480
- earlyDetection: true
4481
- }
4482
- };
4483
- this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
4484
4050
  return;
4485
4051
  }
4486
4052
  }
@@ -4518,8 +4084,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4518
4084
  {
4519
4085
  key: "fragmentMatchesSegment",
4520
4086
  value: function fragmentMatchesSegment(frag, segmentName) {
4521
- var url = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4522
- return url.endsWith(segmentName) || url.includes("/" + segmentName);
4087
+ var _rawUrl_split_;
4088
+ var rawUrl = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4089
+ var url = (_rawUrl_split_ = rawUrl.split("?")[0]) !== null && _rawUrl_split_ !== void 0 ? _rawUrl_split_ : "";
4090
+ var name = segmentName.startsWith("/") ? segmentName : "/" + segmentName;
4091
+ return url.endsWith(name) || url.includes(name);
4523
4092
  }
4524
4093
  },
4525
4094
  {
@@ -4856,13 +4425,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4856
4425
  },
4857
4426
  {
4858
4427
  key: "handleAdStart",
4859
- value: function handleAdStart(_marker) {
4428
+ value: function handleAdStart(durationSeconds) {
4860
4429
  return _async_to_generator(function() {
4861
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
4430
+ var _this_savedMutedStateBeforeAd, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
4862
4431
  return _ts_generator(this, function(_state) {
4863
4432
  switch(_state.label){
4864
4433
  case 0:
4865
- adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : void 0;
4434
+ adBreakDurationMs = durationSeconds != null ? durationSeconds * 1e3 : void 0;
4866
4435
  if (this.config.debugAdTiming) {
4867
4436
  mode = this.isLiveStream ? "LIVE" : "VOD";
4868
4437
  console.log("[CONTINUOUS-FETCH] \uD83D\uDCFA ".concat(mode, " MODE: Target duration=").concat(adBreakDurationMs, "ms"));
@@ -4873,13 +4442,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4873
4442
  this.pendingNextAdBids = null;
4874
4443
  this.isShowingPlaceholder = false;
4875
4444
  this.totalAdRequestsInBreak = 0;
4876
- if (this.savedMutedStateBeforeScte == null && !this.video.muted) {
4877
- this.savedMutedStateBeforeScte = {
4445
+ if (this.savedMutedStateBeforeAd == null && !this.video.muted) {
4446
+ this.savedMutedStateBeforeAd = {
4878
4447
  muted: false,
4879
4448
  volume: this.video.volume
4880
4449
  };
4881
4450
  }
4882
- state = (_this_savedMutedStateBeforeScte = this.savedMutedStateBeforeScte) !== null && _this_savedMutedStateBeforeScte !== void 0 ? _this_savedMutedStateBeforeScte : {
4451
+ state = (_this_savedMutedStateBeforeAd = this.savedMutedStateBeforeAd) !== null && _this_savedMutedStateBeforeAd !== void 0 ? _this_savedMutedStateBeforeAd : {
4883
4452
  muted: this.video.muted,
4884
4453
  volume: this.video.volume
4885
4454
  };
@@ -4906,6 +4475,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4906
4475
  if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
4907
4476
  this.expectedAdBreakDurationMs = adBreakDurationMs;
4908
4477
  }
4478
+ if (this.expectedAdBreakDurationMs != null) {
4479
+ this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4480
+ }
4909
4481
  this.clearPendingAdBreak();
4910
4482
  adBreakToken = Date.now();
4911
4483
  this.activeAdRequestToken = adBreakToken;
@@ -5404,49 +4976,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5404
4976
  this.handleAdPodComplete();
5405
4977
  }
5406
4978
  },
5407
- {
5408
- key: "scheduleAdStartIn",
5409
- value: function scheduleAdStartIn(delayMs) {
5410
- var _this = this;
5411
- this.clearAdStartTimer();
5412
- var ms = Math.max(0, Math.floor(delayMs));
5413
- if (ms === 0) {
5414
- this.handleAdStart({
5415
- type: "start"
5416
- }).catch(function() {});
5417
- return;
5418
- }
5419
- this.adStartTimerId = window.setTimeout(function() {
5420
- _this.handleAdStart({
5421
- type: "start"
5422
- }).catch(function() {});
5423
- }, ms);
5424
- }
5425
- },
5426
- {
5427
- key: "clearAdStartTimer",
5428
- value: function clearAdStartTimer() {
5429
- if (this.adStartTimerId != null) {
5430
- clearTimeout(this.adStartTimerId);
5431
- this.adStartTimerId = void 0;
5432
- }
5433
- }
5434
- },
5435
- {
5436
- key: "updatePtsDrift",
5437
- value: function updatePtsDrift(ptsSecondsSample) {
5438
- var sampleMs = (this.video.currentTime - ptsSecondsSample) * 1e3;
5439
- if (!Number.isFinite(sampleMs) || Math.abs(sampleMs) > 6e4) return;
5440
- var alpha = 0.1;
5441
- this.ptsDriftEmaMs = this.ptsDriftEmaMs * (1 - alpha) + sampleMs * alpha;
5442
- }
5443
- },
5444
4979
  {
5445
4980
  key: "handleAdPodComplete",
5446
4981
  value: function handleAdPodComplete() {
5447
4982
  var _this = this;
5448
4983
  var _ref, _ref1;
5449
- var _this_savedMutedStateBeforeScte, _this_savedMutedStateBeforeScte1;
4984
+ var _this_savedMutedStateBeforeAd, _this_savedMutedStateBeforeAd1;
5450
4985
  if (this.config.debugAdTiming) {
5451
4986
  console.log("[StormcloudVideoPlayer] \uD83C\uDFC1 Ad pod complete - cleaning up");
5452
4987
  }
@@ -5468,7 +5003,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5468
5003
  this.inAdBreak = false;
5469
5004
  this.expectedAdBreakDurationMs = void 0;
5470
5005
  this.currentAdBreakStartWallClockMs = void 0;
5471
- this.clearAdStartTimer();
5472
5006
  this.clearAdStopTimer();
5473
5007
  this.adPodQueue = [];
5474
5008
  this.showAds = false;
@@ -5476,8 +5010,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5476
5010
  this.totalAdsInBreak = 0;
5477
5011
  this.totalAdRequestsInBreak = 0;
5478
5012
  this.consecutiveFailures = 0;
5479
- var restoredMuted = (_ref = (_this_savedMutedStateBeforeScte = this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte === void 0 ? void 0 : _this_savedMutedStateBeforeScte.muted) !== null && _ref !== void 0 ? _ref : this.adLayer.getOriginalMutedState();
5480
- var restoredVolume = (_ref1 = (_this_savedMutedStateBeforeScte1 = this.savedMutedStateBeforeScte) === null || _this_savedMutedStateBeforeScte1 === void 0 ? void 0 : _this_savedMutedStateBeforeScte1.volume) !== null && _ref1 !== void 0 ? _ref1 : this.adLayer.getOriginalVolume();
5013
+ var restoredMuted = (_ref = (_this_savedMutedStateBeforeAd = this.savedMutedStateBeforeAd) === null || _this_savedMutedStateBeforeAd === void 0 ? void 0 : _this_savedMutedStateBeforeAd.muted) !== null && _ref !== void 0 ? _ref : this.adLayer.getOriginalMutedState();
5014
+ var restoredVolume = (_ref1 = (_this_savedMutedStateBeforeAd1 = this.savedMutedStateBeforeAd) === null || _this_savedMutedStateBeforeAd1 === void 0 ? void 0 : _this_savedMutedStateBeforeAd1.volume) !== null && _ref1 !== void 0 ? _ref1 : this.adLayer.getOriginalVolume();
5481
5015
  this.adLayer.updateOriginalMutedState(restoredMuted, restoredVolume);
5482
5016
  this.adLayer.stop().catch(function() {});
5483
5017
  if (this.video.muted !== restoredMuted) {
@@ -5586,7 +5120,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5586
5120
  }, delay);
5587
5121
  });
5588
5122
  }
5589
- this.savedMutedStateBeforeScte = null;
5123
+ this.savedMutedStateBeforeAd = null;
5590
5124
  }
5591
5125
  },
5592
5126
  {
@@ -5736,19 +5270,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5736
5270
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
5737
5271
  }
5738
5272
  },
5739
- {
5740
- key: "pushScteMarker",
5741
- value: function pushScteMarker(marker) {
5742
- if (!this.config.debugAdTiming) return;
5743
- this.scteMarkerHistory.push({
5744
- timestampMs: Date.now(),
5745
- marker: marker
5746
- });
5747
- if (this.scteMarkerHistory.length > DEBUG_HISTORY_LIMIT) {
5748
- this.scteMarkerHistory = this.scteMarkerHistory.slice(-DEBUG_HISTORY_LIMIT);
5749
- }
5750
- }
5751
- },
5752
5273
  {
5753
5274
  key: "pushDebugLog",
5754
5275
  value: function pushDebugLog(level, category, message, details) {
@@ -5766,23 +5287,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5766
5287
  }
5767
5288
  }
5768
5289
  },
5769
- {
5770
- key: "getRecentScteMarkers",
5771
- value: function getRecentScteMarkers() {
5772
- return this.scteMarkerHistory.map(function(entry) {
5773
- return _object_spread({
5774
- timestampMs: entry.timestampMs,
5775
- type: entry.marker.type
5776
- }, entry.marker.ptsSeconds !== void 0 ? {
5777
- ptsSeconds: entry.marker.ptsSeconds
5778
- } : {}, entry.marker.durationSeconds !== void 0 ? {
5779
- durationSeconds: entry.marker.durationSeconds
5780
- } : {}, entry.marker.raw !== void 0 ? {
5781
- raw: entry.marker.raw
5782
- } : {});
5783
- });
5784
- }
5785
- },
5786
5290
  {
5787
5291
  key: "pushAdInsertionDebug",
5788
5292
  value: function pushAdInsertionDebug(event, segmentName, opts) {
@@ -6079,7 +5583,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6079
5583
  this.clearAdInsertionOffsetTimer();
6080
5584
  this.stopContinuousFetching();
6081
5585
  this.stopFillerBreakTimer();
6082
- this.clearAdStartTimer();
6083
5586
  this.clearAdStopTimer();
6084
5587
  this.clearAdFailsafeTimer();
6085
5588
  this.clearAdRequestWatchdog();
@@ -6107,7 +5610,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6107
5610
  (_this_adLayer = this.adLayer) === null || _this_adLayer === void 0 ? void 0 : _this_adLayer.destroy();
6108
5611
  this.consecutiveFailures = 0;
6109
5612
  this.debugLogEntries = [];
6110
- this.scteMarkerHistory = [];
6111
5613
  this.adInsertionDebugHistory = [];
6112
5614
  }
6113
5615
  }