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.
@@ -2046,24 +2046,43 @@ function createVastAdLayer(contentVideo, options) {
2046
2046
  }
2047
2047
  };
2048
2048
  }
2049
+ // src/utils/mqttConfig.ts
2050
+ var DEFAULT_MQTT_CONFIG = {
2051
+ enabled: true,
2052
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2053
+ brokerPort: 8883,
2054
+ wsPort: 8084,
2055
+ username: "for-sonifi",
2056
+ password: "sonifi-mqtt",
2057
+ topicPrefix: "adstorm/players",
2058
+ qos: 1
2059
+ };
2060
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2061
+ function isMQTTEnabled() {
2062
+ return mqttConfig.enabled;
2063
+ }
2064
+ function buildMQTTBrokerUrl() {
2065
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2066
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2067
+ }
2068
+ function buildPlayerTopic(licenseKey, channel) {
2069
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2070
+ }
2049
2071
  // src/utils/mqttClient.ts
2050
2072
  var import_mqtt = __toESM(require("mqtt"), 1);
2051
2073
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2052
2074
  var client = null;
2053
2075
  var status = "disconnected";
2054
- var brokerUrl = "";
2055
- function isMQTTConfigured() {
2056
- return client !== null;
2057
- }
2058
- function initMQTTClient(url) {
2059
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2060
- if (client) return;
2061
- brokerUrl = url;
2076
+ function initMQTTClient() {
2077
+ if (client || !isMQTTEnabled()) return;
2078
+ var url = buildMQTTBrokerUrl();
2062
2079
  status = "connecting";
2063
2080
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2064
2081
  try {
2065
2082
  client = import_mqtt.default.connect(url, {
2066
2083
  clientId: clientId,
2084
+ username: mqttConfig.username,
2085
+ password: mqttConfig.password,
2067
2086
  keepalive: 60,
2068
2087
  clean: true,
2069
2088
  reconnectPeriod: 5e3,
@@ -2097,13 +2116,22 @@ function initMQTTClient(url) {
2097
2116
  }
2098
2117
  });
2099
2118
  }
2119
+ function ensureMQTTClient() {
2120
+ if (isMQTTEnabled() && !client) {
2121
+ initMQTTClient();
2122
+ }
2123
+ }
2100
2124
  function publishMQTT(topic, payload) {
2125
+ if (!isMQTTEnabled()) {
2126
+ return false;
2127
+ }
2128
+ ensureMQTTClient();
2101
2129
  if (!client) {
2102
2130
  return false;
2103
2131
  }
2104
2132
  try {
2105
2133
  client.publish(topic, JSON.stringify(payload), {
2106
- qos: 1
2134
+ qos: mqttConfig.qos
2107
2135
  });
2108
2136
  return true;
2109
2137
  } catch (err) {
@@ -2116,7 +2144,6 @@ function disconnectMQTT() {
2116
2144
  client.end(true);
2117
2145
  client = null;
2118
2146
  status = "disconnected";
2119
- brokerUrl = "";
2120
2147
  }
2121
2148
  }
2122
2149
  // src/utils/tracking.ts
@@ -2338,171 +2365,91 @@ function getBrowserID(clientInfo) {
2338
2365
  });
2339
2366
  })();
2340
2367
  }
