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.js CHANGED
@@ -2030,12 +2030,36 @@ function createVastAdLayer(contentVideo, options) {
2030
2030
  }
2031
2031
  };
2032
2032
  }
2033
+ // src/utils/mqttConfig.ts
2034
+ var DEFAULT_MQTT_CONFIG = {
2035
+ enabled: true,
2036
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2037
+ brokerPort: 8883,
2038
+ wsPort: 8084,
2039
+ username: "for-sonifi",
2040
+ password: "sonifi-mqtt",
2041
+ topicPrefix: "adstorm/players",
2042
+ qos: 1
2043
+ };
2044
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2045
+ function applyMQTTConfig(overrides) {
2046
+ Object.assign(mqttConfig, overrides);
2047
+ }
2048
+ function isMQTTEnabled() {
2049
+ return mqttConfig.enabled;
2050
+ }
2051
+ function buildMQTTBrokerUrl() {
2052
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2053
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2054
+ }
2055
+ function buildPlayerTopic(licenseKey, channel) {
2056
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2057
+ }
2033
2058
  // src/utils/mqttClient.ts
2034
2059
  import mqtt from "mqtt";
2035
2060
  var LOG2 = "[StormcloudVideoPlayer][MQTT]";
2036
2061
  var client = null;
2037
2062
  var status = "disconnected";
2038
- var brokerUrl = "";
2039
2063
  function getMQTTStatus() {
2040
2064
  return status;
2041
2065
  }
@@ -2043,17 +2067,22 @@ function isMQTTConnected() {
2043
2067
  return status === "connected" && client !== null && client.connected;
2044
2068
  }
2045
2069
  function isMQTTConfigured() {
2046
- return client !== null;
2070
+ return isMQTTEnabled();
2047
2071
  }
2048
- function initMQTTClient(url) {
2049
- var _topicPrefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "adstorm";
2050
- if (client) return;
2051
- brokerUrl = url;
2072
+ function configureMQTT(overrides) {
2073
+ applyMQTTConfig(overrides);
2074
+ disconnectMQTT();
2075
+ }
2076
+ function initMQTTClient() {
2077
+ if (client || !isMQTTEnabled()) return;
2078
+ var url = buildMQTTBrokerUrl();
2052
2079
  status = "connecting";
2053
2080
  var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2054
2081
  try {
2055
2082
  client = mqtt.connect(url, {
2056
2083
  clientId: clientId,
2084
+ username: mqttConfig.username,
2085
+ password: mqttConfig.password,
2057
2086
  keepalive: 60,
2058
2087
  clean: true,
2059
2088
  reconnectPeriod: 5e3,
@@ -2087,13 +2116,22 @@ function initMQTTClient(url) {
2087
2116
  }
2088
2117
  });
2089
2118
  }
2119
+ function ensureMQTTClient() {
2120
+ if (isMQTTEnabled() && !client) {
2121
+ initMQTTClient();
2122
+ }
2123
+ }
2090
2124
  function publishMQTT(topic, payload) {
2125
+ if (!isMQTTEnabled()) {
2126
+ return false;
2127
+ }
2128
+ ensureMQTTClient();
2091
2129
  if (!client) {
2092
2130
  return false;
2093
2131
  }
2094
2132
  try {
2095
2133
  client.publish(topic, JSON.stringify(payload), {
2096
- qos: 1
2134
+ qos: mqttConfig.qos
2097
2135
  });
2098
2136
  return true;
2099
2137
  } catch (err) {
@@ -2106,7 +2144,6 @@ function disconnectMQTT() {
2106
2144
  client.end(true);
2107
2145
  client = null;
2108
2146
  status = "disconnected";
2109
- brokerUrl = "";
2110
2147
  }
2111
2148
  }
2112
2149
  // src/utils/tracking.ts
@@ -2328,171 +2365,91 @@ function getBrowserID(clientInfo) {
2328
2365
  });
2329
2366
  })();
2330
2367
  }
