myetv-player 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,8 @@ class VideoPlayerI18n {
27
27
  'playlist_prev': 'Indietro',
28
28
  'settings_menu': 'Impostazioni',
29
29
  'loading': 'Caricamento...',
30
- 'encoding_in_progress': 'Encoding in corso'
30
+ 'encoding_in_progress': 'Encoding in corso',
31
+ 'restart_video': 'Torna all\'inizio'
31
32
  },
32
33
 
33
34
  // English
@@ -51,7 +52,8 @@ class VideoPlayerI18n {
51
52
  'playlist_prev': 'Previous',
52
53
  'settings_menu': 'Settings',
53
54
  'loading': 'Loading...',
54
- 'encoding_in_progress': 'Encoding in progress'
55
+ 'encoding_in_progress': 'Encoding in progress',
56
+ 'restart_video': 'Restart the video'
55
57
  },
56
58
 
57
59
  // Español
@@ -75,7 +77,8 @@ class VideoPlayerI18n {
75
77
  'playlist_prev': 'Anterior',
76
78
  'settings_menu': 'Configuración',
77
79
  'loading': 'Cargando...',
78
- 'encoding_in_progress': 'Codificación en curso'
80
+ 'encoding_in_progress': 'Codificación en curso',
81
+ 'restart_video': 'Reiniciar el vídeo'
79
82
  },
80
83
 
81
84
  // Français
@@ -99,7 +102,8 @@ class VideoPlayerI18n {
99
102
  'playlist_prev': 'Précédent',
100
103
  'settings_menu': 'Paramètres',
101
104
  'loading': 'Chargement...',
102
- 'encoding_in_progress': 'Encodage en cours'
105
+ 'encoding_in_progress': 'Encodage en cours',
106
+ 'restart_video': 'Redémarrer la vidéo'
103
107
  },
104
108
 
105
109
  // Deutsch
@@ -123,7 +127,8 @@ class VideoPlayerI18n {
123
127
  'playlist_prev': 'Zurück',
124
128
  'settings_menu': 'Einstellungen',
125
129
  'loading': 'Laden...',
126
- 'encoding_in_progress': 'Kodierung läuft'
130
+ 'encoding_in_progress': 'Kodierung läuft',
131
+ 'restart_video': 'Video neu starten'
127
132
  },
128
133
 
129
134
  // Português
@@ -147,7 +152,8 @@ class VideoPlayerI18n {
147
152
  'playlist_prev': 'Anterior',
148
153
  'settings_menu': 'Configurações',
149
154
  'loading': 'Carregando...',
150
- 'encoding_in_progress': 'Codificação em andamento'
155
+ 'encoding_in_progress': 'Codificação em andamento',
156
+ 'restart_video': 'Restart video'
151
157
  },
152
158
 
153
159
  // 中文
@@ -171,7 +177,8 @@ class VideoPlayerI18n {
171
177
  'playlist_prev': '上一个',
172
178
  'settings_menu': '设置',
173
179
  'loading': '加载中...',
174
- 'encoding_in_progress': '编码中'
180
+ 'encoding_in_progress': '编码中',
181
+ 'restart_video': '重新开始视频'
175
182
  },
176
183
 
177
184
  // 日本語
@@ -195,7 +202,8 @@ class VideoPlayerI18n {
195
202
  'playlist_prev': '前へ',
196
203
  'settings_menu': '設定',
197
204
  'loading': '読み込み中...',
198
- 'encoding_in_progress': 'エンコード中'
205
+ 'encoding_in_progress': 'エンコード中',
206
+ 'restart_video': 'ビデオを再開'
199
207
  },
200
208
 
201
209
  // Русский
@@ -219,7 +227,8 @@ class VideoPlayerI18n {
219
227
  'playlist_prev': 'Назад',
220
228
  'settings_menu': 'Настройки',
221
229
  'loading': 'Загрузка...',
222
- 'encoding_in_progress': 'Кодирование'
230
+ 'encoding_in_progress': 'Кодирование',
231
+ 'restart_video': 'Перезапустить видео'
223
232
  },
