stormcloud-video-player 0.3.17 → 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.
@@ -309,6 +309,20 @@ function _ts_generator(thisArg, body) {
309
309
  };
310
310
  }
311
311
  }
312
+ function _ts_values(o) {
313
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
314
+ if (m) return m.call(o);
315
+ if (o && typeof o.length === "number") return {
316
+ next: function() {
317
+ if (o && i >= o.length) o = void 0;
318
+ return {
319
+ value: o && o[i++],
320
+ done: !o
321
+ };
322
+ }
323
+ };
324
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
325
+ }
312
326
  var __create = Object.create;
313
327
  var __defProp = Object.defineProperty;
314
328
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1335,6 +1349,7 @@ function createImaController(video, options) {
1335
1349
  },
1336
1350
  showPlaceholder: function showPlaceholder() {
1337
1351
  ensurePlaceholderContainer();
1352
+ hideContentVideo();
1338
1353
  if (adContainerEl) {
1339
1354
  adContainerEl.style.display = "flex";
1340
1355
  adContainerEl.style.backgroundColor = "#000";
@@ -1354,6 +1369,9 @@ function createImaController(video, options) {
1354
1369
  }
1355
1370
  }, 300);
1356
1371
  }
1372
+ if (!adPlaying) {
1373
+ showContentVideo();
1374
+ }
1357
1375
  }
1358
1376
  };
1359
1377
  }
@@ -2128,6 +2146,8 @@ function createHlsAdPlayer(contentVideo, options) {
2128
2146
  return 1;
2129
2147
  },
2130
2148
  showPlaceholder: function showPlaceholder() {
2149
+ contentVideo.style.opacity = "0";
2150
+ contentVideo.style.visibility = "hidden";
2131
2151
  if (!adContainerEl) {
2132
2152
  var _contentVideo_parentElement;
2133
2153
  var container = document.createElement("div");
@@ -2155,6 +2175,10 @@ function createHlsAdPlayer(contentVideo, options) {
2155
2175
  adContainerEl.style.display = "none";
2156
2176
  adContainerEl.style.pointerEvents = "none";
2157
2177
  }
2178
+ if (!adPlaying) {
2179
+ contentVideo.style.visibility = "visible";
2180
+ contentVideo.style.opacity = "1";
2181
+ }
2158
2182
  }
2159
2183
  };
2160
2184
  }
@@ -2809,6 +2833,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2809
2833
  this.maxPlaceholderDurationMs = 5e3;
2810
2834
  this.placeholderStartTimeMs = null;
2811
2835
  this.isShowingPlaceholder = false;
2836
+ this.consecutiveEmptyResponses = 0;
2837
+ this.totalAdRequestsInBreak = 0;
2838
+ this.lastEmptyResponseTimeMs = 0;
2839
+ this.maxTotalAdRequestsPerBreak = 20;
2840
+ this.maxConsecutiveEmptyResponses = 5;
2841
+ this.baseEmptyResponseDelayMs = 2e3;
2842
+ this.maxEmptyResponseDelayMs = 3e4;
2812
2843
  initializePolyfills();
2813
2844
  var browserOverrides = getBrowserConfigOverrides();
2814
2845
  this.config = _object_spread({}, config, browserOverrides);
@@ -2998,6 +3029,87 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2998
3029
  });
2999
3030
  }).call(_this);
3000
3031
  });
