stormcloud-video-player 0.3.16 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -259,6 +259,20 @@ function _ts_generator(thisArg, body) {
259
259
  };
260
260
  }
261
261
  }
262
+ function _ts_values(o) {
263
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
264
+ if (m) return m.call(o);
265
+ if (o && typeof o.length === "number") return {
266
+ next: function() {
267
+ if (o && i >= o.length) o = void 0;
268
+ return {
269
+ value: o && o[i++],
270
+ done: !o
271
+ };
272
+ }
273
+ };
274
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
275
+ }
262
276
  var __create = Object.create;
263
277
  var __defProp = Object.defineProperty;
264
278
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1247,6 +1261,7 @@ function createImaController(video, options) {
1247
1261
  },
1248
1262
  showPlaceholder: function showPlaceholder() {
1249
1263
  ensurePlaceholderContainer();
1264
+ hideContentVideo();
1250
1265
  if (adContainerEl) {
1251
1266
  adContainerEl.style.display = "flex";
1252
1267
  adContainerEl.style.backgroundColor = "#000";
@@ -1266,6 +1281,9 @@ function createImaController(video, options) {
1266
1281
  }
1267
1282
  }, 300);
1268
1283
  }
1284
+ if (!adPlaying) {
1285
+ showContentVideo();
1286
+ }
1269
1287
  }
1270
1288
  };
1271
1289
  }
@@ -2040,6 +2058,8 @@ function createHlsAdPlayer(contentVideo, options) {
2040
2058
  return 1;
2041
2059
  },
2042
2060
  showPlaceholder: function showPlaceholder() {
2061
+ contentVideo.style.opacity = "0";
2062
+ contentVideo.style.visibility = "hidden";
2043
2063
  if (!adContainerEl) {
2044
2064
  var _contentVideo_parentElement;
2045
2065
  var container = document.createElement("div");
@@ -2067,6 +2087,10 @@ function createHlsAdPlayer(contentVideo, options) {
2067
2087
  adContainerEl.style.display = "none";
2068
2088
  adContainerEl.style.pointerEvents = "none";
2069
2089
  }
2090
+ if (!adPlaying) {
2091
+ contentVideo.style.visibility = "visible";
2092
+ contentVideo.style.opacity = "1";
2093
+ }
2070
2094
  }
2071
2095
  };
2072
2096
  }
@@ -2721,6 +2745,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2721
2745
  this.maxPlaceholderDurationMs = 5e3;
2722
2746
  this.placeholderStartTimeMs = null;
2723
2747
  this.isShowingPlaceholder = false;
2748
+ this.consecutiveEmptyResponses = 0;
2749
+ this.totalAdRequestsInBreak = 0;
2750
+ this.lastEmptyResponseTimeMs = 0;
2751
+ this.maxTotalAdRequestsPerBreak = 20;
2752
+ this.maxConsecutiveEmptyResponses = 5;
2753
+ this.baseEmptyResponseDelayMs = 2e3;
2754
+ this.maxEmptyResponseDelayMs = 3e4;
2724
2755
  initializePolyfills();
2725
2756
  var browserOverrides = getBrowserConfigOverrides();
2726
2757
  this.config = _object_spread({}, config, browserOverrides);
@@ -2910,6 +2941,87 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2910
2941
  });
2911
2942
  }).call(_this);
2912
2943
  });
