myetv-player 1.1.1 → 1.1.2

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.
@@ -466,7 +466,6 @@ constructor(videoElement, options = {}) {
466
466
  this.currentQualityIndex = 0;
467
467
  this.qualities = [];
468
468
  this.originalSources = [];
469
- this.setupMenuToggles(); // Initialize menu toggle system
470
469
  this.isPiPSupported = this.checkPiPSupport();
471
470
  this.seekTooltip = null;
472
471
  this.titleOverlay = null;
@@ -578,6 +577,7 @@ constructor(videoElement, options = {}) {
578
577
  this.interceptAutoLoading();
579
578
  this.createPlayerStructure();
580
579
  this.initializeElements();
580
+ this.setupMenuToggles(); // Initialize menu toggle system
581
581
  // audio player adaptation
582
582
  this.adaptToAudioFile = function () {
583
583
  if (this.options.audiofile) {
@@ -1194,74 +1194,84 @@ initializeElements() {
1194
1194
  this.speedMenu = this.controls?.querySelector('.speed-menu');
1195
1195
  this.qualityMenu = this.controls?.querySelector('.quality-menu');
1196
1196
  this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
1197
+ // Apply seek handle shape from options
1198
+ if (this.progressHandle && this.options.seekHandleShape) {
1199
+ this.setSeekHandleShape(this.options.seekHandleShape);
1200
+ }
1197
1201
  }
1198
1202
 
1199
1203
  // Generic method to close all active menus (works with plugins too)
1200
1204
  closeAllMenus() {
1201
- // Find all elements with class ending in '-menu' that have 'active' class
1202
- const allMenus = this.controls?.querySelectorAll('[class*="-menu"].active');
1203
- allMenus?.forEach(menu => {
1204
- menu.classList.remove('active');
1205
- });
1205
+ if (!this.controls) return;
1206
1206
 
1207
- // Remove active state from all control buttons
1208
- const allButtons = this.controls?.querySelectorAll('.control-btn.active');
1209
- allButtons?.forEach(btn => {
1210
- btn.classList.remove('active');
1211
- });
1207
+ const menus = this.controls.querySelectorAll('.speed-menu, .quality-menu, .subtitles-menu, .settings-menu');
1208
+ const buttons = this.controls.querySelectorAll('.control-btn');
1209
+
1210
+ menus.forEach(menu => menu.classList.remove('active'));
1211
+ buttons.forEach(btn => btn.classList.remove('active'));
1212
+
1213
+ this.currentOpenMenu = null;
1214
+
1215
+ if (this.options.debug) {
1216
+ console.log('All menus closed');
1217
+ }
1212
1218
  }
1213
1219
 
1214
1220
  // Generic menu toggle setup (works with core menus and plugin menus)
1215
1221
  setupMenuToggles() {
1216
- // Delegate click events to control bar for any button with associated menu
1217
- if (this.controls) {
1218
- this.controls.addEventListener('click', (e) => {
1219
- // Find if clicked element is a control button or inside one
1220
- const button = e.target.closest('.control-btn');
1221
-
1222
- if (!button) return;
1223
-
1224
- // Get button classes to find associated menu
1225
- const buttonClasses = button.className.split(' ');
1226
- let menuClass = null;
1227
-
1228
- // Find if this button has an associated menu (e.g., speed-btn -> speed-menu)
1229
- for (const cls of buttonClasses) {
1230
- if (cls.endsWith('-btn')) {
1231
- const menuName = cls.replace('-btn', '-menu');
1232
- const menu = this.controls.querySelector('.' + menuName);
1233
- if (menu) {
1234
- menuClass = menuName;
1235
- break;
1236
- }
1237
- }
1222
+ if (!this.controls) return;
1223
+
1224
+ this.currentOpenMenu = null;
1225
+
1226
+ this.controls.addEventListener('click', (e) => {
1227
+ const button = e.target.closest('.control-btn');
1228
+ if (!button) return;
1229
+
1230
+ const buttonClasses = Array.from(button.classList);
1231
+ let menuElement = null;
1232
+
1233
+ for (const cls of buttonClasses) {
1234
+ if (cls.endsWith('-btn')) {
1235
+ const menuClass = cls.replace('-btn', '-menu');
1236
+ menuElement = this.controls.querySelector(`.${menuClass}`);
1237
+ if (menuElement) break;
1238
1238
  }
1239
+ }
1239
1240
 
1240
- if (!menuClass) return;
1241
+ if (!menuElement) return;
1241
1242
 
1242
- e.stopPropagation();
1243
+ e.stopPropagation();
1244
+ e.preventDefault();
1243
1245
 
1244
- // Get the menu element
1245
- const menu = this.controls.querySelector('.' + menuClass);
1246
- const isOpen = menu.classList.contains('active');
1246
+ const isOpen = menuElement.classList.contains('active');
1247
1247
 
1248
- // Close all menus first
1249
- this.closeAllMenus();
1248
+ this.closeAllMenus();
1250
1249
 
1251
- // If menu was closed, open it
1252
- if (!isOpen) {
1253
- menu.classList.add('active');
1254
- button.classList.add('active');
1250
+ if (!isOpen) {
1251
+ menuElement.classList.add('active');
1252
+ button.classList.add('active');
1253
+ this.currentOpenMenu = menuElement;
1254
+ if (this.options.debug) {
1255
+ console.log('Menu opened:', menuElement.className);
1255
1256
  }
1256
- });
1257
- }
1257
+ } else {
1258
+ this.currentOpenMenu = null;
1259
+ if (this.options.debug) {
1260
+ console.log('Menu closed:', menuElement.className);
1261
+ }
1262
+ }
1263
+ });
1258
1264
 
1259
- // Close menus when clicking outside controls
1260
1265
  document.addEventListener('click', (e) => {
1261
- if (!this.controls?.contains(e.target)) {
1266
+ if (!this.controls) return;
1267
+ if (!this.controls.contains(e.target)) {
1262
1268
  this.closeAllMenus();
1263
1269
  }
1264
1270
  });
1271
+
1272
+ if (this.options.debug) {
1273
+ console.log('✅ Menu toggle system initialized (click-based, auto-close)');
1274
+ }
1265
1275
  }
1266
1276
 
1267
1277
  updateVolumeSliderVisual() {
@@ -1572,9 +1582,11 @@ updateBuffer() {
1572
1582
  }
1573
1583
 
1574
1584
  startSeeking(e) {
1585
+ if (e.cancelable) e.preventDefault();
1575
1586
  if (this.isChangingQuality) return;
1576
1587
 
1577
1588
  this.isUserSeeking = true;
1589
+ this.progressContainer.classList.add('seeking');
1578
1590
  this.seek(e);
1579
1591
  e.preventDefault();
1580
1592
 
@@ -1586,6 +1598,7 @@ startSeeking(e) {
1586
1598
  }
1587
1599
 
1588
1600
  continueSeeking(e) {
1601
+ if (e.cancelable) e.preventDefault();
1589
1602
  if (this.isUserSeeking && !this.isChangingQuality) {
1590
1603
  this.seek(e);
1591
1604
  }
@@ -1593,9 +1606,13 @@ continueSeeking(e) {
1593
1606
 
1594
1607
  endSeeking() {
1595
1608
  this.isUserSeeking = false;
1609
+ this.progressContainer.classList.remove('seeking');
1596
1610
  }
1597
1611
 
1598
1612
  seek(e) {
1613
+ if (e.cancelable) {
1614
+ e.preventDefault();
1615
+ }
1599
1616
  if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
1600
1617
 
1601
1618
  const rect = this.progressContainer.getBoundingClientRect();
@@ -2016,7 +2033,7 @@ bindPosterEvents() {
2016
2033
  // Hide poster when video is loading/playing
2017
2034
  this.video.addEventListener('playing', () => {
2018
2035
  this.hidePoster();
2019
- });
2036
+ });
2020
2037
 
2021
2038
  // Show poster on load if not autoplay
2022
2039
  if (!this.options.autoplay) {
@@ -2460,6 +2477,7 @@ addEventListener(eventType, callback) {
2460
2477
  // Playback events
2461
2478
  this.video.addEventListener('playing', () => {
2462
2479
  this.hideLoading();
2480
+ this.closeAllMenus();
2463
2481
  // Trigger playing event - video is now actually playing
2464
2482
  this.triggerEvent('playing', {
2465
2483
  currentTime: this.getCurrentTime(),
@@ -2684,6 +2702,10 @@ addEventListener(eventType, callback) {
2684
2702
  // Mouse events (desktop)
2685
2703
  this.progressContainer.addEventListener('click', (e) => this.seek(e));
2686
2704
  this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
2705
+ if (this.progressHandle) {
2706
+ this.progressHandle.addEventListener('mousedown', this.startSeeking.bind(this));
2707
+ this.progressHandle.addEventListener('touchstart', this.startSeeking.bind(this), { passive: false });
2708
+ }
2687
2709
 
2688
2710
  // Touch events (mobile)
2689
2711
  this.progressContainer.addEventListener('touchstart', (e) => {
@@ -3106,13 +3128,13 @@ createControls() {
3106
3128
  const controlsHTML = `
3107
3129
  <div class="controls" id="${controlsId}">
3108
3130
  <div class="progress-container">
3109
- <div class="progress-bar">
3110
- <div class="progress-buffer"></div>
3111
- <div class="progress-filled"></div>
3112
- <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
3113
- </div>
3114
- ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3115
- </div>
3131
+ <div class="progress-bar">
3132
+ <div class="progress-buffer"></div>
3133
+ <div class="progress-filled"></div>
3134
+ </div>
3135
+ <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div> <!-- ✅ Fuori da progress-bar -->
3136
+ ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3137
+ </div>
3116
3138
 
3117
3139
  <div class="controls-main">
3118
3140
  <div class="controls-left">
@@ -5107,11 +5129,11 @@ updateSubtitlesButton() {
5107
5129
  var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
5108
5130
  if (!subtitlesBtn) return;
5109
5131
 
5132
+ subtitlesBtn.classList.remove('active');
5133
+
5110
5134
  if (this.subtitlesEnabled) {
5111
- subtitlesBtn.classList.add('active');
5112
5135
  subtitlesBtn.title = this.t('subtitlesdisable');
5113
5136
  } else {
5114
- subtitlesBtn.classList.remove('active');
5115
5137
  subtitlesBtn.title = this.t('subtitlesenable');
5116
5138
  }
5117
5139
  }
@@ -5147,14 +5169,6 @@ updateSubtitlesUI() {
5147
5169
  bindSubtitleEvents() {
5148
5170
  var self = this;
5149
5171
 
5150
- var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
5151
- if (subtitlesBtn) {
5152
- subtitlesBtn.addEventListener('click', function (e) {
5153
- e.stopPropagation();
5154
- self.toggleSubtitles();
5155
- });
5156
- }
5157
-
5158
5172
  var subtitlesMenu = this.controls && this.controls.querySelector('.subtitles-menu');
5159
5173
  if (subtitlesMenu) {
5160
5174
  subtitlesMenu.addEventListener('click', function (e) {
@@ -5163,6 +5177,7 @@ bindSubtitleEvents() {
5163
5177
  }
5164
5178
  }
5165
5179
 
5180
+
5166
5181
  handleSubtitlesMenuClick(e) {
5167
5182
  if (!e.target.classList.contains('subtitles-option')) return;
5168
5183
 
@@ -452,7 +452,6 @@ constructor(videoElement, options = {}) {
452
452
  this.currentQualityIndex = 0;
453
453
  this.qualities = [];
454
454
  this.originalSources = [];
455
- this.setupMenuToggles();
456
455
  this.isPiPSupported = this.checkPiPSupport();
457
456
  this.seekTooltip = null;
458
457
  this.titleOverlay = null;
@@ -564,6 +563,7 @@ constructor(videoElement, options = {}) {
564
563
  this.interceptAutoLoading();
565
564
  this.createPlayerStructure();
566
565
  this.initializeElements();
566
+ this.setupMenuToggles();
567
567
 
568
568
  this.adaptToAudioFile = function () {
569
569
  if (this.options.audiofile) {
@@ -1180,72 +1180,82 @@ initializeElements() {
1180
1180
  this.speedMenu = this.controls?.querySelector('.speed-menu');
1181
1181
  this.qualityMenu = this.controls?.querySelector('.quality-menu');
1182
1182
  this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
1183
+
1184
+ if (this.progressHandle && this.options.seekHandleShape) {
1185
+ this.setSeekHandleShape(this.options.seekHandleShape);
1186
+ }
1183
1187
  }
1184
1188
 
1185
1189
  closeAllMenus() {
1186
-
1187
- const allMenus = this.controls?.querySelectorAll('[class*="-menu"].active');
1188
- allMenus?.forEach(menu => {
1189
- menu.classList.remove('active');
1190
- });
1190
+ if (!this.controls) return;
1191
1191
 
1192
-
1193
- const allButtons = this.controls?.querySelectorAll('.control-btn.active');
1194
- allButtons?.forEach(btn => {
1195
- btn.classList.remove('active');
1196
- });
1192
+ const menus = this.controls.querySelectorAll('.speed-menu, .quality-menu, .subtitles-menu, .settings-menu');
1193
+ const buttons = this.controls.querySelectorAll('.control-btn');
1194
+
1195
+ menus.forEach(menu => menu.classList.remove('active'));
1196
+ buttons.forEach(btn => btn.classList.remove('active'));
1197
+
1198
+ this.currentOpenMenu = null;
1199
+
1200
+ if (this.options.debug) {
1201
+ console.log('All menus closed');
1202
+ }
1197
1203
  }
1198
1204
 
1199
1205
  setupMenuToggles() {
1200
-
1201
- if (this.controls) {
1202
- this.controls.addEventListener('click', (e) => {
1203
-
1204
- const button = e.target.closest('.control-btn');
1206
+ if (!this.controls) return;
1205
1207
 
1206
- if (!button) return;
1208
+ this.currentOpenMenu = null;
1207
1209
 
1208
-
1209
- const buttonClasses = button.className.split(' ');
1210
- let menuClass = null;
1210
+ this.controls.addEventListener('click', (e) => {
1211
+ const button = e.target.closest('.control-btn');
1212
+ if (!button) return;
1211
1213
 
1212
-
1213
- for (const cls of buttonClasses) {
1214
- if (cls.endsWith('-btn')) {
1215
- const menuName = cls.replace('-btn', '-menu');
1216
- const menu = this.controls.querySelector('.' + menuName);
1217
- if (menu) {
1218
- menuClass = menuName;
1219
- break;
1220
- }
1221
- }
1214
+ const buttonClasses = Array.from(button.classList);
1215
+ let menuElement = null;
1216
+
1217
+ for (const cls of buttonClasses) {
1218
+ if (cls.endsWith('-btn')) {
1219
+ const menuClass = cls.replace('-btn', '-menu');
1220
+ menuElement = this.controls.querySelector(`.${menuClass}`);
1221
+ if (menuElement) break;
1222
1222
  }
1223
+ }
1223
1224
 
1224
- if (!menuClass) return;
1225
+ if (!menuElement) return;
1225
1226
 
1226
- e.stopPropagation();
1227
+ e.stopPropagation();
1228
+ e.preventDefault();
1227
1229
 
1228
-
1229
- const menu = this.controls.querySelector('.' + menuClass);
1230
- const isOpen = menu.classList.contains('active');
1230
+ const isOpen = menuElement.classList.contains('active');
1231
1231
 
1232
-
1233
- this.closeAllMenus();
1232
+ this.closeAllMenus();
1234
1233
 
1235
-
1236
- if (!isOpen) {
1237
- menu.classList.add('active');
1238
- button.classList.add('active');
1234
+ if (!isOpen) {
1235
+ menuElement.classList.add('active');
1236
+ button.classList.add('active');
1237
+ this.currentOpenMenu = menuElement;
1238
+ if (this.options.debug) {
1239
+ console.log('Menu opened:', menuElement.className);
1239
1240
  }
1240
- });
1241
- }
1241
+ } else {
1242
+ this.currentOpenMenu = null;
1243
+ if (this.options.debug) {
1244
+ console.log('Menu closed:', menuElement.className);
1245
+ }
1246
+ }
1247
+ });
1242
1248
 
1243
-
1244
1249
  document.addEventListener('click', (e) => {
1245
- if (!this.controls?.contains(e.target)) {
1250
+ if (!this.controls) return;
1251
+ if (!this.controls.contains(e.target)) {
1246
1252
  this.closeAllMenus();
1247
1253
  }
1248
1254
  });
1255
+
1256
+ if (this.options.debug) {
1257
+ console.log('✅ Menu toggle system initialized (click-based, auto-close)');
1258
+ }
1249
1259
  }
1250
1260
 
1251
1261
  updateVolumeSliderVisual() {
@@ -1549,9 +1559,11 @@ updateBuffer() {
1549
1559
  }
1550
1560
 
1551
1561
  startSeeking(e) {
1562
+ if (e.cancelable) e.preventDefault();
1552
1563
  if (this.isChangingQuality) return;
1553
1564
 
1554
1565
  this.isUserSeeking = true;
1566
+ this.progressContainer.classList.add('seeking');
1555
1567
  this.seek(e);
1556
1568
  e.preventDefault();
1557
1569
 
@@ -1563,6 +1575,7 @@ startSeeking(e) {
1563
1575
  }
1564
1576
 
1565
1577
  continueSeeking(e) {
1578
+ if (e.cancelable) e.preventDefault();
1566
1579
  if (this.isUserSeeking && !this.isChangingQuality) {
1567
1580
  this.seek(e);
1568
1581
  }
@@ -1570,9 +1583,13 @@ continueSeeking(e) {
1570
1583
 
1571
1584
  endSeeking() {
1572
1585
  this.isUserSeeking = false;
1586
+ this.progressContainer.classList.remove('seeking');
1573
1587
  }
1574
1588
 
1575
1589
  seek(e) {
1590
+ if (e.cancelable) {
1591
+ e.preventDefault();
1592
+ }
1576
1593
  if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
1577
1594
 
1578
1595
  const rect = this.progressContainer.getBoundingClientRect();
@@ -1982,7 +1999,7 @@ bindPosterEvents() {
1982
1999
 
1983
2000
  this.video.addEventListener('playing', () => {
1984
2001
  this.hidePoster();
1985
- });
2002
+ });
1986
2003
 
1987
2004
 
1988
2005
  if (!this.options.autoplay) {
@@ -2375,6 +2392,7 @@ addEventListener(eventType, callback) {
2375
2392
 
2376
2393
  this.video.addEventListener('playing', () => {
2377
2394
  this.hideLoading();
2395
+ this.closeAllMenus();
2378
2396
 
2379
2397
  this.triggerEvent('playing', {
2380
2398
  currentTime: this.getCurrentTime(),
@@ -2599,6 +2617,10 @@ addEventListener(eventType, callback) {
2599
2617
 
2600
2618
  this.progressContainer.addEventListener('click', (e) => this.seek(e));
2601
2619
  this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
2620
+ if (this.progressHandle) {
2621
+ this.progressHandle.addEventListener('mousedown', this.startSeeking.bind(this));
2622
+ this.progressHandle.addEventListener('touchstart', this.startSeeking.bind(this), { passive: false });
2623
+ }
2602
2624
 
2603
2625
 
2604
2626
  this.progressContainer.addEventListener('touchstart', (e) => {
@@ -3017,13 +3039,13 @@ createControls() {
3017
3039
  const controlsHTML = `
3018
3040
  <div class="controls" id="${controlsId}">
3019
3041
  <div class="progress-container">
3020
- <div class="progress-bar">
3021
- <div class="progress-buffer"></div>
3022
- <div class="progress-filled"></div>
3023
- <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
3024
- </div>
3025
- ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3026
- </div>
3042
+ <div class="progress-bar">
3043
+ <div class="progress-buffer"></div>
3044
+ <div class="progress-filled"></div>
3045
+ </div>
3046
+ <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div> <!-- ✅ Fuori da progress-bar -->
3047
+ ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3048
+ </div>
3027
3049
 
3028
3050
  <div class="controls-main">
3029
3051
  <div class="controls-left">
@@ -5012,11 +5034,11 @@ updateSubtitlesButton() {
5012
5034
  var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
5013
5035
  if (!subtitlesBtn) return;
5014
5036
 
5037
+ subtitlesBtn.classList.remove('active');
5038
+
5015
5039
  if (this.subtitlesEnabled) {
5016
- subtitlesBtn.classList.add('active');
5017
5040
  subtitlesBtn.title = this.t('subtitlesdisable');
5018
5041
  } else {
5019
- subtitlesBtn.classList.remove('active');
5020
5042
  subtitlesBtn.title = this.t('subtitlesenable');
5021
5043
  }
5022
5044
  }
@@ -5052,14 +5074,6 @@ updateSubtitlesUI() {
5052
5074
  bindSubtitleEvents() {
5053
5075
  var self = this;
5054
5076
 
5055
- var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
5056
- if (subtitlesBtn) {
5057
- subtitlesBtn.addEventListener('click', function (e) {
5058
- e.stopPropagation();
5059
- self.toggleSubtitles();
5060
- });
5061
- }
5062
-
5063
5077
  var subtitlesMenu = this.controls && this.controls.querySelector('.subtitles-menu');
5064
5078
  if (subtitlesMenu) {
5065
5079
  subtitlesMenu.addEventListener('click', function (e) {
@@ -5068,6 +5082,7 @@ bindSubtitleEvents() {
5068
5082
  }
5069
5083
  }
5070
5084
 
5085
+
5071
5086
  handleSubtitlesMenuClick(e) {
5072
5087
  if (!e.target.classList.contains('subtitles-option')) return;
5073
5088
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myetv-player",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "MYETV Video Player - Modular JavaScript and SCSS Build System",
5
5
  "main": "dist/myetv-player.js",
6
6
  "scripts": {
@@ -34,3 +34,4 @@
34
34
 
35
35
 
36
36
 
37
+
@@ -54,6 +54,7 @@
54
54
  this.mouseMoveOverlay = null;
55
55
  this.qualityCheckAttempts = 0;
56
56
  this.captionCheckAttempts = 0;
57
+ this.liveStreamChecked = false;
57
58
  this.captionStateCheckInterval = null;
58
59
  this.qualityMonitorInterval = null;
59
60
  this.resizeListenerAdded = false;
@@ -77,6 +78,7 @@
77
78
  { code: 'ar', name: 'Arabic' },
78
79
  { code: 'pt', name: 'Portuguese' },
79
80
  { code: 'ru', name: 'Russian' },
81
+ { code: 'zh', name: 'Chinese' },
80
82
  { code: 'ja', name: 'Japanese' },
81
83
  { code: 'de', name: 'German' },
82
84
  { code: 'fr', name: 'French' },
@@ -1179,10 +1181,11 @@ startBufferMonitoring() {
1179
1181
  // Handle responsive layout for PiP and subtitles buttons
1180
1182
  this.handleResponsiveLayout();
1181
1183
 
1184
+ this.playAttemptTimeout = null;
1185
+
1182
1186
  // Hide PiP from settings menu (separate function, called after responsive layout)
1183
1187
  setTimeout(() => this.hidePipFromSettingsMenuOnly(), 500);
1184
- setTimeout(() => this.hidePipFromSettingsMenuOnly(), 1500);
1185
- setTimeout(() => this.hidePipFromSettingsMenuOnly(), 3000);
1188
+
1186
1189
  // Check if this is a live stream
1187
1190
  setTimeout(() => this.checkIfLiveStream(), 2000);
1188
1191
  setTimeout(() => this.checkIfLiveStream(), 5000);
@@ -1199,13 +1202,9 @@ startBufferMonitoring() {
1199
1202
 
1200
1203
  // Load qualities with multiple attempts
1201
1204
  setTimeout(() => this.loadAvailableQualities(), 500);
1202
- setTimeout(() => this.loadAvailableQualities(), 1000);
1203
- setTimeout(() => this.loadAvailableQualities(), 2000);
1204
1205
 
1205
1206
  // Load captions with multiple attempts
1206
1207
  setTimeout(() => this.loadAvailableCaptions(), 500);
1207
- setTimeout(() => this.loadAvailableCaptions(), 1000);
1208
- setTimeout(() => this.loadAvailableCaptions(), 2000);
1209
1208
 
1210
1209
  if (this.options.quality && this.options.quality !== 'default') {
1211
1210
  setTimeout(() => this.setQuality(this.options.quality), 1000);
@@ -2701,6 +2700,49 @@ startBufferMonitoring() {
2701
2700
  };
2702
2701
  if (this.api.player.options.debug) console.log('[YT Plugin] State:', states[event.data], event.data);
2703
2702
 
2703
+ // Know if video have some problems to start
2704
+ if (event.data === YT.PlayerState.UNSTARTED || event.data === -1) {
2705
+ // start timeout when video is unstarted
2706
+ if (this.playAttemptTimeout) {
2707
+ clearTimeout(this.playAttemptTimeout);
2708
+ }
2709
+
2710
+ this.playAttemptTimeout = setTimeout(() => {
2711
+ if (!this.ytPlayer) return;
2712
+
2713
+ const currentState = this.ytPlayer.getPlayerState();
2714
+
2715
+ // If video is unstrated after timeout, consider it restricted
2716
+ if (currentState === YT.PlayerState.UNSTARTED || currentState === -1) {
2717
+ if (this.api.player.options.debug) {
2718
+ console.log('YT Plugin: Video stuck in UNSTARTED - possibly members-only or restricted');
2719
+ }
2720
+
2721
+ // Trigger ended event
2722
+ this.api.triggerEvent('ended', {
2723
+ reason: 'video_restricted_or_membership',
2724
+ state: currentState
2725
+ });
2726
+
2727
+ // Show poster overlay if present
2728
+ this.showPosterOverlay();
2729
+
2730
+ // Trigger custom event
2731
+ this.api.triggerEvent('youtubeplugin:membershiprestricted', {
2732
+ videoId: this.videoId
2733
+ });
2734
+ }
2735
+ }, 15000); // 15 seconds of timeout
2736
+
2737
+ } else if (event.data === YT.PlayerState.PLAYING ||
2738
+ event.data === YT.PlayerState.BUFFERING) {
2739
+ // Clear the timeout if video starts correctly
2740
+ if (this.playAttemptTimeout) {
2741
+ clearTimeout(this.playAttemptTimeout);
2742
+ this.playAttemptTimeout = null;
2743
+ }
2744
+ }
2745
+
2704
2746
  // Get play/pause icons
2705
2747
  const playIcon = this.api.container.querySelector('.play-icon');
2706
2748
  const pauseIcon = this.api.container.querySelector('.pause-icon');
@@ -3286,6 +3328,12 @@ startBufferMonitoring() {
3286
3328
  dispose() {
3287
3329
  if (this.api.player.options.debug) console.log('[YT Plugin] Disposing');
3288
3330
 
3331
+ // Cleanup timeout
3332
+ if (this.playAttemptTimeout) {
3333
+ clearTimeout(this.playAttemptTimeout);
3334
+ this.playAttemptTimeout = null;
3335
+ }
3336
+
3289
3337
  if (this.timeUpdateInterval) {
3290
3338
  clearInterval(this.timeUpdateInterval);
3291
3339
  this.timeUpdateInterval = null;