support for subs on local files

This commit is contained in:
2026-01-06 18:55:03 +01:00
parent 6c9f021e8d
commit ba05e08e71
12 changed files with 760 additions and 810 deletions

View File

@@ -857,27 +857,15 @@ const AnimePlayer = (function() {
if (hlsInstance) hlsInstance.audioTrack = parseInt(value);
} else if (type === 'subtitle') {
const idx = parseInt(value);
_activeSubtitleIndex = idx; // <--- ACTUALIZAMOS EL ESTADO AQUÍ
_activeSubtitleIndex = idx;
// 1. Lógica nativa (para mantener compatibilidad interna)
if (els.video && els.video.textTracks) {
Array.from(els.video.textTracks).forEach((track, i) => {
// Si usamos JASSUB, ocultamos la nativa. Si no, mostramos la seleccionada.
track.mode = (subtitleRenderer && idx !== -1) ? 'hidden' : ((i === idx) ? 'showing' : 'hidden');
track.mode = 'hidden';
});
}
// 2. Lógica de JASSUB
if (subtitleRenderer) {
if (idx === -1) {
subtitleRenderer.dispose();
} else {
const sub = _currentSubtitles[idx];
if (sub) {
subtitleRenderer.setTrack(sub.src);
}
}
}
initSubtitleRenderer();
} else if (type === 'speed') {
if (els.video) els.video.playbackRate = parseFloat(value);
}
@@ -1185,20 +1173,6 @@ const AnimePlayer = (function() {
if(els.loader) els.loader.style.display = 'flex';
}
async function getLocalEntryId() {
if (_localEntryId) return _localEntryId;
try {
const res = await fetch(`/api/library/anime/${_animeId}`);
if (!res.ok) return null;
const data = await res.json();
_localEntryId = data.id;
return _localEntryId;
} catch (e) {
console.error("Error fetching local ID:", e);
return null;
}
}
async function loadStream() {
if (!_currentEpisode) return;
_progressUpdated = false;
@@ -1225,16 +1199,21 @@ const AnimePlayer = (function() {
_rawVideoData = null;
}
// En la función loadStream(), cuando detectas local:
if (currentExt === 'local') {
try {
setLoading("Fetching Local Unit Data...");
// 1. Obtener las unidades locales para encontrar el ID del episodio específico
// 1. Obtener unidades
const unitsRes = await fetch(`/api/library/${_animeId}/units`);
if (!unitsRes.ok) throw new Error("Could not fetch local units");
const unitsData = await unitsRes.json();
const targetUnit = unitsData.units ? unitsData.units.find(u => u.number === parseInt(_currentEpisode)) : null;
_localEntryId = unitsData.entry_id;
const targetUnit = unitsData.units ?
unitsData.units.find(u => u.number === parseInt(_currentEpisode)) : null;
if (!targetUnit) {
throw new Error(`Episode ${_currentEpisode} not found in local library`);
@@ -1242,46 +1221,70 @@ const AnimePlayer = (function() {
setLoading("Initializing HLS Stream...");
const manifestRes = await fetch(`/api/library/stream/anime/${unitsData.entry_id}/${targetUnit.number}/manifest`);
// 2. Obtener Manifest
const manifestRes = await fetch(
`/api/library/stream/anime/${unitsData.entry_id}/${targetUnit.number}/manifest`
);
if (!manifestRes.ok) throw new Error("Failed to generate stream manifest");
const manifestData = await manifestRes.json();
// DEBUG: Verificar que llega aquí
console.log("Manifest Loaded:", manifestData);
// 3. Chapters
if (manifestData.chapters && manifestData.chapters.length > 0) {
_skipIntervals = manifestData.chapters.map(c => ({
startTime: c.start,
endTime: c.end,
type: c.title.toLowerCase().includes('op') ? 'op' :
c.title.toLowerCase().includes('ed') ? 'ed' : 'chapter'
type: (c.title || '').toLowerCase().includes('op') ? 'op' :
(c.title || '').toLowerCase().includes('ed') ? 'ed' : 'chapter'
}));
renderSkipMarkers(_skipIntervals);
monitorSkipButton(_skipIntervals);
}
// 4. Mapear Subtítulos WebVTT
const subs = (manifestData.subtitles || []).map(s => ({
label: s.title || s.language || `Track ${s.index}`,
srclang: s.language || 'unk',
src: s.url // URL al endpoint de conversión VTT (.vtt)
}));
// 4. CORRECCIÓN DE SUBTÍTULOS
const rawSubs = manifestData.subtitles || [];
console.log("Raw Subtitles from JSON:", rawSubs);
const subs = rawSubs.map((s, index) => {
// Limpieza segura del código de idioma (ej. "English" -> "en")
let langCode = 'en';
if (s.language && typeof s.language === 'string' && s.language.length >= 2) {
langCode = s.language.substring(0, 2).toLowerCase();
}
// Título legible (Prioridad: Title > Language > Track #)
const label = s.title || s.language || `Track ${index + 1}`;
return {
label: label,
srclang: langCode,
src: s.url // Usamos la URL directa del JSON local
};
});
// ACTUALIZACIÓN CRÍTICA DEL ESTADO GLOBAL
_currentSubtitles = subs;
console.log("Processed Subtitles:", _currentSubtitles);
// 5. Guardar referencia para MPV o descargas
_rawVideoData = {
url: manifestData.masterPlaylist,
headers: {}
};
// 6. Cargar en el reproductor (Hls.js gestionará los audios del master.m3u8)
// Inicializar video pasando explícitamente los subs procesados
initVideoPlayer(manifestData.masterPlaylist, 'm3u8', subs);
} catch(e) {
console.error("Local HLS Error:", e);
setLoading("Local Error: " + e.message);
// Fallback: si falla, intentar cargar desde extensión online
// Fallback logic...
const localOption = els.extSelect.querySelector('option[value="local"]');
if (localOption) localOption.remove();
const fallbackSource = (_entrySource === 'local') ? 'anilist' : _entrySource;
els.extSelect.value = fallbackSource;
handleExtensionChange(true);