2341
- var mqttTopicPrefix = "adstorm";
2342
- function setMQTTTopicPrefix(prefix) {
2343
- mqttTopicPrefix = prefix || "adstorm";
2344
- }
2345
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2346
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2347
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2348
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2349
- function buildHeaders(licenseKey) {
2350
- var headers = {
2351
- "Content-Type": "application/json"
2352
- };
2353
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2354
- return headers;
2368
+ function canPublish(licenseKey) {
2369
+ return Boolean(isMQTTEnabled() && licenseKey);
2355
2370
  }
2356
- function postJson(url, licenseKey, body) {
2371
+ function buildPlayerMetricEvent() {
2357
2372
  return _async_to_generator(function() {
2358
- var response;
2359
- return _ts_generator(this, function(_state) {
2360
- switch(_state.label){
2361
- case 0:
2362
- return [
2363
- 4,
2364
- fetch(url, {
2365
- method: "POST",
2366
- headers: buildHeaders(licenseKey),
2367
- body: JSON.stringify(body)
2368
- })
2369
- ];
2370
- case 1:
2371
- response = _state.sent();
2372
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2373
- return [
2374
- 4,
2375
- response.json()
2376
- ];
2377
- case 2:
2378
- _state.sent();
2379
- return [
2380
- 2
2381
- ];
2382
- }
2383
- });
2384
- })();
2385
- }
2386
- function buildPlayerMetricEvent(_0) {
2387
- return _async_to_generator(function(licenseKey) {
2388
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2373
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2389
2374
  var _arguments = arguments;
2390
2375
  return _ts_generator(this, function(_state) {
2391
2376
  switch(_state.label){
2392
2377
  case 0:
2393
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2378
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2394
2379
  clientInfo = getClientInfo();
2395
2380
  return [
2396
2381
  4,
2397
2382
  getBrowserID(clientInfo)
2398
2383
  ];
2399
2384
  case 1:
2400
- browserId = _state.sent();
2385
+ playerId = _state.sent();
2401
2386
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2402
2387
  return [
2403
2388
  2,
2404
- {
2405
- player_id: browserId,
2406
- browserId: browserId,
2389
+ _object_spread({
2390
+ player_id: playerId,
2407
2391
  device_type: clientInfo.deviceType,
2408
- deviceType: clientInfo.deviceType,
2409
- input_stream_type: context.inputStreamType,
2410
- os: clientInfo.os,
2411
- ad_loaded: flags.adLoaded,
2412
- ad_detect: flags.adDetect,
2413
- license_key: licenseKey,
2414
- capture_at: captureAt,
2415
- timestamp: captureAt
2416
- }
2392
+ os: clientInfo.os.toLowerCase(),
2393
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
2394
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
2395
+ capture_at: captureAt
2396
+ }, context.inputStreamType ? {
2397
+ input_stream_type: context.inputStreamType
2398
+ } : {})
2417
2399
  ];
2418
2400
  }
2419
2401
  });
2420
2402
  }).apply(this, arguments);
2421
2403
  }
2422
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2423
- return _async_to_generator(function() {
2424
- return _ts_generator(this, function(_state) {
2425
- switch(_state.label){
2426
- case 0:
2427
- if (isMQTTConfigured()) {
2428
- publishMQTT(mqttTopic, body);
2429
- return [
2430
- 2
2431
- ];
2432
- }
2433
- return [
2434
- 4,
2435
- postJson(httpUrl, licenseKey, body)
2436
- ];
2437
- case 1:
2438
- _state.sent();
2439
- return [
2440
- 2
2441
- ];
2442
- }
2443
- });
2444
- })();
2404
+ function publishTracking(licenseKey, channel, body) {
2405
+ ensureMQTTClient();
2406
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2445
2407
  }
2446
2408
  function sendInitialTracking(_0) {
2447
2409
  return _async_to_generator(function(licenseKey) {
2448
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2410
+ var context, metricEvent, error;
2449
2411
  var _arguments = arguments;
2450
2412
  return _ts_generator(this, function(_state) {
2451
2413
  switch(_state.label){
2452
2414
  case 0:
2453
2415
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2416
+ if (!canPublish(licenseKey)) return [
2417
+ 2
2418
+ ];
2454
2419
  _state.label = 1;
2455
2420
  case 1:
2456
2421
  _state.trys.push([
2457
2422
  1,
2458
- 4,
2423
+ 3,
2459
2424
  ,
2460
- 5
2425
+ 4
2461
2426
  ]);
2462
- clientInfo = getClientInfo();
2463
2427
  return [
2464
2428
  4,
2465
- getBrowserID(clientInfo)
2429
+ buildPlayerMetricEvent(context, {
2430
+ adLoaded: false,
2431
+ adDetect: false
2432
+ })
2466
2433
  ];
2467
2434
  case 2:
2468
- browserId = _state.sent();
2469
- captureAt = /* @__PURE__ */ new Date().toISOString();
2470
- trackingData = _object_spread({
2471
- browserId: browserId
2472
- }, clientInfo);
2473
- metricsBody = {
2435
+ metricEvent = _state.sent();
2436
+ publishTracking(licenseKey, "metrics", {
2474
2437
  events: [
2475
- {
2476
- player_id: browserId,
2477
- device_type: clientInfo.deviceType,
2478
- input_stream_type: context.inputStreamType,
2479
- os: clientInfo.os,
2480
- ad_loaded: false,
2481
- ad_detect: false,
2482
- license_key: licenseKey,
2483
- capture_at: captureAt
2484
- }
2485
- ],
2486
- trackingData: trackingData
2487
- };
2488
- return [
2489
- 4,
2490
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2491
- ];
2492
- case 3:
2493
- _state.sent();
2438
+ metricEvent
2439
+ ]
2440
+ });
2494
2441
  return [
2495
2442
  3,
2496
- 5
2443
+ 4
2497
2444
  ];
2498
- case 4:
2445
+ case 3:
2499
2446
  error = _state.sent();
2500
2447
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2501
2448
  return [
2502
2449
  3,
2503
- 5
2450
+ 4
2504
2451
  ];
2505
- case 5:
2452
+ case 4:
2506
2453
  return [
2507
2454
  2
2508
2455
  ];
@@ -2600,61 +2547,54 @@ function sendAdLoadedTracking(_0, _1) {
2600
2547
  }
2601
2548
  function sendAdImpressionTracking(_0, _1) {
2602
2549
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2603
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2550
+ var context, metricEvent, error;
2604
2551
  var _arguments = arguments;
2605
2552
  return _ts_generator(this, function(_state) {
2606
2553
  switch(_state.label){
2607
2554
  case 0:
2608
2555
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2556
+ if (!canPublish(licenseKey)) return [
2557
+ 2
2558
+ ];
2609
2559
  _state.label = 1;
2610
2560
  case 1:
2611
2561
  _state.trys.push([
2612
2562
  1,
2613
- 4,
2563
+ 3,
2614
2564
  ,
2615
- 5
2565
+ 4
2616
2566
  ]);
2617
2567
  return [
2618
2568
  4,
2619
- buildPlayerMetricEvent(licenseKey, context, {
2569
+ buildPlayerMetricEvent(context, {
2620
2570
  captureAt: adImpressionInfo.timestamp
2621
2571
  })
2622
2572
  ];
2623
2573
  case 2:
2624
2574
  metricEvent = _state.sent();
2625
- heartbeatBody = metricEvent;
2626
- impressionsBody = {
2575
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2576
+ publishTracking(licenseKey, "impressions", {
2627
2577
  events: [
2628
2578
  {
2629
2579
  player_id: metricEvent.player_id,
2630
2580
  ad_played_count: 1,
2631
2581
  ad_url: adImpressionInfo.adUrl,
2632
- license_key: licenseKey,
2633
2582
  capture_at: adImpressionInfo.timestamp
2634
2583
  }
2635
2584
  ]
2636
- };
2637
- return [
2638
- 4,
2639
- Promise.all([
2640
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2641
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2642
- ])
2643
- ];
2644
- case 3:
2645
- _state.sent();
2585
+ });
2646
2586
  return [
2647
2587
  3,
2648
- 5
2588
+ 4
2649
2589
  ];
2650
- case 4:
2590
+ case 3:
2651
2591
  error = _state.sent();
2652
2592
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2653
2593
  return [
2654
2594
  3,
2655
- 5
2595
+ 4
2656
2596
  ];
2657
- case 5:
2597
+ case 4:
2658
2598
  return [
2659
2599
  2
2660
2600
  ];
@@ -2670,38 +2610,36 @@ function sendHeartbeat(_0) {
2670
2610
  switch(_state.label){
2671
2611
  case 0:
2672
2612
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2613
+ if (!canPublish(licenseKey)) return [
2614
+ 2
2615
+ ];
2673
2616
  _state.label = 1;
2674
2617
  case 1:
2675
2618
  _state.trys.push([
2676
2619
  1,
2677
- 4,
2620
+ 3,
2678
2621
  ,
2679
- 5
2622
+ 4
2680
2623
  ]);
2681
2624
  return [
2682
2625
  4,
2683
- buildPlayerMetricEvent(licenseKey, context, flags)
2626
+ buildPlayerMetricEvent(context, flags)
2684
2627
  ];
2685
2628
  case 2:
2686
2629
  heartbeatData = _state.sent();
2687
- return [
2688
- 4,
2689
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2690
- ];
2691
- case 3:
2692
- _state.sent();
2630
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2693
2631
  return [
2694
2632
  3,
2695
- 5
2633
+ 4
2696
2634
  ];
2697
- case 4:
2635
+ case 3:
2698
2636
  error = _state.sent();
2699
2637
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2700
2638
  return [
2701
2639
  3,
2702
- 5
2640
+ 4
2703
2641
  ];
2704
- case 5:
2642
+ case 4:
2705
2643
  return [
2706
2644
  2
2707
2645
  ];
@@ -3830,7 +3768,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3830
3768
  _this.adLayer.showPlaceholder();
3831
3769
  }
3832
3770
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
3833
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3771
+ _this.scheduleAdStopAtBreakBoundary();
3834
3772
  if (_this.config.debugAdTiming) {
3835
3773
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
3836
3774
  }
@@ -4264,12 +4202,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4264
4202
  if (marker.durationSeconds != null) {
4265
4203
  var newDurationMs = marker.durationSeconds * 1e3;
4266
4204
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4267
- this.expectedAdBreakDurationMs = newDurationMs;
4268
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4269
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4270
- this.scheduleAdStopCountdown(remainingMs);
4205
+ this.setAdBreakDurationBoundary(newDurationMs);
4206
+ this.scheduleAdStopAtBreakBoundary();
4271
4207
  if (this.config.debugAdTiming) {
4272
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4208
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4273
4209
  }
4274
4210
  }
4275
4211
  }
@@ -4277,8 +4213,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4277
4213
  }
4278
4214
  this.inAdBreak = true;
4279
4215
  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;
4280
- this.expectedAdBreakDurationMs = durationMs;
4281
- this.currentAdBreakStartWallClockMs = Date.now();
4216
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4217
+ this.setAdBreakDurationBoundary(durationMs);
4282
4218
  if (this.config.licenseKey) {
4283
4219
  var _this_pendingAdBreak1;
4284
4220
  var adDetectInfo = _object_spread({
@@ -4342,20 +4278,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4342
4278
  this.clearAdStartTimer();
4343
4279
  this.handleAdStart(marker);
4344
4280
  }
4345
- if (this.expectedAdBreakDurationMs != null) {
4346
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4347
- }
4281
+ this.scheduleAdStopAtBreakBoundary();
4348
4282
  return;
4349
4283
  }
4350
4284
  if (marker.type === "progress" && this.inAdBreak) {
4351
4285
  if (marker.durationSeconds != null) {
4352
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4353
- }
4354
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4355
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4356
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4357
- this.scheduleAdStopCountdown(remainingMs1);
4286
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4358
4287
  }
4288
+ this.scheduleAdStopAtBreakBoundary();
4359
4289
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4360
4290
  var bids = this.pendingNextAdBids;
4361
4291
  this.pendingNextAdBids = null;
@@ -4386,15 +4316,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4386
4316
  }
4387
4317
  return;
4388
4318
  }
4389
- if (adPlaying || remaining > 500) {
4390
- if (this.config.debugAdTiming) {
4391
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4392
- }
4393
- return;
4394
- }
4395
- this.inAdBreak = false;
4396
- this.expectedAdBreakDurationMs = void 0;
4397
- this.currentAdBreakStartWallClockMs = void 0;
4319
+ this.scteAdBreakEndWallClockMs = Date.now();
4398
4320
  this.clearAdStartTimer();
4399
4321
  this.clearAdStopTimer();
4400
4322
  if (adPlaying) {
@@ -4405,6 +4327,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4405
4327
  }
4406
4328
  }
4407
4329
  },
4330
+ {
4331
+ key: "resolveScteBreakStartWallClockMs",
4332
+ value: function resolveScteBreakStartWallClockMs(marker) {
4333
+ var nowWallClockMs = Date.now();
4334
+ if (typeof marker.ptsSeconds !== "number") {
4335
+ return nowWallClockMs;
4336
+ }
4337
+ var nowMs = this.video.currentTime * 1e3;
4338
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4339
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4340
+ if (!Number.isFinite(deltaMs)) {
4341
+ return nowWallClockMs;
4342
+ }
4343
+ return nowWallClockMs + Math.floor(deltaMs);
4344
+ }
4345
+ },
4346
+ {
4347
+ key: "setAdBreakDurationBoundary",
4348
+ value: function setAdBreakDurationBoundary(durationMs) {
4349
+ this.expectedAdBreakDurationMs = durationMs;
4350
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4351
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4352
+ } else {
4353
+ this.scteAdBreakEndWallClockMs = void 0;
4354
+ }
4355
+ }
4356
+ },
4357
+ {
4358
+ key: "scheduleAdStopAtBreakBoundary",
4359
+ value: function scheduleAdStopAtBreakBoundary() {
4360
+ if (this.expectedAdBreakDurationMs == null) {
4361
+ return;
4362
+ }
4363
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4364
+ }
4365
+ },
4408
4366
  {
4409
4367
  key: "parseCueOutDuration",
4410
4368
  value: function parseCueOutDuration(value) {
@@ -4615,10 +4573,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4615
4573
  key: "initializeTracking",
4616
4574
  value: function initializeTracking() {
4617
4575
  var _this = this;
4618
- if (this.config.mqttBrokerUrl) {
4619
- var topicPrefix = "adstorm";
4620
- setMQTTTopicPrefix(topicPrefix);
4621
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4576
+ if (isMQTTEnabled()) {
4577
+ initMQTTClient();
4622
4578
  }
4623
4579
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4624
4580
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5042,7 +4998,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5042
4998
  case 9:
5043
4999
  _state.sent();
5044
5000
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5045
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5001
+ _this.scheduleAdStopAtBreakBoundary();
5046
5002
  }
5047
5003
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5048
5004
  return [
@@ -5147,7 +5103,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5147
5103
  case 19:
5148
5104
  _state.sent();
5149
5105
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5150
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5106
+ _this.scheduleAdStopAtBreakBoundary();
5151
5107
  }
5152
5108
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5153
5109
  _state.label = 20;
@@ -5230,7 +5186,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5230
5186
  key: "handleAdStart",
5231
5187
  value: function handleAdStart(_marker) {
5232
5188
  return _async_to_generator(function() {
5233
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5189
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5234
5190
  return _ts_generator(this, function(_state) {
5235
5191
  switch(_state.label){
5236
5192
  case 0:
@@ -5264,20 +5220,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5264
5220
  }
5265
5221
  }
5266
5222
  this.inAdBreak = true;
5267
- this.currentAdBreakStartWallClockMs = Date.now();
5268
5223
  this.currentAdIndex = 0;
5269
5224
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5270
5225
  this.adPodQueue = [];
5271
5226
  if (!this.config.disableFiller) this.showAds = true;
5272
- if (adBreakDurationMs != null) {
5273
- this.startFillerBreakTimer(adBreakDurationMs);
5227
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5228
+ this.currentAdBreakStartWallClockMs = Date.now();
5229
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5230
+ }
5231
+ fillerRemainingMs = this.getRemainingAdMs();
5232
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5233
+ this.startFillerBreakTimer(fillerRemainingMs);
5274
5234
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5275
5235
  this.showPlaceholderLayer();
5276
5236
  }
5277
5237
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5278
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5279
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5280
- }
5281
5238
  this.clearPendingAdBreak();
5282
5239
  adBreakToken = Date.now();
5283
5240
  this.activeAdRequestToken = adBreakToken;
@@ -5314,7 +5271,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5314
5271
  case 2:
5315
5272
  _state.sent();
5316
5273
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5317
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5274
+ this.scheduleAdStopAtBreakBoundary();
5318
5275
  }
5319
5276
  this.adLayer.setAdVolume(adVolume);
5320
5277
  return [
@@ -5500,7 +5457,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5500
5457
  case 4:
5501
5458
  _state.sent();
5502
5459
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5503
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5460
+ this.scheduleAdStopAtBreakBoundary();
5504
5461
  }
5505
5462
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5506
5463
  _state.label = 5;
@@ -5742,21 +5699,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5742
5699
  {
5743
5700
  key: "ensureAdStoppedByTimer",
5744
5701
  value: function ensureAdStoppedByTimer() {
5745
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5702
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5746
5703
  if (!this.inAdBreak) return;
5747
5704
  this.adStopTimerId = void 0;
5748
5705
  var adPlaying = this.adLayer.isAdPlaying();
5749
- var pendingAds = this.adPodQueue.length > 0;
5750
5706
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5751
5707
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5752
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5753
- var elapsedSinceStartMs = 0;
5754
- if (this.currentAdBreakStartWallClockMs != null) {
5755
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5756
- }
5757
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5758
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5759
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5708
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5709
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5710
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5711
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5760
5712
  if (shouldExtendAdBreak) {
5761
5713
  this.scheduleAdStopCountdown(checkIntervalMs);
5762
5714
  return;
@@ -5830,6 +5782,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5830
5782
  this.inAdBreak = false;
5831
5783
  this.expectedAdBreakDurationMs = void 0;
5832
5784
  this.currentAdBreakStartWallClockMs = void 0;
5785
+ this.scteAdBreakEndWallClockMs = void 0;
5833
5786
  this.clearAdStartTimer();
5834
5787
  this.clearAdStopTimer();
5835
5788
  this.adPodQueue = [];
@@ -6091,10 +6044,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6091
6044
  {
6092
6045
  key: "getRemainingAdMs",
6093
6046
  value: function getRemainingAdMs() {
6047
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6048
+ if (endWallClockMs != null) {
6049
+ return Math.max(0, endWallClockMs - Date.now());
6050
+ }
6094
6051
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6095
6052
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6096
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6097
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6053
+ return 0;
6054
+ }
6055
+ },
6056
+ {
6057
+ key: "getAdBreakEndWallClockMs",
6058
+ value: function getAdBreakEndWallClockMs() {
6059
+ if (this.scteAdBreakEndWallClockMs != null) {
6060
+ return this.scteAdBreakEndWallClockMs;
6061
+ }
6062
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6063
+ return void 0;
6064
+ }
6065
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6098
6066
  }
6099
6067
  },
6100
6068
  {
@@ -6299,7 +6267,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6299
6267
  clearInterval(this.heartbeatInterval);
6300
6268
  this.heartbeatInterval = void 0;
6301
6269
  }
6302
- if (this.config.mqttBrokerUrl) {
6270
+ if (isMQTTEnabled()) {
6303
6271
  disconnectMQTT();
6304
6272
  }
6305
6273
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();
@@ -6323,7 +6291,7 @@ var CRITICAL_PROPS = [
6323
6291
  var CONTROLS_HIDE_DELAY = 3e3;
6324
6292
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
6325
6293
  var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6326
- 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, [
6294
+ 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, [
6327
6295
  "src",
6328
6296
  "autoplay",
6329
6297
  "muted",
@@ -6350,8 +6318,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6350
6318
  "licenseKey",
6351
6319
  "minSegmentsBeforePlay",
6352
6320
  "disableAds",
6353
- "disableFiller",
6354
- "mqttBrokerUrl"
6321
+ "disableFiller"
6355
6322
  ]);
6356
6323
  var videoRef = (0, import_react.useRef)(null);
6357
6324
  var playerRef = (0, import_react.useRef)(null);
@@ -6511,7 +6478,6 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
6511
6478
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;
6512
6479
  if (disableAds !== void 0) cfg.disableAds = disableAds;
6513
6480
  cfg.disableFiller = disableFiller !== null && disableFiller !== void 0 ? disableFiller : true;
6514
- if (mqttBrokerUrl !== void 0) cfg.mqttBrokerUrl = mqttBrokerUrl;
6515
6481
  var player = new StormcloudVideoPlayer(cfg);
6516
6482
  playerRef.current = player;
6517
6483
  player.load().then(function() {