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.
@@ -2060,24 +2060,43 @@ function createVastAdLayer(contentVideo, options) {
2060
2060
  }
2061
2061
  };
2062
2062
  }
2063
+ // src/utils/mqttConfig.ts
2064
+ var DEFAULT_MQTT_CONFIG = {
2065
+ enabled: true,
2066
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2067
+ brokerPort: 8883,
2068
+ wsPort: 8084,
2069
+ username: "for-sonifi",
2070
+ password: "sonifi-mqtt",
2071
+ topicPrefix: "adstorm/players",
2072
+ qos: 1
2073
+ };
2074
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2075
+ function isMQTTEnabled() {
2076
+ return mqttConfig.enabled;
2077
+ }
2078
+ function buildMQTTBrokerUrl() {
2079
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2080
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2081
+ }
2082
+ function buildPlayerTopic(licenseKey, channel) {
2083
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2084
+ }
2063
2085
  // src/utils/mqttClient.ts
2064
2086
  var import_mqtt = __toESM(require("mqtt"), 1);
2065
2087
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2066
2088
  var client = null;
2067
2089
  var status = "disconnected";
2068
- var brokerUrl = "";
2069
- function isMQTTConfigured() {
2070
- return client !== null;
2071
- }
2072
- function initMQTTClient(url) {
2073
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2074
- if (client) return;
2075
- brokerUrl = url;
2090
+ function initMQTTClient() {
2091
+ if (client || !isMQTTEnabled()) return;
2092
+ var url = buildMQTTBrokerUrl();
2076
2093
  status = "connecting";
2077
2094
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2078
2095
  try {
2079
2096
  client = import_mqtt.default.connect(url, {
2080
2097
  clientId: clientId,
2098
+ username: mqttConfig.username,
2099
+ password: mqttConfig.password,
2081
2100
  keepalive: 60,
2082
2101
  clean: true,
2083
2102
  reconnectPeriod: 5e3,
@@ -2111,13 +2130,22 @@ function initMQTTClient(url) {
2111
2130
  }
2112
2131
  });
2113
2132
  }
2133
+ function ensureMQTTClient() {
2134
+ if (isMQTTEnabled() && !client) {
2135
+ initMQTTClient();
2136
+ }
2137
+ }
2114
2138
  function publishMQTT(topic, payload) {
2139
+ if (!isMQTTEnabled()) {
2140
+ return false;
2141
+ }
2142
+ ensureMQTTClient();
2115
2143
  if (!client) {
2116
2144
  return false;
2117
2145
  }
2118
2146
  try {
2119
2147
  client.publish(topic, JSON.stringify(payload), {
2120
- qos: 1
2148
+ qos: mqttConfig.qos
2121
2149
  });
2122
2150
  return true;
2123
2151
  } catch (err) {
@@ -2130,7 +2158,6 @@ function disconnectMQTT() {
2130
2158
  client.end(true);
2131
2159
  client = null;
2132
2160
  status = "disconnected";
2133
- brokerUrl = "";
2134
2161
  }
2135
2162
  }
2136
2163
  // src/utils/tracking.ts
@@ -2352,171 +2379,91 @@ function getBrowserID(clientInfo) {
2352
2379
  });
2353
2380
  })();
2354
2381
  }
