myetv-player 1.4.0 → 1.6.0
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 +65 -1
- package/css/myetv-player.css +7 -2
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +669 -79
- package/dist/myetv-player.min.js +579 -42
- 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/soundcloud/myetv-player-soundcloud-plugin.js +927 -0
- package/plugins/soundcloud/readme.md +89 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +155 -164
package/dist/myetv-player.js
CHANGED
|
@@ -25,7 +25,9 @@ class VideoPlayerI18n {
|
|
|
25
25
|
'prev_video': 'Video precedente (P)',
|
|
26
26
|
'playlist_next': 'Avanti',
|
|
27
27
|
'playlist_prev': 'Indietro',
|
|
28
|
-
'settings_menu': 'Impostazioni'
|
|
28
|
+
'settings_menu': 'Impostazioni',
|
|
29
|
+
'loading': 'Caricamento...',
|
|
30
|
+
'encoding_in_progress': 'Encoding in corso'
|
|
29
31
|
},
|
|
30
32
|
|
|
31
33
|
// English
|
|
@@ -47,7 +49,9 @@ class VideoPlayerI18n {
|
|
|
47
49
|
'prev_video': 'Previous video (P)',
|
|
48
50
|
'playlist_next': 'Next',
|
|
49
51
|
'playlist_prev': 'Previous',
|
|
50
|
-
'settings_menu': 'Settings'
|
|
52
|
+
'settings_menu': 'Settings',
|
|
53
|
+
'loading': 'Loading...',
|
|
54
|
+
'encoding_in_progress': 'Encoding in progress'
|
|
51
55
|
},
|
|
52
56
|
|
|
53
57
|
// Español
|
|
@@ -69,7 +73,9 @@ class VideoPlayerI18n {
|
|
|
69
73
|
'prev_video': 'Vídeo anterior (P)',
|
|
70
74
|
'playlist_next': 'Siguiente',
|
|
71
75
|
'playlist_prev': 'Anterior',
|
|
72
|
-
'settings_menu': 'Configuración'
|
|
76
|
+
'settings_menu': 'Configuración',
|
|
77
|
+
'loading': 'Cargando...',
|
|
78
|
+
'encoding_in_progress': 'Codificación en curso'
|
|
73
79
|
},
|
|
74
80
|
|
|
75
81
|
// Français
|
|
@@ -83,7 +89,7 @@ class VideoPlayerI18n {
|
|
|
83
89
|
'volume': 'Volume',
|
|
84
90
|
'playback_speed': 'Vitesse de lecture',
|
|
85
91
|
'video_quality': 'Qualité vidéo',
|
|
86
|
-
'picture_in_picture': 'Image dans l
|
|
92
|
+
'picture_in_picture': 'Image dans l\'image(P)',
|
|
87
93
|
'fullscreen': 'Plein écran (F)',
|
|
88
94
|
'auto': 'Auto',
|
|
89
95
|
'brand_logo': 'Logo de marque',
|
|
@@ -91,7 +97,9 @@ class VideoPlayerI18n {
|
|
|
91
97
|
'prev_video': 'Vidéo précédente (P)',
|
|
92
98
|
'playlist_next': 'Suivant',
|
|
93
99
|
'playlist_prev': 'Précédent',
|
|
94
|
-
'settings_menu': 'Paramètres'
|
|
100
|
+
'settings_menu': 'Paramètres',
|
|
101
|
+
'loading': 'Chargement...',
|
|
102
|
+
'encoding_in_progress': 'Encodage en cours'
|
|
95
103
|
},
|
|
96
104
|
|
|
97
105
|
// Deutsch
|
|
@@ -113,7 +121,9 @@ class VideoPlayerI18n {
|
|
|
113
121
|
'prev_video': 'Vorheriges Video (P)',
|
|
114
122
|
'playlist_next': 'Weiter',
|
|
115
123
|
'playlist_prev': 'Zurück',
|
|
116
|
-
'settings_menu': 'Einstellungen'
|
|
124
|
+
'settings_menu': 'Einstellungen',
|
|
125
|
+
'loading': 'Laden...',
|
|
126
|
+
'encoding_in_progress': 'Kodierung läuft'
|
|
117
127
|
},
|
|
118
128
|
|
|
119
129
|
// Português
|
|
@@ -135,7 +145,9 @@ class VideoPlayerI18n {
|
|
|
135
145
|
'prev_video': 'Vídeo anterior (P)',
|
|
136
146
|
'playlist_next': 'Próximo',
|
|
137
147
|
'playlist_prev': 'Anterior',
|
|
138
|
-
'settings_menu': 'Configurações'
|
|
148
|
+
'settings_menu': 'Configurações',
|
|
149
|
+
'loading': 'Carregando...',
|
|
150
|
+
'encoding_in_progress': 'Codificação em andamento'
|
|
139
151
|
},
|
|
140
152
|
|
|
141
153
|
// 中文
|
|
@@ -157,7 +169,9 @@ class VideoPlayerI18n {
|
|
|
157
169
|
'prev_video': '上一个视频 (P)',
|
|
158
170
|
'playlist_next': '下一个',
|
|
159
171
|
'playlist_prev': '上一个',
|
|
160
|
-
'settings_menu': '设置'
|
|
172
|
+
'settings_menu': '设置',
|
|
173
|
+
'loading': '加载中...',
|
|
174
|
+
'encoding_in_progress': '编码中'
|
|
161
175
|
},
|
|
162
176
|
|
|
163
177
|
// 日本語
|
|
@@ -179,7 +193,9 @@ class VideoPlayerI18n {
|
|
|
179
193
|
'prev_video': '前の動画 (P)',
|
|
180
194
|
'playlist_next': '次へ',
|
|
181
195
|
'playlist_prev': '前へ',
|
|
182
|
-
'settings_menu': '設定'
|
|
196
|
+
'settings_menu': '設定',
|
|
197
|
+
'loading': '読み込み中...',
|
|
198
|
+
'encoding_in_progress': 'エンコード中'
|
|
183
199
|
},
|
|
184
200
|
|
|
185
201
|
// Русский
|
|
@@ -201,7 +217,9 @@ class VideoPlayerI18n {
|
|
|
201
217
|
'prev_video': 'Предыдущее видео (P)',
|
|
202
218
|
'playlist_next': 'Далее',
|
|
203
219
|
'playlist_prev': 'Назад',
|
|
204
|
-
'settings_menu': 'Настройки'
|
|
220
|
+
'settings_menu': 'Настройки',
|
|
221
|
+
'loading': 'Загрузка...',
|
|
222
|
+
'encoding_in_progress': 'Кодирование'
|
|
205
223
|
},
|
|
206
224
|
|
|
207
225
|
// العربية
|
|
@@ -223,7 +241,9 @@ class VideoPlayerI18n {
|
|
|
223
241
|
'prev_video': 'الفيديو السابق (P)',
|
|
224
242
|
'playlist_next': 'التالي',
|
|
225
243
|
'playlist_prev': 'السابق',
|
|
226
|
-
'settings_menu': 'الإعدادات'
|
|
244
|
+
'settings_menu': 'الإعدادات',
|
|
245
|
+
'loading': 'جاري التحميل...',
|
|
246
|
+
'encoding_in_progress': 'الترميز جارٍ'
|
|
227
247
|
},
|
|
228
248
|
|
|
229
249
|
// 한국어 (Korean)
|
|
@@ -245,7 +265,9 @@ class VideoPlayerI18n {
|
|
|
245
265
|
'prev_video': '이전 비디오 (P)',
|
|
246
266
|
'playlist_next': '다음',
|
|
247
267
|
'playlist_prev': '이전',
|
|
248
|
-
'settings_menu': '설정'
|
|
268
|
+
'settings_menu': '설정',
|
|
269
|
+
'loading': '로딩 중...',
|
|
270
|
+
'encoding_in_progress': '인코딩 진행 중'
|
|
249
271
|
},
|
|
250
272
|
|
|
251
273
|
// Polski
|
|
@@ -267,7 +289,9 @@ class VideoPlayerI18n {
|
|
|
267
289
|
'prev_video': 'Poprzednie wideo (P)',
|
|
268
290
|
'playlist_next': 'Dalej',
|
|
269
291
|
'playlist_prev': 'Wstecz',
|
|
270
|
-
'settings_menu': 'Ustawienia'
|
|
292
|
+
'settings_menu': 'Ustawienia',
|
|
293
|
+
'loading': 'Ładowanie...',
|
|
294
|
+
'encoding_in_progress': 'Kodowanie w toku'
|
|
271
295
|
},
|
|
272
296
|
|
|
273
297
|
// Magyar
|
|
@@ -289,7 +313,9 @@ class VideoPlayerI18n {
|
|
|
289
313
|
'prev_video': 'Előző videó (P)',
|
|
290
314
|
'playlist_next': 'Következő',
|
|
291
315
|
'playlist_prev': 'Előző',
|
|
292
|
-
'settings_menu': 'Beállítások'
|
|
316
|
+
'settings_menu': 'Beállítások',
|
|
317
|
+
'loading': 'Betöltés...',
|
|
318
|
+
'encoding_in_progress': 'Kódolás folyamatban'
|
|
293
319
|
},
|
|
294
320
|
|
|
295
321
|
// Türkçe
|
|
@@ -311,7 +337,9 @@ class VideoPlayerI18n {
|
|
|
311
337
|
'prev_video': 'Önceki video (P)',
|
|
312
338
|
'playlist_next': 'Sonraki',
|
|
313
339
|
'playlist_prev': 'Önceki',
|
|
314
|
-
'settings_menu': 'Ayarlar'
|
|
340
|
+
'settings_menu': 'Ayarlar',
|
|
341
|
+
'loading': 'Yükleniyor...',
|
|
342
|
+
'encoding_in_progress': 'Kodlama devam ediyor'
|
|
315
343
|
},
|
|
316
344
|
|
|
317
345
|
// Nederlands
|
|
@@ -333,7 +361,9 @@ class VideoPlayerI18n {
|
|
|
333
361
|
'prev_video': 'Vorige video (P)',
|
|
334
362
|
'playlist_next': 'Volgende',
|
|
335
363
|
'playlist_prev': 'Vorige',
|
|
336
|
-
'settings_menu': 'Instellingen'
|
|
364
|
+
'settings_menu': 'Instellingen',
|
|
365
|
+
'loading': 'Laden...',
|
|
366
|
+
'encoding_in_progress': 'Codering bezig'
|
|
337
367
|
},
|
|
338
368
|
|
|
339
369
|
// हिन्दी (Hindi)
|
|
@@ -355,7 +385,9 @@ class VideoPlayerI18n {
|
|
|
355
385
|
'prev_video': 'पिछला वीडियो (P)',
|
|
356
386
|
'playlist_next': 'अगला',
|
|
357
387
|
'playlist_prev': 'पिछला',
|
|
358
|
-
'settings_menu': 'सेटिंग्स'
|
|
388
|
+
'settings_menu': 'सेटिंग्स',
|
|
389
|
+
'loading': 'लोड हो रहा है...',
|
|
390
|
+
'encoding_in_progress': 'एन्कोडिंग प्रगति में'
|
|
359
391
|
},
|
|
360
392
|
|
|
361
393
|
// Svenska
|
|
@@ -377,7 +409,9 @@ class VideoPlayerI18n {
|
|
|
377
409
|
'prev_video': 'Föregående video (P)',
|
|
378
410
|
'playlist_next': 'Nästa',
|
|
379
411
|
'playlist_prev': 'Föregående',
|
|
380
|
-
'settings_menu': 'Inställningar'
|
|
412
|
+
'settings_menu': 'Inställningar',
|
|
413
|
+
'loading': 'Laddar...',
|
|
414
|
+
'encoding_in_progress': 'Kodning pågår'
|
|
381
415
|
},
|
|
382
416
|
|
|
383
417
|
// Bahasa Indonesia
|
|
@@ -399,7 +433,9 @@ class VideoPlayerI18n {
|
|
|
399
433
|
'prev_video': 'Video sebelumnya (P)',
|
|
400
434
|
'playlist_next': 'Berikutnya',
|
|
401
435
|
'playlist_prev': 'Sebelumnya',
|
|
402
|
-
'settings_menu': 'Pengaturan'
|
|
436
|
+
'settings_menu': 'Pengaturan',
|
|
437
|
+
'loading': 'Memuat...',
|
|
438
|
+
'encoding_in_progress': 'Encoding sedang berlangsung'
|
|
403
439
|
}
|
|
404
440
|
};
|
|
405
441
|
|
|
@@ -527,7 +563,9 @@ try {
|
|
|
527
563
|
'fullscreen': 'Fullscreen (F)',
|
|
528
564
|
'auto': 'Auto',
|
|
529
565
|
'brand_logo': 'Brand logo',
|
|
530
|
-
'settings_menu': 'Settings'
|
|
566
|
+
'settings_menu': 'Settings',
|
|
567
|
+
'loading': 'Loading...',
|
|
568
|
+
'encoding_in_progress': 'Encoding in progress'
|
|
531
569
|
};
|
|
532
570
|
return fallback[key] || key;
|
|
533
571
|
},
|
|
@@ -865,6 +903,59 @@ constructor(videoElement, options = {}) {
|
|
|
865
903
|
}
|
|
866
904
|
}
|
|
867
905
|
|
|
906
|
+
/**
|
|
907
|
+
* Decode HTML entities to normal characters
|
|
908
|
+
* @param {string} text - Text with HTML entities
|
|
909
|
+
* @returns {string} Decoded text
|
|
910
|
+
*/
|
|
911
|
+
decodeHTMLEntities(text) {
|
|
912
|
+
if (!text) return '';
|
|
913
|
+
const textarea = document.createElement('textarea');
|
|
914
|
+
textarea.innerHTML = text;
|
|
915
|
+
return textarea.value;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// check if the device is Fire TV
|
|
919
|
+
isFireTV() {
|
|
920
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
921
|
+
return ua.includes('aftm') ||
|
|
922
|
+
ua.includes('aftb') ||
|
|
923
|
+
ua.includes('afts') ||
|
|
924
|
+
ua.includes('aftmm') ||
|
|
925
|
+
ua.includes('aftt');
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// apply Fire TV specific optimizations
|
|
929
|
+
optimizeVideoForFireTV() {
|
|
930
|
+
if (!this.isFireTV() || !this.video) return;
|
|
931
|
+
|
|
932
|
+
if (this.options.debug) {
|
|
933
|
+
console.log('Fire TV detected - applying optimizations');
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// set playsinline attributes
|
|
937
|
+
this.video.setAttribute('playsinline', '');
|
|
938
|
+
this.video.setAttribute('webkit-playsinline', '');
|
|
939
|
+
|
|
940
|
+
// CSS optimizations
|
|
941
|
+
this.video.style.transform = 'translateZ(0)';
|
|
942
|
+
this.video.style.webkitTransform = 'translateZ(0)';
|
|
943
|
+
this.video.style.backfaceVisibility = 'hidden';
|
|
944
|
+
this.video.style.webkitBackfaceVisibility = 'hidden';
|
|
945
|
+
this.video.style.willChange = 'transform';
|
|
946
|
+
|
|
947
|
+
// force repaint on loadeddata
|
|
948
|
+
this.video.addEventListener('loadeddata', () => {
|
|
949
|
+
if (this.options.debug) {
|
|
950
|
+
console.log('Fire TV: Video loaded, forcing repaint');
|
|
951
|
+
}
|
|
952
|
+
this.video.style.display = 'none';
|
|
953
|
+
setTimeout(() => {
|
|
954
|
+
this.video.style.display = 'block';
|
|
955
|
+
}, 10);
|
|
956
|
+
}, { once: true });
|
|
957
|
+
}
|
|
958
|
+
|
|
868
959
|
getPlayerState() {
|
|
869
960
|
return {
|
|
870
961
|
isPlaying: !this.isPaused(),
|
|
@@ -1164,6 +1255,7 @@ createPlayerStructure() {
|
|
|
1164
1255
|
|
|
1165
1256
|
this.container = wrapper;
|
|
1166
1257
|
|
|
1258
|
+
this.optimizeVideoForFireTV();
|
|
1167
1259
|
this.createInitialLoading();
|
|
1168
1260
|
this.createLoadingOverlay();
|
|
1169
1261
|
this.collectVideoQualities();
|
|
@@ -1204,14 +1296,14 @@ createTitleOverlay() {
|
|
|
1204
1296
|
|
|
1205
1297
|
const titleText = document.createElement('h2');
|
|
1206
1298
|
titleText.className = 'title-text';
|
|
1207
|
-
titleText.textContent = this.options.videoTitle || '';
|
|
1299
|
+
titleText.textContent = this.decodeHTMLEntities(this.options.videoTitle) || '';
|
|
1208
1300
|
overlay.appendChild(titleText);
|
|
1209
1301
|
|
|
1210
1302
|
// add subtitles
|
|
1211
1303
|
if (this.options.videoSubtitle) {
|
|
1212
1304
|
const subtitleText = document.createElement('p');
|
|
1213
1305
|
subtitleText.className = 'subtitle-text';
|
|
1214
|
-
subtitleText.textContent = this.options.videoSubtitle;
|
|
1306
|
+
subtitleText.textContent = this.decodeHTMLEntities(this.options.videoSubtitle);
|
|
1215
1307
|
overlay.appendChild(subtitleText);
|
|
1216
1308
|
}
|
|
1217
1309
|
|
|
@@ -1266,7 +1358,7 @@ setVideoTitle(title) {
|
|
|
1266
1358
|
if (this.titleOverlay) {
|
|
1267
1359
|
const titleElement = this.titleOverlay.querySelector('.title-text');
|
|
1268
1360
|
if (titleElement) {
|
|
1269
|
-
titleElement.textContent = this.options.videoTitle;
|
|
1361
|
+
titleElement.textContent = this.decodeHTMLEntities(this.options.videoTitle);
|
|
1270
1362
|
}
|
|
1271
1363
|
|
|
1272
1364
|
if (title) {
|
|
@@ -1748,9 +1840,10 @@ updateProgress() {
|
|
|
1748
1840
|
this.progressHandle.style.left = progress + '%';
|
|
1749
1841
|
}
|
|
1750
1842
|
|
|
1843
|
+
// Always call updateTimeDisplay, regardless of duration validity
|
|
1751
1844
|
this.updateTimeDisplay();
|
|
1752
1845
|
|
|
1753
|
-
// Trigger timeupdate event
|
|
1846
|
+
// Trigger timeupdate event with throttling
|
|
1754
1847
|
if (!this.lastTimeUpdate || Date.now() - this.lastTimeUpdate > 250) {
|
|
1755
1848
|
this.triggerEvent('timeupdate', {
|
|
1756
1849
|
currentTime: this.getCurrentTime(),
|
|
@@ -1829,6 +1922,8 @@ updateDuration() {
|
|
|
1829
1922
|
if (this.durationEl && this.video && this.video.duration && !isNaN(this.video.duration)) {
|
|
1830
1923
|
this.durationEl.textContent = this.formatTime(this.video.duration);
|
|
1831
1924
|
}
|
|
1925
|
+
// Call updateTimeDisplay to handle all states (loading, encoding, normal)
|
|
1926
|
+
this.updateTimeDisplay();
|
|
1832
1927
|
}
|
|
1833
1928
|
|
|
1834
1929
|
changeSpeed(e) {
|
|
@@ -2121,7 +2216,7 @@ switchToVideo(newVideoElement, shouldPlay = false) {
|
|
|
2121
2216
|
if (newTitle && this.options.showTitleOverlay) {
|
|
2122
2217
|
this.options.videoTitle = newTitle;
|
|
2123
2218
|
if (this.titleText) {
|
|
2124
|
-
this.titleText.textContent = newTitle;
|
|
2219
|
+
this.titleText.textContent = this.decodeHTMLEntities(newTitle);
|
|
2125
2220
|
}
|
|
2126
2221
|
}
|
|
2127
2222
|
|
|
@@ -2715,6 +2810,10 @@ addEventListener(eventType, callback) {
|
|
|
2715
2810
|
if (!this.isChangingQuality) {
|
|
2716
2811
|
this.showLoading();
|
|
2717
2812
|
}
|
|
2813
|
+
|
|
2814
|
+
// Update time display to show "Loading..." during initial buffering
|
|
2815
|
+
this.updateTimeDisplay();
|
|
2816
|
+
|
|
2718
2817
|
// Trigger loadstart event - browser started loading media
|
|
2719
2818
|
this.triggerEvent('loadstart');
|
|
2720
2819
|
});
|
|
@@ -2722,6 +2821,9 @@ addEventListener(eventType, callback) {
|
|
|
2722
2821
|
this.video.addEventListener('loadedmetadata', () => {
|
|
2723
2822
|
this.updateDuration();
|
|
2724
2823
|
|
|
2824
|
+
// Update time display when metadata is loaded
|
|
2825
|
+
this.updateTimeDisplay();
|
|
2826
|
+
|
|
2725
2827
|
// Trigger loadedmetadata event - video metadata loaded
|
|
2726
2828
|
this.triggerEvent('loadedmetadata', {
|
|
2727
2829
|
duration: this.getDuration(),
|
|
@@ -2739,6 +2841,10 @@ addEventListener(eventType, callback) {
|
|
|
2739
2841
|
if (!this.isChangingQuality) {
|
|
2740
2842
|
this.hideLoading();
|
|
2741
2843
|
}
|
|
2844
|
+
|
|
2845
|
+
// Update time display when data is loaded
|
|
2846
|
+
this.updateTimeDisplay();
|
|
2847
|
+
|
|
2742
2848
|
// Trigger loadeddata event - current frame data loaded
|
|
2743
2849
|
this.triggerEvent('loadeddata', {
|
|
2744
2850
|
currentTime: this.getCurrentTime()
|
|
@@ -2749,6 +2855,10 @@ addEventListener(eventType, callback) {
|
|
|
2749
2855
|
if (!this.isChangingQuality) {
|
|
2750
2856
|
this.hideLoading();
|
|
2751
2857
|
}
|
|
2858
|
+
|
|
2859
|
+
// Update time display when video can play
|
|
2860
|
+
this.updateTimeDisplay();
|
|
2861
|
+
|
|
2752
2862
|
// Trigger canplay event - video can start playing
|
|
2753
2863
|
this.triggerEvent('canplay', {
|
|
2754
2864
|
currentTime: this.getCurrentTime(),
|
|
@@ -2756,6 +2866,21 @@ addEventListener(eventType, callback) {
|
|
|
2756
2866
|
});
|
|
2757
2867
|
});
|
|
2758
2868
|
|
|
2869
|
+
// Also add to waiting event
|
|
2870
|
+
this.video.addEventListener('waiting', () => {
|
|
2871
|
+
if (!this.isChangingQuality) {
|
|
2872
|
+
this.showLoading();
|
|
2873
|
+
|
|
2874
|
+
// Update time display during buffering
|
|
2875
|
+
this.updateTimeDisplay();
|
|
2876
|
+
|
|
2877
|
+
// Trigger waiting event - video is buffering
|
|
2878
|
+
this.triggerEvent('waiting', {
|
|
2879
|
+
currentTime: this.getCurrentTime()
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
});
|
|
2883
|
+
|
|
2759
2884
|
this.video.addEventListener('progress', () => {
|
|
2760
2885
|
this.updateBuffer();
|
|
2761
2886
|
// Trigger progress event - browser is downloading media
|
|
@@ -3498,7 +3623,7 @@ createControls() {
|
|
|
3498
3623
|
<div class="settings-menu"></div>
|
|
3499
3624
|
</div>
|
|
3500
3625
|
|
|
3501
|
-
|
|
3626
|
+
${(this.options.showQualitySelector && this.originalSources && this.originalSources.length > 1) || this.options.adaptiveQualityControl ? `
|
|
3502
3627
|
<div class="quality-control">
|
|
3503
3628
|
<button class="control-btn quality-btn" data-tooltip="video_quality">
|
|
3504
3629
|
<div class="quality-btn-text">
|
|
@@ -4369,6 +4494,11 @@ updateQualityDisplay() {
|
|
|
4369
4494
|
}
|
|
4370
4495
|
|
|
4371
4496
|
updateQualityButton() {
|
|
4497
|
+
if (this.isAdaptiveStream) {
|
|
4498
|
+
if (this.options.debug) console.log('🔒 Adaptive streaming active - quality button managed by streaming.js');
|
|
4499
|
+
return;
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4372
4502
|
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
4373
4503
|
if (!qualityBtn) return;
|
|
4374
4504
|
|
|
@@ -4420,6 +4550,11 @@ updateQualityMenu() {
|
|
|
4420
4550
|
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
4421
4551
|
if (!qualityMenu) return;
|
|
4422
4552
|
|
|
4553
|
+
if (this.isAdaptiveStream) {
|
|
4554
|
+
if (this.options.debug) console.log('🔒 Adaptive streaming active - quality menu managed by streaming.js');
|
|
4555
|
+
return;
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4423
4558
|
let menuHTML = '';
|
|
4424
4559
|
|
|
4425
4560
|
// Check if adaptive streaming is active (HLS/DASH)
|
|
@@ -7095,6 +7230,16 @@ async loadAdaptiveLibraries() {
|
|
|
7095
7230
|
}
|
|
7096
7231
|
|
|
7097
7232
|
try {
|
|
7233
|
+
// Initialize quality selection to Auto BEFORE creating player
|
|
7234
|
+
|
|
7235
|
+
// FORCE Auto mode - always reset at initialization
|
|
7236
|
+
this.selectedQuality = 'auto';
|
|
7237
|
+
this.qualityEventsInitialized = false;
|
|
7238
|
+
|
|
7239
|
+
if (this.options.debug) {
|
|
7240
|
+
console.log('🔍 initializeDash - FORCED selectedQuality to:', this.selectedQuality);
|
|
7241
|
+
}
|
|
7242
|
+
|
|
7098
7243
|
// Destroy existing DASH player
|
|
7099
7244
|
if (this.dashPlayer) {
|
|
7100
7245
|
this.dashPlayer.destroy();
|
|
@@ -7259,35 +7404,464 @@ disableDashTextTracks() {
|
|
|
7259
7404
|
}
|
|
7260
7405
|
}
|
|
7261
7406
|
|
|
7262
|
-
|
|
7263
|
-
|
|
7407
|
+
updateAdaptiveQualities() {
|
|
7408
|
+
this.adaptiveQualities = [];
|
|
7264
7409
|
|
|
7410
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
7411
|
+
try {
|
|
7412
|
+
// dash.js 5.x - Get ALL video tracks
|
|
7413
|
+
const videoTracks = this.dashPlayer.getTracksFor('video');
|
|
7414
|
+
|
|
7415
|
+
if (this.options.debug) {
|
|
7416
|
+
console.log('✅ DASH getTracksFor result:', videoTracks);
|
|
7417
|
+
}
|
|
7418
|
+
|
|
7419
|
+
if (videoTracks && videoTracks.length > 0) {
|
|
7420
|
+
// Collect qualities from ALL video tracks
|
|
7421
|
+
const allQualities = [];
|
|
7422
|
+
|
|
7423
|
+
videoTracks.forEach((track, trackIndex) => {
|
|
7424
|
+
const bitrateList = track.bitrateList || [];
|
|
7425
|
+
|
|
7426
|
+
if (this.options.debug) {
|
|
7427
|
+
console.log(`✅ Track ${trackIndex} (${track.codec}):`, bitrateList);
|
|
7428
|
+
}
|
|
7429
|
+
|
|
7430
|
+
bitrateList.forEach((bitrate, index) => {
|
|
7431
|
+
allQualities.push({
|
|
7432
|
+
trackIndex: trackIndex,
|
|
7433
|
+
bitrateIndex: index,
|
|
7434
|
+
label: `${bitrate.height}p`,
|
|
7435
|
+
height: bitrate.height,
|
|
7436
|
+
width: bitrate.width,
|
|
7437
|
+
bandwidth: bitrate.bandwidth,
|
|
7438
|
+
codec: track.codec
|
|
7439
|
+
});
|
|
7440
|
+
});
|
|
7441
|
+
});
|
|
7442
|
+
|
|
7443
|
+
// Sort by height (descending) and remove duplicates
|
|
7444
|
+
const uniqueHeights = [...new Set(allQualities.map(q => q.height))];
|
|
7445
|
+
uniqueHeights.sort((a, b) => b - a);
|
|
7446
|
+
|
|
7447
|
+
this.adaptiveQualities = uniqueHeights.map((height, index) => {
|
|
7448
|
+
const quality = allQualities.find(q => q.height === height);
|
|
7449
|
+
return {
|
|
7450
|
+
index: index,
|
|
7451
|
+
label: `${height}p`,
|
|
7452
|
+
height: height,
|
|
7453
|
+
trackIndex: quality.trackIndex,
|
|
7454
|
+
bitrateIndex: quality.bitrateIndex,
|
|
7455
|
+
bandwidth: quality.bandwidth,
|
|
7456
|
+
codec: quality.codec
|
|
7457
|
+
};
|
|
7458
|
+
});
|
|
7459
|
+
|
|
7460
|
+
if (this.options.debug) {
|
|
7461
|
+
console.log('✅ All DASH qualities merged:', this.adaptiveQualities);
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
} catch (error) {
|
|
7465
|
+
if (this.options.debug) {
|
|
7466
|
+
console.error('❌ Error getting DASH qualities:', error);
|
|
7467
|
+
}
|
|
7468
|
+
}
|
|
7469
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
7470
|
+
const levels = this.hlsPlayer.levels;
|
|
7471
|
+
this.adaptiveQualities = levels.map((level, index) => ({
|
|
7472
|
+
index: index,
|
|
7473
|
+
label: this.getQualityLabel(level.height, level.width),
|
|
7474
|
+
height: level.height,
|
|
7475
|
+
bandwidth: level.bitrate
|
|
7476
|
+
}));
|
|
7477
|
+
}
|
|
7478
|
+
|
|
7479
|
+
if (this.options.adaptiveQualityControl) {
|
|
7480
|
+
this.updateAdaptiveQualityMenu();
|
|
7481
|
+
}
|
|
7482
|
+
|
|
7483
|
+
if (this.options.debug) {
|
|
7484
|
+
console.log('📡 Adaptive qualities available:', this.adaptiveQualities);
|
|
7485
|
+
console.log('📡 Selected quality mode:', this.selectedQuality);
|
|
7486
|
+
}
|
|
7487
|
+
}
|
|
7488
|
+
|
|
7489
|
+
updateAdaptiveQualityMenu() {
|
|
7490
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
7491
|
+
if (!qualityMenu) {
|
|
7492
|
+
if (this.options.debug) console.log('❌ Quality menu not found in DOM');
|
|
7493
|
+
return;
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7496
|
+
if (this.adaptiveQualities.length === 0) {
|
|
7497
|
+
if (this.options.debug) console.log('❌ No adaptive qualities to display');
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
|
|
7501
|
+
// Generate menu HTML with "Auto" option
|
|
7502
|
+
const isAutoActive = this.selectedQuality === 'auto';
|
|
7503
|
+
let menuHTML = `<div class="quality-option ${isAutoActive ? 'active' : ''}" data-quality="auto">Auto</div>`;
|
|
7504
|
+
|
|
7505
|
+
// Add all quality options
|
|
7506
|
+
this.adaptiveQualities.forEach((quality) => {
|
|
7507
|
+
const isActive = this.selectedQuality === quality.height;
|
|
7508
|
+
|
|
7509
|
+
if (this.options.debug) {
|
|
7510
|
+
console.log('🔍 Quality item:', quality.label, 'height:', quality.height, 'active:', isActive);
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7513
|
+
menuHTML += `<div class="quality-option ${isActive ? 'active' : ''}" data-quality="${quality.height}">
|
|
7514
|
+
${quality.label}
|
|
7515
|
+
<span class="quality-playing" style="display: none; color: #4CAF50; margin-left: 8px; font-size: 0.85em;">● Playing</span>
|
|
7516
|
+
</div>`;
|
|
7517
|
+
});
|
|
7518
|
+
|
|
7519
|
+
qualityMenu.innerHTML = menuHTML;
|
|
7520
|
+
|
|
7521
|
+
if (this.options.debug) {
|
|
7522
|
+
console.log('✅ Quality menu populated with', this.adaptiveQualities.length, 'options');
|
|
7523
|
+
}
|
|
7524
|
+
|
|
7525
|
+
// Bind events ONCE
|
|
7526
|
+
if (!this.qualityEventsInitialized) {
|
|
7527
|
+
this.bindAdaptiveQualityEvents();
|
|
7528
|
+
this.qualityEventsInitialized = true;
|
|
7529
|
+
}
|
|
7530
|
+
|
|
7531
|
+
// Update display
|
|
7532
|
+
this.updateAdaptiveQualityDisplay();
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
updateAdaptiveQualityDisplay() {
|
|
7536
|
+
if (!this.dashPlayer && !this.hlsPlayer) return;
|
|
7537
|
+
|
|
7538
|
+
let currentHeight = null;
|
|
7539
|
+
|
|
7540
|
+
try {
|
|
7265
7541
|
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
7266
|
-
|
|
7267
|
-
this.
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7542
|
+
// Get video element to check actual resolution
|
|
7543
|
+
if (this.video && this.video.videoHeight) {
|
|
7544
|
+
currentHeight = this.video.videoHeight;
|
|
7545
|
+
}
|
|
7546
|
+
|
|
7547
|
+
if (this.options.debug) {
|
|
7548
|
+
console.log('📊 Current video height:', currentHeight, 'Selected mode:', this.selectedQuality);
|
|
7549
|
+
}
|
|
7273
7550
|
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
7274
|
-
const
|
|
7275
|
-
this.
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7551
|
+
const currentLevel = this.hlsPlayer.currentLevel;
|
|
7552
|
+
if (currentLevel >= 0 && this.hlsPlayer.levels[currentLevel]) {
|
|
7553
|
+
currentHeight = this.hlsPlayer.levels[currentLevel].height;
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
|
|
7557
|
+
// Update button text (top text)
|
|
7558
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7559
|
+
if (qualityBtnText) {
|
|
7560
|
+
if (this.selectedQuality === 'auto') {
|
|
7561
|
+
qualityBtnText.textContent = 'Auto';
|
|
7562
|
+
} else {
|
|
7563
|
+
qualityBtnText.textContent = `${this.selectedQuality}p`;
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
|
|
7567
|
+
// Update current quality display (bottom text) - ONLY in Auto mode
|
|
7568
|
+
const currentQualityText = this.controls?.querySelector('.quality-btn .current-quality');
|
|
7569
|
+
if (currentQualityText) {
|
|
7570
|
+
if (this.selectedQuality === 'auto' && currentHeight) {
|
|
7571
|
+
currentQualityText.textContent = `${currentHeight}p`;
|
|
7572
|
+
currentQualityText.style.display = 'block';
|
|
7573
|
+
} else {
|
|
7574
|
+
currentQualityText.textContent = '';
|
|
7575
|
+
currentQualityText.style.display = 'none';
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7579
|
+
// Update menu active states
|
|
7580
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
7581
|
+
if (qualityMenu) {
|
|
7582
|
+
// Remove all active states
|
|
7583
|
+
qualityMenu.querySelectorAll('.quality-option').forEach(opt => {
|
|
7584
|
+
opt.classList.remove('active');
|
|
7585
|
+
});
|
|
7586
|
+
|
|
7587
|
+
// Set active based on selection
|
|
7588
|
+
if (this.selectedQuality === 'auto') {
|
|
7589
|
+
const autoOption = qualityMenu.querySelector('[data-quality="auto"]');
|
|
7590
|
+
if (autoOption) autoOption.classList.add('active');
|
|
7591
|
+
} else {
|
|
7592
|
+
const selectedOption = qualityMenu.querySelector(`[data-quality="${this.selectedQuality}"]`);
|
|
7593
|
+
if (selectedOption) selectedOption.classList.add('active');
|
|
7594
|
+
}
|
|
7595
|
+
|
|
7596
|
+
// Hide all playing indicators
|
|
7597
|
+
qualityMenu.querySelectorAll('.quality-playing').forEach(el => {
|
|
7598
|
+
el.style.display = 'none';
|
|
7599
|
+
});
|
|
7600
|
+
|
|
7601
|
+
// Show playing indicator only in Auto mode
|
|
7602
|
+
if (this.selectedQuality === 'auto' && currentHeight) {
|
|
7603
|
+
const playingOption = qualityMenu.querySelector(`[data-quality="${currentHeight}"] .quality-playing`);
|
|
7604
|
+
if (playingOption) {
|
|
7605
|
+
playingOption.style.display = 'inline';
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
|
|
7610
|
+
} catch (error) {
|
|
7611
|
+
if (this.options.debug) console.error('❌ Error updating quality display:', error);
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7614
|
+
|
|
7615
|
+
updateQualityButtonText() {
|
|
7616
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7617
|
+
if (!qualityBtn) return;
|
|
7618
|
+
|
|
7619
|
+
if (this.selectedQuality === 'auto' || !this.selectedQuality) {
|
|
7620
|
+
qualityBtn.textContent = this.t('auto');
|
|
7621
|
+
} else {
|
|
7622
|
+
const quality = this.adaptiveQualities.find(q => q.index === parseInt(this.selectedQuality));
|
|
7623
|
+
qualityBtn.textContent = quality ? quality.label : 'Auto';
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7626
|
+
|
|
7627
|
+
bindAdaptiveQualityEvents() {
|
|
7628
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
7629
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
7630
|
+
|
|
7631
|
+
if (!qualityMenu || !qualityBtn) return;
|
|
7632
|
+
|
|
7633
|
+
// Toggle menu
|
|
7634
|
+
qualityBtn.addEventListener('click', (e) => {
|
|
7635
|
+
e.stopPropagation();
|
|
7636
|
+
qualityMenu.classList.toggle('active');
|
|
7637
|
+
|
|
7638
|
+
// Update display when opening
|
|
7639
|
+
if (qualityMenu.classList.contains('active')) {
|
|
7640
|
+
this.updateAdaptiveQualityDisplay();
|
|
7281
7641
|
}
|
|
7642
|
+
});
|
|
7282
7643
|
|
|
7283
|
-
|
|
7284
|
-
|
|
7644
|
+
// Close menu on outside click
|
|
7645
|
+
const closeMenuHandler = (e) => {
|
|
7646
|
+
if (!qualityBtn.contains(e.target) && !qualityMenu.contains(e.target)) {
|
|
7647
|
+
qualityMenu.classList.remove('active');
|
|
7285
7648
|
}
|
|
7649
|
+
};
|
|
7650
|
+
document.addEventListener('click', closeMenuHandler);
|
|
7651
|
+
|
|
7652
|
+
// Handle quality selection
|
|
7653
|
+
qualityMenu.addEventListener('click', (e) => {
|
|
7654
|
+
const option = e.target.closest('.quality-option');
|
|
7655
|
+
if (!option) return;
|
|
7656
|
+
|
|
7657
|
+
e.stopPropagation();
|
|
7658
|
+
|
|
7659
|
+
const qualityData = option.getAttribute('data-quality');
|
|
7286
7660
|
|
|
7287
7661
|
if (this.options.debug) {
|
|
7288
|
-
console.log('
|
|
7662
|
+
console.log('🎬 Quality clicked - raw data:', qualityData, 'type:', typeof qualityData);
|
|
7663
|
+
}
|
|
7664
|
+
|
|
7665
|
+
if (qualityData === 'auto') {
|
|
7666
|
+
// Enable auto mode
|
|
7667
|
+
this.selectedQuality = 'auto';
|
|
7668
|
+
|
|
7669
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
7670
|
+
this.dashPlayer.updateSettings({
|
|
7671
|
+
streaming: {
|
|
7672
|
+
abr: {
|
|
7673
|
+
autoSwitchBitrate: { video: true }
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
});
|
|
7677
|
+
if (this.options.debug) console.log('✅ Auto quality enabled');
|
|
7678
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
7679
|
+
this.hlsPlayer.currentLevel = -1;
|
|
7680
|
+
}
|
|
7681
|
+
|
|
7682
|
+
} else {
|
|
7683
|
+
// Manual quality selection
|
|
7684
|
+
const selectedHeight = parseInt(qualityData, 10);
|
|
7685
|
+
|
|
7686
|
+
if (isNaN(selectedHeight)) {
|
|
7687
|
+
if (this.options.debug) console.error('❌ Invalid quality data:', qualityData);
|
|
7688
|
+
return;
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
if (this.options.debug) {
|
|
7692
|
+
console.log('🎬 Setting manual quality to height:', selectedHeight);
|
|
7693
|
+
}
|
|
7694
|
+
|
|
7695
|
+
this.selectedQuality = selectedHeight;
|
|
7696
|
+
|
|
7697
|
+
if (this.adaptiveStreamingType === 'dash') {
|
|
7698
|
+
this.setDashQualityByHeight(selectedHeight);
|
|
7699
|
+
} else if (this.adaptiveStreamingType === 'hls') {
|
|
7700
|
+
const levelIndex = this.hlsPlayer.levels.findIndex(l => l.height === selectedHeight);
|
|
7701
|
+
if (levelIndex >= 0) {
|
|
7702
|
+
this.hlsPlayer.currentLevel = levelIndex;
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7289
7705
|
}
|
|
7706
|
+
|
|
7707
|
+
// Update display immediately
|
|
7708
|
+
this.updateAdaptiveQualityDisplay();
|
|
7709
|
+
|
|
7710
|
+
// Close menu
|
|
7711
|
+
qualityMenu.classList.remove('active');
|
|
7712
|
+
});
|
|
7713
|
+
|
|
7714
|
+
if (this.options.debug) {
|
|
7715
|
+
console.log('✅ Quality events bound');
|
|
7290
7716
|
}
|
|
7717
|
+
}
|
|
7718
|
+
|
|
7719
|
+
setDashQualityByHeight(targetHeight) {
|
|
7720
|
+
if (!this.dashPlayer) return;
|
|
7721
|
+
|
|
7722
|
+
try {
|
|
7723
|
+
const targetQuality = this.adaptiveQualities.find(q => q.height === targetHeight);
|
|
7724
|
+
if (!targetQuality) {
|
|
7725
|
+
if (this.options.debug) console.error('❌ Quality not found for height:', targetHeight);
|
|
7726
|
+
return;
|
|
7727
|
+
}
|
|
7728
|
+
|
|
7729
|
+
if (this.options.debug) {
|
|
7730
|
+
console.log('🎬 Setting quality:', targetQuality);
|
|
7731
|
+
}
|
|
7732
|
+
|
|
7733
|
+
// Disable auto quality
|
|
7734
|
+
this.dashPlayer.updateSettings({
|
|
7735
|
+
streaming: {
|
|
7736
|
+
abr: {
|
|
7737
|
+
autoSwitchBitrate: { video: false }
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7740
|
+
});
|
|
7741
|
+
|
|
7742
|
+
// Get current video track
|
|
7743
|
+
const currentTrack = this.dashPlayer.getCurrentTrackFor('video');
|
|
7744
|
+
|
|
7745
|
+
if (!currentTrack) {
|
|
7746
|
+
if (this.options.debug) console.error('❌ No current video track');
|
|
7747
|
+
return;
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
// Find the correct track for this quality
|
|
7751
|
+
const allTracks = this.dashPlayer.getTracksFor('video');
|
|
7752
|
+
let targetTrack = null;
|
|
7753
|
+
|
|
7754
|
+
for (const track of allTracks) {
|
|
7755
|
+
if (track.bitrateList && track.bitrateList[targetQuality.bitrateIndex]) {
|
|
7756
|
+
const bitrate = track.bitrateList[targetQuality.bitrateIndex];
|
|
7757
|
+
if (bitrate.height === targetHeight) {
|
|
7758
|
+
targetTrack = track;
|
|
7759
|
+
break;
|
|
7760
|
+
}
|
|
7761
|
+
}
|
|
7762
|
+
}
|
|
7763
|
+
|
|
7764
|
+
if (!targetTrack) {
|
|
7765
|
+
if (this.options.debug) console.error('❌ Target track not found');
|
|
7766
|
+
return;
|
|
7767
|
+
}
|
|
7768
|
+
|
|
7769
|
+
// Switch track if different
|
|
7770
|
+
if (currentTrack.index !== targetTrack.index) {
|
|
7771
|
+
this.dashPlayer.setCurrentTrack(targetTrack);
|
|
7772
|
+
if (this.options.debug) {
|
|
7773
|
+
console.log('✅ Switched to track:', targetTrack.index);
|
|
7774
|
+
}
|
|
7775
|
+
}
|
|
7776
|
+
|
|
7777
|
+
// Force quality on current track
|
|
7778
|
+
setTimeout(() => {
|
|
7779
|
+
try {
|
|
7780
|
+
// Use the MediaPlayer API to set quality
|
|
7781
|
+
this.dashPlayer.updateSettings({
|
|
7782
|
+
streaming: {
|
|
7783
|
+
abr: {
|
|
7784
|
+
initialBitrate: { video: targetQuality.bandwidth / 1000 },
|
|
7785
|
+
maxBitrate: { video: targetQuality.bandwidth / 1000 },
|
|
7786
|
+
minBitrate: { video: targetQuality.bandwidth / 1000 }
|
|
7787
|
+
}
|
|
7788
|
+
}
|
|
7789
|
+
});
|
|
7790
|
+
|
|
7791
|
+
if (this.options.debug) {
|
|
7792
|
+
console.log('✅ Quality locked to:', targetHeight + 'p', 'bandwidth:', targetQuality.bandwidth);
|
|
7793
|
+
}
|
|
7794
|
+
|
|
7795
|
+
// Update button text immediately
|
|
7796
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7797
|
+
if (qualityBtnText) {
|
|
7798
|
+
qualityBtnText.textContent = `${targetHeight}p`;
|
|
7799
|
+
}
|
|
7800
|
+
|
|
7801
|
+
// Force reload of segments at new quality
|
|
7802
|
+
const currentTime = this.video.currentTime;
|
|
7803
|
+
this.dashPlayer.seek(currentTime + 0.1);
|
|
7804
|
+
setTimeout(() => {
|
|
7805
|
+
this.dashPlayer.seek(currentTime);
|
|
7806
|
+
}, 100);
|
|
7807
|
+
|
|
7808
|
+
} catch (innerError) {
|
|
7809
|
+
if (this.options.debug) console.error('❌ Error setting quality:', innerError);
|
|
7810
|
+
}
|
|
7811
|
+
}, 100);
|
|
7812
|
+
|
|
7813
|
+
} catch (error) {
|
|
7814
|
+
if (this.options.debug) console.error('❌ Error in setDashQualityByHeight:', error);
|
|
7815
|
+
}
|
|
7816
|
+
}
|
|
7817
|
+
|
|
7818
|
+
setDashQuality(qualityIndex) {
|
|
7819
|
+
if (!this.dashPlayer) return;
|
|
7820
|
+
|
|
7821
|
+
try {
|
|
7822
|
+
const selectedQuality = this.adaptiveQualities[qualityIndex];
|
|
7823
|
+
if (!selectedQuality) {
|
|
7824
|
+
if (this.options.debug) console.error('❌ Quality not found at index:', qualityIndex);
|
|
7825
|
+
return;
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
if (this.options.debug) {
|
|
7829
|
+
console.log('🎬 Setting DASH quality:', selectedQuality);
|
|
7830
|
+
}
|
|
7831
|
+
|
|
7832
|
+
// Disable auto quality
|
|
7833
|
+
this.dashPlayer.updateSettings({
|
|
7834
|
+
streaming: {
|
|
7835
|
+
abr: {
|
|
7836
|
+
autoSwitchBitrate: { video: false }
|
|
7837
|
+
}
|
|
7838
|
+
}
|
|
7839
|
+
});
|
|
7840
|
+
|
|
7841
|
+
// Set the specific quality using bitrateIndex
|
|
7842
|
+
setTimeout(() => {
|
|
7843
|
+
try {
|
|
7844
|
+
this.dashPlayer.setQualityFor('video', selectedQuality.bitrateIndex);
|
|
7845
|
+
|
|
7846
|
+
if (this.options.debug) {
|
|
7847
|
+
console.log('✅ DASH quality set to bitrateIndex:', selectedQuality.bitrateIndex, 'height:', selectedQuality.height);
|
|
7848
|
+
}
|
|
7849
|
+
|
|
7850
|
+
// Update button text immediately
|
|
7851
|
+
const qualityBtnText = this.controls?.querySelector('.quality-btn .selected-quality');
|
|
7852
|
+
if (qualityBtnText) {
|
|
7853
|
+
qualityBtnText.textContent = selectedQuality.label;
|
|
7854
|
+
}
|
|
7855
|
+
|
|
7856
|
+
} catch (innerError) {
|
|
7857
|
+
if (this.options.debug) console.error('❌ Error setting quality:', innerError);
|
|
7858
|
+
}
|
|
7859
|
+
}, 100);
|
|
7860
|
+
|
|
7861
|
+
} catch (error) {
|
|
7862
|
+
if (this.options.debug) console.error('❌ Error in setDashQuality:', error);
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7291
7865
|
|
|
7292
7866
|
handleAdaptiveError(data) {
|
|
7293
7867
|
if (this.options.debug) console.error('📡 Fatal adaptive streaming error:', data);
|
|
@@ -7721,63 +8295,79 @@ removePluginControlButton(buttonId) {
|
|
|
7721
8295
|
}
|
|
7722
8296
|
|
|
7723
8297
|
getBufferedTime() {
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
}
|
|
8298
|
+
if (!this.video || !this.video.buffered || this.video.buffered.length === 0) return 0;
|
|
8299
|
+
try {
|
|
8300
|
+
return this.video.buffered.end(this.video.buffered.length - 1);
|
|
8301
|
+
} catch (error) {
|
|
8302
|
+
return 0;
|
|
7730
8303
|
}
|
|
8304
|
+
}
|
|
7731
8305
|
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
}
|
|
8306
|
+
clearTitleTimeout() {
|
|
8307
|
+
if (this.titleTimeout) {
|
|
8308
|
+
clearTimeout(this.titleTimeout);
|
|
8309
|
+
this.titleTimeout = null;
|
|
7737
8310
|
}
|
|
8311
|
+
}
|
|
7738
8312
|
|
|
7739
|
-
|
|
7740
|
-
|
|
8313
|
+
skipTime(seconds) {
|
|
8314
|
+
if (!this.video || !this.video.duration || this.isChangingQuality) return;
|
|
7741
8315
|
|
|
7742
|
-
|
|
7743
|
-
|
|
8316
|
+
this.video.currentTime = Math.max(0, Math.min(this.video.duration, this.video.currentTime + seconds));
|
|
8317
|
+
}
|
|
7744
8318
|
|
|
7745
8319
|
updateTimeDisplay() {
|
|
7746
|
-
//
|
|
8320
|
+
// Update current time
|
|
7747
8321
|
if (this.currentTimeEl && this.video) {
|
|
7748
8322
|
this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
|
|
7749
8323
|
}
|
|
7750
8324
|
|
|
7751
|
-
//
|
|
8325
|
+
// Update duration or show appropriate message
|
|
7752
8326
|
if (this.durationEl && this.video) {
|
|
7753
8327
|
const duration = this.video.duration;
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
8328
|
+
const readyState = this.video.readyState;
|
|
8329
|
+
const currentTime = this.video.currentTime;
|
|
8330
|
+
const networkState = this.video.networkState;
|
|
8331
|
+
|
|
8332
|
+
// Check for initial buffering state
|
|
8333
|
+
// readyState < 2 means not enough data to play (HAVE_NOTHING or HAVE_METADATA)
|
|
8334
|
+
// currentTime === 0 and duration === 0 indicates initial loading
|
|
8335
|
+
const isInitialBuffering = (readyState < 2 && currentTime === 0) ||
|
|
8336
|
+
(currentTime === 0 && (!duration || duration === 0) && networkState === 2);
|
|
8337
|
+
|
|
8338
|
+
// Check if duration is invalid (NaN or Infinity)
|
|
8339
|
+
const isDurationInvalid = !duration || isNaN(duration) || !isFinite(duration);
|
|
8340
|
+
|
|
8341
|
+
if (isInitialBuffering) {
|
|
8342
|
+
// Initial buffering - show loading message
|
|
8343
|
+
this.durationEl.textContent = t('loading');
|
|
8344
|
+
this.durationEl.classList.remove('encoding-state');
|
|
8345
|
+
this.durationEl.classList.add('loading-state');
|
|
8346
|
+
} else if (isDurationInvalid) {
|
|
8347
|
+
// Video is encoding (FFmpeg still processing) - show encoding badge
|
|
8348
|
+
this.durationEl.textContent = t('encoding_in_progress');
|
|
8349
|
+
this.durationEl.classList.remove('loading-state');
|
|
7759
8350
|
this.durationEl.classList.add('encoding-state');
|
|
7760
8351
|
} else {
|
|
7761
|
-
//
|
|
8352
|
+
// Valid duration - show normal time
|
|
7762
8353
|
this.durationEl.textContent = this.formatTime(duration);
|
|
7763
|
-
this.durationEl.classList.remove('encoding-state');
|
|
8354
|
+
this.durationEl.classList.remove('encoding-state', 'loading-state');
|
|
7764
8355
|
}
|
|
7765
8356
|
}
|
|
7766
8357
|
}
|
|
7767
8358
|
|
|
8359
|
+
formatTime(seconds) {
|
|
8360
|
+
if (isNaN(seconds) || seconds < 0) return '0:00';
|
|
7768
8361
|
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
const hours = Math.floor(seconds / 3600);
|
|
7773
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
7774
|
-
const secs = Math.floor(seconds % 60);
|
|
8362
|
+
const hours = Math.floor(seconds / 3600);
|
|
8363
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
8364
|
+
const secs = Math.floor(seconds % 60);
|
|
7775
8365
|
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
}
|
|
7779
|
-
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
8366
|
+
if (hours > 0) {
|
|
8367
|
+
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
7780
8368
|
}
|
|
8369
|
+
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
8370
|
+
}
|
|
7781
8371
|
|
|
7782
8372
|
}
|
|
7783
8373
|
|