2944
+ this.hls.on(import_hls2.default.Events.LEVEL_LOADED, function(_evt, data) {
2945
+ if (_this.inAdBreak) {
2946
+ return;
2947
+ }
2948
+ var details = data === null || data === void 0 ? void 0 : data.details;
2949
+ if (!details || !details.fragments || details.fragments.length === 0) {
2950
+ return;
2951
+ }
2952
+ var fragmentsToScan = Math.min(5, details.fragments.length);
2953
+ for(var i = 0; i < fragmentsToScan; i++){
2954
+ var frag = details.fragments[i];
2955
+ var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
2956
+ if (!Array.isArray(tagList)) continue;
2957
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2958
+ try {
2959
+ for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2960
+ var entry = _step.value;
2961
+ var tag = "";
2962
+ var value = "";
2963
+ if (Array.isArray(entry)) {
2964
+ var _entry_;
2965
+ tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
2966
+ var _entry_1;
2967
+ value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
2968
+ } else if (typeof entry === "string") {
2969
+ var idx = entry.indexOf(":");
2970
+ if (idx >= 0) {
2971
+ tag = entry.substring(0, idx);
2972
+ value = entry.substring(idx + 1);
2973
+ } else {
2974
+ tag = entry;
2975
+ }
2976
+ }
2977
+ if (!tag) continue;
2978
+ if (tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-DATERANGE")) {
2979
+ var attrs = tag.includes("EXT-X-DATERANGE") ? _this.parseAttributeList(value) : {};
2980
+ var hasScteOut = tag.includes("EXT-X-CUE-OUT") || "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
2981
+ if (hasScteOut) {
2982
+ if (_this.config.debugAdTiming) {
2983
+ console.log("[StormcloudVideoPlayer] \uD83C\uDFAF EARLY SCTE-35 DETECTION: Ad break marker found in fragment", i, "- triggering ad handling immediately");
2984
+ }
2985
+ var durationSeconds = _this.parseCueOutDuration(value);
2986
+ var marker = _object_spread_props(_object_spread({
2987
+ type: "start"
2988
+ }, durationSeconds !== void 0 ? {
2989
+ durationSeconds: durationSeconds
2990
+ } : {}), {
2991
+ raw: {
2992
+ tag: tag,
2993
+ value: value,
2994
+ earlyDetection: true
2995
+ }
2996
+ });
2997
+ _this.inAdBreak = true;
2998
+ _this.expectedAdBreakDurationMs = durationSeconds ? durationSeconds * 1e3 : void 0;
2999
+ _this.currentAdBreakStartWallClockMs = Date.now();
3000
+ _this.clearAdStartTimer();
3001
+ _this.handleAdStart(marker);
3002
+ if (_this.expectedAdBreakDurationMs != null) {
3003
+ _this.scheduleAdStopCountdown(_this.expectedAdBreakDurationMs);
3004
+ }
3005
+ return;
3006
+ }
3007
+ }
3008
+ }
3009
+ } catch (err) {
3010
+ _didIteratorError = true;
3011
+ _iteratorError = err;
3012
+ } finally{
3013
+ try {
3014
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3015
+ _iterator.return();
3016
+ }
3017
+ } finally{
3018
+ if (_didIteratorError) {
3019
+ throw _iteratorError;
3020
+ }
3021
+ }
3022
+ }
3023
+ }
3024
+ });
2913
3025
  this.hls.on(import_hls2.default.Events.FRAG_BUFFERED, function(_evt, data) {
2914
3026
  return _async_to_generator(function() {
2915
3027
  var _this, _this_config_minSegmentsBeforePlay, minSegments, _this_video_play;
@@ -3117,11 +3229,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3117
3229
  this.ima.initialize();
3118
3230
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3119
3231
  this.ima.on("all_ads_completed", function() {
3232
+ var remaining = _this.getRemainingAdMs();
3120
3233
  if (_this.config.debugAdTiming) {
3121
- console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received - ending ad break");
3234
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received - remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length));
3122
3235
  }
3123
3236
  if (_this.inAdBreak) {
3124
- _this.handleAdPodComplete();
3237
+ if (remaining > 500) {
3238
+ _this.tryNextAvailableAd();
3239
+ } else {
3240
+ _this.handleAdPodComplete();
3241
+ }
3125
3242
  }
3126
3243
  });
3127
3244
  this.ima.on("ad_error", function(errorPayload) {
@@ -3916,6 +4033,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3916
4033
  this.continuousFetchingActive = true;
3917
4034
  this.isShowingPlaceholder = false;
3918
4035
  this.placeholderStartTimeMs = null;
4036
+ this.consecutiveEmptyResponses = 0;
4037
+ this.totalAdRequestsInBreak = 0;
4038
+ this.lastEmptyResponseTimeMs = 0;
3919
4039
  currentMuted = this.video.muted;
3920
4040
  currentVolume = this.video.volume;
3921
4041
  this.ima.updateOriginalMutedState(currentMuted, currentVolume);
@@ -3923,6 +4043,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3923
4043
  this.currentAdIndex = 0;
3924
4044
  this.totalAdsInBreak = 1;
3925
4045
  this.adPodQueue = [];
4046
+ this.showAds = true;
4047
+ this.ima.showPlaceholder();
3926
4048
  if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
3927
4049
  this.expectedAdBreakDurationMs = adBreakDurationMs;
3928
4050
  this.currentAdBreakStartWallClockMs = Date.now();
@@ -4009,158 +4131,200 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4009
4131
  key: "continuousFetchLoop",
4010
4132
  value: function continuousFetchLoop(baseVastUrl) {
4011
4133
  return _async_to_generator(function() {
4012
- var remaining, maxQueueSize, newAdUrl, response, xmlText, parser, xmlDoc, mediaFiles, error;
4134
+ var _this, _loop, _ret;
4013
4135
  return _ts_generator(this, function(_state) {
4014
4136
  switch(_state.label){
4015
4137
  case 0:
4016
- if (!(this.continuousFetchingActive && this.inAdBreak)) return [
4017
- 3,
4018
- 14
4019
- ];
4020
- remaining = this.getRemainingAdMs();
4021
- if (remaining <= 0) {
4022
- if (this.config.debugAdTiming) {
4023
- console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
4024
- }
4025
- return [
4026
- 3,
4027
- 14
4028
- ];
4029
- }
4030
- maxQueueSize = 3;
4031
- if (!(this.adRequestQueue.length >= maxQueueSize)) return [
4032
- 3,
4033
- 2
4034
- ];
4035
- if (this.config.debugAdTiming) {
4036
- console.log("[CONTINUOUS-FETCH] ⏸️ Queue full (".concat(this.adRequestQueue.length, "), pausing fetching..."));
4037
- }
4038
- return [
4039
- 4,
4040
- new Promise(function(resolve) {
4041
- return setTimeout(resolve, 2e3);
4042
- })
4043
- ];
4138
+ _loop = function() {
4139
+ var remaining, maxQueueSize, newAdUrl, _this_ima_hasPreloadedAd, _this_ima, _this_ima_hasPreloadedAd1, hasPreloadedAd, backoffDelay, error, backoffDelay1;
4140
+ return _ts_generator(this, function(_state) {
4141
+ switch(_state.label){
4142
+ case 0:
4143
+ remaining = _this.getRemainingAdMs();
4144
+ if (remaining <= 0) {
4145
+ if (_this.config.debugAdTiming) {
4146
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
4147
+ }
4148
+ return [
4149
+ 2,
4150
+ "break"
4151
+ ];
4152
+ }
4153
+ if (_this.totalAdRequestsInBreak >= _this.maxTotalAdRequestsPerBreak) {
4154
+ if (_this.config.debugAdTiming) {
4155
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Maximum ad requests reached (".concat(_this.maxTotalAdRequestsPerBreak, "), stopping fetch loop to prevent server blocks"));
4156
+ }
4157
+ return [
4158
+ 2,
4159
+ "break"
4160
+ ];
4161
+ }
4162
+ if (_this.consecutiveEmptyResponses >= _this.maxConsecutiveEmptyResponses) {
4163
+ if (_this.config.debugAdTiming) {
4164
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Too many consecutive empty responses (".concat(_this.maxConsecutiveEmptyResponses, "), stopping fetch loop"));
4165
+ }
4166
+ return [
4167
+ 2,
4168
+ "break"
4169
+ ];
4170
+ }
4171
+ maxQueueSize = 3;
4172
+ if (!(_this.adRequestQueue.length >= maxQueueSize)) return [
4173
+ 3,
4174
+ 2
4175
+ ];
4176
+ if (_this.config.debugAdTiming) {
4177
+ console.log("[CONTINUOUS-FETCH] ⏸️ Queue full (".concat(_this.adRequestQueue.length, "), pausing fetching..."));
4178
+ }
4179
+ return [
4180
+ 4,
4181
+ new Promise(function(resolve) {
4182
+ return setTimeout(resolve, 2e3);
4183
+ })
4184
+ ];
4185
+ case 1:
4186
+ _state.sent();
4187
+ return [
4188
+ 2,
4189
+ "continue"
4190
+ ];
4191
+ case 2:
4192
+ newAdUrl = _this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
4193
+ if (!(!newAdUrl || _this.failedVastUrls.has(newAdUrl))) return [
4194
+ 3,
4195
+ 4
4196
+ ];
4197
+ return [
4198
+ 4,
4199
+ new Promise(function(resolve) {
4200
+ return setTimeout(resolve, 1e3);
4201
+ })
4202
+ ];
4203
+ case 3:
4204
+ _state.sent();
4205
+ return [
4206
+ 2,
4207
+ "continue"
4208
+ ];
4209
+ case 4:
4210
+ _this.totalAdRequestsInBreak++;
4211
+ if (_this.config.debugAdTiming) {
4212
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDCE1 Attempting to fetch ad (request ".concat(_this.totalAdRequestsInBreak, "/").concat(_this.maxTotalAdRequestsPerBreak, ", queue: ").concat(_this.adRequestQueue.length, ")..."));
4213
+ }
4214
+ _state.label = 5;
4215
+ case 5:
4216
+ _state.trys.push([
4217
+ 5,
4218
+ 11,
4219
+ ,
4220
+ 13
4221
+ ]);
4222
+ if (!_this.ima.preloadAds) return [
4223
+ 3,
4224
+ 7
4225
+ ];
4226
+ return [
4227
+ 4,
4228
+ _this.ima.preloadAds(newAdUrl)
4229
+ ];
4230
+ case 6:
4231
+ _state.sent();
4232
+ _state.label = 7;
4233
+ case 7:
4234
+ hasPreloadedAd = (_this_ima_hasPreloadedAd1 = (_this_ima_hasPreloadedAd = (_this_ima = _this.ima).hasPreloadedAd) === null || _this_ima_hasPreloadedAd === void 0 ? void 0 : _this_ima_hasPreloadedAd.call(_this_ima, newAdUrl)) !== null && _this_ima_hasPreloadedAd1 !== void 0 ? _this_ima_hasPreloadedAd1 : false;
4235
+ if (!!hasPreloadedAd) return [
4236
+ 3,
4237
+ 9
4238
+ ];
4239
+ _this.consecutiveEmptyResponses++;
4240
+ _this.lastEmptyResponseTimeMs = Date.now();
4241
+ backoffDelay = Math.min(_this.baseEmptyResponseDelayMs * Math.pow(2, _this.consecutiveEmptyResponses - 1), _this.maxEmptyResponseDelayMs);
4242
+ if (_this.config.debugAdTiming) {
4243
+ console.log("[CONTINUOUS-FETCH] ⚠️ Empty/invalid VAST response (".concat(_this.consecutiveEmptyResponses, "/").concat(_this.maxConsecutiveEmptyResponses, " consecutive), backing off for ").concat(backoffDelay, "ms"));
4244
+ }
4245
+ _this.failedVastUrls.add(newAdUrl);
4246
+ return [
4247
+ 4,
4248
+ new Promise(function(resolve) {
4249
+ return setTimeout(resolve, backoffDelay);
4250
+ })
4251
+ ];
4252
+ case 8:
4253
+ _state.sent();
4254
+ return [
4255
+ 2,
4256
+ "continue"
4257
+ ];
4258
+ case 9:
4259
+ _this.consecutiveEmptyResponses = 0;
4260
+ if (_this.config.debugAdTiming) {
4261
+ console.log("[CONTINUOUS-FETCH] ✅ Successfully preloaded ad, adding to queue (queue size: ".concat(_this.adRequestQueue.length + 1, ")"));
4262
+ }
4263
+ _this.adRequestQueue.push(newAdUrl);
4264
+ _this.totalAdsInBreak++;
4265
+ return [
4266
+ 4,
4267
+ new Promise(function(resolve) {
4268
+ return setTimeout(resolve, 500);
4269
+ })
4270
+ ];
4271
+ case 10:
4272
+ _state.sent();
4273
+ return [
4274
+ 3,
4275
+ 13
4276
+ ];
4277
+ case 11:
4278
+ error = _state.sent();
4279
+ if (_this.config.debugAdTiming) {
4280
+ console.log("[CONTINUOUS-FETCH] \u274C Ad preload failed:", error.message);
4281
+ }
4282
+ _this.failedVastUrls.add(newAdUrl);
4283
+ _this.consecutiveEmptyResponses++;
4284
+ backoffDelay1 = Math.min(_this.baseEmptyResponseDelayMs * Math.pow(2, _this.consecutiveEmptyResponses - 1), _this.maxEmptyResponseDelayMs);
4285
+ return [
4286
+ 4,
4287
+ new Promise(function(resolve) {
4288
+ return setTimeout(resolve, backoffDelay1);
4289
+ })
4290
+ ];
4291
+ case 12:
4292
+ _state.sent();
4293
+ return [
4294
+ 3,
4295
+ 13
4296
+ ];
4297
+ case 13:
4298
+ return [
4299
+ 2
4300
+ ];
4301
+ }
4302
+ });
4303
+ };
4304
+ _state.label = 1;
4044
4305
  case 1:
4045
- _state.sent();
4046
- return [
4047
- 3,
4048
- 0
4049
- ];
4050
- case 2:
4051
- newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
4052
- if (!(!newAdUrl || this.failedVastUrls.has(newAdUrl))) return [
4306
+ if (!(this.continuousFetchingActive && this.inAdBreak)) return [
4053
4307
  3,
4054
- 4
4055
- ];
4056
- return [
4057
- 4,
4058
- new Promise(function(resolve) {
4059
- return setTimeout(resolve, 1e3);
4060
- })
4308
+ 3
4061
4309
  ];
4062
- case 3:
4063
- _state.sent();
4310
+ _this = this;
4064
4311
  return [
4065
- 3,
4066
- 0
4067
- ];
4068
- case 4:
4069
- if (this.config.debugAdTiming) {
4070
- console.log("[CONTINUOUS-FETCH] \uD83D\uDCE1 Attempting to fetch ad (".concat(this.successfulAdRequests.length + this.adRequestQueue.length + 1, " total)..."));
4071
- }
4072
- _state.label = 5;
4073
- case 5:
4074
- _state.trys.push([
4075
4312
  5,
4076
- 11,
4077
- ,
4078
- 13
4079
- ]);
4080
- return [
4081
- 4,
4082
- fetch(newAdUrl, {
4083
- mode: "cors"
4084
- })
4085
- ];
4086
- case 6:
4087
- response = _state.sent();
4088
- if (!response.ok) {
4089
- throw new Error("Failed to fetch VAST: ".concat(response.status));
4090
- }
4091
- return [
4092
- 4,
4093
- response.text()
4094
- ];
4095
- case 7:
4096
- xmlText = _state.sent();
4097
- parser = new DOMParser();
4098
- xmlDoc = parser.parseFromString(xmlText, "text/xml");
4099
- mediaFiles = xmlDoc.querySelectorAll("MediaFile");
4100
- if (!(mediaFiles.length === 0)) return [
4101
- 3,
4102
- 9
4313
+ _ts_values(_loop())
4103
4314
  ];
4104
- if (this.config.debugAdTiming) {
4105
- console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
4106
- }
4107
- this.failedVastUrls.add(newAdUrl);
4108
- return [
4109
- 4,
4110
- new Promise(function(resolve) {
4111
- return setTimeout(resolve, 1e3);
4112
- })
4113
- ];
4114
- case 8:
4115
- _state.sent();
4116
- return [
4117
- 3,
4118
- 0
4119
- ];
4120
- case 9:
4121
- if (this.config.debugAdTiming) {
4122
- console.log("[CONTINUOUS-FETCH] ✅ Successfully fetched ad, adding to queue (queue size: ".concat(this.adRequestQueue.length + 1, ")"));
4123
- }
4124
- this.adRequestQueue.push(newAdUrl);
4125
- this.totalAdsInBreak++;
4126
- return [
4127
- 4,
4128
- new Promise(function(resolve) {
4129
- return setTimeout(resolve, 500);
4130
- })
4131
- ];
4132
- case 10:
4133
- _state.sent();
4134
- return [
4135
- 3,
4136
- 13
4137
- ];
4138
- case 11:
4139
- error = _state.sent();
4140
- if (this.config.debugAdTiming) {
4141
- console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
4142
- }
4143
- this.failedVastUrls.add(newAdUrl);
4144
- return [
4145
- 4,
4146
- new Promise(function(resolve) {
4147
- return setTimeout(resolve, 2e3);
4148
- })
4149
- ];
4150
- case 12:
4151
- _state.sent();
4152
- return [
4315
+ case 2:
4316
+ _ret = _state.sent();
4317
+ if (_ret === "break") return [
4153
4318
  3,
4154
- 13
4319
+ 3
4155
4320
  ];
4156
- case 13:
4157
4321
  return [
4158
4322
  3,
4159
- 0
4323
+ 1
4160
4324
  ];
4161
- case 14:
4325
+ case 3:
4162
4326
  if (this.config.debugAdTiming) {
4163
- console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Continuous fetch loop ended");
4327
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Continuous fetch loop ended (total requests: ".concat(this.totalAdRequestsInBreak, ", empty responses: ").concat(this.consecutiveEmptyResponses, ")"));
4164
4328
  }
4165
4329
  return [
4166
4330
  2