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.
@@ -2790,6 +2790,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2790
2790
  this.isInAdTransition = false;
2791
2791
  this.maxPlaceholderDurationMs = 5e3;
2792
2792
  this.isShowingPlaceholder = false;
2793
+ this.lastAdInsertionPoint = null;
2794
+ this.processedAdInsertionUpdatedAt = null;
2793
2795
  this.totalAdRequestsInBreak = 0;
2794
2796
  this.maxTotalAdRequestsPerBreak = 20;
2795
2797
  this.pendingAdBreak = null;
@@ -2807,6 +2809,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2807
2809
  this.preloadedTokens = [];
2808
2810
  this.debugLogEntries = [];
2809
2811
  this.scteMarkerHistory = [];
2812
+ this.adInsertionDebugHistory = [];
2810
2813
  initializePolyfills();
2811
2814
  var browserOverrides = getBrowserConfigOverrides();
2812
2815
  this.config = _object_spread({}, browserOverrides, config);
@@ -2991,6 +2994,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2991
2994
  _state.sent();
2992
2995
  _state.label = 2;
2993
2996
  case 2:
2997
+ if (!this.config.disableAds && this.config.projectId) {
2998
+ this.startAdInsertionPolling();
2999
+ }
2994
3000
  return [
2995
3001
  2
2996
3002
  ];
@@ -2998,78 +3004,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2998
3004
  });
2999
3005
  }).call(_this);
3000
3006
  });
3001
- this.hls.on(import_hls.default.Events.LEVEL_LOADED, function(_evt, data) {
3007
+ this.hls.on(import_hls.default.Events.LEVEL_LOADED, function() {
3002
3008
  if (_this.inAdBreak || _this.pendingAdBreak) {
3003
3009
  return;
3004
3010
  }
3005
- var details = data === null || data === void 0 ? void 0 : data.details;
3006
- if (!details || !details.fragments || details.fragments.length === 0) {
3007
- return;
3008
- }
3009
- var fragmentsToScan = Math.min(5, details.fragments.length);
3010
- for(var i = 0; i < fragmentsToScan; i++){
3011
- var frag = details.fragments[i];
3012
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3013
- if (!Array.isArray(tagList)) continue;
3014
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3015
- try {
3016
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3017
- var entry = _step.value;
3018
- var tag = "";
3019
- var value = "";
3020
- if (Array.isArray(entry)) {
3021
- var _entry_, _entry_1;
3022
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3023
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3024
- } else if (typeof entry === "string") {
3025
- var idx = entry.indexOf(":");
3026
- if (idx >= 0) {
3027
- tag = entry.substring(0, idx);
3028
- value = entry.substring(idx + 1);
3029
- } else {
3030
- tag = entry;
3031
- }
3032
- }
3033
- if (!tag) continue;
3034
- if (tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-DATERANGE")) {
3035
- var attrs = tag.includes("EXT-X-DATERANGE") ? _this.parseAttributeList(value) : {};
3036
- var hasScteOut = tag.includes("EXT-X-CUE-OUT") || "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3037
- if (hasScteOut) {
3038
- var durationSeconds = _this.parseCueOutDuration(value);
3039
- var marker = _object_spread_props(_object_spread({
3040
- type: "start"
3041
- }, durationSeconds !== void 0 ? {
3042
- durationSeconds: durationSeconds
3043
- } : {}), {
3044
- raw: {
3045
- tag: tag,
3046
- value: value,
3047
- earlyDetection: true
3048
- }
3049
- });
3050
- if (_this.config.debugAdTiming) {
3051
- console.log("[StormcloudVideoPlayer] \uD83C\uDFAF EARLY SCTE-35 DETECTION: Ad break marker found in fragment", i, "- starting pre-fetch (NOT playing yet)");
3052
- }
3053
- _this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
3054
- return;
3055
- }
3056
- }
3057
- }
3058
- } catch (err) {
3059
- _didIteratorError = true;
3060
- _iteratorError = err;
3061
- } finally{
3062
- try {
3063
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3064
- _iterator.return();
3065
- }
3066
- } finally{
3067
- if (_didIteratorError) {
3068
- throw _iteratorError;
3069
- }
3070
- }
3071
- }
3072
- }
3011
+ _this.checkAdInsertionInManifest();
3073
3012
  });
3074
3013
  this.hls.on(import_hls.default.Events.FRAG_BUFFERED, function(_evt, data) {
3075
3014
  return _async_to_generator(function() {
@@ -3131,124 +3070,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3131
3070
  });
3132
3071
  }).call(_this);
3133
3072
  });
