myetv-player 1.0.6 → 1.0.10

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.
@@ -448,6 +448,8 @@ constructor(videoElement, options = {}) {
448
448
  dashLibUrl: 'https://cdn.dashjs.org/latest/dash.all.min.js', // Dash.js library URL
449
449
  hlsLibUrl: 'https://cdn.jsdelivr.net/npm/hls.js@latest', // HLS.js library URL
450
450
  adaptiveQualityControl: true, // Show quality control for adaptive streams
451
+ //seek shape
452
+ seekHandleShape: 'circle', // Available shape: none, circle, square, diamond, arrow, triangle, heart, star
451
453
  // AUDIO PLAYER
452
454
  audiofile: false,
453
455
  audiowave: false,
@@ -1531,14 +1533,19 @@ seek(e) {
1531
1533
  if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
1532
1534
 
1533
1535
  const rect = this.progressContainer.getBoundingClientRect();
1534
- const clickX = e.clientX - rect.left;
1536
+
1537
+ // Support both mouse and touch events
1538
+ const clientX = e.clientX !== undefined ? e.clientX : (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));
1539
+
1540
+ const clickX = clientX - rect.left;
1535
1541
  const percentage = Math.max(0, Math.min(1, clickX / rect.width));
1536
1542
 
1537
1543
  if (this.video.duration && !isNaN(this.video.duration)) {
1538
1544
  this.video.currentTime = percentage * this.video.duration;
1539
- const progress = percentage * 100;
1540
- this.progressFilled.style.width = progress + '%';
1541
- this.progressHandle.style.left = progress + '%';
1545
+
1546
+ const progress = `${percentage * 100}%`;
1547
+ this.progressFilled.style.width = progress;
1548
+ this.progressHandle.style.left = progress;
1542
1549
  }
1543
1550
  }
1544
1551
 
@@ -2091,6 +2098,51 @@ loadScript(src) {
2091
2098
  });
2092
2099
  }
2093
2100
 
2101
+ /**
2102
+ * Set seek handle shape dynamically
2103
+ * @param {string} shape - Shape type: none, circle, square, diamond, arrow, triangle, heart, star
2104
+ * @returns {Object} this
2105
+ */
2106
+ setSeekHandleShape(shape) {
2107
+ const validShapes = ['none', 'circle', 'square', 'diamond', 'arrow', 'triangle', 'heart', 'star'];
2108
+
2109
+ if (!validShapes.includes(shape)) {
2110
+ if (this.options.debug) console.warn('Invalid seek handle shape:', shape);
2111
+ return this;
2112
+ }
2113
+
2114
+ this.options.seekHandleShape = shape;
2115
+
2116
+ // Update handle class
2117
+ if (this.progressHandle) {
2118
+ // Remove all shape classes
2119
+ validShapes.forEach(s => {
2120
+ this.progressHandle.classList.remove(`progress-handle-${s}`);
2121
+ });
2122
+ // Add new shape class
2123
+ this.progressHandle.classList.add(`progress-handle-${shape}`);
2124
+ }
2125
+
2126
+ if (this.options.debug) console.log('Seek handle shape changed to:', shape);
2127
+ return this;
2128
+ }
2129
+
2130
+ /**
2131
+ * Get current seek handle shape
2132
+ * @returns {string} Current shape
2133
+ */
2134
+ getSeekHandleShape() {
2135
+ return this.options.seekHandleShape;
2136
+ }
2137
+
2138
+ /**
2139
+ * Get available seek handle shapes
2140
+ * @returns {Array} Array of available shapes
2141
+ */
2142
+ getAvailableSeekHandleShapes() {
2143
+ return ['none', 'circle', 'square', 'diamond', 'arrow', 'triangle', 'heart', 'star'];
2144
+ }
2145
+
2094
2146
  dispose() {
2095
2147
  if (this.qualityMonitorInterval) {
2096
2148
  clearInterval(this.qualityMonitorInterval);
@@ -2467,12 +2519,28 @@ addEventListener(eventType, callback) {
2467
2519
  });
2468
2520
  }
