player now supports multiple audio tracks
This commit is contained in:
@@ -463,18 +463,12 @@ const AnimePlayer = (function() {
|
|||||||
|
|
||||||
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
attachSubtitles(subtitles);
|
attachSubtitles(subtitles);
|
||||||
els.video.addEventListener('loadedmetadata', () => {
|
|
||||||
applyAniSkip(_malId, _currentEpisode);
|
|
||||||
}, { once: true });
|
|
||||||
initPlyr();
|
initPlyr();
|
||||||
els.video.addEventListener('canplay', () => {
|
|
||||||
els.video.play().catch(() => {});
|
els.video.play().catch(() => {});
|
||||||
}, { once: true });
|
els.loader.style.display = 'none';
|
||||||
if (els.loader) els.loader.style.display = 'none';
|
|
||||||
});
|
|
||||||
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
|
||||||
if (data.fatal) setLoading("Playback Error: " + data.details);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
els.video.src = url;
|
els.video.src = url;
|
||||||
attachSubtitles(subtitles);
|
attachSubtitles(subtitles);
|
||||||
@@ -499,10 +493,44 @@ const AnimePlayer = (function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPlyr() {
|
function createAudioSelector(hls) {
|
||||||
|
if (!hls.audioTracks || hls.audioTracks.length < 2) return;
|
||||||
|
|
||||||
|
const plyrEl = els.video.closest('.plyr');
|
||||||
|
const controls = plyrEl.querySelector('.plyr__controls');
|
||||||
|
if (!controls) return;
|
||||||
|
|
||||||
|
if (controls.querySelector('#audio-select')) return;
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'plyr__control';
|
||||||
|
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.id = 'audio-select';
|
||||||
|
|
||||||
|
hls.audioTracks.forEach((t, i) => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = i;
|
||||||
|
opt.textContent = t.name || t.lang || `Audio ${i + 1}`;
|
||||||
|
select.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.value = hls.audioTrack;
|
||||||
|
|
||||||
|
select.onchange = () => {
|
||||||
|
hls.audioTrack = Number(select.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper.appendChild(select);
|
||||||
|
controls.insertBefore(wrapper, controls.children[4]); // antes del volumen
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPlyr(enableAudio = false) {
|
||||||
if (plyrInstance) return;
|
if (plyrInstance) return;
|
||||||
|
|
||||||
|
const settings = ['captions', 'quality', 'speed'];
|
||||||
|
if (enableAudio) settings.unshift('audio');
|
||||||
|
|
||||||
plyrInstance = new Plyr(els.video, {
|
plyrInstance = new Plyr(els.video, {
|
||||||
captions: {
|
captions: {
|
||||||
active: true,
|
active: true,
|
||||||
@@ -520,7 +548,7 @@ const AnimePlayer = (function() {
|
|||||||
'mute', 'volume', 'captions', 'settings',
|
'mute', 'volume', 'captions', 'settings',
|
||||||
'fullscreen', 'airplay'
|
'fullscreen', 'airplay'
|
||||||
],
|
],
|
||||||
settings: ['captions', 'quality', 'speed']
|
settings
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = document.querySelector('.player-container');
|
const container = document.querySelector('.player-container');
|
||||||
@@ -530,6 +558,10 @@ const AnimePlayer = (function() {
|
|||||||
const tracks = els.video.textTracks;
|
const tracks = els.video.textTracks;
|
||||||
if (tracks && tracks.length) tracks[0].mode = 'showing';
|
if (tracks && tracks.length) tracks[0].mode = 'showing';
|
||||||
|
|
||||||
|
plyrInstance.on('ready', () => {
|
||||||
|
if (hlsInstance) createAudioSelector(hlsInstance);
|
||||||
|
});
|
||||||
|
|
||||||
plyrInstance.on('timeupdate', (event) => {
|
plyrInstance.on('timeupdate', (event) => {
|
||||||
const instance = event.detail.plyr;
|
const instance = event.detail.plyr;
|
||||||
if (!instance.duration || _progressUpdated) return;
|
if (!instance.duration || _progressUpdated) return;
|
||||||
|
|||||||
@@ -536,4 +536,15 @@ body.stop-scrolling {
|
|||||||
|
|
||||||
.glass-btn-mpv:active {
|
.glass-btn-mpv:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#audio-select {
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
#audio-select option {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|||||||
@@ -386,26 +386,13 @@ const AnimePlayer = (function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
subtitles.forEach((sub, i) => {
|
attachSubtitles(subtitles);
|
||||||
const track = document.createElement('track');
|
|
||||||
track.kind = 'subtitles';
|
|
||||||
track.label = sub.label;
|
|
||||||
track.srclang = sub.srclang;
|
|
||||||
track.src = sub.src;
|
|
||||||
track.default = i === 0;
|
|
||||||
video.appendChild(track);
|
|
||||||
});
|
|
||||||
|
|
||||||
els.video.addEventListener('loadedmetadata', () => {
|
|
||||||
applyAniSkip(_malId, _currentEpisode);
|
|
||||||
}, { once: true });
|
|
||||||
|
|
||||||
initPlyr();
|
initPlyr();
|
||||||
video.addEventListener('canplay', () => {
|
|
||||||
video.play().catch(() => {});
|
els.video.play().catch(() => {});
|
||||||
}, { once: true });
|
els.loader.style.display = 'none';
|
||||||
if (els.loader) els.loader.style.display = 'none';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
||||||
console.error("HLS Error:", data);
|
console.error("HLS Error:", data);
|
||||||
if (data.fatal) {
|
if (data.fatal) {
|
||||||
@@ -413,33 +400,65 @@ const AnimePlayer = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("Using Native Player (MP4/WebM)");
|
els.video.src = url;
|
||||||
video.src = url;
|
attachSubtitles(subtitles);
|
||||||
|
|
||||||
subtitles.forEach((sub, i) => {
|
|
||||||
const track = document.createElement('track');
|
|
||||||
track.kind = 'subtitles';
|
|
||||||
track.label = sub.label;
|
|
||||||
track.srclang = sub.srclang;
|
|
||||||
track.src = sub.src;
|
|
||||||
track.default = i === 0;
|
|
||||||
video.appendChild(track);
|
|
||||||
});
|
|
||||||
|
|
||||||
initPlyr();
|
initPlyr();
|
||||||
|
els.video.play().catch(e => console.log("Autoplay blocked", e));
|
||||||
video.play().catch(e => console.log("Autoplay blocked", e));
|
|
||||||
|
|
||||||
els.video.addEventListener('loadedmetadata', () => {
|
els.video.addEventListener('loadedmetadata', () => {
|
||||||
applyAniSkip(_malId, _currentEpisode);
|
applyAniSkip(_malId, _currentEpisode);
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
|
|
||||||
if(els.loader) els.loader.style.display = 'none';
|
if(els.loader) els.loader.style.display = 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPlyr() {
|
function attachSubtitles(subtitles) {
|
||||||
|
subtitles.forEach((sub, i) => {
|
||||||
|
const track = document.createElement('track');
|
||||||
|
track.kind = 'subtitles';
|
||||||
|
track.label = sub.label;
|
||||||
|
track.srclang = sub.srclang;
|
||||||
|
track.src = sub.src;
|
||||||
|
track.default = i === 0;
|
||||||
|
els.video.appendChild(track);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAudioSelector(hls) {
|
||||||
|
if (!hls.audioTracks || hls.audioTracks.length < 2) return;
|
||||||
|
|
||||||
|
const plyrEl = els.video.closest('.plyr');
|
||||||
|
const controls = plyrEl.querySelector('.plyr__controls');
|
||||||
|
if (!controls) return;
|
||||||
|
|
||||||
|
if (controls.querySelector('#audio-select')) return;
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'plyr__control';
|
||||||
|
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.id = 'audio-select';
|
||||||
|
|
||||||
|
hls.audioTracks.forEach((t, i) => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = i;
|
||||||
|
opt.textContent = t.name || t.lang || `Audio ${i + 1}`;
|
||||||
|
select.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.value = hls.audioTrack;
|
||||||
|
|
||||||
|
select.onchange = () => {
|
||||||
|
hls.audioTrack = Number(select.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper.appendChild(select);
|
||||||
|
controls.insertBefore(wrapper, controls.children[4]); // antes del volumen
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPlyr(enableAudio = false) {
|
||||||
if (plyrInstance) return;
|
if (plyrInstance) return;
|
||||||
|
const settings = ['captions', 'quality', 'speed'];
|
||||||
|
if (enableAudio) settings.unshift('audio');
|
||||||
|
|
||||||
plyrInstance = new Plyr(els.video, {
|
plyrInstance = new Plyr(els.video, {
|
||||||
captions: {
|
captions: {
|
||||||
@@ -451,26 +470,26 @@ const AnimePlayer = (function() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
fallback: true,
|
fallback: true,
|
||||||
iosNative: true,
|
iosNative: true,
|
||||||
container: '.player-container' // IMPORTANTE: El contenedor padre entra en fullscreen
|
container: '.player-container'
|
||||||
},
|
},
|
||||||
controls: [
|
controls: [
|
||||||
'play-large', 'play', 'progress', 'current-time',
|
'play-large', 'play', 'progress', 'current-time',
|
||||||
'mute', 'volume', 'captions', 'settings',
|
'mute', 'volume', 'captions', 'settings',
|
||||||
'fullscreen', 'airplay'
|
'fullscreen', 'airplay'
|
||||||
],
|
],
|
||||||
settings: ['captions', 'quality', 'speed']
|
settings
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- MAGIA NUEVA AQUÍ ---
|
|
||||||
// Sincronizar la UI personalizada con los eventos de Plyr
|
|
||||||
const container = document.querySelector('.player-container');
|
const container = document.querySelector('.player-container');
|
||||||
|
|
||||||
// Cuando Plyr esconde sus controles (inactividad)
|
|
||||||
plyrInstance.on('controlshidden', () => {
|
plyrInstance.on('controlshidden', () => {
|
||||||
container.classList.add('ui-hidden');
|
container.classList.add('ui-hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cuando Plyr muestra sus controles (movimiento de mouse)
|
plyrInstance.on('ready', () => {
|
||||||
|
if (hlsInstance) createAudioSelector(hlsInstance);
|
||||||
|
});
|
||||||
|
|
||||||
plyrInstance.on('controlsshown', () => {
|
plyrInstance.on('controlsshown', () => {
|
||||||
container.classList.remove('ui-hidden');
|
container.classList.remove('ui-hidden');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -504,4 +504,47 @@ body.stop-scrolling {
|
|||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
visibility: visible !important;
|
visibility: visible !important;
|
||||||
pointer-events: auto !important;
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-btn-mpv {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
color: white;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-btn-mpv:hover {
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-btn-mpv svg {
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-btn-mpv:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
#audio-select {
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
#audio-select option {
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user