3134
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, function(_evt, data) {
3135
- var id3Tags = ((data === null || data === void 0 ? void 0 : data.samples) || []).map(function(s) {
3136
- return {
3137
- key: "ID3",
3138
- value: s === null || s === void 0 ? void 0 : s.data,
3139
- ptsSeconds: s === null || s === void 0 ? void 0 : s.pts
3140
- };
3141
- });
3142
- id3Tags.forEach(function(tag) {
3143
- return _this.onId3Tag(tag);
3144
- });
3145
- });
3146
3073
  this.hls.on(import_hls.default.Events.FRAG_CHANGED, function(_evt, data) {
3147
3074
  var frag = data === null || data === void 0 ? void 0 : data.frag;
3148
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3149
- if (!Array.isArray(tagList)) return;
3150
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3151
- try {
3152
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3153
- var entry = _step.value;
3154
- var tag = "";
3155
- var value = "";
3156
- if (Array.isArray(entry)) {
3157
- var _entry_, _entry_1;
3158
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3159
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3160
- } else if (typeof entry === "string") {
3161
- var idx = entry.indexOf(":");
3162
- if (idx >= 0) {
3163
- tag = entry.substring(0, idx);
3164
- value = entry.substring(idx + 1);
3165
- } else {
3166
- tag = entry;
3167
- value = "";
3168
- }
3075
+ if (!frag) return;
3076
+ if (_this.lastAdInsertionPoint && !_this.inAdBreak && _this.lastAdInsertionPoint.updated_at !== _this.processedAdInsertionUpdatedAt) {
3077
+ var segmentName = _this.lastAdInsertionPoint.segment_ts_name;
3078
+ if (_this.fragmentMatchesSegment(frag, segmentName)) {
3079
+ var _ref;
3080
+ _this.processedAdInsertionUpdatedAt = _this.lastAdInsertionPoint.updated_at;
3081
+ var offsetMs = (_this.lastAdInsertionPoint.offset_seconds || 0) * 1e3;
3082
+ _this.pushAdInsertionDebug("segment_playing", segmentName, {
3083
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3084
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
3085
+ });
3086
+ if (_this.config.debugAdTiming) {
3087
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" now playing — scheduling ad start in ').concat(offsetMs, "ms"));
3169
3088
  }
3170
- if (!tag) continue;
3171
- if (tag.includes("EXT-X-CUE-OUT-CONT")) {
3172
- var prog = _this.parseCueOutCont(value);
3173
- var marker = _object_spread_props(_object_spread({
3174
- type: "progress"
3175
- }, (prog === null || prog === void 0 ? void 0 : prog.duration) !== void 0 ? {
3176
- durationSeconds: prog.duration
3177
- } : {}, (prog === null || prog === void 0 ? void 0 : prog.elapsed) !== void 0 ? {
3178
- ptsSeconds: prog.elapsed
3179
- } : {}), {
3180
- raw: {
3181
- tag: tag,
3182
- value: value
3183
- }
3184
- });
3185
- _this.onScte35Marker(marker);
3186
- } else if (tag.includes("EXT-X-CUE-OUT")) {
3187
- var durationSeconds = _this.parseCueOutDuration(value);
3188
- var marker1 = _object_spread_props(_object_spread({
3189
- type: "start"
3190
- }, durationSeconds !== void 0 ? {
3191
- durationSeconds: durationSeconds
3192
- } : {}), {
3193
- raw: {
3194
- tag: tag,
3195
- value: value
3196
- }
3089
+ _this.pushAdInsertionDebug("ad_scheduled", segmentName, {
3090
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3091
+ detail: "in ".concat(offsetMs, "ms, dur=60s")
3092
+ });
3093
+ _this.clearAdInsertionOffsetTimer();
3094
+ _this.adInsertionOffsetTimerId = window.setTimeout(function() {
3095
+ _this.adInsertionOffsetTimerId = void 0;
3096
+ if (_this.inAdBreak) return;
3097
+ _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3098
+ detail: "ad break started (60s)"
3197
3099
  });
3198
- _this.onScte35Marker(marker1);
3199
- } else if (tag.includes("EXT-X-CUE-IN")) {
3200
- _this.onScte35Marker({
3201
- type: "end",
3100
+ var marker = {
3101
+ type: "start",
3102
+ durationSeconds: 60,
3202
3103
  raw: {
3203
- tag: tag,
3204
- value: value
3104
+ apiInsertionPoint: _this.lastAdInsertionPoint
3205
3105
  }
3206
- });
3207
- } else if (tag.includes("EXT-X-DATERANGE")) {
3208
- var _attrs_CLASS;
3209
- var attrs = _this.parseAttributeList(value);
3210
- var hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3211
- var hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
3212
- var klass = String((_attrs_CLASS = attrs["CLASS"]) !== null && _attrs_CLASS !== void 0 ? _attrs_CLASS : "");
3213
- var duration = _this.toNumber(attrs["DURATION"]);
3214
- if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
3215
- var marker2 = _object_spread_props(_object_spread({
3216
- type: "start"
3217
- }, duration !== void 0 ? {
3218
- durationSeconds: duration
3219
- } : {}), {
3220
- raw: {
3221
- tag: tag,
3222
- value: value,
3223
- attrs: attrs
3224
- }
3225
- });
3226
- _this.onScte35Marker(marker2);
3227
- }
3228
- if (hasScteIn) {
3229
- _this.onScte35Marker({
3230
- type: "end",
3231
- raw: {
3232
- tag: tag,
3233
- value: value,
3234
- attrs: attrs
3235
- }
3236
- });
3237
- }
3238
- }
3239
- }
3240
- } catch (err) {
3241
- _didIteratorError = true;
3242
- _iteratorError = err;
3243
- } finally{
3244
- try {
3245
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3246
- _iterator.return();
3247
- }
3248
- } finally{
3249
- if (_didIteratorError) {
3250
- throw _iteratorError;
3251
- }
3106
+ };
3107
+ _this.onScte35Marker(marker);
3108
+ }, offsetMs);
3252
3109
  }
3253
3110
  }
3254
3111
  });
