myetv-player 1.0.0 → 1.0.8
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/.github/workflows/codeql.yml +100 -0
- package/README.md +49 -58
- package/SECURITY.md +50 -0
- package/css/myetv-player.css +424 -219
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +1759 -1502
- package/dist/myetv-player.min.js +1705 -1469
- package/package.json +7 -1
- package/plugins/README.md +1016 -0
- package/plugins/cloudflare/README.md +1068 -0
- package/plugins/cloudflare/myetv-player-cloudflare-stream-plugin.js +556 -0
- package/plugins/facebook/README.md +1024 -0
- package/plugins/facebook/myetv-player-facebook-plugin.js +437 -0
- package/plugins/gamepad-remote-controller/README.md +816 -0
- package/plugins/gamepad-remote-controller/myetv-player-gamepad-remote-plugin.js +678 -0
- package/plugins/google-adsense-ads/README.md +1 -0
- package/plugins/google-adsense-ads/g-adsense-ads-plugin.js +158 -0
- package/plugins/google-ima-ads/README.md +1 -0
- package/plugins/google-ima-ads/g-ima-ads-plugin.js +355 -0
- package/plugins/twitch/README.md +1185 -0
- package/plugins/twitch/myetv-player-twitch-plugin.js +569 -0
- package/plugins/vast-vpaid-ads/README.md +1 -0
- package/plugins/vast-vpaid-ads/vast-vpaid-ads-plugin.js +346 -0
- package/plugins/vimeo/README.md +1416 -0
- package/plugins/vimeo/myetv-player-vimeo.js +640 -0
- package/plugins/youtube/README.md +851 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +1714 -210
- package/scss/README.md +160 -0
- package/scss/_controls.scss +184 -30
- package/scss/_menus.scss +840 -672
- package/scss/_responsive.scss +67 -105
- package/scss/_volume.scss +67 -105
- package/src/README.md +559 -0
- package/src/controls.js +17 -5
- package/src/core.js +1237 -1060
- package/src/i18n.js +27 -1
- package/src/quality.js +478 -436
- package/src/subtitles.js +2 -2
package/dist/myetv-player.min.js
CHANGED
|
@@ -279,14 +279,40 @@ class VideoPlayerI18n {
|
|
|
279
279
|
|
|
280
280
|
addTranslations(lang, translations) {
|
|
281
281
|
try {
|
|
282
|
+
|
|
283
|
+
if (!this.isValidLanguageKey(lang)) {
|
|
284
|
+
console.warn('Invalid language key rejected:', lang);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
282
288
|
if (!this.translations[lang]) {
|
|
283
289
|
this.translations[lang] = {};
|
|
284
290
|
}
|
|
285
|
-
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
for (const key in translations) {
|
|
294
|
+
if (translations.hasOwnProperty(key) && this.isValidLanguageKey(key)) {
|
|
295
|
+
this.translations[lang][key] = translations[key];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
286
298
|
} catch (error) {
|
|
287
299
|
console.warn('Error adding translations:', error);
|
|
288
300
|
}
|
|
289
301
|
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
isValidLanguageKey(key) {
|
|
305
|
+
if (typeof key !== 'string') return false;
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
|
|
309
|
+
if (dangerousKeys.includes(key.toLowerCase())) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
return /^[a-zA-Z0-9_-]+$/.test(key);
|
|
315
|
+
}
|
|
290
316
|
}
|
|
291
317
|
|
|
292
318
|
let VideoPlayerTranslations;
|
|
@@ -359,432 +385,435 @@ window.registerMYETVPlugin = registerPlugin;
|
|
|
359
385
|
class MYETVvideoplayer {
|
|
360
386
|
|
|
361
387
|
constructor(videoElement, options = {}) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (!this.video) {
|
|
367
|
-
throw new Error('Video element not found: ' + videoElement);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
this.options = {
|
|
371
|
-
showQualitySelector: true,
|
|
372
|
-
showSpeedControl: true,
|
|
373
|
-
showFullscreen: true,
|
|
374
|
-
showPictureInPicture: true,
|
|
375
|
-
showSubtitles: true,
|
|
376
|
-
subtitlesEnabled: false,
|
|
377
|
-
autoHide: true,
|
|
378
|
-
autoHideDelay: 3000,
|
|
379
|
-
poster: null,
|
|
380
|
-
showPosterOnEnd: false,
|
|
381
|
-
keyboardControls: true,
|
|
382
|
-
showSeekTooltip: true,
|
|
383
|
-
showTitleOverlay: false,
|
|
384
|
-
videoTitle: '',
|
|
385
|
-
persistentTitle: false,
|
|
386
|
-
debug: false,
|
|
387
|
-
autoplay: false,
|
|
388
|
-
defaultQuality: 'auto',
|
|
389
|
-
language: null,
|
|
390
|
-
pauseClick: true,
|
|
391
|
-
doubleTapPause: true,
|
|
392
|
-
brandLogoEnabled: false,
|
|
393
|
-
brandLogoUrl: '',
|
|
394
|
-
brandLogoLinkUrl: '',
|
|
395
|
-
playlistEnabled: true,
|
|
396
|
-
playlistAutoPlay: true,
|
|
397
|
-
playlistLoop: false,
|
|
398
|
-
loop: false,
|
|
399
|
-
volumeSlider: 'horizontal',
|
|
400
|
-
|
|
401
|
-
watermarkUrl: '',
|
|
402
|
-
watermarkLink: '',
|
|
403
|
-
watermarkPosition: 'bottomright',
|
|
404
|
-
watermarkTitle: '',
|
|
405
|
-
hideWatermark: true,
|
|
406
|
-
|
|
407
|
-
adaptiveStreaming: false,
|
|
408
|
-
dashLibUrl: 'https://cdn.dashjs.org/latest/dash.all.min.js',
|
|
409
|
-
hlsLibUrl: 'https://cdn.jsdelivr.net/npm/hls.js@latest',
|
|
410
|
-
adaptiveQualityControl: true,
|
|
411
|
-
|
|
412
|
-
audiofile: false,
|
|
413
|
-
audiowave: false,
|
|
414
|
-
|
|
415
|
-
resolution: "normal",
|
|
416
|
-
...options
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
this.isUserSeeking = false;
|
|
420
|
-
this.controlsTimeout = null;
|
|
421
|
-
this.titleTimeout = null;
|
|
422
|
-
this.currentQualityIndex = 0;
|
|
423
|
-
this.qualities = [];
|
|
424
|
-
this.originalSources = [];
|
|
425
|
-
this.isPiPSupported = this.checkPiPSupport();
|
|
426
|
-
this.seekTooltip = null;
|
|
427
|
-
this.titleOverlay = null;
|
|
428
|
-
this.isPlayerReady = false;
|
|
388
|
+
this.video = typeof videoElement === 'string'
|
|
389
|
+
? document.getElementById(videoElement)
|
|
390
|
+
: videoElement;
|
|
429
391
|
|
|
392
|
+
if (!this.video) {
|
|
393
|
+
throw new Error('Video element not found: ' + videoElement);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this.options = {
|
|
397
|
+
showQualitySelector: true,
|
|
398
|
+
showSpeedControl: true,
|
|
399
|
+
showFullscreen: true,
|
|
400
|
+
showPictureInPicture: true,
|
|
401
|
+
showSubtitles: true,
|
|
402
|
+
subtitlesEnabled: false,
|
|
403
|
+
autoHide: true,
|
|
404
|
+
autoHideDelay: 3000,
|
|
405
|
+
poster: null,
|
|
406
|
+
showPosterOnEnd: false,
|
|
407
|
+
keyboardControls: true,
|
|
408
|
+
showSeekTooltip: true,
|
|
409
|
+
showTitleOverlay: false,
|
|
410
|
+
videoTitle: '',
|
|
411
|
+
persistentTitle: false,
|
|
412
|
+
debug: false,
|
|
413
|
+
autoplay: false,
|
|
414
|
+
defaultQuality: 'auto',
|
|
415
|
+
language: null,
|
|
416
|
+
pauseClick: true,
|
|
417
|
+
doubleTapPause: true,
|
|
418
|
+
brandLogoEnabled: false,
|
|
419
|
+
brandLogoUrl: '',
|
|
420
|
+
brandLogoLinkUrl: '',
|
|
421
|
+
playlistEnabled: true,
|
|
422
|
+
playlistAutoPlay: true,
|
|
423
|
+
playlistLoop: false,
|
|
424
|
+
loop: false,
|
|
425
|
+
volumeSlider: 'show',
|
|
430
426
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
427
|
+
watermarkUrl: '',
|
|
428
|
+
watermarkLink: '',
|
|
429
|
+
watermarkPosition: 'bottomright',
|
|
430
|
+
watermarkTitle: '',
|
|
431
|
+
hideWatermark: true,
|
|
436
432
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
433
|
+
adaptiveStreaming: false,
|
|
434
|
+
dashLibUrl: 'https://cdn.dashjs.org/latest/dash.all.min.js',
|
|
435
|
+
hlsLibUrl: 'https://cdn.jsdelivr.net/npm/hls.js@latest',
|
|
436
|
+
adaptiveQualityControl: true,
|
|
441
437
|
|
|
442
|
-
|
|
443
|
-
this.currentPlayingQuality = null;
|
|
444
|
-
this.qualityMonitorInterval = null;
|
|
445
|
-
|
|
438
|
+
seekHandleShape: 'circle',
|
|
446
439
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
440
|
+
audiofile: false,
|
|
441
|
+
audiowave: false,
|
|
450
442
|
|
|
451
|
-
|
|
443
|
+
resolution: "normal",
|
|
444
|
+
...options
|
|
445
|
+
};
|
|
452
446
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
447
|
+
this.isUserSeeking = false;
|
|
448
|
+
this.controlsTimeout = null;
|
|
449
|
+
this.titleTimeout = null;
|
|
450
|
+
this.currentQualityIndex = 0;
|
|
451
|
+
this.qualities = [];
|
|
452
|
+
this.originalSources = [];
|
|
453
|
+
this.setupMenuToggles();
|
|
454
|
+
this.isPiPSupported = this.checkPiPSupport();
|
|
455
|
+
this.seekTooltip = null;
|
|
456
|
+
this.titleOverlay = null;
|
|
457
|
+
this.isPlayerReady = false;
|
|
458
458
|
|
|
459
|
-
|
|
460
|
-
|
|
459
|
+
|
|
460
|
+
this.textTracks = [];
|
|
461
|
+
this.currentSubtitleTrack = null;
|
|
462
|
+
this.subtitlesEnabled = false;
|
|
463
|
+
this.customSubtitleRenderer = null;
|
|
461
464
|
|
|
462
465
|
|
|
463
|
-
this.
|
|
466
|
+
this.chapters = [];
|
|
467
|
+
this.chapterMarkersContainer = null;
|
|
468
|
+
this.chapterTooltip = null;
|
|
464
469
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
'subtitlechange': [],
|
|
470
|
-
'chapterchange': [],
|
|
471
|
-
'pipchange': [],
|
|
472
|
-
'fullscreenchange': [],
|
|
473
|
-
'speedchange': [],
|
|
474
|
-
'timeupdate': [],
|
|
475
|
-
'volumechange': [],
|
|
476
|
-
'qualitychange': [],
|
|
477
|
-
'playlistchange': [],
|
|
478
|
-
'ended': []
|
|
479
|
-
};
|
|
470
|
+
|
|
471
|
+
this.selectedQuality = this.options.defaultQuality || 'auto';
|
|
472
|
+
this.currentPlayingQuality = null;
|
|
473
|
+
this.qualityMonitorInterval = null;
|
|
480
474
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
this.playlistId = null;
|
|
485
|
-
this.isPlaylistActive = false;
|
|
475
|
+
|
|
476
|
+
this.qualityChangeTimeout = null;
|
|
477
|
+
this.isChangingQuality = false;
|
|
486
478
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
this.hlsPlayer = null;
|
|
490
|
-
this.adaptiveStreamingType = null;
|
|
491
|
-
this.isAdaptiveStream = false;
|
|
492
|
-
this.adaptiveQualities = [];
|
|
493
|
-
this.librariesLoaded = {
|
|
494
|
-
dash: false,
|
|
495
|
-
hls: false
|
|
496
|
-
};
|
|
479
|
+
|
|
480
|
+
this.debugQuality = false;
|
|
497
481
|
|
|
498
|
-
|
|
482
|
+
|
|
483
|
+
this.autoHideTimer = null;
|
|
484
|
+
this.mouseOverControls = false;
|
|
485
|
+
this.autoHideDebug = false;
|
|
486
|
+
this.autoHideInitialized = false;
|
|
499
487
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
488
|
+
|
|
489
|
+
this.posterOverlay = null;
|
|
503
490
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
491
|
+
|
|
492
|
+
this.watermarkElement = null;
|
|
507
493
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
this.initAudioWave();
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
this.initAudioWave = function () {
|
|
529
|
-
if (!this.video) return;
|
|
494
|
+
|
|
495
|
+
this.eventCallbacks = {
|
|
496
|
+
'played': [],
|
|
497
|
+
'paused': [],
|
|
498
|
+
'subtitlechange': [],
|
|
499
|
+
'chapterchange': [],
|
|
500
|
+
'pipchange': [],
|
|
501
|
+
'fullscreenchange': [],
|
|
502
|
+
'speedchange': [],
|
|
503
|
+
'timeupdate': [],
|
|
504
|
+
'volumechange': [],
|
|
505
|
+
'qualitychange': [],
|
|
506
|
+
'playlistchange': [],
|
|
507
|
+
'ended': []
|
|
508
|
+
};
|
|
530
509
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
510
|
+
|
|
511
|
+
this.playlist = [];
|
|
512
|
+
this.currentPlaylistIndex = -1;
|
|
513
|
+
this.playlistId = null;
|
|
514
|
+
this.isPlaylistActive = false;
|
|
534
515
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
516
|
+
|
|
517
|
+
this.dashPlayer = null;
|
|
518
|
+
this.hlsPlayer = null;
|
|
519
|
+
this.adaptiveStreamingType = null;
|
|
520
|
+
this.isAdaptiveStream = false;
|
|
521
|
+
this.adaptiveQualities = [];
|
|
522
|
+
this.librariesLoaded = {
|
|
523
|
+
dash: false,
|
|
524
|
+
hls: false
|
|
525
|
+
};
|
|
538
526
|
|
|
539
|
-
|
|
540
|
-
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
541
|
-
this.audioCtx = new AudioContext();
|
|
542
|
-
this.analyser = this.audioCtx.createAnalyser();
|
|
543
|
-
this.source = this.audioCtx.createMediaElementSource(this.video);
|
|
544
|
-
this.source.connect(this.analyser);
|
|
545
|
-
this.analyser.connect(this.audioCtx.destination);
|
|
527
|
+
this.lastTimeUpdate = 0;
|
|
546
528
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
529
|
+
if (this.options.language && this.isI18nAvailable()) {
|
|
530
|
+
VideoPlayerTranslations.setLanguage(this.options.language);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (options.autoplay) {
|
|
534
|
+
this.video.autoplay = true;
|
|
535
|
+
}
|
|
550
536
|
|
|
537
|
+
try {
|
|
538
|
+
this.interceptAutoLoading();
|
|
539
|
+
this.createPlayerStructure();
|
|
540
|
+
this.initializeElements();
|
|
541
|
+
|
|
542
|
+
this.adaptToAudioFile = function () {
|
|
543
|
+
if (this.options.audiofile) {
|
|
551
544
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
545
|
+
if (this.video) {
|
|
546
|
+
this.video.style.display = 'none';
|
|
547
|
+
}
|
|
548
|
+
if (this.container) {
|
|
549
|
+
this.container.classList.add('audio-player');
|
|
550
|
+
}
|
|
551
|
+
if (this.options.audiowave) {
|
|
552
|
+
this.initAudioWave();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
this.initAudioWave = function () {
|
|
558
|
+
if (!this.video) return;
|
|
558
559
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
560
|
+
this.audioWaveCanvas = document.createElement('canvas');
|
|
561
|
+
this.audioWaveCanvas.className = 'audio-wave-canvas';
|
|
562
|
+
this.container.appendChild(this.audioWaveCanvas);
|
|
562
563
|
|
|
563
|
-
|
|
564
|
-
|
|
564
|
+
const canvasCtx = this.audioWaveCanvas.getContext('2d');
|
|
565
|
+
const WIDTH = this.audioWaveCanvas.width = this.container.clientWidth;
|
|
566
|
+
const HEIGHT = this.audioWaveCanvas.height = 60;
|
|
565
567
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
568
|
+
|
|
569
|
+
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
570
|
+
this.audioCtx = new AudioContext();
|
|
571
|
+
this.analyser = this.audioCtx.createAnalyser();
|
|
572
|
+
this.source = this.audioCtx.createMediaElementSource(this.video);
|
|
573
|
+
this.source.connect(this.analyser);
|
|
574
|
+
this.analyser.connect(this.audioCtx.destination);
|
|
569
575
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
canvasCtx.lineTo(x, y);
|
|
574
|
-
}
|
|
576
|
+
this.analyser.fftSize = 2048;
|
|
577
|
+
const bufferLength = this.analyser.fftSize;
|
|
578
|
+
const dataArray = new Uint8Array(bufferLength);
|
|
575
579
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
};
|
|
580
|
+
|
|
581
|
+
const draw = () => {
|
|
582
|
+
requestAnimationFrame(draw);
|
|
583
|
+
this.analyser.getByteTimeDomainData(dataArray);
|
|
581
584
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
this.adaptToAudioFile();
|
|
585
|
-
this.bindEvents();
|
|
585
|
+
canvasCtx.fillStyle = '#222';
|
|
586
|
+
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
|
|
586
587
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
588
|
+
canvasCtx.lineWidth = 2;
|
|
589
|
+
canvasCtx.strokeStyle = '#33ccff';
|
|
590
|
+
canvasCtx.beginPath();
|
|
590
591
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this.updateTooltips();
|
|
594
|
-
this.markPlayerReady();
|
|
595
|
-
this.initializePluginSystem();
|
|
596
|
-
this.restoreSourcesAsync();
|
|
592
|
+
const sliceWidth = WIDTH / bufferLength;
|
|
593
|
+
let x = 0;
|
|
597
594
|
|
|
598
|
-
|
|
599
|
-
|
|
595
|
+
for (let i = 0; i < bufferLength; i++) {
|
|
596
|
+
const v = dataArray[i] / 128.0;
|
|
597
|
+
const y = v * HEIGHT / 2;
|
|
600
598
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
599
|
+
if (i === 0) {
|
|
600
|
+
canvasCtx.moveTo(x, y);
|
|
601
|
+
} else {
|
|
602
|
+
canvasCtx.lineTo(x, y);
|
|
603
|
+
}
|
|
605
604
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
605
|
+
x += sliceWidth;
|
|
606
|
+
}
|
|
607
|
+
canvasCtx.lineTo(WIDTH, HEIGHT / 2);
|
|
608
|
+
canvasCtx.stroke();
|
|
609
|
+
};
|
|
610
610
|
|
|
611
|
-
|
|
612
|
-
return {
|
|
613
|
-
isPlaying: !this.isPaused(),
|
|
614
|
-
isPaused: this.isPaused(),
|
|
615
|
-
currentTime: this.getCurrentTime(),
|
|
616
|
-
duration: this.getDuration(),
|
|
617
|
-
volume: this.getVolume(),
|
|
618
|
-
isMuted: this.isMuted(),
|
|
619
|
-
playbackRate: this.getPlaybackRate(),
|
|
620
|
-
isFullscreen: this.isFullscreenActive(),
|
|
621
|
-
isPictureInPicture: this.isPictureInPictureActive(),
|
|
622
|
-
subtitlesEnabled: this.isSubtitlesEnabled(),
|
|
623
|
-
currentSubtitle: this.getCurrentSubtitleTrack(),
|
|
624
|
-
selectedQuality: this.getSelectedQuality(),
|
|
625
|
-
currentQuality: this.getCurrentPlayingQuality(),
|
|
626
|
-
isAutoQuality: this.isAutoQualityActive()
|
|
611
|
+
draw();
|
|
627
612
|
};
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
isI18nAvailable() {
|
|
631
|
-
return typeof VideoPlayerTranslations !== 'undefined' &&
|
|
632
|
-
VideoPlayerTranslations !== null &&
|
|
633
|
-
typeof VideoPlayerTranslations.t === 'function';
|
|
634
|
-
}
|
|
613
|
+
this.adaptToAudioFile();
|
|
614
|
+
this.bindEvents();
|
|
635
615
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
try {
|
|
639
|
-
return VideoPlayerTranslations.t(key);
|
|
640
|
-
} catch (error) {
|
|
641
|
-
if (this.options.debug) console.warn('Translation error:', error);
|
|
642
|
-
}
|
|
616
|
+
if (this.options.keyboardControls) {
|
|
617
|
+
this.setupKeyboardControls();
|
|
643
618
|
}
|
|
644
619
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
'picture_in_picture': 'Picture-in-Picture (P)',
|
|
652
|
-
'fullscreen': 'Fullscreen (F)',
|
|
653
|
-
'subtitles': 'Subtitles (S)',
|
|
654
|
-
'subtitles_enable': 'Enable subtitles',
|
|
655
|
-
'subtitles_disable': 'Disable subtitles',
|
|
656
|
-
'subtitles_off': 'Off',
|
|
657
|
-
'auto': 'Auto',
|
|
658
|
-
'brand_logo': 'Brand logo',
|
|
659
|
-
'next_video': 'Next video (N)',
|
|
660
|
-
'prev_video': 'Previous video (P)',
|
|
661
|
-
'playlist_next': 'Next',
|
|
662
|
-
'playlist_prev': 'Previous'
|
|
663
|
-
};
|
|
620
|
+
this.updateVolumeSliderVisual();
|
|
621
|
+
this.initVolumeTooltip();
|
|
622
|
+
this.updateTooltips();
|
|
623
|
+
this.markPlayerReady();
|
|
624
|
+
this.initializePluginSystem();
|
|
625
|
+
this.restoreSourcesAsync();
|
|
664
626
|
|
|
665
|
-
|
|
627
|
+
this.initializeSubtitles();
|
|
628
|
+
this.initializeQualityMonitoring();
|
|
629
|
+
|
|
630
|
+
this.initializeResolution();
|
|
631
|
+
this.initializeChapters();
|
|
632
|
+
this.initializePoster();
|
|
633
|
+
this.initializeWatermark();
|
|
634
|
+
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (this.options.debug) console.error('Video player initialization error:', error);
|
|
666
637
|
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
getPlayerState() {
|
|
641
|
+
return {
|
|
642
|
+
isPlaying: !this.isPaused(),
|
|
643
|
+
isPaused: this.isPaused(),
|
|
644
|
+
currentTime: this.getCurrentTime(),
|
|
645
|
+
duration: this.getDuration(),
|
|
646
|
+
volume: this.getVolume(),
|
|
647
|
+
isMuted: this.isMuted(),
|
|
648
|
+
playbackRate: this.getPlaybackRate(),
|
|
649
|
+
isFullscreen: this.isFullscreenActive(),
|
|
650
|
+
isPictureInPicture: this.isPictureInPictureActive(),
|
|
651
|
+
subtitlesEnabled: this.isSubtitlesEnabled(),
|
|
652
|
+
currentSubtitle: this.getCurrentSubtitleTrack(),
|
|
653
|
+
selectedQuality: this.getSelectedQuality(),
|
|
654
|
+
currentQuality: this.getCurrentPlayingQuality(),
|
|
655
|
+
isAutoQuality: this.isAutoQualityActive()
|
|
656
|
+
};
|
|
657
|
+
}
|
|
667
658
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
659
|
+
isI18nAvailable() {
|
|
660
|
+
return typeof VideoPlayerTranslations !== 'undefined' &&
|
|
661
|
+
VideoPlayerTranslations !== null &&
|
|
662
|
+
typeof VideoPlayerTranslations.t === 'function';
|
|
663
|
+
}
|
|
671
664
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
665
|
+
t(key) {
|
|
666
|
+
if (this.isI18nAvailable()) {
|
|
667
|
+
try {
|
|
668
|
+
return VideoPlayerTranslations.t(key);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
if (this.options.debug) console.warn('Translation error:', error);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const fallback = {
|
|
675
|
+
'play_pause': 'Play/Pause (Space)',
|
|
676
|
+
'mute_unmute': 'Mute/Unmute (M)',
|
|
677
|
+
'volume': 'Volume',
|
|
678
|
+
'playback_speed': 'Playback speed',
|
|
679
|
+
'video_quality': 'Video quality',
|
|
680
|
+
'picture_in_picture': 'Picture-in-Picture (P)',
|
|
681
|
+
'fullscreen': 'Fullscreen (F)',
|
|
682
|
+
'subtitles': 'Subtitles (S)',
|
|
683
|
+
'subtitles_enable': 'Enable subtitles',
|
|
684
|
+
'subtitles_disable': 'Disable subtitles',
|
|
685
|
+
'subtitles_off': 'Off',
|
|
686
|
+
'auto': 'Auto',
|
|
687
|
+
'brand_logo': 'Brand logo',
|
|
688
|
+
'next_video': 'Next video (N)',
|
|
689
|
+
'prev_video': 'Previous video (P)',
|
|
690
|
+
'playlist_next': 'Next',
|
|
691
|
+
'playlist_prev': 'Previous'
|
|
692
|
+
};
|
|
675
693
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
this.video.removeAttribute('src');
|
|
679
|
-
this.video.src = '';
|
|
680
|
-
}
|
|
694
|
+
return fallback[key] || key;
|
|
695
|
+
}
|
|
681
696
|
|
|
682
|
-
|
|
697
|
+
interceptAutoLoading() {
|
|
698
|
+
this.saveOriginalSources();
|
|
699
|
+
this.disableSources();
|
|
683
700
|
|
|
684
|
-
|
|
701
|
+
this.video.preload = 'none';
|
|
702
|
+
this.video.controls = false;
|
|
703
|
+
this.video.autoplay = false;
|
|
704
|
+
|
|
705
|
+
if (this.video.src && this.video.src !== window.location.href) {
|
|
706
|
+
this.originalSrc = this.video.src;
|
|
707
|
+
this.video.removeAttribute('src');
|
|
708
|
+
this.video.src = '';
|
|
685
709
|
}
|
|
686
710
|
|
|
687
|
-
|
|
688
|
-
const sources = this.video.querySelectorAll('source');
|
|
689
|
-
this.originalSources = [];
|
|
711
|
+
this.hideNativePlayer();
|
|
690
712
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
this.originalSources.push({
|
|
694
|
-
element: source,
|
|
695
|
-
src: source.src,
|
|
696
|
-
type: source.type || 'video/mp4',
|
|
697
|
-
quality: source.getAttribute('data-quality') || `quality-${index}`,
|
|
698
|
-
index: index
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
});
|
|
713
|
+
if (this.options.debug) console.log('📁 Sources temporarily disabled to prevent blocking');
|
|
714
|
+
}
|
|
702
715
|
|
|
703
|
-
|
|
704
|
-
|
|
716
|
+
saveOriginalSources() {
|
|
717
|
+
const sources = this.video.querySelectorAll('source');
|
|
718
|
+
this.originalSources = [];
|
|
719
|
+
|
|
720
|
+
sources.forEach((source, index) => {
|
|
721
|
+
if (source.src) {
|
|
722
|
+
this.originalSources.push({
|
|
723
|
+
element: source,
|
|
724
|
+
src: source.src,
|
|
725
|
+
type: source.type || 'video/mp4',
|
|
726
|
+
quality: source.getAttribute('data-quality') || `quality-${index}`,
|
|
727
|
+
index: index
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
});
|
|
705
731
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
sources.forEach(source => {
|
|
709
|
-
if (source.src) {
|
|
710
|
-
source.removeAttribute('src');
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
}
|
|
732
|
+
if (this.options.debug) console.log(`📁 Saved ${this.originalSources.length} sources originali:`, this.originalSources);
|
|
733
|
+
}
|
|
714
734
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
735
|
+
disableSources() {
|
|
736
|
+
const sources = this.video.querySelectorAll('source');
|
|
737
|
+
sources.forEach(source => {
|
|
738
|
+
if (source.src) {
|
|
739
|
+
source.removeAttribute('src');
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
720
743
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
744
|
+
restoreSourcesAsync() {
|
|
745
|
+
setTimeout(() => {
|
|
746
|
+
this.restoreSources();
|
|
747
|
+
}, 200);
|
|
748
|
+
}
|
|
725
749
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const firstSource = this.originalSources[0];
|
|
731
|
-
if (firstSource.src && this.detectStreamType(firstSource.src)) {
|
|
732
|
-
adaptiveSource = firstSource.src;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
750
|
+
async restoreSources() {
|
|
751
|
+
try {
|
|
752
|
+
|
|
753
|
+
let adaptiveSource = null;
|
|
735
754
|
|
|
755
|
+
if (this.originalSrc) {
|
|
756
|
+
adaptiveSource = this.originalSrc;
|
|
757
|
+
} else if (this.originalSources.length > 0) {
|
|
736
758
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (this.options.debug) console.log('📡 Adaptive streaming initialized');
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
759
|
+
const firstSource = this.originalSources[0];
|
|
760
|
+
if (firstSource.src && this.detectStreamType(firstSource.src)) {
|
|
761
|
+
adaptiveSource = firstSource.src;
|
|
743
762
|
}
|
|
763
|
+
}
|
|
744
764
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
765
|
+
|
|
766
|
+
if (adaptiveSource && this.options.adaptiveStreaming) {
|
|
767
|
+
const adaptiveInitialized = await this.initializeAdaptiveStreaming(adaptiveSource);
|
|
768
|
+
if (adaptiveInitialized) {
|
|
769
|
+
if (this.options.debug) console.log('📡 Adaptive streaming initialized');
|
|
770
|
+
return;
|
|
748
771
|
}
|
|
772
|
+
}
|
|
749
773
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
this.qualities = this.originalSources.map(s => ({
|
|
757
|
-
src: s.src,
|
|
758
|
-
quality: s.quality,
|
|
759
|
-
type: s.type
|
|
760
|
-
}));
|
|
774
|
+
|
|
775
|
+
if (this.originalSrc) {
|
|
776
|
+
this.video.src = this.originalSrc;
|
|
777
|
+
}
|
|
761
778
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
quality: 'default',
|
|
766
|
-
type: 'video/mp4'
|
|
767
|
-
});
|
|
779
|
+
this.originalSources.forEach(sourceData => {
|
|
780
|
+
if (sourceData.element && sourceData.src) {
|
|
781
|
+
sourceData.element.src = sourceData.src;
|
|
768
782
|
}
|
|
783
|
+
});
|
|
769
784
|
|
|
770
|
-
|
|
771
|
-
|
|
785
|
+
this.qualities = this.originalSources.map(s => ({
|
|
786
|
+
src: s.src,
|
|
787
|
+
quality: s.quality,
|
|
788
|
+
type: s.type
|
|
789
|
+
}));
|
|
772
790
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
791
|
+
if (this.originalSrc && this.qualities.length === 0) {
|
|
792
|
+
this.qualities.push({
|
|
793
|
+
src: this.originalSrc,
|
|
794
|
+
quality: 'default',
|
|
795
|
+
type: 'video/mp4'
|
|
796
|
+
});
|
|
797
|
+
}
|
|
781
798
|
|
|
782
|
-
|
|
799
|
+
if (this.qualities.length > 0) {
|
|
800
|
+
this.video.load();
|
|
783
801
|
|
|
784
|
-
|
|
785
|
-
|
|
802
|
+
|
|
803
|
+
this.video.addEventListener('loadedmetadata', () => {
|
|
804
|
+
setTimeout(() => {
|
|
805
|
+
this.reinitializeSubtitles();
|
|
806
|
+
if (this.options.debug) console.log('🔄 Subtitles re-initialized after video load');
|
|
807
|
+
}, 300);
|
|
808
|
+
}, { once: true });
|
|
786
809
|
}
|
|
810
|
+
|
|
811
|
+
if (this.options.debug) console.log('✅ Sources ripristinate:', this.qualities);
|
|
812
|
+
|
|
813
|
+
} catch (error) {
|
|
814
|
+
if (this.options.debug) console.error('❌ Errore ripristino sources:', error);
|
|
787
815
|
}
|
|
816
|
+
}
|
|
788
817
|
|
|
789
818
|
reinitializeSubtitles() {
|
|
790
819
|
if (this.options.debug) console.log('🔄 Re-initializing subtitles...');
|
|
@@ -830,629 +859,697 @@ getDefaultSubtitleTrack() {
|
|
|
830
859
|
return -1;
|
|
831
860
|
}
|
|
832
861
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
862
|
+
markPlayerReady() {
|
|
863
|
+
setTimeout(() => {
|
|
864
|
+
this.isPlayerReady = true;
|
|
865
|
+
if (this.container) {
|
|
866
|
+
this.container.classList.add('player-initialized');
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (this.video) {
|
|
870
|
+
this.video.style.visibility = '';
|
|
871
|
+
this.video.style.opacity = '';
|
|
872
|
+
this.video.style.pointerEvents = '';
|
|
873
|
+
}
|
|
839
874
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
this.
|
|
875
|
+
|
|
876
|
+
setTimeout(() => {
|
|
877
|
+
if (this.options.autoHide && !this.autoHideInitialized) {
|
|
878
|
+
this.initAutoHide();
|
|
844
879
|
}
|
|
845
880
|
|
|
846
881
|
|
|
847
|
-
|
|
848
|
-
if (this.options.
|
|
849
|
-
this.initAutoHide();
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
if (this.selectedQuality && this.qualities && this.qualities.length > 0) {
|
|
854
|
-
if (this.options.debug) console.log(`🎯 Applying defaultQuality: "${this.selectedQuality}"`);
|
|
882
|
+
if (this.selectedQuality && this.qualities && this.qualities.length > 0) {
|
|
883
|
+
if (this.options.debug) console.log(`🎯 Applying defaultQuality: "${this.selectedQuality}"`);
|
|
855
884
|
|
|
856
|
-
|
|
857
|
-
|
|
885
|
+
if (this.selectedQuality === 'auto') {
|
|
886
|
+
this.enableAutoQuality();
|
|
887
|
+
} else {
|
|
888
|
+
|
|
889
|
+
const requestedQuality = this.qualities.find(q => q.quality === this.selectedQuality);
|
|
890
|
+
if (requestedQuality) {
|
|
891
|
+
if (this.options.debug) console.log(`✅ Quality "${this.selectedQuality}" available`);
|
|
892
|
+
this.setQuality(this.selectedQuality);
|
|
858
893
|
} else {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (this.options.debug) console.log(`✅ Quality "${this.selectedQuality}" available`);
|
|
863
|
-
this.setQuality(this.selectedQuality);
|
|
864
|
-
} else {
|
|
865
|
-
if (this.options.debug) console.warn(`⚠️ Quality "${this.selectedQuality}" not available - fallback to auto`);
|
|
866
|
-
if (this.options.debug) console.log('📋 Available qualities:', this.qualities.map(q => q.quality));
|
|
867
|
-
this.enableAutoQuality();
|
|
868
|
-
}
|
|
894
|
+
if (this.options.debug) console.warn(`⚠️ Quality "${this.selectedQuality}" not available - fallback to auto`);
|
|
895
|
+
if (this.options.debug) console.log('📋 Available qualities:', this.qualities.map(q => q.quality));
|
|
896
|
+
this.enableAutoQuality();
|
|
869
897
|
}
|
|
870
898
|
}
|
|
899
|
+
}
|
|
871
900
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
901
|
+
|
|
902
|
+
if (this.options.autoplay) {
|
|
903
|
+
if (this.options.debug) console.log('🎬 Autoplay enabled');
|
|
904
|
+
setTimeout(() => {
|
|
905
|
+
this.video.play().catch(error => {
|
|
906
|
+
if (this.options.debug) console.warn('⚠️ Autoplay blocked:', error);
|
|
907
|
+
});
|
|
908
|
+
}, 100);
|
|
909
|
+
}
|
|
910
|
+
}, 200);
|
|
882
911
|
|
|
883
|
-
|
|
884
|
-
|
|
912
|
+
}, 100);
|
|
913
|
+
}
|
|
885
914
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
915
|
+
createPlayerStructure() {
|
|
916
|
+
let wrapper = this.video.closest('.video-wrapper');
|
|
917
|
+
if (!wrapper) {
|
|
918
|
+
wrapper = document.createElement('div');
|
|
919
|
+
wrapper.className = 'video-wrapper';
|
|
920
|
+
this.video.parentNode.insertBefore(wrapper, this.video);
|
|
921
|
+
wrapper.appendChild(this.video);
|
|
922
|
+
}
|
|
894
923
|
|
|
895
|
-
|
|
924
|
+
this.container = wrapper;
|
|
896
925
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
926
|
+
this.createInitialLoading();
|
|
927
|
+
this.createLoadingOverlay();
|
|
928
|
+
this.collectVideoQualities();
|
|
929
|
+
this.createControls();
|
|
930
|
+
this.createBrandLogo();
|
|
931
|
+
this.detectPlaylist();
|
|
903
932
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
}
|
|
933
|
+
if (this.options.showTitleOverlay) {
|
|
934
|
+
this.createTitleOverlay();
|
|
907
935
|
}
|
|
936
|
+
}
|
|
908
937
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
938
|
+
createInitialLoading() {
|
|
939
|
+
const initialLoader = document.createElement('div');
|
|
940
|
+
initialLoader.className = 'initial-loading';
|
|
941
|
+
initialLoader.innerHTML = '<div class="loading-spinner"></div>';
|
|
942
|
+
this.container.appendChild(initialLoader);
|
|
943
|
+
this.initialLoading = initialLoader;
|
|
944
|
+
}
|
|
916
945
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
946
|
+
collectVideoQualities() {
|
|
947
|
+
if (this.options.debug) console.log('📁 Video qualities will be loaded with restored sources');
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
createLoadingOverlay() {
|
|
951
|
+
const overlay = document.createElement('div');
|
|
952
|
+
overlay.className = 'loading-overlay';
|
|
953
|
+
overlay.id = 'loadingOverlay-' + this.getUniqueId();
|
|
954
|
+
overlay.innerHTML = '<div class="loading-spinner"></div>';
|
|
955
|
+
this.container.appendChild(overlay);
|
|
956
|
+
this.loadingOverlay = overlay;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
createTitleOverlay() {
|
|
960
|
+
const overlay = document.createElement('div');
|
|
961
|
+
overlay.className = 'title-overlay';
|
|
962
|
+
overlay.id = 'titleOverlay-' + this.getUniqueId();
|
|
963
|
+
|
|
964
|
+
const titleText = document.createElement('h2');
|
|
965
|
+
titleText.className = 'title-text';
|
|
966
|
+
titleText.textContent = this.options.videoTitle || '';
|
|
967
|
+
|
|
968
|
+
overlay.appendChild(titleText);
|
|
920
969
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
overlay.id = 'loadingOverlay-' + this.getUniqueId();
|
|
925
|
-
overlay.innerHTML = '<div class="loading-spinner"></div>';
|
|
970
|
+
if (this.controls) {
|
|
971
|
+
this.container.insertBefore(overlay, this.controls);
|
|
972
|
+
} else {
|
|
926
973
|
this.container.appendChild(overlay);
|
|
927
|
-
this.loadingOverlay = overlay;
|
|
928
974
|
}
|
|
929
975
|
|
|
930
|
-
|
|
931
|
-
const overlay = document.createElement('div');
|
|
932
|
-
overlay.className = 'title-overlay';
|
|
933
|
-
overlay.id = 'titleOverlay-' + this.getUniqueId();
|
|
976
|
+
this.titleOverlay = overlay;
|
|
934
977
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
overlay.appendChild(titleText);
|
|
978
|
+
if (this.options.persistentTitle && this.options.videoTitle) {
|
|
979
|
+
this.showTitleOverlay();
|
|
980
|
+
}
|
|
981
|
+
}
|
|
940
982
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
} else {
|
|
944
|
-
this.container.appendChild(overlay);
|
|
945
|
-
}
|
|
983
|
+
updateTooltips() {
|
|
984
|
+
if (!this.controls) return;
|
|
946
985
|
|
|
947
|
-
|
|
986
|
+
try {
|
|
987
|
+
this.controls.querySelectorAll('[data-tooltip]').forEach(element => {
|
|
988
|
+
const key = element.getAttribute('data-tooltip');
|
|
989
|
+
element.title = this.t(key);
|
|
990
|
+
});
|
|
948
991
|
|
|
949
|
-
|
|
950
|
-
|
|
992
|
+
const autoOption = this.controls.querySelector('.quality-option[data-quality="auto"]');
|
|
993
|
+
if (autoOption) {
|
|
994
|
+
autoOption.textContent = this.t('auto');
|
|
951
995
|
}
|
|
996
|
+
} catch (error) {
|
|
997
|
+
if (this.options.debug) console.warn('Errore aggiornamento tooltip:', error);
|
|
952
998
|
}
|
|
999
|
+
}
|
|
953
1000
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1001
|
+
setLanguage(lang) {
|
|
1002
|
+
if (this.isI18nAvailable()) {
|
|
957
1003
|
try {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
const autoOption = this.controls.querySelector('.quality-option[data-quality="auto"]');
|
|
964
|
-
if (autoOption) {
|
|
965
|
-
autoOption.textContent = this.t('auto');
|
|
1004
|
+
if (VideoPlayerTranslations.setLanguage(lang)) {
|
|
1005
|
+
this.updateTooltips();
|
|
1006
|
+
return true;
|
|
966
1007
|
}
|
|
967
1008
|
} catch (error) {
|
|
968
|
-
if (this.options.debug) console.warn('Errore
|
|
1009
|
+
if (this.options.debug) console.warn('Errore cambio lingua:', error);
|
|
969
1010
|
}
|
|
970
1011
|
}
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
971
1014
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1015
|
+
setVideoTitle(title) {
|
|
1016
|
+
this.options.videoTitle = title || '';
|
|
1017
|
+
|
|
1018
|
+
if (this.titleOverlay) {
|
|
1019
|
+
const titleElement = this.titleOverlay.querySelector('.title-text');
|
|
1020
|
+
if (titleElement) {
|
|
1021
|
+
titleElement.textContent = this.options.videoTitle;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (title) {
|
|
1025
|
+
this.showTitleOverlay();
|
|
1026
|
+
|
|
1027
|
+
if (!this.options.persistentTitle) {
|
|
1028
|
+
this.clearTitleTimeout();
|
|
1029
|
+
this.titleTimeout = setTimeout(() => {
|
|
1030
|
+
this.hideTitleOverlay();
|
|
1031
|
+
}, 3000);
|
|
981
1032
|
}
|
|
982
1033
|
}
|
|
983
|
-
return false;
|
|
984
1034
|
}
|
|
985
1035
|
|
|
986
|
-
|
|
987
|
-
|
|
1036
|
+
return this;
|
|
1037
|
+
}
|
|
988
1038
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
titleElement.textContent = this.options.videoTitle;
|
|
993
|
-
}
|
|
1039
|
+
getVideoTitle() {
|
|
1040
|
+
return this.options.videoTitle;
|
|
1041
|
+
}
|
|
994
1042
|
|
|
995
|
-
|
|
996
|
-
|
|
1043
|
+
setPersistentTitle(persistent) {
|
|
1044
|
+
this.options.persistentTitle = persistent;
|
|
997
1045
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1046
|
+
if (this.titleOverlay && this.options.videoTitle) {
|
|
1047
|
+
if (persistent) {
|
|
1048
|
+
this.showTitleOverlay();
|
|
1049
|
+
this.clearTitleTimeout();
|
|
1050
|
+
} else {
|
|
1051
|
+
this.titleOverlay.classList.remove('persistent');
|
|
1052
|
+
if (this.titleOverlay.classList.contains('show')) {
|
|
1053
|
+
this.clearTitleTimeout();
|
|
1054
|
+
this.titleTimeout = setTimeout(() => {
|
|
1055
|
+
this.hideTitleOverlay();
|
|
1056
|
+
}, 3000);
|
|
1004
1057
|
}
|
|
1005
1058
|
}
|
|
1059
|
+
}
|
|
1006
1060
|
|
|
1007
|
-
|
|
1061
|
+
return this;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
enableTitleOverlay() {
|
|
1065
|
+
if (!this.titleOverlay && !this.options.showTitleOverlay) {
|
|
1066
|
+
this.options.showTitleOverlay = true;
|
|
1067
|
+
this.createTitleOverlay();
|
|
1008
1068
|
}
|
|
1069
|
+
return this;
|
|
1070
|
+
}
|
|
1009
1071
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1072
|
+
disableTitleOverlay() {
|
|
1073
|
+
if (this.titleOverlay) {
|
|
1074
|
+
this.titleOverlay.remove();
|
|
1075
|
+
this.titleOverlay = null;
|
|
1012
1076
|
}
|
|
1077
|
+
this.options.showTitleOverlay = false;
|
|
1078
|
+
return this;
|
|
1079
|
+
}
|
|
1013
1080
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1081
|
+
getUniqueId() {
|
|
1082
|
+
return Math.random().toString(36).substr(2, 9);
|
|
1083
|
+
}
|
|
1016
1084
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1085
|
+
initializeElements() {
|
|
1086
|
+
this.progressContainer = this.controls?.querySelector('.progress-container');
|
|
1087
|
+
this.progressFilled = this.controls?.querySelector('.progress-filled');
|
|
1088
|
+
this.progressBuffer = this.controls?.querySelector('.progress-buffer');
|
|
1089
|
+
this.progressHandle = this.controls?.querySelector('.progress-handle');
|
|
1090
|
+
this.seekTooltip = this.controls?.querySelector('.seek-tooltip');
|
|
1091
|
+
|
|
1092
|
+
this.playPauseBtn = this.controls?.querySelector('.play-pause-btn');
|
|
1093
|
+
this.muteBtn = this.controls?.querySelector('.mute-btn');
|
|
1094
|
+
this.fullscreenBtn = this.controls?.querySelector('.fullscreen-btn');
|
|
1095
|
+
this.speedBtn = this.controls?.querySelector('.speed-btn');
|
|
1096
|
+
this.qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
1097
|
+
this.pipBtn = this.controls?.querySelector('.pip-btn');
|
|
1098
|
+
this.subtitlesBtn = this.controls?.querySelector('.subtitles-btn');
|
|
1099
|
+
this.playlistPrevBtn = this.controls?.querySelector('.playlist-prev-btn');
|
|
1100
|
+
this.playlistNextBtn = this.controls?.querySelector('.playlist-next-btn');
|
|
1101
|
+
|
|
1102
|
+
this.playIcon = this.controls?.querySelector('.play-icon');
|
|
1103
|
+
this.pauseIcon = this.controls?.querySelector('.pause-icon');
|
|
1104
|
+
this.volumeIcon = this.controls?.querySelector('.volume-icon');
|
|
1105
|
+
this.muteIcon = this.controls?.querySelector('.mute-icon');
|
|
1106
|
+
this.fullscreenIcon = this.controls?.querySelector('.fullscreen-icon');
|
|
1107
|
+
this.exitFullscreenIcon = this.controls?.querySelector('.exit-fullscreen-icon');
|
|
1108
|
+
this.pipIcon = this.controls?.querySelector('.pip-icon');
|
|
1109
|
+
this.pipExitIcon = this.controls?.querySelector('.pip-exit-icon');
|
|
1110
|
+
|
|
1111
|
+
this.volumeSlider = this.controls?.querySelector('.volume-slider');
|
|
1112
|
+
this.currentTimeEl = this.controls?.querySelector('.current-time');
|
|
1113
|
+
this.durationEl = this.controls?.querySelector('.duration');
|
|
1114
|
+
this.speedMenu = this.controls?.querySelector('.speed-menu');
|
|
1115
|
+
this.qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
1116
|
+
this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
|
|
1117
|
+
}
|
|
1031
1118
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1119
|
+
closeAllMenus() {
|
|
1120
|
+
|
|
1121
|
+
const allMenus = this.controls?.querySelectorAll('[class*="-menu"].active');
|
|
1122
|
+
allMenus?.forEach(menu => {
|
|
1123
|
+
menu.classList.remove('active');
|
|
1124
|
+
});
|
|
1034
1125
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
}
|
|
1126
|
+
|
|
1127
|
+
const allButtons = this.controls?.querySelectorAll('.control-btn.active');
|
|
1128
|
+
allButtons?.forEach(btn => {
|
|
1129
|
+
btn.classList.remove('active');
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1042
1132
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
return this;
|
|
1050
|
-
}
|
|
1133
|
+
setupMenuToggles() {
|
|
1134
|
+
|
|
1135
|
+
if (this.controls) {
|
|
1136
|
+
this.controls.addEventListener('click', (e) => {
|
|
1137
|
+
|
|
1138
|
+
const button = e.target.closest('.control-btn');
|
|
1051
1139
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1140
|
+
if (!button) return;
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
const buttonClasses = button.className.split(' ');
|
|
1144
|
+
let menuClass = null;
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
for (const cls of buttonClasses) {
|
|
1148
|
+
if (cls.endsWith('-btn')) {
|
|
1149
|
+
const menuName = cls.replace('-btn', '-menu');
|
|
1150
|
+
const menu = this.controls.querySelector('.' + menuName);
|
|
1151
|
+
if (menu) {
|
|
1152
|
+
menuClass = menuName;
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1055
1157
|
|
|
1056
|
-
|
|
1057
|
-
this.progressContainer = this.controls?.querySelector('.progress-container');
|
|
1058
|
-
this.progressFilled = this.controls?.querySelector('.progress-filled');
|
|
1059
|
-
this.progressBuffer = this.controls?.querySelector('.progress-buffer');
|
|
1060
|
-
this.progressHandle = this.controls?.querySelector('.progress-handle');
|
|
1061
|
-
this.seekTooltip = this.controls?.querySelector('.seek-tooltip');
|
|
1158
|
+
if (!menuClass) return;
|
|
1062
1159
|
|
|
1063
|
-
|
|
1064
|
-
this.muteBtn = this.controls?.querySelector('.mute-btn');
|
|
1065
|
-
this.fullscreenBtn = this.controls?.querySelector('.fullscreen-btn');
|
|
1066
|
-
this.speedBtn = this.controls?.querySelector('.speed-btn');
|
|
1067
|
-
this.qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
1068
|
-
this.pipBtn = this.controls?.querySelector('.pip-btn');
|
|
1069
|
-
this.subtitlesBtn = this.controls?.querySelector('.subtitles-btn');
|
|
1070
|
-
this.playlistPrevBtn = this.controls?.querySelector('.playlist-prev-btn');
|
|
1071
|
-
this.playlistNextBtn = this.controls?.querySelector('.playlist-next-btn');
|
|
1160
|
+
e.stopPropagation();
|
|
1072
1161
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
this.pipIcon = this.controls?.querySelector('.pip-icon');
|
|
1080
|
-
this.pipExitIcon = this.controls?.querySelector('.pip-exit-icon');
|
|
1162
|
+
|
|
1163
|
+
const menu = this.controls.querySelector('.' + menuClass);
|
|
1164
|
+
const isOpen = menu.classList.contains('active');
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
this.closeAllMenus();
|
|
1081
1168
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1169
|
+
|
|
1170
|
+
if (!isOpen) {
|
|
1171
|
+
menu.classList.add('active');
|
|
1172
|
+
button.classList.add('active');
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1088
1175
|
}
|
|
1089
1176
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1177
|
+
|
|
1178
|
+
document.addEventListener('click', (e) => {
|
|
1179
|
+
if (!this.controls?.contains(e.target)) {
|
|
1180
|
+
this.closeAllMenus();
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
updateVolumeSliderVisual() {
|
|
1186
|
+
if (!this.video || !this.container) return;
|
|
1092
1187
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1188
|
+
const volume = this.video.muted ? 0 : this.video.volume;
|
|
1189
|
+
const percentage = Math.round(volume * 100);
|
|
1095
1190
|
|
|
1096
|
-
|
|
1191
|
+
this.container.style.setProperty('--player-volume-fill', percentage + '%');
|
|
1097
1192
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1193
|
+
if (this.volumeSlider) {
|
|
1194
|
+
this.volumeSlider.value = percentage;
|
|
1101
1195
|
}
|
|
1196
|
+
}
|
|
1102
1197
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1198
|
+
createVolumeTooltip() {
|
|
1199
|
+
const volumeContainer = this.controls?.querySelector('.volume-container');
|
|
1200
|
+
if (!volumeContainer || volumeContainer.querySelector('.volume-tooltip')) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1108
1203
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1204
|
+
const tooltip = document.createElement('div');
|
|
1205
|
+
tooltip.className = 'volume-tooltip';
|
|
1206
|
+
tooltip.textContent = '50%';
|
|
1207
|
+
volumeContainer.appendChild(tooltip);
|
|
1113
1208
|
|
|
1114
|
-
|
|
1209
|
+
this.volumeTooltip = tooltip;
|
|
1115
1210
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1211
|
+
if (this.options.debug) {
|
|
1212
|
+
console.log('Dynamic volume tooltip created');
|
|
1119
1213
|
}
|
|
1214
|
+
}
|
|
1120
1215
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1216
|
+
updateVolumeTooltip() {
|
|
1217
|
+
if (!this.volumeTooltip || !this.video) return;
|
|
1123
1218
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1219
|
+
const volume = Math.round(this.video.volume * 100);
|
|
1220
|
+
this.volumeTooltip.textContent = volume + '%';
|
|
1126
1221
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1222
|
+
|
|
1223
|
+
this.updateVolumeTooltipPosition(this.video.volume);
|
|
1129
1224
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}
|
|
1225
|
+
if (this.options.debug) {
|
|
1226
|
+
console.log('Volume tooltip updated:', volume + '%');
|
|
1133
1227
|
}
|
|
1228
|
+
}
|
|
1134
1229
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1230
|
+
updateVolumeTooltipPosition(volumeValue = null) {
|
|
1231
|
+
if (!this.volumeTooltip || !this.video) return;
|
|
1137
1232
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1233
|
+
const volumeSlider = this.controls?.querySelector('.volume-slider');
|
|
1234
|
+
if (!volumeSlider) return;
|
|
1140
1235
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1236
|
+
|
|
1237
|
+
if (volumeValue === null) {
|
|
1238
|
+
volumeValue = this.video.volume;
|
|
1239
|
+
}
|
|
1145
1240
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1241
|
+
|
|
1242
|
+
const sliderRect = volumeSlider.getBoundingClientRect();
|
|
1243
|
+
const sliderWidth = sliderRect.width;
|
|
1149
1244
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1245
|
+
|
|
1246
|
+
const thumbSize = 14;
|
|
1152
1247
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
const availableWidth = sliderWidth - thumbSize;
|
|
1251
|
+
const thumbCenterPosition = (thumbSize / 2) + (availableWidth * volumeValue);
|
|
1157
1252
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1253
|
+
|
|
1254
|
+
const percentage = (thumbCenterPosition / sliderWidth) * 100;
|
|
1160
1255
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1256
|
+
|
|
1257
|
+
this.volumeTooltip.style.left = percentage + '%';
|
|
1163
1258
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1259
|
+
if (this.options.debug) {
|
|
1260
|
+
console.log('Volume tooltip position updated:', {
|
|
1261
|
+
volumeValue: volumeValue,
|
|
1262
|
+
percentage: percentage + '%',
|
|
1263
|
+
thumbCenter: thumbCenterPosition,
|
|
1264
|
+
sliderWidth: sliderWidth
|
|
1265
|
+
});
|
|
1172
1266
|
}
|
|
1267
|
+
}
|
|
1173
1268
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
this.setupVolumeTooltipEvents();
|
|
1269
|
+
initVolumeTooltip() {
|
|
1270
|
+
this.createVolumeTooltip();
|
|
1177
1271
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1272
|
+
|
|
1273
|
+
setTimeout(() => {
|
|
1274
|
+
if (this.volumeTooltip && this.video) {
|
|
1275
|
+
this.updateVolumeTooltipPosition(this.video.volume);
|
|
1276
|
+
this.updateVolumeTooltip();
|
|
1277
|
+
}
|
|
1278
|
+
}, 50);
|
|
1279
|
+
}
|
|
1186
1280
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1281
|
+
updateVolumeSliderVisualWithTooltip() {
|
|
1282
|
+
const volumeSlider = this.controls?.querySelector('.volume-slider');
|
|
1283
|
+
if (!volumeSlider || !this.video) return;
|
|
1190
1284
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1285
|
+
const volume = this.video.volume || 0;
|
|
1286
|
+
const percentage = Math.round(volume * 100);
|
|
1193
1287
|
|
|
1194
|
-
|
|
1288
|
+
volumeSlider.value = volume;
|
|
1195
1289
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1290
|
+
|
|
1291
|
+
const volumeFillPercentage = percentage + '%';
|
|
1292
|
+
volumeSlider.style.setProperty('--player-volume-fill', volumeFillPercentage);
|
|
1199
1293
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1294
|
+
|
|
1295
|
+
this.updateVolumeTooltip();
|
|
1202
1296
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1297
|
+
if (this.options.debug) {
|
|
1298
|
+
console.log('Volume slider aggiornato:', {
|
|
1299
|
+
volume: volume,
|
|
1300
|
+
percentage: percentage,
|
|
1301
|
+
fillPercentage: volumeFillPercentage
|
|
1302
|
+
});
|
|
1210
1303
|
}
|
|
1304
|
+
}
|
|
1211
1305
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1306
|
+
|
|
1307
|
+
setMobileVolumeSlider(mode) {
|
|
1308
|
+
if (!['show', 'hide'].includes(mode)) {
|
|
1309
|
+
if (this.options.debug) console.warn('Invalid mobile volume slider mode:', mode);
|
|
1215
1310
|
return this;
|
|
1216
1311
|
}
|
|
1217
1312
|
|
|
1218
|
-
this.options.
|
|
1313
|
+
this.options.mobileVolumeSlider = mode;
|
|
1219
1314
|
const volumeContainer = this.controls?.querySelector('.volume-container');
|
|
1220
1315
|
if (volumeContainer) {
|
|
1221
|
-
|
|
1316
|
+
|
|
1317
|
+
volumeContainer.setAttribute('data-mobile-slider', mode);
|
|
1318
|
+
if (this.options.debug) console.log('Mobile volume slider set to:', mode);
|
|
1222
1319
|
}
|
|
1223
|
-
|
|
1224
|
-
if (this.options.debug) console.log('Volume slider orientation set to:', orientation);
|
|
1225
1320
|
return this;
|
|
1226
1321
|
}
|
|
1227
1322
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1323
|
+
|
|
1324
|
+
getMobileVolumeSlider() {
|
|
1325
|
+
return this.options.mobileVolumeSlider;
|
|
1230
1326
|
}
|
|
1231
1327
|
|
|
1328
|
+
initVolumeTooltip() {
|
|
1232
1329
|
|
|
1233
|
-
|
|
1330
|
+
this.createVolumeTooltip();
|
|
1234
1331
|
|
|
1235
|
-
|
|
1332
|
+
setTimeout(() => {
|
|
1333
|
+
this.updateVolumeTooltip();
|
|
1334
|
+
}, 200);
|
|
1236
1335
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1336
|
+
if (this.options.debug) {
|
|
1337
|
+
console.log('Dynamic volume tooltip inizializzation');
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1239
1340
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}, 200);
|
|
1341
|
+
setupSeekTooltip() {
|
|
1342
|
+
if (!this.options.showSeekTooltip || !this.progressContainer || !this.seekTooltip) return;
|
|
1243
1343
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1344
|
+
this.progressContainer.addEventListener('mouseenter', () => {
|
|
1345
|
+
if (this.seekTooltip) {
|
|
1346
|
+
this.seekTooltip.classList.add('visible');
|
|
1246
1347
|
}
|
|
1247
|
-
}
|
|
1348
|
+
});
|
|
1248
1349
|
|
|
1249
|
-
|
|
1250
|
-
if (
|
|
1350
|
+
this.progressContainer.addEventListener('mouseleave', () => {
|
|
1351
|
+
if (this.seekTooltip) {
|
|
1352
|
+
this.seekTooltip.classList.remove('visible');
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1251
1355
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
});
|
|
1356
|
+
this.progressContainer.addEventListener('mousemove', (e) => {
|
|
1357
|
+
this.updateSeekTooltip(e);
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1257
1360
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
this.seekTooltip.classList.remove('visible');
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1361
|
+
updateSeekTooltip(e) {
|
|
1362
|
+
if (!this.seekTooltip || !this.progressContainer || !this.video || !this.video.duration) return;
|
|
1263
1363
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1364
|
+
const rect = this.progressContainer.getBoundingClientRect();
|
|
1365
|
+
const clickX = e.clientX - rect.left;
|
|
1366
|
+
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
1367
|
+
const targetTime = percentage * this.video.duration;
|
|
1268
1368
|
|
|
1269
|
-
|
|
1270
|
-
if (!this.seekTooltip || !this.progressContainer || !this.video || !this.video.duration) return;
|
|
1369
|
+
this.seekTooltip.textContent = this.formatTime(targetTime);
|
|
1271
1370
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
1275
|
-
const targetTime = percentage * this.video.duration;
|
|
1371
|
+
const tooltipRect = this.seekTooltip.getBoundingClientRect();
|
|
1372
|
+
let leftPosition = clickX;
|
|
1276
1373
|
|
|
1277
|
-
|
|
1374
|
+
const tooltipWidth = tooltipRect.width || 50;
|
|
1375
|
+
const containerWidth = rect.width;
|
|
1278
1376
|
|
|
1279
|
-
|
|
1280
|
-
let leftPosition = clickX;
|
|
1377
|
+
leftPosition = Math.max(tooltipWidth / 2, Math.min(containerWidth - tooltipWidth / 2, clickX));
|
|
1281
1378
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1379
|
+
this.seekTooltip.style.left = leftPosition + 'px';
|
|
1380
|
+
}
|
|
1284
1381
|
|
|
1285
|
-
|
|
1382
|
+
play() {
|
|
1383
|
+
if (!this.video || this.isChangingQuality) return;
|
|
1286
1384
|
|
|
1287
|
-
|
|
1288
|
-
|
|
1385
|
+
this.video.play().catch(err => {
|
|
1386
|
+
if (this.options.debug) console.log('Play failed:', err);
|
|
1387
|
+
});
|
|
1289
1388
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1389
|
+
if (this.playIcon) this.playIcon.classList.add('hidden');
|
|
1390
|
+
if (this.pauseIcon) this.pauseIcon.classList.remove('hidden');
|
|
1292
1391
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1392
|
+
|
|
1393
|
+
this.triggerEvent('played', {
|
|
1394
|
+
currentTime: this.getCurrentTime(),
|
|
1395
|
+
duration: this.getDuration()
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1296
1398
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1399
|
+
pause() {
|
|
1400
|
+
if (!this.video) return;
|
|
1299
1401
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
duration: this.getDuration()
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1402
|
+
this.video.pause();
|
|
1403
|
+
if (this.playIcon) this.playIcon.classList.remove('hidden');
|
|
1404
|
+
if (this.pauseIcon) this.pauseIcon.classList.add('hidden');
|
|
1306
1405
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1406
|
+
|
|
1407
|
+
this.triggerEvent('paused', {
|
|
1408
|
+
currentTime: this.getCurrentTime(),
|
|
1409
|
+
duration: this.getDuration()
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1309
1412
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
if (this.pauseIcon) this.pauseIcon.classList.add('hidden');
|
|
1413
|
+
updateVolume(value) {
|
|
1414
|
+
if (!this.video) return;
|
|
1313
1415
|
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
currentTime: this.getCurrentTime(),
|
|
1317
|
-
duration: this.getDuration()
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1416
|
+
const previousVolume = this.video.volume;
|
|
1417
|
+
const previousMuted = this.video.muted;
|
|
1320
1418
|
|
|
1321
|
-
|
|
1322
|
-
if (!this.video) return;
|
|
1419
|
+
this.video.volume = Math.max(0, Math.min(1, value / 100));
|
|
1323
1420
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1421
|
+
if (this.video.volume > 0 && this.video.muted) {
|
|
1422
|
+
this.video.muted = false;
|
|
1423
|
+
}
|
|
1326
1424
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
this.initVolumeTooltip();
|
|
1425
|
+
if (this.volumeSlider) this.volumeSlider.value = value;
|
|
1426
|
+
this.updateMuteButton();
|
|
1427
|
+
this.updateVolumeSliderVisual();
|
|
1428
|
+
this.initVolumeTooltip();
|
|
1332
1429
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
}
|
|
1430
|
+
|
|
1431
|
+
if (Math.abs(previousVolume - this.video.volume) > 0.01 || previousMuted !== this.video.muted) {
|
|
1432
|
+
this.triggerEvent('volumechange', {
|
|
1433
|
+
volume: this.getVolume(),
|
|
1434
|
+
muted: this.isMuted(),
|
|
1435
|
+
previousVolume: previousVolume,
|
|
1436
|
+
previousMuted: previousMuted
|
|
1437
|
+
});
|
|
1342
1438
|
}
|
|
1439
|
+
}
|
|
1343
1440
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1441
|
+
changeVolume(delta) {
|
|
1442
|
+
if (!this.video) return;
|
|
1346
1443
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1444
|
+
const newVolume = Math.max(0, Math.min(1, this.video.volume + delta));
|
|
1445
|
+
this.updateVolume(newVolume * 100);
|
|
1446
|
+
this.updateVolumeSliderVisual();
|
|
1447
|
+
this.initVolumeTooltip();
|
|
1448
|
+
}
|
|
1352
1449
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1450
|
+
updateProgress() {
|
|
1451
|
+
if (!this.video || !this.progressFilled || !this.progressHandle || this.isUserSeeking) return;
|
|
1355
1452
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1453
|
+
if (this.video.duration && !isNaN(this.video.duration)) {
|
|
1454
|
+
const progress = (this.video.currentTime / this.video.duration) * 100;
|
|
1455
|
+
this.progressFilled.style.width = progress + '%';
|
|
1456
|
+
this.progressHandle.style.left = progress + '%';
|
|
1457
|
+
}
|
|
1361
1458
|
|
|
1362
|
-
|
|
1459
|
+
this.updateTimeDisplay();
|
|
1363
1460
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1461
|
+
|
|
1462
|
+
if (!this.lastTimeUpdate || Date.now() - this.lastTimeUpdate > 250) {
|
|
1463
|
+
this.triggerEvent('timeupdate', {
|
|
1464
|
+
currentTime: this.getCurrentTime(),
|
|
1465
|
+
duration: this.getDuration(),
|
|
1466
|
+
progress: (this.getCurrentTime() / this.getDuration()) * 100 || 0
|
|
1467
|
+
});
|
|
1468
|
+
this.lastTimeUpdate = Date.now();
|
|
1373
1469
|
}
|
|
1470
|
+
}
|
|
1374
1471
|
|
|
1375
|
-
|
|
1376
|
-
|
|
1472
|
+
updateBuffer() {
|
|
1473
|
+
if (!this.video || !this.progressBuffer) return;
|
|
1377
1474
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
}
|
|
1383
|
-
} catch (error) {
|
|
1384
|
-
if (this.options.debug) console.log('Buffer update error (non-critical):', error);
|
|
1475
|
+
try {
|
|
1476
|
+
if (this.video.buffered && this.video.buffered.length > 0 && this.video.duration) {
|
|
1477
|
+
const buffered = (this.video.buffered.end(0) / this.video.duration) * 100;
|
|
1478
|
+
this.progressBuffer.style.width = buffered + '%';
|
|
1385
1479
|
}
|
|
1480
|
+
} catch (error) {
|
|
1481
|
+
if (this.options.debug) console.log('Buffer update error (non-critical):', error);
|
|
1386
1482
|
}
|
|
1483
|
+
}
|
|
1387
1484
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1485
|
+
startSeeking(e) {
|
|
1486
|
+
if (this.isChangingQuality) return;
|
|
1390
1487
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1488
|
+
this.isUserSeeking = true;
|
|
1489
|
+
this.seek(e);
|
|
1490
|
+
e.preventDefault();
|
|
1394
1491
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1492
|
+
|
|
1493
|
+
if (this.options.autoHide && this.autoHideInitialized) {
|
|
1494
|
+
this.showControlsNow();
|
|
1495
|
+
this.resetAutoHideTimer();
|
|
1400
1496
|
}
|
|
1497
|
+
}
|
|
1401
1498
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
}
|
|
1499
|
+
continueSeeking(e) {
|
|
1500
|
+
if (this.isUserSeeking && !this.isChangingQuality) {
|
|
1501
|
+
this.seek(e);
|
|
1406
1502
|
}
|
|
1503
|
+
}
|
|
1407
1504
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1505
|
+
endSeeking() {
|
|
1506
|
+
this.isUserSeeking = false;
|
|
1507
|
+
}
|
|
1411
1508
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1509
|
+
seek(e) {
|
|
1510
|
+
if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
|
|
1414
1511
|
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1512
|
+
const rect = this.progressContainer.getBoundingClientRect();
|
|
1513
|
+
const clickX = e.clientX - rect.left;
|
|
1514
|
+
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
1418
1515
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
}
|
|
1516
|
+
if (this.video.duration && !isNaN(this.video.duration)) {
|
|
1517
|
+
this.video.currentTime = percentage * this.video.duration;
|
|
1518
|
+
const progress = percentage * 100;
|
|
1519
|
+
this.progressFilled.style.width = progress + '%';
|
|
1520
|
+
this.progressHandle.style.left = progress + '%';
|
|
1425
1521
|
}
|
|
1522
|
+
}
|
|
1426
1523
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
1524
|
+
updateDuration() {
|
|
1525
|
+
if (this.durationEl && this.video && this.video.duration && !isNaN(this.video.duration)) {
|
|
1526
|
+
this.durationEl.textContent = this.formatTime(this.video.duration);
|
|
1431
1527
|
}
|
|
1528
|
+
}
|
|
1432
1529
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
const speed = parseFloat(e.target.getAttribute('data-speed'));
|
|
1437
|
-
if (speed && speed > 0) {
|
|
1438
|
-
this.video.playbackRate = speed;
|
|
1439
|
-
if (this.speedBtn) this.speedBtn.textContent = speed + 'x';
|
|
1530
|
+
changeSpeed(e) {
|
|
1531
|
+
if (!this.video || !e.target.classList.contains('speed-option') || this.isChangingQuality) return;
|
|
1440
1532
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
e.target.classList.add('active');
|
|
1446
|
-
}
|
|
1533
|
+
const speed = parseFloat(e.target.getAttribute('data-speed'));
|
|
1534
|
+
if (speed && speed > 0) {
|
|
1535
|
+
this.video.playbackRate = speed;
|
|
1536
|
+
if (this.speedBtn) this.speedBtn.textContent = speed + 'x';
|
|
1447
1537
|
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
speed: speed,
|
|
1452
|
-
previousSpeed: previousSpeed
|
|
1538
|
+
if (this.speedMenu) {
|
|
1539
|
+
this.speedMenu.querySelectorAll('.speed-option').forEach(option => {
|
|
1540
|
+
option.classList.remove('active');
|
|
1453
1541
|
});
|
|
1542
|
+
e.target.classList.add('active');
|
|
1454
1543
|
}
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
const previousSpeed = this.video.playbackRate;
|
|
1547
|
+
this.triggerEvent('speedchange', {
|
|
1548
|
+
speed: speed,
|
|
1549
|
+
previousSpeed: previousSpeed
|
|
1550
|
+
});
|
|
1455
1551
|
}
|
|
1552
|
+
}
|
|
1456
1553
|
|
|
1457
1554
|
onVideoEnded() {
|
|
1458
1555
|
if (this.playIcon) this.playIcon.classList.remove('hidden');
|
|
@@ -1478,217 +1575,267 @@ onVideoEnded() {
|
|
|
1478
1575
|
});
|
|
1479
1576
|
}
|
|
1480
1577
|
|
|
1481
|
-
getCurrentTime() { return this.video ? this.video.currentTime || 0 : 0; }
|
|
1482
1578
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1579
|
+
onVideoError(error) {
|
|
1580
|
+
if (this.options.debug) {
|
|
1581
|
+
console.error('Video loading error detected:', {
|
|
1582
|
+
error: error,
|
|
1583
|
+
code: this.video?.error?.code,
|
|
1584
|
+
message: this.video?.error?.message,
|
|
1585
|
+
src: this.video?.currentSrc || this.video?.src
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1486
1588
|
|
|
1487
|
-
|
|
1589
|
+
|
|
1590
|
+
this.hideLoading();
|
|
1591
|
+
if (this.initialLoading) {
|
|
1592
|
+
this.initialLoading.style.display = 'none';
|
|
1593
|
+
}
|
|
1488
1594
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1595
|
+
|
|
1596
|
+
if (this.video?.classList) {
|
|
1597
|
+
this.video.classList.remove('quality-changing');
|
|
1493
1598
|
}
|
|
1494
1599
|
|
|
1495
|
-
|
|
1600
|
+
|
|
1601
|
+
this.isChangingQuality = false;
|
|
1496
1602
|
|
|
1497
|
-
|
|
1603
|
+
|
|
1604
|
+
this.showControlsNow();
|
|
1498
1605
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
this.updateMuteButton();
|
|
1503
|
-
this.updateVolumeSliderVisual();
|
|
1504
|
-
this.initVolumeTooltip();
|
|
1505
|
-
}
|
|
1606
|
+
|
|
1607
|
+
if (this.options.showPosterOnEnd && this.posterOverlay) {
|
|
1608
|
+
this.showPoster();
|
|
1506
1609
|
}
|
|
1507
1610
|
|
|
1508
|
-
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
this.triggerEvent('ended', {
|
|
1614
|
+
currentTime: this.getCurrentTime(),
|
|
1615
|
+
duration: this.getDuration(),
|
|
1616
|
+
error: true,
|
|
1617
|
+
errorCode: this.video?.error?.code,
|
|
1618
|
+
errorMessage: this.video?.error?.message,
|
|
1619
|
+
playlistInfo: this.getPlaylistInfo()
|
|
1620
|
+
});
|
|
1509
1621
|
|
|
1510
|
-
|
|
1622
|
+
if (this.options.debug) {
|
|
1623
|
+
console.log('Video error handled - triggered ended event');
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1511
1626
|
|
|
1512
|
-
isPictureInPictureActive() { return document.pictureInPictureElement === this.video; }
|
|
1513
1627
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1628
|
+
getCurrentTime() { return this.video ? this.video.currentTime || 0 : 0; }
|
|
1629
|
+
|
|
1630
|
+
setCurrentTime(time) { if (this.video && typeof time === 'number' && time >= 0 && !this.isChangingQuality) { this.video.currentTime = time; } }
|
|
1631
|
+
|
|
1632
|
+
getDuration() { return this.video && this.video.duration ? this.video.duration : 0; }
|
|
1633
|
+
|
|
1634
|
+
getVolume() { return this.video ? this.video.volume || 0 : 0; }
|
|
1635
|
+
|
|
1636
|
+
setVolume(volume) {
|
|
1637
|
+
if (typeof volume === 'number' && volume >= 0 && volume <= 1) {
|
|
1638
|
+
this.updateVolume(volume * 100);
|
|
1517
1639
|
}
|
|
1640
|
+
}
|
|
1518
1641
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1642
|
+
isPaused() { return this.video ? this.video.paused : true; }
|
|
1643
|
+
|
|
1644
|
+
isMuted() { return this.video ? this.video.muted : false; }
|
|
1645
|
+
|
|
1646
|
+
setMuted(muted) {
|
|
1647
|
+
if (this.video && typeof muted === 'boolean') {
|
|
1648
|
+
this.video.muted = muted;
|
|
1649
|
+
this.updateMuteButton();
|
|
1650
|
+
this.updateVolumeSliderVisual();
|
|
1651
|
+
this.initVolumeTooltip();
|
|
1522
1652
|
}
|
|
1653
|
+
}
|
|
1523
1654
|
|
|
1524
|
-
|
|
1525
|
-
if (!this.options.brandLogoEnabled || !this.options.brandLogoUrl) return;
|
|
1655
|
+
getPlaybackRate() { return this.video ? this.video.playbackRate || 1 : 1; }
|
|
1526
1656
|
|
|
1527
|
-
|
|
1528
|
-
if (!controlsRight) return;
|
|
1657
|
+
setPlaybackRate(rate) { if (this.video && typeof rate === 'number' && rate > 0 && !this.isChangingQuality) { this.video.playbackRate = rate; if (this.speedBtn) this.speedBtn.textContent = rate + 'x'; } }
|
|
1529
1658
|
|
|
1530
|
-
|
|
1531
|
-
const logo = document.createElement('img');
|
|
1532
|
-
logo.className = 'brand-logo';
|
|
1533
|
-
logo.src = this.options.brandLogoUrl;
|
|
1534
|
-
logo.alt = this.t('brand_logo');
|
|
1659
|
+
isPictureInPictureActive() { return document.pictureInPictureElement === this.video; }
|
|
1535
1660
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
};
|
|
1661
|
+
getCurrentLanguage() {
|
|
1662
|
+
return this.isI18nAvailable() ?
|
|
1663
|
+
VideoPlayerTranslations.getCurrentLanguage() : 'en';
|
|
1664
|
+
}
|
|
1541
1665
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1666
|
+
getSupportedLanguages() {
|
|
1667
|
+
return this.isI18nAvailable() ?
|
|
1668
|
+
VideoPlayerTranslations.getSupportedLanguages() : ['en'];
|
|
1669
|
+
}
|
|
1545
1670
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1671
|
+
createBrandLogo() {
|
|
1672
|
+
if (!this.options.brandLogoEnabled || !this.options.brandLogoUrl) return;
|
|
1673
|
+
|
|
1674
|
+
const controlsRight = this.controls?.querySelector('.controls-right');
|
|
1675
|
+
if (!controlsRight) return;
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
const logo = document.createElement('img');
|
|
1679
|
+
logo.className = 'brand-logo';
|
|
1680
|
+
logo.src = this.options.brandLogoUrl;
|
|
1681
|
+
logo.alt = this.t('brand_logo');
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
logo.onerror = () => {
|
|
1685
|
+
if (this.options.debug) console.warn('Brand logo failed to load:', this.options.brandLogoUrl);
|
|
1686
|
+
logo.style.display = 'none';
|
|
1687
|
+
};
|
|
1557
1688
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1689
|
+
logo.onload = () => {
|
|
1690
|
+
if (this.options.debug) console.log('Brand logo loaded successfully');
|
|
1691
|
+
};
|
|
1560
1692
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1693
|
+
|
|
1694
|
+
if (this.options.brandLogoLinkUrl) {
|
|
1695
|
+
logo.style.cursor = 'pointer';
|
|
1696
|
+
logo.addEventListener('click', (e) => {
|
|
1697
|
+
e.stopPropagation();
|
|
1698
|
+
window.open(this.options.brandLogoLinkUrl, '_blank', 'noopener,noreferrer');
|
|
1699
|
+
if (this.options.debug) console.log('Brand logo clicked, opening:', this.options.brandLogoLinkUrl);
|
|
1700
|
+
});
|
|
1701
|
+
} else {
|
|
1702
|
+
logo.style.cursor = 'default';
|
|
1568
1703
|
}
|
|
1569
1704
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
if (url) {
|
|
1573
|
-
this.options.brandLogoUrl = url;
|
|
1574
|
-
}
|
|
1575
|
-
if (linkUrl !== '') {
|
|
1576
|
-
this.options.brandLogoLinkUrl = linkUrl;
|
|
1577
|
-
}
|
|
1705
|
+
|
|
1706
|
+
controlsRight.insertBefore(logo, controlsRight.firstChild);
|
|
1578
1707
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1708
|
+
if (this.options.debug) {
|
|
1709
|
+
if (this.options.brandLogoLinkUrl) {
|
|
1710
|
+
console.log('Brand logo with click handler created for:', this.options.brandLogoLinkUrl);
|
|
1711
|
+
} else {
|
|
1712
|
+
console.log('Brand logo created (no link)');
|
|
1583
1713
|
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1584
1716
|
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1717
|
+
setBrandLogo(enabled, url = '', linkUrl = '') {
|
|
1718
|
+
this.options.brandLogoEnabled = enabled;
|
|
1719
|
+
if (url) {
|
|
1720
|
+
this.options.brandLogoUrl = url;
|
|
1721
|
+
}
|
|
1722
|
+
if (linkUrl !== '') {
|
|
1723
|
+
this.options.brandLogoLinkUrl = linkUrl;
|
|
1724
|
+
}
|
|
1589
1725
|
|
|
1590
|
-
|
|
1726
|
+
|
|
1727
|
+
const existingLogo = this.controls?.querySelector('.brand-logo');
|
|
1728
|
+
if (existingLogo) {
|
|
1729
|
+
existingLogo.remove();
|
|
1591
1730
|
}
|
|
1592
1731
|
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
url: this.options.brandLogoUrl,
|
|
1597
|
-
linkUrl: this.options.brandLogoLinkUrl
|
|
1598
|
-
};
|
|
1732
|
+
|
|
1733
|
+
if (enabled && this.options.brandLogoUrl) {
|
|
1734
|
+
this.createBrandLogo();
|
|
1599
1735
|
}
|
|
1600
1736
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
if (this.options.debug) console.error('🎵 New video element not found');
|
|
1604
|
-
return false;
|
|
1605
|
-
}
|
|
1737
|
+
return this;
|
|
1738
|
+
}
|
|
1606
1739
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1740
|
+
getBrandLogoSettings() {
|
|
1741
|
+
return {
|
|
1742
|
+
enabled: this.options.brandLogoEnabled,
|
|
1743
|
+
url: this.options.brandLogoUrl,
|
|
1744
|
+
linkUrl: this.options.brandLogoLinkUrl
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1609
1747
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
}));
|
|
1748
|
+
switchToVideo(newVideoElement, shouldPlay = false) {
|
|
1749
|
+
if (!newVideoElement) {
|
|
1750
|
+
if (this.options.debug) console.error('🎵 New video element not found');
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1616
1753
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
return false;
|
|
1620
|
-
}
|
|
1754
|
+
|
|
1755
|
+
this.video.pause();
|
|
1621
1756
|
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1757
|
+
|
|
1758
|
+
const newSources = Array.from(newVideoElement.querySelectorAll('source')).map(source => ({
|
|
1759
|
+
src: source.src,
|
|
1760
|
+
quality: source.getAttribute('data-quality') || 'auto',
|
|
1761
|
+
type: source.type || 'video/mp4'
|
|
1762
|
+
}));
|
|
1763
|
+
|
|
1764
|
+
if (newSources.length === 0) {
|
|
1765
|
+
if (this.options.debug) console.error('🎵 New video has no sources');
|
|
1766
|
+
return false;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
|
|
1770
|
+
if (this.options.adaptiveStreaming && newSources.length > 0) {
|
|
1771
|
+
const firstSource = newSources[0];
|
|
1772
|
+
if (this.detectStreamType(firstSource.src)) {
|
|
1773
|
+
|
|
1774
|
+
this.initializeAdaptiveStreaming(firstSource.src).then((initialized) => {
|
|
1775
|
+
if (initialized && shouldPlay) {
|
|
1776
|
+
const playPromise = this.video.play();
|
|
1777
|
+
if (playPromise) {
|
|
1778
|
+
playPromise.catch(error => {
|
|
1779
|
+
if (this.options.debug) console.log('Autoplay prevented:', error);
|
|
1780
|
+
});
|
|
1635
1781
|
}
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
return true;
|
|
1639
1785
|
}
|
|
1786
|
+
}
|
|
1640
1787
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1788
|
+
|
|
1789
|
+
this.video.innerHTML = '';
|
|
1790
|
+
newSources.forEach(source => {
|
|
1791
|
+
const sourceEl = document.createElement('source');
|
|
1792
|
+
sourceEl.src = source.src;
|
|
1793
|
+
sourceEl.type = source.type;
|
|
1794
|
+
sourceEl.setAttribute('data-quality', source.quality);
|
|
1795
|
+
this.video.appendChild(sourceEl);
|
|
1796
|
+
});
|
|
1650
1797
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1798
|
+
|
|
1799
|
+
const newTracks = Array.from(newVideoElement.querySelectorAll('track'));
|
|
1800
|
+
newTracks.forEach(track => {
|
|
1801
|
+
const trackEl = document.createElement('track');
|
|
1802
|
+
trackEl.kind = track.kind;
|
|
1803
|
+
trackEl.src = track.src;
|
|
1804
|
+
trackEl.srclang = track.srclang;
|
|
1805
|
+
trackEl.label = track.label;
|
|
1806
|
+
if (track.default) trackEl.default = true;
|
|
1807
|
+
this.video.appendChild(trackEl);
|
|
1808
|
+
});
|
|
1662
1809
|
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
}
|
|
1810
|
+
|
|
1811
|
+
const newTitle = newVideoElement.getAttribute('data-video-title');
|
|
1812
|
+
if (newTitle && this.options.showTitleOverlay) {
|
|
1813
|
+
this.options.videoTitle = newTitle;
|
|
1814
|
+
if (this.titleText) {
|
|
1815
|
+
this.titleText.textContent = newTitle;
|
|
1670
1816
|
}
|
|
1817
|
+
}
|
|
1671
1818
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1819
|
+
|
|
1820
|
+
this.video.load();
|
|
1674
1821
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1822
|
+
|
|
1823
|
+
this.collectVideoQualities();
|
|
1824
|
+
this.updateQualityMenu();
|
|
1678
1825
|
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
}
|
|
1826
|
+
|
|
1827
|
+
if (shouldPlay) {
|
|
1828
|
+
const playPromise = this.video.play();
|
|
1829
|
+
if (playPromise) {
|
|
1830
|
+
playPromise.catch(error => {
|
|
1831
|
+
if (this.options.debug) console.log('🎵 Autoplay prevented:', error);
|
|
1832
|
+
});
|
|
1687
1833
|
}
|
|
1688
|
-
|
|
1689
|
-
return true;
|
|
1690
1834
|
}
|
|
1691
1835
|
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1692
1839
|
|
|
1693
1840
|
initializePoster() {
|
|
1694
1841
|
if (!this.video) {
|
|
@@ -1878,59 +2025,94 @@ isPosterVisible() {
|
|
|
1878
2025
|
}
|
|
1879
2026
|
|
|
1880
2027
|
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2028
|
+
loadScript(src) {
|
|
2029
|
+
return new Promise((resolve, reject) => {
|
|
2030
|
+
if (document.querySelector(`script[src="${src}"]`)) {
|
|
2031
|
+
resolve();
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
const script = document.createElement('script');
|
|
2036
|
+
script.src = src;
|
|
2037
|
+
script.onload = resolve;
|
|
2038
|
+
script.onerror = reject;
|
|
2039
|
+
document.head.appendChild(script);
|
|
2040
|
+
});
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
|
|
2044
|
+
setSeekHandleShape(shape) {
|
|
2045
|
+
const validShapes = ['none', 'circle', 'square', 'diamond', 'arrow', 'triangle', 'heart', 'star'];
|
|
2046
|
+
|
|
2047
|
+
if (!validShapes.includes(shape)) {
|
|
2048
|
+
if (this.options.debug) console.warn('Invalid seek handle shape:', shape);
|
|
2049
|
+
return this;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
this.options.seekHandleShape = shape;
|
|
1887
2053
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2054
|
+
|
|
2055
|
+
if (this.progressHandle) {
|
|
2056
|
+
|
|
2057
|
+
validShapes.forEach(s => {
|
|
2058
|
+
this.progressHandle.classList.remove(`progress-handle-${s}`);
|
|
1893
2059
|
});
|
|
2060
|
+
|
|
2061
|
+
this.progressHandle.classList.add(`progress-handle-${shape}`);
|
|
1894
2062
|
}
|
|
1895
2063
|
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
this.qualityMonitorInterval = null;
|
|
1900
|
-
}
|
|
2064
|
+
if (this.options.debug) console.log('Seek handle shape changed to:', shape);
|
|
2065
|
+
return this;
|
|
2066
|
+
}
|
|
1901
2067
|
|
|
1902
|
-
if (this.autoHideTimer) {
|
|
1903
|
-
clearTimeout(this.autoHideTimer);
|
|
1904
|
-
this.autoHideTimer = null;
|
|
1905
|
-
}
|
|
1906
2068
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2069
|
+
getSeekHandleShape() {
|
|
2070
|
+
return this.options.seekHandleShape;
|
|
2071
|
+
}
|
|
1910
2072
|
|
|
1911
|
-
|
|
1912
|
-
this.destroyAdaptivePlayer();
|
|
1913
2073
|
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
if (this.loadingOverlay) {
|
|
1918
|
-
this.loadingOverlay.remove();
|
|
1919
|
-
}
|
|
1920
|
-
if (this.titleOverlay) {
|
|
1921
|
-
this.titleOverlay.remove();
|
|
1922
|
-
}
|
|
1923
|
-
if (this.initialLoading) {
|
|
1924
|
-
this.initialLoading.remove();
|
|
1925
|
-
}
|
|
2074
|
+
getAvailableSeekHandleShapes() {
|
|
2075
|
+
return ['none', 'circle', 'square', 'diamond', 'arrow', 'triangle', 'heart', 'star'];
|
|
2076
|
+
}
|
|
1926
2077
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2078
|
+
dispose() {
|
|
2079
|
+
if (this.qualityMonitorInterval) {
|
|
2080
|
+
clearInterval(this.qualityMonitorInterval);
|
|
2081
|
+
this.qualityMonitorInterval = null;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
if (this.autoHideTimer) {
|
|
2085
|
+
clearTimeout(this.autoHideTimer);
|
|
2086
|
+
this.autoHideTimer = null;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
this.cleanupQualityChange();
|
|
2090
|
+
this.clearControlsTimeout();
|
|
2091
|
+
this.clearTitleTimeout();
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
this.destroyAdaptivePlayer();
|
|
2095
|
+
|
|
2096
|
+
if (this.controls) {
|
|
2097
|
+
this.controls.remove();
|
|
2098
|
+
}
|
|
2099
|
+
if (this.loadingOverlay) {
|
|
2100
|
+
this.loadingOverlay.remove();
|
|
2101
|
+
}
|
|
2102
|
+
if (this.titleOverlay) {
|
|
2103
|
+
this.titleOverlay.remove();
|
|
2104
|
+
}
|
|
2105
|
+
if (this.initialLoading) {
|
|
2106
|
+
this.initialLoading.remove();
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
if (this.video) {
|
|
2110
|
+
this.video.classList.remove('video-player');
|
|
2111
|
+
this.video.controls = true;
|
|
2112
|
+
this.video.style.visibility = '';
|
|
2113
|
+
this.video.style.opacity = '';
|
|
2114
|
+
this.video.style.pointerEvents = '';
|
|
2115
|
+
}
|
|
1934
2116
|
if (this.chapterMarkersContainer) {
|
|
1935
2117
|
this.chapterMarkersContainer.remove();
|
|
1936
2118
|
}
|
|
@@ -1942,15 +2124,15 @@ isPosterVisible() {
|
|
|
1942
2124
|
}
|
|
1943
2125
|
this.disposeAllPlugins();
|
|
1944
2126
|
|
|
1945
|
-
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
|
|
1946
2132
|
|
|
1947
|
-
|
|
1948
2133
|
|
|
1949
|
-
|
|
1950
2134
|
|
|
1951
|
-
|
|
1952
2135
|
|
|
1953
|
-
|
|
1954
2136
|
|
|
1955
2137
|
addEventListener(eventType, callback) {
|
|
1956
2138
|
if (typeof callback !== 'function') {
|
|
@@ -2333,6 +2515,16 @@ initAutoHide() {
|
|
|
2333
2515
|
this.controls.addEventListener('mouseleave', (e) => {
|
|
2334
2516
|
if (this.autoHideDebug) {
|
|
2335
2517
|
if (this.options.debug) console.log('Mouse EXITS controls - restart timer');
|
|
2518
|
+
|
|
2519
|
+
|
|
2520
|
+
this.container.addEventListener('touchstart', () => {
|
|
2521
|
+
this.showControlsNow();
|
|
2522
|
+
this.resetAutoHideTimer();
|
|
2523
|
+
});
|
|
2524
|
+
|
|
2525
|
+
this.container.addEventListener('touchend', () => {
|
|
2526
|
+
this.resetAutoHideTimer();
|
|
2527
|
+
});
|
|
2336
2528
|
}
|
|
2337
2529
|
this.onMouseLeaveControls(e);
|
|
2338
2530
|
});
|
|
@@ -2376,7 +2568,8 @@ resetAutoHideTimer() {
|
|
|
2376
2568
|
this.autoHideTimer = null;
|
|
2377
2569
|
}
|
|
2378
2570
|
|
|
2379
|
-
|
|
2571
|
+
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
2572
|
+
if (this.mouseOverControls && !isTouchDevice) {
|
|
2380
2573
|
if (this.autoHideDebug) {
|
|
2381
2574
|
if (this.options.debug) console.log('Not starting timer - mouse on controls');
|
|
2382
2575
|
}
|
|
@@ -2422,7 +2615,8 @@ showControlsNow() {
|
|
|
2422
2615
|
|
|
2423
2616
|
hideControlsNow() {
|
|
2424
2617
|
|
|
2425
|
-
|
|
2618
|
+
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
2619
|
+
if (this.mouseOverControls && !isTouchDevice) {
|
|
2426
2620
|
if (this.autoHideDebug && this.options.debug) console.log('⏸️ Not hiding - mouse still over controls');
|
|
2427
2621
|
return;
|
|
2428
2622
|
}
|
|
@@ -2615,7 +2809,7 @@ createControls() {
|
|
|
2615
2809
|
<div class="progress-bar">
|
|
2616
2810
|
<div class="progress-buffer"></div>
|
|
2617
2811
|
<div class="progress-filled"></div>
|
|
2618
|
-
<div class="progress-handle"></div>
|
|
2812
|
+
<div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div>
|
|
2619
2813
|
</div>
|
|
2620
2814
|
${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
|
|
2621
2815
|
</div>
|
|
@@ -2632,7 +2826,7 @@ createControls() {
|
|
|
2632
2826
|
<span class="icon mute-icon hidden">🔇</span>
|
|
2633
2827
|
</button>
|
|
2634
2828
|
|
|
2635
|
-
<div class="volume-container" data-
|
|
2829
|
+
<div class="volume-container" data-mobile-slider="${this.options.volumeSlider}">
|
|
2636
2830
|
<input type="range" class="volume-slider" min="0" max="100" value="100" data-tooltip="volume">
|
|
2637
2831
|
</div>
|
|
2638
2832
|
|
|
@@ -3278,140 +3472,160 @@ optimizeButtonsForSmallHeight() {
|
|
|
3278
3472
|
|
|
3279
3473
|
|
|
3280
3474
|
initializeQualityMonitoring() {
|
|
3281
|
-
|
|
3475
|
+
this.qualityMonitorInterval = setInterval(() => {
|
|
3476
|
+
if (!this.isChangingQuality) {
|
|
3477
|
+
this.updateCurrentPlayingQuality();
|
|
3478
|
+
}
|
|
3479
|
+
}, 3000);
|
|
3480
|
+
|
|
3481
|
+
if (this.video) {
|
|
3482
|
+
this.video.addEventListener('loadedmetadata', () => {
|
|
3483
|
+
setTimeout(() => {
|
|
3484
|
+
if (!this.isChangingQuality) {
|
|
3485
|
+
this.updateCurrentPlayingQuality();
|
|
3486
|
+
}
|
|
3487
|
+
}, 100);
|
|
3488
|
+
});
|
|
3489
|
+
|
|
3490
|
+
this.video.addEventListener('resize', () => {
|
|
3282
3491
|
if (!this.isChangingQuality) {
|
|
3283
3492
|
this.updateCurrentPlayingQuality();
|
|
3284
3493
|
}
|
|
3285
|
-
}
|
|
3286
|
-
|
|
3287
|
-
if (this.video) {
|
|
3288
|
-
this.video.addEventListener('loadedmetadata', () => {
|
|
3289
|
-
setTimeout(() => {
|
|
3290
|
-
if (!this.isChangingQuality) {
|
|
3291
|
-
this.updateCurrentPlayingQuality();
|
|
3292
|
-
}
|
|
3293
|
-
}, 100);
|
|
3294
|
-
});
|
|
3494
|
+
});
|
|
3295
3495
|
|
|
3296
|
-
|
|
3496
|
+
this.video.addEventListener('loadeddata', () => {
|
|
3497
|
+
setTimeout(() => {
|
|
3297
3498
|
if (!this.isChangingQuality) {
|
|
3298
3499
|
this.updateCurrentPlayingQuality();
|
|
3299
3500
|
}
|
|
3300
|
-
});
|
|
3301
|
-
|
|
3302
|
-
this.video.addEventListener('loadeddata', () => {
|
|
3303
|
-
setTimeout(() => {
|
|
3304
|
-
if (!this.isChangingQuality) {
|
|
3305
|
-
this.updateCurrentPlayingQuality();
|
|
3306
|
-
}
|
|
3307
|
-
}, 1000);
|
|
3308
|
-
});
|
|
3309
|
-
}
|
|
3501
|
+
}, 1000);
|
|
3502
|
+
});
|
|
3310
3503
|
}
|
|
3504
|
+
}
|
|
3311
3505
|
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
if (this.video.currentSrc && this.qualities && this.qualities.length > 0) {
|
|
3316
|
-
const currentSource = this.qualities.find(q => {
|
|
3317
|
-
const currentUrl = this.video.currentSrc.toLowerCase();
|
|
3318
|
-
const qualityUrl = q.src.toLowerCase();
|
|
3319
|
-
|
|
3320
|
-
if (this.debugQuality) {
|
|
3321
|
-
if (this.options.debug) console.log('Quality comparison:', {
|
|
3322
|
-
current: currentUrl,
|
|
3323
|
-
quality: qualityUrl,
|
|
3324
|
-
qualityName: q.quality,
|
|
3325
|
-
match: currentUrl === qualityUrl || currentUrl.includes(qualityUrl) || qualityUrl.includes(currentUrl)
|
|
3326
|
-
});
|
|
3327
|
-
}
|
|
3506
|
+
getCurrentPlayingQuality() {
|
|
3507
|
+
if (!this.video) return null;
|
|
3328
3508
|
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3509
|
+
if (this.video.currentSrc && this.qualities && this.qualities.length > 0) {
|
|
3510
|
+
const currentSource = this.qualities.find(q => {
|
|
3511
|
+
const currentUrl = this.video.currentSrc.toLowerCase();
|
|
3512
|
+
const qualityUrl = q.src.toLowerCase();
|
|
3333
3513
|
|
|
3334
|
-
if (
|
|
3335
|
-
if (this.
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3514
|
+
if (this.debugQuality) {
|
|
3515
|
+
if (this.options.debug) console.log('Quality comparison:', {
|
|
3516
|
+
current: currentUrl,
|
|
3517
|
+
quality: qualityUrl,
|
|
3518
|
+
qualityName: q.quality,
|
|
3519
|
+
match: currentUrl === qualityUrl || currentUrl.includes(qualityUrl) || qualityUrl.includes(currentUrl)
|
|
3520
|
+
});
|
|
3339
3521
|
}
|
|
3340
|
-
}
|
|
3341
3522
|
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3523
|
+
return currentUrl === qualityUrl ||
|
|
3524
|
+
currentUrl.includes(qualityUrl) ||
|
|
3525
|
+
qualityUrl.includes(currentUrl);
|
|
3526
|
+
});
|
|
3345
3527
|
|
|
3528
|
+
if (currentSource) {
|
|
3346
3529
|
if (this.debugQuality) {
|
|
3347
|
-
if (this.options.debug) console.log('
|
|
3530
|
+
if (this.options.debug) console.log('Quality found from source:', currentSource.quality);
|
|
3348
3531
|
}
|
|
3349
|
-
|
|
3350
|
-
if (height >= 2160) return '4K';
|
|
3351
|
-
if (height >= 1440) return '1440p';
|
|
3352
|
-
if (height >= 1080) return '1080p';
|
|
3353
|
-
if (height >= 720) return '720p';
|
|
3354
|
-
if (height >= 480) return '480p';
|
|
3355
|
-
if (height >= 360) return '360p';
|
|
3356
|
-
if (height >= 240) return '240p';
|
|
3357
|
-
|
|
3358
|
-
return `${height}p`;
|
|
3532
|
+
return currentSource.quality;
|
|
3359
3533
|
}
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
if (this.video.videoHeight && this.video.videoWidth) {
|
|
3537
|
+
const height = this.video.videoHeight;
|
|
3538
|
+
const width = this.video.videoWidth;
|
|
3360
3539
|
|
|
3361
3540
|
if (this.debugQuality) {
|
|
3362
|
-
if (this.options.debug) console.log('
|
|
3363
|
-
currentSrc: this.video.currentSrc,
|
|
3364
|
-
videoHeight: this.video.videoHeight,
|
|
3365
|
-
videoWidth: this.video.videoWidth,
|
|
3366
|
-
qualities: this.qualities
|
|
3367
|
-
});
|
|
3541
|
+
if (this.options.debug) console.log('Risoluzione video:', { height, width });
|
|
3368
3542
|
}
|
|
3369
3543
|
|
|
3370
|
-
return
|
|
3371
|
-
|
|
3544
|
+
if (height >= 2160) return '4K';
|
|
3545
|
+
if (height >= 1440) return '1440p';
|
|
3546
|
+
if (height >= 1080) return '1080p';
|
|
3547
|
+
if (height >= 720) return '720p';
|
|
3548
|
+
if (height >= 480) return '480p';
|
|
3549
|
+
if (height >= 360) return '360p';
|
|
3550
|
+
if (height >= 240) return '240p';
|
|
3372
3551
|
|
|
3373
|
-
|
|
3374
|
-
|
|
3552
|
+
return `${height}p`;
|
|
3553
|
+
}
|
|
3375
3554
|
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
this.
|
|
3379
|
-
this.
|
|
3380
|
-
|
|
3555
|
+
if (this.debugQuality) {
|
|
3556
|
+
if (this.options.debug) console.log('No quality detected:', {
|
|
3557
|
+
currentSrc: this.video.currentSrc,
|
|
3558
|
+
videoHeight: this.video.videoHeight,
|
|
3559
|
+
videoWidth: this.video.videoWidth,
|
|
3560
|
+
qualities: this.qualities
|
|
3561
|
+
});
|
|
3381
3562
|
}
|
|
3382
3563
|
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3564
|
+
return null;
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
updateCurrentPlayingQuality() {
|
|
3568
|
+
const newPlayingQuality = this.getCurrentPlayingQuality();
|
|
3569
|
+
|
|
3570
|
+
if (newPlayingQuality && newPlayingQuality !== this.currentPlayingQuality) {
|
|
3571
|
+
if (this.options.debug) console.log(`Quality changed: ${this.currentPlayingQuality} → ${newPlayingQuality}`);
|
|
3572
|
+
this.currentPlayingQuality = newPlayingQuality;
|
|
3573
|
+
this.updateQualityDisplay();
|
|
3386
3574
|
}
|
|
3575
|
+
}
|
|
3387
3576
|
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3577
|
+
updateQualityDisplay() {
|
|
3578
|
+
this.updateQualityButton();
|
|
3579
|
+
this.updateQualityMenu();
|
|
3580
|
+
}
|
|
3391
3581
|
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
<span class="icon">⚙</span>
|
|
3396
|
-
<div class="quality-btn-text">
|
|
3397
|
-
<div class="selected-quality">${this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality}</div>
|
|
3398
|
-
<div class="current-quality">${this.currentPlayingQuality || ''}</div>
|
|
3399
|
-
</div>
|
|
3400
|
-
`;
|
|
3401
|
-
} else {
|
|
3402
|
-
const selectedEl = btnText.querySelector('.selected-quality');
|
|
3403
|
-
const currentEl = btnText.querySelector('.current-quality');
|
|
3582
|
+
updateQualityButton() {
|
|
3583
|
+
const qualityBtn = this.controls?.querySelector('.quality-btn');
|
|
3584
|
+
if (!qualityBtn) return;
|
|
3404
3585
|
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3586
|
+
let btnText = qualityBtn.querySelector('.quality-btn-text');
|
|
3587
|
+
if (!btnText) {
|
|
3588
|
+
|
|
3589
|
+
qualityBtn.textContent = '';
|
|
3408
3590
|
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3591
|
+
|
|
3592
|
+
const iconSpan = document.createElement('span');
|
|
3593
|
+
iconSpan.className = 'icon';
|
|
3594
|
+
iconSpan.textContent = '⚙';
|
|
3595
|
+
qualityBtn.appendChild(iconSpan);
|
|
3596
|
+
|
|
3597
|
+
|
|
3598
|
+
btnText = document.createElement('div');
|
|
3599
|
+
btnText.className = 'quality-btn-text';
|
|
3600
|
+
|
|
3601
|
+
|
|
3602
|
+
const selectedQualityDiv = document.createElement('div');
|
|
3603
|
+
selectedQualityDiv.className = 'selected-quality';
|
|
3604
|
+
selectedQualityDiv.textContent = this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality;
|
|
3605
|
+
btnText.appendChild(selectedQualityDiv);
|
|
3606
|
+
|
|
3607
|
+
|
|
3608
|
+
const currentQualityDiv = document.createElement('div');
|
|
3609
|
+
currentQualityDiv.className = 'current-quality';
|
|
3610
|
+
currentQualityDiv.textContent = this.currentPlayingQuality || '';
|
|
3611
|
+
btnText.appendChild(currentQualityDiv);
|
|
3612
|
+
|
|
3613
|
+
|
|
3614
|
+
qualityBtn.appendChild(btnText);
|
|
3615
|
+
} else {
|
|
3616
|
+
|
|
3617
|
+
const selectedEl = btnText.querySelector('.selected-quality');
|
|
3618
|
+
const currentEl = btnText.querySelector('.current-quality');
|
|
3619
|
+
|
|
3620
|
+
if (selectedEl) {
|
|
3621
|
+
selectedEl.textContent = this.selectedQuality === 'auto' ? this.t('auto') : this.selectedQuality;
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
if (currentEl) {
|
|
3625
|
+
currentEl.textContent = this.currentPlayingQuality || '';
|
|
3413
3626
|
}
|
|
3414
3627
|
}
|
|
3628
|
+
}
|
|
3415
3629
|
|
|
3416
3630
|
updateQualityMenu() {
|
|
3417
3631
|
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
@@ -3465,213 +3679,217 @@ updateQualityMenu() {
|
|
|
3465
3679
|
qualityMenu.innerHTML = menuHTML;
|
|
3466
3680
|
}
|
|
3467
3681
|
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3682
|
+
getQualityStatus() {
|
|
3683
|
+
return {
|
|
3684
|
+
selected: this.selectedQuality,
|
|
3685
|
+
playing: this.currentPlayingQuality,
|
|
3686
|
+
isAuto: this.selectedQuality === 'auto',
|
|
3687
|
+
isChanging: this.isChangingQuality
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3476
3690
|
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3691
|
+
getSelectedQuality() {
|
|
3692
|
+
return this.selectedQuality;
|
|
3693
|
+
}
|
|
3480
3694
|
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3695
|
+
isAutoQualityActive() {
|
|
3696
|
+
return this.selectedQuality === 'auto';
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
enableQualityDebug() {
|
|
3700
|
+
this.debugQuality = true;
|
|
3701
|
+
this.enableAutoHideDebug();
|
|
3702
|
+
if (this.options.debug) console.log('Quality AND auto-hide debug enabled');
|
|
3703
|
+
this.updateCurrentPlayingQuality();
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
disableQualityDebug() {
|
|
3707
|
+
this.debugQuality = false;
|
|
3708
|
+
this.disableAutoHideDebug();
|
|
3709
|
+
if (this.options.debug) console.log('Quality AND auto-hide debug disabled');
|
|
3710
|
+
}
|
|
3484
3711
|
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
if (this.options.debug) console.log('Quality AND auto-hide debug enabled');
|
|
3489
|
-
this.updateCurrentPlayingQuality();
|
|
3490
|
-
}
|
|
3712
|
+
changeQuality(e) {
|
|
3713
|
+
if (!e.target.classList.contains('quality-option')) return;
|
|
3714
|
+
if (this.isChangingQuality) return;
|
|
3491
3715
|
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3716
|
+
|
|
3717
|
+
const adaptiveQuality = e.target.getAttribute('data-adaptive-quality');
|
|
3718
|
+
if (adaptiveQuality !== null && this.isAdaptiveStream) {
|
|
3719
|
+
const qualityIndex = adaptiveQuality === 'auto' ? -1 : parseInt(adaptiveQuality);
|
|
3720
|
+
this.setAdaptiveQuality(qualityIndex);
|
|
3721
|
+
this.updateAdaptiveQualityMenu();
|
|
3722
|
+
return;
|
|
3496
3723
|
}
|
|
3497
3724
|
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
if (this.isChangingQuality) return;
|
|
3725
|
+
const quality = e.target.getAttribute('data-quality');
|
|
3726
|
+
if (!quality || quality === this.selectedQuality) return;
|
|
3501
3727
|
|
|
3502
|
-
|
|
3503
|
-
const adaptiveQuality = e.target.getAttribute('data-adaptive-quality');
|
|
3504
|
-
if (adaptiveQuality !== null && this.isAdaptiveStream) {
|
|
3505
|
-
const qualityIndex = adaptiveQuality === 'auto' ? -1 : parseInt(adaptiveQuality);
|
|
3506
|
-
this.setAdaptiveQuality(qualityIndex);
|
|
3507
|
-
this.updateAdaptiveQualityMenu();
|
|
3508
|
-
return;
|
|
3509
|
-
}
|
|
3728
|
+
if (this.options.debug) console.log(`Quality change requested: ${this.selectedQuality} → ${quality}`);
|
|
3510
3729
|
|
|
3511
|
-
|
|
3512
|
-
if (!quality || quality === this.selectedQuality) return;
|
|
3730
|
+
this.selectedQuality = quality;
|
|
3513
3731
|
|
|
3514
|
-
|
|
3732
|
+
if (quality === 'auto') {
|
|
3733
|
+
this.enableAutoQuality();
|
|
3734
|
+
} else {
|
|
3735
|
+
this.setQuality(quality);
|
|
3736
|
+
}
|
|
3515
3737
|
|
|
3516
|
-
|
|
3738
|
+
this.updateQualityDisplay();
|
|
3739
|
+
}
|
|
3517
3740
|
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
} else {
|
|
3521
|
-
this.setQuality(quality);
|
|
3522
|
-
}
|
|
3741
|
+
setQuality(targetQuality) {
|
|
3742
|
+
if (this.options.debug) console.log(`setQuality("${targetQuality}") called`);
|
|
3523
3743
|
|
|
3524
|
-
|
|
3744
|
+
if (!targetQuality) {
|
|
3745
|
+
if (this.options.debug) console.error('targetQuality is empty!');
|
|
3746
|
+
return;
|
|
3525
3747
|
}
|
|
3526
3748
|
|
|
3527
|
-
|
|
3528
|
-
|
|
3749
|
+
if (!this.video || !this.qualities || this.qualities.length === 0) return;
|
|
3750
|
+
if (this.isChangingQuality) return;
|
|
3529
3751
|
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3752
|
+
const newSource = this.qualities.find(q => q.quality === targetQuality);
|
|
3753
|
+
if (!newSource || !newSource.src) {
|
|
3754
|
+
if (this.options.debug) console.error(`Quality "${targetQuality}" not found`);
|
|
3755
|
+
return;
|
|
3756
|
+
}
|
|
3534
3757
|
|
|
3535
|
-
|
|
3536
|
-
|
|
3758
|
+
const currentTime = this.video.currentTime || 0;
|
|
3759
|
+
const wasPlaying = !this.video.paused;
|
|
3537
3760
|
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
return;
|
|
3542
|
-
}
|
|
3761
|
+
this.isChangingQuality = true;
|
|
3762
|
+
this.selectedQuality = targetQuality;
|
|
3763
|
+
this.video.pause();
|
|
3543
3764
|
|
|
3544
|
-
|
|
3545
|
-
|
|
3765
|
+
|
|
3766
|
+
this.showLoading();
|
|
3767
|
+
if (this.video.classList) {
|
|
3768
|
+
this.video.classList.add('quality-changing');
|
|
3769
|
+
}
|
|
3546
3770
|
|
|
3547
|
-
|
|
3548
|
-
this.
|
|
3549
|
-
this.video.
|
|
3771
|
+
const onLoadedData = () => {
|
|
3772
|
+
if (this.options.debug) console.log(`Quality ${targetQuality} applied!`);
|
|
3773
|
+
this.video.currentTime = currentTime;
|
|
3550
3774
|
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3775
|
+
if (wasPlaying) {
|
|
3776
|
+
this.video.play().catch(e => {
|
|
3777
|
+
if (this.options.debug) console.log('Play error:', e);
|
|
3778
|
+
});
|
|
3555
3779
|
}
|
|
3556
3780
|
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
if (wasPlaying) {
|
|
3562
|
-
this.video.play().catch(e => {
|
|
3563
|
-
if (this.options.debug) console.log('Play error:', e);
|
|
3564
|
-
});
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
this.currentPlayingQuality = targetQuality;
|
|
3568
|
-
this.updateQualityDisplay();
|
|
3569
|
-
this.isChangingQuality = false;
|
|
3781
|
+
this.currentPlayingQuality = targetQuality;
|
|
3782
|
+
this.updateQualityDisplay();
|
|
3783
|
+
this.isChangingQuality = false;
|
|
3570
3784
|
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3785
|
+
|
|
3786
|
+
this.restoreResolutionAfterQualityChange();
|
|
3787
|
+
cleanup();
|
|
3788
|
+
};
|
|
3575
3789
|
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
cleanup();
|
|
3580
|
-
};
|
|
3790
|
+
const onError = (error) => {
|
|
3791
|
+
if (this.options.debug) console.error(`Loading error ${targetQuality}:`, error);
|
|
3792
|
+
this.isChangingQuality = false;
|
|
3581
3793
|
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
this.video.removeEventListener('error', onError);
|
|
3585
|
-
};
|
|
3794
|
+
|
|
3795
|
+
this.onVideoError(error);
|
|
3586
3796
|
|
|
3587
|
-
|
|
3588
|
-
|
|
3797
|
+
cleanup();
|
|
3798
|
+
};
|
|
3589
3799
|
|
|
3590
|
-
|
|
3591
|
-
this.video.
|
|
3592
|
-
|
|
3800
|
+
const cleanup = () => {
|
|
3801
|
+
this.video.removeEventListener('loadeddata', onLoadedData);
|
|
3802
|
+
this.video.removeEventListener('error', onError);
|
|
3803
|
+
};
|
|
3593
3804
|
|
|
3594
|
-
|
|
3595
|
-
|
|
3805
|
+
this.video.addEventListener('loadeddata', onLoadedData, { once: true });
|
|
3806
|
+
this.video.addEventListener('error', onError, { once: true });
|
|
3596
3807
|
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
}
|
|
3808
|
+
this.video.src = newSource.src;
|
|
3809
|
+
this.video.load();
|
|
3810
|
+
}
|
|
3601
3811
|
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
if (success && currentTime > 0 && this.video.duration) {
|
|
3605
|
-
this.video.currentTime = Math.min(currentTime, this.video.duration);
|
|
3606
|
-
}
|
|
3812
|
+
finishQualityChange(success, wasPlaying, currentTime, currentVolume, wasMuted, targetQuality) {
|
|
3813
|
+
if (this.options.debug) console.log(`Quality change completion: success=${success}, target=${targetQuality}`);
|
|
3607
3814
|
|
|
3608
|
-
|
|
3609
|
-
|
|
3815
|
+
if (this.qualityChangeTimeout) {
|
|
3816
|
+
clearTimeout(this.qualityChangeTimeout);
|
|
3817
|
+
this.qualityChangeTimeout = null;
|
|
3818
|
+
}
|
|
3610
3819
|
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
}
|
|
3616
|
-
} catch (error) {
|
|
3617
|
-
if (this.options.debug) console.error('Errore ripristino stato:', error);
|
|
3820
|
+
if (this.video) {
|
|
3821
|
+
try {
|
|
3822
|
+
if (success && currentTime > 0 && this.video.duration) {
|
|
3823
|
+
this.video.currentTime = Math.min(currentTime, this.video.duration);
|
|
3618
3824
|
}
|
|
3619
3825
|
|
|
3620
|
-
|
|
3621
|
-
|
|
3826
|
+
this.video.volume = currentVolume;
|
|
3827
|
+
this.video.muted = wasMuted;
|
|
3828
|
+
|
|
3829
|
+
if (success && wasPlaying) {
|
|
3830
|
+
this.video.play().catch(err => {
|
|
3831
|
+
if (this.options.debug) console.warn('Play after quality change failed:', err);
|
|
3832
|
+
});
|
|
3622
3833
|
}
|
|
3834
|
+
} catch (error) {
|
|
3835
|
+
if (this.options.debug) console.error('Errore ripristino stato:', error);
|
|
3623
3836
|
}
|
|
3624
3837
|
|
|
3625
|
-
this.
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
if (success) {
|
|
3629
|
-
if (this.options.debug) console.log('Quality change completed successfully');
|
|
3630
|
-
setTimeout(() => {
|
|
3631
|
-
this.currentPlayingQuality = targetQuality;
|
|
3632
|
-
this.updateQualityDisplay();
|
|
3633
|
-
if (this.options.debug) console.log(`🎯 Quality confirmed active: ${targetQuality}`);
|
|
3634
|
-
}, 100);
|
|
3635
|
-
} else {
|
|
3636
|
-
if (this.options.debug) console.warn('Quality change failed or timeout');
|
|
3838
|
+
if (this.video.classList) {
|
|
3839
|
+
this.video.classList.remove('quality-changing');
|
|
3637
3840
|
}
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
this.hideLoading();
|
|
3844
|
+
this.isChangingQuality = false;
|
|
3638
3845
|
|
|
3846
|
+
if (success) {
|
|
3847
|
+
if (this.options.debug) console.log('Quality change completed successfully');
|
|
3639
3848
|
setTimeout(() => {
|
|
3640
|
-
this.
|
|
3641
|
-
|
|
3849
|
+
this.currentPlayingQuality = targetQuality;
|
|
3850
|
+
this.updateQualityDisplay();
|
|
3851
|
+
if (this.options.debug) console.log(`🎯 Quality confirmed active: ${targetQuality}`);
|
|
3852
|
+
}, 100);
|
|
3853
|
+
} else {
|
|
3854
|
+
if (this.options.debug) console.warn('Quality change failed or timeout');
|
|
3642
3855
|
}
|
|
3643
3856
|
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
}
|
|
3649
|
-
}
|
|
3857
|
+
setTimeout(() => {
|
|
3858
|
+
this.updateCurrentPlayingQuality();
|
|
3859
|
+
}, 2000);
|
|
3860
|
+
}
|
|
3650
3861
|
|
|
3651
|
-
|
|
3652
|
-
|
|
3862
|
+
cleanupQualityChange() {
|
|
3863
|
+
if (this.qualityChangeTimeout) {
|
|
3864
|
+
clearTimeout(this.qualityChangeTimeout);
|
|
3865
|
+
this.qualityChangeTimeout = null;
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3653
3868
|
|
|
3654
|
-
|
|
3655
|
-
|
|
3869
|
+
enableAutoQuality() {
|
|
3870
|
+
if (this.options.debug) console.log('🔄 enableAutoQuality - keeping selectedQuality as "auto"');
|
|
3656
3871
|
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
this.updateQualityDisplay();
|
|
3660
|
-
return;
|
|
3661
|
-
}
|
|
3872
|
+
|
|
3873
|
+
this.selectedQuality = 'auto';
|
|
3662
3874
|
|
|
3663
|
-
|
|
3664
|
-
|
|
3875
|
+
if (!this.qualities || this.qualities.length === 0) {
|
|
3876
|
+
if (this.options.debug) console.warn('⚠️ No qualities available for auto selection');
|
|
3877
|
+
this.updateQualityDisplay();
|
|
3878
|
+
return;
|
|
3879
|
+
}
|
|
3665
3880
|
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
console.log('📊 selectedQuality remains: "auto" (for UI)');
|
|
3669
|
-
}
|
|
3881
|
+
|
|
3882
|
+
let autoSelectedQuality = this.getAutoQualityBasedOnConnection();
|
|
3670
3883
|
|
|
3671
|
-
|
|
3672
|
-
|
|
3884
|
+
if (this.options.debug) {
|
|
3885
|
+
console.log('🎯 Auto quality selected:', autoSelectedQuality);
|
|
3886
|
+
console.log('📊 selectedQuality remains: "auto" (for UI)');
|
|
3673
3887
|
}
|
|
3674
3888
|
|
|
3889
|
+
|
|
3890
|
+
this.applyAutoQuality(autoSelectedQuality);
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3675
3893
|
|
|
3676
3894
|
getAutoQualityBasedOnConnection() {
|
|
3677
3895
|
|
|
@@ -3936,102 +4154,120 @@ getAutoQualityBasedOnConnection() {
|
|
|
3936
4154
|
return maxQuality.quality;
|
|
3937
4155
|
}
|
|
3938
4156
|
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
4157
|
+
applyAutoQuality(targetQuality) {
|
|
4158
|
+
if (!targetQuality || !this.video || !this.qualities || this.qualities.length === 0) {
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
3943
4161
|
|
|
3944
|
-
|
|
4162
|
+
if (this.isChangingQuality) return;
|
|
3945
4163
|
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
4164
|
+
const newSource = this.qualities.find(q => q.quality === targetQuality);
|
|
4165
|
+
if (!newSource || !newSource.src) {
|
|
4166
|
+
if (this.options.debug) console.error('Auto quality', targetQuality, 'not found');
|
|
4167
|
+
return;
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
|
|
4171
|
+
const currentResolution = this.getCurrentResolution();
|
|
4172
|
+
|
|
4173
|
+
const currentTime = this.video.currentTime || 0;
|
|
4174
|
+
const wasPlaying = !this.video.paused;
|
|
4175
|
+
|
|
4176
|
+
this.isChangingQuality = true;
|
|
4177
|
+
this.video.pause();
|
|
4178
|
+
|
|
4179
|
+
|
|
4180
|
+
this.showLoading();
|
|
4181
|
+
if (this.video.classList) {
|
|
4182
|
+
this.video.classList.add('quality-changing');
|
|
4183
|
+
}
|
|
3951
4184
|
|
|
4185
|
+
|
|
4186
|
+
const onLoadedData = () => {
|
|
4187
|
+
if (this.options.debug) console.log('Auto quality', targetQuality, 'applied');
|
|
4188
|
+
this.video.currentTime = currentTime;
|
|
4189
|
+
if (wasPlaying) {
|
|
4190
|
+
this.video.play().catch(e => {
|
|
4191
|
+
if (this.options.debug) console.log('Autoplay prevented:', e);
|
|
4192
|
+
});
|
|
4193
|
+
}
|
|
4194
|
+
this.currentPlayingQuality = targetQuality;
|
|
3952
4195
|
|
|
3953
|
-
|
|
4196
|
+
this.updateQualityDisplay();
|
|
3954
4197
|
|
|
3955
|
-
|
|
3956
|
-
|
|
4198
|
+
|
|
4199
|
+
this.hideLoading();
|
|
4200
|
+
if (this.video.classList) {
|
|
4201
|
+
this.video.classList.remove('quality-changing');
|
|
4202
|
+
}
|
|
3957
4203
|
|
|
3958
|
-
this.isChangingQuality =
|
|
3959
|
-
|
|
4204
|
+
this.isChangingQuality = false;
|
|
4205
|
+
cleanup();
|
|
4206
|
+
};
|
|
3960
4207
|
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
if (wasPlaying) {
|
|
3965
|
-
this.video.play().catch(e => {
|
|
3966
|
-
if (this.options.debug) console.log('Autoplay prevented:', e);
|
|
3967
|
-
});
|
|
3968
|
-
}
|
|
3969
|
-
this.currentPlayingQuality = targetQuality;
|
|
3970
|
-
|
|
3971
|
-
this.updateQualityDisplay();
|
|
3972
|
-
this.isChangingQuality = false;
|
|
3973
|
-
cleanup();
|
|
3974
|
-
};
|
|
4208
|
+
const onError = (error) => {
|
|
4209
|
+
if (this.options.debug) console.error('Auto quality loading error:', error);
|
|
4210
|
+
this.isChangingQuality = false;
|
|
3975
4211
|
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
this.isChangingQuality = false;
|
|
3979
|
-
cleanup();
|
|
3980
|
-
};
|
|
4212
|
+
|
|
4213
|
+
this.onVideoError(error);
|
|
3981
4214
|
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
this.video.removeEventListener('error', onError);
|
|
3985
|
-
};
|
|
4215
|
+
cleanup();
|
|
4216
|
+
};
|
|
3986
4217
|
|
|
3987
|
-
|
|
3988
|
-
this.video.
|
|
3989
|
-
this.video.
|
|
3990
|
-
|
|
3991
|
-
}
|
|
4218
|
+
const cleanup = () => {
|
|
4219
|
+
this.video.removeEventListener('loadeddata', onLoadedData);
|
|
4220
|
+
this.video.removeEventListener('error', onError);
|
|
4221
|
+
};
|
|
3992
4222
|
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4223
|
+
this.video.addEventListener('loadeddata', onLoadedData, { once: true });
|
|
4224
|
+
this.video.addEventListener('error', onError, { once: true });
|
|
4225
|
+
this.video.src = newSource.src;
|
|
4226
|
+
this.video.load();
|
|
4227
|
+
}
|
|
3997
4228
|
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
}
|
|
4229
|
+
setDefaultQuality(quality) {
|
|
4230
|
+
if (this.options.debug) console.log(`🔧 Setting defaultQuality: "${quality}"`);
|
|
4231
|
+
this.options.defaultQuality = quality;
|
|
4232
|
+
this.selectedQuality = quality;
|
|
4003
4233
|
|
|
4004
|
-
|
|
4234
|
+
if (quality === 'auto') {
|
|
4235
|
+
this.enableAutoQuality();
|
|
4236
|
+
} else {
|
|
4237
|
+
this.setQuality(quality);
|
|
4005
4238
|
}
|
|
4006
4239
|
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
}
|
|
4240
|
+
return this;
|
|
4241
|
+
}
|
|
4010
4242
|
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
if (height >= 1080) return '1080p';
|
|
4015
|
-
if (height >= 720) return '720p';
|
|
4016
|
-
if (height >= 480) return '480p';
|
|
4017
|
-
if (height >= 360) return '360p';
|
|
4018
|
-
if (height >= 240) return '240p';
|
|
4019
|
-
return `${height}p`;
|
|
4020
|
-
}
|
|
4243
|
+
getDefaultQuality() {
|
|
4244
|
+
return this.options.defaultQuality;
|
|
4245
|
+
}
|
|
4021
4246
|
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4247
|
+
getQualityLabel(height, width) {
|
|
4248
|
+
if (height >= 2160) return '4K';
|
|
4249
|
+
if (height >= 1440) return '1440p';
|
|
4250
|
+
if (height >= 1080) return '1080p';
|
|
4251
|
+
if (height >= 720) return '720p';
|
|
4252
|
+
if (height >= 480) return '480p';
|
|
4253
|
+
if (height >= 360) return '360p';
|
|
4254
|
+
if (height >= 240) return '240p';
|
|
4255
|
+
return `${height}p`;
|
|
4256
|
+
}
|
|
4025
4257
|
|
|
4026
|
-
|
|
4258
|
+
updateAdaptiveQualityMenu() {
|
|
4259
|
+
const qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
4260
|
+
if (!qualityMenu || !this.isAdaptiveStream) return;
|
|
4027
4261
|
|
|
4028
|
-
|
|
4029
|
-
const isActive = this.getCurrentAdaptiveQuality() === quality.index;
|
|
4030
|
-
menuHTML += `<div class="quality-option ${isActive ? 'active' : ''}" data-adaptive-quality="${quality.index}">${quality.label}</div>`;
|
|
4031
|
-
});
|
|
4262
|
+
let menuHTML = `<div class="quality-option ${this.isAutoQuality() ? 'active' : ''}" data-adaptive-quality="auto">Auto</div>`;
|
|
4032
4263
|
|
|
4033
|
-
|
|
4034
|
-
|
|
4264
|
+
this.adaptiveQualities.forEach(quality => {
|
|
4265
|
+
const isActive = this.getCurrentAdaptiveQuality() === quality.index;
|
|
4266
|
+
menuHTML += `<div class="quality-option ${isActive ? 'active' : ''}" data-adaptive-quality="${quality.index}">${quality.label}</div>`;
|
|
4267
|
+
});
|
|
4268
|
+
|
|
4269
|
+
qualityMenu.innerHTML = menuHTML;
|
|
4270
|
+
}
|
|
4035
4271
|
|
|
4036
4272
|
updateAdaptiveQualityDisplay() {
|
|
4037
4273
|
if (!this.isAdaptiveStream) return;
|
|
@@ -4057,61 +4293,61 @@ updateAdaptiveQualityDisplay() {
|
|
|
4057
4293
|
}
|
|
4058
4294
|
}
|
|
4059
4295
|
|
|
4060
|
-
|
|
4061
|
-
|
|
4296
|
+
setAdaptiveQuality(qualityIndex) {
|
|
4297
|
+
if (!this.isAdaptiveStream) return;
|
|
4062
4298
|
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
}
|
|
4088
|
-
this.selectedQuality = this.adaptiveQualities[qualityIndex]?.label || 'Unknown';
|
|
4299
|
+
try {
|
|
4300
|
+
if (qualityIndex === 'auto' || qualityIndex === -1) {
|
|
4301
|
+
|
|
4302
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
4303
|
+
this.dashPlayer.updateSettings({
|
|
4304
|
+
streaming: {
|
|
4305
|
+
abr: { autoSwitchBitrate: { video: true } }
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
4308
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
4309
|
+
this.hlsPlayer.currentLevel = -1;
|
|
4310
|
+
}
|
|
4311
|
+
this.selectedQuality = 'auto';
|
|
4312
|
+
} else {
|
|
4313
|
+
|
|
4314
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
4315
|
+
this.dashPlayer.updateSettings({
|
|
4316
|
+
streaming: {
|
|
4317
|
+
abr: { autoSwitchBitrate: { video: false } }
|
|
4318
|
+
}
|
|
4319
|
+
});
|
|
4320
|
+
this.dashPlayer.setQualityFor('video', qualityIndex);
|
|
4321
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
4322
|
+
this.hlsPlayer.currentLevel = qualityIndex;
|
|
4089
4323
|
}
|
|
4324
|
+
this.selectedQuality = this.adaptiveQualities[qualityIndex]?.label || 'Unknown';
|
|
4325
|
+
}
|
|
4090
4326
|
|
|
4091
|
-
|
|
4092
|
-
|
|
4327
|
+
this.updateAdaptiveQualityDisplay();
|
|
4328
|
+
if (this.options.debug) console.log('📡 Adaptive quality set to:', qualityIndex);
|
|
4093
4329
|
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
}
|
|
4330
|
+
} catch (error) {
|
|
4331
|
+
if (this.options.debug) console.error('📡 Error setting adaptive quality:', error);
|
|
4097
4332
|
}
|
|
4333
|
+
}
|
|
4098
4334
|
|
|
4099
|
-
|
|
4100
|
-
|
|
4335
|
+
getCurrentAdaptiveQuality() {
|
|
4336
|
+
if (!this.isAdaptiveStream) return null;
|
|
4101
4337
|
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
}
|
|
4108
|
-
} catch (error) {
|
|
4109
|
-
if (this.options.debug) console.error('📡 Error getting current quality:', error);
|
|
4338
|
+
try {
|
|
4339
|
+
if (this.adaptiveStreamingType === 'dash' && this.dashPlayer) {
|
|
4340
|
+
return this.dashPlayer.getQualityFor('video');
|
|
4341
|
+
} else if (this.adaptiveStreamingType === 'hls' && this.hlsPlayer) {
|
|
4342
|
+
return this.hlsPlayer.currentLevel;
|
|
4110
4343
|
}
|
|
4111
|
-
|
|
4112
|
-
|
|
4344
|
+
} catch (error) {
|
|
4345
|
+
if (this.options.debug) console.error('📡 Error getting current quality:', error);
|
|
4113
4346
|
}
|
|
4114
4347
|
|
|
4348
|
+
return null;
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4115
4351
|
getCurrentAdaptiveQualityLabel() {
|
|
4116
4352
|
const currentIndex = this.getCurrentAdaptiveQuality();
|
|
4117
4353
|
if (currentIndex === null || currentIndex === -1) {
|
|
@@ -4120,75 +4356,75 @@ getCurrentAdaptiveQualityLabel() {
|
|
|
4120
4356
|
return this.adaptiveQualities[currentIndex]?.label || this.tauto;
|
|
4121
4357
|
}
|
|
4122
4358
|
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
}
|
|
4128
|
-
return this.selectedQuality === 'auto';
|
|
4359
|
+
isAutoQuality() {
|
|
4360
|
+
if (this.isAdaptiveStream) {
|
|
4361
|
+
const currentQuality = this.getCurrentAdaptiveQuality();
|
|
4362
|
+
return currentQuality === null || currentQuality === -1 || this.selectedQuality === 'auto';
|
|
4129
4363
|
}
|
|
4364
|
+
return this.selectedQuality === 'auto';
|
|
4365
|
+
}
|
|
4130
4366
|
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4367
|
+
setResolution(resolution) {
|
|
4368
|
+
if (!this.video || !this.container) {
|
|
4369
|
+
if (this.options.debug) console.warn("Video or container not available for setResolution");
|
|
4370
|
+
return;
|
|
4371
|
+
}
|
|
4136
4372
|
|
|
4137
|
-
|
|
4138
|
-
|
|
4373
|
+
|
|
4374
|
+
const supportedResolutions = ["normal", "4:3", "16:9", "stretched", "fit-to-screen", "scale-to-fit"];
|
|
4139
4375
|
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4376
|
+
if (!supportedResolutions.includes(resolution)) {
|
|
4377
|
+
if (this.options.debug) console.warn(`Resolution "${resolution}" not supported. Supported values: ${supportedResolutions.join(", ")}`);
|
|
4378
|
+
return;
|
|
4379
|
+
}
|
|
4144
4380
|
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4381
|
+
|
|
4382
|
+
const allResolutionClasses = [
|
|
4383
|
+
"resolution-normal", "resolution-4-3", "resolution-16-9",
|
|
4384
|
+
"resolution-stretched", "resolution-fit-to-screen", "resolution-scale-to-fit"
|
|
4385
|
+
];
|
|
4150
4386
|
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4387
|
+
this.video.classList.remove(...allResolutionClasses);
|
|
4388
|
+
if (this.container) {
|
|
4389
|
+
this.container.classList.remove(...allResolutionClasses);
|
|
4390
|
+
}
|
|
4155
4391
|
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4392
|
+
|
|
4393
|
+
const cssClass = `resolution-${resolution.replace(":", "-")}`;
|
|
4394
|
+
this.video.classList.add(cssClass);
|
|
4395
|
+
if (this.container) {
|
|
4396
|
+
this.container.classList.add(cssClass);
|
|
4397
|
+
}
|
|
4162
4398
|
|
|
4163
|
-
|
|
4164
|
-
|
|
4399
|
+
|
|
4400
|
+
this.options.resolution = resolution;
|
|
4165
4401
|
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
}
|
|
4402
|
+
if (this.options.debug) {
|
|
4403
|
+
console.log(`Resolution applied: ${resolution} (CSS class: ${cssClass})`);
|
|
4169
4404
|
}
|
|
4405
|
+
}
|
|
4170
4406
|
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4407
|
+
getCurrentResolution() {
|
|
4408
|
+
return this.options.resolution || "normal";
|
|
4409
|
+
}
|
|
4174
4410
|
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
}
|
|
4411
|
+
initializeResolution() {
|
|
4412
|
+
if (this.options.resolution && this.options.resolution !== "normal") {
|
|
4413
|
+
this.setResolution(this.options.resolution);
|
|
4179
4414
|
}
|
|
4415
|
+
}
|
|
4180
4416
|
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
}
|
|
4186
|
-
|
|
4187
|
-
setTimeout(() => {
|
|
4188
|
-
this.setResolution(this.options.resolution);
|
|
4189
|
-
}, 150);
|
|
4417
|
+
restoreResolutionAfterQualityChange() {
|
|
4418
|
+
if (this.options.resolution && this.options.resolution !== "normal") {
|
|
4419
|
+
if (this.options.debug) {
|
|
4420
|
+
console.log(`Restoring resolution "${this.options.resolution}" after quality change`);
|
|
4190
4421
|
}
|
|
4422
|
+
|
|
4423
|
+
setTimeout(() => {
|
|
4424
|
+
this.setResolution(this.options.resolution);
|
|
4425
|
+
}, 150);
|
|
4191
4426
|
}
|
|
4427
|
+
}
|
|
4192
4428
|
|
|
4193
4429
|
|
|
4194
4430
|
|
|
@@ -4230,7 +4466,7 @@ createCustomSubtitleOverlay() {
|
|
|
4230
4466
|
'bottom: 80px;' +
|
|
4231
4467
|
'left: 50%;' +
|
|
4232
4468
|
'transform: translateX(-50%);' +
|
|
4233
|
-
'z-index: 5;' +
|
|
4469
|
+
'z-index: 5;' +
|
|
4234
4470
|
'color: white;' +
|
|
4235
4471
|
'font-family: Arial, sans-serif;' +
|
|
4236
4472
|
'font-size: clamp(12px, 4vw, 18px);' +
|
|
@@ -4324,7 +4560,7 @@ parseCustomSRT(srtText) {
|
|
|
4324
4560
|
if (timeMatch) {
|
|
4325
4561
|
var startTime = this.customTimeToSeconds(timeMatch[1]);
|
|
4326
4562
|
var endTime = this.customTimeToSeconds(timeMatch[2]);
|
|
4327
|
-
var text = lines.slice(2).join(' ').trim()
|
|
4563
|
+
var text = this.sanitizeSubtitleText(lines.slice(2).join(' ').trim());
|
|
4328
4564
|
|
|
4329
4565
|
if (text.length > 0 && startTime < endTime) {
|
|
4330
4566
|
subtitles.push({
|