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.
@@ -1391,7 +1391,6 @@ function createAdStormPlayer(contentVideo, options) {
1391
1391
  contentVideo.muted = originalMutedState;
1392
1392
  contentVideo.volume = originalVolume;
1393
1393
  currentAd = void 0;
1394
- tornDown = false;
1395
1394
  return [
1396
1395
  2,
1397
1396
  Promise.resolve()
@@ -1506,6 +1505,15 @@ function createAdStormPlayer(contentVideo, options) {
1506
1505
  if (!token) return [
1507
1506
  2
1508
1507
  ];
1508
+ if (currentAd) {
1509
+ preloadSlots.set(token, {
1510
+ ad: currentAd
1511
+ });
1512
+ currentAd = void 0;
1513
+ return [
1514
+ 2
1515
+ ];
1516
+ }
1509
1517
  requestContext = typeof arg1 === "string" ? arg2 : arg1;
1510
1518
  return [
1511
1519
  4,
@@ -1999,56 +2007,6 @@ function sendInitialTracking(licenseKey) {
1999
2007
  });
2000
2008
  })();
2001
2009
  }
2002
- function sendAdDetectTracking(licenseKey, adDetectInfo) {
2003
- return _async_to_generator(function() {
2004
- var clientInfo, browserId, trackingData, error;
2005
- return _ts_generator(this, function(_state) {
2006
- switch(_state.label){
2007
- case 0:
2008
- _state.trys.push([
2009
- 0,
2010
- 3,
2011
- ,
2012
- 4
2013
- ]);
2014
- clientInfo = getClientInfo();
2015
- return [
2016
- 4,
2017
- getBrowserID(clientInfo)
2018
- ];
2019
- case 1:
2020
- browserId = _state.sent();
2021
- trackingData = _object_spread({
2022
- browserId: browserId
2023
- }, clientInfo);
2024
- return [
2025
- 4,
2026
- sendTrackRequest(licenseKey, _object_spread_props(_object_spread({}, trackingData), {
2027
- licenseKey: licenseKey,
2028
- adDetectInfo: adDetectInfo
2029
- }))
2030
- ];
2031
- case 2:
2032
- _state.sent();
2033
- return [
2034
- 3,
2035
- 4
2036
- ];
2037
- case 3:
2038
- error = _state.sent();
2039
- console.error("[StormcloudVideoPlayer] Error sending ad detect tracking:", error);
2040
- return [
2041
- 3,
2042
- 4
2043
- ];
2044
- case 4:
2045
- return [
2046
- 2
2047
- ];
2048
- }
2049
- });
2050
- })();
2051
- }
2052
2010
  function sendAdLoadedTracking(licenseKey, adLoadedInfo) {
2053
2011
  return _async_to_generator(function() {
2054
2012
  var clientInfo, browserId, trackingData, error;
@@ -2733,7 +2691,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2733
2691
  this.continuousFetchLoopPromise = null;
2734
2692
  this.attached = false;
2735
2693
  this.inAdBreak = false;
2736
- this.ptsDriftEmaMs = 0;
2737
2694
  this.adPodQueue = [];
2738
2695
  this.lastHeartbeatTime = 0;
2739
2696
  this.currentAdIndex = 0;
@@ -2757,7 +2714,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2757
2714
  this.totalAdRequestsInBreak = 0;
2758
2715
  this.maxTotalAdRequestsPerBreak = 20;
2759
2716
  this.pendingAdBreak = null;
2760
- this.savedMutedStateBeforeScte = null;
2717
+ this.savedMutedStateBeforeAd = null;
2761
2718
  this.consecutiveFailures = 0;
2762
2719
  this.maxConsecutiveFailures = 5;
2763
2720
  this.lastAdRequestTime = 0;
@@ -2770,7 +2727,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2770
2727
  this.adRequestRetryBackoffMs = 1500;
2771
2728
  this.preloadedTokens = [];
2772
2729
  this.debugLogEntries = [];
2773
- this.scteMarkerHistory = [];
2774
2730
  this.adInsertionDebugHistory = [];
2775
2731
  initializePolyfills();
2776
2732
  var browserOverrides = getBrowserConfigOverrides();
@@ -3059,18 +3015,44 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3059
3015
  _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3060
3016
  detail: "ad break started (60s)"
3061
3017
  });
3062
- var marker = {
3063
- type: "start",
3064
- durationSeconds: 60,
3065
- raw: {
3066
- apiInsertionPoint: _this.lastAdInsertionPoint
3067
- }
3068
- };
3069
- _this.onScte35Marker(marker);
3018
+ void _this.handleAdStart(60);
3070
3019
  }, offsetMs);
3071
3020
  }
3072
3021
  }
3073
3022
  });