@@ -4448,6 +4305,182 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4448
4305
  this.preloadedTokens = [];
4449
4306
  }
4450
4307
  },
4308
+ {
4309
+ key: "startAdInsertionPolling",
4310
+ value: function startAdInsertionPolling() {
4311
+ var _this = this;
4312
+ if (this.adInsertionPollingId != null) return;
4313
+ this.fetchAdInsertionPoint();
4314
+ this.adInsertionPollingId = window.setInterval(function() {
4315
+ _this.fetchAdInsertionPoint();
4316
+ }, 1e3);
4317
+ }
4318
+ },
4319
+ {
4320
+ key: "stopAdInsertionPolling",
4321
+ value: function stopAdInsertionPolling() {
4322
+ if (this.adInsertionPollingId != null) {
4323
+ clearInterval(this.adInsertionPollingId);
4324
+ this.adInsertionPollingId = void 0;
4325
+ }
4326
+ }
4327
+ },
4328
+ {
4329
+ key: "fetchAdInsertionPoint",
4330
+ value: function fetchAdInsertionPoint() {
4331
+ return _async_to_generator(function() {
4332
+ var _this_lastAdInsertionPoint, resp, data, isNew, unused;
4333
+ return _ts_generator(this, function(_state) {
4334
+ switch(_state.label){
4335
+ case 0:
4336
+ if (!this.config.projectId) return [
4337
+ 2
4338
+ ];
4339
+ _state.label = 1;
4340
+ case 1:
4341
+ _state.trys.push([
4342
+ 1,
4343
+ 4,
4344
+ ,
4345
+ 5
4346
+ ]);
4347
+ return [
4348
+ 4,
4349
+ fetch("https://adstorm.co/api-adstorm-dev/adstorm/swirl/projects/".concat(encodeURIComponent(this.config.projectId), "/ad-insertion-point"))
4350
+ ];
4351
+ case 2:
4352
+ resp = _state.sent();
4353
+ if (!resp.ok) return [
4354
+ 2
4355
+ ];
4356
+ return [
4357
+ 4,
4358
+ resp.json()
4359
+ ];
4360
+ case 3:
4361
+ data = _state.sent();
4362
+ isNew = data.updated_at !== ((_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.updated_at);
4363
+ this.lastAdInsertionPoint = data;
4364
+ if (isNew) {
4365
+ this.pushAdInsertionDebug("api_response", data.segment_ts_name, {
4366
+ offsetSeconds: data.offset_seconds,
4367
+ updatedAt: data.updated_at,
4368
+ detail: "project=".concat(data.project_id)
4369
+ });
4370
+ }
4371
+ if (this.config.debugAdTiming) {
4372
+ console.log("[StormcloudVideoPlayer] Ad insertion point API response:", data);
4373
+ }
4374
+ return [
4375
+ 3,
4376
+ 5
4377
+ ];
4378
+ case 4:
4379
+ unused = _state.sent();
4380
+ if (this.config.debugAdTiming) {
4381
+ console.warn("[StormcloudVideoPlayer] Ad insertion point API fetch failed");
4382
+ }
4383
+ return [
4384
+ 3,
4385
+ 5
4386
+ ];
4387
+ case 5:
4388
+ return [
4389
+ 2
4390
+ ];
4391
+ }
4392
+ });
4393
+ }).call(this);
4394
+ }
4395
+ },
4396
+ {
4397
+ key: "checkAdInsertionInManifest",
4398
+ value: function checkAdInsertionInManifest() {
4399
+ var _this_hls;
4400
+ if (!this.lastAdInsertionPoint) return;
4401
+ if (this.inAdBreak || this.pendingAdBreak) return;
4402
+ if (this.lastAdInsertionPoint.updated_at === this.processedAdInsertionUpdatedAt) return;
4403
+ var segmentName = this.lastAdInsertionPoint.segment_ts_name;
4404
+ var levels = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.levels;
4405
+ if (!levels) return;
4406
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4407
+ try {
4408
+ for(var _iterator = levels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4409
+ var level = _step.value;
4410
+ var _level_details;
4411
+ var fragments = (_level_details = level.details) === null || _level_details === void 0 ? void 0 : _level_details.fragments;
4412
+ if (!Array.isArray(fragments)) continue;
4413
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
4414
+ try {
4415
+ for(var _iterator1 = fragments[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
4416
+ var frag = _step1.value;
4417
+ if (this.fragmentMatchesSegment(frag, segmentName)) {
4418
+ var _ref;
4419
+ this.pushAdInsertionDebug("segment_found", segmentName, {
4420
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
4421
+ });
4422
+ if (this.config.debugAdTiming) {
4423
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — starting pre-fetch'));
4424
+ }
4425
+ var marker = {
4426
+ type: "start",
4427
+ durationSeconds: 60,
4428
+ raw: {
4429
+ apiInsertionPoint: this.lastAdInsertionPoint,
4430
+ earlyDetection: true
4431
+ }
4432
+ };
4433
+ this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
4434
+ return;
4435
+ }
4436
+ }
4437
+ } catch (err) {
4438
+ _didIteratorError1 = true;
4439
+ _iteratorError1 = err;
4440
+ } finally{
4441
+ try {
4442
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
4443
+ _iterator1.return();
4444
+ }
4445
+ } finally{
4446
+ if (_didIteratorError1) {
4447
+ throw _iteratorError1;
4448
+ }
4449
+ }
4450
+ }
4451
+ }
4452
+ } catch (err) {
4453
+ _didIteratorError = true;
4454
+ _iteratorError = err;
4455
+ } finally{
4456
+ try {
4457
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4458
+ _iterator.return();
4459
+ }
4460
+ } finally{
4461
+ if (_didIteratorError) {
4462
+ throw _iteratorError;
4463
+ }
4464
+ }
4465
+ }
4466
+ }
4467
+ },
4468
+ {
4469
+ key: "fragmentMatchesSegment",
4470
+ value: function fragmentMatchesSegment(frag, segmentName) {
4471
+ var url = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4472
+ return url.endsWith(segmentName) || url.includes("/" + segmentName);
4473
+ }
4474
+ },
4475
+ {
4476
+ key: "clearAdInsertionOffsetTimer",
4477
+ value: function clearAdInsertionOffsetTimer() {
4478
+ if (this.adInsertionOffsetTimerId != null) {
4479
+ clearTimeout(this.adInsertionOffsetTimerId);
4480
+ this.adInsertionOffsetTimerId = void 0;
4481
+ }
4482
+ }
4483
+ },
4451
4484
  {
4452
4485
  key: "startContinuousFetchLoop",
4453
4486
  value: function startContinuousFetchLoop() {
@@ -5369,6 +5402,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5369
5402
  }
5370
5403
  this.clearAdRequestWatchdog();
5371
5404
  this.clearAdFailsafeTimer();
5405
+ this.clearAdInsertionOffsetTimer();
5372
5406
  this.activeAdRequestToken = null;
5373
5407
  this.isInAdTransition = false;
5374
5408
  this.stopContinuousFetching();
@@ -5699,6 +5733,32 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5699
5733
  });
