@stormstreaming/stormstreamer 1.0.6 → 1.0.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.0.7
4
+
5
+ Fixed race condition in StageController where resize methods could fire after detach, causing "Cannot read properties of null" errors.
6
+
3
7
  ## 1.0.6
4
8
 
5
9
  Fixed intervals not being properly cleaned up when player instance is removed,
package/dist/amd/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * contact@stormstreaming.com
5
5
  * https://stormstreaming.com
6
6
  *
7
- * Version: 1.0.6
8
- * Version: 2/22/2026, 12:57:01 PM
7
+ * Version: 1.0.7
8
+ * Version: 2/24/2026, 9:59:15 AM
9
9
  *
10
10
  * LEGAL NOTICE:
11
11
  * This software is subject to the terms and conditions defined in
@@ -2774,12 +2774,12 @@
2774
2774
  */
2775
2775
  this._tempContainerHeight = 0;
2776
2776
  /**
2777
- * Video width (initally 0, after "onmetadata" event is updated to match the real one
2777
+ * Video width (initially 0, after "onmetadata" event is updated to match the real one)
2778
2778
  * @private
2779
2779
  */
2780
2780
  this._videoWidth = 0;
2781
2781
  /**
2782
- * Video width (initally 0, after "onmetadata" event is updated to match the real one
2782
+ * Video height (initially 0, after "onmetadata" event is updated to match the real one)
2783
2783
  * @private
2784
2784
  */
2785
2785
  this._videoHeight = 0;
@@ -2799,7 +2799,7 @@
2799
2799
  */
2800
2800
  this._isResizing = false;
2801
2801
  /**
2802
- * Deco
2802
+ * Auto resize flag
2803
2803
  * @private
2804
2804
  */
2805
2805
  this._autoResizeEnabled = true;
@@ -2855,16 +2855,17 @@
2855
2855
  this._screenElement = new ScreenElement(this._main);
2856
2856
  this._videoContainer.appendChild(this._screenElement.getVideoElement());
2857
2857
  const debounceValue = this._main.getConfigManager().getSettingsData().getVideoData().resizeDebounce;
