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.
@@ -2008,24 +2008,43 @@ function createVastAdLayer(contentVideo, options) {
2008
2008
  }
2009
2009
  };
2010
2010
  }
2011
+ // src/utils/mqttConfig.ts
2012
+ var DEFAULT_MQTT_CONFIG = {
2013
+ enabled: true,
2014
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2015
+ brokerPort: 8883,
2016
+ wsPort: 8084,
2017
+ username: "for-sonifi",
2018
+ password: "sonifi-mqtt",
2019
+ topicPrefix: "adstorm/players",
2020
+ qos: 1
2021
+ };
2022
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2023
+ function isMQTTEnabled() {
2024
+ return mqttConfig.enabled;
2025
+ }
2026
+ function buildMQTTBrokerUrl() {
2027
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2028
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2029
+ }
2030
+ function buildPlayerTopic(licenseKey, channel) {
2031
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2032
+ }
2011
2033
  // src/utils/mqttClient.ts
2012
2034
  var import_mqtt = __toESM(require("mqtt"), 1);
2013
2035
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2014
2036
  var client = null;
2015
2037
  var status = "disconnected";
2016
- var brokerUrl = "";
2017
- function isMQTTConfigured() {
2018
- return client !== null;
2019
- }
2020
- function initMQTTClient(url) {
2021
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2022
- if (client) return;
2023
- brokerUrl = url;
2038
+ function initMQTTClient() {
2039
+ if (client || !isMQTTEnabled()) return;
2040
+ var url = buildMQTTBrokerUrl();
2024
2041
  status = "connecting";
2025
2042
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2026
2043
  try {
2027
2044
  client = import_mqtt.default.connect(url, {
2028
2045
  clientId: clientId,
2046
+ username: mqttConfig.username,
2047
+ password: mqttConfig.password,
2029
2048
  keepalive: 60,
2030
2049
  clean: true,
2031
2050
  reconnectPeriod: 5e3,
@@ -2059,13 +2078,22 @@ function initMQTTClient(url) {
2059
2078
  }
2060
2079
  });
2061
2080
  }
2081
+ function ensureMQTTClient() {
2082
+ if (isMQTTEnabled() && !client) {
2083
+ initMQTTClient();
2084
+ }
2085
+ }
2062
2086
  function publishMQTT(topic, payload) {
2087
+ if (!isMQTTEnabled()) {
2088
+ return false;
2089
+ }
2090
+ ensureMQTTClient();
2063
2091
  if (!client) {
2064
2092
  return false;
2065
2093
  }
2066
2094
  try {
2067
2095
  client.publish(topic, JSON.stringify(payload), {
2068
- qos: 1
2096
+ qos: mqttConfig.qos
2069
2097
  });
2070
2098
  return true;
2071
2099
  } catch (err) {
@@ -2078,7 +2106,6 @@ function disconnectMQTT() {
2078
2106
  client.end(true);
2079
2107
  client = null;
2080
2108
  status = "disconnected";
2081
- brokerUrl = "";
2082
2109
  }
2083
2110
  }
2084
2111
  // src/utils/tracking.ts
@@ -2300,171 +2327,91 @@ function getBrowserID(clientInfo) {
2300
2327
  });
2301
2328
  })();
2302
2329
  }
