myetv-player 1.1.6 → 1.3.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.
Files changed (47) hide show
  1. package/css/myetv-player.css +153 -0
  2. package/css/myetv-player.min.css +1 -1
  3. package/dist/myetv-player.js +654 -129
  4. package/dist/myetv-player.min.js +579 -115
  5. package/package.json +35 -16
  6. package/plugins/twitch/myetv-player-twitch-plugin.js +125 -11
  7. package/plugins/vimeo/myetv-player-vimeo.js +80 -49
  8. package/plugins/youtube/README.md +5 -2
  9. package/plugins/youtube/myetv-player-youtube-plugin.js +891 -14
  10. package/.github/workflows/codeql.yml +0 -100
  11. package/.github/workflows/npm-publish.yml +0 -30
  12. package/SECURITY.md +0 -50
  13. package/build.js +0 -195
  14. package/scss/README.md +0 -161
  15. package/scss/_audio-player.scss +0 -21
  16. package/scss/_base.scss +0 -116
  17. package/scss/_controls.scss +0 -188
  18. package/scss/_loading.scss +0 -111
  19. package/scss/_menus.scss +0 -432
  20. package/scss/_mixins.scss +0 -112
  21. package/scss/_poster.scss +0 -8
  22. package/scss/_progress-bar.scss +0 -319
  23. package/scss/_resolution.scss +0 -68
  24. package/scss/_responsive.scss +0 -1360
  25. package/scss/_themes.scss +0 -30
  26. package/scss/_title-overlay.scss +0 -60
  27. package/scss/_tooltips.scss +0 -7
  28. package/scss/_variables.scss +0 -49
  29. package/scss/_video.scss +0 -221
  30. package/scss/_volume.scss +0 -122
  31. package/scss/_watermark.scss +0 -128
  32. package/scss/myetv-player.scss +0 -51
  33. package/scss/package.json +0 -16
  34. package/src/README.md +0 -560
  35. package/src/chapters.js +0 -521
  36. package/src/controls.js +0 -1243
  37. package/src/core.js +0 -1922
  38. package/src/events.js +0 -456
  39. package/src/fullscreen.js +0 -82
  40. package/src/i18n.js +0 -374
  41. package/src/playlist.js +0 -177
  42. package/src/plugins.js +0 -384
  43. package/src/quality.js +0 -963
  44. package/src/streaming.js +0 -346
  45. package/src/subtitles.js +0 -524
  46. package/src/utils.js +0 -65
  47. package/src/watermark.js +0 -246
