myetv-player 1.5.0 → 1.6.1
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/README.md +66 -1
- package/css/myetv-player.css +15 -1
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +590 -56
- package/dist/myetv-player.min.js +524 -30
- package/package.json +4 -2
- package/plugins/google-adsense-ads/README.md +230 -0
- package/plugins/google-adsense-ads/g-adsense-ads-plugin.js +75 -8
- package/plugins/google-ima-ads/README.md +258 -0
- package/plugins/google-ima-ads/g-ima-ads-plugin.js +2 -2
- package/plugins/iframe-ads/myetv-iframe-banner-ads.js +926 -0
- package/plugins/iframe-ads/readme.md +191 -0
- package/plugins/vast-vpaid-ads/README.md +146 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +72 -39
package/dist/myetv-player.min.js
CHANGED
|
@@ -25,7 +25,8 @@ class VideoPlayerI18n {
|
|
|
25
25
|
'playlist_prev': 'Indietro',
|
|
26
26
|
'settings_menu': 'Impostazioni',
|
|
27
27
|
'loading': 'Caricamento...',
|
|
28
|
-
'encoding_in_progress': 'Encoding in corso'
|
|
28
|
+
'encoding_in_progress': 'Encoding in corso',
|
|
29
|
+
'restart_video': 'Torna all\'inizio'
|
|
29
30
|
},
|
|
30
31
|
|
|
31
32
|
'en': {
|
|
@@ -48,7 +49,8 @@ class VideoPlayerI18n {
|
|
|
48
49
|
'playlist_prev': 'Previous',
|
|
49
50
|
'settings_menu': 'Settings',
|
|
50
51
|
'loading': 'Loading...',
|
|
51
|
-
'encoding_in_progress': 'Encoding in progress'
|
|
52
|
+
'encoding_in_progress': 'Encoding in progress',
|
|
53
|
+
'restart_video': 'Restart the video'
|
|
52
54
|
},
|
|
53
55
|
|
|
54
56
|
'es': {
|
|
@@ -71,7 +73,8 @@ class VideoPlayerI18n {
|
|
|
71
73
|
'playlist_prev': 'Anterior',
|
|
72
74
|
'settings_menu': 'Configuración',
|
|
73
75
|
'loading': 'Cargando...',
|
|
74
|
-
'encoding_in_progress': 'Codificación en curso'
|
|
76
|
+
'encoding_in_progress': 'Codificación en curso',
|
|
77
|
+
'restart_video': 'Reiniciar el vídeo'
|
|
75
78
|
},
|
|
76
79
|
|
|
77
80
|
'fr': {
|
|
@@ -94,7 +97,8 @@ class VideoPlayerI18n {
|
|
|
94
97
|
'playlist_prev': 'Précédent',
|
|
95
98
|
'settings_menu': 'Paramètres',
|
|
96
99
|
'loading': 'Chargement...',
|
|
97
|
-
'encoding_in_progress': 'Encodage en cours'
|
|
100
|
+
'encoding_in_progress': 'Encodage en cours',
|
|
101
|
+
'restart_video': 'Redémarrer la vidéo'
|
|
98
102
|
},
|
|
99
103
|
|
|
100
104
|
'de': {
|
|
@@ -117,7 +121,8 @@ class VideoPlayerI18n {
|
|
|
117
121
|
'playlist_prev': 'Zurück',
|
|
118
122
|
'settings_menu': 'Einstellungen',
|
|
119
123
|
'loading': 'Laden...',
|
|
120
|
-
'encoding_in_progress': 'Kodierung läuft'
|
|
124
|
+
'encoding_in_progress': 'Kodierung läuft',
|
|
125
|
+
'restart_video': 'Video neu starten'
|
|
121
126
|
},
|
|
122
127
|
|
|
123
128
|
'pt': {
|
|
@@ -140,7 +145,8 @@ class VideoPlayerI18n {
|
|
|
140
145
|
'playlist_prev': 'Anterior',
|
|
141
146
|
'settings_menu': 'Configurações',
|
|
142
147
|
'loading': 'Carregando...',
|
|
143
|
-
'encoding_in_progress': 'Codificação em andamento'
|
|
148
|
+
'encoding_in_progress': 'Codificação em andamento',
|
|
149
|
+
'restart_video': 'Restart video'
|
|
144
150
|
},
|
|
145
151
|
|
|
146
152
|
'zh': {
|
|
@@ -163,7 +169,8 @@ class VideoPlayerI18n {
|
|
|
163
169
|
'playlist_prev': '上一个',
|
|
164
170
|
'settings_menu': '设置',
|
|
165
171
|
'loading': '加载中...',
|
|
166
|
-
'encoding_in_progress': '编码中'
|
|
172
|
+
'encoding_in_progress': '编码中',
|
|
173
|
+
'restart_video': '重新开始视频'
|
|
167
174
|
},
|
|
168
175
|
|
|
169
176
|
'ja': {
|
|
@@ -186,7 +193,8 @@ class VideoPlayerI18n {
|
|
|
186
193
|
'playlist_prev': '前へ',
|
|
187
194
|
'settings_menu': '設定',
|
|
188
195
|
'loading': '読み込み中...',
|
|
189
|
-
'encoding_in_progress': 'エンコード中'
|
|
196
|
+
'encoding_in_progress': 'エンコード中',
|
|
197
|
+
'restart_video': 'ビデオを再開'
|
|
190
198
|
},
|
|
191
199
|
|
|
192
200
|
'ru': {
|
|
@@ -209,7 +217,8 @@ class VideoPlayerI18n {
|
|
|
209
217
|
'playlist_prev': 'Назад',
|
|
210
218
|
'settings_menu': 'Настройки',
|
|
211
219
|
'loading': 'Загрузка...',
|
|
212
|
-
'encoding_in_progress': 'Кодирование'
|
|
220
|
+
'encoding_in_progress': 'Кодирование',
|
|
221
|
+
'restart_video': 'Перезапустить видео'
|
|
213
222
|
},
|
|
214
223
|
|
|
215
224
|
'ar': {
|
|
@@ -232,7 +241,8 @@ class VideoPlayerI18n {
|
|
|
232
241
|
'playlist_prev': 'السابق',
|
|
233
242
|
'settings_menu': 'الإعدادات',
|
|
234
243
|
'loading': 'جاري التحميل...',
|
|
235
|
-
'encoding_in_progress': 'الترميز جارٍ'
|
|
244
|
+
'encoding_in_progress': 'الترميز جارٍ',
|
|
245
|
+
'restart_video': 'إعادة تشغيل الفيديو'
|
|
236
246
|
},
|
|
237
247
|
|
|
238
248
|
'ko': {
|
|
@@ -255,7 +265,8 @@ class VideoPlayerI18n {
|
|
|
255
265
|
'playlist_prev': '이전',
|
|
256
266
|
'settings_menu': '설정',
|
|
257
267
|
'loading': '로딩 중...',
|
|
258
|
-
'encoding_in_progress': '인코딩 진행 중'
|
|
268
|
+
'encoding_in_progress': '인코딩 진행 중',
|
|
269
|
+
'restart_video': 'Restart video'
|
|
259
270
|
},
|
|
260
271
|
|
|
261
272
|
'pl': {
|
|
@@ -278,7 +289,8 @@ class VideoPlayerI18n {
|
|
|
278
289
|
'playlist_prev': 'Wstecz',
|
|
279
290
|
'settings_menu': 'Ustawienia',
|
|
280
291
|
'loading': 'Ładowanie...',
|
|
281
|
-
'encoding_in_progress': 'Kodowanie w toku'
|
|
292
|
+
'encoding_in_progress': 'Kodowanie w toku',
|
|
293
|
+
'restart_video': 'Restart video'
|
|
282
294
|
},
|
|
283
295
|
|
|
284
296
|
'hu': {
|
|
@@ -301,7 +313,8 @@ class VideoPlayerI18n {
|
|
|
301
313
|
'playlist_prev': 'Előző',
|
|
302
314
|
'settings_menu': 'Beállítások',
|
|
303
315
|
'loading': 'Betöltés...',
|
|
304
|
-
'encoding_in_progress': 'Kódolás folyamatban'
|
|
316
|
+
'encoding_in_progress': 'Kódolás folyamatban',
|
|
317
|
+
'restart_video': 'Restart video'
|
|
305
318
|
},
|
|
306
319
|
|
|
307
320
|
'tr': {
|
|
@@ -324,7 +337,8 @@ class VideoPlayerI18n {
|
|
|
324
337
|
'playlist_prev': 'Önceki',
|
|
325
338
|
'settings_menu': 'Ayarlar',
|
|
326
339
|
'loading': 'Yükleniyor...',
|
|
327
|
-
'encoding_in_progress': 'Kodlama devam ediyor'
|
|
340
|
+
'encoding_in_progress': 'Kodlama devam ediyor',
|
|
341
|
+
'restart_video': 'Restart video'
|
|
328
342
|
},
|
|
329
343
|
|
|
330
344
|
'nl': {
|
|
@@ -347,7 +361,8 @@ class VideoPlayerI18n {
|
|
|
347
361
|
'playlist_prev': 'Vorige',
|
|
348
362
|
'settings_menu': 'Instellingen',
|
|
349
363
|
'loading': 'Laden...',
|
|
350
|
-
'encoding_in_progress': 'Codering bezig'
|
|
364
|
+
'encoding_in_progress': 'Codering bezig',
|
|
365
|
+
'restart_video': 'Restart video'
|
|
351
366
|
},
|
|
352
367
|
|
|
353
368
|
'hi': {
|
|
@@ -370,7 +385,8 @@ class VideoPlayerI18n {
|
|
|
370
385
|
'playlist_prev': 'पिछला',
|
|
371
386
|
'settings_menu': 'सेटिंग्स',
|
|
372
387
|
'loading': 'लोड हो रहा है...',
|
|
373
|
-
'encoding_in_progress': 'एन्कोडिंग प्रगति में'
|
|
388
|
+
'encoding_in_progress': 'एन्कोडिंग प्रगति में',
|
|
389
|
+
'restart_video': 'Restart video'
|
|
374
390
|
},
|
|
375
391
|
|
|
376
392
|
'sv': {
|
|
@@ -393,7 +409,8 @@ class VideoPlayerI18n {
|
|
|
393
409
|
'playlist_prev': 'Föregående',
|
|
394
410
|
'settings_menu': 'Inställningar',
|
|
395
411
|
'loading': 'Laddar...',
|
|
396
|
-
'encoding_in_progress': 'Kodning pågår'
|
|
412
|
+
'encoding_in_progress': 'Kodning pågår',
|
|
413
|
+
'restart_video': 'Restart video'
|
|
397
414
|
},
|
|
398
415
|
|
|
399
416
|
'id': {
|
|
@@ -416,7 +433,8 @@ class VideoPlayerI18n {
|
|
|
416
433
|
'playlist_prev': 'Sebelumnya',
|
|
417
434
|
'settings_menu': 'Pengaturan',
|
|
418
435
|
'loading': 'Memuat...',
|
|
419
|
-
'encoding_in_progress': 'Encoding sedang berlangsung'
|
|
436
|
+
'encoding_in_progress': 'Encoding sedang berlangsung',
|
|
437
|
+
'restart_video': 'Restart video'
|
|
420
438
|
}
|
|
421
439
|
};
|
|
422
440
|
|
|
@@ -531,7 +549,8 @@ try {
|
|
|
531
549
|
'brand_logo': 'Brand logo',
|
|
532
550
|
'settings_menu': 'Settings',
|
|
533
551
|
'loading': 'Loading...',
|
|
534
|
-
'encoding_in_progress': 'Encoding in progress'
|
|
552
|
+
'encoding_in_progress': 'Encoding in progress',
|
|
553
|
+
'restart_video': 'Restart video'
|
|
535
554
|
};
|
|
536
555
|
return fallback[key] || key;
|
|
537
556
|
},
|
|
@@ -584,6 +603,7 @@ constructor(videoElement, options = {}) {
|
|
|
584
603
|
}
|
|
585
604
|
|
|
586
605
|
this.options = {
|
|
606
|
+
playFromStartButton: false, // Enable play from start button (restart video)
|
|
587
607
|
showQualitySelector: true, // Enable quality selector button
|
|
588
608
|
showSpeedControl: true, // Enable speed control button
|
|
589
609
|
showFullscreen: true, // Enable fullscreen button
|
|
@@ -683,6 +703,7 @@ constructor(videoElement, options = {}) {
|
|
|
683
703
|
'played': [], // Fired when video starts playing
|
|
684
704
|
'paused': [], // Fired when video is paused
|
|
685
705
|
'ended': [], // Fired when video playback ends
|
|
706
|
+
'restarted': [], // Fired when video is restarted from beginning
|
|
686
707
|
|
|
687
708
|
'playing': [], // Fired when video is actually playing (after buffering)
|
|
688
709
|
'waiting': [], // Fired when video is waiting for data (buffering)
|
|
@@ -1370,6 +1391,7 @@ initializeElements() {
|
|
|
1370
1391
|
this.progressHandle = this.controls?.querySelector('.progress-handle');
|
|
1371
1392
|
this.seekTooltip = this.controls?.querySelector('.seek-tooltip');
|
|
1372
1393
|
|
|
1394
|
+
this.playFromStartBtn = this.controls?.querySelector('.play-from-start-btn');
|
|
1373
1395
|
this.playPauseBtn = this.controls?.querySelector('.play-pause-btn');
|
|
1374
1396
|
this.muteBtn = this.controls?.querySelector('.mute-btn');
|
|
1375
1397
|
this.fullscreenBtn = this.controls?.querySelector('.fullscreen-btn');
|
|
@@ -1473,6 +1495,42 @@ setupMenuToggles() {
|
|
|
1473
1495
|
}
|
|
1474
1496
|
}
|
|
1475
1497
|
|
|
1498
|
+
restartVideo() {
|
|
1499
|
+
if (!this.video) return this;
|
|
1500
|
+
|
|
1501
|
+
const previousTime = this.getCurrentTime();
|
|
1502
|
+
const wasPaused = this.isPaused();
|
|
1503
|
+
|
|
1504
|
+
this.video.currentTime = 0;
|
|
1505
|
+
|
|
1506
|
+
if (typeof this.seek === 'function') {
|
|
1507
|
+
this.seek(0);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
if (!wasPaused) {
|
|
1511
|
+
|
|
1512
|
+
if (typeof this.play === 'function') {
|
|
1513
|
+
this.play();
|
|
1514
|
+
} else {
|
|
1515
|
+
this.video.play().catch(error => {
|
|
1516
|
+
if (this.options.debug) console.warn('⚠️ Restart play failed:', error);
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
if (this.options.debug) {
|
|
1522
|
+
console.log(`🔄 Video restarted from ${this.formatTime(previousTime)} to 0:00`);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
this.triggerEvent('restarted', {
|
|
1526
|
+
previousTime: previousTime,
|
|
1527
|
+
duration: this.getDuration(),
|
|
1528
|
+
autoPlayed: !wasPaused
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
return this;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1476
1534
|
updateVolumeSliderVisual() {
|
|
1477
1535
|
if (!this.video || !this.container) return;
|
|
1478
1536
|
|
|
@@ -2521,6 +2579,13 @@ addEventListener(eventType, callback) {
|
|
|
2521
2579
|
bindEvents() {
|
|
2522
2580
|
if (this.video) {
|
|
2523
2581
|
|
|
2582
|
+
if (this.playFromStartBtn) {
|
|
2583
|
+
this.playFromStartBtn.addEventListener('click', (e) => {
|
|
2584
|
+
e.stopPropagation();
|
|
2585
|
+
this.restartVideo();
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2524
2589
|
this.video.addEventListener('playing', () => {
|
|
2525
2590
|
this.hideLoading();
|
|
2526
2591
|
this.closeAllMenus();
|
|
@@ -3287,10 +3352,20 @@ createControls() {
|
|
|
3287
3352
|
|
|
3288
3353
|
<div class="controls-main">
|
|
3289
3354
|
<div class="controls-left">
|
|
3290
|
-
|
|
3355
|
+
${this.options.playFromStartButton ? `
|
|
3356
|
+
<button class="control-btn play-from-start-btn" data-tooltip="restart_video">
|
|
3357
|
+
<span class="icon restart-icon">
|
|
3358
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
3359
|
+
<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
|
|
3360
|
+
</svg>
|
|
3361
|
+
</span>
|
|
3362
|
+
</button>
|
|
3363
|
+
` : ''}
|
|
3364
|
+
|
|
3365
|
+
<button class="control-btn play-pause-btn" data-tooltip="play_pause">
|
|
3291
3366
|
<span class="icon play-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></span>
|
|
3292
3367
|
<span class="icon pause-icon hidden"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M6 4h4v16H6zm8 0h4v16h-4z"/></svg></span>
|
|
3293
|
-
|
|
3368
|
+
</button>
|
|
3294
3369
|
|
|
3295
3370
|
<button class="control-btn mute-btn" data-tooltip="mute_unmute">
|
|
3296
3371
|
<span class="icon volume-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303z"/><path d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89z"/><path d="M10.025 8a4.486 4.486 0 0 1-1.318 3.182L8 10.475A3.489 3.489 0 0 0 9.025 8c0-.966-.392-1.841-1.025-2.475l.707-.707A4.486 4.486 0 0 1 10.025 8M7 4a.5.5 0 0 0-.812-.39L3.825 5.5H1.5A.5.5 0 0 0 1 6v4a.5.5 0 0 0 .5.5h2.325l2.363 1.89A.5.5 0 0 0 7 12z"/></svg></span>
|
|
@@ -3306,7 +3381,7 @@ createControls() {
|
|
|
3306
3381
|
<span>/</span>
|
|
3307
3382
|
<span class="duration">0:00</span>
|
|
3308
3383
|
</div>
|
|
3309
|
-
|
|
3384
|
+
</div>
|
|
3310
3385
|
|
|
3311
3386
|
<div class="controls-right">
|
|
3312
3387
|
<button class="control-btn playlist-prev-btn" data-tooltip="prevvideo" style="display: none;">
|
|
@@ -3323,7 +3398,7 @@ createControls() {
|
|
|
3323
3398
|
<div class="settings-menu"></div>
|
|
3324
3399
|
</div>
|
|
3325
3400
|
|
|
3326
|
-
|
|
3401
|
+
${(this.options.showQualitySelector && this.originalSources && this.originalSources.length > 1) || this.options.adaptiveQualityControl ? `
|
|
3327
3402
|
<div class="quality-control">
|
|
3328
3403
|
<button class="control-btn quality-btn" data-tooltip="video_quality">
|
|
3329
3404
|
<div class="quality-btn-text">
|
|
@@ -4115,6 +4190,11 @@ updateQualityDisplay() {
|
|
|
4115
4190
|
}
|
|
4116
4191
|
|
|
4117
4192
|
updateQualityButton() {
|
|
4193
|
+
if (this.isAdaptiveStream) {
|
|
4194
|
+
if (this.options.debug) console.log('🔒 Adaptive streaming active - quality button managed by streaming.js');
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4118
4198
|
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
4119
4199
|
if (!qualityBtn) return;
|
|
4120
4200
|
|
|
@@ -4161,6 +4241,11 @@ updateQualityMenu() {
|
|
|
4161
4241
|
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
4162
4242
|
if (!qualityMenu) return;
|
|
4163
4243
|
|
|
4244
|
+
if (this.isAdaptiveStream) {
|
|
4245
|
+
if (this.options.debug) console.log('🔒 Adaptive streaming active - quality menu managed by streaming.js');
|
|
4246
|
+
return;
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4164
4249
|
let menuHTML = '';
|
|
4165
4250
|
|
|
4166
4251
|
if (this.isAdaptiveStream && this.adaptiveQualities && this.adaptiveQualities.length > 0) {
|
|
@@ -6495,6 +6580,13 @@ async loadAdaptiveLibraries() {
|
|
|
6495
6580
|
|
|
6496
6581
|
try {
|
|
6497
6582
|
|
|
6583
|
+
this.selectedQuality = 'auto';
|
|
6584
|
+
this.qualityEventsInitialized = false;
|
|
6585
|
+
|
|
6586
|
+
if (this.options.debug) {
|
|
6587
|
+
console.log('🔍 initializeDash - FORCED selectedQuality to:', this.selectedQuality);
|
|
6588
|
+
}
|
|
6589
|
+
|
|
6498
6590
|
if (this.dashPlayer) {
|
|
6499
6591
|
this.dashPlayer.destroy();
|
|
6500
6592
|
}
|
|
@@ -6646,17 +6738,67 @@ disableDashTextTracks() {
|
|
|
6646
6738
|
}
|
|
6647
6739
|
}
|
|
6648
6740
|
|
|
6649
|
-
|
|
6741
|
+
updateAdaptiveQualities() {
|
|
6650
6742
|
this.adaptiveQualities = [];
|
|
6651
6743
|
|
|
6652
6744
|
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6745
|
+
try {
|
|
6746
|
+
|
|
6747
|
+
const videoTracks = this.dashPlayer.getTracksFor('video');
|
|
6748
|
+
|
|
6749
|
+
if (this.options.debug) {
|
|
6750
|
+
console.log('✅ DASH getTracksFor result:', videoTracks);
|
|
6751
|
+
}
|
|
6752
|
+
|
|
6753
|
+
if (videoTracks && videoTracks.length > 0) {
|
|
6754
|
+
|
|
6755
|
+
const allQualities = [];
|
|
6756
|
+
|
|
6757
|
+
videoTracks.forEach((track, trackIndex) => {
|
|
6758
|
+
const bitrateList = track.bitrateList || [];
|
|
6759
|
+
|
|
6760
|
+
if (this.options.debug) {
|
|
6761
|
+
console.log(`✅ Track ${trackIndex} (${track.codec}):`, bitrateList);
|
|
6762
|
+
}
|
|
6763
|
+
|
|
6764
|
+
bitrateList.forEach((bitrate, index) => {
|
|
6765
|
+
allQualities.push({
|
|
6766
|
+
trackIndex: trackIndex,
|
|
6767
|
+
bitrateIndex: index,
|
|
6768
|
+
label: `${bitrate.height}p`,
|
|
6657
6769
|
height: bitrate.height,
|
|
6658
|
-
|
|
6659
|
-
|
|
6770
|
+
width: bitrate.width,
|
|
6771
|
+
bandwidth: bitrate.bandwidth,
|
|
6772
|
+
codec: track.codec
|
|
6773
|
+
});
|
|
6774
|
+
});
|
|
6775
|
+
});
|
|
6776
|
+
|
|
6777
|
+
const uniqueHeights = [...new Set(allQualities.map(q => q.height))];
|
|
6778
|
+
uniqueHeights.sort((a, b) => b - a);
|
|
6779
|
+
|
|
6780
|
+
this.adaptiveQualities = uniqueHeights.map((height, index) => {
|
|
6781
|
+
const quality = allQualities.find(q => q.height === height);
|
|
6782
|
+
return {
|
|
6783
|
+
index: index,
|
|
6784
|
+
label: `${height}p`,
|
|
6785
|
+
height: height,
|
|
6786
|
+
trackIndex: quality.trackIndex,
|
|
6787
|
+
bitrateIndex: quality.bitrateIndex,
|
|
6788
|
+
bandwidth: quality.bandwidth,
|
|
6789
|
+
codec: quality.codec
|
|
6790
|
+
};
|
|
6791
|
+
});
|
|
6792
|
+
|
|
6793
|
+
if (this.options.debug) {
|
|
6794
|
+
console.log('✅ All DASH qualities merged:', this.adaptiveQualities);
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
} catch (error) {
|
|
6798
|
+
if (this.options.debug) {
|
|
6799
|
+
console.error('❌ Error getting DASH qualities:', error);
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6660
6802
|
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
6661
6803
|
const levels = this.hlsPlayer.levels;
|
|
6662
6804
|
this.adaptiveQualities = levels.map((level, index) => ({
|
|
@@ -6673,9 +6815,361 @@ disableDashTextTracks() {
|
|
|
6673
6815
|
|
|
6674
6816
|
if (this.options.debug) {
|
|
6675
6817
|
console.log('📡 Adaptive qualities available:', this.adaptiveQualities);
|
|
6818
|
+
console.log('📡 Selected quality mode:', this.selectedQuality);
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
updateAdaptiveQualityMenu() {
|
|
6823
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
6824
|
+
if (!qualityMenu) {
|
|
6825
|
+
if (this.options.debug) console.log('❌ Quality menu not found in DOM');
|
|
6826
|
+
return;
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6829
|
+
if (this.adaptiveQualities.length === 0) {
|
|
6830
|
+
if (this.options.debug) console.log('❌ No adaptive qualities to display');
|
|
6831
|
+
return;
|
|
6832
|
+
}
|
|
6833
|
+
|
|
6834
|
+
const isAutoActive = this.selectedQuality === 'auto';
|
|
6835
|
+
let menuHTML = `<div class="quality-option ${isAutoActive ? 'active' : ''}" data-quality="auto">Auto</div>`;
|
|
6836
|
+
|
|
6837
|
+
this.adaptiveQualities.forEach((quality) => {
|
|
6838
|
+
const isActive = this.selectedQuality === quality.height;
|
|
6839
|
+
|
|
6840
|
+
if (this.options.debug) {
|
|
6841
|
+
console.log('🔍 Quality item:', quality.label, 'height:', quality.height, 'active:', isActive);
|
|
6842
|
+
}
|
|
6843
|
+
|
|
6844
|
+
menuHTML += `<div class="quality-option ${isActive ? 'active' : ''}" data-quality="${quality.height}">
|
|
6845
|
+
${quality.label}
|
|
6846
|
+
<span class="quality-playing" style="display: none; color: #4CAF50; margin-left: 8px; font-size: 0.85em;">● Playing</span>
|
|
6847
|
+
</div>`;
|
|
6848
|
+
});
|
|
6849
|
+
|
|
6850
|
+
qualityMenu.innerHTML = menuHTML;
|
|
6851
|
+
|
|
6852
|
+
if (this.options.debug) {
|
|
6853
|
+
console.log('✅ Quality menu populated with', this.adaptiveQualities.length, 'options');
|
|
6676
6854
|
}
|
|
6855
|
+
|
|
6856
|
+
if (!this.qualityEventsInitialized) {
|
|
6857
|
+
this.bindAdaptiveQualityEvents();
|
|
6858
|
+
this.qualityEventsInitialized = true;
|
|
6677
6859
|
}
|
|
6678
6860
|
|
|
6861
|
+
this.updateAdaptiveQualityDisplay();
|
|
6862
|
+
}
|
|
6863
|
+
|
|
6864
|
+
updateAdaptiveQualityDisplay() {
|
|
6865
|
+
if (!this.dashPlayer && !this.hlsPlayer) return;
|
|
6866
|
+
|
|
6867
|
+
let currentHeight = null;
|
|
6868
|
+
|
|
6869
|
+
try {
|
|
6870
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
6871
|
+
|
|
6872
|
+
if (this.video && this.video.videoHeight) {
|
|
6873
|
+
currentHeight = this.video.videoHeight;
|
|
6874
|
+
}
|
|
6875
|
+
|
|
6876
|
+
if (this.options.debug) {
|
|
6877
|
+
console.log('📊 Current video height:', currentHeight, 'Selected mode:', this.selectedQuality);
|
|
6878
|
+
}
|
|
6879
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
6880
|
+
const currentLevel = this.hlsPlayer.currentLevel;
|
|
6881
|
+
if (currentLevel >= 0 && this.hlsPlayer.levels[currentLevel]) {
|
|
6882
|
+
currentHeight = this.hlsPlayer.levels[currentLevel].height;
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6886
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
6887
|
+
if (qualityBtnText) {
|
|
6888
|
+
if (this.selectedQuality === 'auto') {
|
|
6889
|
+
qualityBtnText.textContent = 'Auto';
|
|
6890
|
+
} else {
|
|
6891
|
+
qualityBtnText.textContent = `${this.selectedQuality}p`;
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
const currentQualityText = this.controls?.querySelector('.quality-btn .current-quality');
|
|
6896
|
+
if (currentQualityText) {
|
|
6897
|
+
if (this.selectedQuality === 'auto' && currentHeight) {
|
|
6898
|
+
currentQualityText.textContent = `${currentHeight}p`;
|
|
6899
|
+
currentQualityText.style.display = 'block';
|
|
6900
|
+
} else {
|
|
6901
|
+
currentQualityText.textContent = '';
|
|
6902
|
+
currentQualityText.style.display = 'none';
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
|
|
6906
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
6907
|
+
if (qualityMenu) {
|
|
6908
|
+
|
|
6909
|
+
qualityMenu.querySelectorAll('.quality-option').forEach(opt => {
|
|
6910
|
+
opt.classList.remove('active');
|
|
6911
|
+
});
|
|
6912
|
+
|
|
6913
|
+
if (this.selectedQuality === 'auto') {
|
|
6914
|
+
const autoOption = qualityMenu.querySelector('[data-quality="auto"]');
|
|
6915
|
+
if (autoOption) autoOption.classList.add('active');
|
|
6916
|
+
} else {
|
|
6917
|
+
const selectedOption = qualityMenu.querySelector(`[data-quality="${this.selectedQuality}"]`);
|
|
6918
|
+
if (selectedOption) selectedOption.classList.add('active');
|
|
6919
|
+
}
|
|
6920
|
+
|
|
6921
|
+
qualityMenu.querySelectorAll('.quality-playing').forEach(el => {
|
|
6922
|
+
el.style.display = 'none';
|
|
6923
|
+
});
|
|
6924
|
+
|
|
6925
|
+
if (this.selectedQuality === 'auto' && currentHeight) {
|
|
6926
|
+
const playingOption = qualityMenu.querySelector(`[data-quality="${currentHeight}"] .quality-playing`);
|
|
6927
|
+
if (playingOption) {
|
|
6928
|
+
playingOption.style.display = 'inline';
|
|
6929
|
+
}
|
|
6930
|
+
}
|
|
6931
|
+
}
|
|
6932
|
+
|
|
6933
|
+
} catch (error) {
|
|
6934
|
+
if (this.options.debug) console.error('❌ Error updating quality display:', error);
|
|
6935
|
+
}
|
|
6936
|
+
}
|
|
6937
|
+
|
|
6938
|
+
updateQualityButtonText() {
|
|
6939
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
6940
|
+
if (!qualityBtn) return;
|
|
6941
|
+
|
|
6942
|
+
if (this.selectedQuality === 'auto' || !this.selectedQuality) {
|
|
6943
|
+
qualityBtn.textContent = this.t('auto');
|
|
6944
|
+
} else {
|
|
6945
|
+
const quality = this.adaptiveQualities.find(q => q.index === parseInt(this.selectedQuality));
|
|
6946
|
+
qualityBtn.textContent = quality ? quality.label : 'Auto';
|
|
6947
|
+
}
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6950
|
+
bindAdaptiveQualityEvents() {
|
|
6951
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
6952
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
6953
|
+
|
|
6954
|
+
if (!qualityMenu || !qualityBtn) return;
|
|
6955
|
+
|
|
6956
|
+
qualityBtn.addEventListener('click', (e) => {
|
|
6957
|
+
e.stopPropagation();
|
|
6958
|
+
qualityMenu.classList.toggle('active');
|
|
6959
|
+
|
|
6960
|
+
if (qualityMenu.classList.contains('active')) {
|
|
6961
|
+
this.updateAdaptiveQualityDisplay();
|
|
6962
|
+
}
|
|
6963
|
+
});
|
|
6964
|
+
|
|
6965
|
+
const closeMenuHandler = (e) => {
|
|
6966
|
+
if (!qualityBtn.contains(e.target) && !qualityMenu.contains(e.target)) {
|
|
6967
|
+
qualityMenu.classList.remove('active');
|
|
6968
|
+
}
|
|
6969
|
+
};
|
|
6970
|
+
document.addEventListener('click', closeMenuHandler);
|
|
6971
|
+
|
|
6972
|
+
qualityMenu.addEventListener('click', (e) => {
|
|
6973
|
+
const option = e.target.closest('.quality-option');
|
|
6974
|
+
if (!option) return;
|
|
6975
|
+
|
|
6976
|
+
e.stopPropagation();
|
|
6977
|
+
|
|
6978
|
+
const qualityData = option.getAttribute('data-quality');
|
|
6979
|
+
|
|
6980
|
+
if (this.options.debug) {
|
|
6981
|
+
console.log('🎬 Quality clicked - raw data:', qualityData, 'type:', typeof qualityData);
|
|
6982
|
+
}
|
|
6983
|
+
|
|
6984
|
+
if (qualityData === 'auto') {
|
|
6985
|
+
|
|
6986
|
+
this.selectedQuality = 'auto';
|
|
6987
|
+
|
|
6988
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
6989
|
+
this.dashPlayer.updateSettings({
|
|
6990
|
+
streaming: {
|
|
6991
|
+
abr: {
|
|
6992
|
+
autoSwitchBitrate: { video: true }
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
});
|
|
6996
|
+
if (this.options.debug) console.log('✅ Auto quality enabled');
|
|
6997
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
6998
|
+
this.hlsPlayer.currentLevel = -1;
|
|
6999
|
+
}
|
|
7000
|
+
|
|
7001
|
+
} else {
|
|
7002
|
+
|
|
7003
|
+
const selectedHeight = parseInt(qualityData, 10);
|
|
7004
|
+
|
|
7005
|
+
if (isNaN(selectedHeight)) {
|
|
7006
|
+
if (this.options.debug) console.error('❌ Invalid quality data:', qualityData);
|
|
7007
|
+
return;
|
|
7008
|
+
}
|
|
7009
|
+
|
|
7010
|
+
if (this.options.debug) {
|
|
7011
|
+
console.log('🎬 Setting manual quality to height:', selectedHeight);
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
this.selectedQuality = selectedHeight;
|
|
7015
|
+
|
|
7016
|
+
if (this.adaptiveStreamingType === 'dash') {
|
|
7017
|
+
this.setDashQualityByHeight(selectedHeight);
|
|
7018
|
+
} else if (this.adaptiveStreamingType === 'hls') {
|
|
7019
|
+
const levelIndex = this.hlsPlayer.levels.findIndex(l => l.height === selectedHeight);
|
|
7020
|
+
if (levelIndex >= 0) {
|
|
7021
|
+
this.hlsPlayer.currentLevel = levelIndex;
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
|
|
7026
|
+
this.updateAdaptiveQualityDisplay();
|
|
7027
|
+
|
|
7028
|
+
qualityMenu.classList.remove('active');
|
|
7029
|
+
});
|
|
7030
|
+
|
|
7031
|
+
if (this.options.debug) {
|
|
7032
|
+
console.log('✅ Quality events bound');
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
7035
|
+
|
|
7036
|
+
setDashQualityByHeight(targetHeight) {
|
|
7037
|
+
if (!this.dashPlayer) return;
|
|
7038
|
+
|
|
7039
|
+
try {
|
|
7040
|
+
const targetQuality = this.adaptiveQualities.find(q => q.height === targetHeight);
|
|
7041
|
+
if (!targetQuality) {
|
|
7042
|
+
if (this.options.debug) console.error('❌ Quality not found for height:', targetHeight);
|
|
7043
|
+
return;
|
|
7044
|
+
}
|
|
7045
|
+
|
|
7046
|
+
if (this.options.debug) {
|
|
7047
|
+
console.log('🎬 Setting quality:', targetQuality);
|
|
7048
|
+
}
|
|
7049
|
+
|
|
7050
|
+
this.dashPlayer.updateSettings({
|
|
7051
|
+
streaming: {
|
|
7052
|
+
abr: {
|
|
7053
|
+
autoSwitchBitrate: { video: false }
|
|
7054
|
+
}
|
|
7055
|
+
}
|
|
7056
|
+
});
|
|
7057
|
+
|
|
7058
|
+
const currentTrack = this.dashPlayer.getCurrentTrackFor('video');
|
|
7059
|
+
|
|
7060
|
+
if (!currentTrack) {
|
|
7061
|
+
if (this.options.debug) console.error('❌ No current video track');
|
|
7062
|
+
return;
|
|
7063
|
+
}
|
|
7064
|
+
|
|
7065
|
+
const allTracks = this.dashPlayer.getTracksFor('video');
|
|
7066
|
+
let targetTrack = null;
|
|
7067
|
+
|
|
7068
|
+
for (const track of allTracks) {
|
|
7069
|
+
if (track.bitrateList && track.bitrateList[targetQuality.bitrateIndex]) {
|
|
7070
|
+
const bitrate = track.bitrateList[targetQuality.bitrateIndex];
|
|
7071
|
+
if (bitrate.height === targetHeight) {
|
|
7072
|
+
targetTrack = track;
|
|
7073
|
+
break;
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
|
|
7078
|
+
if (!targetTrack) {
|
|
7079
|
+
if (this.options.debug) console.error('❌ Target track not found');
|
|
7080
|
+
return;
|
|
7081
|
+
}
|
|
7082
|
+
|
|
7083
|
+
if (currentTrack.index !== targetTrack.index) {
|
|
7084
|
+
this.dashPlayer.setCurrentTrack(targetTrack);
|
|
7085
|
+
if (this.options.debug) {
|
|
7086
|
+
console.log('✅ Switched to track:', targetTrack.index);
|
|
7087
|
+
}
|
|
7088
|
+
}
|
|
7089
|
+
|
|
7090
|
+
setTimeout(() => {
|
|
7091
|
+
try {
|
|
7092
|
+
|
|
7093
|
+
this.dashPlayer.updateSettings({
|
|
7094
|
+
streaming: {
|
|
7095
|
+
abr: {
|
|
7096
|
+
initialBitrate: { video: targetQuality.bandwidth / 1000 },
|
|
7097
|
+
maxBitrate: { video: targetQuality.bandwidth / 1000 },
|
|
7098
|
+
minBitrate: { video: targetQuality.bandwidth / 1000 }
|
|
7099
|
+
}
|
|
7100
|
+
}
|
|
7101
|
+
});
|
|
7102
|
+
|
|
7103
|
+
if (this.options.debug) {
|
|
7104
|
+
console.log('✅ Quality locked to:', targetHeight + 'p', 'bandwidth:', targetQuality.bandwidth);
|
|
7105
|
+
}
|
|
7106
|
+
|
|
7107
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7108
|
+
if (qualityBtnText) {
|
|
7109
|
+
qualityBtnText.textContent = `${targetHeight}p`;
|
|
7110
|
+
}
|
|
7111
|
+
|
|
7112
|
+
const currentTime = this.video.currentTime;
|
|
7113
|
+
this.dashPlayer.seek(currentTime + 0.1);
|
|
7114
|
+
setTimeout(() => {
|
|
7115
|
+
this.dashPlayer.seek(currentTime);
|
|
7116
|
+
}, 100);
|
|
7117
|
+
|
|
7118
|
+
} catch (innerError) {
|
|
7119
|
+
if (this.options.debug) console.error('❌ Error setting quality:', innerError);
|
|
7120
|
+
}
|
|
7121
|
+
}, 100);
|
|
7122
|
+
|
|
7123
|
+
} catch (error) {
|
|
7124
|
+
if (this.options.debug) console.error('❌ Error in setDashQualityByHeight:', error);
|
|
7125
|
+
}
|
|
7126
|
+
}
|
|
7127
|
+
|
|
7128
|
+
setDashQuality(qualityIndex) {
|
|
7129
|
+
if (!this.dashPlayer) return;
|
|
7130
|
+
|
|
7131
|
+
try {
|
|
7132
|
+
const selectedQuality = this.adaptiveQualities[qualityIndex];
|
|
7133
|
+
if (!selectedQuality) {
|
|
7134
|
+
if (this.options.debug) console.error('❌ Quality not found at index:', qualityIndex);
|
|
7135
|
+
return;
|
|
7136
|
+
}
|
|
7137
|
+
|
|
7138
|
+
if (this.options.debug) {
|
|
7139
|
+
console.log('🎬 Setting DASH quality:', selectedQuality);
|
|
7140
|
+
}
|
|
7141
|
+
|
|
7142
|
+
this.dashPlayer.updateSettings({
|
|
7143
|
+
streaming: {
|
|
7144
|
+
abr: {
|
|
7145
|
+
autoSwitchBitrate: { video: false }
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
});
|
|
7149
|
+
|
|
7150
|
+
setTimeout(() => {
|
|
7151
|
+
try {
|
|
7152
|
+
this.dashPlayer.setQualityFor('video', selectedQuality.bitrateIndex);
|
|
7153
|
+
|
|
7154
|
+
if (this.options.debug) {
|
|
7155
|
+
console.log('✅ DASH quality set to bitrateIndex:', selectedQuality.bitrateIndex, 'height:', selectedQuality.height);
|
|
7156
|
+
}
|
|
7157
|
+
|
|
7158
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7159
|
+
if (qualityBtnText) {
|
|
7160
|
+
qualityBtnText.textContent = selectedQuality.label;
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7163
|
+
} catch (innerError) {
|
|
7164
|
+
if (this.options.debug) console.error('❌ Error setting quality:', innerError);
|
|
7165
|
+
}
|
|
7166
|
+
}, 100);
|
|
7167
|
+
|
|
7168
|
+
} catch (error) {
|
|
7169
|
+
if (this.options.debug) console.error('❌ Error in setDashQuality:', error);
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
|
|
6679
7173
|
handleAdaptiveError(data) {
|
|
6680
7174
|
if (this.options.debug) console.error('📡 Fatal adaptive streaming error:', data);
|
|
6681
7175
|
|