5700
5734
  }
5701
5735
  },
5736
+ {
5737
+ key: "pushAdInsertionDebug",
5738
+ value: function pushAdInsertionDebug(event, segmentName, opts) {
5739
+ if (!this.config.debugAdTiming) return;
5740
+ this.adInsertionDebugHistory.push(_object_spread({
5741
+ timestampMs: Date.now(),
5742
+ event: event,
5743
+ segmentName: segmentName
5744
+ }, (opts === null || opts === void 0 ? void 0 : opts.offsetSeconds) !== void 0 ? {
5745
+ offsetSeconds: opts.offsetSeconds
5746
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.updatedAt) ? {
5747
+ updatedAt: opts.updatedAt
5748
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.detail) ? {
5749
+ detail: opts.detail
5750
+ } : {}));
5751
+ if (this.adInsertionDebugHistory.length > DEBUG_HISTORY_LIMIT) {
5752
+ this.adInsertionDebugHistory = this.adInsertionDebugHistory.slice(-DEBUG_HISTORY_LIMIT);
5753
+ }
5754
+ }
5755
+ },
5756
+ {
5757
+ key: "getAdInsertionDebugLog",
5758
+ value: function getAdInsertionDebugLog() {
5759
+ return this.adInsertionDebugHistory.slice();
5760
+ }
5761
+ },
5702
5762
  {
5703
5763
  key: "getDebugLogs",
5704
5764
  value: function getDebugLogs() {
@@ -5965,6 +6025,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5965
6025
  key: "destroy",
5966
6026
  value: function destroy() {
5967
6027
  var _this_hls, _this_adLayer;
6028
+ this.stopAdInsertionPolling();
6029
+ this.clearAdInsertionOffsetTimer();
5968
6030
  this.stopContinuousFetching();
5969
6031
  this.stopFillerBreakTimer();
5970
6032
  this.clearAdStartTimer();
@@ -5996,6 +6058,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5996
6058
  this.consecutiveFailures = 0;
5997
6059
  this.debugLogEntries = [];
5998
6060
  this.scteMarkerHistory = [];
6061
+ this.adInsertionDebugHistory = [];
5999
6062
  }
6000
6063
  }
6001
6064
  ]);
