stormcloud-video-player 0.7.9 → 0.7.11

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.
@@ -2752,6 +2752,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2752
2752
  this.isInAdTransition = false;
2753
2753
  this.maxPlaceholderDurationMs = 5e3;
2754
2754
  this.isShowingPlaceholder = false;
2755
+ this.lastAdInsertionPoint = null;
2756
+ this.processedAdInsertionUpdatedAt = null;
2755
2757
  this.totalAdRequestsInBreak = 0;
2756
2758
  this.maxTotalAdRequestsPerBreak = 20;
2757
2759
  this.pendingAdBreak = null;
@@ -2769,6 +2771,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2769
2771
  this.preloadedTokens = [];
2770
2772
  this.debugLogEntries = [];
2771
2773
  this.scteMarkerHistory = [];
2774
+ this.adInsertionDebugHistory = [];
2772
2775
  initializePolyfills();
2773
2776
  var browserOverrides = getBrowserConfigOverrides();
2774
2777
  this.config = _object_spread({}, browserOverrides, config);
@@ -2953,6 +2956,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2953
2956
  _state.sent();
2954
2957
  _state.label = 2;
2955
2958
  case 2:
2959
+ if (!this.config.disableAds && this.config.projectId) {
2960
+ this.startAdInsertionPolling();
2961
+ }
2956
2962
  return [
2957
2963
  2
2958
2964
  ];
@@ -2960,78 +2966,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2960
2966
  });
2961
2967
  }).call(_this);
2962
2968
  });
2963
- this.hls.on(import_hls.default.Events.LEVEL_LOADED, function(_evt, data) {
2969
+ this.hls.on(import_hls.default.Events.LEVEL_LOADED, function() {
2964
2970
  if (_this.inAdBreak || _this.pendingAdBreak) {
2965
2971
  return;
2966
2972
  }
2967
- var details = data === null || data === void 0 ? void 0 : data.details;
2968
- if (!details || !details.fragments || details.fragments.length === 0) {
2969
- return;
2970
- }
2971
- var fragmentsToScan = Math.min(5, details.fragments.length);
2972
- for(var i = 0; i < fragmentsToScan; i++){
2973
- var frag = details.fragments[i];
2974
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
2975
- if (!Array.isArray(tagList)) continue;
2976
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2977
- try {
2978
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2979
- var entry = _step.value;
2980
- var tag = "";
2981
- var value = "";
2982
- if (Array.isArray(entry)) {
2983
- var _entry_, _entry_1;
2984
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
2985
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
2986
- } else if (typeof entry === "string") {
2987
- var idx = entry.indexOf(":");
2988
- if (idx >= 0) {
2989
- tag = entry.substring(0, idx);
2990
- value = entry.substring(idx + 1);
2991
- } else {
2992
- tag = entry;
2993
- }
2994
- }
2995
- if (!tag) continue;
2996
- if (tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-DATERANGE")) {
2997
- var attrs = tag.includes("EXT-X-DATERANGE") ? _this.parseAttributeList(value) : {};
2998
- var hasScteOut = tag.includes("EXT-X-CUE-OUT") || "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
2999
- if (hasScteOut) {
3000
- var durationSeconds = _this.parseCueOutDuration(value);
3001
- var marker = _object_spread_props(_object_spread({
3002
- type: "start"
3003
- }, durationSeconds !== void 0 ? {
3004
- durationSeconds: durationSeconds
3005
- } : {}), {
3006
- raw: {
3007
- tag: tag,
3008
- value: value,
3009
- earlyDetection: true
3010
- }
3011
- });
3012
- if (_this.config.debugAdTiming) {
3013
- console.log("[StormcloudVideoPlayer] \uD83C\uDFAF EARLY SCTE-35 DETECTION: Ad break marker found in fragment", i, "- starting pre-fetch (NOT playing yet)");
3014
- }
3015
- _this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
3016
- return;
3017
- }
3018
- }
3019
- }
3020
- } catch (err) {
3021
- _didIteratorError = true;
3022
- _iteratorError = err;
3023
- } finally{
3024
- try {
3025
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3026
- _iterator.return();
3027
- }
3028
- } finally{
3029
- if (_didIteratorError) {
3030
- throw _iteratorError;
3031
- }
3032
- }
3033
- }
3034
- }
2973
+ _this.checkAdInsertionInManifest();
3035
2974
  });
