myetv-player 1.1.0 → 1.1.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.
@@ -419,6 +419,7 @@ constructor(videoElement, options = {}) {
419
419
  brandLogoEnabled: false,
420
420
  brandLogoUrl: '',
421
421
  brandLogoLinkUrl: '',
422
+ brandLogoTooltipText: '',
422
423
  playlistEnabled: true,
423
424
  playlistAutoPlay: true,
424
425
  playlistLoop: false,
@@ -451,7 +452,6 @@ constructor(videoElement, options = {}) {
451
452
  this.currentQualityIndex = 0;
452
453
  this.qualities = [];
453
454
  this.originalSources = [];
454
- this.setupMenuToggles();
455
455
  this.isPiPSupported = this.checkPiPSupport();
456
456
  this.seekTooltip = null;
457
457
  this.titleOverlay = null;
@@ -494,18 +494,42 @@ constructor(videoElement, options = {}) {
494
494
 
495
495
 
496
496
  this.eventCallbacks = {
497
- 'played': [],
498
- 'paused': [],
499
- 'subtitlechange': [],
500
- 'chapterchange': [],
501
- 'pipchange': [],
497
+
498
+ 'playerready': [],
499
+ 'played': [],
500
+ 'paused': [],
501
+ 'ended': [],
502
+
503
+
504
+ 'playing': [],
505
+ 'waiting': [],
506
+ 'seeking': [],
507
+ 'seeked': [],
508
+
509
+
510
+ 'loadstart': [],
511
+ 'loadedmetadata': [],
512
+ 'loadeddata': [],
513
+ 'canplay': [],
514
+ 'progress': [],
515
+ 'durationchange': [],
516
+
517
+
518
+ 'error': [],
519
+ 'stalled': [],
520
+
521
+
522
+ 'timeupdate': [],
523
+ 'volumechange': [],
524
+ 'speedchange': [],
525
+ 'qualitychange': [],
526
+
527
+
528
+ 'subtitlechange': [],
529
+ 'chapterchange': [],
530
+ 'pipchange': [],
502
531
  'fullscreenchange': [],
503
- 'speedchange': [],
504
- 'timeupdate': [],
505
- 'volumechange': [],
506
- 'qualitychange': [],
507
- 'playlistchange': [],
508
- 'ended': []
532
+ 'playlistchange': []
509
533
  };
510
534
 
511
535
 
@@ -539,6 +563,7 @@ constructor(videoElement, options = {}) {
539
563
  this.interceptAutoLoading();
540
564
  this.createPlayerStructure();
541
565
  this.initializeElements();
566
+ this.setupMenuToggles();
542
567
 
543
568
  this.adaptToAudioFile = function () {
544
569
  if (this.options.audiofile) {
@@ -867,6 +892,14 @@ markPlayerReady() {
867
892
  this.container.classList.add('player-initialized');
868
893
  }
869
894
 
895
+ this.triggerEvent('playerready', {
896
+ playerState: this.getPlayerState(),
897
+ qualities: this.qualities,
898
+ subtitles: this.textTracks,
899
+ chapters: this.chapters,
900
+ playlist: this.getPlaylistInfo()
901
+ });
902
+
870
903
  if (this.video) {
871
904
  this.video.style.visibility = '';
872
905
  this.video.style.opacity = '';
@@ -1147,72 +1180,82 @@ initializeElements() {
1147
1180
  this.speedMenu = this.controls?.querySelector('.speed-menu');
1148
1181
  this.qualityMenu = this.controls?.querySelector('.quality-menu');
1149
1182
  this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
1183
+
1184
+ if (this.progressHandle && this.options.seekHandleShape) {
1185
+ this.setSeekHandleShape(this.options.seekHandleShape);
1186
+ }
1150
1187
  }
1151
1188
 
1152
1189
  closeAllMenus() {
1153
-
1154
- const allMenus = this.controls?.querySelectorAll('[class*="-menu"].active');
1155
- allMenus?.forEach(menu => {
1156
- menu.classList.remove('active');
1157
- });
1190
+ if (!this.controls) return;
1158
1191
 
1159
-
1160
- const allButtons = this.controls?.querySelectorAll('.control-btn.active');
1161
- allButtons?.forEach(btn => {
1162
- btn.classList.remove('active');
1163
- });
1192
+ const menus = this.controls.querySelectorAll('.speed-menu, .quality-menu, .subtitles-menu, .settings-menu');
1193
+ const buttons = this.controls.querySelectorAll('.control-btn');
1194
+
1195
+ menus.forEach(menu => menu.classList.remove('active'));
1196
+ buttons.forEach(btn => btn.classList.remove('active'));
1197
+
1198
+ this.currentOpenMenu = null;
1199
+
1200
+ if (this.options.debug) {
1201
+ console.log('All menus closed');
1202
+ }
1164
1203
  }
1165
1204
 
1166
1205
  setupMenuToggles() {
1167
-
1168
- if (this.controls) {
1169
- this.controls.addEventListener('click', (e) => {
1170
-
1171
- const button = e.target.closest('.control-btn');
1206
+ if (!this.controls) return;
1172
1207
 
1173
- if (!button) return;
1208
+ this.currentOpenMenu = null;
1174
1209
 
1175
-
1176
- const buttonClasses = button.className.split(' ');
1177
- let menuClass = null;
1210
+ this.controls.addEventListener('click', (e) => {
1211
+ const button = e.target.closest('.control-btn');
1212
+ if (!button) return;
1178
1213
 
1179
-
1180
- for (const cls of buttonClasses) {
1181
- if (cls.endsWith('-btn')) {
1182
- const menuName = cls.replace('-btn', '-menu');
1183
- const menu = this.controls.querySelector('.' + menuName);
1184
- if (menu) {
1185
- menuClass = menuName;
1186
- break;
1187
- }
1188
- }
1214
+ const buttonClasses = Array.from(button.classList);
1215
+ let menuElement = null;
1216
+
1217
+ for (const cls of buttonClasses) {
1218
+ if (cls.endsWith('-btn')) {
1219
+ const menuClass = cls.replace('-btn', '-menu');
1220
+ menuElement = this.controls.querySelector(`.${menuClass}`);
1221
+ if (menuElement) break;
1189
1222
  }
1223
+ }
1190
1224
 
1191
- if (!menuClass) return;
1225
+ if (!menuElement) return;
1192
1226
 
1193
- e.stopPropagation();
1227
+ e.stopPropagation();
1228
+ e.preventDefault();
1194
1229
 
1195
-
1196
- const menu = this.controls.querySelector('.' + menuClass);
1197
- const isOpen = menu.classList.contains('active');
1230
+ const isOpen = menuElement.classList.contains('active');
1198
1231
 
1199
-
1200
- this.closeAllMenus();
1232
+ this.closeAllMenus();
1201
1233
 
1202
-
1203
- if (!isOpen) {
1204
- menu.classList.add('active');
1205
- button.classList.add('active');
1234
+ if (!isOpen) {
1235
+ menuElement.classList.add('active');
1236
+ button.classList.add('active');
1237
+ this.currentOpenMenu = menuElement;
1238
+ if (this.options.debug) {
1239
+ console.log('Menu opened:', menuElement.className);
1206
1240
  }
1207
- });
1208
- }
1241
+ } else {
1242
+ this.currentOpenMenu = null;
1243
+ if (this.options.debug) {
1244
+ console.log('Menu closed:', menuElement.className);
1245
+ }
1246
+ }
1247
+ });
1209
1248
 
1210
-
1211
1249
  document.addEventListener('click', (e) => {
1212
- if (!this.controls?.contains(e.target)) {
1250
+ if (!this.controls) return;
1251
+ if (!this.controls.contains(e.target)) {
1213
1252
  this.closeAllMenus();
1214
1253
  }
1215
1254
  });
1255
+
1256
+ if (this.options.debug) {
1257
+ console.log('✅ Menu toggle system initialized (click-based, auto-close)');
1258
+ }
1216
1259
  }
1217
1260
 
1218
1261
  updateVolumeSliderVisual() {
@@ -1516,9 +1559,11 @@ updateBuffer() {
1516
1559
  }
1517
1560
 
1518
1561
  startSeeking(e) {
1562
+ if (e.cancelable) e.preventDefault();
1519
1563
  if (this.isChangingQuality) return;
1520
1564
 
1521
1565
  this.isUserSeeking = true;
1566
+ this.progressContainer.classList.add('seeking');
1522
1567
  this.seek(e);
1523
1568
  e.preventDefault();
1524
1569
 
@@ -1530,6 +1575,7 @@ startSeeking(e) {
1530
1575
  }
1531
1576
 
1532
1577
  continueSeeking(e) {
1578
+ if (e.cancelable) e.preventDefault();
1533
1579
  if (this.isUserSeeking && !this.isChangingQuality) {
1534
1580
  this.seek(e);
1535
1581
  }
@@ -1537,9 +1583,13 @@ continueSeeking(e) {
1537
1583
 
1538
1584
  endSeeking() {
1539
1585
  this.isUserSeeking = false;
1586
+ this.progressContainer.classList.remove('seeking');
1540
1587
  }
1541
1588
 
1542
1589
  seek(e) {
1590
+ if (e.cancelable) {
1591
+ e.preventDefault();
1592
+ }
1543
1593
  if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
1544
1594
 
1545
1595
  const rect = this.progressContainer.getBoundingClientRect();
@@ -1716,7 +1766,14 @@ createBrandLogo() {
1716
1766
  const logo = document.createElement('img');
1717
1767
  logo.className = 'brand-logo';
1718
1768
  logo.src = this.options.brandLogoUrl;
1719
- logo.alt = this.t('brand_logo');
1769
+ logo.alt = 'Brand logo';
1770
+
1771
+
1772
+ if (this.options.brandLogoLinkUrl) {
1773
+
1774
+ logo.title = this.options.brandLogoTooltipText || this.options.brandLogoLinkUrl;
1775
+
1776
+ }
1720
1777
 
1721
1778
 
1722
1779
  logo.onerror = () => {
@@ -1740,15 +1797,10 @@ createBrandLogo() {
1740
1797
  logo.style.cursor = 'default';
1741
1798
  }
1742
1799
 
1743
-
1744
1800
  controlsRight.insertBefore(logo, controlsRight.firstChild);
1745
1801
 
1746
1802
  if (this.options.debug) {
1747
- if (this.options.brandLogoLinkUrl) {
1748
- console.log('Brand logo with click handler created for:', this.options.brandLogoLinkUrl);
1749
- } else {
1750
- console.log('Brand logo created (no link)');
1751
- }
1803
+ console.log('Brand logo created with tooltip:', logo.title || 'no tooltip');
1752
1804
  }
1753
1805
  }
1754
1806
 
@@ -1947,7 +1999,7 @@ bindPosterEvents() {
1947
1999
 
1948
2000
  this.video.addEventListener('playing', () => {
1949
2001
  this.hidePoster();
1950
- });
2002
+ });
1951
2003
 
1952
2004
 
1953
2005
  if (!this.options.autoplay) {
@@ -2335,36 +2387,130 @@ addEventListener(eventType, callback) {
2335
2387
  }
2336
2388
 
2337
2389
  bindEvents() {
2338
- if (this.video) {
2339
- this.video.addEventListener('loadedmetadata', () => {
2340
- this.updateDuration();
2341
- setTimeout(() => {
2342
- this.initializeSubtitles();
2343
- }, 100);
2390
+ if (this.video) {
2391
+
2392
+
2393
+ this.video.addEventListener('playing', () => {
2394
+ this.hideLoading();
2395
+ this.closeAllMenus();
2396
+
2397
+ this.triggerEvent('playing', {
2398
+ currentTime: this.getCurrentTime(),
2399
+ duration: this.getDuration()
2344
2400
  });
2345
- this.video.addEventListener('timeupdate', () => this.updateProgress());
2346
- this.video.addEventListener('progress', () => this.updateBuffer());
2347
- this.video.addEventListener('waiting', () => {
2348
- if (!this.isChangingQuality) {
2349
- this.showLoading();
2350
- }
2401
+ });
2402
+
2403
+ this.video.addEventListener('waiting', () => {
2404
+ if (!this.isChangingQuality) {
2405
+ this.showLoading();
2406
+
2407
+ this.triggerEvent('waiting', {
2408
+ currentTime: this.getCurrentTime()
2409
+ });
2410
+ }
2411
+ });
2412
+
2413
+ this.video.addEventListener('seeking', () => {
2414
+
2415
+ this.triggerEvent('seeking', {
2416
+ currentTime: this.getCurrentTime(),
2417
+ targetTime: this.video.currentTime
2351
2418
  });
2352
- this.video.addEventListener('canplay', () => {
2353
- if (!this.isChangingQuality) {
2354
- this.hideLoading();
2355
- }
2419
+ });
2420
+
2421
+ this.video.addEventListener('seeked', () => {
2422
+
2423
+ this.triggerEvent('seeked', {
2424
+ currentTime: this.getCurrentTime()
2356
2425
  });
2357
- this.video.addEventListener('ended', () => this.onVideoEnded());
2358
- this.video.addEventListener('loadstart', () => {
2359
- if (!this.isChangingQuality) {
2360
- this.showLoading();
2361
- }
2426
+ });
2427
+
2428
+
2429
+ this.video.addEventListener('loadstart', () => {
2430
+ if (!this.isChangingQuality) {
2431
+ this.showLoading();
2432
+ }
2433
+
2434
+ this.triggerEvent('loadstart');
2435
+ });
2436
+
2437
+ this.video.addEventListener('loadedmetadata', () => {
2438
+ this.updateDuration();
2439
+
2440
+
2441
+ this.triggerEvent('loadedmetadata', {
2442
+ duration: this.getDuration(),
2443
+ videoWidth: this.video.videoWidth,
2444
+ videoHeight: this.video.videoHeight
2362
2445
  });
2363
- this.video.addEventListener('loadeddata', () => {
2364
- if (!this.isChangingQuality) {
2365
- this.hideLoading();
2366
- }
2446
+
2447
+
2448
+ setTimeout(() => {
2449
+ this.initializeSubtitles();
2450
+ }, 100);
2451
+ });
2452
+
2453
+ this.video.addEventListener('loadeddata', () => {
2454
+ if (!this.isChangingQuality) {
2455
+ this.hideLoading();
2456
+ }
2457
+
2458
+ this.triggerEvent('loadeddata', {
2459
+ currentTime: this.getCurrentTime()
2460
+ });
2461
+ });
2462
+
2463
+ this.video.addEventListener('canplay', () => {
2464
+ if (!this.isChangingQuality) {
2465
+ this.hideLoading();
2466
+ }
2467
+
2468
+ this.triggerEvent('canplay', {
2469
+ currentTime: this.getCurrentTime(),
2470
+ duration: this.getDuration()
2367
2471
  });
2472
+ });
2473
+
2474
+ this.video.addEventListener('progress', () => {
2475
+ this.updateBuffer();
2476
+
2477
+ this.triggerEvent('progress', {
2478
+ buffered: this.getBufferedTime(),
2479
+ duration: this.getDuration()
2480
+ });
2481
+ });
2482
+
2483
+ this.video.addEventListener('durationchange', () => {
2484
+ this.updateDuration();
2485
+
2486
+ this.triggerEvent('durationchange', {
2487
+ duration: this.getDuration()
2488
+ });
2489
+ });
2490
+
2491
+
2492
+ this.video.addEventListener('error', (e) => {
2493
+ this.onVideoError(e);
2494
+
2495
+ this.triggerEvent('error', {
2496
+ code: this.video.error?.code,
2497
+ message: this.video.error?.message,
2498
+ src: this.video.currentSrc || this.video.src
2499
+ });
2500
+ });
2501
+
2502
+ this.video.addEventListener('stalled', () => {
2503
+
2504
+ this.triggerEvent('stalled', {
2505
+ currentTime: this.getCurrentTime()
2506
+ });
2507
+ });
2508
+
2509
+
2510
+ this.video.addEventListener('timeupdate', () => this.updateProgress());
2511
+
2512
+ this.video.addEventListener('ended', () => this.onVideoEnded());
2513
+
2368
2514
 
2369
2515
  this.video.addEventListener('click', () => {
2370
2516
  if (!this.options.pauseClick) return;
@@ -2471,6 +2617,10 @@ addEventListener(eventType, callback) {
2471
2617
 
2472
2618
  this.progressContainer.addEventListener('click', (e) => this.seek(e));
2473
2619
  this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
2620
+ if (this.progressHandle) {
2621
+ this.progressHandle.addEventListener('mousedown', this.startSeeking.bind(this));
2622
+ this.progressHandle.addEventListener('touchstart', this.startSeeking.bind(this), { passive: false });
2623
+ }
2474
2624
 
2475
2625
 
2476
2626
  this.progressContainer.addEventListener('touchstart', (e) => {
@@ -2889,13 +3039,13 @@ createControls() {
2889
3039
  const controlsHTML = `
2890
3040
  <div class="controls" id="${controlsId}">
2891
3041
  <div class="progress-container">
2892
- <div class="progress-bar">
2893
- <div class="progress-buffer"></div>
2894
- <div class="progress-filled"></div>
2895
- <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
2896
- </div>
2897
- ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
2898
- </div>
3042
+ <div class="progress-bar">
3043
+ <div class="progress-buffer"></div>
3044
+ <div class="progress-filled"></div>
3045
+ </div>
3046
+ <div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div> <!-- ✅ Fuori da progress-bar -->
3047
+ ${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
3048
+ </div>
2899
3049
 
2900
3050
  <div class="controls-main">
2901
3051
  <div class="controls-left">
@@ -4884,11 +5034,11 @@ updateSubtitlesButton() {
4884
5034
  var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
4885
5035
  if (!subtitlesBtn) return;
4886
5036
 
5037
+ subtitlesBtn.classList.remove('active');
5038
+
4887
5039
  if (this.subtitlesEnabled) {
4888
- subtitlesBtn.classList.add('active');
4889
5040
  subtitlesBtn.title = this.t('subtitlesdisable');
4890
5041
  } else {
4891
- subtitlesBtn.classList.remove('active');
4892
5042
  subtitlesBtn.title = this.t('subtitlesenable');
4893
5043
  }
4894
5044
  }
@@ -4924,14 +5074,6 @@ updateSubtitlesUI() {
4924
5074
  bindSubtitleEvents() {
4925
5075
  var self = this;
4926
5076
 
4927
- var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
4928
- if (subtitlesBtn) {
4929
- subtitlesBtn.addEventListener('click', function (e) {
4930
- e.stopPropagation();
4931
- self.toggleSubtitles();
4932
- });
4933
- }
4934
-
4935
5077
  var subtitlesMenu = this.controls && this.controls.querySelector('.subtitles-menu');
4936
5078
  if (subtitlesMenu) {
4937
5079
  subtitlesMenu.addEventListener('click', function (e) {
@@ -4940,6 +5082,7 @@ bindSubtitleEvents() {
4940
5082
  }
4941
5083
  }
4942
5084
 
5085
+
4943
5086
  handleSubtitlesMenuClick(e) {
4944
5087
  if (!e.target.classList.contains('subtitles-option')) return;
4945
5088
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myetv-player",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MYETV Video Player - Modular JavaScript and SCSS Build System",
5
5
  "main": "dist/myetv-player.js",
6
6
  "scripts": {
@@ -33,3 +33,5 @@
33
33
 
34
34
 
35
35
 
36
+
37
+