stormcloud-video-player 0.6.12 → 0.6.14

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.
@@ -2096,24 +2096,43 @@ function createVastAdLayer(contentVideo, options) {
2096
2096
  }
2097
2097
  };
2098
2098
  }
2099
+ // src/utils/mqttConfig.ts
2100
+ var DEFAULT_MQTT_CONFIG = {
2101
+ enabled: true,
2102
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2103
+ brokerPort: 8883,
2104
+ wsPort: 8084,
2105
+ username: "for-sonifi",
2106
+ password: "sonifi-mqtt",
2107
+ topicPrefix: "adstorm/players",
2108
+ qos: 1
2109
+ };
2110
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2111
+ function isMQTTEnabled() {
2112
+ return mqttConfig.enabled;
2113
+ }
2114
+ function buildMQTTBrokerUrl() {
2115
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2116
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2117
+ }
2118
+ function buildPlayerTopic(licenseKey, channel) {
2119
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2120
+ }
2099
2121
  // src/utils/mqttClient.ts
2100
2122
  var import_mqtt = __toESM(require("mqtt"), 1);
2101
2123
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2102
2124
  var client = null;
2103
2125
  var status = "disconnected";
2104
- var brokerUrl = "";
2105
- function isMQTTConfigured() {
2106
- return client !== null;
2107
- }
2108
- function initMQTTClient(url) {
2109
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2110
- if (client) return;
2111
- brokerUrl = url;
2126
+ function initMQTTClient() {
2127
+ if (client || !isMQTTEnabled()) return;
2128
+ var url = buildMQTTBrokerUrl();
2112
2129
  status = "connecting";
2113
2130
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2114
2131
  try {
2115
2132
  client = import_mqtt.default.connect(url, {
2116
2133
  clientId: clientId,
2134
+ username: mqttConfig.username,
2135
+ password: mqttConfig.password,
2117
2136
  keepalive: 60,
2118
2137
  clean: true,
2119
2138
  reconnectPeriod: 5e3,
@@ -2147,13 +2166,22 @@ function initMQTTClient(url) {
2147
2166
  }
2148
2167
  });
2149
2168
  }
2169
+ function ensureMQTTClient() {
2170
+ if (isMQTTEnabled() && !client) {
2171
+ initMQTTClient();
2172
+ }
2173
+ }
2150
2174
  function publishMQTT(topic, payload) {
2175
+ if (!isMQTTEnabled()) {
2176
+ return false;
2177
+ }
2178
+ ensureMQTTClient();
2151
2179
  if (!client) {
2152
2180
  return false;
2153
2181
  }
2154
2182
  try {
2155
2183
  client.publish(topic, JSON.stringify(payload), {
2156
- qos: 1
2184
+ qos: mqttConfig.qos
2157
2185
  });
2158
2186
  return true;
2159
2187
  } catch (err) {
@@ -2166,7 +2194,6 @@ function disconnectMQTT() {
2166
2194
  client.end(true);
2167
2195
  client = null;
2168
2196
  status = "disconnected";
2169
- brokerUrl = "";
2170
2197
  }
2171
2198
  }
2172
2199
  // src/utils/tracking.ts
@@ -2388,171 +2415,91 @@ function getBrowserID(clientInfo) {
2388
2415
  });
2389
2416
  })();
2390
2417
  }
