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.
package/lib/index.cjs CHANGED
@@ -2968,6 +2968,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2968
2968
  this.isInAdTransition = false;
2969
2969
  this.maxPlaceholderDurationMs = 5e3;
2970
2970
  this.isShowingPlaceholder = false;
2971
+ this.lastAdInsertionPoint = null;
2972
+ this.processedAdInsertionUpdatedAt = null;
2971
2973
  this.totalAdRequestsInBreak = 0;
2972
2974
  this.maxTotalAdRequestsPerBreak = 20;
2973
2975
  this.pendingAdBreak = null;
@@ -2985,6 +2987,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2985
2987
  this.preloadedTokens = [];
2986
2988
  this.debugLogEntries = [];
2987
2989
  this.scteMarkerHistory = [];
2990
+ this.adInsertionDebugHistory = [];
2988
2991
  initializePolyfills();
2989
2992
  var browserOverrides = getBrowserConfigOverrides();
2990
2993
  this.config = _object_spread({}, browserOverrides, config);
@@ -3169,6 +3172,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3169
3172
  _state.sent();
3170
3173
  _state.label = 2;
3171
3174
  case 2:
3175
+ if (!this.config.disableAds && this.config.projectId) {
3176
+ this.startAdInsertionPolling();
3177
+ }
3172
3178
  return [
3173
3179
  2
3174
3180
  ];
@@ -3176,78 +3182,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3176
3182
  });
3177
3183
  }).call(_this);
3178
3184
  });
3179
- this.hls.on(import_hls.default.Events.LEVEL_LOADED, function(_evt, data) {
3185
+ this.hls.on(import_hls.default.Events.LEVEL_LOADED, function() {
3180
3186
  if (_this.inAdBreak || _this.pendingAdBreak) {
3181
3187
  return;
3182
3188
  }
3183
- var details = data === null || data === void 0 ? void 0 : data.details;
3184
- if (!details || !details.fragments || details.fragments.length === 0) {
3185
- return;
3186
- }
3187
- var fragmentsToScan = Math.min(5, details.fragments.length);
3188
- for(var i = 0; i < fragmentsToScan; i++){
3189
- var frag = details.fragments[i];
3190
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3191
- if (!Array.isArray(tagList)) continue;
3192
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3193
- try {
3194
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3195
- var entry = _step.value;
3196
- var tag = "";
3197
- var value = "";
3198
- if (Array.isArray(entry)) {
3199
- var _entry_, _entry_1;
3200
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3201
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3202
- } else if (typeof entry === "string") {
3203
- var idx = entry.indexOf(":");
3204
- if (idx >= 0) {
3205
- tag = entry.substring(0, idx);
3206
- value = entry.substring(idx + 1);
3207
- } else {
3208
- tag = entry;
3209
- }
3210
- }
3211
- if (!tag) continue;
3212
- if (tag.includes("EXT-X-CUE-OUT") || tag.includes("EXT-X-DATERANGE")) {
3213
- var attrs = tag.includes("EXT-X-DATERANGE") ? _this.parseAttributeList(value) : {};
3214
- var hasScteOut = tag.includes("EXT-X-CUE-OUT") || "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3215
- if (hasScteOut) {
3216
- var durationSeconds = _this.parseCueOutDuration(value);
3217
- var marker = _object_spread_props(_object_spread({
3218
- type: "start"
3219
- }, durationSeconds !== void 0 ? {
3220
- durationSeconds: durationSeconds
3221
- } : {}), {
3222
- raw: {
3223
- tag: tag,
3224
- value: value,
3225
- earlyDetection: true
3226
- }
3227
- });
3228
- if (_this.config.debugAdTiming) {
3229
- console.log("[StormcloudVideoPlayer] \uD83C\uDFAF EARLY SCTE-35 DETECTION: Ad break marker found in fragment", i, "- starting pre-fetch (NOT playing yet)");
3230
- }
3231
- _this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
3232
- return;
3233
- }
3234
- }
3235
- }
3236
- } catch (err) {
3237
- _didIteratorError = true;
3238
- _iteratorError = err;
3239
- } finally{
3240
- try {
3241
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3242
- _iterator.return();
3243
- }
3244
- } finally{
3245
- if (_didIteratorError) {
3246
- throw _iteratorError;
3247
- }
3248
- }
3249
- }
3250
- }
3189
+ _this.checkAdInsertionInManifest();
3251
3190
  });
3252
3191
  this.hls.on(import_hls.default.Events.FRAG_BUFFERED, function(_evt, data) {
3253
3192
  return _async_to_generator(function() {
@@ -3309,124 +3248,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3309
3248
  });
3310
3249
  }).call(_this);
3311
3250
  });