2858
+ // FIX: debounce(() => ()=>{}) zwracało funkcję zamiast ją wywoływać
2858
2859
  if (debounceValue > 0) {
2859
- this._resizeObserver = new ResizeObserver(debounce$1(() => () => {
2860
- if (this._autoResizeEnabled) this.handleResize();
2860
+ this._resizeObserver = new ResizeObserver(debounce$1(() => {
2861
+ if (this._autoResizeEnabled && this._parentElement) this.handleResize();
2861
2862
  }, debounceValue, {
2862
2863
  leading: false,
2863
2864
  trailing: true
2864
2865
  }));
2865
2866
  } else {
2866
2867
  this._resizeObserver = new ResizeObserver(() => {
2867
- if (this._autoResizeEnabled) this.handleResize();
2868
+ if (this._autoResizeEnabled && this._parentElement) this.handleResize();
2868
2869
  });
2869
2870
  }
2870
2871
  this._fullscreenChangeHandler = this.onFullScreenChange;
@@ -2892,7 +2893,7 @@
2892
2893
  */
2893
2894
  attachToParent(container) {
2894
2895
  // preparing elements
2895
- let result = false; // To indicate the success of the operation
2896
+ let result = false;
2896
2897
  let tempParentElement = null;
2897
2898
  // Check if container is either an ID string or an HTMLElement instance
2898
2899
  if (typeof container === "string") {
@@ -2914,20 +2915,19 @@
2914
2915
  this._parentElement = tempParentElement;
2915
2916
  this._parentElement.appendChild(this._videoContainer);
2916
2917
  this._resizeObserver.observe(this._parentElement);
2917
- this._parentElement.addEventListener("transitionend", () => {
2918
- this.handleResize();
2919
- });
2918
+ // FIX: użyj zapisanego handlera zamiast anonimowej funkcji
2919
+ this._parentElement.addEventListener("transitionend", this._transitionEndHandler);
2920
2920
  this._main.dispatchEvent("containerChange", {
2921
2921
  ref: this._main,
2922
2922
  container: this._parentElement
2923
2923
  });
2924
2924
  this.handleResize();
2925
- result = true; // Operation successful
2925
+ result = true;
2926
2926
  } else {
2927
2927
  if (this._debug) this._logger.decoratedLog("Attach To Parent: " + container + " (failure - container not found)", "dark-pink");
2928
2928
  this._logger.warning(this, "attachToParent :: container \"" + container + "\"+ was not found");
2929
2929
  }
2930
- return result; // Return the result of the operation
2930
+ return result;
2931
2931
  }
2932
2932
  /**
2933
2933
  * Detaches the library from a parent container
@@ -2937,7 +2937,16 @@
2937
2937
  let result = false;
2938
2938
  if (this._parentElement != null && this._videoContainer != null) {
2939
2939
  this._logger.info(this, "Detaching from parent: " + this._videoContainer);
2940
- // POPRAWKA: Usuń event listener z zapisanym handlerem
2940
+ // FIX: anuluj pending requestAnimationFrame żeby callback nie strzelił na null parent
2941
+ if (this._animationFrameId !== null) {
2942
+ cancelAnimationFrame(this._animationFrameId);
2943
+ this._animationFrameId = null;
2944
+ // przywróć overflow jeśli został zmieniony przez handleResize
2945
+ if (this._isResizing) {
2946
+ this._parentElement.style.overflow = this._parentOriginalOverflow;
2947
+ }
2948
+ this._isResizing = false;
2949
+ }
2941
2950
  if (this._transitionEndHandler) {
2942
2951
  this._parentElement.removeEventListener("transitionend", this._transitionEndHandler);
2943
2952
  }
@@ -2958,7 +2967,6 @@
2958
2967
  }
2959
2968
  handleResize() {
2960
2969
  if (!this._parentElement || this._isResizing || this._isDestroying) return;
2961
- // POPRAWKA: Anulowanie poprzedniego requestAnimationFrame
2962
2970
  if (this._animationFrameId !== null) {
2963
2971
  cancelAnimationFrame(this._animationFrameId);
2964
2972
  }
@@ -2966,7 +2974,12 @@
2966
2974
  this._parentOriginalOverflow = this._parentElement.style.overflow;
2967
2975
  this._parentElement.style.overflow = 'hidden';
2968
2976
  this._animationFrameId = requestAnimationFrame(() => {
2969
- if (this._isDestroying) return;
2977
+ // FIX: ponowny guard - parentElement mógł zostać null między kolejkowaniem a wykonaniem RAF
2978
+ if (this._isDestroying || !this._parentElement) {
2979
+ this._isResizing = false;
2980
+ this._animationFrameId = null;
2981
+ return;
2982
+ }
2970
2983
  this.calculateNewDimensions();
2971
2984
  if (this._parentElement) {
2972
2985
  this._parentElement.style.overflow = this._parentOriginalOverflow;
@@ -2983,6 +2996,7 @@
2983
2996
  });
2984
2997
  }
2985
2998
  calculateNewDimensions() {
2999
+ if (!this._parentElement) return;
2986
3000
  const calcMethod = this._main.getConfigManager().getSettingsData().getVideoData().parentSizeCalculationMethod;
2987
3001
  switch (calcMethod) {
2988
3002
  case SizeCalculationType.CLIENT_DIMENSIONS:
@@ -3080,7 +3094,6 @@
3080
3094
  //--- LETTER BOX
3081
3095
  case ScalingType.LETTER_BOX:
3082
3096
  {
3083
- // jeżeli szerokość
3084
3097
  videoWidth = this._containerWidth;
3085
3098
  videoHeight = this._videoHeight * this._containerWidth / this._videoWidth;
3086
3099
  if (videoHeight <= this._containerHeight) {
@@ -3255,42 +3268,37 @@
3255
3268
  }
3256
3269
  this._fullscreenChangeHandler = null;
3257
3270
  }
3258
- // 6. Zniszcz ScreenElement
3271
+ // 5. Zniszcz ScreenElement
3259
3272
  if (this._screenElement) {
3260
- // Wyczyść video element
3261
3273
  const videoElement = this._screenElement.getVideoElement();
3262
3274
  if (videoElement) {
3263
- // Zatrzymaj wszelkie strumienie
3264
3275
  if (videoElement.srcObject instanceof MediaStream) {
3265
3276
  videoElement.srcObject.getTracks().forEach(track => {
3266
3277
  track.enabled = false;
3267
3278
  track.stop();
3268
3279
  });
3269
3280
  }
3270
- // Wyczyść element
3271
3281
  videoElement.pause();
3272
3282
  videoElement.removeAttribute('src');
3273
3283
  videoElement.srcObject = null;
3274
3284
  videoElement.load();
3275
- // Usuń z DOM jeśli jest podłączony
3276
3285
  if (videoElement.parentNode) {
3277
3286
  videoElement.parentNode.removeChild(videoElement);
3278
3287
  }
3279
3288
  }
3280
- // Jeśli ScreenElement ma własną metodę destroy, wywołaj ją
3281
3289
  if (typeof this._screenElement.destroy === 'function') {
3282
3290
  this._screenElement.destroy();
3283
3291
  }
3284
3292
  this._screenElement = null;
3285
3293
  }
3286
- // 7. Usuń kontener video z DOM
3294
+ // 6. Usuń kontener video z DOM
3287
3295
  if (this._videoContainer) {
3288
3296
  if (this._videoContainer.parentNode) {
3289
3297
  this._videoContainer.parentNode.removeChild(this._videoContainer);
3290
3298
  }
3291
3299
  this._videoContainer = null;
3292
3300
  }
3293
- // 8. Resetuj zmienne
3301
+ // 7. Resetuj zmienne
3294
3302
  this._containerWidth = 0;
3295
3303
  this._containerHeight = 0;
3296
3304
  this._tempContainerWidth = 0;
@@ -3300,6 +3308,7 @@
3300
3308
  this.isInFullScreenMode = false;
3301
3309
  this._isResizing = false;
3302
3310
  this._autoResizeEnabled = false;
3311
+ this._transitionEndHandler = null;
3303
3312
  this._logger.success(this, "StageController successfully destroyed");
3304
3313
  } catch (error) {
3305
3314
  this._logger.error(this, "Error during destroy: " + error);
@@ -6760,8 +6769,8 @@
6760
6769
  constructor(streamConfig, autoInitialize = false) {
6761
6770
  super();
6762
6771
  this.DEV_MODE = true;
6763
- this.STREAMER_VERSION = "1.0.6";
6764
- this.COMPILE_DATE = "2/22/2026, 12:56:59 PM";
6772
+ this.STREAMER_VERSION = "1.0.7";
6773
+ this.COMPILE_DATE = "2/24/2026, 9:59:13 AM";
6765
6774
  this.STREAMER_BRANCH = "Experimental";
6766
6775
  this.STREAMER_PROTOCOL_VERSION = 1;
6767
6776
  this._initialized = false;