2331
- var mqttTopicPrefix = "adstorm";
2332
- function setMQTTTopicPrefix(prefix) {
2333
- mqttTopicPrefix = prefix || "adstorm";
2368
+ function canPublish(licenseKey) {
2369
+ return Boolean(isMQTTEnabled() && licenseKey);
2334
2370
  }
2335
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2336
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2337
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2338
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2339
- function buildHeaders(licenseKey) {
2340
- var headers = {
2341
- "Content-Type": "application/json"
2342
- };
2343
- if (licenseKey) headers["Authorization"] = "Bearer ".concat(licenseKey);
2344
- return headers;
2345
- }
2346
- function postJson(url, licenseKey, body) {
2371
+ function buildPlayerMetricEvent() {
2347
2372
  return _async_to_generator(function() {
2348
- var response;
2349
- return _ts_generator(this, function(_state) {
2350
- switch(_state.label){
2351
- case 0:
2352
- return [
2353
- 4,
2354
- fetch(url, {
2355
- method: "POST",
2356
- headers: buildHeaders(licenseKey),
2357
- body: JSON.stringify(body)
2358
- })
2359
- ];
2360
- case 1:
2361
- response = _state.sent();
2362
- if (!response.ok) throw new Error("HTTP error! status: ".concat(response.status));
2363
- return [
2364
- 4,
2365
- response.json()
2366
- ];
2367
- case 2:
2368
- _state.sent();
2369
- return [
2370
- 2
2371
- ];
2372
- }
2373
- });
2374
- })();
2375
- }
2376
- function buildPlayerMetricEvent(_0) {
2377
- return _async_to_generator(function(licenseKey) {
2378
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
2373
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2379
2374
  var _arguments = arguments;
2380
2375
  return _ts_generator(this, function(_state) {
2381
2376
  switch(_state.label){
2382
2377
  case 0:
2383
- 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] : {};
2384
2379
  clientInfo = getClientInfo();
2385
2380
  return [
2386
2381
  4,
2387
2382
  getBrowserID(clientInfo)
2388
2383
  ];
2389
2384
  case 1:
2390
- browserId = _state.sent();
2385
+ playerId = _state.sent();
2391
2386
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2392
2387
  return [
2393
2388
  2,
2394
- {
2395
- player_id: browserId,
2396
- browserId: browserId,
2389
+ _object_spread({
2390
+ player_id: playerId,
2397
2391
  device_type: clientInfo.deviceType,
2398
- deviceType: clientInfo.deviceType,
2399
- input_stream_type: context.inputStreamType,
2400
- os: clientInfo.os,
2401
- ad_loaded: flags.adLoaded,
2402
- ad_detect: flags.adDetect,
2403
- license_key: licenseKey,
2404
- capture_at: captureAt,
2405
- timestamp: captureAt
2406
- }
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
+ } : {})
2407
2399
  ];
2408
2400
  }
2409
2401
  });
2410
2402
  }).apply(this, arguments);
2411
2403
  }
2412
- function publishOrPost(mqttTopic, httpUrl, licenseKey, body) {
2413
- return _async_to_generator(function() {
2414
- return _ts_generator(this, function(_state) {
2415
- switch(_state.label){
2416
- case 0:
2417
- if (isMQTTConfigured()) {
2418
- publishMQTT(mqttTopic, body);
2419
- return [
2420
- 2
2421
- ];
2422
- }
2423
- return [
2424
- 4,
2425
- postJson(httpUrl, licenseKey, body)
2426
- ];
2427
- case 1:
2428
- _state.sent();
2429
- return [
2430
- 2
2431
- ];
2432
- }
2433
- });
2434
- })();
2404
+ function publishTracking(licenseKey, channel, body) {
2405
+ ensureMQTTClient();
2406
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
2435
2407
  }
2436
2408
  function sendInitialTracking(_0) {
2437
2409
  return _async_to_generator(function(licenseKey) {
2438
- var context, clientInfo, browserId, captureAt, trackingData, metricsBody, error;
2410
+ var context, metricEvent, error;
2439
2411
  var _arguments = arguments;
2440
2412
  return _ts_generator(this, function(_state) {
2441
2413
  switch(_state.label){
2442
2414
  case 0:
2443
2415
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2416
+ if (!canPublish(licenseKey)) return [
2417
+ 2
2418
+ ];
2444
2419
  _state.label = 1;
2445
2420
  case 1:
2446
2421
  _state.trys.push([
2447
2422
  1,
2448
- 4,
2423
+ 3,
2449
2424
  ,
2450
- 5
2425
+ 4
2451
2426
  ]);
2452
- clientInfo = getClientInfo();
2453
2427
  return [
2454
2428
  4,
2455
- getBrowserID(clientInfo)
2429
+ buildPlayerMetricEvent(context, {
2430
+ adLoaded: false,
2431
+ adDetect: false
2432
+ })
2456
2433
  ];
2457
2434
  case 2:
2458
- browserId = _state.sent();
2459
- captureAt = /* @__PURE__ */ new Date().toISOString();
2460
- trackingData = _object_spread({
2461
- browserId: browserId
2462
- }, clientInfo);
2463
- metricsBody = {
2435
+ metricEvent = _state.sent();
2436
+ publishTracking(licenseKey, "metrics", {
2464
2437
  events: [
2465
- {
2466
- player_id: browserId,
2467
- device_type: clientInfo.deviceType,
2468
- input_stream_type: context.inputStreamType,
2469
- os: clientInfo.os,
2470
- ad_loaded: false,
2471
- ad_detect: false,
2472
- license_key: licenseKey,
2473
- capture_at: captureAt
2474
- }
2475
- ],
2476
- trackingData: trackingData
2477
- };
2478
- return [
2479
- 4,
2480
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/metrics"), TRACK_URL, licenseKey, metricsBody)
2481
- ];
2482
- case 3:
2483
- _state.sent();
2438
+ metricEvent
2439
+ ]
2440
+ });
2484
2441
  return [
2485
2442
  3,
2486
- 5
2443
+ 4
2487
2444
  ];
2488
- case 4:
2445
+ case 3:
2489
2446
  error = _state.sent();
2490
2447
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
2491
2448
  return [
2492
2449
  3,
2493
- 5
2450
+ 4
2494
2451
  ];
2495
- case 5:
2452
+ case 4:
2496
2453
  return [
2497
2454
  2
2498
2455
  ];
@@ -2590,61 +2547,54 @@ function sendAdLoadedTracking(_0, _1) {
2590
2547
  }
2591
2548
  function sendAdImpressionTracking(_0, _1) {
2592
2549
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
2593
- var context, metricEvent, heartbeatBody, impressionsBody, error;
2550
+ var context, metricEvent, error;
2594
2551
  var _arguments = arguments;
2595
2552
  return _ts_generator(this, function(_state) {
2596
2553
  switch(_state.label){
2597
2554
  case 0:
2598
2555
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2556
+ if (!canPublish(licenseKey)) return [
2557
+ 2
2558
+ ];
2599
2559
  _state.label = 1;
2600
2560
  case 1:
2601
2561
  _state.trys.push([
2602
2562
  1,
2603
- 4,
2563
+ 3,
2604
2564
  ,
2605
- 5
2565
+ 4
2606
2566
  ]);
2607
2567
  return [
2608
2568
  4,
2609
- buildPlayerMetricEvent(licenseKey, context, {
2569
+ buildPlayerMetricEvent(context, {
2610
2570
  captureAt: adImpressionInfo.timestamp
2611
2571
  })
2612
2572
  ];
2613
2573
  case 2:
2614
2574
  metricEvent = _state.sent();
2615
- heartbeatBody = metricEvent;
2616
- impressionsBody = {
2575
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2576
+ publishTracking(licenseKey, "impressions", {
2617
2577
  events: [
2618
2578
  {
2619
2579
  player_id: metricEvent.player_id,
2620
2580
  ad_played_count: 1,
2621
2581
  ad_url: adImpressionInfo.adUrl,
2622
- license_key: licenseKey,
2623
2582
  capture_at: adImpressionInfo.timestamp
2624
2583
  }
2625
2584
  ]
2626
- };
2627
- return [
2628
- 4,
2629
- Promise.all([
2630
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatBody),
2631
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/impressions"), IMPRESSIONS_URL, licenseKey, impressionsBody)
2632
- ])
2633
- ];
2634
- case 3:
2635
- _state.sent();
2585
+ });
2636
2586
  return [
2637
2587
  3,
2638
- 5
2588
+ 4
2639
2589
  ];
2640
- case 4:
2590
+ case 3:
2641
2591
  error = _state.sent();
2642
2592
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2643
2593
  return [
2644
2594
  3,
2645
- 5
2595
+ 4
2646
2596
  ];
2647
- case 5:
2597
+ case 4:
2648
2598
  return [
2649
2599
  2
2650
2600
  ];
@@ -2660,38 +2610,36 @@ function sendHeartbeat(_0) {
2660
2610
  switch(_state.label){
2661
2611
  case 0:
2662
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
+ ];
2663
2616
  _state.label = 1;
2664
2617
  case 1:
2665
2618
  _state.trys.push([
2666
2619
  1,
2667
- 4,
2620
+ 3,
2668
2621
  ,
2669
- 5
2622
+ 4
2670
2623
  ]);
2671
2624
  return [
2672
2625
  4,
2673
- buildPlayerMetricEvent(licenseKey, context, flags)
2626
+ buildPlayerMetricEvent(context, flags)
2674
2627
  ];
2675
2628
  case 2:
2676
2629
  heartbeatData = _state.sent();
2677
- return [
2678
- 4,
2679
- publishOrPost("".concat(mqttTopicPrefix, "/tracking/heartbeat"), HEARTBEAT_URL, licenseKey, heartbeatData)
2680
- ];
2681
- case 3:
2682
- _state.sent();
2630
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2683
2631
  return [
2684
2632
  3,
2685
- 5
2633
+ 4
2686
2634
  ];
2687
- case 4:
2635
+ case 3:
2688
2636
  error = _state.sent();
2689
2637
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2690
2638
  return [
2691
2639
  3,
2692
- 5
2640
+ 4
2693
2641
  ];
2694
- case 5:
2642
+ case 4:
2695
2643
  return [
2696
2644
  2
2697
2645
  ];
@@ -3848,7 +3796,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3848
3796
  _this.adLayer.showPlaceholder();
3849
3797
  }
3850
3798
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
3851
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
3799
+ _this.scheduleAdStopAtBreakBoundary();
3852
3800
  if (_this.config.debugAdTiming) {
3853
3801
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
3854
3802
  }
@@ -4282,12 +4230,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4282
4230
  if (marker.durationSeconds != null) {
4283
4231
  var newDurationMs = marker.durationSeconds * 1e3;
4284
4232
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4285
- this.expectedAdBreakDurationMs = newDurationMs;
4286
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4287
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4288
- this.scheduleAdStopCountdown(remainingMs);
4233
+ this.setAdBreakDurationBoundary(newDurationMs);
4234
+ this.scheduleAdStopAtBreakBoundary();
4289
4235
  if (this.config.debugAdTiming) {
4290
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4236
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4291
4237
  }
4292
4238
  }
4293
4239
  }
@@ -4295,8 +4241,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4295
4241
  }
4296
4242
  this.inAdBreak = true;
4297
4243
  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;
4298
- this.expectedAdBreakDurationMs = durationMs;
4299
- this.currentAdBreakStartWallClockMs = Date.now();
4244
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4245
+ this.setAdBreakDurationBoundary(durationMs);
4300
4246
  if (this.config.licenseKey) {
4301
4247
  var _this_pendingAdBreak1;
4302
4248
  var adDetectInfo = _object_spread({
@@ -4360,20 +4306,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4360
4306
  this.clearAdStartTimer();
4361
4307
  this.handleAdStart(marker);
4362
4308
  }
4363
- if (this.expectedAdBreakDurationMs != null) {
4364
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4365
- }
4309
+ this.scheduleAdStopAtBreakBoundary();
4366
4310
  return;
4367
4311
  }
4368
4312
  if (marker.type === "progress" && this.inAdBreak) {
4369
4313
  if (marker.durationSeconds != null) {
4370
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4371
- }
4372
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4373
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4374
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4375
- this.scheduleAdStopCountdown(remainingMs1);
4314
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4376
4315
  }
4316
+ this.scheduleAdStopAtBreakBoundary();
4377
4317
  if (!this.adLayer.isAdPlaying() && this.pendingNextAdBids != null && this.pendingNextAdBids.length > 0) {
4378
4318
  var bids = this.pendingNextAdBids;
4379
4319
  this.pendingNextAdBids = null;
@@ -4404,15 +4344,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4404
4344
  }
4405
4345
  return;
4406
4346
  }
4407
- if (adPlaying || remaining > 500) {
4408
- if (this.config.debugAdTiming) {
4409
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active or time remaining");
4410
- }
4411
- return;
4412
- }
4413
- this.inAdBreak = false;
4414
- this.expectedAdBreakDurationMs = void 0;
4415
- this.currentAdBreakStartWallClockMs = void 0;
4347
+ this.scteAdBreakEndWallClockMs = Date.now();
4416
4348
  this.clearAdStartTimer();
4417
4349
  this.clearAdStopTimer();
4418
4350
  if (adPlaying) {
@@ -4423,6 +4355,42 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4423
4355
  }
4424
4356
  }
4425
4357
  },
4358
+ {
4359
+ key: "resolveScteBreakStartWallClockMs",
4360
+ value: function resolveScteBreakStartWallClockMs(marker) {
4361
+ var nowWallClockMs = Date.now();
4362
+ if (typeof marker.ptsSeconds !== "number") {
4363
+ return nowWallClockMs;
4364
+ }
4365
+ var nowMs = this.video.currentTime * 1e3;
4366
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4367
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4368
+ if (!Number.isFinite(deltaMs)) {
4369
+ return nowWallClockMs;
4370
+ }
4371
+ return nowWallClockMs + Math.floor(deltaMs);
4372
+ }
4373
+ },
4374
+ {
4375
+ key: "setAdBreakDurationBoundary",
4376
+ value: function setAdBreakDurationBoundary(durationMs) {
4377
+ this.expectedAdBreakDurationMs = durationMs;
4378
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4379
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4380
+ } else {
4381
+ this.scteAdBreakEndWallClockMs = void 0;
4382
+ }
4383
+ }
4384
+ },
4385
+ {
4386
+ key: "scheduleAdStopAtBreakBoundary",
4387
+ value: function scheduleAdStopAtBreakBoundary() {
4388
+ if (this.expectedAdBreakDurationMs == null) {
4389
+ return;
4390
+ }
4391
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4392
+ }
4393
+ },
4426
4394
  {
4427
4395
  key: "parseCueOutDuration",
4428
4396
  value: function parseCueOutDuration(value) {
@@ -4633,10 +4601,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4633
4601
  key: "initializeTracking",
4634
4602
  value: function initializeTracking() {
4635
4603
  var _this = this;
4636
- if (this.config.mqttBrokerUrl) {
4637
- var topicPrefix = "adstorm";
4638
- setMQTTTopicPrefix(topicPrefix);
4639
- initMQTTClient(this.config.mqttBrokerUrl, topicPrefix);
4604
+ if (isMQTTEnabled()) {
4605
+ initMQTTClient();
4640
4606
  }
4641
4607
  sendInitialTracking(this.config.licenseKey, this.getAnalyticsContext()).then(function() {
4642
4608
  _this.heartbeatInterval = window.setInterval(function() {
@@ -5060,7 +5026,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5060
5026
  case 9:
5061
5027
  _state.sent();
5062
5028
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5063
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5029
+ _this.scheduleAdStopAtBreakBoundary();
5064
5030
  }
5065
5031
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5066
5032
  return [
@@ -5165,7 +5131,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5165
5131
  case 19:
5166
5132
  _state.sent();
5167
5133
  if (_this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
5168
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
5134
+ _this.scheduleAdStopAtBreakBoundary();
5169
5135
  }
5170
5136
  _this.adLayer.setAdVolume(_this.adLayer.getOriginalMutedState() ? 1 : _this.adLayer.getOriginalVolume());
5171
5137
  _state.label = 20;
@@ -5248,7 +5214,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5248
5214
  key: "handleAdStart",
5249
5215
  value: function handleAdStart(_marker) {
5250
5216
  return _async_to_generator(function() {
5251
- var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, adBreakToken, adVolume, token, remaining, err;
5217
+ var _this_savedMutedStateBeforeScte, adBreakDurationMs, mode, state, fillerRemainingMs, adBreakToken, adVolume, token, remaining, err;
5252
5218
  return _ts_generator(this, function(_state) {
5253
5219
  switch(_state.label){
5254
5220
  case 0:
@@ -5282,20 +5248,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5282
5248
  }
5283
5249
  }
5284
5250
  this.inAdBreak = true;
5285
- this.currentAdBreakStartWallClockMs = Date.now();
5286
5251
  this.currentAdIndex = 0;
5287
5252
  this.totalAdsInBreak = Math.max(1, this.preloadedTokens.length);
5288
5253
  this.adPodQueue = [];
5289
5254
  if (!this.config.disableFiller) this.showAds = true;
5290
- if (adBreakDurationMs != null) {
5291
- this.startFillerBreakTimer(adBreakDurationMs);
5255
+ if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5256
+ this.currentAdBreakStartWallClockMs = Date.now();
5257
+ this.setAdBreakDurationBoundary(adBreakDurationMs);
5258
+ }
5259
+ fillerRemainingMs = this.getRemainingAdMs();
5260
+ if (this.expectedAdBreakDurationMs != null && fillerRemainingMs > 0) {
5261
+ this.startFillerBreakTimer(fillerRemainingMs);
5292
5262
  } else if (!this.config.disableFiller && this.preloadedTokens.length === 0) {
5293
5263
  this.showPlaceholderLayer();
5294
5264
  }
5295
5265
  if (!this.config.disableFiller) this.adLayer.showPlaceholder();
5296
- if (this.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
5297
- this.expectedAdBreakDurationMs = adBreakDurationMs;
5298
- }
5299
5266
  this.clearPendingAdBreak();
5300
5267
  adBreakToken = Date.now();
5301
5268
  this.activeAdRequestToken = adBreakToken;
@@ -5332,7 +5299,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5332
5299
  case 2:
5333
5300
  _state.sent();
5334
5301
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5335
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5302
+ this.scheduleAdStopAtBreakBoundary();
5336
5303
  }
5337
5304
  this.adLayer.setAdVolume(adVolume);
5338
5305
  return [
@@ -5518,7 +5485,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5518
5485
  case 4:
5519
5486
  _state.sent();
5520
5487
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5521
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
5488
+ this.scheduleAdStopAtBreakBoundary();
5522
5489
  }
5523
5490
  this.adLayer.setAdVolume(this.adLayer.getOriginalMutedState() ? 1 : this.adLayer.getOriginalVolume());
5524
5491
  _state.label = 5;
@@ -5760,21 +5727,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5760
5727
  {
5761
5728
  key: "ensureAdStoppedByTimer",
5762
5729
  value: function ensureAdStoppedByTimer() {
5763
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
5730
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
5764
5731
  if (!this.inAdBreak) return;
5765
5732
  this.adStopTimerId = void 0;
5766
5733
  var adPlaying = this.adLayer.isAdPlaying();
5767
- var pendingAds = this.adPodQueue.length > 0;
5768
5734
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
5769
5735
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
5770
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
5771
- var elapsedSinceStartMs = 0;
5772
- if (this.currentAdBreakStartWallClockMs != null) {
5773
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
5774
- }
5775
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
5776
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
5777
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
5736
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
5737
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
5738
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
5739
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
5778
5740
  if (shouldExtendAdBreak) {
5779
5741
  this.scheduleAdStopCountdown(checkIntervalMs);
5780
5742
  return;
@@ -5848,6 +5810,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5848
5810
  this.inAdBreak = false;
5849
5811
  this.expectedAdBreakDurationMs = void 0;
5850
5812
  this.currentAdBreakStartWallClockMs = void 0;
5813
+ this.scteAdBreakEndWallClockMs = void 0;
5851
5814
  this.clearAdStartTimer();
5852
5815
  this.clearAdStopTimer();
5853
5816
  this.adPodQueue = [];
@@ -6109,10 +6072,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6109
6072
  {
6110
6073
  key: "getRemainingAdMs",
6111
6074
  value: function getRemainingAdMs() {
6075
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
6076
+ if (endWallClockMs != null) {
6077
+ return Math.max(0, endWallClockMs - Date.now());
6078
+ }
6112
6079
  if (this.currentAdBreakStartWallClockMs == null) return 0;
6113
6080
  if (this.expectedAdBreakDurationMs == null) return Number.MAX_SAFE_INTEGER;
6114
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
6115
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
6081
+ return 0;
6082
+ }
6083
+ },
6084
+ {
6085
+ key: "getAdBreakEndWallClockMs",
6086
+ value: function getAdBreakEndWallClockMs() {
6087
+ if (this.scteAdBreakEndWallClockMs != null) {
6088
+ return this.scteAdBreakEndWallClockMs;
6089
+ }
6090
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
6091
+ return void 0;
6092
+ }
6093
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
6116
6094
  }
6117
6095
  },
6118
6096
  {
@@ -6317,7 +6295,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6317
6295
  clearInterval(this.heartbeatInterval);
6318
6296
  this.heartbeatInterval = void 0;
6319
6297
  }
6320
- if (this.config.mqttBrokerUrl) {
6298
+ if (isMQTTEnabled()) {
6321
6299
  disconnectMQTT();
6322
6300
  }
6323
6301
  (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : _this_hls.destroy();
@@ -6341,7 +6319,7 @@ var CRITICAL_PROPS = [
6341
6319
  var CONTROLS_HIDE_DELAY = 3e3;
6342
6320
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
6343
6321
  var StormcloudVideoPlayerComponent = React.memo(function(props) {
6344
- 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, [
6322
+ 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, [
6345
6323
  "src",
6346
6324
  "autoplay",
6347
6325
  "muted",
@@ -6368,8 +6346,7 @@ var StormcloudVideoPlayerComponent = React.memo(function(props) {
6368
6346
  "licenseKey",
6369
6347
  "minSegmentsBeforePlay",
6370
6348
  "disableAds",
6371
- "disableFiller",
6372
- "mqttBrokerUrl"
6349
+ "disableFiller"
6373
6350
  ]);
6374
6351
  var videoRef = useRef(null);
6375
6352
  var playerRef = useRef(null);
@@ -6529,7 +6506,6 @@ var StormcloudVideoPlayerComponent = React.memo(function(props) {
6529
6506
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;
6530
6507
  if (disableAds !== void 0) cfg.disableAds = disableAds;
6531
6508
  cfg.disableFiller = disableFiller !== null && disableFiller !== void 0 ? disableFiller : true;
6532
- if (mqttBrokerUrl !== void 0) cfg.mqttBrokerUrl = mqttBrokerUrl;
6533
6509
  var player = new StormcloudVideoPlayer(cfg);
6534
6510
  playerRef.current = player;
6535
6511
  player.load().then(function() {
@@ -8764,5 +8740,5 @@ var createStormcloudPlayer = function createStormcloudPlayer(playerList, fallbac
8764
8740
  };
8765
8741
  var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[players_default.length - 1]);
8766
8742
  var StormcloudPlayer_default = StormcloudPlayer;
8767
- export { IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, canPlay, createStormcloudPlayer, createVastAdLayer, createVastManager, StormcloudVideoPlayerComponent as default, detectBrowser, disconnectMQTT, getBrowserConfigOverrides, getBrowserID, getClientInfo, getMQTTStatus, initMQTTClient, initializePolyfills, isMQTTConfigured, isMQTTConnected, isMediaStream, lazy, logBrowserInfo, merge, omit, parseQuery, players_default as players, publishMQTT, randomString, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode };
8743
+ export { DEFAULT_MQTT_CONFIG, IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, applyMQTTConfig, buildMQTTBrokerUrl, buildPlayerTopic, canPlay, configureMQTT, createStormcloudPlayer, createVastAdLayer, createVastManager, StormcloudVideoPlayerComponent as default, detectBrowser, disconnectMQTT, ensureMQTTClient, getBrowserConfigOverrides, getBrowserID, getClientInfo, getMQTTStatus, initMQTTClient, initializePolyfills, isMQTTConfigured, isMQTTConnected, isMQTTEnabled, isMediaStream, lazy, logBrowserInfo, merge, mqttConfig, omit, parseQuery, players_default as players, publishMQTT, randomString, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode };
8768
8744
  //# sourceMappingURL=index.js.map