2469
2521
 
2470
- if (this.progressContainer) {
2471
- this.progressContainer.addEventListener('click', (e) => this.seek(e));
2472
- this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
2473
- }
2522
+ if (this.progressContainer) {
2523
+ // Mouse events (desktop)
2524
+ this.progressContainer.addEventListener('click', (e) => this.seek(e));
2525
+ this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
2526
+
2527
+ // Touch events (mobile)
2528
+ this.progressContainer.addEventListener('touchstart', (e) => {
2529
+ e.preventDefault(); // Prevent scrolling when touching the seek bar
2530
+ this.startSeeking(e);
2531
+ }, { passive: false });
2474
2532
 
2475
2533
  this.setupSeekTooltip();
2534
+ }
2535
+
2536
+ // Add touch events directly on the handle for better mobile dragging
2537
+ if (this.progressHandle) {
2538
+ this.progressHandle.addEventListener('touchstart', (e) => {
2539
+ e.preventDefault(); // Prevent default touch behavior
2540
+ e.stopPropagation(); // Stop event from bubbling to progressContainer
2541
+ this.startSeeking(e);
2542
+ }, { passive: false });
2543
+ }
2476
2544
 
2477
2545
  // NOTE: Auto-hide events are handled in initAutoHide() after everything is ready
2478
2546
 
@@ -2493,7 +2561,19 @@ addEventListener(eventType, callback) {
2493
2561
  document.addEventListener('mozfullscreenchange', () => this.updateFullscreenButton());
2494
2562
 
2495
2563
  document.addEventListener('mousemove', (e) => this.continueSeeking(e));
2496
- document.addEventListener('mouseup', () => this.endSeeking());
2564
+ document.addEventListener('mouseup', () => this.endSeeking());
2565
+
2566
+ // Touch events for seeking (mobile)
2567
+ document.addEventListener('touchmove', (e) => {
2568
+ if (this.isUserSeeking) {
2569
+ e.preventDefault(); // Prevent scrolling while seeking
2570
+ this.continueSeeking(e);
2571
+ }
2572
+ }, { passive: false });
2573
+
2574
+ document.addEventListener('touchend', () => this.endSeeking());
2575
+ document.addEventListener('touchcancel', () => this.endSeeking());
2576
+
2497
2577
  }
2498
2578
 
2499
2579
  /* Controls Module for MYETV Video Player
@@ -2501,7 +2581,7 @@ addEventListener(eventType, callback) {
2501
2581
  * Created by https://www.myetv.tv https://oskarcosimo.com
2502
2582
  */
2503
2583
 