@@ -9,14 +9,15 @@ class VideoPlayerI18n {
9
9
  // Italiano
10
10
  'it': {
11
11
  'subtitles': 'Sottotitoli (C)',
12
- 'subtitlesdisable': 'Disabilita Sottotitoli',
13
- 'subtitlesenable': 'Abilita Sottotitoli',
12
+ 'subtitlesoff': 'Disattivati',
13
+ 'subtitlesdisable': 'Disabilita sottotitoli',
14
+ 'subtitlesenable': 'Abilita sottotitoli',
14
15
  'play_pause': 'Play/Pausa (Spazio)',
15
16
  'mute_unmute': 'Muta/Smuta (M)',
16
17
  'volume': 'Volume',
17
18
  'playback_speed': 'Velocità riproduzione',
18
19
  'video_quality': 'Qualità video',
19
- 'picture_in_picture': 'Picture-in-Picture (P)',
20
+ 'picture_in_picture': 'Finestra sovrapposta (P)',
20
21
  'fullscreen': 'Schermo intero (F)',
21
22
  'auto': 'Auto',
22
23
  'brand_logo': 'Logo',
@@ -30,6 +31,7 @@ class VideoPlayerI18n {
30
31
  // English
31
32
  'en': {
32
33
  'subtitles': 'Subtitles (C)',
34
+ 'subtitlesoff': 'Off',
33
35
  'subtitlesdisable': 'Disable Subtitles',
34
36
  'subtitlesenable': 'Enable Subtitles',
35
37
  'play_pause': 'Play/Pause (Space)',
@@ -45,24 +47,21 @@ class VideoPlayerI18n {
45
47
  'prev_video': 'Previous video (P)',
46
48
  'playlist_next': 'Next',
47
49
  'playlist_prev': 'Previous',
48
- 'next_video': 'Next video (N)',
49
- 'prev_video': 'Previous video (P)',
50
- 'playlist_next': 'Next',
51
- 'playlist_prev': 'Previous',
52
50
  'settings_menu': 'Settings'
53
51
  },
54
52
 
55
53
  // Español
56
54
  'es': {
57
55
  'subtitles': 'Subtítulos (C)',
58
- 'subtitlesdisable': 'Disable Subtitles',
59
- 'subtitlesenable': 'Enable Subtitles',
56
+ 'subtitlesoff': 'Desactivados',
57
+ 'subtitlesdisable': 'Desactivar subtítulos',
58
+ 'subtitlesenable': 'Activar subtítulos',
60
59
  'play_pause': 'Reproducir/Pausar (Espacio)',
61
60
  'mute_unmute': 'Silenciar (M)',
62
61
  'volume': 'Volumen',
63
62
  'playback_speed': 'Velocidad de reproducción',
64
63
  'video_quality': 'Calidad de vídeo',
65
- 'picture_in_picture': 'Picture-in-Picture (P)',
64
+ 'picture_in_picture': 'Imagen en imagen (P)',
66
65
  'fullscreen': 'Pantalla completa (F)',
67
66
  'auto': 'Auto',
68
67
  'brand_logo': 'Logo de marca',
@@ -70,20 +69,21 @@ class VideoPlayerI18n {
70
69
  'prev_video': 'Vídeo anterior (P)',
71
70
  'playlist_next': 'Siguiente',
72
71
  'playlist_prev': 'Anterior',
73
- 'settings_menu': 'Settings'
72
+ 'settings_menu': 'Configuración'
74
73
  },
75
74
 
76
75
  // Français
77
76
  'fr': {
78
77
  'subtitles': 'Sous-titres (C)',
79
- 'subtitlesdisable': 'Disable Subtitles',
80
- 'subtitlesenable': 'Enable Subtitles',
78
+ 'subtitlesoff': 'Désactivés',
79
+ 'subtitlesdisable': 'Désactiver les sous-titres',
80
+ 'subtitlesenable': 'Activer les sous-titres',
81
81
  'play_pause': 'Lecture/Pause (Espace)',
82
82
  'mute_unmute': 'Muet (M)',
83
83
  'volume': 'Volume',
84
84
  'playback_speed': 'Vitesse de lecture',
85
85
  'video_quality': 'Qualité vidéo',
86
- 'picture_in_picture': 'Picture-in-Picture (P)',
86
+ 'picture_in_picture': 'Image dans l’image (P)',
87
87
  'fullscreen': 'Plein écran (F)',
88
88
  'auto': 'Auto',
89
89
  'brand_logo': 'Logo de marque',
@@ -91,20 +91,21 @@ class VideoPlayerI18n {
91
91
  'prev_video': 'Vidéo précédente (P)',
92
92
  'playlist_next': 'Suivant',
93
93
  'playlist_prev': 'Précédent',
94
- 'settings_menu': 'Settings'
94
+ 'settings_menu': 'Paramètres'
95
95
  },
96
96
 
97
97
  // Deutsch
98
98
  'de': {
99
99
  'subtitles': 'Untertitel (C)',
100
- 'subtitlesdisable': 'Disable Subtitles',
101
- 'subtitlesenable': 'Enable Subtitles',
100
+ 'subtitlesoff': 'Aus',
101
+ 'subtitlesdisable': 'Untertitel deaktivieren',
102
+ 'subtitlesenable': 'Untertitel aktivieren',
102
103
  'play_pause': 'Abspielen/Pausieren (Leertaste)',
103
104
  'mute_unmute': 'Stumm (M)',
104
105
  'volume': 'Lautstärke',
105
106
  'playback_speed': 'Wiedergabegeschwindigkeit',
106
107
  'video_quality': 'Videoqualität',
107
- 'picture_in_picture': 'Picture-in-Picture (P)',
108
+ 'picture_in_picture': 'Bild-in-Bild (P)',
108
109
  'fullscreen': 'Vollbild (F)',
109
110
  'auto': 'Auto',
110
111
  'brand_logo': 'Markenlogo',
@@ -112,20 +113,21 @@ class VideoPlayerI18n {
112
113
  'prev_video': 'Vorheriges Video (P)',
113
114
  'playlist_next': 'Weiter',
114
115
  'playlist_prev': 'Zurück',
115
- 'settings_menu': 'Settings'
116
+ 'settings_menu': 'Einstellungen'
116
117
  },
117
118
 
118
119
  // Português
119
120
  'pt': {
120
121
  'subtitles': 'Legendas (C)',
121
- 'subtitlesdisable': 'Disable Subtitles',
122
- 'subtitlesenable': 'Enable Subtitles',
122
+ 'subtitlesoff': 'Desativadas',
123
+ 'subtitlesdisable': 'Desativar legendas',
124
+ 'subtitlesenable': 'Ativar legendas',
123
125
  'play_pause': 'Reproduzir/Pausar (Espaço)',
124
126
  'mute_unmute': 'Silenciar (M)',
125
127
  'volume': 'Volume',
126
128
  'playback_speed': 'Velocidade de reprodução',
127
129
  'video_quality': 'Qualidade do vídeo',
128
- 'picture_in_picture': 'Picture-in-Picture (P)',
130
+ 'picture_in_picture': 'Imagem em imagem (P)',
129
131
  'fullscreen': 'Tela cheia (F)',
130
132
  'auto': 'Auto',
131
133
  'brand_logo': 'Logo da marca',
@@ -133,14 +135,15 @@ class VideoPlayerI18n {
133
135
  'prev_video': 'Vídeo anterior (P)',
134
136
  'playlist_next': 'Próximo',
135
137
  'playlist_prev': 'Anterior',
136
- 'settings_menu': 'Settings'
138
+ 'settings_menu': 'Configurações'
137
139
  },
138
140
 
139
141
  // 中文
140
142
  'zh': {
141
143
  'subtitles': '字幕 (C)',
142
- 'subtitlesdisable': 'Disable Subtitles',
143
- 'subtitlesenable': 'Enable Subtitles',
144
+ 'subtitlesoff': '关闭',
145
+ 'subtitlesdisable': '禁用字幕',
146
+ 'subtitlesenable': '启用字幕',
144
147
  'play_pause': '播放/暂停 (空格)',
145
148
  'mute_unmute': '静音 (M)',
146
149
  'volume': '音量',
@@ -154,14 +157,15 @@ class VideoPlayerI18n {
154
157
  'prev_video': '上一个视频 (P)',
155
158
  'playlist_next': '下一个',
156
159
  'playlist_prev': '上一个',
157
- 'settings_menu': 'Settings'
160
+ 'settings_menu': '设置'
158
161
  },
159
162
 
160
163
  // 日本語
161
164
  'ja': {
162
165
  'subtitles': '字幕 (C)',
163
- 'subtitlesdisable': 'Disable Subtitles',
164
- 'subtitlesenable': 'Enable Subtitles',
166
+ 'subtitlesoff': 'オフ',
167
+ 'subtitlesdisable': '字幕を無効にする',
168
+ 'subtitlesenable': '字幕を有効にする',
165
169
  'play_pause': '再生/一時停止 (スペース)',
166
170
  'mute_unmute': 'ミュート (M)',
167
171
  'volume': '音量',
@@ -175,14 +179,15 @@ class VideoPlayerI18n {
175
179
  'prev_video': '前の動画 (P)',
176
180
  'playlist_next': '次へ',
177
181
  'playlist_prev': '前へ',
178
- 'settings_menu': 'Settings'
182
+ 'settings_menu': '設定'
179
183
  },
180
184
 
181
185
  // Русский
182
186
  'ru': {
183
187
  'subtitles': 'Субтитры (C)',
184
- 'subtitlesdisable': 'Disable Subtitles',
185
- 'subtitlesenable': 'Enable Subtitles',
188
+ 'subtitlesoff': 'Выкл',
189
+ 'subtitlesdisable': 'Отключить субтитры',
190
+ 'subtitlesenable': 'Включить субтитры',
186
191
  'play_pause': 'Воспроизведение/Пауза (Пробел)',
187
192
  'mute_unmute': 'Звук (M)',
188
193
  'volume': 'Громкость',
@@ -196,14 +201,15 @@ class VideoPlayerI18n {
196
201
  'prev_video': 'Предыдущее видео (P)',
197
202
  'playlist_next': 'Далее',
198
203
  'playlist_prev': 'Назад',
199
- 'settings_menu': 'Settings'
204
+ 'settings_menu': 'Настройки'
200
205
  },
201
206
 
202
207
  // العربية
203
208
  'ar': {
204
209
  'subtitles': 'الترجمة (C)',
205
- 'subtitlesdisable': 'Disable Subtitles',
206
- 'subtitlesenable': 'Enable Subtitles',
210
+ 'subtitlesoff': 'إيقاف',
211
+ 'subtitlesdisable': 'تعطيل الترجمة',
212
+ 'subtitlesenable': 'تفعيل الترجمة',
207
213
  'play_pause': 'تشغيل/إيقاف مؤقت (مسافة)',
208
214
  'mute_unmute': 'كتم الصوت (M)',
209
215
  'volume': 'مستوى الصوت',
@@ -217,10 +223,187 @@ class VideoPlayerI18n {
217
223
  'prev_video': 'الفيديو السابق (P)',
218
224
  'playlist_next': 'التالي',
219
225
  'playlist_prev': 'السابق',
220
- 'settings_menu': 'Settings'
226
+ 'settings_menu': 'الإعدادات'
227
+ },
228
+
229
+ // 한국어 (Korean)
230
+ 'ko': {
231
+ 'subtitles': '자막 (C)',
232
+ 'subtitlesoff': '끄기',
233
+ 'subtitlesdisable': '자막 비활성화',
234
+ 'subtitlesenable': '자막 활성화',
235
+ 'play_pause': '재생/일시정지 (스페이스)',
236
+ 'mute_unmute': '음소거 (M)',
237
+ 'volume': '음량',
238
+ 'playback_speed': '재생 속도',
239
+ 'video_quality': '비디오 품질',
240
+ 'picture_in_picture': '화면 속 화면 (P)',
241
+ 'fullscreen': '전체 화면 (F)',
242
+ 'auto': '자동',
243
+ 'brand_logo': '브랜드 로고',
244
+ 'next_video': '다음 비디오 (N)',
245
+ 'prev_video': '이전 비디오 (P)',
246
+ 'playlist_next': '다음',
247
+ 'playlist_prev': '이전',
248
+ 'settings_menu': '설정'
249
+ },
250
+
251
+ // Polski
252
+ 'pl': {
253
+ 'subtitles': 'Napisy (C)',
254
+ 'subtitlesoff': 'Wyłączone',
255
+ 'subtitlesdisable': 'Wyłącz napisy',
256
+ 'subtitlesenable': 'Włącz napisy',
257
+ 'play_pause': 'Odtwarzaj/Pauza (Spacja)',
258
+ 'mute_unmute': 'Wycisz (M)',
259
+ 'volume': 'Głośność',
260
+ 'playback_speed': 'Prędkość odtwarzania',
261
+ 'video_quality': 'Jakość wideo',
262
+ 'picture_in_picture': 'Obraz w obrazie (P)',
263
+ 'fullscreen': 'Pełny ekran (F)',
264
+ 'auto': 'Auto',
265
+ 'brand_logo': 'Logo marki',
266
+ 'next_video': 'Następne wideo (N)',
267
+ 'prev_video': 'Poprzednie wideo (P)',
268
+ 'playlist_next': 'Dalej',
269
+ 'playlist_prev': 'Wstecz',
270
+ 'settings_menu': 'Ustawienia'
271
+ },
272
+
273
+ // Magyar
274
+ 'hu': {
275
+ 'subtitles': 'Feliratok (C)',
276
+ 'subtitlesoff': 'Kikapcsolva',
277
+ 'subtitlesdisable': 'Feliratok kikapcsolása',
278
+ 'subtitlesenable': 'Feliratok bekapcsolása',
279
+ 'play_pause': 'Lejátszás/Szünet (Szóköz)',
280
+ 'mute_unmute': 'Némítás (M)',
281
+ 'volume': 'Hangerő',
282
+ 'playback_speed': 'Lejátszási sebesség',
283
+ 'video_quality': 'Videó minősége',
284
+ 'picture_in_picture': 'Kép a képben (P)',
285
+ 'fullscreen': 'Teljes képernyő (F)',
286
+ 'auto': 'Auto',
287
+ 'brand_logo': 'Márka logó',
288
+ 'next_video': 'Következő videó (N)',
289
+ 'prev_video': 'Előző videó (P)',
290
+ 'playlist_next': 'Következő',
291
+ 'playlist_prev': 'Előző',
292
+ 'settings_menu': 'Beállítások'
293
+ },
294
+
295
+ // Türkçe
296
+ 'tr': {
297
+ 'subtitles': 'Altyazılar (C)',
298
+ 'subtitlesoff': 'Kapalı',
299
+ 'subtitlesdisable': 'Altyazıları kapat',
300
+ 'subtitlesenable': 'Altyazıları aç',
301
+ 'play_pause': 'Oynat/Duraklat (Boşluk)',
302
+ 'mute_unmute': 'Sessize al (M)',
303
+ 'volume': 'Ses düzeyi',
304
+ 'playback_speed': 'Oynatma hızı',
305
+ 'video_quality': 'Video kalitesi',
306
+ 'picture_in_picture': 'Resim içinde resim (P)',
307
+ 'fullscreen': 'Tam ekran (F)',
308
+ 'auto': 'Otomatik',
309
+ 'brand_logo': 'Marka logosu',
310
+ 'next_video': 'Sonraki video (N)',
311
+ 'prev_video': 'Önceki video (P)',
312
+ 'playlist_next': 'Sonraki',
313
+ 'playlist_prev': 'Önceki',
314
+ 'settings_menu': 'Ayarlar'
315
+ },
316
+
317
+ // Nederlands
318
+ 'nl': {
319
+ 'subtitles': 'Ondertitels (C)',
320
+ 'subtitlesoff': 'Uit',
321
+ 'subtitlesdisable': 'Ondertitels uitschakelen',
322
+ 'subtitlesenable': 'Ondertitels inschakelen',
323
+ 'play_pause': 'Afspelen/Pauzeren (Spatie)',
324
+ 'mute_unmute': 'Dempen (M)',
325
+ 'volume': 'Volume',
326
+ 'playback_speed': 'Afspeelsnelheid',
327
+ 'video_quality': 'Videokwaliteit',
328
+ 'picture_in_picture': 'Beeld-in-beeld (P)',
329
+ 'fullscreen': 'Volledig scherm (F)',
330
+ 'auto': 'Auto',
331
+ 'brand_logo': 'Merklogo',
332
+ 'next_video': 'Volgende video (N)',
333
+ 'prev_video': 'Vorige video (P)',
334
+ 'playlist_next': 'Volgende',
335
+ 'playlist_prev': 'Vorige',
336
+ 'settings_menu': 'Instellingen'
337
+ },
338
+
339
+ // हिन्दी (Hindi)
340
+ 'hi': {
341
+ 'subtitles': 'उपशीर्षक (C)',
342
+ 'subtitlesoff': 'बंद',
343
+ 'subtitlesdisable': 'उपशीर्षक अक्षम करें',
344
+ 'subtitlesenable': 'उपशीर्षक सक्षम करें',
345
+ 'play_pause': 'चलाएं/रोकें (स्पेस)',
346
+ 'mute_unmute': 'म्यूट (M)',
347
+ 'volume': 'वॉल्यूम',
348
+ 'playback_speed': 'प्लेबैक गति',
349
+ 'video_quality': 'वीडियो गुणवत्ता',
350
+ 'picture_in_picture': 'चित्र-में-चित्र (P)',
351
+ 'fullscreen': 'पूर्ण स्क्रीन (F)',
352
+ 'auto': 'स्वतः',
353
+ 'brand_logo': 'ब्रांड लोगो',
354
+ 'next_video': 'अगला वीडियो (N)',
355
+ 'prev_video': 'पिछला वीडियो (P)',
356
+ 'playlist_next': 'अगला',
357
+ 'playlist_prev': 'पिछला',
358
+ 'settings_menu': 'सेटिंग्स'
359
+ },
360
+
361
+ // Svenska
362
+ 'sv': {
363
+ 'subtitles': 'Undertexter (C)',
364
+ 'subtitlesoff': 'Av',
365
+ 'subtitlesdisable': 'Inaktivera undertexter',
366
+ 'subtitlesenable': 'Aktivera undertexter',
367
+ 'play_pause': 'Spela/Pausa (Blanksteg)',
368
+ 'mute_unmute': 'Stäng av ljud (M)',
369
+ 'volume': 'Volym',
370
+ 'playback_speed': 'Uppspelningshastighet',
371
+ 'video_quality': 'Videokvalitet',
372
+ 'picture_in_picture': 'Bild i bild (P)',
373
+ 'fullscreen': 'Fullskärm (F)',
374
+ 'auto': 'Auto',
375
+ 'brand_logo': 'Varumärkeslogotyp',
376
+ 'next_video': 'Nästa video (N)',
377
+ 'prev_video': 'Föregående video (P)',
378
+ 'playlist_next': 'Nästa',
379
+ 'playlist_prev': 'Föregående',
380
+ 'settings_menu': 'Inställningar'
381
+ },
382
+
383
+ // Bahasa Indonesia
384
+ 'id': {
385
+ 'subtitles': 'Teks (C)',
386
+ 'subtitlesoff': 'Mati',
387
+ 'subtitlesdisable': 'Nonaktifkan teks',
388
+ 'subtitlesenable': 'Aktifkan teks',
389
+ 'play_pause': 'Putar/Jeda (Spasi)',
390
+ 'mute_unmute': 'Bisu (M)',
391
+ 'volume': 'Volume',
392
+ 'playback_speed': 'Kecepatan pemutaran',
393
+ 'video_quality': 'Kualitas video',
394
+ 'picture_in_picture': 'Gambar-dalam-gambar (P)',
395
+ 'fullscreen': 'Layar penuh (F)',
396
+ 'auto': 'Otomatis',
397
+ 'brand_logo': 'Logo merek',
398
+ 'next_video': 'Video berikutnya (N)',
399
+ 'prev_video': 'Video sebelumnya (P)',
400
+ 'playlist_next': 'Berikutnya',
401
+ 'playlist_prev': 'Sebelumnya',
402
+ 'settings_menu': 'Pengaturan'
221
403
  }
222
404
  };
223
405
 
406
+
224
407
  // THEN detect language (after defining translations)
225
408
  this.currentLanguage = this.detectLanguage();
226
409
  }
@@ -2483,6 +2666,13 @@ addEventListener(eventType, callback) {
2483
2666
  this.video.addEventListener('playing', () => {
2484
2667
  this.hideLoading();
2485
2668
  this.closeAllMenus();
2669
+
2670
+ // Update play/pause button when video actually starts playing
2671
+ if (this.playIcon && this.pauseIcon) {
2672
+ this.playIcon.classList.add('hidden');
2673
+ this.pauseIcon.classList.remove('hidden');
2674
+ }
2675
+
2486
2676
  // Trigger playing event - video is now actually playing
2487
2677
  this.triggerEvent('playing', {
2488
2678
  currentTime: this.getCurrentTime(),
@@ -2691,17 +2881,91 @@ addEventListener(eventType, callback) {
2691
2881
  this.pipBtn.addEventListener('click', () => this.togglePictureInPicture());
2692
2882
  }
2693
2883
 
2694
- if (this.subtitlesBtn) {
2695
- this.subtitlesBtn.addEventListener('click', () => this.toggleSubtitles());
2696
- }
2884
+ if (this.volumeSlider) {
2885
+ let isDraggingVolume = false;
2886
+
2887
+ // Input event
2888
+ this.volumeSlider.addEventListener('input', (e) => {
2889
+ this.updateVolume(e.target.value);
2890
+ this.updateVolumeSliderVisual();
2891
+ this.initVolumeTooltip();
2892
+ });
2893
+
2894
+ // MOUSE DRAG - Start
2895
+ this.volumeSlider.addEventListener('mousedown', (e) => {
2896
+ isDraggingVolume = true;
2897
+ if (this.volumeTooltip) {
2898
+ this.volumeTooltip.classList.add('visible');
2899
+ }
2900
+ });
2697
2901
 
2698
- if (this.volumeSlider) {
2699
- this.volumeSlider.addEventListener('input', (e) => {
2700
- this.updateVolume(e.target.value);
2902
+ // MOUSE DRAG - Move
2903
+ document.addEventListener('mousemove', (e) => {
2904
+ if (isDraggingVolume && this.volumeSlider) {
2905
+ const rect = this.volumeSlider.getBoundingClientRect();
2906
+ const clickX = e.clientX - rect.left;
2907
+ const percentage = Math.max(0, Math.min(1, clickX / rect.width));
2908
+ const value = Math.round(percentage * 100);
2909
+
2910
+ this.volumeSlider.value = value;
2911
+ this.updateVolume(value);
2701
2912
  this.updateVolumeSliderVisual();
2702
- this.initVolumeTooltip();
2703
- });
2704
- }
2913
+ if (this.volumeTooltip) {
2914
+ this.updateVolumeTooltipPosition(value / 100);
2915
+ }
2916
+ }
2917
+ });
2918
+
2919
+ // MOUSE DRAG - End
2920
+ document.addEventListener('mouseup', () => {
2921
+ if (isDraggingVolume) {
2922
+ isDraggingVolume = false;
2923
+ if (this.volumeTooltip) {
2924
+ setTimeout(() => {
2925
+ this.volumeTooltip.classList.remove('visible');
2926
+ }, 300);
2927
+ }
2928
+ }
2929
+ });
2930
+
2931
+ // TOUCH DRAG - Start
2932
+ this.volumeSlider.addEventListener('touchstart', (e) => {
2933
+ isDraggingVolume = true;
2934
+ if (this.volumeTooltip) {
2935
+ this.volumeTooltip.classList.add('visible');
2936
+ }
2937
+ }, { passive: true });
2938
+
2939
+ // TOUCH DRAG - Move
2940
+ this.volumeSlider.addEventListener('touchmove', (e) => {
2941
+ if (isDraggingVolume) {
2942
+ const touch = e.touches[0];
2943
+ const rect = this.volumeSlider.getBoundingClientRect();
2944
+ const touchX = touch.clientX - rect.left;
2945
+ const percentage = Math.max(0, Math.min(1, touchX / rect.width));
2946
+ const value = Math.round(percentage * 100);
2947
+
2948
+ this.volumeSlider.value = value;
2949
+ this.updateVolume(value);
2950
+ this.updateVolumeSliderVisual();
2951
+ if (this.volumeTooltip) {
2952
+ this.updateVolumeTooltipPosition(value / 100);
2953
+ }
2954
+ }
2955
+ }, { passive: true });
2956
+
2957
+ // TOUCH DRAG - End
2958
+ this.volumeSlider.addEventListener('touchend', () => {
2959
+ if (isDraggingVolume) {
2960
+ isDraggingVolume = false;
2961
+ if (this.volumeTooltip) {
2962
+ setTimeout(() => {
2963
+ this.volumeTooltip.classList.remove('visible');
2964
+ }, 300);
2965
+ }
2966
+ }
2967
+ }, { passive: true });
2968
+ }
2705
2969
 
2706
2970
  if (this.progressContainer) {
2707
2971
  // Mouse events (desktop)
@@ -3187,20 +3451,20 @@ createControls() {
3187
3451
  <div class="progress-buffer"></div>
3188
3452
  <div class="progress-filled"></div>
3189
3453
  </div>
3190
- <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div> <!-- ✅ Fuori da progress-bar -->
3454
+ <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
3191
3455
  ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3192
3456
  </div>
3193
3457
 
3194
3458
  <div class="controls-main">
3195
3459
  <div class="controls-left">
3196
3460
  <button class="control-btn play-pause-btn" data-tooltip="play_pause">
3197
- <span class="icon play-icon">▶</span>
3198
- <span class="icon pause-icon hidden">⏸</span>
3461
+ <span class="icon play-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></span>
3462
+ <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>
3199
3463
  </button>
3200
3464
 
3201
3465
  <button class="control-btn mute-btn" data-tooltip="mute_unmute">
3202
- <span class="icon volume-icon">🔊</span>
3203
- <span class="icon mute-icon hidden">🔇</span>
3466
+ <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>
3467
+ <span class="icon mute-icon hidden"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06m7.137 2.096a.5.5 0 0 1 0 .708L12.207 8l1.647 1.646a.5.5 0 0 1-.708.708L11.5 8.707l-1.646 1.647a.5.5 0 0 1-.708-.708L10.793 8 9.146 6.354a.5.5 0 1 1 .708-.708L11.5 7.293l1.646-1.647a.5.5 0 0 1 .708 0"/></svg></span>
3204
3468
  </button>
3205
3469
 
3206
3470
  <div class="volume-container" data-mobile-slider="${this.options.volumeSlider}">
@@ -3215,18 +3479,17 @@ createControls() {
3215
3479
  </div>
3216
3480
 
3217
3481
  <div class="controls-right">
3218
- <button class="control-btn playlist-prev-btn" data-tooltip="prevvideo" style="display: none;">
3219
- <span class="icon">⏮</span>
3220
- </button>
3221
-
3222
- <button class="control-btn playlist-next-btn" data-tooltip="nextvideo" style="display: none;">
3223
- <span class="icon">⏭</span>
3224
- </button>
3482
+ <button class="control-btn playlist-prev-btn" data-tooltip="prevvideo" style="display: none;">
3483
+ <span class="icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M3.5 12V4l7 4zm8-8v8l-7-4z"/></svg></span>
3484
+ </button>
3485
+ <button class="control-btn playlist-next-btn" data-tooltip="nextvideo" style="display: none;">
3486
+ <span class="icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M12.5 4v8l-7-4zm-8 0v8l7-4z"/></svg></span>
3487
+ </button>
3225
3488
 
3226
3489
  ${this.options.showSubtitles ? `
3227
3490
  <div class="subtitles-control" style="display: none;">
3228
3491
  <button class="control-btn subtitles-btn" data-tooltip="subtitles">
3229
- <span class="icon">CC</span>
3492
+ <span class="icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1z"/><path d="M6.096 4.972c.234 0 .44.05.617.152.177.1.312.235.405.403.093.169.14.36.14.577 0 .216-.047.406-.14.572a1.03 1.03 0 0 1-.405.403 1.2 1.2 0 0 1-.617.152 1.2 1.2 0 0 1-.615-.152 1.03 1.03 0 0 1-.406-.403 1.28 1.28 0 0 1-.14-.572c0-.216.046-.408.14-.577.093-.168.228-.303.406-.403.177-.101.383-.152.615-.152m4.915 0c.234 0 .44.05.617.152.177.1.312.235.405.403.093.169.14.36.14.577 0 .216-.047.406-.14.572a1.03 1.03 0 0 1-.405.403 1.2 1.2 0 0 1-.617.152 1.2 1.2 0 0 1-.615-.152 1.03 1.03 0 0 1-.406-.403 1.28 1.28 0 0 1-.14-.572c0-.216.046-.408.14-.577.093-.168.228-.303.406-.403.177-.101.383-.152.615-.152M6.096 9.972c.234 0 .44.05.617.152.177.1.312.235.405.403.093.169.14.36.14.577 0 .216-.047.406-.14.572a1.03 1.03 0 0 1-.405.403 1.2 1.2 0 0 1-.617.152 1.2 1.2 0 0 1-.615-.152 1.03 1.03 0 0 1-.406-.403 1.28 1.28 0 0 1-.14-.572c0-.216.046-.408.14-.577.093-.168.228-.303.406-.403.177-.101.383-.152.615-.152m4.915 0c.234 0 .44.05.617.152.177.1.312.235.405.403.093.169.14.36.14.577 0 .216-.047.406-.14.572a1.03 1.03 0 0 1-.405.403 1.2 1.2 0 0 1-.617.152 1.2 1.2 0 0 1-.615-.152 1.03 1.03 0 0 1-.406-.403 1.28 1.28 0 0 1-.14-.572c0-.216.046-.408.14-.577.093-.168.228-.303.406-.403.177-.101.383-.152.615-.152"/></svg></span>
3230
3493
  </button>
3231
3494
  <div class="subtitles-menu">
3232
3495
  <div class="subtitles-option active" data-track="off">Off</div>
@@ -3267,22 +3530,22 @@ createControls() {
3267
3530
 
3268
3531
  ${this.options.showPictureInPicture && this.isPiPSupported ? `
3269
3532
  <button class="control-btn pip-btn" data-tooltip="picture_in_picture">
3270
- <span class="icon pip-icon">⧉</span>
3271
- <span class="icon pip-exit-icon hidden">⧉</span>
3533
+ <span class="icon pip-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5zM1.5 3a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5z"/><path d="M8 8.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5z"/></svg></span>
3534
+ <span class="icon pip-exit-icon hidden"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5zM1.5 3a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5z"/></svg></span>
3272
3535
  </button>
3273
3536
  ` : ''}
3274
3537
 
3275
3538
  <div class="settings-control">
3276
3539
  <button class="control-btn settings-btn" data-tooltip="settings_menu">
3277
- <span class="">⚙</span>
3540
+ <span class="icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0"/><path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52z"/></svg></span>
3278
3541
  </button>
3279
3542
  <div class="settings-menu"></div>
3280
3543
  </div>
3281
3544
 
3282
3545
  ${this.options.showFullscreen ? `
3283
3546
  <button class="control-btn fullscreen-btn" data-tooltip="fullscreen">
3284
- <span class="icon fullscreen-icon">⛶</span>
3285
- <span class="icon exit-fullscreen-icon hidden">⛉</span>
3547
+ <span class="icon fullscreen-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1zM10 .5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5M.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5m15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5 1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5"/></svg></span>
3548
+ <span class="icon exit-fullscreen-icon hidden"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M5.5 0a.5.5 0 0 1 .5.5v4A1.5 1.5 0 0 1 4.5 6h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5m5 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 10 4.5v-4a.5.5 0 0 1 .5-.5M0 10.5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 6 11.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5m10 1a1.5 1.5 0 0 1 1.5-1.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0z"/></svg></span>
3286
3549
  </button>
3287
3550
  ` : ''}
3288
3551
  </div>
@@ -5639,46 +5902,99 @@ parseTimeToSeconds(timeStr) {
5639
5902
  * Create visual chapter markers on the progress bar
5640
5903
  */
5641
5904
  createChapterMarkers() {
5642
- if (!this.progressContainer || !this.video || !this.chapters) {
5905
+ if (!this.progressContainer || !this.video || !this.chapters) return;
5906
+
5907
+ const duration = this.video.duration;
5908
+ if (!duration || isNaN(duration)) {
5909
+ // Wait for metadata
5910
+ const loadedMetadataHandler = () => {
5911
+ this.createChapterMarkers();
5912
+ this.video.removeEventListener('loadedmetadata', loadedMetadataHandler);
5913
+ };
5914
+ this.video.addEventListener('loadedmetadata', loadedMetadataHandler);
5643
5915
  return;
5644
5916
  }
5645
5917
 
5646
- // Create container for chapter markers
5918
+ // Remove existing markers
5919
+ const existingMarkers = this.progressContainer.querySelector('.chapter-markers-container');
5920
+ if (existingMarkers) {
5921
+ existingMarkers.remove();
5922
+ }
5923
+
5924
+ // Create container for chapter segments
5647
5925
  const markersContainer = document.createElement('div');
5648
5926
  markersContainer.className = 'chapter-markers-container';
5649
5927
 
5928
+ // Create segments for each chapter
5650
5929
  this.chapters.forEach((chapter, index) => {
5651
- const marker = document.createElement('div');
5652
- marker.className = 'chapter-marker';
5653
- marker.setAttribute('data-chapter-index', index);
5654
- marker.setAttribute('data-chapter-time', chapter.time);
5655
- marker.setAttribute('data-chapter-title', chapter.title);
5656
-
5657
- // Set custom color if provided
5658
- if (chapter.color) {
5659
- marker.style.backgroundColor = chapter.color;
5930
+ const nextChapter = this.chapters[index + 1];
5931
+ const startPercent = (chapter.time / duration) * 100;
5932
+ const endPercent = nextChapter ? (nextChapter.time / duration) * 100 : 100;
5933
+
5934
+ // Calculate segment width minus the gap
5935
+ const gapSize = nextChapter ? 6 : 0; // 6px gap between segments
5936
+ const widthPercent = endPercent - startPercent;
5937
+
5938
+ // Create segment container
5939
+ const segment = document.createElement('div');
5940
+ segment.className = 'chapter-segment';
5941
+ segment.style.cssText = `
5942
+ position: absolute;
5943
+ left: ${startPercent}%;
5944
+ top: 0;
5945
+ width: calc(${widthPercent}% - ${gapSize}px);
5946
+ height: 100%;
5947
+ background: rgba(255, 255, 255, 0.3);
5948
+ cursor: pointer;
5949
+ z-index: 3;
5950
+ transition: background 0.2s;
5951
+ pointer-events: none;
5952
+ `;
5953
+
5954
+ segment.setAttribute('data-chapter-index', index);
5955
+ segment.setAttribute('data-chapter-time', chapter.time);
5956
+ segment.setAttribute('data-chapter-title', chapter.title);
5957
+
5958
+ markersContainer.appendChild(segment);
5959
+
5960
+ // Add marker at the START of next segment (transparent divider)
5961
+ if (nextChapter) {
5962
+ const marker = document.createElement('div');
5963
+ marker.className = 'chapter-marker';
5964
+ marker.style.cssText = `
5965
+ position: absolute !important;
5966
+ left: ${endPercent}% !important;
5967
+ top: 0 !important;
5968
+ width: 6px !important;
5969
+ height: 100% !important;
5970
+ background: transparent !important;
5971
+ border: none !important;
5972
+ box-shadow: none !important;
5973
+ margin-left: -3px !important;
5974
+ cursor: pointer !important;
5975
+ z-index: 10 !important;
5976
+ `;
5977
+
5978
+ marker.setAttribute('data-chapter-time', nextChapter.time);
5979
+ marker.setAttribute('data-chapter-title', nextChapter.title);
5980
+
5981
+ // Click on marker to jump to chapter start
5982
+ marker.addEventListener('click', (e) => {
5983
+ e.stopPropagation();
5984
+ this.jumpToChapter(index + 1);
5985
+ });
5986
+
5987
+ markersContainer.appendChild(marker);
5660
5988
  }
5661
-
5662
- markersContainer.appendChild(marker);
5663
5989
  });
5664
5990
 
5665
5991
  // Insert markers container into progress container
5666
5992
  this.progressContainer.appendChild(markersContainer);
5667
5993
  this.chapterMarkersContainer = markersContainer;
5668
5994
 
5669
- // Update marker positions when video duration is known
5670
- if (this.video.duration && !isNaN(this.video.duration)) {
5671
- this.updateChapterMarkerPositions();
5672
- } else {
5673
- // Wait for metadata to be loaded
5674
- const loadedMetadataHandler = () => {
5675
- this.updateChapterMarkerPositions();
5676
- this.video.removeEventListener('loadedmetadata', loadedMetadataHandler);
5677
- };
5678
- this.video.addEventListener('loadedmetadata', loadedMetadataHandler);
5995
+ if (this.options.debug) {
5996
+ console.log(`Chapter markers created: ${this.chapters.length} segments`);
5679
5997
  }
5680
-
5681
- if (this.options.debug) console.log('📚 Chapter markers created on timeline');
5682
5998
  }
5683
5999
 
5684
6000
  /**
@@ -5703,70 +6019,278 @@ updateChapterMarkerPositions() {
5703
6019
  }
5704
6020
 
5705
6021
  /**
5706
- * Create chapter tooltip
6022
+ * Create chapter tooltip with title and image
5707
6023
  */
5708
6024
  createChapterTooltip() {
5709
- if (!this.progressContainer) {
5710
- return;
6025
+ if (!this.progressContainer) return;
6026
+
6027
+ // Remove existing chapter tooltip
6028
+ let chapterTooltip = this.progressContainer.querySelector('.chapter-tooltip');
6029
+ if (chapterTooltip) {
6030
+ chapterTooltip.remove();
5711
6031
  }
5712
6032
 
5713
- const tooltip = document.createElement('div');
5714
- tooltip.className = 'chapter-tooltip';
5715
- tooltip.style.opacity = '0';
5716
- tooltip.style.visibility = 'hidden';
6033
+ // Create chapter tooltip container (positioned ABOVE the time tooltip)
6034
+ chapterTooltip = document.createElement('div');
6035
+ chapterTooltip.className = 'chapter-tooltip';
6036
+ chapterTooltip.style.cssText = `
6037
+ position: absolute;
6038
+ bottom: calc(100% + 35px);
6039
+ left: 0;
6040
+ background: rgba(28, 28, 28, 0.95);
6041
+ color: #fff;
6042
+ border-radius: 4px;
6043
+ pointer-events: none;
6044
+ visibility: hidden;
6045
+ opacity: 0;
6046
+ z-index: 100001;
6047
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
6048
+ max-width: 300px;
6049
+ overflow: hidden;
6050
+ transform: translateX(-50%);
6051
+ transition: opacity 0.15s, visibility 0.15s;
6052
+ display: flex;
6053
+ flex-direction: column;
6054
+ `;
5717
6055
 
5718
- // Tooltip content structure
5719
- tooltip.innerHTML = `
5720
- <div class="chapter-tooltip-image"></div>
5721
- <div class="chapter-tooltip-title"></div>
5722
- <div class="chapter-tooltip-time"></div>
6056
+ // Create inner content structure
6057
+ chapterTooltip.innerHTML = `
6058
+ <div class="chapter-tooltip-content" style="display: flex; flex-direction: column; gap: 8px; padding: 8px;">
6059
+ <div class="chapter-tooltip-image" style="
6060
+ width: 100%;
6061
+ height: 120px;
6062
+ background-size: cover;
6063
+ background-position: center;
6064
+ border-radius: 3px;
6065
+ display: none;
6066
+ "></div>
6067
+ <div class="chapter-tooltip-info" style="display: flex; flex-direction: column; gap: 4px;">
6068
+ <div class="chapter-tooltip-title" style="
6069
+ font-size: 13px;
6070
+ font-weight: 600;
6071
+ color: #fff;
6072
+ max-width: 280px;
6073
+ overflow: hidden;
6074
+ text-overflow: ellipsis;
6075
+ white-space: nowrap;
6076
+ "></div>
6077
+ <div class="chapter-tooltip-time" style="
6078
+ font-size: 12px;
6079
+ font-weight: 400;
6080
+ color: rgba(255, 255, 255, 0.8);
6081
+ "></div>
6082
+ </div>
6083
+ </div>
5723
6084
  `;
5724
6085
 
5725
- this.progressContainer.appendChild(tooltip);
5726
- this.chapterTooltip = tooltip;
6086
+ this.progressContainer.appendChild(chapterTooltip);
6087
+ this.chapterTooltip = chapterTooltip;
5727
6088
 
5728
- if (this.options.debug) console.log('📚 Chapter tooltip created');
6089
+ if (this.options.debug) {
6090
+ console.log('Chapter tooltip created');
6091
+ }
5729
6092
  }
5730
6093
 
5731
6094
  /**
5732
- * Bind chapter-related events
6095
+ * Bind chapter-related events - tooltip on progressbar mousemove
5733
6096
  */
5734
6097
  bindChapterEvents() {
5735
- if (!this.chapterMarkersContainer || !this.chapterTooltip) {
5736
- return;
6098
+ if (!this.progressContainer) return;
6099
+
6100
+ // Remove existing chapter tooltip if present
6101
+ let chapterTooltip = this.progressContainer.querySelector('.chapter-tooltip-hover');
6102
+ if (chapterTooltip) {
6103
+ chapterTooltip.remove();
5737
6104
  }
5738
6105
 
5739
- // Hover on chapter markers
5740
- const markers = this.chapterMarkersContainer.querySelectorAll('.chapter-marker');
6106
+ // Create chapter tooltip
6107
+ chapterTooltip = document.createElement('div');
6108
+ chapterTooltip.className = 'chapter-tooltip-hover';
6109
+ chapterTooltip.style.cssText = `
6110
+ position: absolute;
6111
+ bottom: calc(100% + 35px);
6112
+ left: 0;
6113
+ background: rgba(28, 28, 28, 0.95);
6114
+ color: #fff;
6115
+ border-radius: 4px;
6116
+ padding: 8px;
6117
+ pointer-events: none;
6118
+ visibility: hidden;
6119
+ opacity: 0;
6120
+ z-index: 100001;
6121
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
6122
+ max-width: 300px;
6123
+ transform: translateX(-50%);
6124
+ transition: opacity 0.15s, visibility 0.15s;
6125
+ `;
5741
6126
 
5742
- markers.forEach((marker, index) => {
5743
- marker.addEventListener('mouseenter', (e) => {
5744
- this.showChapterTooltip(index, e);
5745
- });
6127
+ this.progressContainer.appendChild(chapterTooltip);
6128
+ this.chapterTooltip = chapterTooltip;
5746
6129
 
5747
- marker.addEventListener('mousemove', (e) => {
5748
- this.updateChapterTooltipPosition(e);
5749
- });
6130
+ // Get player container for edge detection
6131
+ const getPlayerBounds = () => {
6132
+ return this.container ? this.container.getBoundingClientRect() : null;
6133
+ };
5750
6134
 
5751
- marker.addEventListener('mouseleave', () => {
5752
- this.hideChapterTooltip();
5753
- });
6135
+ // Mousemove handler to show tooltip with title and image
6136
+ this.progressContainer.addEventListener('mousemove', (e) => {
6137
+ if (!this.video || !this.video.duration || !this.chapters || this.chapters.length === 0) {
6138
+ return;
6139
+ }
5754
6140
 
5755
- // Click to jump to chapter
5756
- marker.addEventListener('click', (e) => {
5757
- e.stopPropagation();
5758
- this.jumpToChapter(index);
5759
- });
6141
+ const rect = this.progressContainer.getBoundingClientRect();
6142
+ const playerRect = getPlayerBounds();
6143
+ const mouseX = e.clientX - rect.left;
6144
+ const percentage = Math.max(0, Math.min(1, mouseX / rect.width));
6145
+ const time = percentage * this.video.duration;
6146
+
6147
+ // Find chapter at current time
6148
+ let currentChapter = null;
6149
+ for (let i = this.chapters.length - 1; i >= 0; i--) {
6150
+ if (time >= this.chapters[i].time) {
6151
+ currentChapter = this.chapters[i];
6152
+ break;
6153
+ }
6154
+ }
6155
+
6156
+ if (currentChapter) {
6157
+ // Build tooltip HTML
6158
+ let tooltipHTML = '<div style="display: flex; flex-direction: column; gap: 6px;">';
6159
+
6160
+ // Add image if available
6161
+ if (currentChapter.image) {
6162
+ tooltipHTML += `
6163
+ <div style="
6164
+ width: 100%;
6165
+ height: 120px;
6166
+ background-image: url('${currentChapter.image}');
6167
+ background-size: cover;
6168
+ background-position: center;
6169
+ border-radius: 3px;
6170
+ "></div>
6171
+ `;
6172
+ }
6173
+
6174
+ // Add title
6175
+ tooltipHTML += `
6176
+ <div style="
6177
+ font-size: 13px;
6178
+ font-weight: 600;
6179
+ max-width: 280px;
6180
+ overflow: hidden;
6181
+ text-overflow: ellipsis;
6182
+ white-space: nowrap;
6183
+ ">
6184
+ ${currentChapter.title}
6185
+ </div>
6186
+ `;
6187
+
6188
+ // Add time
6189
+ tooltipHTML += `
6190
+ <div style="
6191
+ font-size: 12px;
6192
+ font-weight: 400;
6193
+ color: rgba(255, 255, 255, 0.8);
6194
+ ">
6195
+ ${this.formatTime(currentChapter.time)}
6196
+ </div>
6197
+ `;
6198
+
6199
+ tooltipHTML += '</div>';
6200
+
6201
+ chapterTooltip.innerHTML = tooltipHTML;
6202
+ chapterTooltip.style.visibility = 'visible';
6203
+ chapterTooltip.style.opacity = '1';
6204
+
6205
+ // Position tooltip with edge detection
6206
+ setTimeout(() => {
6207
+ const tooltipWidth = chapterTooltip.offsetWidth;
6208
+ const tooltipHalfWidth = tooltipWidth / 2;
6209
+ const absoluteX = e.clientX;
6210
+
6211
+ if (playerRect) {
6212
+ // Left edge
6213
+ if (absoluteX - tooltipHalfWidth < playerRect.left) {
6214
+ chapterTooltip.style.left = `${playerRect.left - rect.left + tooltipHalfWidth}px`;
6215
+ }
6216
+ // Right edge
6217
+ else if (absoluteX + tooltipHalfWidth > playerRect.right) {
6218
+ chapterTooltip.style.left = `${playerRect.right - rect.left - tooltipHalfWidth}px`;
6219
+ }
6220
+ // Normal center
6221
+ else {
6222
+ chapterTooltip.style.left = `${mouseX}px`;
6223
+ }
6224
+ } else {
6225
+ chapterTooltip.style.left = `${mouseX}px`;
6226
+ }
6227
+ }, 0);
6228
+ } else {
6229
+ chapterTooltip.style.visibility = 'hidden';
6230
+ chapterTooltip.style.opacity = '0';
6231
+ }
6232
+ });
6233
+
6234
+ // Mouseleave handler
6235
+ this.progressContainer.addEventListener('mouseleave', () => {
6236
+ chapterTooltip.style.visibility = 'hidden';
6237
+ chapterTooltip.style.opacity = '0';
5760
6238
  });
5761
6239
 
5762
6240
  // Update active chapter during playback
5763
6241
  if (this.video) {
5764
- this.video.addEventListener('timeupdate', () => {
5765
- this.updateActiveChapter();
5766
- });
6242
+ this.video.addEventListener('timeupdate', () => this.updateActiveChapter());
5767
6243
  }
5768
6244
 
5769
- if (this.options.debug) console.log('📚 Chapter events bound');
6245
+ if (this.options.debug) {
6246
+ console.log('Chapter events bound with tooltip');
6247
+ }
6248
+ }
6249
+
6250
+ /**
6251
+ * Update chapter name in title overlay dynamically
6252
+ */
6253
+ updateChapterInTitleOverlay() {
6254
+ if (!this.video || !this.chapters || this.chapters.length === 0) return;
6255
+
6256
+ const titleOverlay = this.container ? this.container.querySelector('.title-overlay') : null;
6257
+ if (!titleOverlay) return;
6258
+
6259
+ // Find or create chapter name element
6260
+ let chapterElement = titleOverlay.querySelector('.chapter-name');
6261
+ if (!chapterElement) {
6262
+ chapterElement = document.createElement('div');
6263
+ chapterElement.className = 'chapter-name';
6264
+ chapterElement.style.cssText = `
6265
+ font-size: 13px;
6266
+ font-weight: 500;
6267
+ color: rgba(255, 255, 255, 0.9);
6268
+ margin-top: 6px;
6269
+ max-width: 400px;
6270
+ overflow: hidden;
6271
+ text-overflow: ellipsis;
6272
+ white-space: nowrap;
6273
+ `;
6274
+ titleOverlay.appendChild(chapterElement);
6275
+ }
6276
+
6277
+ // Find current chapter
6278
+ const currentTime = this.video.currentTime;
6279
+ let currentChapter = null;
6280
+ for (let i = this.chapters.length - 1; i >= 0; i--) {
6281
+ if (currentTime >= this.chapters[i].time) {
6282
+ currentChapter = this.chapters[i];
6283
+ break;
6284
+ }
6285
+ }
6286
+
6287
+ // Update or hide chapter name
6288
+ if (currentChapter) {
6289
+ chapterElement.textContent = currentChapter.title;
6290
+ chapterElement.style.display = 'block';
6291
+ } else {
6292
+ chapterElement.style.display = 'none';
6293
+ }
5770
6294
  }
5771
6295
 
5772
6296
  /**
@@ -5870,9 +6394,7 @@ jumpToChapter(chapterIndex) {
5870
6394
  * Update active chapter marker during playback
5871
6395
  */
5872
6396
  updateActiveChapter() {
5873
- if (!this.video || !this.chapterMarkersContainer || !this.chapters) {
5874
- return;
5875
- }
6397
+ if (!this.video || !this.chapterMarkersContainer || !this.chapters) return;
5876
6398
 
5877
6399
  const currentTime = this.video.currentTime;
5878
6400
  const markers = this.chapterMarkersContainer.querySelectorAll('.chapter-marker');
@@ -5894,6 +6416,9 @@ updateActiveChapter() {
5894
6416
  marker.classList.remove('active');
5895
6417
  }
5896
6418
  });
6419
+
6420
+ // Update chapter name in title overlay
6421
+ this.updateChapterInTitleOverlay();
5897
6422
  }
5898
6423
 
5899
6424
  /**