2355
- var mqttTopicPrefix = "adstorm";
2356
- function setMQTTTopicPrefix(prefix) {
2357
- mqttTopicPrefix = prefix || "adstorm";
2358
- }
2359
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2360
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2361
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2362
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2363
- function buildHeaders(licenseKey) {
2364
- var headers = {
2365
- "Content-Type": "application/json"
2366
- };
2367
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2368
- return headers;
2382
+ function canPublish(licenseKey) {
2383
+ return Boolean(isMQTTEnabled() && licenseKey);
2369
2384
  }
2370
- function postJson(url, licenseKey, body) {
2385
+ function buildPlayerMetricEvent() {
2371
2386
  return _async_to_generator(function() {
2372
- var response;
2373
- return _ts_generator(this, function(_state) {
2374
- switch(_state.label){
2375
- case 0:
2376
- return [
2377
- 4,
2378
- fetch(url, {
2379
- method: "POST",
2380
- headers: buildHeaders(licenseKey),
2381
- body: JSON.stringify(body)
2382
- })
2383
- ];
2384
- case 1:
2385
- response = _state.sent();
2386
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2387
- return [
2388
- 4,
2389
- response.json()
2390
- ];
2391
- case 2:
2392
- _state.sent();
2393
- return [
2394
- 2
2395
- ];
2396
- }
2397
- });
2398
- })();
2399
- }
2400
- function buildPlayerMetricEvent(_0) {
2401
- return _async_to_generator(function(licenseKey) {
2402
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2387
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2403
2388
  var _arguments = arguments;
2404
2389
  return _ts_generator(this, function(_state) {
2405
2390
  switch(_state.label){
2406
2391
  case 0:
2407
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2392
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2408
2393
  clientInfo = getClientInfo();
2409
2394
  return [
2410
2395
  4,
2411
2396
  getBrowserID(clientInfo)
2412
2397
  ];
2413
2398
  case 1:
2414
- browserId = _state.sent();
2399
+ playerId = _state.sent();
2415
2400
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2416
2401
  return [
2417
2402
  2,
2418
- {
2419
- player_id: browserId,
2420
- browserId: browserId,
2403
+ _object_spread({
2404
+ player_id: playerId,
2421
2405
  device_type: clientInfo.deviceType,
2422
- deviceType: clientInfo.deviceType,
2423
- input_stream_type: context.inputStreamType,
2424
- os: clientInfo.os,
2425
- ad_loaded: flags.adLoaded,
2426
- ad_detect: flags.adDetect,
2427
- license_key: licenseKey,
2428
- capture_at: captureAt,
2429
- timestamp: captureAt
2430
- }
2406
+ os: clientInfo.os.toLowerCase(),
2407
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
2408
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
2409
+ capture_at: captureAt
2410
+ }, context.inputStreamType ? {
2411
+ input_stream_type: context.inputStreamType
2412
+ } : {})
2431
2413
  ];
2432
2414
  }
2433
2415
  });
2434
2416
  }).apply(this, arguments);
2435
2417
  }
2436
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2437
- return _async_to_generator(function() {
2438
- return _ts_generator(this, function(_state) {
2439
- switch(_state.label){
2440
- case 0:
2441
- if (isMQTTConfigured()) {
2442
- publishMQTT(mqttTopic, body);
2443
- return [
2444
- 2
2445
- ];
2446
- }
2447
- return [
2448
- 4,
2449
- postJson(httpUrl, licenseKey, body)
2450
- ];
2451
- case 1:
2452
- _state.sent();
2453
- return [
2454
- 2
2455
- ];
2456
- }
2457
- });
2458
- })();
2418
+ function publishTracking(licenseKey, channel, body) {
2419
+ ensureMQTTClient();
2420
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2459
2421
  }
2460
2422
  function sendInitialTracking(_0) {
2461
2423
  return _async_to_generator(function(licenseKey) {
2462
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2424
+ var context, metricEvent, error;
2463
2425
  var _arguments = arguments;
2464
2426
  return _ts_generator(this, function(_state) {
2465
2427
  switch(_state.label){
2466
2428
  case 0:
2467
2429
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2430
+ if (!canPublish(licenseKey)) return [
2431
+ 2
2432
+ ];
2468
2433
  _state.label = 1;
2469
2434
  case 1:
2470
2435
  _state.trys.push([
2471
2436
  1,
2472
- 4,
2437
+ 3,
2473
2438
  ,
2474
- 5
2439
+ 4
2475
2440
  ]);
2476
- clientInfo = getClientInfo();
2477
2441
  return [
2478
2442
  4,
2479
- getBrowserID(clientInfo)
2443
+ buildPlayerMetricEvent(context, {
2444
+ adLoaded: false,
2445
+ adDetect: false
2446
+ })
2480
2447
  ];
2481
2448
  case 2:
2482
- browserId = _state.sent();
2483
- captureAt = /* @__PURE__ */ new Date().toISOString();
2484
- trackingData = _object_spread({
2485
- browserId: browserId
2486
- }, clientInfo);
2487
- metricsBody = {
2449
+ metricEvent = _state.sent();
2450
+ publishTracking(licenseKey, "metrics", {
2488
2451
  events: [
2489
- {
2490
- player_id: browserId,
2491
- device_type: clientInfo.deviceType,
2492
- input_stream_type: context.inputStreamType,
2493
- os: clientInfo.os,
2494
- ad_loaded: false,
2495
- ad_detect: false,
2496
- license_key: licenseKey,
2497
- capture_at: captureAt
2498
- }
2499
- ],
2500
- trackingData: trackingData
2501
- };
2502
- return [
2503
- 4,
2504
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2505
- ];
2506
- case 3:
2507
- _state.sent();
2452
+ metricEvent
2453
+ ]
2454
+ });
2508
2455
  return [
2509
2456
  3,
2510
- 5
2457
+ 4
2511
2458
  ];
2512
- case 4:
2459
+ case 3:
2513
2460
  error = _state.sent();
2514
2461
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2515
2462
  return [
2516
2463
  3,
2517
- 5
2464
+ 4
2518
2465
  ];
2519
- case 5:
2466
+ case 4:
2520
2467
  return [
2521
2468
  2
2522
2469
  ];
@@ -2614,61 +2561,54 @@ function sendAdLoadedTracking(_0, _1) {
2614
2561
  }
2615
2562
  function sendAdImpressionTracking(_0, _1) {
2616
2563
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2617
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2564
+ var context, metricEvent, error;
2618
2565
  var _arguments = arguments;
2619
2566
  return _ts_generator(this, function(_state) {
2620
2567
  switch(_state.label){
2621
2568
  case 0:
2622
2569
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2570
+ if (!canPublish(licenseKey)) return [
2571
+ 2
2572
+ ];
2623
2573
  _state.label = 1;
2624
2574
  case 1:
2625
2575
  _state.trys.push([
2626
2576
  1,
2627
- 4,
2577
+ 3,
2628
2578
  ,
2629
- 5
2579
+ 4
2630
2580
  ]);
2631
2581
  return [
2632
2582
  4,
2633
- buildPlayerMetricEvent(licenseKey, context, {
2583
+ buildPlayerMetricEvent(context, {
2634
2584
  captureAt: adImpressionInfo.timestamp
2635
2585
  })
2636
2586
  ];
2637
2587
  case 2:
2638
2588
  metricEvent = _state.sent();
2639
- heartbeatBody = metricEvent;
2640
- impressionsBody = {
2589
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2590
+ publishTracking(licenseKey, "impressions", {
2641
2591
  events: [
2642
2592
  {
2643
2593
  player_id: metricEvent.player_id,
2644
2594
  ad_played_count: 1,
2645
2595
  ad_url: adImpressionInfo.adUrl,
2646
- license_key: licenseKey,
2647
2596
  capture_at: adImpressionInfo.timestamp
2648
2597
  }
2649
2598
  ]
2650
- };
2651
- return [
2652
- 4,
2653
- Promise.all([
2654
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2655
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2656
- ])
2657
- ];
2658
- case 3:
2659
- _state.sent();
2599
+ });
2660
2600
  return [
2661
2601
  3,
2662
- 5
2602
+ 4
2663
2603
  ];
2664
- case 4:
2604
+ case 3:
2665
2605
  error = _state.sent();
2666
2606
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2667
2607
  return [
2668
2608
  3,
2669
- 5
2609
+ 4
2670
2610
  ];
2671
- case 5:
2611
+ case 4:
2672
2612
  return [
2673
2613
  2
2674
2614
  ];
@@ -2684,38 +2624,36 @@ function sendHeartbeat(_0) {
2684
2624
  switch(_state.label){
2685
2625
  case 0:
2686
2626
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2627
+ if (!canPublish(licenseKey)) return [
2628
+ 2
2629
+ ];
2687
2630
  _state.label = 1;
2688
2631
  case 1:
2689
2632
  _state.trys.push([
2690
2633
  1,
2691
- 4,
2634
+ 3,
2692
2635
  ,
2693
- 5
2636
+ 4
2694
2637
  ]);
2695
2638
  return [
2696
2639
  4,
2697
- buildPlayerMetricEvent(licenseKey, context, flags)
2640
+ buildPlayerMetricEvent(context, flags)
2698
2641
  ];
2699
2642
  case 2:
2700
2643
  heartbeatData = _state.sent();
2701
- return [
2702
- 4,
2703
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2704
- ];
2705
- case 3:
2706
- _state.sent();
2644
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2707
2645
  return [
2708
2646
  3,
2709
- 5
2647
+ 4
2710
2648
  ];
2711
- case 4:
2649
+ case 3:
2712
2650
  error = _state.sent();
2713
2651
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2714
2652
  return [
2715
2653
  3,
2716
- 5
2654
+ 4
2717
2655
  ];
2718
- case 5:
2656
+ case 4:
2719
2657
  return [
2720
2658
  2
2721
2659
  ];
@@ -3844,7 +3782,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3844
3782
  _this.adLayer.showPlaceholder();
3845
3783
  }
3846
3784
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
3847
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3785
+ _this.scheduleAdStopAtBreakBoundary();
3848
3786
  if (_this.config.debugAdTiming) {
3849
3787
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
3850
3788
  }
@@ -4278,12 +4216,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4278
4216
  if (marker.durationSeconds != null) {
4279
4217
  var newDurationMs = marker.durationSeconds * 1e3;
4280
4218
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4281
- this.expectedAdBreakDurationMs = newDurationMs;
4282
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4283
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4284
- this.scheduleAdStopCountdown(remainingMs);
4219
+ this.setAdBreakDurationBoundary(newDurationMs);
4220
+ this.scheduleAdStopAtBreakBoundary();
4285
4221
  if (this.config.debugAdTiming) {
4286
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4222
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4287
4223
  }
4288
4224
  }
4289
4225
  }
@@ -4291,8 +4227,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4291
4227
  }
4292
4228
  this.inAdBreak = true;
4293
4229
  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;
4294
- this.expectedAdBreakDurationMs = durationMs;
4295
- this.currentAdBreakStartWallClockMs = Date.now();
4230
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4231
+ this.setAdBreakDurationBoundary(durationMs);
4296
4232
  if (this.config.licenseKey) {
4297
4233
  var _this_pendingAdBreak1;
4298
4234
  var adDetectInfo = _object_spread({
@@ -4356,20 +4292,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4356
4292
  this.clearAdStartTimer();
4357
4293
  this.handleAdStart(marker);
4358
4294
  }
4359
- if (this.expectedAdBreakDurationMs != null) {
4360
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4361
- }
4295
+ this.scheduleAdStopAtBreakBoundary();
4362
4296
  return;
4363
4297
  }
4364
4298
  if (marker.type === "progress" && this.inAdBreak) {
4365
4299
  if (marker.durationSeconds != null) {
4366
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4367
- }
4368
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4369
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4370
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4371
- this.scheduleAdStopCountdown(remainingMs1);
4300
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4372
4301
  }
4302
+ this.scheduleAdStopAtBreakBoundary();
4373
4303
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4374
4304
  var bids = this.pendingNextAdBids;
4375
4305
  this.pendingNextAdBids = null;
@@ -4400,15 +4330,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4400
4330
  }
4401
4331
  return;
4402
4332
  }
4403
- if (adPlaying || remaining > 500) {
4404
- if (this.config.debugAdTiming) {
4405
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4406
- }
4407
- return;
4408
- }
4409
- this.inAdBreak = false;
4410
- this.expectedAdBreakDurationMs = void 0;
4411
- this.currentAdBreakStartWallClockMs = void 0;
4333
+ this.scteAdBreakEndWallClockMs = Date.now();
4412
4334
  this.clearAdStartTimer();
4413
4335
  this.clearAdStopTimer();
4414
4336
  if (adPlaying) {
@@ -4419,6 +4341,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4419
4341
  }
4420
4342
  }
4421
4343
  },
4344
+ {
4345
+ key: "resolveScteBreakStartWallClockMs",
4346
+ value: function resolveScteBreakStartWallClockMs(marker) {
4347
+ var nowWallClockMs = Date.now();
4348
+ if (typeof marker.ptsSeconds !== "number") {
4349
+ return nowWallClockMs;
4350
+ }
4351
+ var nowMs = this.video.currentTime * 1e3;
4352
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4353
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4354
+ if (!Number.isFinite(deltaMs)) {
4355
+ return nowWallClockMs;
4356
+ }
4357
+ return nowWallClockMs + Math.floor(deltaMs);
4358
+ }
4359
+ },
4360
+ {
4361
+ key: "setAdBreakDurationBoundary",
4362
+ value: function setAdBreakDurationBoundary(durationMs) {
4363
+ this.expectedAdBreakDurationMs = durationMs;
4364
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4365
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4366
+ } else {
4367
+ this.scteAdBreakEndWallClockMs = void 0;
4368
+ }
4369
+ }
4370
+ },
4371
+ {
4372
+ key: "scheduleAdStopAtBreakBoundary",
4373
+ value: function scheduleAdStopAtBreakBoundary() {
4374
+ if (this.expectedAdBreakDurationMs == null) {
4375
+ return;
4376
+ }
4377
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4378
+ }
4379
+ },
4422
4380
  {
4423
4381
  key: "parseCueOutDuration",
4424
4382
  value: function parseCueOutDuration(value) {
@@ -4629,10 +4587,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4629
4587
  key: "initializeTracking",
4630
4588
  value: function initializeTracking() {
4631
4589
  var _this = this;
4632
- if (this.config.mqttBrokerUrl) {
4633
- var topicPrefix = "adstorm";
4634
- setMQTTTopicPrefix(topicPrefix);
4635
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4590
+ if (isMQTTEnabled()) {
4591
+ initMQTTClient();
4636
4592
  }
4637
4593
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4638
4594
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5056,7 +5012,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5056
5012
  case 9:
5057
5013
  _state.sent();
5058
5014
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5059
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5015
+ _this.scheduleAdStopAtBreakBoundary();
5060
5016
  }
5061
5017
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5062
5018
  return [
@@ -5161,7 +5117,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5161
5117
  case 19:
5162
5118
  _state.sent();
5163
5119
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5164
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5120
+ _this.scheduleAdStopAtBreakBoundary();
5165
5121
  }
5166
5122
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5167
5123
  _state.label = 20;
@@ -5244,7 +5200,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5244
5200
  key: "handleAdStart",
5245
5201
  value: function handleAdStart(_marker) {
5246
5202
  return _async_to_generator(function() {
5247
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5203
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5248
5204
  return _ts_generator(this, function(_state) {
5249
5205
  switch(_state.label){
5250
5206
  case 0:
@@ -5278,20 +5234,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5278
5234
  }
5279
5235
  }
5280
5236
  this.inAdBreak = true;
5281
- this.currentAdBreakStartWallClockMs = Date.now();
5282
5237
  this.currentAdIndex = 0;
5283
5238
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5284
5239
  this.adPodQueue = [];
5285
5240
  if (!this.config.disableFiller) this.showAds = true;
5286
- if (adBreakDurationMs != null) {
5287
- this.startFillerBreakTimer(adBreakDurationMs);
5241
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5242
+ this.currentAdBreakStartWallClockMs = Date.now();
5243
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5244
+ }
5245
+ fillerRemainingMs = this.getRemainingAdMs();
5246
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5247
+ this.startFillerBreakTimer(fillerRemainingMs);
5288
5248
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5289
5249
  this.showPlaceholderLayer();
5290
5250
  }
5291
5251
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5292
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5293
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5294
- }
5295
5252
  this.clearPendingAdBreak();
5296
5253
  adBreakToken = Date.now();
5297
5254
  this.activeAdRequestToken = adBreakToken;
@@ -5328,7 +5285,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5328
5285
  case 2:
5329
5286
  _state.sent();
5330
5287
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5331
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5288
+ this.scheduleAdStopAtBreakBoundary();
5332
5289
  }
5333
5290
  this.adLayer.setAdVolume(adVolume);
5334
5291
  return [
@@ -5514,7 +5471,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5514
5471
  case 4:
5515
5472
  _state.sent();
5516
5473
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5517
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5474
+ this.scheduleAdStopAtBreakBoundary();
5518
5475
  }
5519
5476
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5520
5477
  _state.label = 5;
@@ -5756,21 +5713,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5756
5713
  {
5757
5714
  key: "ensureAdStoppedByTimer",
5758
5715
  value: function ensureAdStoppedByTimer() {
5759
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5716
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5760
5717
  if (!this.inAdBreak) return;
5761
5718
  this.adStopTimerId = void 0;
5762
5719
  var adPlaying = this.adLayer.isAdPlaying();
5763
- var pendingAds = this.adPodQueue.length > 0;
5764
5720
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5765
5721
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5766
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5767
- var elapsedSinceStartMs = 0;
5768
- if (this.currentAdBreakStartWallClockMs != null) {
5769
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5770
- }
5771
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5772
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5773
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5722
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5723
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5724
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5725
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5774
5726
  if (shouldExtendAdBreak) {
5775
5727
  this.scheduleAdStopCountdown(checkIntervalMs);
5776
5728
  return;
@@ -5844,6 +5796,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5844
5796
  this.inAdBreak = false;
5845
5797
  this.expectedAdBreakDurationMs = void 0;
5846
5798
  this.currentAdBreakStartWallClockMs = void 0;
5799
+ this.scteAdBreakEndWallClockMs = void 0;
5847
5800
  this.clearAdStartTimer();
5848
5801
  this.clearAdStopTimer();
5849
5802
  this.adPodQueue = [];
@@ -6105,10 +6058,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6105
6058
  {
6106
6059
  key: "getRemainingAdMs",
6107
6060
  value: function getRemainingAdMs() {
6061
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6062
+ if (endWallClockMs != null) {
6063
+ return Math.max(0, endWallClockMs - Date.now());
6064
+ }
6108
6065
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6109
6066
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6110
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6111
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6067
+ return 0;
6068
+ }
6069
+ },
6070
+ {
6071
+ key: "getAdBreakEndWallClockMs",
6072
+ value: function getAdBreakEndWallClockMs() {
6073
+ if (this.scteAdBreakEndWallClockMs != null) {
6074
+ return this.scteAdBreakEndWallClockMs;
6075
+ }
6076
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6077
+ return void 0;
6078
+ }
6079
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6112
6080
  }
6113
6081
  },
6114
6082
  {
@@ -6313,7 +6281,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6313
6281
  clearInterval(this.heartbeatInterval);
6314
6282
  this.heartbeatInterval = void 0;
6315
6283
  }
6316
- if (this.config.mqttBrokerUrl) {
6284
+ if (isMQTTEnabled()) {
6317
6285
  disconnectMQTT();
6318
6286
  }
6319
6287
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();