3036
2975
  this.hls.on(import_hls.default.Events.FRAG_BUFFERED, function(_evt, data) {
3037
2976
  return _async_to_generator(function() {
@@ -3093,124 +3032,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3093
3032
  });
3094
3033
  }).call(_this);
3095
3034
  });
3096
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, function(_evt, data) {
3097
- var id3Tags = ((data === null || data === void 0 ? void 0 : data.samples) || []).map(function(s) {
3098
- return {
3099
- key: "ID3",
3100
- value: s === null || s === void 0 ? void 0 : s.data,
3101
- ptsSeconds: s === null || s === void 0 ? void 0 : s.pts
3102
- };
3103
- });
3104
- id3Tags.forEach(function(tag) {
3105
- return _this.onId3Tag(tag);
3106
- });
3107
- });
3108
3035
  this.hls.on(import_hls.default.Events.FRAG_CHANGED, function(_evt, data) {
3109
3036
  var frag = data === null || data === void 0 ? void 0 : data.frag;
3110
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3111
- if (!Array.isArray(tagList)) return;
3112
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3113
- try {
3114
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3115
- var entry = _step.value;
3116
- var tag = "";
3117
- var value = "";
3118
- if (Array.isArray(entry)) {
3119
- var _entry_, _entry_1;
3120
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3121
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3122
- } else if (typeof entry === "string") {
3123
- var idx = entry.indexOf(":");
3124
- if (idx >= 0) {
3125
- tag = entry.substring(0, idx);
3126
- value = entry.substring(idx + 1);
3127
- } else {
3128
- tag = entry;
3129
- value = "";
3130
- }
3037
+ if (!frag) return;
3038
+ if (_this.lastAdInsertionPoint && !_this.inAdBreak && _this.lastAdInsertionPoint.updated_at !== _this.processedAdInsertionUpdatedAt) {
3039
+ var segmentName = _this.lastAdInsertionPoint.segment_ts_name;
3040
+ if (_this.fragmentMatchesSegment(frag, segmentName)) {
3041
+ var _ref;
3042
+ _this.processedAdInsertionUpdatedAt = _this.lastAdInsertionPoint.updated_at;
3043
+ var offsetMs = (_this.lastAdInsertionPoint.offset_seconds || 0) * 1e3;
3044
+ _this.pushAdInsertionDebug("segment_playing", segmentName, {
3045
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3046
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
3047
+ });
3048
+ if (_this.config.debugAdTiming) {
3049
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" now playing — scheduling ad start in ').concat(offsetMs, "ms"));
3131
3050
  }
3132
- if (!tag) continue;
3133
- if (tag.includes("EXT-X-CUE-OUT-CONT")) {
3134
- var prog = _this.parseCueOutCont(value);
3135
- var marker = _object_spread_props(_object_spread({
3136
- type: "progress"
3137
- }, (prog === null || prog === void 0 ? void 0 : prog.duration) !== void 0 ? {
3138
- durationSeconds: prog.duration
3139
- } : {}, (prog === null || prog === void 0 ? void 0 : prog.elapsed) !== void 0 ? {
3140
- ptsSeconds: prog.elapsed
3141
- } : {}), {
3142
- raw: {
3143
- tag: tag,
3144
- value: value
3145
- }
3051
+ _this.pushAdInsertionDebug("ad_scheduled", segmentName, {
3052
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3053
+ detail: "in ".concat(offsetMs, "ms, dur=60s")
3054
+ });
3055
+ _this.clearAdInsertionOffsetTimer();
3056
+ _this.adInsertionOffsetTimerId = window.setTimeout(function() {
3057
+ _this.adInsertionOffsetTimerId = void 0;
3058
+ if (_this.inAdBreak) return;
3059
+ _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3060
+ detail: "ad break started (60s)"
3146
3061
  });
3147
- _this.onScte35Marker(marker);
3148
- } else if (tag.includes("EXT-X-CUE-OUT")) {
3149
- var durationSeconds = _this.parseCueOutDuration(value);
3150
- var marker1 = _object_spread_props(_object_spread({
3151
- type: "start"
3152
- }, durationSeconds !== void 0 ? {
3153
- durationSeconds: durationSeconds
3154
- } : {}), {
3062
+ var marker = {
3063
+ type: "start",
3064
+ durationSeconds: 60,
3155
3065
  raw: {
3156
- tag: tag,
3157
- value: value
3066
+ apiInsertionPoint: _this.lastAdInsertionPoint
3158
3067
  }
3159
- });
3160
- _this.onScte35Marker(marker1);
3161
- } else if (tag.includes("EXT-X-CUE-IN")) {
3162
- _this.onScte35Marker({
3163
- type: "end",
3164
- raw: {
3165
- tag: tag,
3166
- value: value
3167
- }
3168
- });
3169
- } else if (tag.includes("EXT-X-DATERANGE")) {
3170
- var _attrs_CLASS;
3171
- var attrs = _this.parseAttributeList(value);
3172
- var hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3173
- var hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
3174
- var klass = String((_attrs_CLASS = attrs["CLASS"]) !== null && _attrs_CLASS !== void 0 ? _attrs_CLASS : "");
3175
- var duration = _this.toNumber(attrs["DURATION"]);
3176
- if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
3177
- var marker2 = _object_spread_props(_object_spread({
3178
- type: "start"
3179
- }, duration !== void 0 ? {
3180
- durationSeconds: duration
3181
- } : {}), {
3182
- raw: {
3183
- tag: tag,
3184
- value: value,
3185
- attrs: attrs
3186
- }
3187
- });
3188
- _this.onScte35Marker(marker2);
3189
- }
3190
- if (hasScteIn) {
3191
- _this.onScte35Marker({
3192
- type: "end",
3193
- raw: {
3194
- tag: tag,
3195
- value: value,
3196
- attrs: attrs
3197
- }
3198
- });
3199
- }
3200
- }
3201
- }
3202
- } catch (err) {
3203
- _didIteratorError = true;
3204
- _iteratorError = err;
3205
- } finally{
3206
- try {
3207
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3208
- _iterator.return();
3209
- }
3210
- } finally{
3211
- if (_didIteratorError) {
3212
- throw _iteratorError;
3213
- }
3068
+ };
3069
+ _this.onScte35Marker(marker);
3070
+ }, offsetMs);
3214
3071
  }
3215
3072
  }
3216
3073
  });