@@ -7053,10 +7116,13 @@ function hexToRgb(hex) {
7053
7116
  var num = parseInt(clean.length === 3 ? clean.replace(/./g, "$&$&") : clean, 16);
7054
7117
  return "".concat(num >> 16 & 255, ",").concat(num >> 8 & 255, ",").concat(num & 255);
7055
7118
  }
7119
+ var FADE_DURATION_MS = 1e3;
7056
7120
  var OverlayRenderer = function OverlayRenderer(param) {
7057
7121
  var overlays = param.overlays, currentTime = param.currentTime, videoRef = param.videoRef, coordinateSpace = param.coordinateSpace;
7058
7122
  var _ref = _sliced_to_array((0, import_react.useState)(null), 2), dims = _ref[0], setDims = _ref[1];
7059
7123
  var rafRef = (0, import_react.useRef)(null);
7124
+ var _ref1 = _sliced_to_array((0, import_react.useState)(/* @__PURE__ */ new Map()), 2), fadeMap = _ref1[0], setFadeMap = _ref1[1];
7125
+ var removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());
7060
7126
  var updateDims = (0, import_react.useCallback)(function() {
7061
7127
  var video = videoRef.current;
7062
7128
  if (video) {
@@ -7087,10 +7153,181 @@ var OverlayRenderer = function OverlayRenderer(param) {
7087
7153
  }, [
7088
7154
  updateDims
7089
7155
  ]);
7090
- var activeOverlays = overlays.filter(function(o) {
7091
- return isOverlayActive(o, currentTime);
7092
- });
7093
- if (!dims || activeOverlays.length === 0) return null;
7156
+ var activeOverlays = (0, import_react.useMemo)(function() {
7157
+ return overlays.filter(function(o) {
7158
+ return isOverlayActive(o, currentTime);
7159
+ });
7160
+ }, [
7161
+ overlays,
7162
+ currentTime
7163
+ ]);
7164
+ (0, import_react.useEffect)(function() {
7165
+ var activeIds = new Set(activeOverlays.map(function(o) {
7166
+ return o.id;
7167
+ }));
7168
+ setFadeMap(function(prev) {
7169
+ var next = new Map(prev);
7170
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7171
+ try {
7172
+ for(var _iterator = activeOverlays[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7173
+ var overlay = _step.value;
7174
+ if (!next.has(overlay.id)) {
7175
+ next.set(overlay.id, {
7176
+ overlay: overlay,
7177
+ visible: false
7178
+ });
7179
+ } else {
7180
+ var existing = next.get(overlay.id);
7181
+ next.set(overlay.id, _object_spread_props(_object_spread({}, existing), {
7182
+ overlay: overlay
7183
+ }));
7184
+ }
7185
+ }
7186
+ } catch (err) {
7187
+ _didIteratorError = true;
7188
+ _iteratorError = err;
7189
+ } finally{
7190
+ try {
7191
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7192
+ _iterator.return();
7193
+ }
7194
+ } finally{
7195
+ if (_didIteratorError) {
7196
+ throw _iteratorError;
7197
+ }
7198
+ }
7199
+ }
7200
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
7201
+ try {
7202
+ var _loop = function() {
7203
+ var _step_value = _sliced_to_array(_step1.value, 2), id = _step_value[0], state = _step_value[1];
7204
+ if (!activeIds.has(id) && state.visible) {
7205
+ next.set(id, _object_spread_props(_object_spread({}, state), {
7206
+ visible: false
7207
+ }));
7208
+ if (!removeTimers.current.has(id)) {
7209
+ var timer = setTimeout(function() {
7210
+ setFadeMap(function(m) {
7211
+ var updated = new Map(m);
7212
+ updated.delete(id);
7213
+ return updated;
7214
+ });
7215
+ removeTimers.current.delete(id);
7216
+ }, FADE_DURATION_MS);
7217
+ removeTimers.current.set(id, timer);
7218
+ }
7219
+ } else if (!activeIds.has(id) && !state.visible) {}
7220
+ };
7221
+ for(var _iterator1 = next[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true)_loop();
7222
+ } catch (err) {
7223
+ _didIteratorError1 = true;
7224
+ _iteratorError1 = err;
7225
+ } finally{
7226
+ try {
7227
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
7228
+ _iterator1.return();
7229
+ }
7230
+ } finally{
7231
+ if (_didIteratorError1) {
7232
+ throw _iteratorError1;
7233
+ }
7234
+ }
7235
+ }
7236
+ return next;
7237
+ });
7238
+ }, [
7239
+ activeOverlays
7240
+ ]);
7241
+ (0, import_react.useEffect)(function() {
7242
+ var toFadeIn = [];
7243
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7244
+ try {
7245
+ var _loop = function() {
7246
+ var _step_value = _sliced_to_array(_step.value, 2), id = _step_value[0], state = _step_value[1];
7247
+ if (!state.visible) {
7248
+ var isActive = activeOverlays.some(function(o) {
7249
+ return o.id === id;
7250
+ });
7251
+ if (isActive) toFadeIn.push(id);
7252
+ }
7253
+ };
7254
+ for(var _iterator = fadeMap[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
7255
+ } catch (err) {
7256
+ _didIteratorError = true;
7257
+ _iteratorError = err;
7258
+ } finally{
7259
+ try {
7260
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7261
+ _iterator.return();
7262
+ }
7263
+ } finally{
7264
+ if (_didIteratorError) {
7265
+ throw _iteratorError;
7266
+ }
7267
+ }
7268
+ }
7269
+ if (toFadeIn.length === 0) return;
7270
+ var raf = requestAnimationFrame(function() {
7271
+ setFadeMap(function(prev) {
7272
+ var next = new Map(prev);
7273
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7274
+ try {
7275
+ for(var _iterator = toFadeIn[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7276
+ var id = _step.value;
7277
+ var state = next.get(id);
7278
+ if (state) next.set(id, _object_spread_props(_object_spread({}, state), {
7279
+ visible: true
7280
+ }));
7281
+ }
7282
+ } catch (err) {
7283
+ _didIteratorError = true;
7284
+ _iteratorError = err;
7285
+ } finally{
7286
+ try {
7287
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7288
+ _iterator.return();
7289
+ }
7290
+ } finally{
7291
+ if (_didIteratorError) {
7292
+ throw _iteratorError;
7293
+ }
7294
+ }
7295
+ }
7296
+ return next;
7297
+ });
7298
+ });
7299
+ return function() {
7300
+ return cancelAnimationFrame(raf);
7301
+ };
7302
+ }, [
7303
+ fadeMap,
7304
+ activeOverlays
7305
+ ]);
7306
+ (0, import_react.useEffect)(function() {
7307
+ return function() {
7308
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7309
+ try {
7310
+ for(var _iterator = removeTimers.current.values()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7311
+ var timer = _step.value;
7312
+ clearTimeout(timer);
7313
+ }
7314
+ } catch (err) {
7315
+ _didIteratorError = true;
7316
+ _iteratorError = err;
7317
+ } finally{
7318
+ try {
7319
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7320
+ _iterator.return();
7321
+ }
7322
+ } finally{
7323
+ if (_didIteratorError) {
7324
+ throw _iteratorError;
7325
+ }
7326
+ }
7327
+ }
7328
+ };
7329
+ }, []);
7330
+ if (!dims || fadeMap.size === 0) return null;
7094
7331
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7095
7332
  "aria-hidden": "true",
7096
7333
  style: {
@@ -7103,14 +7340,16 @@ var OverlayRenderer = function OverlayRenderer(param) {
7103
7340
  overflow: "hidden",
7104
7341
  zIndex: 8
7105
7342
  },
7106
- children: activeOverlays.map(function(overlay) {
7343
+ children: _to_consumable_array(fadeMap.values()).map(function(param) {
7344
+ var overlay = param.overlay, visible = param.visible;
7107
7345
  var scaleX = (coordinateSpace === null || coordinateSpace === void 0 ? void 0 : coordinateSpace.width) ? dims.displayWidth / coordinateSpace.width : dims.scaleX;
7108
7346
  var scaleY = (coordinateSpace === null || coordinateSpace === void 0 ? void 0 : coordinateSpace.height) ? dims.displayHeight / coordinateSpace.height : dims.scaleY;
7109
7347
  var left = overlay.x * scaleX;
7110
7348
  var top = overlay.y * scaleY;
7111
7349
  var width = overlay.width * scaleX;
7112
7350
  var height = overlay.height * scaleY;
7113
- var opacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;
7351
+ var baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;
7352
+ var opacity = visible ? baseOpacity : 0;
7114
7353
  var sz = {
7115
7354
  w: width,
7116
7355
  h: height
@@ -7123,6 +7362,7 @@ var OverlayRenderer = function OverlayRenderer(param) {
7123
7362
  width: "".concat(width, "px"),
7124
7363
  height: "".concat(height, "px"),
7125
7364
  opacity: opacity,
7365
+ transition: "opacity ".concat(FADE_DURATION_MS, "ms ease"),
7126
7366
  zIndex: overlay.z_index,
7127
7367
  overflow: "hidden"
7128
7368
  },
@@ -7195,7 +7435,7 @@ var AI_CONTEXT_MIN_POLL_MS = 800;
7195
7435
  var PANEL_BASE_RIGHT_OFFSET = 10;
7196
7436
  var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props) {
7197
7437
  var _ref;
7198
- var _aiLiveContext_context, _aiLiveContext_context_keywords, _aiLiveContext_context1;
7438
+ var _aiLiveContext_context;
7199
7439
  var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, minSegmentsBeforePlay = props.minSegmentsBeforePlay, disableAds = props.disableAds, disableFiller = props.disableFiller, swirlProjectId = props.swirlProjectId, restVideoAttrs = _object_without_properties(props, [
7200
7440
  "src",
7201
7441
  "autoplay",
@@ -7287,13 +7527,39 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7287
7527
  return "--:--:--";
7288
7528
  }
7289
7529
  };
7290
- var formatDebugRaw = function formatDebugRaw(raw) {
7291
- if (!raw || (typeof raw === "undefined" ? "undefined" : _type_of(raw)) !== "object") return "";
7292
- var obj = raw;
7293
- if (typeof obj.tag === "string") return obj.tag;
7294
- if (typeof obj.id3 === "string") return "ID3";
7295
- if (typeof obj.splice_command_type === "number") return "binary splice";
7296
- return "marker";
7530
+ var formatAdInsertionEvent = function formatAdInsertionEvent(event) {
7531
+ switch(event){
7532
+ case "api_response":
7533
+ return {
7534
+ label: "API",
7535
+ color: "#60a5fa"
7536
+ };
7537
+ case "segment_found":
7538
+ return {
7539
+ label: "FOUND",
7540
+ color: "#a78bfa"
7541
+ };
7542
+ case "segment_playing":
7543
+ return {
7544
+ label: "PLAYING",
7545
+ color: "#fbbf24"
7546
+ };
7547
+ case "ad_scheduled":
7548
+ return {
7549
+ label: "SCHED",
7550
+ color: "#fb923c"
7551
+ };
7552
+ case "ad_triggered":
7553
+ return {
7554
+ label: "TRIGGER",
7555
+ color: "#34d399"
7556
+ };
7557
+ default:
7558
+ return {
7559
+ label: event,
7560
+ color: "rgba(255,255,255,0.68)"
7561
+ };
7562
+ }
7297
7563
  };
7298
7564
  var formatAiRelativeTime = function formatAiRelativeTime(timestamp) {
7299
7565
  var epochMs = Date.parse(timestamp);
@@ -7706,7 +7972,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7706
7972
  var updateDebugData = function updateDebugData() {
7707
7973
  var player = playerRef.current;
7708
7974
  if (!player) return;
7709
- setDebugMarkers(player.getRecentScteMarkers().slice(-DEBUG_PANEL_MARKER_LIMIT).reverse());
7975
+ setDebugMarkers(player.getAdInsertionDebugLog().slice(-DEBUG_PANEL_MARKER_LIMIT).reverse());
7710
7976
  };
7711
7977
  updateDebugData();
7712
7978
  var interval = window.setInterval(updateDebugData, 500);
@@ -8241,34 +8507,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8241
8507
  whiteSpace: "pre-wrap"
8242
8508
  },
8243
8509
  children: (_ref = (_aiLiveContext_context = aiLiveContext.context) === null || _aiLiveContext_context === void 0 ? void 0 : _aiLiveContext_context.context) !== null && _ref !== void 0 ? _ref : aiLiveContext.isLoading ? "Analyzing live stream..." : "Waiting for AI context response."
8244
- }),
8245
- ((_aiLiveContext_context1 = aiLiveContext.context) === null || _aiLiveContext_context1 === void 0 ? void 0 : (_aiLiveContext_context_keywords = _aiLiveContext_context1.keywords) === null || _aiLiveContext_context_keywords === void 0 ? void 0 : _aiLiveContext_context_keywords.length) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8246
- style: {
8247
- marginTop: "10px",
8248
- display: "flex",
8249
- flexWrap: "wrap",
8250
- gap: "6px"
8251
- },
8252
- children: aiLiveContext.context.keywords.slice(0, 12).map(function(kw) {
8253
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8254
- style: {
8255
- fontSize: "10px",
8256
- fontWeight: 600,
8257
- padding: "4px 7px",
8258
- borderRadius: "999px",
8259
- background: "rgba(236, 72, 153, 0.2)",
8260
- border: "1px solid rgba(244, 114, 182, 0.42)",
8261
- color: "#fce7f3",
8262
- maxWidth: "100%",
8263
- overflow: "hidden",
8264
- textOverflow: "ellipsis",
8265
- whiteSpace: "nowrap"
8266
- },
8267
- title: kw,
8268
- children: kw
8269
- }, kw);
8270
- })
8271
- }) : null
8510
+ })
8272
8511
  ]
