myetv-player 1.1.3 → 1.1.5

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.
@@ -372,22 +372,23 @@ constructor(videoElement, options = {}) {
372
372
  }
373
373
 
374
374
  this.options = {
375
- showQualitySelector: true,
376
- showSpeedControl: true,
377
- showFullscreen: true,
378
- showPictureInPicture: true,
379
- showSubtitles: true,
380
- subtitlesEnabled: false,
381
- autoHide: true,
382
- autoHideDelay: 3000,
375
+ showQualitySelector: true, // Enable quality selector button
376
+ showSpeedControl: true, // Enable speed control button
377
+ showFullscreen: true, // Enable fullscreen button
378
+ showPictureInPicture: true, // Enable PiP button
379
+ showSubtitles: true, // Enable subtitles button
380
+ subtitlesEnabled: false, // Enable subtitles by default if available
381
+ autoHide: true, // auto-hide controls when idle
382
+ autoHideDelay: 3000, // hide controls after ... seconds of inactivity (specificed in milliseconds)
383
+ hideCursor: true, // hide mouse cursor when idle
383
384
  poster: null, // URL of poster image
384
385
  showPosterOnEnd: false, // Show poster again when video ends
385
- keyboardControls: true,
386
- showSeekTooltip: true,
387
- showTitleOverlay: false,
388
- videoTitle: '',
389
- videoSubtitle: '',
390
- persistentTitle: false,
386
+ keyboardControls: true, // Enable keyboard controls
387
+ showSeekTooltip: true, // Show tooltip on seek bar at mouse hover
388
+ showTitleOverlay: false, // Show video title overlay
389
+ videoTitle: '', // Title text to show in overlay
390
+ videoSubtitle: '', // Subtitle text to show in overlay
391
+ persistentTitle: false, // If true, title overlay stays visible
391
392
  debug: false, // Enable/disable debug logging
392
393
  autoplay: false, // if video should autoplay at start
393
394
  defaultQuality: 'auto', // 'auto', '1080p', '720p', '480p', etc.
@@ -417,8 +418,8 @@ constructor(videoElement, options = {}) {
417
418
 
418
419
  seekHandleShape: 'circle', // Available shape: none, circle, square, diamond, arrow, triangle, heart, star
419
420
 
420
- audiofile: false,
421
- audiowave: false,
421
+ audiofile: false, // if true, adapt player to audio file (hide video element)
422
+ audiowave: false, // if true, show audio wave visualization (Web Audio API)
422
423
 
423
424
  resolution: "normal", // "normal", "4:3", "16:9", "stretched", "fit-to-screen", "scale-to-fit"
424
425
  ...options
@@ -2598,6 +2599,7 @@ initAutoHide() {
2598
2599
 
2599
2600
  onMouseMoveInPlayer(e) {
2600
2601
  this.showControlsNow();
2602
+ this.showCursor();
2601
2603
  this.resetAutoHideTimer();
2602
2604
  }
2603
2605
 
@@ -2655,39 +2657,37 @@ resetAutoHideTimer() {
2655
2657
  showControlsNow() {
2656
2658
  if (this.controls) {
2657
2659
  this.controls.classList.add('show');
2658
- }
2659
2660
 
2660
2661
  if (this.container) {
2661
2662
  this.container.classList.add('has-controls');
2663
+ }
2664
+
2662
2665
  this.updateControlbarHeight();
2663
2666
 
2664
2667
  if (this.updateWatermarkPosition) {
2665
2668
  this.updateWatermarkPosition();
2666
2669
  }
2667
- }
2668
2670
 
2669
2671
  if (this.options.showTitleOverlay && !this.options.persistentTitle && this.options.videoTitle) {
2670
2672
  this.showTitleOverlay();
2671
2673
  }
2672
2674
 
2675
+ this.showCursor();
2676
+
2673
2677
  if (this.autoHideDebug && this.options.debug) console.log('✅ Controls shown');
2678
+ }
2674
2679
  }
2675
2680
 
2676
2681
  hideControlsNow() {
2677
2682
 
2678
2683
  const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
2679
-
2680
2684
  if (this.mouseOverControls && !isTouchDevice) {
2681
- if (this.autoHideDebug && this.options.debug) {
2682
- console.log('🚫 Not hiding - mouse still over controls');
2683
- }
2685
+ if (this.autoHideDebug && this.options.debug) console.log('❌ Not hiding - mouse still over controls');
2684
2686
  return;
2685
2687
  }
2686
2688
 
2687
2689
  if (this.video && this.video.paused) {
2688
- if (this.autoHideDebug && this.options.debug) {
2689
- console.log('🚫 Not hiding - video is paused');
2690
- }
2690
+ if (this.autoHideDebug && this.options.debug) console.log('❌ Not hiding - video is paused');
2691
2691
  return;
2692
2692
  }
2693
2693
 
@@ -2696,20 +2696,21 @@ hideControlsNow() {
2696
2696
 
2697
2697
  if (this.container) {
2698
2698
  this.container.classList.remove('has-controls');
2699
+ }
2700
+
2699
2701
  this.updateControlbarHeight();
2700
2702
 
2701
2703
  if (this.updateWatermarkPosition) {
2702
2704
  this.updateWatermarkPosition();
2703
2705
  }
2704
- }
2705
- }
2706
2706
 
2707
2707
  if (this.options.showTitleOverlay && !this.options.persistentTitle) {
2708
2708
  this.hideTitleOverlay();
2709
2709
  }
2710
2710
 
2711
- if (this.autoHideDebug && this.options.debug) {
2712
- console.log('👁️ Controls hidden');
2711
+ this.hideCursor();
2712
+
2713
+ if (this.autoHideDebug && this.options.debug) console.log('✅ Controls hidden');
2713
2714
  }
2714
2715
  }
2715
2716
 
@@ -3107,46 +3108,87 @@ populateSettingsMenu() {
3107
3108
  if (this.options.showSpeedControl) {
3108
3109
  const speedLabel = this.t('playback_speed') || 'Playback Speed';
3109
3110
  const currentSpeed = this.video ? this.video.playbackRate : 1;
3110
- menuHTML += `<div class="settings-option" data-action="speed">
3111
- <span class="settings-option-label">${speedLabel}</span>
3112
- <span class="settings-option-value">${currentSpeed}x</span>
3113
- <div class="settings-submenu speed-submenu">
3114
- <div class="settings-suboption ${currentSpeed === 0.5 ? 'active' : ''}" data-speed="0.5">0.5x</div>
3115
- <div class="settings-suboption ${currentSpeed === 0.75 ? 'active' : ''}" data-speed="0.75">0.75x</div>
3116
- <div class="settings-suboption ${currentSpeed === 1 ? 'active' : ''}" data-speed="1">1x</div>
3117
- <div class="settings-suboption ${currentSpeed === 1.25 ? 'active' : ''}" data-speed="1.25">1.25x</div>
3118
- <div class="settings-suboption ${currentSpeed === 1.5 ? 'active' : ''}" data-speed="1.5">1.5x</div>
3119
- <div class="settings-suboption ${currentSpeed === 2 ? 'active' : ''}" data-speed="2">2x</div>
3111
+
3112
+ menuHTML += `
3113
+ <div class="settings-expandable-wrapper">
3114
+ <div class="settings-option expandable-trigger" data-action="speed-expand">
3115
+ <span class="settings-option-label">${speedLabel}: ${currentSpeed}x</span>
3116
+ <span class="expand-arrow">▼</span>
3120
3117
  </div>
3121
- </div>`;
3118
+ <div class="settings-expandable-content" style="display: none;">`;
3119
+
3120
+ const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
3121
+ speeds.forEach(speed => {
3122
+ const isActive = Math.abs(speed - currentSpeed) < 0.01;
3123
+ menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-speed="${speed}">${speed}x</div>`;
3124
+ });
3125
+
3126
+ menuHTML += `</div></div>`;
3122
3127
  }
3123
3128
 
3124
3129
  if (this.options.showSubtitles && this.textTracks && this.textTracks.length > 0) {
3125
3130
  const subtitlesLabel = this.t('subtitles') || 'Subtitles';
3126
3131
  const currentTrack = this.currentSubtitleTrack;
3127
- const currentLabel = this.subtitlesEnabled && currentTrack
3128
- ? (currentTrack.label || 'Track')
3129
- : (this.t('subtitlesoff') || 'Off');
3130
-
3131
- menuHTML += `<div class="settings-option" data-action="subtitles">
3132
- <span class="settings-option-label">${subtitlesLabel}</span>
3133
- <span class="settings-option-value">${currentLabel}</span>
3134
- <div class="settings-submenu subtitles-submenu">
3135
- <div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">
3136
- ${this.t('subtitlesoff') || 'Off'}
3137
- </div>`;
3132
+ const currentLabel = this.subtitlesEnabled && currentTrack ? currentTrack.label : (this.t('subtitlesoff') || 'Off');
3133
+
3134
+ menuHTML += `
3135
+ <div class="settings-expandable-wrapper">
3136
+ <div class="settings-option expandable-trigger" data-action="subtitles-expand">
3137
+ <span class="settings-option-label">${subtitlesLabel}: ${currentLabel}</span>
3138
+ <span class="expand-arrow">▼</span>
3139
+ </div>
3140
+ <div class="settings-expandable-content" style="display: none;">`;
3141
+
3142
+ menuHTML += `<div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">${this.t('subtitlesoff') || 'Off'}</div>`;
3138
3143
 
3139
3144
  this.textTracks.forEach((trackData, index) => {
3140
3145
  const isActive = this.currentSubtitleTrack === trackData.track;
3141
- menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-track="${index}">
3142
- ${trackData.label}
3143
- </div>`;
3146
+ menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-track="${index}">${trackData.label}</div>`;
3144
3147
  });
3145
3148
 
3146
- menuHTML += '</div></div>';
3149
+ menuHTML += `</div></div>`;
3147
3150
  }
3148
3151
 
3149
3152
  settingsMenu.innerHTML = menuHTML;
3153
+
3154
+ this.addSettingsMenuScrollbar();
3155
+ }
3156
+
3157
+ addSettingsMenuScrollbar() {
3158
+ const settingsMenu = this.controls?.querySelector('.settings-menu');
3159
+ if (!settingsMenu) return;
3160
+
3161
+ const playerHeight = this.container.offsetHeight;
3162
+ const maxMenuHeight = playerHeight - 100;
3163
+
3164
+ settingsMenu.style.maxHeight = `${maxMenuHeight}px`;
3165
+ settingsMenu.style.overflowY = 'auto';
3166
+ settingsMenu.style.overflowX = 'hidden';
3167
+
3168
+ if (!document.getElementById('player-settings-scrollbar-style')) {
3169
+ const scrollbarStyle = document.createElement('style');
3170
+ scrollbarStyle.id = 'player-settings-scrollbar-style';
3171
+ scrollbarStyle.textContent = `
3172
+ .settings-menu::-webkit-scrollbar {
3173
+ width: 6px;
3174
+ }
3175
+ .settings-menu::-webkit-scrollbar-track {
3176
+ background: rgba(255,255,255,0.05);
3177
+ border-radius: 3px;
3178
+ }
3179
+ .settings-menu::-webkit-scrollbar-thumb {
3180
+ background: rgba(255,255,255,0.3);
3181
+ border-radius: 3px;
3182
+ }
3183
+ .settings-menu::-webkit-scrollbar-thumb:hover {
3184
+ background: rgba(255,255,255,0.5);
3185
+ }
3186
+ `;
3187
+ document.head.appendChild(scrollbarStyle);
3188
+ }
3189
+
3190
+ settingsMenu.style.scrollbarWidth = 'thin';
3191
+ settingsMenu.style.scrollbarColor = 'rgba(255,255,255,0.3) transparent';
3150
3192
  }
3151
3193
 
3152
3194
  bindSettingsMenuEvents() {
@@ -3156,6 +3198,24 @@ bindSettingsMenuEvents() {
3156
3198
  settingsMenu.addEventListener('click', (e) => {
3157
3199
  e.stopPropagation();
3158
3200
 
3201
+ if (e.target.classList.contains('expandable-trigger') || e.target.closest('.expandable-trigger')) {
3202
+ const trigger = e.target.classList.contains('expandable-trigger') ? e.target : e.target.closest('.expandable-trigger');
3203
+ const wrapper = trigger.closest('.settings-expandable-wrapper');
3204
+ const content = wrapper.querySelector('.settings-expandable-content');
3205
+ const arrow = trigger.querySelector('.expand-arrow');
3206
+
3207
+ const isExpanded = content.style.display !== 'none';
3208
+
3209
+ if (isExpanded) {
3210
+ content.style.display = 'none';
3211
+ arrow.style.transform = 'rotate(0deg)';
3212
+ } else {
3213
+ content.style.display = 'block';
3214
+ arrow.style.transform = 'rotate(180deg)';
3215
+ }
3216
+ return;
3217
+ }
3218
+
3159
3219
  if (e.target.classList.contains('settings-option') || e.target.closest('.settings-option')) {
3160
3220
  const option = e.target.classList.contains('settings-option') ? e.target : e.target.closest('.settings-option');
3161
3221
  const action = option.getAttribute('data-action');
@@ -3167,29 +3227,28 @@ bindSettingsMenuEvents() {
3167
3227
  }
3168
3228
 
3169
3229
  if (e.target.classList.contains('settings-suboption')) {
3170
- const parent = e.target.closest('.settings-option');
3171
- const action = parent?.getAttribute('data-action');
3230
+ const wrapper = e.target.closest('.settings-expandable-wrapper');
3231
+ const trigger = wrapper.querySelector('.expandable-trigger');
3232
+ const action = trigger.getAttribute('data-action');
3172
3233
 
3173
- if (action === 'speed') {
3234
+ if (action === 'speed-expand') {
3174
3235
  const speed = parseFloat(e.target.getAttribute('data-speed'));
3175
3236
  if (speed && speed > 0 && this.video && !this.isChangingQuality) {
3176
3237
  this.video.playbackRate = speed;
3177
3238
 
3178
- parent.querySelectorAll('.settings-suboption').forEach(opt => {
3179
- opt.classList.remove('active');
3180
- });
3239
+ wrapper.querySelectorAll('.settings-suboption').forEach(opt => opt.classList.remove('active'));
3181
3240
  e.target.classList.add('active');
3182
3241
 
3183
- const valueEl = parent.querySelector('.settings-option-value');
3184
- if (valueEl) valueEl.textContent = speed + 'x';
3242
+ const label = trigger.querySelector('.settings-option-label');
3243
+ if (label) {
3244
+ const speedLabel = this.t('playback_speed') || 'Playback Speed';
3245
+ label.textContent = `${speedLabel}: ${speed}x`;
3246
+ }
3185
3247
 
3186
3248
  this.triggerEvent('speedchange', { speed, previousSpeed: this.video.playbackRate });
3187
3249
  }
3188
- }
3189
-
3190
- else if (action === 'subtitles') {
3250
+ } else if (action === 'subtitles-expand') {
3191
3251
  const trackData = e.target.getAttribute('data-track');
3192
-
3193
3252
  if (trackData === 'off') {
3194
3253
  this.disableSubtitles();
3195
3254
  } else {
@@ -3197,13 +3256,14 @@ bindSettingsMenuEvents() {
3197
3256
  this.enableSubtitleTrack(trackIndex);
3198
3257
  }
3199
3258
 
3200
- parent.querySelectorAll('.settings-suboption').forEach(opt => {
3201
- opt.classList.remove('active');
3202
- });
3259
+ wrapper.querySelectorAll('.settings-suboption').forEach(opt => opt.classList.remove('active'));
3203
3260
  e.target.classList.add('active');
3204
3261
 
3205
- const valueEl = parent.querySelector('.settings-option-value');
3206
- if (valueEl) valueEl.textContent = e.target.textContent;
3262
+ const label = trigger.querySelector('.settings-option-label');
3263
+ if (label) {
3264
+ const subtitlesLabel = this.t('subtitles') || 'Subtitles';
3265
+ label.textContent = `${subtitlesLabel}: ${e.target.textContent}`;
3266
+ }
3207
3267
  }
3208
3268
  }
3209
3269
  });
@@ -3464,6 +3524,41 @@ isAutoHideInitialized() {
3464
3524
  return this.autoHideInitialized;
3465
3525
  }
3466
3526
 
3527
+ hideCursor() {
3528
+ if (!this.options.hideCursor) {
3529
+ return; // Do not hide cursor if option is disabled
3530
+ }
3531
+
3532
+ if (this.container) {
3533
+ this.container.classList.add('hide-cursor');
3534
+ if (this.options.debug) console.log('🖱️ Cursor hidden');
3535
+ }
3536
+ }
3537
+
3538
+ showCursor() {
3539
+ if (this.container) {
3540
+ this.container.classList.remove('hide-cursor');
3541
+ if (this.options.debug) console.log('🖱️ Cursor shown');
3542
+ }
3543
+ }
3544
+
3545
+ enableCursorHiding() {
3546
+ this.options.hideCursor = true;
3547
+ if (this.options.debug) console.log('Cursor hiding enabled');
3548
+ return this;
3549
+ }
3550
+
3551
+ disableCursorHiding() {
3552
+ this.options.hideCursor = false;
3553
+ this.showCursor(); // Ensure cursor is shown immediately
3554
+ if (this.options.debug) console.log('Cursor hiding disabled');
3555
+ return this;
3556
+ }
3557
+
3558
+ isCursorHidingEnabled() {
3559
+ return this.options.hideCursor;
3560
+ }
3561
+
3467
3562
  showPlaylistControls() {
3468
3563
  if (!this.playlistPrevBtn || !this.playlistNextBtn) return;
3469
3564
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myetv-player",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "MYETV Video Player - Modular JavaScript and SCSS Build System",
5
5
  "main": "dist/myetv-player.js",
6
6
  "scripts": {
@@ -36,3 +36,5 @@
36
36
 
37
37
 
38
38
 
39
+
40
+
@@ -264,6 +264,9 @@
264
264
  // Setup event listeners
265
265
  this.setupEventListeners();
266
266
 
267
+ //create overlay for mouse detection
268
+ this.createMouseMoveOverlay();
269
+
267
270
  // Load available qualities
268
271
  this.loadQualities();
269
272
 
@@ -429,6 +432,224 @@
429
432
  });
430
433
  }
431
434
 
435
+ /**
436
+ * Create transparent overlay for click detection
437
+ */
438
+ createMouseMoveOverlay() {
439
+ if (this.mouseMoveOverlay) return;
440
+
441
+ this.mouseMoveOverlay = document.createElement('div');
442
+ this.mouseMoveOverlay.className = 'vimeo-mousemove-overlay';
443
+ this.mouseMoveOverlay.style.cssText = `
444
+ position: absolute;
445
+ top: 0;
446
+ left: 0;
447
+ width: 100%;
448
+ height: 100%;
449
+ z-index: 2;
450
+ background: transparent;
451
+ pointer-events: auto;
452
+ cursor: default;
453
+ `;
454
+
455
+ // Insert before controls
456
+ this.player.container.insertBefore(this.mouseMoveOverlay, this.player.controls);
457
+
458
+ // Setup mouse detection
459
+ this.setupMouseMoveDetection();
460
+
461
+ // Click handler for play/pause
462
+ this.mouseMoveOverlay.addEventListener('click', (e) => {
463
+ e.preventDefault();
464
+ e.stopPropagation();
465
+
466
+ const doubleTap = this.player.options.doubleTapPause;
467
+ const pauseClick = this.player.options.pauseClick;
468
+
469
+ if (doubleTap) {
470
+ // Check if controls are hidden
471
+ let controlsHidden = false;
472
+ if (this.player.controls) {
473
+ controlsHidden = this.player.controls.classList.contains('hide');
474
+ }
475
+
476
+ if (!controlsHidden) {
477
+ const controls = this.player.container.querySelector('.controls');
478
+ if (controls) {
479
+ controlsHidden = controls.classList.contains('hide');
480
+ }
481
+ }
482
+
483
+ if (!controlsHidden && this.player.controls) {
484
+ const style = window.getComputedStyle(this.player.controls);
485
+ controlsHidden = (style.opacity === '0' || style.visibility === 'hidden');
486
+ }
487
+
488
+ if (controlsHidden) {
489
+ // Show controls
490
+ if (this.player.showControlsNow) {
491
+ this.player.showControlsNow();
492
+ }
493
+ if (this.player.resetAutoHideTimer) {
494
+ this.player.resetAutoHideTimer();
495
+ }
496
+ return;
497
+ }
498
+
499
+ this.togglePlayPause();
500
+ } else if (pauseClick) {
501
+ this.togglePlayPause();
502
+ }
503
+ });
504
+
505
+ // Mouse move handler
506
+ this.mouseMoveOverlay.addEventListener('mousemove', (e) => {
507
+ if (this.player.onMouseMove) {
508
+ this.player.onMouseMove(e);
509
+ }
510
+ if (this.player.resetAutoHideTimer) {
511
+ this.player.resetAutoHideTimer();
512
+ }
513
+ });
514
+
515
+ if (this.options.debug) {
516
+ console.log('[Vimeo] Mouse overlay created');
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Setup mouse move detection
522
+ */
523
+ setupMouseMoveDetection() {
524
+ // Track last mouse position
525
+ this.lastMouseX = null;
526
+ this.lastMouseY = null;
527
+ this.mouseCheckInterval = null;
528
+
529
+ // Mouse enter
530
+ this.player.container.addEventListener('mouseenter', () => {
531
+ if (this.options.debug) {
532
+ console.log('[Vimeo] Mouse entered - show controls');
533
+ }
534
+ if (this.player.showControlsNow) {
535
+ this.player.showControlsNow();
536
+ }
537
+ if (this.player.resetAutoHideTimer) {
538
+ this.player.resetAutoHideTimer();
539
+ }
540
+ this.startMousePositionTracking();
541
+ });
542
+
543
+ // Mouse leave
544
+ this.player.container.addEventListener('mouseleave', () => {
545
+ if (this.options.debug) {
546
+ console.log('[Vimeo] Mouse left');
547
+ }
548
+ this.stopMousePositionTracking();
549
+ });
550
+
551
+ // Mouse move
552
+ this.player.container.addEventListener('mousemove', (e) => {
553
+ this.lastMouseX = e.clientX;
554
+ this.lastMouseY = e.clientY;
555
+ if (this.player.onMouseMove) {
556
+ this.player.onMouseMove(e);
557
+ }
558
+ if (this.player.resetAutoHideTimer) {
559
+ this.player.resetAutoHideTimer();
560
+ }
561
+ });
562
+ }
563
+
564
+ /**
565
+ * Start mouse position tracking
566
+ */
567
+ startMousePositionTracking() {
568
+ if (this.mouseCheckInterval) return;
569
+
570
+ this.mouseCheckInterval = setInterval(() => {
571
+ // Track mouse position over iframe
572
+ const handleGlobalMove = (e) => {
573
+ const newX = e.clientX;
574
+ const newY = e.clientY;
575
+
576
+ if (this.lastMouseX !== newX || this.lastMouseY !== newY) {
577
+ this.lastMouseX = newX;
578
+ this.lastMouseY = newY;
579
+
580
+ const rect = this.player.container.getBoundingClientRect();
581
+ const isInside = (
582
+ newX >= rect.left &&
583
+ newX <= rect.right &&
584
+ newY >= rect.top &&
585
+ newY <= rect.bottom
586
+ );
587
+
588
+ if (isInside) {
589
+ if (this.player.showControlsNow) {
590
+ this.player.showControlsNow();
591
+ }
592
+ if (this.player.resetAutoHideTimer) {
593
+ this.player.resetAutoHideTimer();
594
+ }
595
+ }
596
+ }
597
+ };
598
+
599
+ document.addEventListener('mousemove', handleGlobalMove, { once: true, passive: true });
600
+ }, 100);
601
+ }
602
+
603
+ /**
604
+ * Stop mouse position tracking
605
+ */
606
+ stopMousePositionTracking() {
607
+ if (this.mouseCheckInterval) {
608
+ clearInterval(this.mouseCheckInterval);
609
+ this.mouseCheckInterval = null;
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Toggle play/pause
615
+ */
616
+ togglePlayPause() {
617
+ if (!this.vimeoPlayer) return;
618
+
619
+ this.vimeoPlayer.getPaused().then(paused => {
620
+ if (paused) {
621
+ // Use player's play method to trigger UI update
622
+ if (this.player.play) {
623
+ this.player.play();
624
+ } else if (this.player.video && this.player.video.play) {
625
+ this.player.video.play();
626
+ }
627
+ } else {
628
+ // Use player's pause method to trigger UI update
629
+ if (this.player.pause) {
630
+ this.player.pause();
631
+ } else if (this.player.video && this.player.video.pause) {
632
+ this.player.video.pause();
633
+ }
634
+ }
635
+ }).catch(err => {
636
+ if (this.options.debug) {
637
+ console.error('[Vimeo] GetPaused error:', err);
638
+ }
639
+ });
640
+ }
641
+
642
+ /**
643
+ * Remove mouse overlay
644
+ */
645
+ removeMouseMoveOverlay() {
646
+ if (this.mouseMoveOverlay) {
647
+ this.mouseMoveOverlay.remove();
648
+ this.mouseMoveOverlay = null;
649
+ }
650
+ this.stopMousePositionTracking();
651
+ }
652
+
432
653
  /**
433
654
  * Load available qualities and sync with MYETV player
434
655
  */
@@ -912,6 +1133,8 @@
912
1133
  * Dispose plugin
913
1134
  */
914
1135
  dispose() {
1136
+ this.removeMouseMoveOverlay();
1137
+
915
1138
  // Cleanup PIP observers and listeners
916
1139
  if (this.pipObserver) {
917
1140
  this.pipObserver.disconnect();