224
233
 
225
234
  // العربية
@@ -243,7 +252,8 @@ class VideoPlayerI18n {
243
252
  'playlist_prev': 'السابق',
244
253
  'settings_menu': 'الإعدادات',
245
254
  'loading': 'جاري التحميل...',
246
- 'encoding_in_progress': 'الترميز جارٍ'
255
+ 'encoding_in_progress': 'الترميز جارٍ',
256
+ 'restart_video': 'إعادة تشغيل الفيديو'
247
257
  },
248
258
 
249
259
  // 한국어 (Korean)
@@ -267,7 +277,8 @@ class VideoPlayerI18n {
267
277
  'playlist_prev': '이전',
268
278
  'settings_menu': '설정',
269
279
  'loading': '로딩 중...',
270
- 'encoding_in_progress': '인코딩 진행 중'
280
+ 'encoding_in_progress': '인코딩 진행 중',
281
+ 'restart_video': 'Restart video'
271
282
  },
272
283
 
273
284
  // Polski
@@ -291,7 +302,8 @@ class VideoPlayerI18n {
291
302
  'playlist_prev': 'Wstecz',
292
303
  'settings_menu': 'Ustawienia',
293
304
  'loading': 'Ładowanie...',
294
- 'encoding_in_progress': 'Kodowanie w toku'
305
+ 'encoding_in_progress': 'Kodowanie w toku',
306
+ 'restart_video': 'Restart video'
295
307
  },
296
308
 
297
309
  // Magyar
@@ -315,7 +327,8 @@ class VideoPlayerI18n {
315
327
  'playlist_prev': 'Előző',
316
328
  'settings_menu': 'Beállítások',
317
329
  'loading': 'Betöltés...',
318
- 'encoding_in_progress': 'Kódolás folyamatban'
330
+ 'encoding_in_progress': 'Kódolás folyamatban',
331
+ 'restart_video': 'Restart video'
319
332
  },
320
333
 
321
334
  // Türkçe
@@ -339,7 +352,8 @@ class VideoPlayerI18n {
339
352
  'playlist_prev': 'Önceki',
340
353
  'settings_menu': 'Ayarlar',
341
354
  'loading': 'Yükleniyor...',
342
- 'encoding_in_progress': 'Kodlama devam ediyor'
355
+ 'encoding_in_progress': 'Kodlama devam ediyor',
356
+ 'restart_video': 'Restart video'
343
357
  },
344
358
 
345
359
  // Nederlands
@@ -363,7 +377,8 @@ class VideoPlayerI18n {
363
377
  'playlist_prev': 'Vorige',
364
378
  'settings_menu': 'Instellingen',
365
379
  'loading': 'Laden...',
366
- 'encoding_in_progress': 'Codering bezig'
380
+ 'encoding_in_progress': 'Codering bezig',
381
+ 'restart_video': 'Restart video'
367
382
  },
368
383
 
369
384
  // हिन्दी (Hindi)
@@ -387,7 +402,8 @@ class VideoPlayerI18n {
387
402
  'playlist_prev': 'पिछला',
388
403
  'settings_menu': 'सेटिंग्स',
389
404
  'loading': 'लोड हो रहा है...',
390
- 'encoding_in_progress': 'एन्कोडिंग प्रगति में'
405
+ 'encoding_in_progress': 'एन्कोडिंग प्रगति में',
406
+ 'restart_video': 'Restart video'
391
407
  },
392
408
 
393
409
  // Svenska
@@ -411,7 +427,8 @@ class VideoPlayerI18n {
411
427
  'playlist_prev': 'Föregående',
412
428
  'settings_menu': 'Inställningar',
413
429
  'loading': 'Laddar...',
414
- 'encoding_in_progress': 'Kodning pågår'
430
+ 'encoding_in_progress': 'Kodning pågår',
431
+ 'restart_video': 'Restart video'
415
432
  },
416
433
 
417
434
  // Bahasa Indonesia