3312
- this.hls.on(import_hls.default.Events.FRAG_PARSING_METADATA, function(_evt, data) {
3313
- var id3Tags = ((data === null || data === void 0 ? void 0 : data.samples) || []).map(function(s) {
3314
- return {
3315
- key: "ID3",
3316
- value: s === null || s === void 0 ? void 0 : s.data,
3317
- ptsSeconds: s === null || s === void 0 ? void 0 : s.pts
3318
- };
3319
- });
3320
- id3Tags.forEach(function(tag) {
3321
- return _this.onId3Tag(tag);
3322
- });
3323
- });
3324
3251
  this.hls.on(import_hls.default.Events.FRAG_CHANGED, function(_evt, data) {
3325
3252
  var frag = data === null || data === void 0 ? void 0 : data.frag;
3326
- var tagList = frag === null || frag === void 0 ? void 0 : frag.tagList;
3327
- if (!Array.isArray(tagList)) return;
3328
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3329
- try {
3330
- for(var _iterator = tagList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3331
- var entry = _step.value;
3332
- var tag = "";
3333
- var value = "";
3334
- if (Array.isArray(entry)) {
3335
- var _entry_, _entry_1;
3336
- tag = String((_entry_ = entry[0]) !== null && _entry_ !== void 0 ? _entry_ : "");
3337
- value = String((_entry_1 = entry[1]) !== null && _entry_1 !== void 0 ? _entry_1 : "");
3338
- } else if (typeof entry === "string") {
3339
- var idx = entry.indexOf(":");
3340
- if (idx >= 0) {
3341
- tag = entry.substring(0, idx);
3342
- value = entry.substring(idx + 1);
3343
- } else {
3344
- tag = entry;
3345
- value = "";
3346
- }
3253
+ if (!frag) return;
3254
+ if (_this.lastAdInsertionPoint && !_this.inAdBreak && _this.lastAdInsertionPoint.updated_at !== _this.processedAdInsertionUpdatedAt) {
3255
+ var segmentName = _this.lastAdInsertionPoint.segment_ts_name;
3256
+ if (_this.fragmentMatchesSegment(frag, segmentName)) {
3257
+ var _ref;
3258
+ _this.processedAdInsertionUpdatedAt = _this.lastAdInsertionPoint.updated_at;
3259
+ var offsetMs = (_this.lastAdInsertionPoint.offset_seconds || 0) * 1e3;
3260
+ _this.pushAdInsertionDebug("segment_playing", segmentName, {
3261
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3262
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
3263
+ });
3264
+ if (_this.config.debugAdTiming) {
3265
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" now playing — scheduling ad start in ').concat(offsetMs, "ms"));
3347
3266
  }
3348
- if (!tag) continue;
3349
- if (tag.includes("EXT-X-CUE-OUT-CONT")) {
3350
- var prog = _this.parseCueOutCont(value);
3351
- var marker = _object_spread_props(_object_spread({
3352
- type: "progress"
3353
- }, (prog === null || prog === void 0 ? void 0 : prog.duration) !== void 0 ? {
3354
- durationSeconds: prog.duration
3355
- } : {}, (prog === null || prog === void 0 ? void 0 : prog.elapsed) !== void 0 ? {
3356
- ptsSeconds: prog.elapsed
3357
- } : {}), {
3358
- raw: {
3359
- tag: tag,
3360
- value: value
3361
- }
3362
- });
3363
- _this.onScte35Marker(marker);
3364
- } else if (tag.includes("EXT-X-CUE-OUT")) {
3365
- var durationSeconds = _this.parseCueOutDuration(value);
3366
- var marker1 = _object_spread_props(_object_spread({
3367
- type: "start"
3368
- }, durationSeconds !== void 0 ? {
3369
- durationSeconds: durationSeconds
3370
- } : {}), {
3371
- raw: {
3372
- tag: tag,
3373
- value: value
3374
- }
3267
+ _this.pushAdInsertionDebug("ad_scheduled", segmentName, {
3268
+ offsetSeconds: _this.lastAdInsertionPoint.offset_seconds,
3269
+ detail: "in ".concat(offsetMs, "ms, dur=60s")
3270
+ });
3271
+ _this.clearAdInsertionOffsetTimer();
3272
+ _this.adInsertionOffsetTimerId = window.setTimeout(function() {
3273
+ _this.adInsertionOffsetTimerId = void 0;
3274
+ if (_this.inAdBreak) return;
3275
+ _this.pushAdInsertionDebug("ad_triggered", segmentName, {
3276
+ detail: "ad break started (60s)"
3375
3277
  });
3376
- _this.onScte35Marker(marker1);
3377
- } else if (tag.includes("EXT-X-CUE-IN")) {
3378
- _this.onScte35Marker({
3379
- type: "end",
3278
+ var marker = {
3279
+ type: "start",
3280
+ durationSeconds: 60,
3380
3281
  raw: {
3381
- tag: tag,
3382
- value: value
3282
+ apiInsertionPoint: _this.lastAdInsertionPoint
3383
3283
  }
3384
- });
3385
- } else if (tag.includes("EXT-X-DATERANGE")) {
3386
- var _attrs_CLASS;
3387
- var attrs = _this.parseAttributeList(value);
3388
- var hasScteOut = "SCTE35-OUT" in attrs || attrs["SCTE35-OUT"] !== void 0;
3389
- var hasScteIn = "SCTE35-IN" in attrs || attrs["SCTE35-IN"] !== void 0;
3390
- var klass = String((_attrs_CLASS = attrs["CLASS"]) !== null && _attrs_CLASS !== void 0 ? _attrs_CLASS : "");
3391
- var duration = _this.toNumber(attrs["DURATION"]);
3392
- if (hasScteOut || /com\.apple\.hls\.cue/i.test(klass)) {
3393
- var marker2 = _object_spread_props(_object_spread({
3394
- type: "start"
3395
- }, duration !== void 0 ? {
3396
- durationSeconds: duration
3397
- } : {}), {
3398
- raw: {
3399
- tag: tag,
3400
- value: value,
3401
- attrs: attrs
3402
- }
3403
- });
3404
- _this.onScte35Marker(marker2);
3405
- }
3406
- if (hasScteIn) {
3407
- _this.onScte35Marker({
3408
- type: "end",
3409
- raw: {
3410
- tag: tag,
3411
- value: value,
3412
- attrs: attrs
3413
- }
3414
- });
3415
- }
3416
- }
3417
- }
3418
- } catch (err) {
3419
- _didIteratorError = true;
3420
- _iteratorError = err;
3421
- } finally{
3422
- try {
3423
- if (!_iteratorNormalCompletion && _iterator.return != null) {
3424
- _iterator.return();
3425
- }
3426
- } finally{
3427
- if (_didIteratorError) {
3428
- throw _iteratorError;
3429
- }
3284
+ };
3285
+ _this.onScte35Marker(marker);
3286
+ }, offsetMs);
3430
3287
  }
3431
3288
  }
3432
3289
  });
@@ -4626,6 +4483,182 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4626
4483
  this.preloadedTokens = [];
4627
4484
  }
4628
4485
  },
4486
+ {
4487
+ key: "startAdInsertionPolling",
4488
+ value: function startAdInsertionPolling() {
4489
+ var _this = this;
4490
+ if (this.adInsertionPollingId != null) return;
4491
+ this.fetchAdInsertionPoint();
4492
+ this.adInsertionPollingId = window.setInterval(function() {
4493
+ _this.fetchAdInsertionPoint();
4494
+ }, 1e3);
4495
+ }
4496
+ },
4497
+ {
4498
+ key: "stopAdInsertionPolling",
4499
+ value: function stopAdInsertionPolling() {
4500
+ if (this.adInsertionPollingId != null) {
4501
+ clearInterval(this.adInsertionPollingId);
4502
+ this.adInsertionPollingId = void 0;
4503
+ }
4504
+ }
4505
+ },
4506
+ {
4507
+ key: "fetchAdInsertionPoint",
4508
+ value: function fetchAdInsertionPoint() {
4509
+ return _async_to_generator(function() {
4510
+ var _this_lastAdInsertionPoint, resp, data, isNew, unused;
4511
+ return _ts_generator(this, function(_state) {
4512
+ switch(_state.label){
4513
+ case 0:
4514
+ if (!this.config.projectId) return [
4515
+ 2
4516
+ ];
4517
+ _state.label = 1;
4518
+ case 1:
4519
+ _state.trys.push([
4520
+ 1,
4521
+ 4,
4522
+ ,
4523
+ 5
4524
+ ]);
4525
+ return [
4526
+ 4,
4527
+ fetch("https://adstorm.co/api-adstorm-dev/adstorm/swirl/projects/".concat(encodeURIComponent(this.config.projectId), "/ad-insertion-point"))
4528
+ ];
4529
+ case 2:
4530
+ resp = _state.sent();
4531
+ if (!resp.ok) return [
4532
+ 2
4533
+ ];
4534
+ return [
4535
+ 4,
4536
+ resp.json()
4537
+ ];
4538
+ case 3:
4539
+ data = _state.sent();
4540
+ isNew = data.updated_at !== ((_this_lastAdInsertionPoint = this.lastAdInsertionPoint) === null || _this_lastAdInsertionPoint === void 0 ? void 0 : _this_lastAdInsertionPoint.updated_at);
4541
+ this.lastAdInsertionPoint = data;
4542
+ if (isNew) {
4543
+ this.pushAdInsertionDebug("api_response", data.segment_ts_name, {
4544
+ offsetSeconds: data.offset_seconds,
4545
+ updatedAt: data.updated_at,
4546
+ detail: "project=".concat(data.project_id)
4547
+ });
4548
+ }
4549
+ if (this.config.debugAdTiming) {
4550
+ console.log("[StormcloudVideoPlayer] Ad insertion point API response:", data);
4551
+ }
4552
+ return [
4553
+ 3,
4554
+ 5
4555
+ ];
4556
+ case 4:
4557
+ unused = _state.sent();
4558
+ if (this.config.debugAdTiming) {
4559
+ console.warn("[StormcloudVideoPlayer] Ad insertion point API fetch failed");
4560
+ }
4561
+ return [
4562
+ 3,
4563
+ 5
4564
+ ];
4565
+ case 5:
4566
+ return [
4567
+ 2
4568
+ ];
4569
+ }
4570
+ });
4571
+ }).call(this);
4572
+ }
4573
+ },
4574
+ {
4575
+ key: "checkAdInsertionInManifest",
4576
+ value: function checkAdInsertionInManifest() {
4577
+ var _this_hls;
4578
+ if (!this.lastAdInsertionPoint) return;
4579
+ if (this.inAdBreak || this.pendingAdBreak) return;
4580
+ if (this.lastAdInsertionPoint.updated_at === this.processedAdInsertionUpdatedAt) return;
4581
+ var segmentName = this.lastAdInsertionPoint.segment_ts_name;
4582
+ var levels = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.levels;
4583
+ if (!levels) return;
4584
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4585
+ try {
4586
+ for(var _iterator = levels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4587
+ var level = _step.value;
4588
+ var _level_details;
4589
+ var fragments = (_level_details = level.details) === null || _level_details === void 0 ? void 0 : _level_details.fragments;
4590
+ if (!Array.isArray(fragments)) continue;
4591
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
4592
+ try {
4593
+ for(var _iterator1 = fragments[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
4594
+ var frag = _step1.value;
4595
+ if (this.fragmentMatchesSegment(frag, segmentName)) {
4596
+ var _ref;
4597
+ this.pushAdInsertionDebug("segment_found", segmentName, {
4598
+ detail: "sn=".concat((_ref = frag === null || frag === void 0 ? void 0 : frag.sn) !== null && _ref !== void 0 ? _ref : "?")
4599
+ });
4600
+ if (this.config.debugAdTiming) {
4601
+ console.log('[StormcloudVideoPlayer] Ad insertion segment "'.concat(segmentName, '" found in manifest — starting pre-fetch'));
4602
+ }
4603
+ var marker = {
4604
+ type: "start",
4605
+ durationSeconds: 60,
4606
+ raw: {
4607
+ apiInsertionPoint: this.lastAdInsertionPoint,
4608
+ earlyDetection: true
4609
+ }
4610
+ };
4611
+ this.startAdPrefetch(marker, frag === null || frag === void 0 ? void 0 : frag.sn);
4612
+ return;
4613
+ }
4614
+ }
4615
+ } catch (err) {
4616
+ _didIteratorError1 = true;
4617
+ _iteratorError1 = err;
4618
+ } finally{
4619
+ try {
4620
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
4621
+ _iterator1.return();
4622
+ }
4623
+ } finally{
4624
+ if (_didIteratorError1) {
4625
+ throw _iteratorError1;
4626
+ }
4627
+ }
4628
+ }
4629
+ }
4630
+ } catch (err) {
4631
+ _didIteratorError = true;
4632
+ _iteratorError = err;
4633
+ } finally{
4634
+ try {
4635
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4636
+ _iterator.return();
4637
+ }
4638
+ } finally{
4639
+ if (_didIteratorError) {
4640
+ throw _iteratorError;
4641
+ }
4642
+ }
4643
+ }
4644
+ }
4645
+ },
4646
+ {
4647
+ key: "fragmentMatchesSegment",
4648
+ value: function fragmentMatchesSegment(frag, segmentName) {
4649
+ var url = (frag === null || frag === void 0 ? void 0 : frag.url) || (frag === null || frag === void 0 ? void 0 : frag.relurl) || "";
4650
+ return url.endsWith(segmentName) || url.includes("/" + segmentName);
4651
+ }
4652
+ },
4653
+ {
4654
+ key: "clearAdInsertionOffsetTimer",
4655
+ value: function clearAdInsertionOffsetTimer() {
4656
+ if (this.adInsertionOffsetTimerId != null) {
4657
+ clearTimeout(this.adInsertionOffsetTimerId);
4658
+ this.adInsertionOffsetTimerId = void 0;
4659
+ }
4660
+ }
4661
+ },
4629
4662
  {
4630
4663
  key: "startContinuousFetchLoop",
4631
4664
  value: function startContinuousFetchLoop() {
@@ -5547,6 +5580,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5547
5580
  }
5548
5581
  this.clearAdRequestWatchdog();
5549
5582
  this.clearAdFailsafeTimer();
5583
+ this.clearAdInsertionOffsetTimer();
5550
5584
  this.activeAdRequestToken = null;
5551
5585
  this.isInAdTransition = false;
5552
5586
  this.stopContinuousFetching();
@@ -5877,6 +5911,32 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5877
5911
  });
5878
5912
  }