@@ -4410,6 +4267,182 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4410
4267
  this.preloadedTokens = [];
4411
4268
  }
4412
4269
  },
4270
+ {
4271
+ key: "startAdInsertionPolling",
4272
+ value: function startAdInsertionPolling() {
4273
+ var _this = this;
4274
+ if (this.adInsertionPollingId != null) return;
4275
+ this.fetchAdInsertionPoint();
4276
+ this.adInsertionPollingId = window.setInterval(function() {
4277
+ _this.fetchAdInsertionPoint();
4278
+ }, 1e3);
4279
+ }
4280
+ },
4281
+ {
4282
+ key: "stopAdInsertionPolling",
4283
+ value: function stopAdInsertionPolling() {
4284
+ if (this.adInsertionPollingId != null) {
4285
+ clearInterval(this.adInsertionPollingId);
4286
+ this.adInsertionPollingId = void 0;
4287
+ }
4288
+ }
4289
+ },
4290
+ {
4291
+ key: "fetchAdInsertionPoint",
4292
+ value: function fetchAdInsertionPoint() {
4293
+ return _async_to_generator(function() {
4294
+ var _this_lastAdInsertionPoint, resp, data, isNew, unused;
4295
+ return _ts_generator(this, function(_state) {
4296
+ switch(_state.label){
4297
+ case 0:
4298
+ if (!this.config.projectId) return [
4299
+ 2
4300
+ ];
4301
+ _state.label = 1;
4302
+ case 1:
4303
+ _state.trys.push([
4304
+ 1,
4305
+ 4,
4306
+ ,
4307
+ 5
4308
+ ]);
4309
+ return [
4310
+ 4,
4311
+ fetch("https://adstorm.co/api-adstorm-dev/adstorm/swirl/projects/".concat(encodeURIComponent(this.config.projectId), "/ad-insertion-point"))
4312
+ ];
4313
+ case 2:
4314
+ resp = _state.sent();
4315
+ if (!resp.ok) return [
4316
+ 2
4317
+ ];
4318
+ return [
4319
+ 4,
4320
+ resp.json()
4321
+ ];
4322
+ case 3:
4323
+ data = _state.sent();
4324
+ isNew = data.updated_at !== ((_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.updated_at);
4325
+ this.lastAdInsertionPoint = data;
4326
+ if (isNew) {
4327
+ this.pushAdInsertionDebug("api_response", data.segment_ts_name, {
4328
+ offsetSeconds: data.offset_seconds,
4329
+ updatedAt: data.updated_at,
4330
+ detail: "project=".concat(data.project_id)
4331
+ });
4332
+ }
4333
+ if (this.config.debugAdTiming) {
4334
+ console.log("[StormcloudVideoPlayer] Ad insertion point API response:", data);
4335
+ }
4336
+ return [
4337
+ 3,
4338
+ 5
4339
+ ];
4340
+ case 4:
4341
+ unused = _state.sent();
4342
+ if (this.config.debugAdTiming) {
4343
+ console.warn("[StormcloudVideoPlayer] Ad insertion point API fetch failed");
4344
+ }
4345
+ return [
4346
+ 3,
4347
+ 5
4348
+ ];
4349
+ case 5:
4350
+ return [
4351
+ 2
4352
+ ];
4353
+ }
4354
+ });
4355
+ }).call(this);
4356
+ }
4357
+ },
4358
+ {
4359
+ key: "checkAdInsertionInManifest",
4360
+ value: function checkAdInsertionInManifest() {
4361
+ var _this_hls;
4362
+ if (!this.lastAdInsertionPoint) return;
4363
+ if (this.inAdBreak || this.pendingAdBreak) return;
4364
+ if (this.lastAdInsertionPoint.updated_at === this.processedAdInsertionUpdatedAt) return;
4365
+ var segmentName = this.lastAdInsertionPoint.segment_ts_name;
4366
+ var levels = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.levels;
4367
+ if (!levels) return;
4368
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4369
+ try {
4370
+ for(var _iterator = levels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4371
+ var level = _step.value;
4372
+ var _level_details;
4373
+ var fragments = (_level_details = level.details) === null || _level_details === void 0 ? void 0 : _level_details.fragments;
4374
+ if (!Array.isArray(fragments)) continue;
4375
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
4376
+ try {
4377
+ for(var _iterator1 = fragments[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
4378
+ var frag = _step1.value;
4379
+ if (this.fragmentMatchesSegment(frag, segmentName)) {
4380
+ var _ref;
4381
+ this.pushAdInsertionDebug("segment_found", segmentName, {
4382
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
4383
+ });
4384
+ if (this.config.debugAdTiming) {
4385
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — starting pre-fetch'));
4386
+ }
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
+ return;
4397
+ }
4398
+ }
4399
+ } catch (err) {
4400
+ _didIteratorError1 = true;
4401
+ _iteratorError1 = err;
4402
+ } finally{
4403
+ try {
4404
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
4405
+ _iterator1.return();
4406
+ }
4407
+ } finally{
4408
+ if (_didIteratorError1) {
4409
+ throw _iteratorError1;
4410
+ }
4411
+ }
4412
+ }
4413
+ }
4414
+ } catch (err) {
4415
+ _didIteratorError = true;
4416
+ _iteratorError = err;
4417
+ } finally{
4418
+ try {
4419
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4420
+ _iterator.return();
4421
+ }
4422
+ } finally{
4423
+ if (_didIteratorError) {
4424
+ throw _iteratorError;
4425
+ }
4426
+ }
4427
+ }
4428
+ }
4429
+ },
4430
+ {
4431
+ key: "fragmentMatchesSegment",
4432
+ 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);
4435
+ }
4436
+ },
4437
+ {
4438
+ key: "clearAdInsertionOffsetTimer",
4439
+ value: function clearAdInsertionOffsetTimer() {
4440
+ if (this.adInsertionOffsetTimerId != null) {
4441
+ clearTimeout(this.adInsertionOffsetTimerId);
4442
+ this.adInsertionOffsetTimerId = void 0;
4443
+ }
4444
+ }
4445
+ },
4413
4446
  {
4414
4447
  key: "startContinuousFetchLoop",
4415
4448
  value: function startContinuousFetchLoop() {
@@ -5331,6 +5364,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5331
5364
  }
5332
5365
  this.clearAdRequestWatchdog();
5333
5366
  this.clearAdFailsafeTimer();
5367
+ this.clearAdInsertionOffsetTimer();
5334
5368
  this.activeAdRequestToken = null;
5335
5369
  this.isInAdTransition = false;
5336
5370
  this.stopContinuousFetching();
@@ -5661,6 +5695,32 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5661
5695
  });