2303
- var mqttTopicPrefix = "adstorm";
2304
- function setMQTTTopicPrefix(prefix) {
2305
- mqttTopicPrefix = prefix || "adstorm";
2306
- }
2307
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2308
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2309
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2310
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2311
- function buildHeaders(licenseKey) {
2312
- var headers = {
2313
- "Content-Type": "application/json"
2314
- };
2315
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2316
- return headers;
2330
+ function canPublish(licenseKey) {
2331
+ return Boolean(isMQTTEnabled() && licenseKey);
2317
2332
  }
2318
- function postJson(url, licenseKey, body) {
2333
+ function buildPlayerMetricEvent() {
2319
2334
  return _async_to_generator(function() {
2320
- var response;
2321
- return _ts_generator(this, function(_state) {
2322
- switch(_state.label){
2323
- case 0:
2324
- return [
2325
- 4,
2326
- fetch(url, {
2327
- method: "POST",
2328
- headers: buildHeaders(licenseKey),
2329
- body: JSON.stringify(body)
2330
- })
2331
- ];
2332
- case 1:
2333
- response = _state.sent();
2334
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2335
- return [
2336
- 4,
2337
- response.json()
2338
- ];
2339
- case 2:
2340
- _state.sent();
2341
- return [
2342
- 2
2343
- ];
2344
- }
2345
- });
2346
- })();
2347
- }
2348
- function buildPlayerMetricEvent(_0) {
2349
- return _async_to_generator(function(licenseKey) {
2350
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2335
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2351
2336
  var _arguments = arguments;
2352
2337
  return _ts_generator(this, function(_state) {
2353
2338
  switch(_state.label){
2354
2339
  case 0:
2355
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2340
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2356
2341
  clientInfo = getClientInfo();
2357
2342
  return [
2358
2343
  4,
2359
2344
  getBrowserID(clientInfo)
2360
2345
  ];
2361
2346
  case 1:
2362
- browserId = _state.sent();
2347
+ playerId = _state.sent();
2363
2348
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2364
2349
  return [
2365
2350
  2,
2366
- {
2367
- player_id: browserId,
2368
- browserId: browserId,
2351
+ _object_spread({
2352
+ player_id: playerId,
2369
2353
  device_type: clientInfo.deviceType,
2370
- deviceType: clientInfo.deviceType,
2371
- input_stream_type: context.inputStreamType,
2372
- os: clientInfo.os,
2373
- ad_loaded: flags.adLoaded,
2374
- ad_detect: flags.adDetect,
2375
- license_key: licenseKey,
2376
- capture_at: captureAt,
2377
- timestamp: captureAt
2378
- }
2354
+ os: clientInfo.os.toLowerCase(),
2355
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
2356
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
2357
+ capture_at: captureAt
2358
+ }, context.inputStreamType ? {
2359
+ input_stream_type: context.inputStreamType
2360
+ } : {})
2379
2361
  ];
2380
2362
  }
2381
2363
  });
2382
2364
  }).apply(this, arguments);
2383
2365
  }
2384
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2385
- return _async_to_generator(function() {
2386
- return _ts_generator(this, function(_state) {
2387
- switch(_state.label){
2388
- case 0:
2389
- if (isMQTTConfigured()) {
2390
- publishMQTT(mqttTopic, body);
2391
- return [
2392
- 2
2393
- ];
2394
- }
2395
- return [
2396
- 4,
2397
- postJson(httpUrl, licenseKey, body)
2398
- ];
2399
- case 1:
2400
- _state.sent();
2401
- return [
2402
- 2
2403
- ];
2404
- }
2405
- });
2406
- })();
2366
+ function publishTracking(licenseKey, channel, body) {
2367
+ ensureMQTTClient();
2368
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2407
2369
  }
2408
2370
  function sendInitialTracking(_0) {
2409
2371
  return _async_to_generator(function(licenseKey) {
2410
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2372
+ var context, metricEvent, error;
2411
2373
  var _arguments = arguments;
2412
2374
  return _ts_generator(this, function(_state) {
2413
2375
  switch(_state.label){
2414
2376
  case 0:
2415
2377
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2378
+ if (!canPublish(licenseKey)) return [
2379
+ 2
2380
+ ];
2416
2381
  _state.label = 1;
2417
2382
  case 1:
2418
2383
  _state.trys.push([
2419
2384
  1,
2420
- 4,
2385
+ 3,
2421
2386
  ,
2422
- 5
2387
+ 4
2423
2388
  ]);
2424
- clientInfo = getClientInfo();
2425
2389
  return [
2426
2390
  4,
2427
- getBrowserID(clientInfo)
2391
+ buildPlayerMetricEvent(context, {
2392
+ adLoaded: false,
2393
+ adDetect: false
2394
+ })
2428
2395
  ];
2429
2396
  case 2:
2430
- browserId = _state.sent();
2431
- captureAt = /* @__PURE__ */ new Date().toISOString();
2432
- trackingData = _object_spread({
2433
- browserId: browserId
2434
- }, clientInfo);
2435
- metricsBody = {
2397
+ metricEvent = _state.sent();
2398
+ publishTracking(licenseKey, "metrics", {
2436
2399
  events: [
2437
- {
2438
- player_id: browserId,
2439
- device_type: clientInfo.deviceType,
2440
- input_stream_type: context.inputStreamType,
2441
- os: clientInfo.os,
2442
- ad_loaded: false,
2443
- ad_detect: false,
2444
- license_key: licenseKey,
2445
- capture_at: captureAt
2446
- }
2447
- ],
2448
- trackingData: trackingData
2449
- };
2450
- return [
2451
- 4,
2452
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2453
- ];
2454
- case 3:
2455
- _state.sent();
2400
+ metricEvent
2401
+ ]
2402
+ });
2456
2403
  return [
2457
2404
  3,
2458
- 5
2405
+ 4
2459
2406
  ];
2460
- case 4:
2407
+ case 3:
2461
2408
  error = _state.sent();
2462
2409
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2463
2410
  return [
2464
2411
  3,
2465
- 5
2412
+ 4
2466
2413
  ];
2467
- case 5:
2414
+ case 4:
2468
2415
  return [
2469
2416
  2
2470
2417
  ];
@@ -2562,61 +2509,54 @@ function sendAdLoadedTracking(_0, _1) {
2562
2509
  }
2563
2510
  function sendAdImpressionTracking(_0, _1) {
2564
2511
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2565
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2512
+ var context, metricEvent, error;
2566
2513
  var _arguments = arguments;
2567
2514
  return _ts_generator(this, function(_state) {
2568
2515
  switch(_state.label){
2569
2516
  case 0:
2570
2517
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2518
+ if (!canPublish(licenseKey)) return [
2519
+ 2
2520
+ ];
2571
2521
  _state.label = 1;
2572
2522
  case 1:
2573
2523
  _state.trys.push([
2574
2524
  1,
2575
- 4,
2525
+ 3,
2576
2526
  ,
2577
- 5
2527
+ 4
2578
2528
  ]);
2579
2529
  return [
2580
2530
  4,
2581
- buildPlayerMetricEvent(licenseKey, context, {
2531
+ buildPlayerMetricEvent(context, {
2582
2532
  captureAt: adImpressionInfo.timestamp
2583
2533
  })
2584
2534
  ];
2585
2535
  case 2:
2586
2536
  metricEvent = _state.sent();
2587
- heartbeatBody = metricEvent;
2588
- impressionsBody = {
2537
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2538
+ publishTracking(licenseKey, "impressions", {
2589
2539
  events: [
2590
2540
  {
2591
2541
  player_id: metricEvent.player_id,
2592
2542
  ad_played_count: 1,
2593
2543
  ad_url: adImpressionInfo.adUrl,
2594
- license_key: licenseKey,
2595
2544
  capture_at: adImpressionInfo.timestamp
2596
2545
  }
2597
2546
  ]
2598
- };
2599
- return [
2600
- 4,
2601
- Promise.all([
2602
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2603
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2604
- ])
2605
- ];
2606
- case 3:
2607
- _state.sent();
2547
+ });
2608
2548
  return [
2609
2549
  3,
2610
- 5
2550
+ 4
2611
2551
  ];
2612
- case 4:
2552
+ case 3:
2613
2553
  error = _state.sent();
2614
2554
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2615
2555
  return [
2616
2556
  3,
2617
- 5
2557
+ 4
2618
2558
  ];
2619
- case 5:
2559
+ case 4:
2620
2560
  return [
2621
2561
  2
2622
2562
  ];
@@ -2632,38 +2572,36 @@ function sendHeartbeat(_0) {
2632
2572
  switch(_state.label){
2633
2573
  case 0:
2634
2574
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2575
+ if (!canPublish(licenseKey)) return [
2576
+ 2
2577
+ ];
2635
2578
  _state.label = 1;
2636
2579
  case 1:
2637
2580
  _state.trys.push([
2638
2581
  1,
2639
- 4,
2582
+ 3,
2640
2583
  ,
2641
- 5
2584
+ 4
2642
2585
  ]);
2643
2586
  return [
2644
2587
  4,
2645
- buildPlayerMetricEvent(licenseKey, context, flags)
2588
+ buildPlayerMetricEvent(context, flags)
2646
2589
  ];
2647
2590
  case 2:
2648
2591
  heartbeatData = _state.sent();
2649
- return [
2650
- 4,
2651
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2652
- ];
2653
- case 3:
2654
- _state.sent();
2592
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2655
2593
  return [
2656
2594
  3,
2657
- 5
2595
+ 4
2658
2596
  ];
2659
- case 4:
2597
+ case 3:
2660
2598
  error = _state.sent();
2661
2599
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2662
2600
  return [
2663
2601
  3,
2664
- 5
2602
+ 4
2665
2603
  ];
2666
- case 5:
2604
+ case 4:
2667
2605
  return [
2668
2606
  2
2669
2607
  ];
@@ -3792,7 +3730,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3792
3730
  _this.adLayer.showPlaceholder();
3793
3731
  }
3794
3732
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
3795
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3733
+ _this.scheduleAdStopAtBreakBoundary();
3796
3734
  if (_this.config.debugAdTiming) {
3797
3735
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
3798
3736
  }
@@ -4226,12 +4164,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4226
4164
  if (marker.durationSeconds != null) {
4227
4165
  var newDurationMs = marker.durationSeconds * 1e3;
4228
4166
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4229
- this.expectedAdBreakDurationMs = newDurationMs;
4230
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4231
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4232
- this.scheduleAdStopCountdown(remainingMs);
4167
+ this.setAdBreakDurationBoundary(newDurationMs);
4168
+ this.scheduleAdStopAtBreakBoundary();
4233
4169
  if (this.config.debugAdTiming) {
4234
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4170
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4235
4171
  }
4236
4172
  }
4237
4173
  }
@@ -4239,8 +4175,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4239
4175
  }
4240
4176
  this.inAdBreak = true;
4241
4177
  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;
4242
- this.expectedAdBreakDurationMs = durationMs;
4243
- this.currentAdBreakStartWallClockMs = Date.now();
4178
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4179
+ this.setAdBreakDurationBoundary(durationMs);
4244
4180
  if (this.config.licenseKey) {
4245
4181
  var _this_pendingAdBreak1;
4246
4182
  var adDetectInfo = _object_spread({
@@ -4304,20 +4240,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4304
4240
  this.clearAdStartTimer();
4305
4241
  this.handleAdStart(marker);
4306
4242
  }
4307
- if (this.expectedAdBreakDurationMs != null) {
4308
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4309
- }
4243
+ this.scheduleAdStopAtBreakBoundary();
4310
4244
  return;
4311
4245
  }
4312
4246
  if (marker.type === "progress" && this.inAdBreak) {
4313
4247
  if (marker.durationSeconds != null) {
4314
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4315
- }
4316
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4317
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4318
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4319
- this.scheduleAdStopCountdown(remainingMs1);
4248
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4320
4249
  }
4250
+ this.scheduleAdStopAtBreakBoundary();
4321
4251
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4322
4252
  var bids = this.pendingNextAdBids;
4323
4253
  this.pendingNextAdBids = null;
@@ -4348,15 +4278,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4348
4278
  }
4349
4279
  return;
4350
4280
  }
4351
- if (adPlaying || remaining > 500) {
4352
- if (this.config.debugAdTiming) {
4353
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4354
- }
4355
- return;
4356
- }
4357
- this.inAdBreak = false;
4358
- this.expectedAdBreakDurationMs = void 0;
4359
- this.currentAdBreakStartWallClockMs = void 0;
4281
+ this.scteAdBreakEndWallClockMs = Date.now();
4360
4282
  this.clearAdStartTimer();
4361
4283
  this.clearAdStopTimer();
4362
4284
  if (adPlaying) {
@@ -4367,6 +4289,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4367
4289
  }
4368
4290
  }
4369
4291
  },
4292
+ {
4293
+ key: "resolveScteBreakStartWallClockMs",
4294
+ value: function resolveScteBreakStartWallClockMs(marker) {
4295
+ var nowWallClockMs = Date.now();
4296
+ if (typeof marker.ptsSeconds !== "number") {
4297
+ return nowWallClockMs;
4298
+ }
4299
+ var nowMs = this.video.currentTime * 1e3;
4300
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4301
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4302
+ if (!Number.isFinite(deltaMs)) {
4303
+ return nowWallClockMs;
4304
+ }
4305
+ return nowWallClockMs + Math.floor(deltaMs);
4306
+ }
4307
+ },
4308
+ {
4309
+ key: "setAdBreakDurationBoundary",
4310
+ value: function setAdBreakDurationBoundary(durationMs) {
4311
+ this.expectedAdBreakDurationMs = durationMs;
4312
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4313
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4314
+ } else {
4315
+ this.scteAdBreakEndWallClockMs = void 0;
4316
+ }
4317
+ }
4318
+ },
4319
+ {
4320
+ key: "scheduleAdStopAtBreakBoundary",
4321
+ value: function scheduleAdStopAtBreakBoundary() {
4322
+ if (this.expectedAdBreakDurationMs == null) {
4323
+ return;
4324
+ }
4325
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4326
+ }
4327
+ },
4370
4328
  {
4371
4329
  key: "parseCueOutDuration",
4372
4330
  value: function parseCueOutDuration(value) {
@@ -4577,10 +4535,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4577
4535
  key: "initializeTracking",
4578
4536
  value: function initializeTracking() {
4579
4537
  var _this = this;
4580
- if (this.config.mqttBrokerUrl) {
4581
- var topicPrefix = "adstorm";
4582
- setMQTTTopicPrefix(topicPrefix);
4583
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4538
+ if (isMQTTEnabled()) {
4539
+ initMQTTClient();
4584
4540
  }
4585
4541
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4586
4542
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5004,7 +4960,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5004
4960
  case 9:
5005
4961
  _state.sent();
5006
4962
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5007
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
4963
+ _this.scheduleAdStopAtBreakBoundary();
5008
4964
  }
5009
4965
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5010
4966
  return [
@@ -5109,7 +5065,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5109
5065
  case 19:
5110
5066
  _state.sent();
5111
5067
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5112
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5068
+ _this.scheduleAdStopAtBreakBoundary();
5113
5069
  }
5114
5070
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5115
5071
  _state.label = 20;
@@ -5192,7 +5148,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5192
5148
  key: "handleAdStart",
5193
5149
  value: function handleAdStart(_marker) {
5194
5150
  return _async_to_generator(function() {
5195
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5151
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5196
5152
  return _ts_generator(this, function(_state) {
5197
5153
  switch(_state.label){
5198
5154
  case 0:
@@ -5226,20 +5182,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5226
5182
  }
5227
5183
  }
5228
5184
  this.inAdBreak = true;
5229
- this.currentAdBreakStartWallClockMs = Date.now();
5230
5185
  this.currentAdIndex = 0;
5231
5186
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5232
5187
  this.adPodQueue = [];
5233
5188
  if (!this.config.disableFiller) this.showAds = true;
5234
- if (adBreakDurationMs != null) {
5235
- this.startFillerBreakTimer(adBreakDurationMs);
5189
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5190
+ this.currentAdBreakStartWallClockMs = Date.now();
5191
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5192
+ }
5193
+ fillerRemainingMs = this.getRemainingAdMs();
5194
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5195
+ this.startFillerBreakTimer(fillerRemainingMs);
5236
5196
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5237
5197
  this.showPlaceholderLayer();
5238
5198
  }
5239
5199
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5240
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5241
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5242
- }
5243
5200
  this.clearPendingAdBreak();
5244
5201
  adBreakToken = Date.now();
5245
5202
  this.activeAdRequestToken = adBreakToken;
@@ -5276,7 +5233,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5276
5233
  case 2:
5277
5234
  _state.sent();
5278
5235
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5279
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5236
+ this.scheduleAdStopAtBreakBoundary();
5280
5237
  }
5281
5238
  this.adLayer.setAdVolume(adVolume);
5282
5239
  return [
@@ -5462,7 +5419,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5462
5419
  case 4:
5463
5420
  _state.sent();
5464
5421
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5465
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5422
+ this.scheduleAdStopAtBreakBoundary();
5466
5423
  }
5467
5424
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5468
5425
  _state.label = 5;
@@ -5704,21 +5661,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5704
5661
  {
5705
5662
  key: "ensureAdStoppedByTimer",
5706
5663
  value: function ensureAdStoppedByTimer() {
5707
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5664
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5708
5665
  if (!this.inAdBreak) return;
5709
5666
  this.adStopTimerId = void 0;
5710
5667
  var adPlaying = this.adLayer.isAdPlaying();
5711
- var pendingAds = this.adPodQueue.length > 0;
5712
5668
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5713
5669
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5714
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5715
- var elapsedSinceStartMs = 0;
5716
- if (this.currentAdBreakStartWallClockMs != null) {
5717
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5718
- }
5719
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5720
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5721
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5670
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5671
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5672
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5673
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5722
5674
  if (shouldExtendAdBreak) {
5723
5675
  this.scheduleAdStopCountdown(checkIntervalMs);
5724
5676
  return;
@@ -5792,6 +5744,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5792
5744
  this.inAdBreak = false;
5793
5745
  this.expectedAdBreakDurationMs = void 0;
5794
5746
  this.currentAdBreakStartWallClockMs = void 0;
5747
+ this.scteAdBreakEndWallClockMs = void 0;
5795
5748
  this.clearAdStartTimer();
5796
5749
  this.clearAdStopTimer();
5797
5750
  this.adPodQueue = [];
@@ -6053,10 +6006,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6053
6006
  {
6054
6007
  key: "getRemainingAdMs",
6055
6008
  value: function getRemainingAdMs() {
6009
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6010
+ if (endWallClockMs != null) {
6011
+ return Math.max(0, endWallClockMs - Date.now());
6012
+ }
6056
6013
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6057
6014
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6058
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6059
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6015
+ return 0;
6016
+ }
6017
+ },
6018
+ {
6019
+ key: "getAdBreakEndWallClockMs",
6020
+ value: function getAdBreakEndWallClockMs() {
6021
+ if (this.scteAdBreakEndWallClockMs != null) {
6022
+ return this.scteAdBreakEndWallClockMs;
6023
+ }
6024
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6025
+ return void 0;
6026
+ }
6027
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6060
6028
  }
6061
6029
  },
6062
6030
  {
@@ -6261,7 +6229,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6261
6229
  clearInterval(this.heartbeatInterval);
6262
6230
  this.heartbeatInterval = void 0;
6263
6231
  }
6264
- if (this.config.mqttBrokerUrl) {
6232
+ if (isMQTTEnabled()) {
6265
6233
  disconnectMQTT();
6266
6234
  }
6267
6235
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();