myetv-player 1.4.0 → 1.5.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 +1 -1
- package/css/myetv-player.css +7 -2
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +200 -59
- package/dist/myetv-player.min.js +152 -34
- package/package.json +2 -1
- 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 +83 -125
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
|
|
@@ -7721,63 +7846,79 @@ removePluginControlButton(buttonId) {
|
|
|
7721
7846
|
}
|
|
7722
7847
|
|
|
7723
7848
|
getBufferedTime() {
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
}
|
|
7849
|
+
if (!this.video || !this.video.buffered || this.video.buffered.length === 0) return 0;
|
|
7850
|
+
try {
|
|
7851
|
+
return this.video.buffered.end(this.video.buffered.length - 1);
|
|
7852
|
+
} catch (error) {
|
|
7853
|
+
return 0;
|
|
7730
7854
|
}
|
|
7855
|
+
}
|
|
7731
7856
|
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
}
|
|
7857
|
+
clearTitleTimeout() {
|
|
7858
|
+
if (this.titleTimeout) {
|
|
7859
|
+
clearTimeout(this.titleTimeout);
|
|
7860
|
+
this.titleTimeout = null;
|
|
7737
7861
|
}
|
|
7862
|
+
}
|
|
7738
7863
|
|
|
7739
|
-
|
|
7740
|
-
|
|
7864
|
+
skipTime(seconds) {
|
|
7865
|
+
if (!this.video || !this.video.duration || this.isChangingQuality) return;
|
|
7741
7866
|
|
|
7742
|
-
|
|
7743
|
-
|
|
7867
|
+
this.video.currentTime = Math.max(0, Math.min(this.video.duration, this.video.currentTime + seconds));
|
|
7868
|
+
}
|
|
7744
7869
|
|
|
7745
7870
|
updateTimeDisplay() {
|
|
7746
|
-
//
|
|
7871
|
+
// Update current time
|
|
7747
7872
|
if (this.currentTimeEl && this.video) {
|
|
7748
7873
|
this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
|
|
7749
7874
|
}
|
|
7750
7875
|
|
|
7751
|
-
//
|
|
7876
|
+
// Update duration or show appropriate message
|
|
7752
7877
|
if (this.durationEl && this.video) {
|
|
7753
7878
|
const duration = this.video.duration;
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7879
|
+
const readyState = this.video.readyState;
|
|
7880
|
+
const currentTime = this.video.currentTime;
|
|
7881
|
+
const networkState = this.video.networkState;
|
|
7882
|
+
|
|
7883
|
+
// Check for initial buffering state
|
|
7884
|
+
// readyState < 2 means not enough data to play (HAVE_NOTHING or HAVE_METADATA)
|
|
7885
|
+
// currentTime === 0 and duration === 0 indicates initial loading
|
|
7886
|
+
const isInitialBuffering = (readyState < 2 && currentTime === 0) ||
|
|
7887
|
+
(currentTime === 0 && (!duration || duration === 0) && networkState === 2);
|
|
7888
|
+
|
|
7889
|
+
// Check if duration is invalid (NaN or Infinity)
|
|
7890
|
+
const isDurationInvalid = !duration || isNaN(duration) || !isFinite(duration);
|
|
7891
|
+
|
|
7892
|
+
if (isInitialBuffering) {
|
|
7893
|
+
// Initial buffering - show loading message
|
|
7894
|
+
this.durationEl.textContent = t('loading');
|
|
7895
|
+
this.durationEl.classList.remove('encoding-state');
|
|
7896
|
+
this.durationEl.classList.add('loading-state');
|
|
7897
|
+
} else if (isDurationInvalid) {
|
|
7898
|
+
// Video is encoding (FFmpeg still processing) - show encoding badge
|
|
7899
|
+
this.durationEl.textContent = t('encoding_in_progress');
|
|
7900
|
+
this.durationEl.classList.remove('loading-state');
|
|
7759
7901
|
this.durationEl.classList.add('encoding-state');
|
|
7760
7902
|
} else {
|
|
7761
|
-
//
|
|
7903
|
+
// Valid duration - show normal time
|
|
7762
7904
|
this.durationEl.textContent = this.formatTime(duration);
|
|
7763
|
-
this.durationEl.classList.remove('encoding-state');
|
|
7905
|
+
this.durationEl.classList.remove('encoding-state', 'loading-state');
|
|
7764
7906
|
}
|
|
7765
7907
|
}
|
|
7766
7908
|
}
|
|
7767
7909
|
|
|
7910
|
+
formatTime(seconds) {
|
|
7911
|
+
if (isNaN(seconds) || seconds < 0) return '0:00';
|
|
7768
7912
|
|
|
7769
|
-
|
|
7770
|
-
|
|
7913
|
+
const hours = Math.floor(seconds / 3600);
|
|
7914
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
7915
|
+
const secs = Math.floor(seconds % 60);
|
|
7771
7916
|
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
const secs = Math.floor(seconds % 60);
|
|
7775
|
-
|
|
7776
|
-
if (hours > 0) {
|
|
7777
|
-
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
7778
|
-
}
|
|
7779
|
-
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
7917
|
+
if (hours > 0) {
|
|
7918
|
+
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
7780
7919
|
}
|
|
7920
|
+
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
7921
|
+
}
|
|
7781
7922
|
|
|
7782
7923
|
}
|
|
7783
7924
|
|