2391
- var mqttTopicPrefix = "adstorm";
2392
- function setMQTTTopicPrefix(prefix) {
2393
- mqttTopicPrefix = prefix || "adstorm";
2394
- }
2395
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2396
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2397
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2398
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2399
- function buildHeaders(licenseKey) {
2400
- var headers = {
2401
- "Content-Type": "application/json"
2402
- };
2403
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2404
- return headers;
2418
+ function canPublish(licenseKey) {
2419
+ return Boolean(isMQTTEnabled() && licenseKey);
2405
2420
  }
2406
- function postJson(url, licenseKey, body) {
2421
+ function buildPlayerMetricEvent() {
2407
2422
  return _async_to_generator(function() {
2408
- var response;
2409
- return _ts_generator(this, function(_state) {
2410
- switch(_state.label){
2411
- case 0:
2412
- return [
2413
- 4,
2414
- fetch(url, {
2415
- method: "POST",
2416
- headers: buildHeaders(licenseKey),
2417
- body: JSON.stringify(body)
2418
- })
2419
- ];
2420
- case 1:
2421
- response = _state.sent();
2422
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2423
- return [
2424
- 4,
2425
- response.json()
2426
- ];
2427
- case 2:
2428
- _state.sent();
2429
- return [
2430
- 2
2431
- ];
2432
- }
2433
- });
2434
- })();
2435
- }
2436
- function buildPlayerMetricEvent(_0) {
2437
- return _async_to_generator(function(licenseKey) {
2438
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2423
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2439
2424
  var _arguments = arguments;
2440
2425
  return _ts_generator(this, function(_state) {
2441
2426
  switch(_state.label){
2442
2427
  case 0:
2443
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2428
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2444
2429
  clientInfo = getClientInfo();
2445
2430
  return [
2446
2431
  4,
2447
2432
  getBrowserID(clientInfo)
2448
2433
  ];
2449
2434
  case 1:
2450
- browserId = _state.sent();
2435
+ playerId = _state.sent();
2451
2436
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2452
2437
  return [
2453
2438
  2,
2454
- {
2455
- player_id: browserId,
2456
- browserId: browserId,
2439
+ _object_spread({
2440
+ player_id: playerId,
2457
2441
  device_type: clientInfo.deviceType,
2458
- deviceType: clientInfo.deviceType,
2459
- input_stream_type: context.inputStreamType,
2460
- os: clientInfo.os,
2461
- ad_loaded: flags.adLoaded,
2462
- ad_detect: flags.adDetect,
2463
- license_key: licenseKey,
2464
- capture_at: captureAt,
2465
- timestamp: captureAt
2466
- }
2442
+ os: clientInfo.os.toLowerCase(),
2443
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
2444
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
2445
+ capture_at: captureAt
2446
+ }, context.inputStreamType ? {
2447
+ input_stream_type: context.inputStreamType
2448
+ } : {})
2467
2449
  ];
2468
2450
  }
2469
2451
  });
2470
2452
  }).apply(this, arguments);
2471
2453
  }
2472
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2473
- return _async_to_generator(function() {
2474
- return _ts_generator(this, function(_state) {
2475
- switch(_state.label){
2476
- case 0:
2477
- if (isMQTTConfigured()) {
2478
- publishMQTT(mqttTopic, body);
2479
- return [
2480
- 2
2481
- ];
2482
- }
2483
- return [
2484
- 4,
2485
- postJson(httpUrl, licenseKey, body)
2486
- ];
2487
- case 1:
2488
- _state.sent();
2489
- return [
2490
- 2
2491
- ];
2492
- }
2493
- });
2494
- })();
2454
+ function publishTracking(licenseKey, channel, body) {
2455
+ ensureMQTTClient();
2456
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2495
2457
  }
2496
2458
  function sendInitialTracking(_0) {
2497
2459
  return _async_to_generator(function(licenseKey) {
2498
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2460
+ var context, metricEvent, error;
2499
2461
  var _arguments = arguments;
2500
2462
  return _ts_generator(this, function(_state) {
2501
2463
  switch(_state.label){
2502
2464
  case 0:
2503
2465
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2466
+ if (!canPublish(licenseKey)) return [
2467
+ 2
2468
+ ];
2504
2469
  _state.label = 1;
2505
2470
  case 1:
2506
2471
  _state.trys.push([
2507
2472
  1,
2508
- 4,
2473
+ 3,
2509
2474
  ,
2510
- 5
2475
+ 4
2511
2476
  ]);
2512
- clientInfo = getClientInfo();
2513
2477
  return [
2514
2478
  4,
2515
- getBrowserID(clientInfo)
2479
+ buildPlayerMetricEvent(context, {
2480
+ adLoaded: false,
2481
+ adDetect: false
2482
+ })
2516
2483
  ];
2517
2484
  case 2:
2518
- browserId = _state.sent();
2519
- captureAt = /* @__PURE__ */ new Date().toISOString();
2520
- trackingData = _object_spread({
2521
- browserId: browserId
2522
- }, clientInfo);
2523
- metricsBody = {
2485
+ metricEvent = _state.sent();
2486
+ publishTracking(licenseKey, "metrics", {
2524
2487
  events: [
2525
- {
2526
- player_id: browserId,
2527
- device_type: clientInfo.deviceType,
2528
- input_stream_type: context.inputStreamType,
2529
- os: clientInfo.os,
2530
- ad_loaded: false,
2531
- ad_detect: false,
2532
- license_key: licenseKey,
2533
- capture_at: captureAt
2534
- }
2535
- ],
2536
- trackingData: trackingData
2537
- };
2538
- return [
2539
- 4,
2540
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2541
- ];
2542
- case 3:
2543
- _state.sent();
2488
+ metricEvent
2489
+ ]
2490
+ });
2544
2491
  return [
2545
2492
  3,
2546
- 5
2493
+ 4
2547
2494
  ];
2548
- case 4:
2495
+ case 3:
2549
2496
  error = _state.sent();
2550
2497
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2551
2498
  return [
2552
2499
  3,
2553
- 5
2500
+ 4
2554
2501
  ];
2555
- case 5:
2502
+ case 4:
2556
2503
  return [
2557
2504
  2
2558
2505
  ];
@@ -2650,61 +2597,54 @@ function sendAdLoadedTracking(_0, _1) {
2650
2597
  }
2651
2598
  function sendAdImpressionTracking(_0, _1) {
2652
2599
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2653
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2600
+ var context, metricEvent, error;
2654
2601
  var _arguments = arguments;
2655
2602
  return _ts_generator(this, function(_state) {
2656
2603
  switch(_state.label){
2657
2604
  case 0:
2658
2605
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2606
+ if (!canPublish(licenseKey)) return [
2607
+ 2
2608
+ ];
2659
2609
  _state.label = 1;
2660
2610
  case 1:
2661
2611
  _state.trys.push([
2662
2612
  1,
2663
- 4,
2613
+ 3,
2664
2614
  ,
2665
- 5
2615
+ 4
2666
2616
  ]);
2667
2617
  return [
2668
2618
  4,
2669
- buildPlayerMetricEvent(licenseKey, context, {
2619
+ buildPlayerMetricEvent(context, {
2670
2620
  captureAt: adImpressionInfo.timestamp
2671
2621
  })
2672
2622
  ];
2673
2623
  case 2:
2674
2624
  metricEvent = _state.sent();
2675
- heartbeatBody = metricEvent;
2676
- impressionsBody = {
2625
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2626
+ publishTracking(licenseKey, "impressions", {
2677
2627
  events: [
2678
2628
  {
2679
2629
  player_id: metricEvent.player_id,
2680
2630
  ad_played_count: 1,
2681
2631
  ad_url: adImpressionInfo.adUrl,
2682
- license_key: licenseKey,
2683
2632
  capture_at: adImpressionInfo.timestamp
2684
2633
  }
2685
2634
  ]
2686
- };
2687
- return [
2688
- 4,
2689
- Promise.all([
2690
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2691
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2692
- ])
2693
- ];
2694
- case 3:
2695
- _state.sent();
2635
+ });
2696
2636
  return [
2697
2637
  3,
2698
- 5
2638
+ 4
2699
2639
  ];
2700
- case 4:
2640
+ case 3:
2701
2641
  error = _state.sent();
2702
2642
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2703
2643
  return [
2704
2644
  3,
2705
- 5
2645
+ 4
2706
2646
  ];
2707
- case 5:
2647
+ case 4:
2708
2648
  return [
2709
2649
  2
2710
2650
  ];
@@ -2720,38 +2660,36 @@ function sendHeartbeat(_0) {
2720
2660
  switch(_state.label){
2721
2661
  case 0:
2722
2662
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2663
+ if (!canPublish(licenseKey)) return [
2664
+ 2
2665
+ ];
2723
2666
  _state.label = 1;
2724
2667
  case 1:
2725
2668
  _state.trys.push([
2726
2669
  1,
2727
- 4,
2670
+ 3,
2728
2671
  ,
2729
- 5
2672
+ 4
2730
2673
  ]);
2731
2674
  return [
2732
2675
  4,
2733
- buildPlayerMetricEvent(licenseKey, context, flags)
2676
+ buildPlayerMetricEvent(context, flags)
2734
2677
  ];
2735
2678
  case 2:
2736
2679
  heartbeatData = _state.sent();
2737
- return [
2738
- 4,
2739
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2740
- ];
2741
- case 3:
2742
- _state.sent();
2680
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2743
2681
  return [
2744
2682
  3,
2745
- 5
2683
+ 4
2746
2684
  ];
2747
- case 4:
2685
+ case 3:
2748
2686
  error = _state.sent();
2749
2687
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2750
2688
  return [
2751
2689
  3,
2752
- 5
2690
+ 4
2753
2691
  ];
2754
- case 5:
2692
+ case 4:
2755
2693
  return [
2756
2694
  2
2757
2695
  ];
@@ -3880,7 +3818,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3880
3818
  _this.adLayer.showPlaceholder();
3881
3819
  }
3882
3820
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
3883
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3821
+ _this.scheduleAdStopAtBreakBoundary();
3884
3822
  if (_this.config.debugAdTiming) {
3885
3823
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
3886
3824
  }
@@ -4314,12 +4252,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4314
4252
  if (marker.durationSeconds != null) {
4315
4253
  var newDurationMs = marker.durationSeconds * 1e3;
4316
4254
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4317
- this.expectedAdBreakDurationMs = newDurationMs;
4318
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4319
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4320
- this.scheduleAdStopCountdown(remainingMs);
4255
+ this.setAdBreakDurationBoundary(newDurationMs);
4256
+ this.scheduleAdStopAtBreakBoundary();
4321
4257
  if (this.config.debugAdTiming) {
4322
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4258
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4323
4259
  }
4324
4260
  }
4325
4261
  }
@@ -4327,8 +4263,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4327
4263
  }
4328
4264
  this.inAdBreak = true;
4329
4265
  var durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : ((_this_pendingAdBreak = this.pendingAdBreak) === null || _this_pendingAdBreak === void 0 ? void 0 : _this_pendingAdBreak.marker.durationSeconds) != null ? this.pendingAdBreak.marker.durationSeconds * 1e3 : void 0;
4330
- this.expectedAdBreakDurationMs = durationMs;
4331
- this.currentAdBreakStartWallClockMs = Date.now();
4266
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4267
+ this.setAdBreakDurationBoundary(durationMs);
4332
4268
  if (this.config.licenseKey) {
4333
4269
  var _this_pendingAdBreak1;
4334
4270
  var adDetectInfo = _object_spread({
@@ -4392,20 +4328,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4392
4328
  this.clearAdStartTimer();
4393
4329
  this.handleAdStart(marker);
4394
4330
  }
4395
- if (this.expectedAdBreakDurationMs != null) {
4396
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4397
- }
4331
+ this.scheduleAdStopAtBreakBoundary();
4398
4332
  return;
4399
4333
  }
4400
4334
  if (marker.type === "progress" && this.inAdBreak) {
4401
4335
  if (marker.durationSeconds != null) {
4402
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4403
- }
4404
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4405
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4406
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4407
- this.scheduleAdStopCountdown(remainingMs1);
4336
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4408
4337
  }
4338
+ this.scheduleAdStopAtBreakBoundary();
4409
4339
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4410
4340
  var bids = this.pendingNextAdBids;
4411
4341
  this.pendingNextAdBids = null;
@@ -4436,15 +4366,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4436
4366
  }
4437
4367
  return;
4438
4368
  }
4439
- if (adPlaying || remaining > 500) {
4440
- if (this.config.debugAdTiming) {
4441
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4442
- }
4443
- return;
4444
- }
4445
- this.inAdBreak = false;
4446
- this.expectedAdBreakDurationMs = void 0;
4447
- this.currentAdBreakStartWallClockMs = void 0;
4369
+ this.scteAdBreakEndWallClockMs = Date.now();
4448
4370
  this.clearAdStartTimer();
4449
4371
  this.clearAdStopTimer();
4450
4372
  if (adPlaying) {
@@ -4455,6 +4377,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4455
4377
  }
4456
4378
  }
4457
4379
  },
4380
+ {
4381
+ key: "resolveScteBreakStartWallClockMs",
4382
+ value: function resolveScteBreakStartWallClockMs(marker) {
4383
+ var nowWallClockMs = Date.now();
4384
+ if (typeof marker.ptsSeconds !== "number") {
4385
+ return nowWallClockMs;
4386
+ }
4387
+ var nowMs = this.video.currentTime * 1e3;
4388
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4389
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4390
+ if (!Number.isFinite(deltaMs)) {
4391
+ return nowWallClockMs;
4392
+ }
4393
+ return nowWallClockMs + Math.floor(deltaMs);
4394
+ }
4395
+ },
4396
+ {
4397
+ key: "setAdBreakDurationBoundary",
4398
+ value: function setAdBreakDurationBoundary(durationMs) {
4399
+ this.expectedAdBreakDurationMs = durationMs;
4400
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4401
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4402
+ } else {
4403
+ this.scteAdBreakEndWallClockMs = void 0;
4404
+ }
4405
+ }
4406
+ },
4407
+ {
4408
+ key: "scheduleAdStopAtBreakBoundary",
4409
+ value: function scheduleAdStopAtBreakBoundary() {
4410
+ if (this.expectedAdBreakDurationMs == null) {
4411
+ return;
4412
+ }
4413
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4414
+ }
4415
+ },
4458
4416
  {
4459
4417
  key: "parseCueOutDuration",
4460
4418
  value: function parseCueOutDuration(value) {
@@ -4665,10 +4623,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4665
4623
  key: "initializeTracking",
4666
4624
  value: function initializeTracking() {
4667
4625
  var _this = this;
4668
- if (this.config.mqttBrokerUrl) {
4669
- var topicPrefix = "adstorm";
4670
- setMQTTTopicPrefix(topicPrefix);
4671
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4626
+ if (isMQTTEnabled()) {
4627
+ initMQTTClient();
4672
4628
  }
4673
4629
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4674
4630
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5092,7 +5048,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5092
5048
  case 9:
5093
5049
  _state.sent();
5094
5050
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5095
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5051
+ _this.scheduleAdStopAtBreakBoundary();
5096
5052
  }
5097
5053
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5098
5054
  return [
@@ -5197,7 +5153,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5197
5153
  case 19:
5198
5154
  _state.sent();
5199
5155
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5200
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5156
+ _this.scheduleAdStopAtBreakBoundary();
5201
5157
  }
5202
5158
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5203
5159
  _state.label = 20;
@@ -5280,7 +5236,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5280
5236
  key: "handleAdStart",
5281
5237
  value: function handleAdStart(_marker) {
5282
5238
  return _async_to_generator(function() {
5283
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5239
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5284
5240
  return _ts_generator(this, function(_state) {
5285
5241
  switch(_state.label){
5286
5242
  case 0:
@@ -5314,20 +5270,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5314
5270
  }
5315
5271
  }
5316
5272
  this.inAdBreak = true;
5317
- this.currentAdBreakStartWallClockMs = Date.now();
5318
5273
  this.currentAdIndex = 0;
5319
5274
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5320
5275
  this.adPodQueue = [];
5321
5276
  if (!this.config.disableFiller) this.showAds = true;
5322
- if (adBreakDurationMs != null) {
5323
- this.startFillerBreakTimer(adBreakDurationMs);
5277
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5278
+ this.currentAdBreakStartWallClockMs = Date.now();
5279
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5280
+ }
5281
+ fillerRemainingMs = this.getRemainingAdMs();
5282
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5283
+ this.startFillerBreakTimer(fillerRemainingMs);
5324
5284
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5325
5285
  this.showPlaceholderLayer();
5326
5286
  }
5327
5287
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5328
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5329
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5330
- }
5331
5288
  this.clearPendingAdBreak();
5332
5289
  adBreakToken = Date.now();
5333
5290
  this.activeAdRequestToken = adBreakToken;
@@ -5364,7 +5321,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5364
5321
  case 2:
5365
5322
  _state.sent();
5366
5323
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5367
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5324
+ this.scheduleAdStopAtBreakBoundary();
5368
5325
  }
5369
5326
  this.adLayer.setAdVolume(adVolume);
5370
5327
  return [
@@ -5550,7 +5507,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5550
5507
  case 4:
5551
5508
  _state.sent();
5552
5509
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5553
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5510
+ this.scheduleAdStopAtBreakBoundary();
5554
5511
  }
5555
5512
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5556
5513
  _state.label = 5;
@@ -5792,21 +5749,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5792
5749
  {
5793
5750
  key: "ensureAdStoppedByTimer",
5794
5751
  value: function ensureAdStoppedByTimer() {
5795
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5752
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5796
5753
  if (!this.inAdBreak) return;
5797
5754
  this.adStopTimerId = void 0;
5798
5755
  var adPlaying = this.adLayer.isAdPlaying();
5799
- var pendingAds = this.adPodQueue.length > 0;
5800
5756
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5801
5757
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5802
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5803
- var elapsedSinceStartMs = 0;
5804
- if (this.currentAdBreakStartWallClockMs != null) {
5805
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5806
- }
5807
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5808
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5809
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5758
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5759
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5760
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5761
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5810
5762
  if (shouldExtendAdBreak) {
5811
5763
  this.scheduleAdStopCountdown(checkIntervalMs);
5812
5764
  return;
@@ -5880,6 +5832,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5880
5832
  this.inAdBreak = false;
5881
5833
  this.expectedAdBreakDurationMs = void 0;
5882
5834
  this.currentAdBreakStartWallClockMs = void 0;
5835
+ this.scteAdBreakEndWallClockMs = void 0;
5883
5836
  this.clearAdStartTimer();
5884
5837
  this.clearAdStopTimer();
5885
5838
  this.adPodQueue = [];
@@ -6141,10 +6094,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6141
6094
  {
6142
6095
  key: "getRemainingAdMs",
6143
6096
  value: function getRemainingAdMs() {
6097
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6098
+ if (endWallClockMs != null) {
6099
+ return Math.max(0, endWallClockMs - Date.now());
6100
+ }
6144
6101
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6145
6102
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6146
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6147
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6103
+ return 0;
6104
+ }
6105
+ },
6106
+ {
6107
+ key: "getAdBreakEndWallClockMs",
6108
+ value: function getAdBreakEndWallClockMs() {
6109
+ if (this.scteAdBreakEndWallClockMs != null) {
6110
+ return this.scteAdBreakEndWallClockMs;
6111
+ }
6112
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6113
+ return void 0;
6114
+ }
6115
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6148
6116
  }
6149
6117
  },
6150
6118
  {
@@ -6349,7 +6317,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6349
6317
  clearInterval(this.heartbeatInterval);
6350
6318
  this.heartbeatInterval = void 0;
6351
6319
  }
6352
- if (this.config.mqttBrokerUrl) {
6320
+ if (isMQTTEnabled()) {
6353
6321
  disconnectMQTT();
6354
6322
  }
6355
6323
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();