code organisation & refactor

This commit is contained in:
2025-11-27 20:23:00 +01:00
parent 03636fee99
commit 2b29beeeb5
33 changed files with 2099 additions and 1947 deletions

211
src/scripts/anime/player.js Normal file
View File

@@ -0,0 +1,211 @@
const pathParts = window.location.pathname.split('/');
const animeId = pathParts[2];
const currentEpisode = parseInt(pathParts[3]);
let audioMode = 'sub';
let currentExtension = '';
let plyrInstance;
let hlsInstance;
document.getElementById('back-link').href = `/anime/${animeId}`;
document.getElementById('episode-label').innerText = `Episode ${currentEpisode}`;
async function loadMetadata() {
try {
const res = await fetch(`/api/anime/${animeId}`);
const data = await res.json();
if(!data.error) {
const title = data.title.english || data.title.romaji;
document.getElementById('anime-title').innerText = title;
document.title = `Watching ${title} - Ep ${currentEpisode}`;
}
} catch(e) { console.error(e); }
}
async function loadExtensions() {
try {
const res = await fetch('/api/extensions');
const data = await res.json();
const select = document.getElementById('extension-select');
if (data.extensions && data.extensions.length > 0) {
data.extensions.forEach(extName => {
const opt = document.createElement('option');
opt.value = extName;
opt.innerText = extName;
select.appendChild(opt);
});
} else {
select.innerHTML = '<option>No Extensions</option>';
select.disabled = true;
setLoading("No extensions found in WaifuBoards folder.");
}
} catch(e) { console.error("Extension Error:", e); }
}
async function onExtensionChange() {
const select = document.getElementById('extension-select');
currentExtension = select.value;
setLoading("Fetching extension settings...");
try {
const res = await fetch(`/api/extension/${currentExtension}/settings`);
const settings = await res.json();
const toggle = document.getElementById('sd-toggle');
if (settings.supportsDub) {
toggle.style.display = 'flex';
setAudioMode('sub');
} else {
toggle.style.display = 'none';
setAudioMode('sub');
}
const serverSelect = document.getElementById('server-select');
serverSelect.innerHTML = '';
if (settings.episodeServers && settings.episodeServers.length > 0) {
settings.episodeServers.forEach(srv => {
const opt = document.createElement('option');
opt.value = srv;
opt.innerText = srv;
serverSelect.appendChild(opt);
});
serverSelect.style.display = 'block';
} else {
serverSelect.style.display = 'none';
}
loadStream();
} catch (err) {
console.error(err);
setLoading("Failed to load extension settings.");
}
}
function toggleAudioMode() {
const newMode = audioMode === 'sub' ? 'dub' : 'sub';
setAudioMode(newMode);
loadStream();
}
function setAudioMode(mode) {
audioMode = mode;
const toggle = document.getElementById('sd-toggle');
const subOpt = document.getElementById('opt-sub');
const dubOpt = document.getElementById('opt-dub');
toggle.setAttribute('data-state', mode);
if (mode === 'sub') {
subOpt.classList.add('active');
dubOpt.classList.remove('active');
} else {
subOpt.classList.remove('active');
dubOpt.classList.add('active');
}
}
async function loadStream() {
if (!currentExtension) return;
const serverSelect = document.getElementById('server-select');
const server = serverSelect.value || "default";
setLoading(`Searching & Resolving Stream (${audioMode})...`);
try {
const url = `/api/watch/stream?animeId=${animeId}&episode=${currentEpisode}&server=${server}&category=${audioMode}&ext=${currentExtension}`;
const res = await fetch(url);
const data = await res.json();
if (data.error) {
setLoading(`Error: ${data.error}`);
return;
}
if (!data.videoSources || data.videoSources.length === 0) {
setLoading("No video sources found.");
return;
}
const source = data.videoSources.find(s => s.type === 'm3u8') || data.videoSources[0];
const headers = data.headers || {};
let proxyUrl = `/api/proxy?url=${encodeURIComponent(source.url)}`;
if (headers['Referer']) proxyUrl += `&referer=${encodeURIComponent(headers['Referer'])}`;
if (headers['Origin']) proxyUrl += `&origin=${encodeURIComponent(headers['Origin'])}`;
if (headers['User-Agent']) proxyUrl += `&userAgent=${encodeURIComponent(headers['User-Agent'])}`;
playVideo(proxyUrl, data.videoSources[0].subtitles);
document.getElementById('loading-overlay').style.display = 'none';
} catch (err) {
setLoading("Stream Error. Check console.");
console.error(err);
}
}
function playVideo(url, subtitles) {
const video = document.getElementById('player');
if (Hls.isSupported()) {
if (hlsInstance) hlsInstance.destroy();
hlsInstance = new Hls({
xhrSetup: (xhr, url) => {
xhr.withCredentials = false;
}
});
hlsInstance.loadSource(url);
hlsInstance.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
}
if (plyrInstance) plyrInstance.destroy();
while (video.firstChild) {
video.removeChild(video.firstChild);
}
if (subtitles && subtitles.length > 0) {
subtitles.forEach(sub => {
const track = document.createElement('track');
track.kind = 'captions';
track.label = sub.language;
track.srclang = sub.language.slice(0, 2).toLowerCase();
track.src = sub.url;
if (sub.default || sub.language.toLowerCase().includes('english')) {
track.default = true;
}
video.appendChild(track);
});
}
plyrInstance = new Plyr(video, {
captions: { active: true, update: true, language: 'en' },
controls: ['play-large', 'play', 'progress', 'current-time', 'duration', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'],
settings: ['captions', 'quality', 'speed']
});
video.play().catch(e => console.log("Auto-play blocked"));
}
function setLoading(msg) {
const overlay = document.getElementById('loading-overlay');
const text = document.getElementById('loading-text');
overlay.style.display = 'flex';
text.innerText = msg;
}
document.getElementById('prev-btn').onclick = () => {
if(currentEpisode > 1) window.location.href = `/watch/${animeId}/${currentEpisode - 1}`;
};
document.getElementById('next-btn').onclick = () => {
window.location.href = `/watch/${animeId}/${currentEpisode + 1}`;
};
if(currentEpisode <= 1) document.getElementById('prev-btn').disabled = true;
loadMetadata();
loadExtensions();