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.
- package/css/myetv-player.css +726 -11937
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +81 -66
- package/dist/myetv-player.min.js +78 -63
- package/package.json +2 -1
- package/plugins/youtube/myetv-player-youtube-plugin.js +54 -6
- package/scss/_controls.scss +120 -317
- package/scss/_menus.scss +117 -4023
- package/scss/_progress-bar.scss +168 -2052
- package/scss/_title-overlay.scss +10 -2239
- package/scss/_video.scss +62 -2167
- package/scss/_volume.scss +25 -1846
- package/src/controls.js +7 -7
- package/src/core.js +66 -49
- package/src/events.js +5 -0
- package/src/subtitles.js +3 -10
package/dist/myetv-player.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
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
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
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
|
-
|
|
1241
|
+
if (!menuElement) return;
|
|
1241
1242
|
|
|
1242
|
-
|
|
1243
|
+
e.stopPropagation();
|
|
1244
|
+
e.preventDefault();
|
|
1243
1245
|
|
|
1244
|
-
|
|
1245
|
-
const menu = this.controls.querySelector('.' + menuClass);
|
|
1246
|
-
const isOpen = menu.classList.contains('active');
|
|
1246
|
+
const isOpen = menuElement.classList.contains('active');
|
|
1247
1247
|
|
|
1248
|
-
|
|
1249
|
-
this.closeAllMenus();
|
|
1248
|
+
this.closeAllMenus();
|
|
1250
1249
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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
|
|
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
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
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
|
|
package/dist/myetv-player.min.js
CHANGED
|
@@ -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
|
|
1194
|
-
|
|
1195
|
-
|
|
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
|
-
|
|
1208
|
+
this.currentOpenMenu = null;
|
|
1207
1209
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1210
|
+
this.controls.addEventListener('click', (e) => {
|
|
1211
|
+
const button = e.target.closest('.control-btn');
|
|
1212
|
+
if (!button) return;
|
|
1211
1213
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
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
|
-
|
|
1225
|
+
if (!menuElement) return;
|
|
1225
1226
|
|
|
1226
|
-
|
|
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
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
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
|
|
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
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
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
|
@@ -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
|
-
|
|
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;
|