5879
5913
  },
5914
+ {
5915
+ key: "pushAdInsertionDebug",
5916
+ value: function pushAdInsertionDebug(event, segmentName, opts) {
5917
+ if (!this.config.debugAdTiming) return;
5918
+ this.adInsertionDebugHistory.push(_object_spread({
5919
+ timestampMs: Date.now(),
5920
+ event: event,
5921
+ segmentName: segmentName
5922
+ }, (opts === null || opts === void 0 ? void 0 : opts.offsetSeconds) !== void 0 ? {
5923
+ offsetSeconds: opts.offsetSeconds
5924
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.updatedAt) ? {
5925
+ updatedAt: opts.updatedAt
5926
+ } : {}, (opts === null || opts === void 0 ? void 0 : opts.detail) ? {
5927
+ detail: opts.detail
5928
+ } : {}));
5929
+ if (this.adInsertionDebugHistory.length > DEBUG_HISTORY_LIMIT) {
5930
+ this.adInsertionDebugHistory = this.adInsertionDebugHistory.slice(-DEBUG_HISTORY_LIMIT);
5931
+ }
5932
+ }
5933
+ },
5934
+ {
5935
+ key: "getAdInsertionDebugLog",
5936
+ value: function getAdInsertionDebugLog() {
5937
+ return this.adInsertionDebugHistory.slice();
5938
+ }
5939
+ },
5880
5940
  {
5881
5941
  key: "getDebugLogs",
5882
5942
  value: function getDebugLogs() {
@@ -6143,6 +6203,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6143
6203
  key: "destroy",
6144
6204
  value: function destroy() {
6145
6205
  var _this_hls, _this_adLayer;
6206
+ this.stopAdInsertionPolling();
6207
+ this.clearAdInsertionOffsetTimer();
6146
6208
  this.stopContinuousFetching();
6147
6209
  this.stopFillerBreakTimer();
6148
6210
  this.clearAdStartTimer();
@@ -6174,6 +6236,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6174
6236
  this.consecutiveFailures = 0;
6175
6237
  this.debugLogEntries = [];
6176
6238
  this.scteMarkerHistory = [];
6239
+ this.adInsertionDebugHistory = [];
6177
6240
  }
6178
6241
  }
6179
6242
  ]);
