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