stormcloud-video-player 0.3.63 → 0.3.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -428,6 +428,9 @@ var __toCommonJS = function __toCommonJS(mod) {
428
428
  // src/index.ts
429
429
  var index_exports = {};
430
430
  __export(index_exports, {
431
+ DEFAULT_MQTT_CONFIG: function DEFAULT_MQTT_CONFIG1() {
432
+ return DEFAULT_MQTT_CONFIG;
433
+ },
431
434
  IS_BROWSER: function IS_BROWSER1() {
432
435
  return IS_BROWSER;
433
436
  },
@@ -440,6 +443,9 @@ __export(index_exports, {
440
443
  IS_SAFARI: function IS_SAFARI1() {
441
444
  return IS_SAFARI;
442
445
  },
446
+ MQTT_CA_CERT_FILE: function MQTT_CA_CERT_FILE1() {
447
+ return MQTT_CA_CERT_FILE;
448
+ },
443
449
  SUPPORTS_DASH: function SUPPORTS_DASH1() {
444
450
  return SUPPORTS_DASH;
445
451
  },
@@ -455,9 +461,21 @@ __export(index_exports, {
455
461
  StormcloudVideoPlayerComponent: function StormcloudVideoPlayerComponent1() {
456
462
  return StormcloudVideoPlayerComponent;
457
463
  },
464
+ applyMQTTConfig: function applyMQTTConfig1() {
465
+ return applyMQTTConfig;
466
+ },
467
+ buildMQTTBrokerUrl: function buildMQTTBrokerUrl1() {
468
+ return buildMQTTBrokerUrl;
469
+ },
470
+ buildPlayerTopic: function buildPlayerTopic1() {
471
+ return buildPlayerTopic;
472
+ },
458
473
  canPlay: function canPlay1() {
459
474
  return canPlay;
460
475
  },
476
+ configureMQTT: function configureMQTT1() {
477
+ return configureMQTT;
478
+ },
461
479
  createHlsAdPlayer: function createHlsAdPlayer1() {
462
480
  return createHlsAdPlayer;
463
481
  },
@@ -473,6 +491,12 @@ __export(index_exports, {
473
491
  detectBrowser: function detectBrowser1() {
474
492
  return detectBrowser;
475
493
  },
494
+ disconnectMQTT: function disconnectMQTT1() {
495
+ return disconnectMQTT;
496
+ },
497
+ ensureMQTTClient: function ensureMQTTClient1() {
498
+ return ensureMQTTClient;
499
+ },
476
500
  getBrowserConfigOverrides: function getBrowserConfigOverrides1() {
477
501
  return getBrowserConfigOverrides;
478
502
  },
@@ -482,12 +506,27 @@ __export(index_exports, {
482
506
  getClientInfo: function getClientInfo1() {
483
507
  return getClientInfo;
484
508
  },
509
+ getMQTTStatus: function getMQTTStatus1() {
510
+ return getMQTTStatus;
511
+ },
485
512
  getRecommendedAdPlayer: function getRecommendedAdPlayer1() {
486
513
  return getRecommendedAdPlayer;
487
514
  },
515
+ initMQTTClient: function initMQTTClient1() {
516
+ return initMQTTClient;
517
+ },
488
518
  initializePolyfills: function initializePolyfills1() {
489
519
  return initializePolyfills;
490
520
  },
521
+ isMQTTConfigured: function isMQTTConfigured1() {
522
+ return isMQTTConfigured;
523
+ },
524
+ isMQTTConnected: function isMQTTConnected1() {
525
+ return isMQTTConnected;
526
+ },
527
+ isMQTTEnabled: function isMQTTEnabled1() {
528
+ return isMQTTEnabled;
529
+ },
491
530
  isMediaStream: function isMediaStream1() {
492
531
  return isMediaStream;
493
532
  },
@@ -500,6 +539,9 @@ __export(index_exports, {
500
539
  merge: function merge1() {
501
540
  return merge;
502
541
  },
542
+ mqttConfig: function mqttConfig1() {
543
+ return mqttConfig;
544
+ },
503
545
  omit: function omit1() {
504
546
  return omit;
505
547
  },
@@ -509,6 +551,9 @@ __export(index_exports, {
509
551
  players: function players() {
510
552
  return players_default;
511
553
  },
554
+ publishMQTT: function publishMQTT1() {
555
+ return publishMQTT;
556
+ },
512
557
  randomString: function randomString1() {
513
558
  return randomString;
514
559
  },
@@ -2604,6 +2649,123 @@ function createHlsAdPlayer(contentVideo, options) {
2604
2649
  }
2605
2650
  };
2606
2651
  }
2652
+ // src/utils/mqttConfig.ts
2653
+ var MQTT_CA_CERT_FILE = "src/certs/emqxsl-ca.crt";
2654
+ var DEFAULT_MQTT_CONFIG = {
2655
+ enabled: true,
2656
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
2657
+ brokerPort: 8883,
2658
+ wsPort: 8084,
2659
+ username: "for-sonifi",
2660
+ password: "sonifi-mqtt",
2661
+ topicPrefix: "adstorm/players",
2662
+ qos: 1
2663
+ };
2664
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
2665
+ function applyMQTTConfig(overrides) {
2666
+ Object.assign(mqttConfig, overrides);
2667
+ }
2668
+ function isMQTTEnabled() {
2669
+ return mqttConfig.enabled;
2670
+ }
2671
+ function buildMQTTBrokerUrl() {
2672
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
2673
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
2674
+ }
2675
+ function buildPlayerTopic(licenseKey, channel) {
2676
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
2677
+ }
2678
+ // src/utils/mqttClient.ts
2679
+ var import_mqtt = __toESM(require("mqtt"), 1);
2680
+ var LOG = "[StormcloudVideoPlayer][MQTT]";
2681
+ var client = null;
2682
+ var status = "disconnected";
2683
+ function getMQTTStatus() {
2684
+ return status;
2685
+ }
2686
+ function isMQTTConnected() {
2687
+ return status === "connected" && client !== null && client.connected;
2688
+ }
2689
+ function isMQTTConfigured() {
2690
+ return isMQTTEnabled();
2691
+ }
2692
+ function configureMQTT(overrides) {
2693
+ applyMQTTConfig(overrides);
2694
+ disconnectMQTT();
2695
+ }
2696
+ function initMQTTClient() {
2697
+ if (client || !isMQTTEnabled()) return;
2698
+ var url = buildMQTTBrokerUrl();
2699
+ status = "connecting";
2700
+ var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
2701
+ try {
2702
+ client = import_mqtt.default.connect(url, {
2703
+ clientId: clientId,
2704
+ username: mqttConfig.username,
2705
+ password: mqttConfig.password,
2706
+ keepalive: 60,
2707
+ clean: true,
2708
+ reconnectPeriod: 5e3,
2709
+ connectTimeout: 1e4,
2710
+ queueQoSZero: false
2711
+ });
2712
+ } catch (err) {
2713
+ status = "error";
2714
+ console.warn("".concat(LOG, " connect() threw:"), err);
2715
+ return;
2716
+ }
2717
+ client.on("connect", function() {
2718
+ status = "connected";
2719
+ console.info("".concat(LOG, " connected to ").concat(url));
2720
+ });
2721
+ client.on("reconnect", function() {
2722
+ status = "connecting";
2723
+ console.info("".concat(LOG, " reconnecting…"));
2724
+ });
2725
+ client.on("offline", function() {
2726
+ status = "disconnected";
2727
+ console.warn("".concat(LOG, " offline"));
2728
+ });
2729
+ client.on("error", function(err) {
2730
+ status = "error";
2731
+ console.warn("".concat(LOG, " error:"), err.message);
2732
+ });
2733
+ client.on("close", function() {
2734
+ if (status === "connected") {
2735
+ status = "disconnected";
2736
+ }
2737
+ });
2738
+ }
2739
+ function ensureMQTTClient() {
2740
+ if (isMQTTEnabled() && !client) {
2741
+ initMQTTClient();
2742
+ }
2743
+ }
2744
+ function publishMQTT(topic, payload) {
2745
+ if (!isMQTTEnabled()) {
2746
+ return false;
2747
+ }
2748
+ ensureMQTTClient();
2749
+ if (!client) {
2750
+ return false;
2751
+ }
2752
+ try {
2753
+ client.publish(topic, JSON.stringify(payload), {
2754
+ qos: mqttConfig.qos
2755
+ });
2756
+ return true;
2757
+ } catch (err) {
2758
+ console.warn("".concat(LOG, " publish failed on ").concat(topic, ":"), err);
2759
+ return false;
2760
+ }
2761
+ }
2762
+ function disconnectMQTT() {
2763
+ if (client) {
2764
+ client.end(true);
2765
+ client = null;
2766
+ status = "disconnected";
2767
+ }
2768
+ }
2607
2769
  // src/utils/tracking.ts
2608
2770
  var cachedBrowserId = null;
2609
2771
  function getClientInfo() {
@@ -2750,7 +2912,7 @@ function getClientInfo() {
2750
2912
  }
2751
2913
  function getBrowserID(clientInfo) {
2752
2914
  return _async_to_generator(function() {
2753
- var fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashArray, hashHex, error, hash, i1, char, fallbackHash, timestamp, random;
2915
+ var _crypto_subtle, fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashHex, unused, hash, i1, char, fallbackHash, timestamp, random;
2754
2916
  return _ts_generator(this, function(_state) {
2755
2917
  switch(_state.label){
2756
2918
  case 0:
@@ -2761,7 +2923,7 @@ function getBrowserID(clientInfo) {
2761
2923
  ];
2762
2924
  }
2763
2925
  fingerprintString = JSON.stringify(clientInfo);
2764
- if (!(typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest)) return [
2926
+ if (!(typeof crypto !== "undefined" && ((_crypto_subtle = crypto.subtle) === null || _crypto_subtle === void 0 ? void 0 : _crypto_subtle.digest))) return [
2765
2927
  3,
2766
2928
  5
2767
2929
  ];
@@ -2799,8 +2961,7 @@ function getBrowserID(clientInfo) {
2799
2961
  ];
2800
2962
  case 3:
2801
2963
  hashBuffer = _state.sent();
2802
- hashArray = Array.from(new Uint8Array(hashBuffer));
2803
- hashHex = hashArray.map(function(b) {
2964
+ hashHex = Array.from(new Uint8Array(hashBuffer)).map(function(b) {
2804
2965
  return b.toString(16).padStart(2, "0");
2805
2966
  }).join("");
2806
2967
  cachedBrowserId = hashHex;
@@ -2809,8 +2970,8 @@ function getBrowserID(clientInfo) {
2809
2970
  hashHex
2810
2971
  ];
2811
2972
  case 4:
2812
- error = _state.sent();
2813
- console.warn("[StormcloudVideoPlayer] crypto.subtle.digest not supported, using fallback hash");
2973
+ unused = _state.sent();
2974
+ console.warn("[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash");
2814
2975
  return [
2815
2976
  3,
2816
2977
  5
@@ -2834,177 +2995,91 @@ function getBrowserID(clientInfo) {
2834
2995
  });
2835
2996
  })();
2836
2997
  }
2837
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
2838
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
2839
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
2840
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
2841
- function buildHeaders(licenseKey) {
2842
- var headers = {
2843
- "Content-Type": "application/json"
2844
- };
2845
- if (licenseKey) {
2846
- headers["Authorization"] = "Bearer ".concat(licenseKey);
2847
- }
2848
- return headers;
2849
- }
2850
- function sendTrackRequest(licenseKey, body) {
2851
- return _async_to_generator(function() {
2852
- var response;
2853
- return _ts_generator(this, function(_state) {
2854
- switch(_state.label){
2855
- case 0:
2856
- return [
2857
- 4,
2858
- fetch(TRACK_URL, {
2859
- method: "POST",
2860
- headers: buildHeaders(licenseKey),
2861
- body: JSON.stringify(body)
2862
- })
2863
- ];
2864
- case 1:
2865
- response = _state.sent();
2866
- if (!response.ok) {
2867
- throw new Error("HTTP error! status: ".concat(response.status));
2868
- }
2869
- return [
2870
- 4,
2871
- response.json()
2872
- ];
2873
- case 2:
2874
- _state.sent();
2875
- return [
2876
- 2
2877
- ];
2878
- }
2879
- });
2880
- })();
2998
+ function canPublish(licenseKey) {
2999
+ return Boolean(isMQTTEnabled() && licenseKey);
2881
3000
  }
2882
- function postJson(url, licenseKey, body) {
3001
+ function buildPlayerMetricEvent() {
2883
3002
  return _async_to_generator(function() {
2884
- var response;
2885
- return _ts_generator(this, function(_state) {
2886
- switch(_state.label){
2887
- case 0:
2888
- return [
2889
- 4,
2890
- fetch(url, {
2891
- method: "POST",
2892
- headers: buildHeaders(licenseKey),
2893
- body: JSON.stringify(body)
2894
- })
2895
- ];
2896
- case 1:
2897
- response = _state.sent();
2898
- if (!response.ok) {
2899
- throw new Error("HTTP error! status: ".concat(response.status));
2900
- }
2901
- return [
2902
- 4,
2903
- response.json()
2904
- ];
2905
- case 2:
2906
- _state.sent();
2907
- return [
2908
- 2
2909
- ];
2910
- }
2911
- });
2912
- })();
2913
- }
2914
- function buildPlayerMetricEvent(_0) {
2915
- return _async_to_generator(function(licenseKey) {
2916
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
3003
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2917
3004
  var _arguments = arguments;
2918
3005
  return _ts_generator(this, function(_state) {
2919
3006
  switch(_state.label){
2920
3007
  case 0:
2921
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
3008
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2922
3009
  clientInfo = getClientInfo();
2923
3010
  return [
2924
3011
  4,
2925
3012
  getBrowserID(clientInfo)
2926
3013
  ];
2927
3014
  case 1:
2928
- browserId = _state.sent();
3015
+ playerId = _state.sent();
2929
3016
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2930
3017
  return [
2931
3018
  2,
2932
- {
2933
- player_id: browserId,
2934
- browserId: browserId,
3019
+ _object_spread({
3020
+ player_id: playerId,
2935
3021
  device_type: clientInfo.deviceType,
2936
- deviceType: clientInfo.deviceType,
2937
- input_stream_type: context.inputStreamType,
2938
- os: clientInfo.os,
2939
- ad_loaded: flags.adLoaded,
2940
- ad_detect: flags.adDetect,
2941
- license_key: licenseKey,
2942
- capture_at: captureAt,
2943
- timestamp: captureAt
2944
- }
3022
+ os: clientInfo.os.toLowerCase(),
3023
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
3024
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
3025
+ capture_at: captureAt
3026
+ }, context.inputStreamType ? {
3027
+ input_stream_type: context.inputStreamType
3028
+ } : {})
2945
3029
  ];
2946
3030
  }
2947
3031
  });
2948
3032
  }).apply(this, arguments);
2949
3033
  }
3034
+ function publishTracking(licenseKey, channel, body) {
3035
+ ensureMQTTClient();
3036
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
3037
+ }
2950
3038
  function sendInitialTracking(_0) {
2951
3039
  return _async_to_generator(function(licenseKey) {
2952
- var context, clientInfo, browserId, trackingData, error;
3040
+ var context, metricEvent, error;
2953
3041
  var _arguments = arguments;
2954
3042
  return _ts_generator(this, function(_state) {
2955
3043
  switch(_state.label){
2956
3044
  case 0:
2957
3045
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
3046
+ if (!canPublish(licenseKey)) return [
3047
+ 2
3048
+ ];
2958
3049
  _state.label = 1;
2959
3050
  case 1:
2960
3051
  _state.trys.push([
2961
3052
  1,
2962
- 4,
3053
+ 3,
2963
3054
  ,
2964
- 5
3055
+ 4
2965
3056
  ]);
2966
- clientInfo = getClientInfo();
2967
3057
  return [
2968
3058
  4,
2969
- getBrowserID(clientInfo)
2970
- ];
2971
- case 2:
2972
- browserId = _state.sent();
2973
- trackingData = _object_spread({
2974
- browserId: browserId
2975
- }, clientInfo);
2976
- return [
2977
- 4,
2978
- sendTrackRequest(licenseKey, {
2979
- events: [
2980
- {
2981
- player_id: browserId,
2982
- device_type: clientInfo.deviceType,
2983
- input_stream_type: context.inputStreamType,
2984
- os: clientInfo.os,
2985
- ad_loaded: false,
2986
- ad_detect: false,
2987
- license_key: licenseKey,
2988
- capture_at: /* @__PURE__ */ new Date().toISOString()
2989
- }
2990
- ],
2991
- trackingData: trackingData
3059
+ buildPlayerMetricEvent(context, {
3060
+ adLoaded: false,
3061
+ adDetect: false
2992
3062
  })
2993
3063
  ];
2994
- case 3:
2995
- _state.sent();
3064
+ case 2:
3065
+ metricEvent = _state.sent();
3066
+ publishTracking(licenseKey, "metrics", {
3067
+ events: [
3068
+ metricEvent
3069
+ ]
3070
+ });
2996
3071
  return [
2997
3072
  3,
2998
- 5
3073
+ 4
2999
3074
  ];
3000
- case 4:
3075
+ case 3:
3001
3076
  error = _state.sent();
3002
3077
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
3003
3078
  return [
3004
3079
  3,
3005
- 5
3080
+ 4
3006
3081
  ];
3007
- case 5:
3082
+ case 4:
3008
3083
  return [
3009
3084
  2
3010
3085
  ];
@@ -3108,53 +3183,48 @@ function sendAdImpressionTracking(_0, _1) {
3108
3183
  switch(_state.label){
3109
3184
  case 0:
3110
3185
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
3186
+ if (!canPublish(licenseKey)) return [
3187
+ 2
3188
+ ];
3111
3189
  _state.label = 1;
3112
3190
  case 1:
3113
3191
  _state.trys.push([
3114
3192
  1,
3115
- 4,
3193
+ 3,
3116
3194
  ,
3117
- 5
3195
+ 4
3118
3196
  ]);
3119
3197
  return [
3120
3198
  4,
3121
- buildPlayerMetricEvent(licenseKey, context, {
3199
+ buildPlayerMetricEvent(context, {
3122
3200
  captureAt: adImpressionInfo.timestamp
3123
3201
  })
3124
3202
  ];
3125
3203
  case 2:
3126
3204
  metricEvent = _state.sent();
3127
- return [
3128
- 4,
3129
- Promise.all([
3130
- postJson(HEARTBEAT_URL, licenseKey, metricEvent),
3131
- postJson(IMPRESSIONS_URL, licenseKey, {
3132
- events: [
3133
- {
3134
- player_id: metricEvent.player_id,
3135
- ad_played_count: 1,
3136
- ad_url: adImpressionInfo.adUrl,
3137
- license_key: licenseKey,
3138
- capture_at: adImpressionInfo.timestamp
3139
- }
3140
- ]
3141
- })
3142
- ])
3143
- ];
3144
- case 3:
3145
- _state.sent();
3205
+ publishTracking(licenseKey, "heartbeat", metricEvent);
3206
+ publishTracking(licenseKey, "impressions", {
3207
+ events: [
3208
+ {
3209
+ player_id: metricEvent.player_id,
3210
+ ad_played_count: 1,
3211
+ ad_url: adImpressionInfo.adUrl,
3212
+ capture_at: adImpressionInfo.timestamp
3213
+ }
3214
+ ]
3215
+ });
3146
3216
  return [
3147
3217
  3,
3148
- 5
3218
+ 4
3149
3219
  ];
3150
- case 4:
3220
+ case 3:
3151
3221
  error = _state.sent();
3152
3222
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
3153
3223
  return [
3154
3224
  3,
3155
- 5
3225
+ 4
3156
3226
  ];
3157
- case 5:
3227
+ case 4:
3158
3228
  return [
3159
3229
  2
3160
3230
  ];
@@ -3170,38 +3240,36 @@ function sendHeartbeat(_0) {
3170
3240
  switch(_state.label){
3171
3241
  case 0:
3172
3242
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
3243
+ if (!canPublish(licenseKey)) return [
3244
+ 2
3245
+ ];
3173
3246
  _state.label = 1;
3174
3247
  case 1:
3175
3248
  _state.trys.push([
3176
3249
  1,
3177
- 4,
3250
+ 3,
3178
3251
  ,
3179
- 5
3252
+ 4
3180
3253
  ]);
3181
3254
  return [
3182
3255
  4,
3183
- buildPlayerMetricEvent(licenseKey, context, flags)
3256
+ buildPlayerMetricEvent(context, flags)
3184
3257
  ];
3185
3258
  case 2:
3186
3259
  heartbeatData = _state.sent();
3187
- return [
3188
- 4,
3189
- postJson(HEARTBEAT_URL, licenseKey, heartbeatData)
3190
- ];
3191
- case 3:
3192
- _state.sent();
3260
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
3193
3261
  return [
3194
3262
  3,
3195
- 5
3263
+ 4
3196
3264
  ];
3197
- case 4:
3265
+ case 3:
3198
3266
  error = _state.sent();
3199
3267
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
3200
3268
  return [
3201
3269
  3,
3202
- 5
3270
+ 4
3203
3271
  ];
3204
- case 5:
3272
+ case 4:
3205
3273
  return [
3206
3274
  2
3207
3275
  ];
@@ -4054,7 +4122,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4054
4122
  _this.showAds = true;
4055
4123
  _this.resetGamNoFillCounter();
4056
4124
  if (_this.inAdBreak && _this.expectedAdBreakDurationMs != null && _this.adStopTimerId == null) {
4057
- _this.scheduleAdStopCountdown(_this.getRemainingAdMs());
4125
+ _this.scheduleAdStopAtBreakBoundary();
4058
4126
  if (_this.config.debugAdTiming) {
4059
4127
  console.log("[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)");
4060
4128
  }
@@ -4698,12 +4766,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4698
4766
  if (marker.durationSeconds != null) {
4699
4767
  var newDurationMs = marker.durationSeconds * 1e3;
4700
4768
  if (this.expectedAdBreakDurationMs == null || newDurationMs > this.expectedAdBreakDurationMs) {
4701
- this.expectedAdBreakDurationMs = newDurationMs;
4702
- var elapsedMs = this.currentAdBreakStartWallClockMs != null ? Date.now() - this.currentAdBreakStartWallClockMs : 0;
4703
- var remainingMs = Math.max(0, newDurationMs - elapsedMs);
4704
- this.scheduleAdStopCountdown(remainingMs);
4769
+ this.setAdBreakDurationBoundary(newDurationMs);
4770
+ this.scheduleAdStopAtBreakBoundary();
4705
4771
  if (this.config.debugAdTiming) {
4706
- console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(remainingMs, "ms"));
4772
+ console.log("[StormcloudVideoPlayer] Updated ad break duration from subsequent marker: ".concat(newDurationMs, "ms, remaining: ").concat(this.getRemainingAdMs(), "ms"));
4707
4773
  }
4708
4774
  }
4709
4775
  }
@@ -4777,13 +4843,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4777
4843
  }
4778
4844
  if (marker.type === "progress" && this.inAdBreak) {
4779
4845
  if (marker.durationSeconds != null) {
4780
- this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
4781
- }
4782
- if (this.expectedAdBreakDurationMs != null && this.currentAdBreakStartWallClockMs != null) {
4783
- var elapsedMs1 = Date.now() - this.currentAdBreakStartWallClockMs;
4784
- var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4785
- this.scheduleAdStopCountdown(remainingMs1);
4846
+ this.setAdBreakDurationBoundary(marker.durationSeconds * 1e3);
4786
4847
  }
4848
+ this.scheduleAdStopAtBreakBoundary();
4787
4849
  if (!this.ima.isAdPlaying() && this.activeAdRequestToken === null && this.adRequestQueue.length > 0) {
4788
4850
  this.tryNextAvailableAdWithRateLimit();
4789
4851
  }
@@ -4808,15 +4870,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4808
4870
  activeAdRequest: this.activeAdRequestToken !== null
4809
4871
  });
4810
4872
  }
4811
- if (adPlaying || remaining > 1e3 && hasQueuedAds) {
4812
- if (this.config.debugAdTiming) {
4813
- console.log("[StormcloudVideoPlayer] Ignoring premature SCTE-35 end marker - ads still active");
4814
- }
4815
- return;
4816
- }
4817
- this.inAdBreak = false;
4818
- this.expectedAdBreakDurationMs = void 0;
4819
- this.currentAdBreakStartWallClockMs = void 0;
4873
+ this.scteAdBreakEndWallClockMs = Date.now();
4820
4874
  this.clearAdStartTimer();
4821
4875
  this.clearAdStopTimer();
4822
4876
  if (adPlaying) {
@@ -4840,12 +4894,46 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4840
4894
  this.inAdBreak = true;
4841
4895
  this.activeScte35BreakKey = cueKey !== null && cueKey !== void 0 ? cueKey : this.pendingScte35CueKey;
4842
4896
  this.scheduledScte35BreakKey = void 0;
4843
- this.expectedAdBreakDurationMs = durationMs;
4844
- this.currentAdBreakStartWallClockMs = Date.now();
4897
+ this.currentAdBreakStartWallClockMs = this.resolveScteBreakStartWallClockMs(marker);
4898
+ this.setAdBreakDurationBoundary(durationMs);
4845
4899
  this.handleAdStart(marker);
4846
- if (this.expectedAdBreakDurationMs != null) {
4847
- this.scheduleAdStopCountdown(this.expectedAdBreakDurationMs);
4900
+ this.scheduleAdStopAtBreakBoundary();
4901
+ }
4902
+ },
4903
+ {
4904
+ key: "resolveScteBreakStartWallClockMs",
4905
+ value: function resolveScteBreakStartWallClockMs(marker) {
4906
+ var nowWallClockMs = Date.now();
4907
+ if (typeof marker.ptsSeconds !== "number") {
4908
+ return nowWallClockMs;
4848
4909
  }
4910
+ var nowMs = this.video.currentTime * 1e3;
4911
+ var estimatedCurrentPtsMs = nowMs - this.ptsDriftEmaMs;
4912
+ var deltaMs = marker.ptsSeconds * 1e3 - estimatedCurrentPtsMs;
4913
+ if (!Number.isFinite(deltaMs)) {
4914
+ return nowWallClockMs;
4915
+ }
4916
+ return nowWallClockMs + Math.floor(deltaMs);
4917
+ }
4918
+ },
4919
+ {
4920
+ key: "setAdBreakDurationBoundary",
4921
+ value: function setAdBreakDurationBoundary(durationMs) {
4922
+ this.expectedAdBreakDurationMs = durationMs;
4923
+ if (durationMs != null && this.currentAdBreakStartWallClockMs != null) {
4924
+ this.scteAdBreakEndWallClockMs = this.currentAdBreakStartWallClockMs + durationMs;
4925
+ } else {
4926
+ this.scteAdBreakEndWallClockMs = void 0;
4927
+ }
4928
+ }
4929
+ },
4930
+ {
4931
+ key: "scheduleAdStopAtBreakBoundary",
4932
+ value: function scheduleAdStopAtBreakBoundary() {
4933
+ if (this.expectedAdBreakDurationMs == null) {
4934
+ return;
4935
+ }
4936
+ this.scheduleAdStopCountdown(this.getRemainingAdMs());
4849
4937
  }
4850
4938
  },
4851
4939
  {
@@ -6473,7 +6561,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6473
6561
  }
6474
6562
  }
6475
6563
  this.inAdBreak = true;
6476
- this.currentAdBreakStartWallClockMs = Date.now();
6477
6564
  this.currentAdIndex = 0;
6478
6565
  this.totalAdsInBreak = 1;
6479
6566
  this.adPodQueue = [];
@@ -6560,7 +6647,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6560
6647
  case 2:
6561
6648
  _state.sent();
6562
6649
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6563
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
6650
+ this.scheduleAdStopAtBreakBoundary();
6564
6651
  }
6565
6652
  adVolume = currentMuted ? 0 : currentVolume;
6566
6653
  this.ima.setAdVolume(adVolume);
@@ -6603,7 +6690,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6603
6690
  case 6:
6604
6691
  _state.sent();
6605
6692
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6606
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
6693
+ this.scheduleAdStopAtBreakBoundary();
6607
6694
  }
6608
6695
  adVolume1 = currentMuted ? 0 : currentVolume;
6609
6696
  this.ima.setAdVolume(adVolume1);
@@ -6658,7 +6745,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6658
6745
  case 10:
6659
6746
  _state.sent();
6660
6747
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6661
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
6748
+ this.scheduleAdStopAtBreakBoundary();
6662
6749
  }
6663
6750
  adVolume2 = currentMuted ? 0 : currentVolume;
6664
6751
  this.ima.setAdVolume(adVolume2);
@@ -7010,7 +7097,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7010
7097
  case 2:
7011
7098
  _state.sent();
7012
7099
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7013
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
7100
+ this.scheduleAdStopAtBreakBoundary();
7014
7101
  }
7015
7102
  currentMuted = this.video.muted;
7016
7103
  currentVolume = this.video.volume;
@@ -7510,21 +7597,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7510
7597
  {
7511
7598
  key: "ensureAdStoppedByTimer",
7512
7599
  value: function ensureAdStoppedByTimer() {
7513
- var _this_config_adBreakCheckIntervalMs, _this_expectedAdBreakDurationMs;
7600
+ var _this_config_adBreakCheckIntervalMs, _this_getAdBreakEndWallClockMs;
7514
7601
  if (!this.inAdBreak) return;
7515
7602
  this.adStopTimerId = void 0;
7516
7603
  var adPlaying = this.ima.isAdPlaying();
7517
- var pendingAds = this.adPodQueue.length > 0;
7518
7604
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
7519
7605
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
7520
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
7521
- var elapsedSinceStartMs = 0;
7522
- if (this.currentAdBreakStartWallClockMs != null) {
7523
- elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
7524
- }
7525
- var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
7526
- var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
7527
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
7606
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
7607
+ var endWallClockMs = (_this_getAdBreakEndWallClockMs = this.getAdBreakEndWallClockMs()) !== null && _this_getAdBreakEndWallClockMs !== void 0 ? _this_getAdBreakEndWallClockMs : Date.now();
7608
+ var overrunMs = Math.max(0, Date.now() - endWallClockMs);
7609
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
7528
7610
  if (shouldExtendAdBreak) {
7529
7611
  this.scheduleAdStopCountdown(checkIntervalMs);
7530
7612
  return;
@@ -7790,7 +7872,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7790
7872
  case 5:
7791
7873
  _state.sent();
7792
7874
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7793
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
7875
+ this.scheduleAdStopAtBreakBoundary();
7794
7876
  }
7795
7877
  currentMuted = this.video.muted;
7796
7878
  currentVolume = this.video.volume;
@@ -7840,7 +7922,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7840
7922
  case 8:
7841
7923
  _state.sent();
7842
7924
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7843
- this.scheduleAdStopCountdown(this.getRemainingAdMs());
7925
+ this.scheduleAdStopAtBreakBoundary();
7844
7926
  }
7845
7927
  currentMuted1 = this.video.muted;
7846
7928
  currentVolume1 = this.video.volume;
@@ -7985,6 +8067,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7985
8067
  this.scheduledScte35BreakKey = void 0;
7986
8068
  this.expectedAdBreakDurationMs = void 0;
7987
8069
  this.currentAdBreakStartWallClockMs = void 0;
8070
+ this.scteAdBreakEndWallClockMs = void 0;
7988
8071
  this.clearAdStartTimer();
7989
8072
  this.clearAdStopTimer();
7990
8073
  this.adPodQueue = [];
@@ -8174,9 +8257,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8174
8257
  {
8175
8258
  key: "getRemainingAdMs",
8176
8259
  value: function getRemainingAdMs() {
8177
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
8178
- var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
8179
- return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
8260
+ var endWallClockMs = this.getAdBreakEndWallClockMs();
8261
+ if (endWallClockMs != null) {
8262
+ return Math.max(0, endWallClockMs - Date.now());
8263
+ }
8264
+ return 0;
8265
+ }
8266
+ },
8267
+ {
8268
+ key: "getAdBreakEndWallClockMs",
8269
+ value: function getAdBreakEndWallClockMs() {
8270
+ if (this.scteAdBreakEndWallClockMs != null) {
8271
+ return this.scteAdBreakEndWallClockMs;
8272
+ }
8273
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return void 0;
8274
+ return this.currentAdBreakStartWallClockMs + this.expectedAdBreakDurationMs;
8180
8275
  }
8181
8276
  },
8182
8277
  {
@@ -10963,32 +11058,47 @@ var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[p
10963
11058
  var StormcloudPlayer_default = StormcloudPlayer;
10964
11059
  // Annotate the CommonJS export names for ESM import in node:
10965
11060
  0 && (module.exports = {
11061
+ DEFAULT_MQTT_CONFIG: DEFAULT_MQTT_CONFIG,
10966
11062
  IS_BROWSER: IS_BROWSER,
10967
11063
  IS_GLOBAL: IS_GLOBAL,
10968
11064
  IS_IOS: IS_IOS,
10969
11065
  IS_SAFARI: IS_SAFARI,
11066
+ MQTT_CA_CERT_FILE: MQTT_CA_CERT_FILE,
10970
11067
  SUPPORTS_DASH: SUPPORTS_DASH,
10971
11068
  SUPPORTS_HLS: SUPPORTS_HLS,
10972
11069
  StormcloudPlayer: StormcloudPlayer,
10973
11070
  StormcloudVideoPlayer: StormcloudVideoPlayer,
10974
11071
  StormcloudVideoPlayerComponent: StormcloudVideoPlayerComponent,
11072
+ applyMQTTConfig: applyMQTTConfig,
11073
+ buildMQTTBrokerUrl: buildMQTTBrokerUrl,
11074
+ buildPlayerTopic: buildPlayerTopic,
10975
11075
  canPlay: canPlay,
11076
+ configureMQTT: configureMQTT,
10976
11077
  createHlsAdPlayer: createHlsAdPlayer,
10977
11078
  createImaController: createImaController,
10978
11079
  createStormcloudPlayer: createStormcloudPlayer,
10979
11080
  detectBrowser: detectBrowser,
11081
+ disconnectMQTT: disconnectMQTT,
11082
+ ensureMQTTClient: ensureMQTTClient,
10980
11083
  getBrowserConfigOverrides: getBrowserConfigOverrides,
10981
11084
  getBrowserID: getBrowserID,
10982
11085
  getClientInfo: getClientInfo,
11086
+ getMQTTStatus: getMQTTStatus,
10983
11087
  getRecommendedAdPlayer: getRecommendedAdPlayer,
11088
+ initMQTTClient: initMQTTClient,
10984
11089
  initializePolyfills: initializePolyfills,
11090
+ isMQTTConfigured: isMQTTConfigured,
11091
+ isMQTTConnected: isMQTTConnected,
11092
+ isMQTTEnabled: isMQTTEnabled,
10985
11093
  isMediaStream: isMediaStream,
10986
11094
  lazy: lazy,
10987
11095
  logBrowserInfo: logBrowserInfo,
10988
11096
  merge: merge,
11097
+ mqttConfig: mqttConfig,
10989
11098
  omit: omit,
10990
11099
  parseQuery: parseQuery,
10991
11100
  players: players,
11101
+ publishMQTT: publishMQTT,
10992
11102
  randomString: randomString,
10993
11103
  sendAdDetectTracking: sendAdDetectTracking,
10994
11104
  sendAdImpressionTracking: sendAdImpressionTracking,