8273
8512
  })
8274
8513
  ]
@@ -8310,7 +8549,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8310
8549
  fontWeight: 700,
8311
8550
  letterSpacing: "0.02em"
8312
8551
  },
8313
- children: "Debug Ad Timing"
8552
+ children: "Ad Insertion Debug"
8314
8553
  }),
8315
8554
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8316
8555
  className: "sc-ctrl-btn",
@@ -8351,7 +8590,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8351
8590
  color: "rgba(255,255,255,0.68)",
8352
8591
  marginBottom: "8px"
8353
8592
  },
8354
- children: "SCTE-35 markers"
8593
+ children: "Ad Insertion Points"
8355
8594
  }),
8356
8595
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8357
8596
  style: {
@@ -8363,12 +8602,13 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8363
8602
  fontSize: "12px",
8364
8603
  color: "rgba(255,255,255,0.55)"
8365
8604
  },
8366
- children: "No markers detected yet."
8605
+ children: "No ad insertion events yet."
8367
8606
  }) : debugMarkers.map(function(entry, idx) {
8607
+ var evt = formatAdInsertionEvent(entry.event);
8368
8608
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8369
8609
  style: {
8370
8610
  display: "grid",
8371
- gridTemplateColumns: "56px 52px 1fr",
8611
+ gridTemplateColumns: "56px 54px 1fr",
8372
8612
  gap: "8px",
8373
8613
  alignItems: "center",
8374
8614
  fontFamily: "'SF Mono', 'Cascadia Code', monospace",
@@ -8387,11 +8627,11 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8387
8627
  }),
8388
8628
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8389
8629
  style: {
8390
- textTransform: "uppercase",
8391
8630
  fontWeight: 700,
8392
- color: entry.type === "start" ? "#34d399" : entry.type === "end" ? "#f87171" : "#fbbf24"
8631
+ color: evt.color,
8632
+ fontSize: "10px"
8393
8633
  },
8394
- children: entry.type
8634
+ children: evt.label
8395
8635
  }),
8396
8636
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8397
8637
  style: {
@@ -8407,11 +8647,9 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8407
8647
  whiteSpace: "nowrap"
8408
8648
  },
8409
8649
  children: [
8410
- entry.durationSeconds != null ? "dur:".concat(entry.durationSeconds.toFixed(2), "s") : "dur:-",
8411
- " ",
8412
- entry.ptsSeconds != null ? "pts:".concat(entry.ptsSeconds.toFixed(2)) : "pts:-",
8413
- " ",
8414
- formatDebugRaw(entry.raw)
8650
+ entry.segmentName,
8651
+ entry.offsetSeconds != null ? " +".concat(entry.offsetSeconds, "s") : "",
8652
+ entry.detail ? " (".concat(entry.detail, ")") : ""
8415
8653
  ]
8416
8654
  })
8417
8655
  })