5662
5696
  }
5663
5697
  },
5698
+ {
5699
+ key: "pushAdInsertionDebug",
5700
+ value: function pushAdInsertionDebug(event, segmentName, opts) {
5701
+ if (!this.config.debugAdTiming) return;
5702
+ this.adInsertionDebugHistory.push(_object_spread({
5703
+ timestampMs: Date.now(),
5704
+ event: event,
5705
+ segmentName: segmentName
5706
+ }, (opts === null || opts === void 0 ? void 0 : opts.offsetSeconds) !== void 0 ? {
5707
+ offsetSeconds: opts.offsetSeconds
5708
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.updatedAt) ? {
5709
+ updatedAt: opts.updatedAt
5710
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.detail) ? {
5711
+ detail: opts.detail
5712
+ } : {}));
5713
+ if (this.adInsertionDebugHistory.length > DEBUG_HISTORY_LIMIT) {
5714
+ this.adInsertionDebugHistory = this.adInsertionDebugHistory.slice(-DEBUG_HISTORY_LIMIT);
5715
+ }
5716
+ }
5717
+ },
5718
+ {
5719
+ key: "getAdInsertionDebugLog",
5720
+ value: function getAdInsertionDebugLog() {
5721
+ return this.adInsertionDebugHistory.slice();
5722
+ }
5723
+ },
5664
5724
  {
5665
5725
  key: "getDebugLogs",
5666
5726
  value: function getDebugLogs() {
@@ -5927,6 +5987,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5927
5987
  key: "destroy",
5928
5988
  value: function destroy() {
5929
5989
  var _this_hls, _this_adLayer;
5990
+ this.stopAdInsertionPolling();
5991
+ this.clearAdInsertionOffsetTimer();
5930
5992
  this.stopContinuousFetching();
5931
5993
  this.stopFillerBreakTimer();
5932
5994
  this.clearAdStartTimer();
@@ -5958,6 +6020,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5958
6020
  this.consecutiveFailures = 0;
5959
6021
  this.debugLogEntries = [];
5960
6022
  this.scteMarkerHistory = [];
6023
+ this.adInsertionDebugHistory = [];
5961
6024
  }
5962
6025
  }
5963
6026
  ]);