3032
+ this.hls.on(import_hls2.default.Events.LEVEL_LOADED, function(_evt, data) {
3033
+ if (_this.inAdBreak) {
3034
+ return;
3035
+ }
3036
+ var details = data === null || data === void 0 ? void 0 : data.details;
3037
+ if (!details || !details.fragments || details.fragments.length === 0) {
3038
+ return;
3039
+ }
3040
+ var fragmentsToScan = Math.min(5, details.fragments.length);
3041
+ for(var i = 0; i < fragmentsToScan; i++){
3042
+ var frag = details.fragments[i];
3043
+ var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3044
+ if (!Array.isArray(tagList)) continue;
3045
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3046
+ try {
3047
+ for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3048
+ var entry = _step.value;
3049
+ var tag = "";
3050
+ var value = "";
3051
+ if (Array.isArray(entry)) {
3052
+ var _entry_;
3053
+ tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3054
+ var _entry_1;
3055
+ value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3056
+ } else if (typeof entry === "string") {
3057
+ var idx = entry.indexOf(":");
3058
+ if (idx >= 0) {
3059
+ tag = entry.substring(0, idx);
3060
+ value = entry.substring(idx + 1);
3061
+ } else {
3062
+ tag = entry;
3063
+ }
3064
+ }
3065
+ if (!tag) continue;
3066
+ if (tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-DATERANGE")) {
3067
+ var attrs = tag.includes("EXT-X-DATERANGE") ? _this.parseAttributeList(value) : {};
3068
+ var hasScteOut = tag.includes("EXT-X-CUE-OUT") || "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3069
+ if (hasScteOut) {
3070
+ if (_this.config.debugAdTiming) {
3071
+ console.log("[StormcloudVideoPlayer] \uD83C\uDFAF EARLY SCTE-35 DETECTION: Ad break marker found in fragment", i, "- triggering ad handling immediately");
3072
+ }
3073
+ var durationSeconds = _this.parseCueOutDuration(value);
3074
+ var marker = _object_spread_props(_object_spread({
3075
+ type: "start"
3076
+ }, durationSeconds !== void 0 ? {
3077
+ durationSeconds: durationSeconds
3078
+ } : {}), {
3079
+ raw: {
3080
+ tag: tag,
3081
+ value: value,
3082
+ earlyDetection: true
3083
+ }
3084
+ });
3085
+ _this.inAdBreak = true;
3086
+ _this.expectedAdBreakDurationMs = durationSeconds ? durationSeconds * 1e3 : void 0;
3087
+ _this.currentAdBreakStartWallClockMs = Date.now();
3088
+ _this.clearAdStartTimer();
3089
+ _this.handleAdStart(marker);
3090
+ if (_this.expectedAdBreakDurationMs != null) {
3091
+ _this.scheduleAdStopCountdown(_this.expectedAdBreakDurationMs);
3092
+ }
3093
+ return;
3094
+ }
3095
+ }
3096
+ }
3097
+ } catch (err) {
3098
+ _didIteratorError = true;
3099
+ _iteratorError = err;
3100
+ } finally{
3101
+ try {
3102
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3103
+ _iterator.return();
3104
+ }
3105
+ } finally{
3106
+ if (_didIteratorError) {
3107
+ throw _iteratorError;
3108
+ }
3109
+ }
3110
+ }
3111
+ }
3112
+ });
3001
3113
  this.hls.on(import_hls2.default.Events.FRAG_BUFFERED, function(_evt, data) {
3002
3114
  return _async_to_generator(function() {
3003
3115
  var _this, _this_config_minSegmentsBeforePlay, minSegments, _this_video_play;
@@ -3205,11 +3317,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3205
3317
  this.ima.initialize();
3206
3318
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
3207
3319
  this.ima.on("all_ads_completed", function() {
3320
+ var remaining = _this.getRemainingAdMs();
3208
3321
  if (_this.config.debugAdTiming) {
3209
- console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received - ending ad break");
3322
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received - remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length));
3210
3323
  }
3211
3324
  if (_this.inAdBreak) {
3212
- _this.handleAdPodComplete();
3325
+ if (remaining > 500) {
3326
+ _this.tryNextAvailableAd();
3327
+ } else {
3328
+ _this.handleAdPodComplete();
3329
+ }
3213
3330
  }
3214
3331
  });
3215
3332
  this.ima.on("ad_error", function(errorPayload) {
@@ -4004,6 +4121,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4004
4121
  this.continuousFetchingActive = true;
4005
4122
  this.isShowingPlaceholder = false;
4006
4123
  this.placeholderStartTimeMs = null;
4124
+ this.consecutiveEmptyResponses = 0;
4125
+ this.totalAdRequestsInBreak = 0;
4126
+ this.lastEmptyResponseTimeMs = 0;
4007
4127
  currentMuted = this.video.muted;
4008
4128
  currentVolume = this.video.volume;
4009
4129
  this.ima.updateOriginalMutedState(currentMuted, currentVolume);
@@ -4011,6 +4131,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4011
4131
  this.currentAdIndex = 0;
4012
4132
  this.totalAdsInBreak = 1;
4013
4133
  this.adPodQueue = [];
4134
+ this.showAds = true;
4135
+ this.ima.showPlaceholder();
4014
4136
  if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
4015
4137
  this.expectedAdBreakDurationMs = adBreakDurationMs;
4016
4138
  this.currentAdBreakStartWallClockMs = Date.now();
@@ -4097,158 +4219,200 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4097
4219
  key: "continuousFetchLoop",
4098
4220
  value: function continuousFetchLoop(baseVastUrl) {
4099
4221
  return _async_to_generator(function() {
4100
- var remaining, maxQueueSize, newAdUrl, response, xmlText, parser, xmlDoc, mediaFiles, error;
4222
+ var _this, _loop, _ret;
4101
4223
  return _ts_generator(this, function(_state) {
4102
4224
  switch(_state.label){
4103
4225
  case 0:
4104
- if (!(this.continuousFetchingActive && this.inAdBreak)) return [
4105
- 3,
4106
- 14
4107
- ];
4108
- remaining = this.getRemainingAdMs();
4109
- if (remaining <= 0) {
4110
- if (this.config.debugAdTiming) {
4111
- console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
4112
- }
4113
- return [
4114
- 3,
4115
- 14
4116
- ];
4117
- }
4118
- maxQueueSize = 3;
4119
- if (!(this.adRequestQueue.length >= maxQueueSize)) return [
4120
- 3,
4121
- 2
4122
- ];
4123
- if (this.config.debugAdTiming) {
4124
- console.log("[CONTINUOUS-FETCH] ⏸️ Queue full (".concat(this.adRequestQueue.length, "), pausing fetching..."));
4125
- }
4126
- return [
4127
- 4,
4128
- new Promise(function(resolve) {
4129
- return setTimeout(resolve, 2e3);
4130
- })
4131
- ];
4226
+ _loop = function() {
4227
+ var remaining, maxQueueSize, newAdUrl, _this_ima_hasPreloadedAd, _this_ima, _this_ima_hasPreloadedAd1, hasPreloadedAd, backoffDelay, error, backoffDelay1;
4228
+ return _ts_generator(this, function(_state) {
4229
+ switch(_state.label){
4230
+ case 0:
4231
+ remaining = _this.getRemainingAdMs();
4232
+ if (remaining <= 0) {
4233
+ if (_this.config.debugAdTiming) {
4234
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping fetch loop");
4235
+ }
4236
+ return [
4237
+ 2,
4238
+ "break"
4239
+ ];
4240
+ }
4241
+ if (_this.totalAdRequestsInBreak >= _this.maxTotalAdRequestsPerBreak) {
4242
+ if (_this.config.debugAdTiming) {
4243
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Maximum ad requests reached (".concat(_this.maxTotalAdRequestsPerBreak, "), stopping fetch loop to prevent server blocks"));
4244
+ }
4245
+ return [
4246
+ 2,
4247
+ "break"
4248
+ ];
4249
+ }
4250
+ if (_this.consecutiveEmptyResponses >= _this.maxConsecutiveEmptyResponses) {
4251
+ if (_this.config.debugAdTiming) {
4252
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Too many consecutive empty responses (".concat(_this.maxConsecutiveEmptyResponses, "), stopping fetch loop"));
4253
+ }
4254
+ return [
4255
+ 2,
4256
+ "break"
4257
+ ];
4258
+ }
4259
+ maxQueueSize = 3;
4260
+ if (!(_this.adRequestQueue.length >= maxQueueSize)) return [
4261
+ 3,
4262
+ 2
4263
+ ];
4264
+ if (_this.config.debugAdTiming) {
4265
+ console.log("[CONTINUOUS-FETCH] ⏸️ Queue full (".concat(_this.adRequestQueue.length, "), pausing fetching..."));
4266
+ }
4267
+ return [
4268
+ 4,
4269
+ new Promise(function(resolve) {
4270
+ return setTimeout(resolve, 2e3);
4271
+ })
4272
+ ];
4273
+ case 1:
4274
+ _state.sent();
4275
+ return [
4276
+ 2,
4277
+ "continue"
4278
+ ];
4279
+ case 2:
4280
+ newAdUrl = _this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
4281
+ if (!(!newAdUrl || _this.failedVastUrls.has(newAdUrl))) return [
4282
+ 3,
4283
+ 4
4284
+ ];
4285
+ return [
4286
+ 4,
4287
+ new Promise(function(resolve) {
4288
+ return setTimeout(resolve, 1e3);
4289
+ })
4290
+ ];
4291
+ case 3:
4292
+ _state.sent();
4293
+ return [
4294
+ 2,
4295
+ "continue"
4296
+ ];
4297
+ case 4:
4298
+ _this.totalAdRequestsInBreak++;
4299
+ if (_this.config.debugAdTiming) {
4300
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDCE1 Attempting to fetch ad (request ".concat(_this.totalAdRequestsInBreak, "/").concat(_this.maxTotalAdRequestsPerBreak, ", queue: ").concat(_this.adRequestQueue.length, ")..."));
4301
+ }
4302
+ _state.label = 5;
4303
+ case 5:
4304
+ _state.trys.push([
4305
+ 5,
4306
+ 11,
4307
+ ,
4308
+ 13
4309
+ ]);
4310
+ if (!_this.ima.preloadAds) return [
4311
+ 3,
4312
+ 7
4313
+ ];
4314
+ return [
4315
+ 4,
4316
+ _this.ima.preloadAds(newAdUrl)
4317
+ ];
4318
+ case 6:
4319
+ _state.sent();
4320
+ _state.label = 7;
4321
+ case 7:
4322
+ 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;
4323
+ if (!!hasPreloadedAd) return [
4324
+ 3,
4325
+ 9
4326
+ ];
4327
+ _this.consecutiveEmptyResponses++;
4328
+ _this.lastEmptyResponseTimeMs = Date.now();
4329
+ backoffDelay = Math.min(_this.baseEmptyResponseDelayMs * Math.pow(2, _this.consecutiveEmptyResponses - 1), _this.maxEmptyResponseDelayMs);
4330
+ if (_this.config.debugAdTiming) {
4331
+ console.log("[CONTINUOUS-FETCH] ⚠️ Empty/invalid VAST response (".concat(_this.consecutiveEmptyResponses, "/").concat(_this.maxConsecutiveEmptyResponses, " consecutive), backing off for ").concat(backoffDelay, "ms"));
4332
+ }
4333
+ _this.failedVastUrls.add(newAdUrl);
4334
+ return [
4335
+ 4,
4336
+ new Promise(function(resolve) {
4337
+ return setTimeout(resolve, backoffDelay);
4338
+ })
4339
+ ];
4340
+ case 8:
4341
+ _state.sent();
4342
+ return [
4343
+ 2,
4344
+ "continue"
4345
+ ];
4346
+ case 9:
4347
+ _this.consecutiveEmptyResponses = 0;
4348
+ if (_this.config.debugAdTiming) {
4349
+ console.log("[CONTINUOUS-FETCH] ✅ Successfully preloaded ad, adding to queue (queue size: ".concat(_this.adRequestQueue.length + 1, ")"));
4350
+ }
4351
+ _this.adRequestQueue.push(newAdUrl);
4352
+ _this.totalAdsInBreak++;
4353
+ return [
4354
+ 4,
4355
+ new Promise(function(resolve) {
4356
+ return setTimeout(resolve, 500);
4357
+ })
4358
+ ];
4359
+ case 10:
4360
+ _state.sent();
4361
+ return [
4362
+ 3,
4363
+ 13
4364
+ ];
4365
+ case 11:
4366
+ error = _state.sent();
4367
+ if (_this.config.debugAdTiming) {
4368
+ console.log("[CONTINUOUS-FETCH] \u274C Ad preload failed:", error.message);
4369
+ }
4370
+ _this.failedVastUrls.add(newAdUrl);
4371
+ _this.consecutiveEmptyResponses++;
4372
+ backoffDelay1 = Math.min(_this.baseEmptyResponseDelayMs * Math.pow(2, _this.consecutiveEmptyResponses - 1), _this.maxEmptyResponseDelayMs);
4373
+ return [
4374
+ 4,
4375
+ new Promise(function(resolve) {
4376
+ return setTimeout(resolve, backoffDelay1);
4377
+ })
4378
+ ];
4379
+ case 12:
4380
+ _state.sent();
4381
+ return [
4382
+ 3,
4383
+ 13
4384
+ ];
4385
+ case 13:
4386
+ return [
4387
+ 2
4388
+ ];
4389
+ }
4390
+ });
4391
+ };
4392
+ _state.label = 1;
4132
4393
  case 1:
4133
- _state.sent();
4134
- return [
4135
- 3,
4136
- 0
4137
- ];
4138
- case 2:
4139
- newAdUrl = this.generateVastUrlsWithCorrelators(baseVastUrl, 1)[0];
4140
- if (!(!newAdUrl || this.failedVastUrls.has(newAdUrl))) return [
4394
+ if (!(this.continuousFetchingActive && this.inAdBreak)) return [
4141
4395
  3,
4142
- 4
4143
- ];
4144
- return [
4145
- 4,
4146
- new Promise(function(resolve) {
4147
- return setTimeout(resolve, 1e3);
4148
- })
4396
+ 3
4149
4397
  ];
4150
- case 3:
4151
- _state.sent();
4398
+ _this = this;
4152
4399
  return [
4153
- 3,
4154
- 0
4155
- ];
4156
- case 4:
4157
- if (this.config.debugAdTiming) {
4158
- console.log("[CONTINUOUS-FETCH] \uD83D\uDCE1 Attempting to fetch ad (".concat(this.successfulAdRequests.length + this.adRequestQueue.length + 1, " total)..."));
4159
- }
4160
- _state.label = 5;
4161
- case 5:
4162
- _state.trys.push([
4163
4400
  5,
4164
- 11,
4165
- ,
4166
- 13
4167
- ]);
4168
- return [
4169
- 4,
4170
- fetch(newAdUrl, {
4171
- mode: "cors"
4172
- })
4173
- ];
4174
- case 6:
4175
- response = _state.sent();
4176
- if (!response.ok) {
4177
- throw new Error("Failed to fetch VAST: ".concat(response.status));
4178
- }
4179
- return [
4180
- 4,
4181
- response.text()
4182
- ];
4183
- case 7:
4184
- xmlText = _state.sent();
4185
- parser = new DOMParser();
4186
- xmlDoc = parser.parseFromString(xmlText, "text/xml");
4187
- mediaFiles = xmlDoc.querySelectorAll("MediaFile");
4188
- if (!(mediaFiles.length === 0)) return [
4189
- 3,
4190
- 9
4401
+ _ts_values(_loop())
4191
4402
  ];
4192
- if (this.config.debugAdTiming) {
4193
- console.log("[CONTINUOUS-FETCH] \u26A0\uFE0F VAST response has no media files, skipping");
4194
- }
4195
- this.failedVastUrls.add(newAdUrl);
4196
- return [
4197
- 4,
4198
- new Promise(function(resolve) {
4199
- return setTimeout(resolve, 1e3);
4200
- })
4201
- ];
4202
- case 8:
4203
- _state.sent();
4204
- return [
4205
- 3,
4206
- 0
4207
- ];
4208
- case 9:
4209
- if (this.config.debugAdTiming) {
4210
- console.log("[CONTINUOUS-FETCH] ✅ Successfully fetched ad, adding to queue (queue size: ".concat(this.adRequestQueue.length + 1, ")"));
4211
- }
4212
- this.adRequestQueue.push(newAdUrl);
4213
- this.totalAdsInBreak++;
4214
- return [
4215
- 4,
4216
- new Promise(function(resolve) {
4217
- return setTimeout(resolve, 500);
4218
- })
4219
- ];
4220
- case 10:
4221
- _state.sent();
4222
- return [
4223
- 3,
4224
- 13
4225
- ];
4226
- case 11:
4227
- error = _state.sent();
4228
- if (this.config.debugAdTiming) {
4229
- console.log("[CONTINUOUS-FETCH] \u274C Ad fetch failed:", error.message);
4230
- }
4231
- this.failedVastUrls.add(newAdUrl);
4232
- return [
4233
- 4,
4234
- new Promise(function(resolve) {
4235
- return setTimeout(resolve, 2e3);
4236
- })
4237
- ];
4238
- case 12:
4239
- _state.sent();
4240
- return [
4403
+ case 2:
4404
+ _ret = _state.sent();
4405
+ if (_ret === "break") return [
4241
4406
  3,
4242
- 13
4407
+ 3
4243
4408
  ];
4244
- case 13:
4245
4409
  return [
4246
4410
  3,
4247
- 0
4411
+ 1
4248
4412
  ];
4249
- case 14:
4413
+ case 3:
4250
4414
  if (this.config.debugAdTiming) {
4251
- console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Continuous fetch loop ended");
4415
+ console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Continuous fetch loop ended (total requests: ".concat(this.totalAdRequestsInBreak, ", empty responses: ").concat(this.consecutiveEmptyResponses, ")"));
4252
4416
  }
4253
4417
  return [
4254
4418
  2