@@ -7231,10 +7294,13 @@ function hexToRgb(hex) {
7231
7294
  var num = parseInt(clean.length === 3 ? clean.replace(/./g, "$&$&") : clean, 16);
7232
7295
  return "".concat(num >> 16 & 255, ",").concat(num >> 8 & 255, ",").concat(num & 255);
7233
7296
  }
7297
+ var FADE_DURATION_MS = 1e3;
7234
7298
  var OverlayRenderer = function OverlayRenderer(param) {
7235
7299
  var overlays = param.overlays, currentTime = param.currentTime, videoRef = param.videoRef, coordinateSpace = param.coordinateSpace;
7236
7300
  var _ref = _sliced_to_array((0, import_react.useState)(null), 2), dims = _ref[0], setDims = _ref[1];
7237
7301
  var rafRef = (0, import_react.useRef)(null);
7302
+ var _ref1 = _sliced_to_array((0, import_react.useState)(/* @__PURE__ */ new Map()), 2), fadeMap = _ref1[0], setFadeMap = _ref1[1];
7303
+ var removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());
7238
7304
  var updateDims = (0, import_react.useCallback)(function() {
7239
7305
  var video = videoRef.current;
7240
7306
  if (video) {
@@ -7265,10 +7331,181 @@ var OverlayRenderer = function OverlayRenderer(param) {
7265
7331
  }, [
7266
7332
  updateDims
7267
7333
  ]);
7268
- var activeOverlays = overlays.filter(function(o) {
7269
- return isOverlayActive(o, currentTime);
7270
- });
7271
- if (!dims || activeOverlays.length === 0) return null;
7334
+ var activeOverlays = (0, import_react.useMemo)(function() {
7335
+ return overlays.filter(function(o) {
7336
+ return isOverlayActive(o, currentTime);
7337
+ });
7338
+ }, [
7339
+ overlays,
7340
+ currentTime
7341
+ ]);
7342
+ (0, import_react.useEffect)(function() {
7343
+ var activeIds = new Set(activeOverlays.map(function(o) {
7344
+ return o.id;
7345
+ }));
7346
+ setFadeMap(function(prev) {
7347
+ var next = new Map(prev);
7348
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7349
+ try {
7350
+ for(var _iterator = activeOverlays[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7351
+ var overlay = _step.value;
7352
+ if (!next.has(overlay.id)) {
7353
+ next.set(overlay.id, {
7354
+ overlay: overlay,
7355
+ visible: false
7356
+ });
7357
+ } else {
7358
+ var existing = next.get(overlay.id);
7359
+ next.set(overlay.id, _object_spread_props(_object_spread({}, existing), {
7360
+ overlay: overlay
7361
+ }));
7362
+ }
7363
+ }
7364
+ } catch (err) {
7365
+ _didIteratorError = true;
7366
+ _iteratorError = err;
7367
+ } finally{
7368
+ try {
7369
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7370
+ _iterator.return();
7371
+ }
7372
+ } finally{
7373
+ if (_didIteratorError) {
7374
+ throw _iteratorError;
7375
+ }
7376
+ }
7377
+ }
7378
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
7379
+ try {
7380
+ var _loop = function() {
7381
+ var _step_value = _sliced_to_array(_step1.value, 2), id = _step_value[0], state = _step_value[1];
7382
+ if (!activeIds.has(id) && state.visible) {
7383
+ next.set(id, _object_spread_props(_object_spread({}, state), {
7384
+ visible: false
7385
+ }));
7386
+ if (!removeTimers.current.has(id)) {
7387
+ var timer = setTimeout(function() {
7388
+ setFadeMap(function(m) {
7389
+ var updated = new Map(m);
7390
+ updated.delete(id);
7391
+ return updated;
7392
+ });
7393
+ removeTimers.current.delete(id);
7394
+ }, FADE_DURATION_MS);
7395
+ removeTimers.current.set(id, timer);
7396
+ }
7397
+ } else if (!activeIds.has(id) && !state.visible) {}
7398
+ };
7399
+ for(var _iterator1 = next[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true)_loop();
7400
+ } catch (err) {
7401
+ _didIteratorError1 = true;
7402
+ _iteratorError1 = err;
7403
+ } finally{
7404
+ try {
7405
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
7406
+ _iterator1.return();
7407
+ }
7408
+ } finally{
7409
+ if (_didIteratorError1) {
7410
+ throw _iteratorError1;
7411
+ }
7412
+ }
7413
+ }
7414
+ return next;
7415
+ });
7416
+ }, [
7417
+ activeOverlays
7418
+ ]);
7419
+ (0, import_react.useEffect)(function() {
7420
+ var toFadeIn = [];
7421
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7422
+ try {
7423
+ var _loop = function() {
7424
+ var _step_value = _sliced_to_array(_step.value, 2), id = _step_value[0], state = _step_value[1];
7425
+ if (!state.visible) {
7426
+ var isActive = activeOverlays.some(function(o) {
7427
+ return o.id === id;
7428
+ });
7429
+ if (isActive) toFadeIn.push(id);
7430
+ }
7431
+ };
7432
+ for(var _iterator = fadeMap[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
7433
+ } catch (err) {
7434
+ _didIteratorError = true;
7435
+ _iteratorError = err;
7436
+ } finally{
7437
+ try {
7438
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7439
+ _iterator.return();
7440
+ }
7441
+ } finally{
7442
+ if (_didIteratorError) {
7443
+ throw _iteratorError;
7444
+ }
7445
+ }
7446
+ }
7447
+ if (toFadeIn.length === 0) return;
7448
+ var raf = requestAnimationFrame(function() {
7449
+ setFadeMap(function(prev) {
7450
+ var next = new Map(prev);
7451
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7452
+ try {
7453
+ for(var _iterator = toFadeIn[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7454
+ var id = _step.value;
7455
+ var state = next.get(id);
7456
+ if (state) next.set(id, _object_spread_props(_object_spread({}, state), {
7457
+ visible: true
7458
+ }));
7459
+ }
7460
+ } catch (err) {
7461
+ _didIteratorError = true;
7462
+ _iteratorError = err;
7463
+ } finally{
7464
+ try {
7465
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7466
+ _iterator.return();
7467
+ }
7468
+ } finally{
7469
+ if (_didIteratorError) {
7470
+ throw _iteratorError;
7471
+ }
7472
+ }
7473
+ }
7474
+ return next;
7475
+ });
7476
+ });
7477
+ return function() {
7478
+ return cancelAnimationFrame(raf);
7479
+ };
7480
+ }, [
7481
+ fadeMap,
7482
+ activeOverlays
7483
+ ]);
7484
+ (0, import_react.useEffect)(function() {
7485
+ return function() {
7486
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7487
+ try {
7488
+ for(var _iterator = removeTimers.current.values()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
7489
+ var timer = _step.value;
7490
+ clearTimeout(timer);
7491
+ }
7492
+ } catch (err) {
7493
+ _didIteratorError = true;
7494
+ _iteratorError = err;
7495
+ } finally{
7496
+ try {
7497
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
7498
+ _iterator.return();
7499
+ }
7500
+ } finally{
7501
+ if (_didIteratorError) {
7502
+ throw _iteratorError;
7503
+ }
7504
+ }
7505
+ }
7506
+ };
7507
+ }, []);
7508
+ if (!dims || fadeMap.size === 0) return null;
7272
7509
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7273
7510
  "aria-hidden": "true",
7274
7511
  style: {
@@ -7281,14 +7518,16 @@ var OverlayRenderer = function OverlayRenderer(param) {
7281
7518
  overflow: "hidden",
7282
7519
  zIndex: 8
7283
7520
  },
7284
- children: activeOverlays.map(function(overlay) {
7521
+ children: _to_consumable_array(fadeMap.values()).map(function(param) {
7522
+ var overlay = param.overlay, visible = param.visible;
7285
7523
  var scaleX = (coordinateSpace === null || coordinateSpace === void 0 ? void 0 : coordinateSpace.width) ? dims.displayWidth / coordinateSpace.width : dims.scaleX;
7286
7524
  var scaleY = (coordinateSpace === null || coordinateSpace === void 0 ? void 0 : coordinateSpace.height) ? dims.displayHeight / coordinateSpace.height : dims.scaleY;
7287
7525
  var left = overlay.x * scaleX;
7288
7526
  var top = overlay.y * scaleY;
7289
7527
  var width = overlay.width * scaleX;
7290
7528
  var height = overlay.height * scaleY;
7291
- var opacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;
7529
+ var baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;
7530
+ var opacity = visible ? baseOpacity : 0;
7292
7531
  var sz = {
7293
7532
  w: width,
7294
7533
  h: height
@@ -7301,6 +7540,7 @@ var OverlayRenderer = function OverlayRenderer(param) {
7301
7540
  width: "".concat(width, "px"),
7302
7541
  height: "".concat(height, "px"),
7303
7542
  opacity: opacity,
7543
+ transition: "opacity ".concat(FADE_DURATION_MS, "ms ease"),
7304
7544
  zIndex: overlay.z_index,
7305
7545
  overflow: "hidden"
7306
7546
  },
@@ -7373,7 +7613,7 @@ var AI_CONTEXT_MIN_POLL_MS = 800;
7373
7613
  var PANEL_BASE_RIGHT_OFFSET = 10;
7374
7614
  var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props) {
7375
7615
  var _ref;
7376
- var _aiLiveContext_context, _aiLiveContext_context_keywords, _aiLiveContext_context1;
7616
+ var _aiLiveContext_context;
7377
7617
  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, [
7378
7618
  "src",
7379
7619
  "autoplay",
@@ -7465,13 +7705,39 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7465
7705
  return "--:--:--";
7466
7706
  }
7467
7707
  };
7468
- var formatDebugRaw = function formatDebugRaw(raw) {
7469
- if (!raw || (typeof raw === "undefined" ? "undefined" : _type_of(raw)) !== "object") return "";
7470
- var obj = raw;
7471
- if (typeof obj.tag === "string") return obj.tag;
7472
- if (typeof obj.id3 === "string") return "ID3";
7473
- if (typeof obj.splice_command_type === "number") return "binary splice";
7474
- return "marker";
7708
+ var formatAdInsertionEvent = function formatAdInsertionEvent(event) {
7709
+ switch(event){
7710
+ case "api_response":
7711
+ return {
7712
+ label: "API",
7713
+ color: "#60a5fa"
7714
+ };
7715
+ case "segment_found":
7716
+ return {
7717
+ label: "FOUND",
7718
+ color: "#a78bfa"
7719
+ };
7720
+ case "segment_playing":
7721
+ return {
7722
+ label: "PLAYING",
7723
+ color: "#fbbf24"
7724
+ };
7725
+ case "ad_scheduled":
7726
+ return {
7727
+ label: "SCHED",
7728
+ color: "#fb923c"
7729
+ };
7730
+ case "ad_triggered":
7731
+ return {
7732
+ label: "TRIGGER",
7733
+ color: "#34d399"
7734
+ };
7735
+ default:
7736
+ return {
7737
+ label: event,
7738
+ color: "rgba(255,255,255,0.68)"
7739
+ };
7740
+ }
7475
7741
  };
7476
7742
  var formatAiRelativeTime = function formatAiRelativeTime(timestamp) {
7477
7743
  var epochMs = Date.parse(timestamp);
@@ -7884,7 +8150,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
7884
8150
  var updateDebugData = function updateDebugData() {
7885
8151
  var player = playerRef.current;
7886
8152
  if (!player) return;
7887
- setDebugMarkers(player.getRecentScteMarkers().slice(-DEBUG_PANEL_MARKER_LIMIT).reverse());
8153
+ setDebugMarkers(player.getAdInsertionDebugLog().slice(-DEBUG_PANEL_MARKER_LIMIT).reverse());
7888
8154
  };
7889
8155
  updateDebugData();
7890
8156
  var interval = window.setInterval(updateDebugData, 500);
@@ -8419,34 +8685,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8419
8685
  whiteSpace: "pre-wrap"
8420
8686
  },
8421
8687
  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."
8422
- }),
8423
- ((_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", {
8424
- style: {
8425
- marginTop: "10px",
8426
- display: "flex",
8427
- flexWrap: "wrap",
8428
- gap: "6px"
8429
- },
8430
- children: aiLiveContext.context.keywords.slice(0, 12).map(function(kw) {
8431
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8432
- style: {
8433
- fontSize: "10px",
8434
- fontWeight: 600,
8435
- padding: "4px 7px",
8436
- borderRadius: "999px",
8437
- background: "rgba(236, 72, 153, 0.2)",
8438
- border: "1px solid rgba(244, 114, 182, 0.42)",
8439
- color: "#fce7f3",
8440
- maxWidth: "100%",
8441
- overflow: "hidden",
8442
- textOverflow: "ellipsis",
8443
- whiteSpace: "nowrap"
8444
- },
8445
- title: kw,
8446
- children: kw
8447
- }, kw);
8448
- })
8449
- }) : null
8688
+ })
8450
8689
  ]
8451
8690
  })