3023
+ this.hls.on(import_hls.default.Events.FRAG_PARSING_USERDATA, function(_evt, data) {
3024
+ var _ref;
3025
+ var samples = (_ref = data === null || data === void 0 ? void 0 : data.samples) !== null && _ref !== void 0 ? _ref : [];
3026
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3027
+ try {
3028
+ for(var _iterator = samples[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3029
+ var sample = _step.value;
3030
+ var _ref1, _ref2;
3031
+ var _data_frag, _data_frag1;
3032
+ var bytes = sample === null || sample === void 0 ? void 0 : sample.data;
3033
+ if (!bytes || bytes.length < 5) continue;
3034
+ var isSCTE35 = bytes[0] === 252;
3035
+ if (!isSCTE35) continue;
3036
+ 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 : "";
3037
+ _this.pushAdInsertionDebug("scte35_inserted", segName, {
3038
+ detail: "len=".concat(bytes.length, "B")
3039
+ });
3040
+ }
3041
+ } catch (err) {
3042
+ _didIteratorError = true;
3043
+ _iteratorError = err;
3044
+ } finally{
3045
+ try {
3046
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3047
+ _iterator.return();
3048
+ }
3049
+ } finally{
3050
+ if (_didIteratorError) {
3051
+ throw _iteratorError;
3052
+ }
3053
+ }
3054
+ }
3055
+ });
3074
3056
  this.hls.on(import_hls.default.Events.ERROR, function(_evt, data) {
3075
3057
  if (data === null || data === void 0 ? void 0 : data.fatal) {
3076
3058
  switch(data.type){
@@ -3145,8 +3127,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3145
3127
  _this.activeAdRequestToken = null;
3146
3128
  _this.showAds = true;
3147
3129
  if (_this.config.disableFiller) {
3148
- if (_this.savedMutedStateBeforeScte == null) {
3149
- _this.savedMutedStateBeforeScte = {
3130
+ if (_this.savedMutedStateBeforeAd == null) {
3131
+ _this.savedMutedStateBeforeAd = {
3150
3132
  muted: _this.video.muted,
3151
3133
  volume: _this.video.volume
3152
3134
  };
@@ -3174,10 +3156,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3174
3156
  });
3175
3157
  this.adLayer.on("content_resume", function() {
3176
3158
  var _ref, _ref1;
3177
- var _this_savedMutedStateBeforeScte, _this_savedMutedStateBeforeScte1;
3159
+ var _this_savedMutedStateBeforeAd, _this_savedMutedStateBeforeAd1;
3178
3160
  var remaining = _this.getRemainingAdMs();
3179
- 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();
3180
- 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();
3161
+ 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();
3162
+ 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();
3181
3163
  if (_this.config.debugAdTiming) {
3182
3164
  console.log("[StormcloudVideoPlayer] content_resume received, inAdBreak=%s, remaining=%s, preloadedTokens=%d, pendingNext=%s", _this.inAdBreak, remaining, _this.preloadedTokens.length, !!_this.pendingNextAdBids);
3183
3165
  }
@@ -3254,7 +3236,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3254
3236
  var remainingFinal = _this.getRemainingAdMs();
3255
3237
  if (_this.inAdBreak && remainingFinal > _this.MIN_AD_REMAINING_MS) {
3256
3238
  if (_this.config.debugAdTiming) {
3257
- console.log("[StormcloudVideoPlayer] content_resume: no more ads, showing filler for", remainingFinal, "ms");
3239
+ console.log("[StormcloudVideoPlayer] content_resume: ad ended/failed with time remaining, showing filler and continuing fetch for", remainingFinal, "ms");
3258
3240
  }
3259
3241
  if (!_this.config.disableFiller) {
3260
3242
  _this.showPlaceholderLayer();
@@ -3272,7 +3254,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3272
3254
  (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3273
3255
  }
3274
3256
  }
3275
- _this.stopContinuousFetching();
3257
+ _this.continuousFetchingActive = true;
3258
+ _this.startContinuousFetchLoop();
3276
3259
  return;
3277
3260
  }
3278
3261
  if (_this.config.debugAdTiming) {
@@ -3419,547 +3402,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3419
3402
  return !!(this.config.allowNativeHls && canNative);
3420
3403
  }
3421
3404
  },
3422
- {
3423
- key: "onId3Tag",
3424
- value: function onId3Tag(tag) {
3425
- if (typeof tag.ptsSeconds === "number") {
3426
- this.updatePtsDrift(tag.ptsSeconds);
3427
- }
3428
- var marker = this.parseScte35FromId3(tag);
3429
- if (marker) {
3430
- this.onScte35Marker(marker);
3431
- }
3432
- }
3433
- },
3434
- {
3435
- key: "parseScte35FromId3",
3436
- value: function parseScte35FromId3(tag) {
3437
- var text = this.decodeId3ValueToText(tag.value);
3438
- if (!text) return void 0;
3439
- var cueOutMatch = text.match(/EXT-X-CUE-OUT(?::([^\r\n]*))?/i) || text.match(/CUE-OUT(?::([^\r\n]*))?/i);
3440
- if (cueOutMatch) {
3441
- var _cueOutMatch_;
3442
- var arg = ((_cueOutMatch_ = cueOutMatch[1]) !== null && _cueOutMatch_ !== void 0 ? _cueOutMatch_ : "").trim();
3443
- var dur = this.parseCueOutDuration(arg);
3444
- var marker = _object_spread_props(_object_spread({
3445
- type: "start"
3446
- }, tag.ptsSeconds !== void 0 ? {
3447
- ptsSeconds: tag.ptsSeconds
3448
- } : {}, dur !== void 0 ? {
3449
- durationSeconds: dur
3450
- } : {}), {
3451
- raw: {
3452
- id3: text
3453
- }
3454
- });
3455
- return marker;
3456
- }
3457
- var cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\r\n]*)/i);
3458
- if (cueOutContMatch) {
3459
- var _cueOutContMatch_;
3460
- var arg1 = ((_cueOutContMatch_ = cueOutContMatch[1]) !== null && _cueOutContMatch_ !== void 0 ? _cueOutContMatch_ : "").trim();
3461
- var cont = this.parseCueOutCont(arg1);
3462
- var marker1 = _object_spread_props(_object_spread({
3463
- type: "progress"
3464
- }, tag.ptsSeconds !== void 0 ? {
3465
- ptsSeconds: tag.ptsSeconds
3466
- } : {}, (cont === null || cont === void 0 ? void 0 : cont.duration) !== void 0 ? {
3467
- durationSeconds: cont.duration
3468
- } : {}), {
3469
- raw: {
3470
- id3: text
3471
- }
3472
- });
3473
- return marker1;
3474
- }
3475
- var cueInMatch = text.match(/EXT-X-CUE-IN\b/i) || text.match(/CUE-IN\b/i);
3476
- if (cueInMatch) {
3477
- var marker2 = _object_spread_props(_object_spread({
3478
- type: "end"
3479
- }, tag.ptsSeconds !== void 0 ? {
3480
- ptsSeconds: tag.ptsSeconds
3481
- } : {}), {
3482
- raw: {
3483
- id3: text
3484
- }
3485
- });
3486
- return marker2;
3487
- }
3488
- var daterangeMatch = text.match(/EXT-X-DATERANGE:([^\r\n]*)/i);
3489
- if (daterangeMatch) {
3490
- var _daterangeMatch_, _attrs_CLASS;
3491
- var attrs = this.parseAttributeList((_daterangeMatch_ = daterangeMatch[1]) !== null && _daterangeMatch_ !== void 0 ? _daterangeMatch_ : "");
3492
- var hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3493
- var hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
3494
- var klass = String((_attrs_CLASS = attrs["CLASS"]) !== null && _attrs_CLASS !== void 0 ? _attrs_CLASS : "");
3495
- var duration = this.toNumber(attrs["DURATION"]);
3496
- if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
3497
- var marker3 = _object_spread_props(_object_spread({
3498
- type: "start"
3499
- }, tag.ptsSeconds !== void 0 ? {
3500
- ptsSeconds: tag.ptsSeconds
3501
- } : {}, duration !== void 0 ? {
3502
- durationSeconds: duration
3503
- } : {}), {
3504
- raw: {
3505
- id3: text,
3506
- attrs: attrs
3507
- }
3508
- });
3509
- return marker3;
3510
- }
3511
- if (hasScteIn) {
3512
- var marker4 = _object_spread_props(_object_spread({
3513
- type: "end"
3514
- }, tag.ptsSeconds !== void 0 ? {
3515
- ptsSeconds: tag.ptsSeconds
3516
- } : {}), {
3517
- raw: {
3518
- id3: text,
3519
- attrs: attrs
3520
- }
3521
- });
3522
- return marker4;
3523
- }
3524
- }
3525
- if (/SCTE35-OUT/i.test(text)) {
3526
- var marker5 = _object_spread_props(_object_spread({
3527
- type: "start"
3528
- }, tag.ptsSeconds !== void 0 ? {
3529
- ptsSeconds: tag.ptsSeconds
3530
- } : {}), {
3531
- raw: {
3532
- id3: text
3533
- }
3534
- });
3535
- return marker5;
3536
- }
3537
- if (/SCTE35-IN/i.test(text)) {
3538
- var marker6 = _object_spread_props(_object_spread({
3539
- type: "end"
3540
- }, tag.ptsSeconds !== void 0 ? {
3541
- ptsSeconds: tag.ptsSeconds
3542
- } : {}), {
3543
- raw: {
3544
- id3: text
3545
- }
3546
- });
3547
- return marker6;
3548
- }
3549
- if (_instanceof(tag.value, Uint8Array)) {
3550
- var bin = this.parseScte35Binary(tag.value);
3551
- if (bin) return bin;
3552
- }
3553
- return void 0;
3554
- }
3555
- },
3556
- {
3557
- key: "decodeId3ValueToText",
3558
- value: function decodeId3ValueToText(value) {
3559
- try {
3560
- if (typeof value === "string") return value;
3561
- var decoder = new TextDecoder("utf-8", {
3562
- fatal: false
3563
- });
3564
- var text = decoder.decode(value);
3565
- if (text && /[\x20-\x7E]/.test(text)) return text;
3566
- var out = "";
3567
- for(var i = 0; i < value.length; i++)out += String.fromCharCode(value[i]);
3568
- return out;
3569
- } catch (unused) {
3570
- return void 0;
3571
- }
3572
- }
3573
- },
3574
- {
3575
- key: "onScte35Marker",
3576
- value: function onScte35Marker(marker) {
3577
- var _this = this;
3578
- if (this.config.disableAds) return;
3579
- this.pushScteMarker(marker);
3580
- this.pushDebugLog("info", "scte35", "SCTE-35 marker detected", {
3581
- type: marker.type,
3582
- ptsSeconds: marker.ptsSeconds,
3583
- durationSeconds: marker.durationSeconds,
3584
- currentTime: this.video.currentTime
3585
- });
3586
- if (this.config.debugAdTiming) {
3587
- console.log("[StormcloudVideoPlayer] SCTE-35 marker detected:", {
3588
- type: marker.type,
3589
- ptsSeconds: marker.ptsSeconds,
3590
- durationSeconds: marker.durationSeconds,
3591
- currentTime: this.video.currentTime,
3592
- raw: marker.raw,
3593
- hasPendingAdBreak: !!this.pendingAdBreak
3594
- });
3595
- }
3596
- if (marker.type === "start") {
3597
- var _this_config_immediateManifestAds;
3598
- var _this_pendingAdBreak;
3599
- if (this.savedMutedStateBeforeScte == null) {
3600
- this.savedMutedStateBeforeScte = {
3601
- muted: this.video.muted,
3602
- volume: this.video.volume
3603
- };
3604
- this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
3605
- }
3606
- if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
3607
- this.video.muted = true;
3608
- this.video.volume = 0;
3609
- if (this.config.debugAdTiming) {
3610
- console.log("[StormcloudVideoPlayer] Muted video on SCTE start marker");
3611
- }
3612
- }
3613
- if (this.inAdBreak) {
3614
- if (marker.durationSeconds != null) {
3615
- var newDurationMs = marker.durationSeconds * 1e3;
3616
- if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
3617
- this.expectedAdBreakDurationMs = newDurationMs;
3618
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
3619
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
3620
- this.scheduleAdStopCountdown(remainingMs);
3621
- if (this.config.debugAdTiming) {
3622
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
3623
- }
3624
- }
3625
- }
3626
- return;
3627
- }
3628
- this.inAdBreak = true;
3629
- 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;
3630
- this.expectedAdBreakDurationMs = durationMs;
3631
- this.currentAdBreakStartWallClockMs = Date.now();
3632
- if (this.config.licenseKey) {
3633
- var _this_pendingAdBreak1;
3634
- var adDetectInfo = _object_spread({
3635
- source: "scte35",
3636
- timestamp: /* @__PURE__ */ new Date().toISOString()
3637
- }, marker.durationSeconds != null && {
3638
- durationSeconds: marker.durationSeconds
3639
- }, marker.ptsSeconds != null && {
3640
- ptsSeconds: marker.ptsSeconds
3641
- }, ((_this_pendingAdBreak1 = this.pendingAdBreak) === null || _this_pendingAdBreak1 === void 0 ? void 0 : _this_pendingAdBreak1.detectedAtFragmentSn) != null && {
3642
- detectedAtFragmentSn: this.pendingAdBreak.detectedAtFragmentSn
3643
- });
3644
- sendAdDetectTracking(this.config.licenseKey, adDetectInfo);
3645
- }
3646
- var isManifestMarker = this.isManifestBasedMarker(marker);
3647
- var forceImmediate = (_this_config_immediateManifestAds = this.config.immediateManifestAds) !== null && _this_config_immediateManifestAds !== void 0 ? _this_config_immediateManifestAds : true;
3648
- if (this.config.debugAdTiming) {
3649
- console.log("[StormcloudVideoPlayer] Ad start decision:", {
3650
- isManifestMarker: isManifestMarker,
3651
- forceImmediate: forceImmediate,
3652
- hasPts: typeof marker.ptsSeconds === "number"
3653
- });
3654
- }
3655
- if (isManifestMarker && forceImmediate) {
3656
- if (this.config.debugAdTiming) {
3657
- console.log("[StormcloudVideoPlayer] Starting ad immediately (manifest-based)");
3658
- }
3659
- this.clearAdStartTimer();
3660
- this.handleAdStart(marker);
3661
- } else if (typeof marker.ptsSeconds === "number") {
3662
- var _this_config_driftToleranceMs;
3663
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
3664
- var nowMs = this.video.currentTime * 1e3;
3665
- var estCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
3666
- var deltaMs = Math.floor(marker.ptsSeconds * 1e3 - estCurrentPtsMs);
3667
- if (this.config.debugAdTiming) {
3668
- console.log("[StormcloudVideoPlayer] PTS-based timing calculation:", {
3669
- nowMs: nowMs,
3670
- estCurrentPtsMs: estCurrentPtsMs,
3671
- markerPtsMs: marker.ptsSeconds * 1e3,
3672
- deltaMs: deltaMs,
3673
- tolerance: tol
3674
- });
3675
- }
3676
- if (deltaMs > tol) {
3677
- if (this.config.debugAdTiming) {
3678
- console.log("[StormcloudVideoPlayer] Scheduling ad start in ".concat(deltaMs, "ms"));
3679
- }
3680
- this.scheduleAdStartIn(deltaMs);
3681
- } else {
3682
- if (this.config.debugAdTiming) {
3683
- console.log("[StormcloudVideoPlayer] Starting ad immediately (within tolerance)");
3684
- }
3685
- this.clearAdStartTimer();
3686
- this.handleAdStart(marker);
3687
- }
3688
- } else {
3689
- if (this.config.debugAdTiming) {
3690
- console.log("[StormcloudVideoPlayer] Starting ad immediately (fallback)");
3691
- }
3692
- this.clearAdStartTimer();
3693
- this.handleAdStart(marker);
3694
- }
3695
- if (this.expectedAdBreakDurationMs != null) {
3696
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
3697
- }
3698
- return;
3699
- }
3700
- if (marker.type === "progress" && this.inAdBreak) {
3701
- if (marker.durationSeconds != null) {
3702
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
3703
- }
3704
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
3705
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
3706
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
3707
- this.scheduleAdStopCountdown(remainingMs1);
3708
- }
3709
- if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
3710
- var bids = this.pendingNextAdBids;
3711
- this.pendingNextAdBids = null;
3712
- this.currentAdIndex++;
3713
- this.adLayer.playAd(bids).catch(function() {
3714
- return _this.handleAdFailure();
3715
- });
3716
- }
3717
- return;
3718
- }
3719
- if (marker.type === "end") {
3720
- var remaining = this.getRemainingAdMs();
3721
- var adPlaying = this.adLayer.isAdPlaying();
3722
- var hasQueuedAds = this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0;
3723
- if (this.config.debugAdTiming) {
3724
- console.log("[StormcloudVideoPlayer] SCTE-35 end marker received:", {
3725
- inAdBreak: this.inAdBreak,
3726
- remaining: remaining,
3727
- adPlaying: adPlaying,
3728
- hasQueuedAds: hasQueuedAds,
3729
- activeAdRequest: this.activeAdRequestToken !== null
3730
- });
3731
- }
3732
- if (!this.inAdBreak) {
3733
- if (this.config.debugAdTiming) {
3734
- console.log("[StormcloudVideoPlayer] Ignoring SCTE-35 end marker - not in ad break");
3735
- }
3736
- return;
3737
- }
3738
- if (adPlaying || remaining > 500) {
3739
- if (this.config.debugAdTiming) {
3740
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
3741
- }
3742
- return;
3743
- }
3744
- this.inAdBreak = false;
3745
- this.expectedAdBreakDurationMs = void 0;
3746
- this.currentAdBreakStartWallClockMs = void 0;
3747
- this.clearAdStartTimer();
3748
- this.clearAdStopTimer();
3749
- if (adPlaying) {
3750
- this.adLayer.stop().catch(function() {});
3751
- }
3752
- this.handleAdPodComplete();
3753
- return;
3754
- }
3755
- }
3756
- },
3757
- {
3758
- key: "parseCueOutDuration",
3759
- value: function parseCueOutDuration(value) {
3760
- var num = parseFloat(value.trim());
3761
- if (!Number.isNaN(num)) return num;
3762
- var match = value.match(/(?:^|[,\s])DURATION\s*=\s*([0-9.]+)/i) || value.match(/Duration\s*=\s*([0-9.]+)/i);
3763
- if (match && match[1] != null) {
3764
- var dStr = match[1];
3765
- var d = parseFloat(dStr);
3766
- return Number.isNaN(d) ? void 0 : d;
3767
- }
3768
- return void 0;
3769
- }
3770
- },
3771
- {
3772
- key: "parseCueOutCont",
3773
- value: function parseCueOutCont(value) {
3774
- var res = {};
3775
- var elapsedMatch = value.match(/Elapsed\s*=\s*([0-9.]+)/i);
3776
- var durationMatch = value.match(/Duration\s*=\s*([0-9.]+)/i);
3777
- if (elapsedMatch && elapsedMatch[1] != null) {
3778
- var e = parseFloat(elapsedMatch[1]);
3779
- if (!Number.isNaN(e)) res.elapsed = e;
3780
- }
3781
- if (durationMatch && durationMatch[1] != null) {
3782
- var d = parseFloat(durationMatch[1]);
3783
- if (!Number.isNaN(d)) res.duration = d;
3784
- }
3785
- if (!("elapsed" in res) || !("duration" in res)) {
3786
- var slashMatch = value.match(/([0-9.]+)\s*\/\s*([0-9.]+)/);
3787
- if (slashMatch && slashMatch[1] && slashMatch[2]) {
3788
- var elapsed = parseFloat(slashMatch[1]);
3789
- var duration = parseFloat(slashMatch[2]);
3790
- if (!Number.isNaN(elapsed) && !("elapsed" in res)) res.elapsed = elapsed;
3791
- if (!Number.isNaN(duration) && !("duration" in res)) res.duration = duration;
3792
- }
3793
- }
3794
- if ("elapsed" in res || "duration" in res) return res;
3795
- return void 0;
3796
- }
3797
- },
3798
- {
3799
- key: "parseAttributeList",
3800
- value: function parseAttributeList(value) {
3801
- var attrs = {};
3802
- var regex = /([A-Z0-9-]+)=(("[^"]*")|([^",]*))(?:,|$)/gi;
3803
- var match;
3804
- while((match = regex.exec(value)) !== null){
3805
- var _match_, _ref, _match_1;
3806
- var key = (_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : "";
3807
- var rawVal = (_ref = (_match_1 = match[3]) !== null && _match_1 !== void 0 ? _match_1 : match[4]) !== null && _ref !== void 0 ? _ref : "";
3808
- if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
3809
- rawVal = rawVal.slice(1, -1);
3810
- }
3811
- if (key) {
3812
- attrs[key] = rawVal;
3813
- }
3814
- }
3815
- return attrs;
3816
- }
3817
- },
3818
- {
3819
- key: "toNumber",
3820
- value: function toNumber(val) {
3821
- if (val == null) return void 0;
3822
- var n = typeof val === "string" ? parseFloat(val) : Number(val);
3823
- return Number.isNaN(n) ? void 0 : n;
3824
- }
3825
- },
3826
- {
3827
- key: "isManifestBasedMarker",
3828
- value: function isManifestBasedMarker(marker) {
3829
- var raw = marker.raw;
3830
- if (!raw) return false;
3831
- if (raw.tag) {
3832
- var tag = String(raw.tag);
3833
- return tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-CUE-IN") || tag.includes("EXT-X-DATERANGE");
3834
- }
3835
- if (raw.id3) return false;
3836
- if (raw.splice_command_type) return false;
3837
- return false;
3838
- }
3839
- },
3840
- {
3841
- key: "parseScte35Binary",
3842
- value: function parseScte35Binary(data) {
3843
- var BitReader = /*#__PURE__*/ function() {
3844
- function BitReader(buf) {
3845
- _class_call_check(this, BitReader);
3846
- this.buf = buf;
3847
- this.bytePos = 0;
3848
- this.bitPos = 0;
3849
- }
3850
- _create_class(BitReader, [
3851
- {
3852
- key: "readBits",
3853
- value: function readBits(numBits) {
3854
- var result = 0;
3855
- while(numBits > 0){
3856
- if (this.bytePos >= this.buf.length) return result;
3857
- var remainingInByte = 8 - this.bitPos;
3858
- var toRead = Math.min(numBits, remainingInByte);
3859
- var currentByte = this.buf[this.bytePos];
3860
- var shift = remainingInByte - toRead;
3861
- var mask = (1 << toRead) - 1 & 255;
3862
- var bits = currentByte >> shift & mask;
3863
- result = result << toRead | bits;
3864
- this.bitPos += toRead;
3865
- if (this.bitPos >= 8) {
3866
- this.bitPos = 0;
3867
- this.bytePos += 1;
3868
- }
3869
- numBits -= toRead;
3870
- }
3871
- return result >>> 0;
3872
- }
3873
- },
3874
- {
3875
- key: "skipBits",
3876
- value: function skipBits(n) {
3877
- this.readBits(n);
3878
- }
3879
- }
3880
- ]);
3881
- return BitReader;
3882
- }();
3883
- var r = new BitReader(data);
3884
- var tableId = r.readBits(8);
3885
- if (tableId !== 252) return void 0;
3886
- r.readBits(1);
3887
- r.readBits(1);
3888
- r.readBits(2);
3889
- var sectionLength = r.readBits(12);
3890
- r.readBits(8);
3891
- r.readBits(1);
3892
- r.readBits(6);
3893
- var ptsAdjHigh = r.readBits(1);
3894
- var ptsAdjLow = r.readBits(32);
3895
- void ptsAdjHigh;
3896
- void ptsAdjLow;
3897
- r.readBits(8);
3898
- r.readBits(12);
3899
- var spliceCommandLength = r.readBits(12);
3900
- var spliceCommandType = r.readBits(8);
3901
- if (spliceCommandType !== 5) {
3902
- return void 0;
3903
- }
3904
- r.readBits(32);
3905
- var cancel = r.readBits(1) === 1;
3906
- r.readBits(7);
3907
- if (cancel) return void 0;
3908
- var outOfNetwork = r.readBits(1) === 1;
3909
- var programSpliceFlag = r.readBits(1) === 1;
3910
- var durationFlag = r.readBits(1) === 1;
3911
- var spliceImmediateFlag = r.readBits(1) === 1;
3912
- r.readBits(4);
3913
- if (programSpliceFlag && !spliceImmediateFlag) {
3914
- var timeSpecifiedFlag = r.readBits(1) === 1;
3915
- if (timeSpecifiedFlag) {
3916
- r.readBits(6);
3917
- r.readBits(33);
3918
- } else {
3919
- r.readBits(7);
3920
- }
3921
- } else if (!programSpliceFlag) {
3922
- var componentCount = r.readBits(8);
3923
- for(var i = 0; i < componentCount; i++){
3924
- r.readBits(8);
3925
- if (!spliceImmediateFlag) {
3926
- var timeSpecifiedFlag1 = r.readBits(1) === 1;
3927
- if (timeSpecifiedFlag1) {
3928
- r.readBits(6);
3929
- r.readBits(33);
3930
- } else {
3931
- r.readBits(7);
3932
- }
3933
- }
3934
- }
3935
- }
3936
- var durationSeconds = void 0;
3937
- if (durationFlag) {
3938
- r.readBits(6);
3939
- r.readBits(1);
3940
- var high = r.readBits(1);
3941
- var low = r.readBits(32);
3942
- var durationTicks = high * 4294967296 + low;
3943
- durationSeconds = durationTicks / 9e4;
3944
- }
3945
- r.readBits(16);
3946
- r.readBits(8);
3947
- r.readBits(8);
3948
- if (outOfNetwork) {
3949
- var marker = _object_spread_props(_object_spread({
3950
- type: "start"
3951
- }, durationSeconds !== void 0 ? {
3952
- durationSeconds: durationSeconds
3953
- } : {}), {
3954
- raw: {
3955
- splice_command_type: 5
3956
- }
3957
- });
3958
- return marker;
3959
- }
3960
- return void 0;
3961
- }
3962
- },
3963
3405
  {
3964
3406
  key: "initializeTracking",
3965
3407
  value: function initializeTracking() {
@@ -4081,35 +3523,35 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4081
3523
  },
4082
3524
  {
4083
3525
  key: "startAdPrefetch",
4084
- value: function startAdPrefetch(marker, fragmentSn) {
3526
+ value: function startAdPrefetch(durationSeconds, fragmentSn) {
4085
3527
  if (this.config.disableAds) return;
4086
3528
  if (this.pendingAdBreak || this.inAdBreak) {
4087
3529
  return;
4088
3530
  }
4089
- this.pendingAdBreak = _object_spread_props(_object_spread({
4090
- marker: marker
4091
- }, fragmentSn !== void 0 ? {
3531
+ this.pendingAdBreak = _object_spread_props(_object_spread({}, durationSeconds !== void 0 ? {
3532
+ durationSeconds: durationSeconds
3533
+ } : {}, fragmentSn !== void 0 ? {
4092
3534
  detectedAtFragmentSn: fragmentSn
4093
3535
  } : {}), {
4094
3536
  isFetching: false,
4095
3537
  fetchStartTime: Date.now()
4096
3538
  });
4097
- void this.runAdPrefetch(marker);
3539
+ void this.runAdPrefetch(durationSeconds);
4098
3540
  if (this.config.debugAdTiming) {
4099
- console.log("[PREFETCH] Ad break marker registered, multi-ad prefetch started");
3541
+ console.log("[PREFETCH] Ad break registered, multi-ad prefetch started");
4100
3542
  }
4101
3543
  }
4102
3544
  },
4103
3545
  {
4104
3546
  key: "runAdPrefetch",
4105
- value: function runAdPrefetch(marker) {
3547
+ value: function runAdPrefetch(durationSeconds) {
4106
3548
  return _async_to_generator(function() {
4107
- var _this, _marker_durationSeconds, _ref, _firstBids_, durSec, context, firstBids, unused, adDurationSec, estimatedCount, firstToken, remaining, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, result, token;
3549
+ var _this, _ref, _firstBids_, durSec, context, firstBids, unused, adDurationSec, estimatedCount, firstToken, unused1, remaining, results, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, result, token, unused2, err;
4108
3550
  return _ts_generator(this, function(_state) {
4109
3551
  switch(_state.label){
4110
3552
  case 0:
4111
3553
  _this = this;
4112
- durSec = (_marker_durationSeconds = marker.durationSeconds) !== null && _marker_durationSeconds !== void 0 ? _marker_durationSeconds : 60;
3554
+ durSec = durationSeconds !== null && durationSeconds !== void 0 ? durationSeconds : 60;
4113
3555
  context = {
4114
3556
  breakDurationSec: durSec,
4115
3557
  remainingBreakSec: durSec
@@ -4149,6 +3591,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4149
3591
  if (this.config.debugAdTiming) {
4150
3592
  console.log("[PREFETCH] First VAST request returned no ad, aborting prefetch");
4151
3593
  }
3594
+ this.clearPendingAdBreak();
4152
3595
  return [
4153
3596
  2
4154
3597
  ];
@@ -4159,14 +3602,43 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4159
3602
  console.log("[PREFETCH] Ad duration=".concat(adDurationSec, "s, break=").concat(durSec, "s → ").concat(estimatedCount, " ad(s) needed"));
4160
3603
  }
4161
3604
  firstToken = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
4162
- this.preloadedTokens.push(firstToken);
4163
- void this.adLayer.preloadAd(firstBids, firstToken);
3605
+ _state.label = 5;
3606
+ case 5:
3607
+ _state.trys.push([
3608
+ 5,
3609
+ 7,
3610
+ ,
3611
+ 8
3612
+ ]);
3613
+ return [
3614
+ 4,
3615
+ this.adLayer.preloadAd(firstBids, firstToken)
3616
+ ];
3617
+ case 6:
3618
+ _state.sent();
3619
+ if (!this.inAdBreak) {
3620
+ this.preloadedTokens.push(firstToken);
3621
+ if (this.config.debugAdTiming) {
3622
+ console.log("[PREFETCH] First ad preloaded and queued, token=".concat(firstToken));
3623
+ }
3624
+ }
3625
+ return [
3626
+ 3,
3627
+ 8
3628
+ ];
3629
+ case 7:
3630
+ unused1 = _state.sent();
4164
3631
  if (this.config.debugAdTiming) {
4165
- console.log("[PREFETCH] First ad preloading, token=".concat(firstToken));
3632
+ console.warn("[PREFETCH] First ad preload failed, token=".concat(firstToken));
4166
3633
  }
3634
+ return [
3635
+ 3,
3636
+ 8
3637
+ ];
3638
+ case 8:
4167
3639
  if (!(estimatedCount > 1)) return [
4168
3640
  3,
4169
- 6
3641
+ 19
4170
3642
  ];
4171
3643
  remaining = Array.from({
4172
3644
  length: estimatedCount - 1
@@ -4188,38 +3660,100 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4188
3660
  4,
4189
3661
  Promise.all(remaining)
4190
3662
  ];
4191
- case 5:
3663
+ case 9:
4192
3664
  results = _state.sent();
4193
3665
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3666
+ _state.label = 10;
3667
+ case 10:
3668
+ _state.trys.push([
3669
+ 10,
3670
+ 17,
3671
+ 18,
3672
+ 19
3673
+ ]);
3674
+ _iterator = results[Symbol.iterator]();
3675
+ _state.label = 11;
3676
+ case 11:
3677
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
3678
+ 3,
3679
+ 16
3680
+ ];
3681
+ result = _step.value;
3682
+ if (this.inAdBreak) return [
3683
+ 3,
3684
+ 16
3685
+ ];
3686
+ if (!(result.ok && result.value.length > 0)) return [
3687
+ 3,
3688
+ 15
3689
+ ];
3690
+ token = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
3691
+ _state.label = 12;
3692
+ case 12:
3693
+ _state.trys.push([
3694
+ 12,
3695
+ 14,
3696
+ ,
3697
+ 15
3698
+ ]);
3699
+ return [
3700
+ 4,
3701
+ this.adLayer.preloadAd(result.value, token)
3702
+ ];
3703
+ case 13:
3704
+ _state.sent();
3705
+ if (!this.inAdBreak) {
3706
+ this.preloadedTokens.push(token);
3707
+ if (this.config.debugAdTiming) {
3708
+ console.log("[PREFETCH] Additional ad preloaded and queued, token=".concat(token));
3709
+ }
3710
+ }
3711
+ return [
3712
+ 3,
3713
+ 15
3714
+ ];
3715
+ case 14:
3716
+ unused2 = _state.sent();
3717
+ if (this.config.debugAdTiming) {
3718
+ console.warn("[PREFETCH] Additional ad preload failed, token=".concat(token));
3719
+ }
3720
+ return [
3721
+ 3,
3722
+ 15
3723
+ ];
3724
+ case 15:
3725
+ _iteratorNormalCompletion = true;
3726
+ return [
3727
+ 3,
3728
+ 11
3729
+ ];
3730
+ case 16:
3731
+ return [
3732
+ 3,
3733
+ 19
3734
+ ];
3735
+ case 17:
3736
+ err = _state.sent();
3737
+ _didIteratorError = true;
3738
+ _iteratorError = err;
3739
+ return [
3740
+ 3,
3741
+ 19
3742
+ ];
3743
+ case 18:
4194
3744
  try {
4195
- for(_iterator = results[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4196
- result = _step.value;
4197
- if (this.inAdBreak) break;
4198
- if (result.ok && result.value.length > 0) {
4199
- token = "preload_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 7));
4200
- this.preloadedTokens.push(token);
4201
- void this.adLayer.preloadAd(result.value, token);
4202
- if (this.config.debugAdTiming) {
4203
- console.log("[PREFETCH] Additional ad preloading, token=".concat(token));
4204
- }
4205
- }
3745
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3746
+ _iterator.return();
4206
3747
  }
4207
- } catch (err) {
4208
- _didIteratorError = true;
4209
- _iteratorError = err;
4210
3748
  } finally{
4211
- try {
4212
- if (!_iteratorNormalCompletion && _iterator.return != null) {
4213
- _iterator.return();
4214
- }
4215
- } finally{
4216
- if (_didIteratorError) {
4217
- throw _iteratorError;
4218
- }
3749
+ if (_didIteratorError) {
3750
+ throw _iteratorError;
4219
3751
  }
4220
3752
  }
4221
- _state.label = 6;
4222
- case 6:
3753
+ return [
3754
+ 7
3755
+ ];
3756
+ case 19:
4223
3757
  if (this.config.debugAdTiming) {
4224
3758
  console.log("[PREFETCH] Pre-fetch complete: ".concat(this.preloadedTokens.length, " ad(s) queued"));
4225
3759
  }
@@ -4291,7 +3825,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4291
3825
  key: "fetchAdInsertionPoint",
4292
3826
  value: function fetchAdInsertionPoint() {
4293
3827
  return _async_to_generator(function() {
4294
- var _this_lastAdInsertionPoint, resp, data, isNew, unused;
3828
+ var _this_lastAdInsertionPoint, _this_lastAdInsertionPoint1, resp, data, prevSegment, isNew, segmentChanged, unused;
4295
3829
  return _ts_generator(this, function(_state) {
4296
3830
  switch(_state.label){
4297
3831
  case 0:
@@ -4321,7 +3855,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4321
3855
  ];
4322
3856
  case 3:
4323
3857
  data = _state.sent();
4324
- isNew = data.updated_at !== ((_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.updated_at);
3858
+ prevSegment = (_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.segment_ts_name;
3859
+ isNew = data.updated_at !== ((_this_lastAdInsertionPoint1 = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint1 === void 0 ? void 0 : _this_lastAdInsertionPoint1.updated_at);
3860
+ segmentChanged = prevSegment !== data.segment_ts_name;
4325
3861
  this.lastAdInsertionPoint = data;
4326
3862
  if (isNew) {
4327
3863
  this.pushAdInsertionDebug("api_response", data.segment_ts_name, {
@@ -4329,6 +3865,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4329
3865
  updatedAt: data.updated_at,
4330
3866
  detail: "project=".concat(data.project_id)
4331
3867
  });
3868
+ if (segmentChanged && (this.pendingAdBreak || this.adInsertionOffsetTimerId != null)) {
3869
+ this.clearAdInsertionOffsetTimer();
3870
+ this.clearPendingAdBreak();
3871
+ this.cancelAndClearPreloadedTokens();
3872
+ if (this.config.debugAdTiming) {
3873
+ console.log("[StormcloudVideoPlayer] Insertion segment changed (".concat(prevSegment, " → ").concat(data.segment_ts_name, "), stale pending state cleared"));
3874
+ }
3875
+ }
3876
+ this.checkAdInsertionInManifest();
4332
3877
  }
4333
3878
  if (this.config.debugAdTiming) {
4334
3879
  console.log("[StormcloudVideoPlayer] Ad insertion point API response:", data);
@@ -4358,6 +3903,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4358
3903
  {
4359
3904
  key: "checkAdInsertionInManifest",
4360
3905
  value: function checkAdInsertionInManifest() {
3906
+ var _this = this;
4361
3907
  var _this_hls;
4362
3908
  if (!this.lastAdInsertionPoint) return;
4363
3909
  if (this.inAdBreak || this.pendingAdBreak) return;
@@ -4377,22 +3923,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4377
3923
  for(var _iterator1 = fragments[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
4378
3924
  var frag = _step1.value;
4379
3925
  if (this.fragmentMatchesSegment(frag, segmentName)) {
4380
- var _ref;
3926
+ var _frag_start, _frag_duration, _ref;
3927
+ var currentTime = this.video.currentTime;
3928
+ var fragStart = (_frag_start = frag.start) !== null && _frag_start !== void 0 ? _frag_start : 0;
3929
+ var fragDuration = (_frag_duration = frag.duration) !== null && _frag_duration !== void 0 ? _frag_duration : 0;
3930
+ var isCurrentlyPlaying = fragDuration > 0 && currentTime >= fragStart && currentTime < fragStart + fragDuration;
4381
3931
  this.pushAdInsertionDebug("segment_found", segmentName, {
4382
- detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
3932
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?", " ").concat(isCurrentlyPlaying ? "(playing)" : "(future)")
4383
3933
  });
4384
3934
  if (this.config.debugAdTiming) {
4385
- console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — starting pre-fetch'));
3935
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — ') + (isCurrentlyPlaying ? "currently playing, scheduling directly" : "starting pre-fetch"));
3936
+ }
3937
+ this.startAdPrefetch(60, frag === null || frag === void 0 ? void 0 : frag.sn);
3938
+ if (isCurrentlyPlaying && !this.inAdBreak) {
3939
+ var _this_lastAdInsertionPoint_offset_seconds, _ref1;
3940
+ this.processedAdInsertionUpdatedAt = this.lastAdInsertionPoint.updated_at;
3941
+ var rawOffsetMs = ((_this_lastAdInsertionPoint_offset_seconds = this.lastAdInsertionPoint.offset_seconds) !== null && _this_lastAdInsertionPoint_offset_seconds !== void 0 ? _this_lastAdInsertionPoint_offset_seconds : 0) * 1e3;
3942
+ var elapsedInFragMs = (currentTime - fragStart) * 1e3;
3943
+ var remainingOffsetMs = Math.max(0, rawOffsetMs - elapsedInFragMs);
3944
+ this.pushAdInsertionDebug("segment_playing", segmentName, {
3945
+ offsetSeconds: this.lastAdInsertionPoint.offset_seconds,
3946
+ detail: "sn=".concat((_ref1 = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref1 !== void 0 ? _ref1 : "?", " (caught in manifest check)")
3947
+ });
3948
+ this.pushAdInsertionDebug("ad_scheduled", segmentName, {
3949
+ offsetSeconds: this.lastAdInsertionPoint.offset_seconds,
3950
+ detail: "in ".concat(remainingOffsetMs, "ms (adjusted for elapsed ").concat(Math.round(elapsedInFragMs), "ms)")
3951
+ });
3952
+ this.clearAdInsertionOffsetTimer();
3953
+ this.adInsertionOffsetTimerId = window.setTimeout(function() {
3954
+ _this.adInsertionOffsetTimerId = void 0;
3955
+ if (_this.inAdBreak) return;
3956
+ _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3957
+ detail: "ad break started (60s, via manifest-check path)"
3958
+ });
3959
+ void _this.handleAdStart(60);
3960
+ }, remainingOffsetMs);
4386
3961
  }
4387
- var marker = {
4388
- type: "start",
4389
- durationSeconds: 60,
4390
- raw: {
4391
- apiInsertionPoint: this.lastAdInsertionPoint,
4392
- earlyDetection: true
4393
- }
4394
- };
4395
- this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
4396
3962
  return;
4397
3963
  }
4398
3964
  }
@@ -4430,8 +3996,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4430
3996
  {
4431
3997
  key: "fragmentMatchesSegment",
4432
3998
  value: function fragmentMatchesSegment(frag, segmentName) {
4433
- var url = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4434
- return url.endsWith(segmentName) || url.includes("/" + segmentName);
3999
+ var _rawUrl_split_;
4000
+ var rawUrl = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4001
+ var url = (_rawUrl_split_ = rawUrl.split("?")[0]) !== null && _rawUrl_split_ !== void 0 ? _rawUrl_split_ : "";
4002
+ var name = segmentName.startsWith("/") ? segmentName : "/" + segmentName;
4003
+ return url.endsWith(name) || url.includes(name);
4435
4004
  }
4436
4005
  },
4437
4006
  {
@@ -4768,13 +4337,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4768
4337
  },
4769
4338
  {
4770
4339
  key: "handleAdStart",
4771
- value: function handleAdStart(_marker) {
4340
+ value: function handleAdStart(durationSeconds) {
4772
4341
  return _async_to_generator(function() {
4773
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
4342
+ var _this_savedMutedStateBeforeAd, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
4774
4343
  return _ts_generator(this, function(_state) {
4775
4344
  switch(_state.label){
4776
4345
  case 0:
4777
- adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : void 0;
4346
+ adBreakDurationMs = durationSeconds != null ? durationSeconds * 1e3 : void 0;
4778
4347
  if (this.config.debugAdTiming) {
4779
4348
  mode = this.isLiveStream ? "LIVE" : "VOD";
4780
4349
  console.log("[CONTINUOUS-FETCH] \uD83D\uDCFA ".concat(mode, " MODE: Target duration=").concat(adBreakDurationMs, "ms"));
@@ -4785,13 +4354,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4785
4354
  this.pendingNextAdBids = null;
4786
4355
  this.isShowingPlaceholder = false;
4787
4356
  this.totalAdRequestsInBreak = 0;
4788
- if (this.savedMutedStateBeforeScte == null && !this.video.muted) {
4789
- this.savedMutedStateBeforeScte = {
4357
+ if (this.savedMutedStateBeforeAd == null && !this.video.muted) {
4358
+ this.savedMutedStateBeforeAd = {
4790
4359
  muted: false,
4791
4360
  volume: this.video.volume
4792
4361
  };
4793
4362
  }
4794
- state = (_this_savedMutedStateBeforeScte = this.savedMutedStateBeforeScte) !== null && _this_savedMutedStateBeforeScte !== void 0 ? _this_savedMutedStateBeforeScte : {
4363
+ state = (_this_savedMutedStateBeforeAd = this.savedMutedStateBeforeAd) !== null && _this_savedMutedStateBeforeAd !== void 0 ? _this_savedMutedStateBeforeAd : {
4795
4364
  muted: this.video.muted,
4796
4365
  volume: this.video.volume
4797
4366
  };
@@ -4818,6 +4387,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4818
4387
  if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
4819
4388
  this.expectedAdBreakDurationMs = adBreakDurationMs;
4820
4389
  }
4390
+ if (this.expectedAdBreakDurationMs != null) {
4391
+ this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4392
+ }
4821
4393
  this.clearPendingAdBreak();
4822
4394
  adBreakToken = Date.now();
4823
4395
  this.activeAdRequestToken = adBreakToken;
@@ -5316,49 +4888,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5316
4888
  this.handleAdPodComplete();
5317
4889
  }
5318
4890
  },
5319
- {
5320
- key: "scheduleAdStartIn",
5321
- value: function scheduleAdStartIn(delayMs) {
5322
- var _this = this;
5323
- this.clearAdStartTimer();
5324
- var ms = Math.max(0, Math.floor(delayMs));
5325
- if (ms === 0) {
5326
- this.handleAdStart({
5327
- type: "start"
5328
- }).catch(function() {});
5329
- return;
5330
- }
5331
- this.adStartTimerId = window.setTimeout(function() {
5332
- _this.handleAdStart({
5333
- type: "start"
5334
- }).catch(function() {});
5335
- }, ms);
5336
- }
5337
- },
5338
- {
5339
- key: "clearAdStartTimer",
5340
- value: function clearAdStartTimer() {
5341
- if (this.adStartTimerId != null) {
5342
- clearTimeout(this.adStartTimerId);
5343
- this.adStartTimerId = void 0;
5344
- }
5345
- }
5346
- },
5347
- {
5348
- key: "updatePtsDrift",
5349
- value: function updatePtsDrift(ptsSecondsSample) {
5350
- var sampleMs = (this.video.currentTime - ptsSecondsSample) * 1e3;
5351
- if (!Number.isFinite(sampleMs) || Math.abs(sampleMs) > 6e4) return;
5352
- var alpha = 0.1;
5353
- this.ptsDriftEmaMs = this.ptsDriftEmaMs * (1 - alpha) + sampleMs * alpha;
5354
- }
5355
- },
5356
4891
  {
5357
4892
  key: "handleAdPodComplete",
5358
4893
  value: function handleAdPodComplete() {
5359
4894
  var _this = this;
5360
4895
  var _ref, _ref1;
5361
- var _this_savedMutedStateBeforeScte, _this_savedMutedStateBeforeScte1;
4896
+ var _this_savedMutedStateBeforeAd, _this_savedMutedStateBeforeAd1;
5362
4897
  if (this.config.debugAdTiming) {
5363
4898
  console.log("[StormcloudVideoPlayer] \uD83C\uDFC1 Ad pod complete - cleaning up");
5364
4899
  }
@@ -5380,7 +4915,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5380
4915
  this.inAdBreak = false;
5381
4916
  this.expectedAdBreakDurationMs = void 0;
5382
4917
  this.currentAdBreakStartWallClockMs = void 0;
5383
- this.clearAdStartTimer();
5384
4918
  this.clearAdStopTimer();
5385
4919
  this.adPodQueue = [];
5386
4920
  this.showAds = false;
@@ -5388,8 +4922,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5388
4922
  this.totalAdsInBreak = 0;
5389
4923
  this.totalAdRequestsInBreak = 0;
5390
4924
  this.consecutiveFailures = 0;
5391
- 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();
5392
- 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();
4925
+ 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();
4926
+ 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();
5393
4927
  this.adLayer.updateOriginalMutedState(restoredMuted, restoredVolume);
5394
4928
  this.adLayer.stop().catch(function() {});
5395
4929
  if (this.video.muted !== restoredMuted) {
@@ -5498,7 +5032,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5498
5032
  }, delay);
5499
5033
  });
5500
5034
  }
5501
- this.savedMutedStateBeforeScte = null;
5035
+ this.savedMutedStateBeforeAd = null;
5502
5036
  }
5503
5037
  },
5504
5038
  {
@@ -5648,19 +5182,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5648
5182
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
5649
5183
  }
5650
5184
  },
5651
- {
5652
- key: "pushScteMarker",
5653
- value: function pushScteMarker(marker) {
5654
- if (!this.config.debugAdTiming) return;
5655
- this.scteMarkerHistory.push({
5656
- timestampMs: Date.now(),
5657
- marker: marker
5658
- });
5659
- if (this.scteMarkerHistory.length > DEBUG_HISTORY_LIMIT) {
5660
- this.scteMarkerHistory = this.scteMarkerHistory.slice(-DEBUG_HISTORY_LIMIT);
5661
- }
5662
- }
5663
- },
5664
5185
  {
5665
5186
  key: "pushDebugLog",
5666
5187
  value: function pushDebugLog(level, category, message, details) {
@@ -5678,23 +5199,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5678
5199
  }
5679
5200
  }
5680
5201
  },
5681
- {
5682
- key: "getRecentScteMarkers",
5683
- value: function getRecentScteMarkers() {
5684
- return this.scteMarkerHistory.map(function(entry) {
5685
- return _object_spread({
5686
- timestampMs: entry.timestampMs,
5687
- type: entry.marker.type
5688
- }, entry.marker.ptsSeconds !== void 0 ? {
5689
- ptsSeconds: entry.marker.ptsSeconds
5690
- } : {}, entry.marker.durationSeconds !== void 0 ? {
5691
- durationSeconds: entry.marker.durationSeconds
5692
- } : {}, entry.marker.raw !== void 0 ? {
5693
- raw: entry.marker.raw
5694
- } : {});
5695
- });
5696
- }
5697
- },
5698
5202
  {
5699
5203
  key: "pushAdInsertionDebug",
5700
5204
  value: function pushAdInsertionDebug(event, segmentName, opts) {
@@ -5991,7 +5495,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5991
5495
  this.clearAdInsertionOffsetTimer();
5992
5496
  this.stopContinuousFetching();
5993
5497
  this.stopFillerBreakTimer();
5994
- this.clearAdStartTimer();
5995
5498
  this.clearAdStopTimer();
5996
5499
  this.clearAdFailsafeTimer();
5997
5500
  this.clearAdRequestWatchdog();
@@ -6019,7 +5522,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6019
5522
  (_this_adLayer = this.adLayer) === null || _this_adLayer === void 0 ? void 0 : _this_adLayer.destroy();
6020
5523
  this.consecutiveFailures = 0;
6021
5524
  this.debugLogEntries = [];
6022
- this.scteMarkerHistory = [];
6023
5525
  this.adInsertionDebugHistory = [];
6024
5526
  }
6025
5527
  }