@@ -435,7 +452,8 @@ class VideoPlayerI18n {
435
452
  'playlist_prev': 'Sebelumnya',
436
453
  'settings_menu': 'Pengaturan',
437
454
  'loading': 'Memuat...',
438
- 'encoding_in_progress': 'Encoding sedang berlangsung'
455
+ 'encoding_in_progress': 'Encoding sedang berlangsung',
456
+ 'restart_video': 'Restart video'
439
457
  }
440
458
  };
441
459
 
@@ -565,7 +583,8 @@ try {
565
583
  'brand_logo': 'Brand logo',
566
584
  'settings_menu': 'Settings',
567
585
  'loading': 'Loading...',
568
- 'encoding_in_progress': 'Encoding in progress'
586
+ 'encoding_in_progress': 'Encoding in progress',
587
+ 'restart_video': 'Restart video'
569
588
  };
570
589
  return fallback[key] || key;
571
590
  },
@@ -629,6 +648,7 @@ constructor(videoElement, options = {}) {
629
648
  }
630
649
 
631
650
  this.options = {
651
+ playFromStartButton: false, // Enable play from start button (restart video)
632
652
  showQualitySelector: true, // Enable quality selector button
633
653
  showSpeedControl: true, // Enable speed control button
634
654
  showFullscreen: true, // Enable fullscreen button
@@ -737,6 +757,7 @@ constructor(videoElement, options = {}) {
737
757
  'played': [], // Fired when video starts playing
738
758
  'paused': [], // Fired when video is paused
739
759
  'ended': [], // Fired when video playback ends
760
+ 'restarted': [], // Fired when video is restarted from beginning
740
761
 
741
762
  // Playback state events
742
763
  'playing': [], // Fired when video is actually playing (after buffering)
@@ -1271,7 +1292,7 @@ createPlayerStructure() {
1271
1292
  createInitialLoading() {
1272
1293
  const initialLoader = document.createElement('div');
1273
1294
  initialLoader.className = 'initial-loading';
1274
- initialLoader.innerHTML = '<div class="loading-spinner"></div>';
1295
+ initialLoader.innerHTML = '<div class="loading-spinner"></div><div class="loading-text"></div>';
1275
1296
  this.container.appendChild(initialLoader);
1276
1297
  this.initialLoading = initialLoader;
1277
1298
  }
@@ -1284,7 +1305,7 @@ createLoadingOverlay() {
1284
1305
  const overlay = document.createElement('div');
1285
1306
  overlay.className = 'loading-overlay';
1286
1307
  overlay.id = 'loadingOverlay-' + this.getUniqueId();
1287
- overlay.innerHTML = '<div class="loading-spinner"></div>';
1308
+ overlay.innerHTML = '<div class="loading-spinner"></div><div class="loading-text"></div>';
1288
1309
  this.container.appendChild(overlay);
1289
1310
  this.loadingOverlay = overlay;
1290
1311
  }
@@ -1454,6 +1475,7 @@ initializeElements() {
1454
1475
  this.progressHandle = this.controls?.querySelector('.progress-handle');
1455
1476
  this.seekTooltip = this.controls?.querySelector('.seek-tooltip');
1456
1477
 
1478
+ this.playFromStartBtn = this.controls?.querySelector('.play-from-start-btn');
1457
1479
  this.playPauseBtn = this.controls?.querySelector('.play-pause-btn');
1458
1480
  this.muteBtn = this.controls?.querySelector('.mute-btn');
1459
1481
  this.fullscreenBtn = this.controls?.querySelector('.fullscreen-btn');
@@ -1559,6 +1581,51 @@ setupMenuToggles() {
1559
1581
  }
1560
1582
  }
1561
1583
 
1584
+ /**
1585
+ * Restart video from beginning - Works with HTML5 video and ALL plugins
1586
+ * @returns {this} Returns this for method chaining
1587
+ */
1588
+ restartVideo() {
1589
+ if (!this.video) return this;
1590
+
1591
+ const previousTime = this.getCurrentTime();
1592
+ const wasPaused = this.isPaused();
1593
+
1594
+ // Set video to beginning (0 seconds)
1595
+ // This works for both HTML5 video and plugins
1596
+ this.video.currentTime = 0;
1597
+
1598
+ // Alternative: use seek method if available (for plugins)
1599
+ if (typeof this.seek === 'function') {
1600
+ this.seek(0);
1601
+ }
1602
+
1603
+ // Auto-play after restart if video was playing
1604
+ if (!wasPaused) {
1605
+ // Use player's play method (works for HTML5 and plugins)
1606
+ if (typeof this.play === 'function') {
1607
+ this.play();
1608
+ } else {
1609
+ this.video.play().catch(error => {
1610
+ if (this.options.debug) console.warn('⚠️ Restart play failed:', error);
1611
+ });
1612
+ }
1613
+ }
1614
+
1615
+ if (this.options.debug) {
1616
+ console.log(`🔄 Video restarted from ${this.formatTime(previousTime)} to 0:00`);
1617
+ }
1618
+
1619
+ // Trigger custom event
1620
+ this.triggerEvent('restarted', {
1621
+ previousTime: previousTime,
1622
+ duration: this.getDuration(),
1623
+ autoPlayed: !wasPaused
1624
+ });
1625
+
1626
+ return this;
1627
+ }
1628
+
1562
1629
  updateVolumeSliderVisual() {
1563
1630
  if (!this.video || !this.container) return;
1564
1631
 
@@ -1854,6 +1921,18 @@ updateProgress() {
1854
1921
  }
1855
1922
  }
1856
1923
 
1924
+ // Helper to update loading text in center overlays
1925
+ updateLoadingText(text) {
1926
+ if (this.initialLoading) {
1927
+ const textEl = this.initialLoading.querySelector('.loading-text');
1928
+ if (textEl) textEl.textContent = text;
1929
+ }
1930
+ if (this.loadingOverlay) {
1931
+ const textEl = this.loadingOverlay.querySelector('.loading-text');
1932
+ if (textEl) textEl.textContent = text;
1933
+ }
1934
+ }
1935
+
1857
1936
  updateBuffer() {
1858
1937
  if (!this.video || !this.progressBuffer) return;
1859
1938
 
@@ -2762,6 +2841,14 @@ addEventListener(eventType, callback) {
2762
2841
  bindEvents() {
2763
2842
  if (this.video) {
2764
2843
 
2844
+ // Play from start button
2845
+ if (this.playFromStartBtn) {
2846
+ this.playFromStartBtn.addEventListener('click', (e) => {
2847
+ e.stopPropagation();
2848
+ this.restartVideo();
2849
+ });
2850
+ }
2851
+
2765
2852
  // Playback events
2766
2853
  this.video.addEventListener('playing', () => {
2767
2854
  this.hideLoading();
@@ -3587,26 +3674,36 @@ createControls() {
3587
3674
 
3588
3675
  <div class="controls-main">
3589
3676
  <div class="controls-left">
3590
- <button class="control-btn play-pause-btn" data-tooltip="play_pause">
3591
- <span class="icon play-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></span>
3592
- <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>
3593
- </button>
3677
+ ${this.options.playFromStartButton ? `
3678
+ <button class="control-btn play-from-start-btn" data-tooltip="restart_video">
3679
+ <span class="icon restart-icon">
3680
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
3681
+ <path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
3682
+ </svg>
3683
+ </span>
3684
+ </button>
3685
+ ` : ''}
3594
3686
 
3595
- <button class="control-btn mute-btn" data-tooltip="mute_unmute">
3596
- <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>
3597
- <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>
3598
- </button>
3687
+ <button class="control-btn play-pause-btn" data-tooltip="play_pause">
3688
+ <span class="icon play-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></span>
3689
+ <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>
3690
+ </button>
3599
3691
 
3600
- <div class="volume-container" data-mobile-slider="${this.options.volumeSlider}">
3601
- <input type="range" class="volume-slider" min="0" max="100" value="100" data-tooltip="volume">
3602
- </div>
3692
+ <button class="control-btn mute-btn" data-tooltip="mute_unmute">
3693
+ <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>
3694
+ <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>
3695
+ </button>
3603
3696
 
3604
- <div class="time-display">
3605
- <span class="current-time">0:00</span>
3606
- <span>/</span>
3607
- <span class="duration">0:00</span>
3608
- </div>
3609
- </div>
3697
+ <div class="volume-container" data-mobile-slider="${this.options.volumeSlider}">
3698
+ <input type="range" class="volume-slider" min="0" max="100" value="100" data-tooltip="volume">
3699
+ </div>
3700
+
3701
+ <div class="time-display">
3702
+ <span class="current-time">0:00</span>
3703
+ <span>/</span>
3704
+ <span class="duration">0:00</span>
3705
+ </div>
3706
+ </div>
3610
3707
 
3611
3708
  <div class="controls-right">
3612
3709
  <button class="control-btn playlist-prev-btn" data-tooltip="prevvideo" style="display: none;">
@@ -8317,12 +8414,10 @@ skipTime(seconds) {
8317
8414
  }
8318
8415
 
8319
8416
  updateTimeDisplay() {
8320
- // Update current time
8321
8417
  if (this.currentTimeEl && this.video) {
8322
8418
  this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
8323
8419
  }
8324
8420
 
8325
- // Update duration or show appropriate message
8326
8421
  if (this.durationEl && this.video) {
8327
8422
  const duration = this.video.duration;
8328
8423
  const readyState = this.video.readyState;
@@ -8330,28 +8425,47 @@ updateTimeDisplay() {
8330
8425
  const networkState = this.video.networkState;
8331
8426
 
8332
8427
  // 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
-
8428
+ const isInitialBuffering = (readyState < 2 && currentTime === 0) || (currentTime === 0 && !duration) || (duration === 0 && networkState === 2);
8338
8429
  // Check if duration is invalid (NaN or Infinity)
8339
8430
  const isDurationInvalid = !duration || isNaN(duration) || !isFinite(duration);
8340
8431
 
8432
+ // Text for translations
8433
+ const t = (key) => {
8434
+ if (this.isI18nAvailable()) {
8435
+ try {
8436
+ return VideoPlayerTranslations.t(key);
8437
+ } catch (error) {
8438
+ return key;
8439
+ }
8440
+ }
8441
+ const fallback = {
8442
+ 'loading': 'Loading...',
8443
+ 'encodinginprogress': 'Encoding in progress...'
8444
+ };
8445
+ return fallback[key] || key;
8446
+ };
8447
+
8341
8448
  if (isInitialBuffering) {
8342
- // Initial buffering - show loading message
8343
- this.durationEl.textContent = t('loading');
8449
+ // CHANGED: Move text to center overlay, clear control bar text
8450
+ this.updateLoadingText(t('loading'));
8451
+ this.durationEl.textContent = this.formatTime(0); // Just show 00:00 or empty
8344
8452
  this.durationEl.classList.remove('encoding-state');
8345
8453
  this.durationEl.classList.add('loading-state');
8346
8454
  } else if (isDurationInvalid) {
8347
- // Video is encoding (FFmpeg still processing) - show encoding badge
8348
- this.durationEl.textContent = t('encoding_in_progress');
8455
+ // CHANGED: Move text to center overlay
8456
+ this.updateLoadingText(t('encodinginprogress'));
8457
+ // Optional: you might want to keep encoding text in bar OR move it too.
8458
+ // If you want it ONLY in center:
8459
+ this.durationEl.textContent = "--:--";
8460
+
8349
8461
  this.durationEl.classList.remove('loading-state');
8350
8462
  this.durationEl.classList.add('encoding-state');
8351
8463
  } else {
8352
8464
  // Valid duration - show normal time
8353
8465
  this.durationEl.textContent = this.formatTime(duration);
8354
8466
  this.durationEl.classList.remove('encoding-state', 'loading-state');
8467
+ // Clear loading text when playing normally
8468
+ this.updateLoadingText('');
8355
8469
  }
8356
8470
  }
8357
8471
  }