8452
8691
  ]
@@ -8488,7 +8727,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8488
8727
  fontWeight: 700,
8489
8728
  letterSpacing: "0.02em"
8490
8729
  },
8491
- children: "Debug Ad Timing"
8730
+ children: "Ad Insertion Debug"
8492
8731
  }),
8493
8732
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", {
8494
8733
  className: "sc-ctrl-btn",
@@ -8529,7 +8768,7 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8529
8768
  color: "rgba(255,255,255,0.68)",
8530
8769
  marginBottom: "8px"
8531
8770
  },
8532
- children: "SCTE-35 markers"
8771
+ children: "Ad Insertion Points"
8533
8772
  }),
8534
8773
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {
8535
8774
  style: {
@@ -8541,12 +8780,13 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8541
8780
  fontSize: "12px",
8542
8781
  color: "rgba(255,255,255,0.55)"
8543
8782
  },
8544
- children: "No markers detected yet."
8783
+ children: "No ad insertion events yet."
8545
8784
  }) : debugMarkers.map(function(entry, idx) {
8785
+ var evt = formatAdInsertionEvent(entry.event);
8546
8786
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", {
8547
8787
  style: {
8548
8788
  display: "grid",
8549
- gridTemplateColumns: "56px 52px 1fr",
8789
+ gridTemplateColumns: "56px 54px 1fr",
8550
8790
  gap: "8px",
8551
8791
  alignItems: "center",
8552
8792
  fontFamily: "'SF Mono', 'Cascadia Code', monospace",
@@ -8565,11 +8805,11 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8565
8805
  }),
8566
8806
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8567
8807
  style: {
8568
- textTransform: "uppercase",
8569
8808
  fontWeight: 700,
8570
- color: entry.type === "start" ? "#34d399" : entry.type === "end" ? "#f87171" : "#fbbf24"
8809
+ color: evt.color,
8810
+ fontSize: "10px"
8571
8811
  },
8572
- children: entry.type
8812
+ children: evt.label
8573
8813
  }),
8574
8814
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {
8575
8815
  style: {
@@ -8585,11 +8825,9 @@ var StormcloudVideoPlayerComponent = import_react2.default.memo(function(props)
8585
8825
  whiteSpace: "nowrap"
8586
8826
  },
8587
8827
  children: [
8588
- entry.durationSeconds != null ? "dur:".concat(entry.durationSeconds.toFixed(2), "s") : "dur:-",
8589
- " ",
8590
- entry.ptsSeconds != null ? "pts:".concat(entry.ptsSeconds.toFixed(2)) : "pts:-",
8591
- " ",
8592
- formatDebugRaw(entry.raw)
8828
+ entry.segmentName,
8829
+ entry.offsetSeconds != null ? " +".concat(entry.offsetSeconds, "s") : "",
8830
+ entry.detail ? " (".concat(entry.detail, ")") : ""
8593
8831
  ]
8594
8832
  })
8595
8833
  })