2504
- /* AUTO-HIDE SYSTEM - IMPROVED AND COMPREHENSIVE */
2584
+ /* AUTO-HIDE SYSTEM */
2505
2585
  initAutoHide() {
2506
2586
  if (!this.options.autoHide) {
2507
2587
  if (this.options.debug) console.log('Auto-hide disabled in options');
@@ -2641,12 +2721,17 @@ showControlsNow() {
2641
2721
  this.controls.classList.add('show');
2642
2722
  }
2643
2723
 
2644
- // ADD THIS: Add has-controls class to container for watermark visibility
2724
+ // Add has-controls class to container for watermark visibility
2645
2725
  if (this.container) {
2646
2726
  this.container.classList.add('has-controls');
2727
+ this.updateControlbarHeight();
2728
+ // Update watermark position
2729
+ if (this.updateWatermarkPosition) {
2730
+ this.updateWatermarkPosition();
2731
+ }
2647
2732
  }
2648
2733
 
2649
- // Fix: Show title overlay with controls if not persistent
2734
+ // Show title overlay with controls if not persistent
2650
2735
  if (this.options.showTitleOverlay && !this.options.persistentTitle && this.options.videoTitle) {
2651
2736
  this.showTitleOverlay();
2652
2737
  }
@@ -2657,32 +2742,44 @@ showControlsNow() {
2657
2742
  hideControlsNow() {
2658
2743
  // Don't hide if mouse is still over controls (allow hiding on touch devices)
2659
2744
  const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
2745
+
2660
2746
  if (this.mouseOverControls && !isTouchDevice) {
2661
- if (this.autoHideDebug && this.options.debug) console.log('⏸️ Not hiding - mouse still over controls');
2747
+ if (this.autoHideDebug && this.options.debug) {
2748
+ console.log('🚫 Not hiding - mouse still over controls');
2749
+ }
2662
2750
  return;
2663
2751
  }
2664
2752
 
2665
2753
  // Don't hide if video is paused
2666
2754
  if (this.video && this.video.paused) {
2667
- if (this.autoHideDebug && this.options.debug) console.log('⏸️ Not hiding - video is paused');
2755
+ if (this.autoHideDebug && this.options.debug) {
2756
+ console.log('🚫 Not hiding - video is paused');
2757
+ }
2668
2758
  return;
2669
2759
  }
2670
2760
 
2671
2761
  if (this.controls) {
2672
2762
  this.controls.classList.remove('show');
2673
- }
2674
2763
 
2675
- // ADD THIS: Remove has-controls class from container for watermark visibility
2676
- if (this.container) {
2677
- this.container.classList.remove('has-controls');
2764
+ // Remove has-controls class from container for watermark visibility
2765
+ if (this.container) {
2766
+ this.container.classList.remove('has-controls');
2767
+ this.updateControlbarHeight();
2768
+ // Update watermark position
2769
+ if (this.updateWatermarkPosition) {
2770
+ this.updateWatermarkPosition();
2771
+ }
2772
+ }
2678
2773
  }
2679
2774
 
2680
- // Fix: Hide title overlay with controls if not persistent
2775
+ // Hide title overlay with controls (if not persistent)
2681
2776
  if (this.options.showTitleOverlay && !this.options.persistentTitle) {
2682
2777
  this.hideTitleOverlay();
2683
2778
  }
2684
2779
 
2685
- if (this.autoHideDebug && this.options.debug) console.log('❌ Controls hidden');
2780
+ if (this.autoHideDebug && this.options.debug) {
2781
+ console.log('👁️ Controls hidden');
2782
+ }
2686
2783
  }
2687
2784
 
2688
2785
  showControls() {
@@ -2851,7 +2948,7 @@ createControls() {
2851
2948
  <div class="progress-bar">
2852
2949
  <div class="progress-buffer"></div>
2853
2950
  <div class="progress-filled"></div>
2854
- <div class="progress-handle"></div>
2951
+ <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
2855
2952
  </div>
2856
2953
  ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
2857
2954
  </div>
@@ -2961,28 +3058,62 @@ createControls() {
2961
3058
  // NEW: Initialize responsive settings menu
2962
3059
  setTimeout(() => {
2963
3060
  this.initializeResponsiveMenu();
3061
+ this.updateControlbarHeight();
2964
3062
  }, 100);
2965
3063
  }
2966
3064
 
2967
- /* NEW: Initialize responsive menu with dynamic width calculation */
3065
+ /* Initialize responsive menu with dynamic width calculation */
2968
3066
  initializeResponsiveMenu() {
2969
3067
  if (!this.controls) return;
2970
3068
 
2971
3069
  // Track screen size
2972
3070
  this.isSmallScreen = false;
2973
3071
 
2974
- // Check initial size and set up listener
3072
+ // Check initial size
2975
3073
  this.checkScreenSize();
2976
3074
 
2977
- window.addEventListener('resize', () => {
3075
+ // Bind resize handler with updateControlbarHeight
3076
+ const resizeHandler = () => {
2978
3077
  this.checkScreenSize();
2979
- });
3078
+ this.updateControlbarHeight();
3079
+ };
3080
+
3081
+ // Bind del context
3082
+ this.resizeHandler = resizeHandler.bind(this);
3083
+ window.addEventListener('resize', this.resizeHandler);
2980
3084
 
2981
3085
  // Bind events for settings menu
2982
3086
  this.bindSettingsMenuEvents();
2983
3087
  }
2984
3088
 
2985
- /* NEW: Dynamic width calculation based on logo presence */
3089
+ // Dynamic controlbar height tracking for watermark positioning
3090
+ updateControlbarHeight() {
3091
+ if (!this.controls) return;
3092
+
3093
+ const height = this.controls.offsetHeight;
3094
+ if (this.container) {
3095
+
3096
+ this.container.style.setProperty('--player-controls-height', `${height}px`);
3097
+
3098
+ const watermark = this.container.querySelector('.video-watermark.watermark-bottomleft, .video-watermark.watermark-bottomright');
3099
+ if (watermark) {
3100
+ const hasControls = this.container.classList.contains('has-controls');
3101
+ const isHideOnAutoHide = watermark.classList.contains('hide-on-autohide');
3102
+
3103
+ if (hasControls || !isHideOnAutoHide) {
3104
+ watermark.style.bottom = `${height + 15}px`;
3105
+ } else {
3106
+ watermark.style.bottom = '15px';
3107
+ }
3108
+ }
3109
+ }
3110
+
3111
+ if (this.options.debug) {
3112
+ console.log(`Controlbar height updated: ${height}px`);
3113
+ }
3114
+ }
3115
+
3116
+ /* Dynamic width calculation based on logo presence */
2986
3117
  getResponsiveThreshold() {
2987
3118
  // Check if brand logo is enabled and present
2988
3119
  const hasLogo = this.options.brandLogoEnabled && this.options.brandLogoUrl;
@@ -2991,7 +3122,7 @@ getResponsiveThreshold() {
2991
3122
  return hasLogo ? 650 : 550;
2992
3123
  }
2993
3124
 
2994
- /* NEW: Check if screen is under dynamic threshold */
3125
+ /* Check if screen is under dynamic threshold */
2995
3126
  checkScreenSize() {
2996
3127
  const threshold = this.getResponsiveThreshold();
2997
3128
  const newIsSmallScreen = window.innerWidth <= threshold;
@@ -3001,12 +3132,12 @@ checkScreenSize() {
3001
3132
  this.updateSettingsMenuVisibility();
3002
3133
 
3003
3134
  if (this.options.debug) {
3004
- console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px threshold (logo: ${this.options.brandLogoEnabled}), small: ${this.isSmallScreen}`);
3135
+ console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px (threshold), logo: ${this.options.brandLogoEnabled}, small: ${this.isSmallScreen}`);
3005
3136
  }
3006
3137
  }
3007
3138
  }
3008
3139
 
3009
- /* NEW: Update settings menu visibility based on screen size */
3140
+ /* Update settings menu visibility based on screen size */
3010
3141
  updateSettingsMenuVisibility() {
3011
3142
  const settingsControl = this.controls?.querySelector('.settings-control');
3012
3143
  if (!settingsControl) return;
@@ -3046,7 +3177,7 @@ updateSettingsMenuVisibility() {
3046
3177
  }
3047
3178
  }
3048
3179
 
3049
- /* NEW: Populate settings menu with controls */
3180
+ /* Populate settings menu with controls */
3050
3181
  populateSettingsMenu() {
3051
3182
  const settingsMenu = this.controls?.querySelector('.settings-menu');
3052
3183
  if (!settingsMenu) return;
@@ -3108,7 +3239,7 @@ populateSettingsMenu() {
3108
3239
  settingsMenu.innerHTML = menuHTML;
3109
3240
  }
3110
3241
 
3111
- /* NEW: Bind settings menu events */
3242
+ /* Bind settings menu events */
3112
3243
  bindSettingsMenuEvents() {
3113
3244
  const settingsMenu = this.controls?.querySelector('.settings-menu');
3114
3245
  if (!settingsMenu) return;
@@ -5737,9 +5868,19 @@ initializeWatermark() {
5737
5868
  this.container.appendChild(watermark);
5738
5869
  }
5739
5870
 
5871
+ // Store reference to watermark element
5740
5872
  // Store reference to watermark element
5741
5873
  this.watermarkElement = watermark;
5742
5874
 
5875
+ // Set initial position
5876
+ this.updateWatermarkPosition();
5877
+
5878
+ // Update position on window resize
5879
+ this.watermarkResizeHandler = () => {
5880
+ this.updateWatermarkPosition();
5881
+ };
5882
+ window.addEventListener('resize', this.watermarkResizeHandler);
5883
+
5743
5884
  if (this.options.debug) {
5744
5885
  console.log('🏷️ Watermark created:', {
5745
5886
  url: this.options.watermarkUrl,
@@ -5758,6 +5899,7 @@ initializeWatermark() {
5758
5899
  * @param {string} position - Position of watermark (topleft, topright, bottomleft, bottomright)
5759
5900
  * @param {string} title - Optional tooltip title for the watermark
5760
5901
  */
5902
+
5761
5903
  setWatermark(url, link = '', position = 'bottomright', title = '') {
5762
5904
  // Update options
5763
5905
  this.options.watermarkUrl = url;
@@ -5788,6 +5930,12 @@ removeWatermark() {
5788
5930
  this.watermarkElement = null;
5789
5931
  }
5790
5932
 
5933
+ // Remove resize listener
5934
+ if (this.watermarkResizeHandler) {
5935
+ window.removeEventListener('resize', this.watermarkResizeHandler);
5936
+ this.watermarkResizeHandler = null;
5937
+ }
5938
+
5791
5939
  this.options.watermarkUrl = '';
5792
5940
  this.options.watermarkLink = '';
5793
5941
  this.options.watermarkPosition = 'bottomright';
@@ -5802,6 +5950,7 @@ removeWatermark() {
5802
5950
  * Update watermark position
5803
5951
  * @param {string} position - New position (topleft, topright, bottomleft, bottomright)
5804
5952
  */
5953
+
5805
5954
  setWatermarkPosition(position) {
5806
5955
  if (!['topleft', 'topright', 'bottomleft', 'bottomright'].includes(position)) {
5807
5956
  if (this.options.debug) console.warn('🏷️ Invalid watermark position:', position);
@@ -5828,6 +5977,39 @@ setWatermarkPosition(position) {
5828
5977
  return this;
5829
5978
  }
5830
5979
 
5980
+ /**
5981
+ * Update watermark position based on current controlbar height
5982
+ * Called during window resize to keep watermark above controlbar
5983
+ */
5984
+ updateWatermarkPosition() {
5985
+ if (!this.watermarkElement) return;
5986
+ if (!this.controls) return;
5987
+
5988
+ const position = this.options.watermarkPosition || 'bottomright';
5989
+
5990
+ // Only update bottom positions (top positions don't need adjustment)
5991
+ if (position === 'bottomleft' || position === 'bottomright') {
5992
+ const controlsHeight = this.controls.offsetHeight;
5993
+ const spacing = 15; // Same spacing used in CSS
5994
+ const bottomValue = controlsHeight + spacing;
5995
+
5996
+ // Check if controls are visible
5997
+ const hasControls = this.container.classList.contains('has-controls');
5998
+
5999
+ if (hasControls || !this.options.hideWatermark) {
6000
+ // Position above controlbar
6001
+ this.watermarkElement.style.bottom = `${bottomValue}px`;
6002
+ } else {
6003
+ // Position at bottom corner when controls hidden
6004
+ this.watermarkElement.style.bottom = '15px';
6005
+ }
6006
+
6007
+ if (this.options.debug) {
6008
+ console.log(`🏷️ Watermark position updated: bottom ${this.watermarkElement.style.bottom}`);
6009
+ }
6010
+ }
6011
+ }
6012
+
5831
6013
  /**
5832
6014
  * Set whether watermark should hide with controls
5833
6015
  * @param {boolean} hide - True to hide watermark with controls, false to keep always visible