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.
package/lib/index.cjs CHANGED
@@ -428,6 +428,9 @@ var __toCommonJS = function __toCommonJS(mod) {
428
428
  // src/index.ts
429
429
  var index_exports = {};
430
430
  __export(index_exports, {
431
+ DEFAULT_MQTT_CONFIG: function DEFAULT_MQTT_CONFIG1() {
432
+ return DEFAULT_MQTT_CONFIG;
433
+ },
431
434
  IS_BROWSER: function IS_BROWSER1() {
432
435
  return IS_BROWSER;
433
436
  },
@@ -455,9 +458,21 @@ __export(index_exports, {
455
458
  StormcloudVideoPlayerComponent: function StormcloudVideoPlayerComponent1() {
456
459
  return StormcloudVideoPlayerComponent;
457
460
  },
461
+ applyMQTTConfig: function applyMQTTConfig1() {
462
+ return applyMQTTConfig;
463
+ },
464
+ buildMQTTBrokerUrl: function buildMQTTBrokerUrl1() {
465
+ return buildMQTTBrokerUrl;
466
+ },
467
+ buildPlayerTopic: function buildPlayerTopic1() {
468
+ return buildPlayerTopic;
469
+ },
458
470
  canPlay: function canPlay1() {
459
471
  return canPlay;
460
472
  },
473
+ configureMQTT: function configureMQTT1() {
474
+ return configureMQTT;
475
+ },
461
476
  createStormcloudPlayer: function createStormcloudPlayer1() {
462
477
  return createStormcloudPlayer;
463
478
  },
@@ -476,6 +491,9 @@ __export(index_exports, {
476
491
  disconnectMQTT: function disconnectMQTT1() {
477
492
  return disconnectMQTT;
478
493
  },
494
+ ensureMQTTClient: function ensureMQTTClient1() {
495
+ return ensureMQTTClient;
496
+ },
479
497
  getBrowserConfigOverrides: function getBrowserConfigOverrides1() {
480
498
  return getBrowserConfigOverrides;
481
499
  },
@@ -500,6 +518,9 @@ __export(index_exports, {
500
518
  isMQTTConnected: function isMQTTConnected1() {
501
519
  return isMQTTConnected;
502
520
  },
521
+ isMQTTEnabled: function isMQTTEnabled1() {
522
+ return isMQTTEnabled;
523
+ },
503
524
  isMediaStream: function isMediaStream1() {
504
525
  return isMediaStream;
505
526
  },
@@ -512,6 +533,9 @@ __export(index_exports, {
512
533
  merge: function merge1() {
513
534
  return merge;
514
535
  },
536
+ mqttConfig: function mqttConfig1() {
537
+ return mqttConfig;
538
+ },
515
539
  omit: function omit1() {
516
540
  return omit;
517
541
  },
@@ -2208,12 +2232,36 @@ function createVastAdLayer(contentVideo, options) {
2208
2232
  }
2209
2233
  };
2210
2234
  }
2235
+ // src/utils/mqttConfig.ts
2236
+ var DEFAULT_MQTT_CONFIG = {
2237
+ enabled: true,
2238
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2239
+ brokerPort: 8883,
2240
+ wsPort: 8084,
2241
+ username: "for-sonifi",
2242
+ password: "sonifi-mqtt",
2243
+ topicPrefix: "adstorm/players",
2244
+ qos: 1
2245
+ };
2246
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2247
+ function applyMQTTConfig(overrides) {
2248
+ Object.assign(mqttConfig, overrides);
2249
+ }
2250
+ function isMQTTEnabled() {
2251
+ return mqttConfig.enabled;
2252
+ }
2253
+ function buildMQTTBrokerUrl() {
2254
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2255
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2256
+ }
2257
+ function buildPlayerTopic(licenseKey, channel) {
2258
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2259
+ }
2211
2260
  // src/utils/mqttClient.ts
2212
2261
  var import_mqtt = __toESM(require("mqtt"), 1);
2213
2262
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2214
2263
  var client = null;
2215
2264
  var status = "disconnected";
2216
- var brokerUrl = "";
2217
2265
  function getMQTTStatus() {
2218
2266
  return status;
2219
2267
  }
@@ -2221,17 +2269,22 @@ function isMQTTConnected() {
2221
2269
  return status === "connected" && client !== null && client.connected;
2222
2270
  }
2223
2271
  function isMQTTConfigured() {
2224
- return client !== null;
2272
+ return isMQTTEnabled();
2273
+ }
2274
+ function configureMQTT(overrides) {
2275
+ applyMQTTConfig(overrides);
2276
+ disconnectMQTT();
2225
2277
  }
2226
- function initMQTTClient(url) {
2227
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2228
- if (client) return;
2229
- brokerUrl = url;
2278
+ function initMQTTClient() {
2279
+ if (client || !isMQTTEnabled()) return;
2280
+ var url = buildMQTTBrokerUrl();
2230
2281
  status = "connecting";
2231
2282
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2232
2283
  try {
2233
2284
  client = import_mqtt.default.connect(url, {
2234
2285
  clientId: clientId,
2286
+ username: mqttConfig.username,
2287
+ password: mqttConfig.password,
2235
2288
  keepalive: 60,
2236
2289
  clean: true,
2237
2290
  reconnectPeriod: 5e3,
@@ -2265,13 +2318,22 @@ function initMQTTClient(url) {
2265
2318
  }
2266
2319
  });
2267
2320
  }
2321
+ function ensureMQTTClient() {
2322
+ if (isMQTTEnabled() && !client) {
2323
+ initMQTTClient();
2324
+ }
2325
+ }
2268
2326
  function publishMQTT(topic, payload) {
2327
+ if (!isMQTTEnabled()) {
2328
+ return false;
2329
+ }
2330
+ ensureMQTTClient();
2269
2331
  if (!client) {
2270
2332
  return false;
2271
2333
  }
2272
2334
  try {
2273
2335
  client.publish(topic, JSON.stringify(payload), {
2274
- qos: 1
2336
+ qos: mqttConfig.qos
2275
2337
  });
2276
2338
  return true;
2277
2339
  } catch (err) {
@@ -2284,7 +2346,6 @@ function disconnectMQTT() {
2284
2346
  client.end(true);
2285
2347
  client = null;
2286
2348
  status = "disconnected";
2287
- brokerUrl = "";
2288
2349
  }
2289
2350
  }
2290
2351
  // src/utils/tracking.ts
@@ -2506,171 +2567,91 @@ function getBrowserID(clientInfo) {
2506
2567
  });
2507
2568
  })();
2508
2569
  }
2509
- var mqttTopicPrefix = "adstorm";
2510
- function setMQTTTopicPrefix(prefix) {
2511
- mqttTopicPrefix = prefix || "adstorm";
2570
+ function canPublish(licenseKey) {
2571
+ return Boolean(isMQTTEnabled() && licenseKey);
2512
2572
  }
2513
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2514
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2515
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2516
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2517
- function buildHeaders(licenseKey) {
2518
- var headers = {
2519
- "Content-Type": "application/json"
2520
- };
2521
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2522
- return headers;
2523
- }
2524
- function postJson(url, licenseKey, body) {
2573
+ function buildPlayerMetricEvent() {
2525
2574
  return _async_to_generator(function() {
2526
- var response;
2527
- return _ts_generator(this, function(_state) {
2528
- switch(_state.label){
2529
- case 0:
2530
- return [
2531
- 4,
2532
- fetch(url, {
2533
- method: "POST",
2534
- headers: buildHeaders(licenseKey),
2535
- body: JSON.stringify(body)
2536
- })
2537
- ];
2538
- case 1:
2539
- response = _state.sent();
2540
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2541
- return [
2542
- 4,
2543
- response.json()
2544
- ];
2545
- case 2:
2546
- _state.sent();
2547
- return [
2548
- 2
2549
- ];
2550
- }
2551
- });
2552
- })();
2553
- }
2554
- function buildPlayerMetricEvent(_0) {
2555
- return _async_to_generator(function(licenseKey) {
2556
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2575
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2557
2576
  var _arguments = arguments;
2558
2577
  return _ts_generator(this, function(_state) {
2559
2578
  switch(_state.label){
2560
2579
  case 0:
2561
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2580
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2562
2581
  clientInfo = getClientInfo();
2563
2582
  return [
2564
2583
  4,
2565
2584
  getBrowserID(clientInfo)
2566
2585
  ];
2567
2586
  case 1:
2568
- browserId = _state.sent();
2587
+ playerId = _state.sent();
2569
2588
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2570
2589
  return [
2571
2590
  2,
2572
- {
2573
- player_id: browserId,
2574
- browserId: browserId,
2591
+ _object_spread({
2592
+ player_id: playerId,
2575
2593
  device_type: clientInfo.deviceType,
2576
- deviceType: clientInfo.deviceType,
2577
- input_stream_type: context.inputStreamType,
2578
- os: clientInfo.os,
2579
- ad_loaded: flags.adLoaded,
2580
- ad_detect: flags.adDetect,
2581
- license_key: licenseKey,
2582
- capture_at: captureAt,
2583
- timestamp: captureAt
2584
- }
2594
+ os: clientInfo.os.toLowerCase(),
2595
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
2596
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
2597
+ capture_at: captureAt
2598
+ }, context.inputStreamType ? {
2599
+ input_stream_type: context.inputStreamType
2600
+ } : {})
2585
2601
  ];
2586
2602
  }
2587
2603
  });
2588
2604
  }).apply(this, arguments);
2589
2605
  }
2590
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2591
- return _async_to_generator(function() {
2592
- return _ts_generator(this, function(_state) {
2593
- switch(_state.label){
2594
- case 0:
2595
- if (isMQTTConfigured()) {
2596
- publishMQTT(mqttTopic, body);
2597
- return [
2598
- 2
2599
- ];
2600
- }
2601
- return [
2602
- 4,
2603
- postJson(httpUrl, licenseKey, body)
2604
- ];
2605
- case 1:
2606
- _state.sent();
2607
- return [
2608
- 2
2609
- ];
2610
- }
2611
- });
2612
- })();
2606
+ function publishTracking(licenseKey, channel, body) {
2607
+ ensureMQTTClient();
2608
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2613
2609
  }
2614
2610
  function sendInitialTracking(_0) {
2615
2611
  return _async_to_generator(function(licenseKey) {
2616
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2612
+ var context, metricEvent, error;
2617
2613
  var _arguments = arguments;
2618
2614
  return _ts_generator(this, function(_state) {
2619
2615
  switch(_state.label){
2620
2616
  case 0:
2621
2617
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2618
+ if (!canPublish(licenseKey)) return [
2619
+ 2
2620
+ ];
2622
2621
  _state.label = 1;
2623
2622
  case 1:
2624
2623
  _state.trys.push([
2625
2624
  1,
2626
- 4,
2625
+ 3,
2627
2626
  ,
2628
- 5
2627
+ 4
2629
2628
  ]);
2630
- clientInfo = getClientInfo();
2631
2629
  return [
2632
2630
  4,
2633
- getBrowserID(clientInfo)
2631
+ buildPlayerMetricEvent(context, {
2632
+ adLoaded: false,
2633
+ adDetect: false
2634
+ })
2634
2635
  ];
2635
2636
  case 2:
2636
- browserId = _state.sent();
2637
- captureAt = /* @__PURE__ */ new Date().toISOString();
2638
- trackingData = _object_spread({
2639
- browserId: browserId
2640
- }, clientInfo);
2641
- metricsBody = {
2637
+ metricEvent = _state.sent();
2638
+ publishTracking(licenseKey, "metrics", {
2642
2639
  events: [
2643
- {
2644
- player_id: browserId,
2645
- device_type: clientInfo.deviceType,
2646
- input_stream_type: context.inputStreamType,
2647
- os: clientInfo.os,
2648
- ad_loaded: false,
2649
- ad_detect: false,
2650
- license_key: licenseKey,
2651
- capture_at: captureAt
2652
- }
2653
- ],
2654
- trackingData: trackingData
2655
- };
2656
- return [
2657
- 4,
2658
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2659
- ];
2660
- case 3:
2661
- _state.sent();
2640
+ metricEvent
2641
+ ]
2642
+ });
2662
2643
  return [
2663
2644
  3,
2664
- 5
2645
+ 4
2665
2646
  ];
2666
- case 4:
2647
+ case 3:
2667
2648
  error = _state.sent();
2668
2649
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2669
2650
  return [
2670
2651
  3,
2671
- 5
2652
+ 4
2672
2653
  ];
2673
- case 5:
2654
+ case 4:
2674
2655
  return [
2675
2656
  2
2676
2657
  ];
@@ -2768,61 +2749,54 @@ function sendAdLoadedTracking(_0, _1) {
2768
2749
  }
2769
2750
  function sendAdImpressionTracking(_0, _1) {
2770
2751
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2771
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2752
+ var context, metricEvent, error;
2772
2753
  var _arguments = arguments;
2773
2754
  return _ts_generator(this, function(_state) {
2774
2755
  switch(_state.label){
2775
2756
  case 0:
2776
2757
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2758
+ if (!canPublish(licenseKey)) return [
2759
+ 2
2760
+ ];
2777
2761
  _state.label = 1;
2778
2762
  case 1:
2779
2763
  _state.trys.push([
2780
2764
  1,
2781
- 4,
2765
+ 3,
2782
2766
  ,
2783
- 5
2767
+ 4
2784
2768
  ]);
2785
2769
  return [
2786
2770
  4,
2787
- buildPlayerMetricEvent(licenseKey, context, {
2771
+ buildPlayerMetricEvent(context, {
2788
2772
  captureAt: adImpressionInfo.timestamp
2789
2773
  })
2790
2774
  ];
2791
2775
  case 2:
2792
2776
  metricEvent = _state.sent();
2793
- heartbeatBody = metricEvent;
2794
- impressionsBody = {
2777
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2778
+ publishTracking(licenseKey, "impressions", {
2795
2779
  events: [
2796
2780
  {
2797
2781
  player_id: metricEvent.player_id,
2798
2782
  ad_played_count: 1,
2799
2783
  ad_url: adImpressionInfo.adUrl,
2800
- license_key: licenseKey,
2801
2784
  capture_at: adImpressionInfo.timestamp
2802
2785
  }
2803
2786
  ]
2804
- };
2805
- return [
2806
- 4,
2807
- Promise.all([
2808
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2809
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2810
- ])
2811
- ];
2812
- case 3:
2813
- _state.sent();
2787
+ });
2814
2788
  return [
2815
2789
  3,
2816
- 5
2790
+ 4
2817
2791
  ];
2818
- case 4:
2792
+ case 3:
2819
2793
  error = _state.sent();
2820
2794
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2821
2795
  return [
2822
2796
  3,
2823
- 5
2797
+ 4
2824
2798
  ];
2825
- case 5:
2799
+ case 4:
2826
2800
  return [
2827
2801
  2
2828
2802
  ];
@@ -2838,38 +2812,36 @@ function sendHeartbeat(_0) {
2838
2812
  switch(_state.label){
2839
2813
  case 0:
2840
2814
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2815
+ if (!canPublish(licenseKey)) return [
2816
+ 2
2817
+ ];
2841
2818
  _state.label = 1;
2842
2819
  case 1:
2843
2820
  _state.trys.push([
2844
2821
  1,
2845
- 4,
2822
+ 3,
2846
2823
  ,
2847
- 5
2824
+ 4
2848
2825
  ]);
2849
2826
  return [
2850
2827
  4,
2851
- buildPlayerMetricEvent(licenseKey, context, flags)
2828
+ buildPlayerMetricEvent(context, flags)
2852
2829
  ];
2853
2830
  case 2:
2854
2831
  heartbeatData = _state.sent();
2855
- return [
2856
- 4,
2857
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2858
- ];
2859
- case 3:
2860
- _state.sent();
2832
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2861
2833
  return [
2862
2834
  3,
2863
- 5
2835
+ 4
2864
2836
  ];
2865
- case 4:
2837
+ case 3:
2866
2838
  error = _state.sent();
2867
2839
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2868
2840
  return [
2869
2841
  3,
2870
- 5
2842
+ 4
2871
2843
  ];
2872
- case 5:
2844
+ case 4:
2873
2845
  return [
2874
2846
  2
2875
2847
  ];
@@ -4023,7 +3995,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4023
3995
  _this.adLayer.showPlaceholder();
4024
3996
  }
4025
3997
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
4026
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3998
+ _this.scheduleAdStopAtBreakBoundary();
4027
3999
  if (_this.config.debugAdTiming) {
4028
4000
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
4029
4001
  }
@@ -4457,12 +4429,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4457
4429
  if (marker.durationSeconds != null) {
4458
4430
  var newDurationMs = marker.durationSeconds * 1e3;
4459
4431
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4460
- this.expectedAdBreakDurationMs = newDurationMs;
4461
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4462
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4463
- this.scheduleAdStopCountdown(remainingMs);
4432
+ this.setAdBreakDurationBoundary(newDurationMs);
4433
+ this.scheduleAdStopAtBreakBoundary();
4464
4434
  if (this.config.debugAdTiming) {
4465
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4435
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4466
4436
  }
4467
4437
  }
4468
4438
  }
@@ -4470,8 +4440,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4470
4440
  }
4471
4441
  this.inAdBreak = true;
4472
4442
  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;
4473
- this.expectedAdBreakDurationMs = durationMs;
4474
- this.currentAdBreakStartWallClockMs = Date.now();
4443
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4444
+ this.setAdBreakDurationBoundary(durationMs);
4475
4445
  if (this.config.licenseKey) {
4476
4446
  var _this_pendingAdBreak1;
4477
4447
  var adDetectInfo = _object_spread({
@@ -4535,20 +4505,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4535
4505
  this.clearAdStartTimer();
4536
4506
  this.handleAdStart(marker);
4537
4507
  }
4538
- if (this.expectedAdBreakDurationMs != null) {
4539
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4540
- }
4508
+ this.scheduleAdStopAtBreakBoundary();
4541
4509
  return;
4542
4510
  }
4543
4511
  if (marker.type === "progress" && this.inAdBreak) {
4544
4512
  if (marker.durationSeconds != null) {
4545
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4546
- }
4547
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4548
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4549
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4550
- this.scheduleAdStopCountdown(remainingMs1);
4513
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4551
4514
  }
4515
+ this.scheduleAdStopAtBreakBoundary();
4552
4516
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4553
4517
  var bids = this.pendingNextAdBids;
4554
4518
  this.pendingNextAdBids = null;
@@ -4579,15 +4543,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4579
4543
  }
4580
4544
  return;
4581
4545
  }
4582
- if (adPlaying || remaining > 500) {
4583
- if (this.config.debugAdTiming) {
4584
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4585
- }
4586
- return;
4587
- }
4588
- this.inAdBreak = false;
4589
- this.expectedAdBreakDurationMs = void 0;
4590
- this.currentAdBreakStartWallClockMs = void 0;
4546
+ this.scteAdBreakEndWallClockMs = Date.now();
4591
4547
  this.clearAdStartTimer();
4592
4548
  this.clearAdStopTimer();
4593
4549
  if (adPlaying) {
@@ -4598,6 +4554,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4598
4554
  }
4599
4555
  }
4600
4556
  },
4557
+ {
4558
+ key: "resolveScteBreakStartWallClockMs",
4559
+ value: function resolveScteBreakStartWallClockMs(marker) {
4560
+ var nowWallClockMs = Date.now();
4561
+ if (typeof marker.ptsSeconds !== "number") {
4562
+ return nowWallClockMs;
4563
+ }
4564
+ var nowMs = this.video.currentTime * 1e3;
4565
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4566
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4567
+ if (!Number.isFinite(deltaMs)) {
4568
+ return nowWallClockMs;
4569
+ }
4570
+ return nowWallClockMs + Math.floor(deltaMs);
4571
+ }
4572
+ },
4573
+ {
4574
+ key: "setAdBreakDurationBoundary",
4575
+ value: function setAdBreakDurationBoundary(durationMs) {
4576
+ this.expectedAdBreakDurationMs = durationMs;
4577
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4578
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4579
+ } else {
4580
+ this.scteAdBreakEndWallClockMs = void 0;
4581
+ }
4582
+ }
4583
+ },
4584
+ {
4585
+ key: "scheduleAdStopAtBreakBoundary",
4586
+ value: function scheduleAdStopAtBreakBoundary() {
4587
+ if (this.expectedAdBreakDurationMs == null) {
4588
+ return;
4589
+ }
4590
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4591
+ }
4592
+ },
4601
4593
  {
4602
4594
  key: "parseCueOutDuration",
4603
4595
  value: function parseCueOutDuration(value) {
@@ -4808,10 +4800,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4808
4800
  key: "initializeTracking",
4809
4801
  value: function initializeTracking() {
4810
4802
  var _this = this;
4811
- if (this.config.mqttBrokerUrl) {
4812
- var topicPrefix = "adstorm";
4813
- setMQTTTopicPrefix(topicPrefix);
4814
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4803
+ if (isMQTTEnabled()) {
4804
+ initMQTTClient();
4815
4805
  }
4816
4806
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4817
4807
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5235,7 +5225,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5235
5225
  case 9:
5236
5226
  _state.sent();
5237
5227
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5238
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5228
+ _this.scheduleAdStopAtBreakBoundary();
5239
5229
  }
5240
5230
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5241
5231
  return [
@@ -5340,7 +5330,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5340
5330
  case 19:
5341
5331
  _state.sent();
5342
5332
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5343
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5333
+ _this.scheduleAdStopAtBreakBoundary();
5344
5334
  }
5345
5335
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5346
5336
  _state.label = 20;
@@ -5423,7 +5413,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5423
5413
  key: "handleAdStart",
5424
5414
  value: function handleAdStart(_marker) {
5425
5415
  return _async_to_generator(function() {
5426
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5416
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5427
5417
  return _ts_generator(this, function(_state) {
5428
5418
  switch(_state.label){
5429
5419
  case 0:
@@ -5457,20 +5447,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5457
5447
  }
5458
5448
  }
5459
5449
  this.inAdBreak = true;
5460
- this.currentAdBreakStartWallClockMs = Date.now();
5461
5450
  this.currentAdIndex = 0;
5462
5451
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5463
5452
  this.adPodQueue = [];
5464
5453
  if (!this.config.disableFiller) this.showAds = true;
5465
- if (adBreakDurationMs != null) {
5466
- this.startFillerBreakTimer(adBreakDurationMs);
5454
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5455
+ this.currentAdBreakStartWallClockMs = Date.now();
5456
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5457
+ }
5458
+ fillerRemainingMs = this.getRemainingAdMs();
5459
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5460
+ this.startFillerBreakTimer(fillerRemainingMs);
5467
5461
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5468
5462
  this.showPlaceholderLayer();
5469
5463
  }
5470
5464
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5471
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5472
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5473
- }
5474
5465
  this.clearPendingAdBreak();
5475
5466
  adBreakToken = Date.now();
5476
5467
  this.activeAdRequestToken = adBreakToken;
@@ -5507,7 +5498,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5507
5498
  case 2:
5508
5499
  _state.sent();
5509
5500
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5510
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5501
+ this.scheduleAdStopAtBreakBoundary();
5511
5502
  }
5512
5503
  this.adLayer.setAdVolume(adVolume);
5513
5504
  return [
@@ -5693,7 +5684,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5693
5684
  case 4:
5694
5685
  _state.sent();
5695
5686
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5696
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5687
+ this.scheduleAdStopAtBreakBoundary();
5697
5688
  }
5698
5689
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5699
5690
  _state.label = 5;
@@ -5935,21 +5926,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5935
5926
  {
5936
5927
  key: "ensureAdStoppedByTimer",
5937
5928
  value: function ensureAdStoppedByTimer() {
5938
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5929
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5939
5930
  if (!this.inAdBreak) return;
5940
5931
  this.adStopTimerId = void 0;
5941
5932
  var adPlaying = this.adLayer.isAdPlaying();
5942
- var pendingAds = this.adPodQueue.length > 0;
5943
5933
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5944
5934
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5945
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5946
- var elapsedSinceStartMs = 0;
5947
- if (this.currentAdBreakStartWallClockMs != null) {
5948
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5949
- }
5950
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5951
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5952
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5935
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5936
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5937
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5938
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5953
5939
  if (shouldExtendAdBreak) {
5954
5940
  this.scheduleAdStopCountdown(checkIntervalMs);
5955
5941
  return;
@@ -6023,6 +6009,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6023
6009
  this.inAdBreak = false;
6024
6010
  this.expectedAdBreakDurationMs = void 0;
6025
6011
  this.currentAdBreakStartWallClockMs = void 0;
6012
+ this.scteAdBreakEndWallClockMs = void 0;
6026
6013
  this.clearAdStartTimer();
6027
6014
  this.clearAdStopTimer();
6028
6015
  this.adPodQueue = [];
@@ -6284,10 +6271,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6284
6271
  {
6285
6272
  key: "getRemainingAdMs",
6286
6273
  value: function getRemainingAdMs() {
6274
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6275
+ if (endWallClockMs != null) {
6276
+ return Math.max(0, endWallClockMs - Date.now());
6277
+ }
6287
6278
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6288
6279
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6289
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6290
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6280
+ return 0;
6281
+ }
6282
+ },
6283
+ {
6284
+ key: "getAdBreakEndWallClockMs",
6285
+ value: function getAdBreakEndWallClockMs() {
6286
+ if (this.scteAdBreakEndWallClockMs != null) {
6287
+ return this.scteAdBreakEndWallClockMs;
6288
+ }
6289
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6290
+ return void 0;
6291
+ }
6292
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6291
6293
  }
6292
6294
  },
6293
6295
  {
@@ -6492,7 +6494,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6492
6494
  clearInterval(this.heartbeatInterval);
6493
6495
  this.heartbeatInterval = void 0;
6494
6496
  }
6495
- if (this.config.mqttBrokerUrl) {
6497
+ if (isMQTTEnabled()) {
6496
6498
  disconnectMQTT();
6497
6499
  }
6498
6500
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();
@@ -6516,7 +6518,7 @@ var CRITICAL_PROPS = [
6516
6518
  var CONTROLS_HIDE_DELAY = 3e3;
6517
6519
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
6518
6520
  var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6519
- 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, mqttBrokerUrl = props.mqttBrokerUrl, restVideoAttrs = _object_without_properties(props, [
6521
+ 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, restVideoAttrs = _object_without_properties(props, [
6520
6522
  "src",
6521
6523
  "autoplay",
6522
6524
  "muted",
@@ -6543,8 +6545,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6543
6545
  "licenseKey",
6544
6546
  "minSegmentsBeforePlay",
6545
6547
  "disableAds",
6546
- "disableFiller",
6547
- "mqttBrokerUrl"
6548
+ "disableFiller"
6548
6549
  ]);
6549
6550
  var videoRef = (0, import_react.useRef)(null);
6550
6551
  var playerRef = (0, import_react.useRef)(null);
@@ -6704,7 +6705,6 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6704
6705
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;
6705
6706
  if (disableAds !== void 0) cfg.disableAds = disableAds;
6706
6707
  cfg.disableFiller = disableFiller !== null && disableFiller !== void 0 ? disableFiller : true;
6707
- if (mqttBrokerUrl !== void 0) cfg.mqttBrokerUrl = mqttBrokerUrl;
6708
6708
  var player = new StormcloudVideoPlayer(cfg);
6709
6709
  playerRef.current = player;
6710
6710
  player.load().then(function() {
@@ -8937,6 +8937,7 @@ var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[p
8937
8937
  var StormcloudPlayer_default = StormcloudPlayer;
8938
8938
  // Annotate the CommonJS export names for ESM import in node:
8939
8939
  0 && (module.exports = {
8940
+ DEFAULT_MQTT_CONFIG: DEFAULT_MQTT_CONFIG,
8940
8941
  IS_BROWSER: IS_BROWSER,
8941
8942
  IS_GLOBAL: IS_GLOBAL,
8942
8943
  IS_IOS: IS_IOS,
@@ -8946,12 +8947,17 @@ var StormcloudPlayer_default = StormcloudPlayer;
8946
8947
  StormcloudPlayer: StormcloudPlayer,
8947
8948
  StormcloudVideoPlayer: StormcloudVideoPlayer,
8948
8949
  StormcloudVideoPlayerComponent: StormcloudVideoPlayerComponent,
8950
+ applyMQTTConfig: applyMQTTConfig,
8951
+ buildMQTTBrokerUrl: buildMQTTBrokerUrl,
8952
+ buildPlayerTopic: buildPlayerTopic,
8949
8953
  canPlay: canPlay,
8954
+ configureMQTT: configureMQTT,
8950
8955
  createStormcloudPlayer: createStormcloudPlayer,
8951
8956
  createVastAdLayer: createVastAdLayer,
8952
8957
  createVastManager: createVastManager,
8953
8958
  detectBrowser: detectBrowser,
8954
8959
  disconnectMQTT: disconnectMQTT,
8960
+ ensureMQTTClient: ensureMQTTClient,
8955
8961
  getBrowserConfigOverrides: getBrowserConfigOverrides,
8956
8962
  getBrowserID: getBrowserID,
8957
8963
  getClientInfo: getClientInfo,
@@ -8960,10 +8966,12 @@ var StormcloudPlayer_default = StormcloudPlayer;
8960
8966
  initializePolyfills: initializePolyfills,
8961
8967
  isMQTTConfigured: isMQTTConfigured,
8962
8968
  isMQTTConnected: isMQTTConnected,
8969
+ isMQTTEnabled: isMQTTEnabled,
8963
8970
  isMediaStream: isMediaStream,
8964
8971
  lazy: lazy,
8965
8972
  logBrowserInfo: logBrowserInfo,
8966
8973
  merge: merge,
8974
+ mqttConfig: mqttConfig,
8967
8975
  omit: omit,
8968
8976
  parseQuery: parseQuery,
8969
8977
  players: players,