Files
WaifuBoard/src/scripts/anime/anime.js

243 lines
8.9 KiB
JavaScript

const animeId = window.location.pathname.split('/').pop();
let player;
let totalEpisodes = 0;
let currentPage = 1;
const itemsPerPage = 12;
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
let extensionName;
async function loadAnime() {
try {
const path = window.location.pathname;
const parts = path.split("/").filter(Boolean);
let animeId;
if (parts.length === 3) {
extensionName = parts[1];
animeId = parts[2];
} else {
animeId = parts[1];
}
const fetchUrl = extensionName
? `/api/anime/${animeId.slice(0,40)}?ext=${extensionName}`
: `/api/anime/${animeId}`;
const res = await fetch(fetchUrl);
const data = await res.json();
if(data.error) {
document.getElementById('title').innerText = "Anime Not Found";
return;
}
const title = data.title?.english || data.title?.romaji || "Unknown Title";
document.title = `${title} | WaifuBoard`;
document.getElementById('title').innerText = title;
if (data.coverImage?.extraLarge) {
document.getElementById('poster').src = data.coverImage.extraLarge;
}
const rawDesc = data.description || "No description available.";
handleDescription(rawDesc);
document.getElementById('score').innerText = (data.averageScore || '?') + '% Score';
document.getElementById('year').innerText = data.seasonYear || data.startDate?.year || '????';
document.getElementById('genres').innerText = data.genres?.length > 0 ? data.genres.slice(0, 3).join(' • ') : '';
document.getElementById('format').innerText = data.format || 'TV';
document.getElementById('status').innerText = data.status || 'Unknown';
const extensionPill = document.getElementById('extension-pill');
if (extensionName && extensionPill) {
extensionPill.textContent = `${extensionName.charAt(0).toUpperCase() + extensionName.slice(1).toLowerCase()}`;
extensionPill.style.display = 'inline-flex';
} else if (extensionPill) {
extensionPill.style.display = 'none';
}
let seasonText = '';
if (data.season && data.seasonYear) {
seasonText = `${data.season} ${data.seasonYear}`;
} else if (data.startDate?.year) {
const months = ['', 'Winter', 'Winter', 'Spring', 'Spring', 'Spring', 'Summer', 'Summer', 'Summer', 'Fall', 'Fall', 'Fall', 'Winter'];
const month = data.startDate.month || 1;
const estimatedSeason = months[month] || '';
seasonText = `${estimatedSeason} ${data.startDate.year}`.trim();
}
document.getElementById('season').innerText = seasonText || 'Unknown';
let studioName = 'Unknown Studio';
if (data.studios?.nodes?.length > 0) {
studioName = data.studios.nodes[0].name;
} else if (data.studios?.edges?.length > 0) {
studioName = data.studios.edges[0]?.node?.name || 'Unknown Studio';
}
document.getElementById('studio').innerText = studioName;
const charContainer = document.getElementById('char-list');
charContainer.innerHTML = '';
let characters = [];
if (data.characters?.nodes?.length > 0) {
characters = data.characters.nodes.slice(0, 5);
}
else if (data.characters?.edges?.length > 0) {
characters = data.characters.edges
.filter(edge => edge?.node?.name?.full)
.slice(0, 5)
.map(edge => edge.node);
}
if (characters.length > 0) {
characters.forEach(char => {
if (char?.name?.full) {
charContainer.innerHTML += `
<div class="character-item">
<div class="char-dot"></div> ${char.name.full}
</div>`;
}
});
} else {
charContainer.innerHTML = `
<div class="character-item" style="color: #666;">
No character data available
</div>`;
}
document.getElementById('watch-btn').onclick = () => {
window.location.href = `/watch/${animeId}/1`;
};
if (data.trailer && data.trailer.site === 'youtube') {
window.onYouTubeIframeAPIReady = function() {
player = new YT.Player('player', {
height: '100%',
width: '100%',
videoId: data.trailer.id,
playerVars: {
'autoplay': 1, 'controls': 0, 'mute': 1,
'loop': 1, 'playlist': data.trailer.id,
'showinfo': 0, 'modestbranding': 1, 'disablekb': 1
},
events: { 'onReady': (e) => e.target.playVideo() }
});
};
} else {
const banner = data.bannerImage || data.coverImage?.extraLarge || '';
if (banner) {
document.querySelector('.video-background').innerHTML = `<img src="${banner}" style="width:100%; height:100%; object-fit:cover;">`;
}
}
if (data.nextAiringEpisode?.episode) {
totalEpisodes = data.nextAiringEpisode.episode - 1;
} else if (data.episodes) {
totalEpisodes = data.episodes;
} else {
totalEpisodes = 12;
}
totalEpisodes = Math.min(Math.max(totalEpisodes, 1), 5000);
document.getElementById('episodes').innerText = totalEpisodes;
renderEpisodes();
} catch (err) {
console.error('Error loading anime:', err);
document.getElementById('title').innerText = "Error loading anime";
}
}
function handleDescription(text) {
const tmp = document.createElement("DIV");
tmp.innerHTML = text;
const cleanText = tmp.textContent || tmp.innerText || "";
const sentences = cleanText.match(/[^\.!\?]+[\.!\?]+/g) || [cleanText];
document.getElementById('full-description').innerHTML = text;
if (sentences.length > 4) {
const shortText = sentences.slice(0, 4).join(' ');
document.getElementById('description-preview').innerText = shortText + '...';
document.getElementById('read-more-btn').style.display = 'inline-flex';
} else {
document.getElementById('description-preview').innerHTML = text;
document.getElementById('read-more-btn').style.display = 'none';
}
}
function openModal() {
document.getElementById('desc-modal').classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeModal() {
document.getElementById('desc-modal').classList.remove('active');
document.body.style.overflow = '';
}
document.getElementById('desc-modal').addEventListener('click', (e) => {
if (e.target.id === 'desc-modal') closeModal();
});
function renderEpisodes() {
const grid = document.getElementById('episodes-grid');
grid.innerHTML = '';
const start = (currentPage - 1) * itemsPerPage + 1;
const end = Math.min(start + itemsPerPage - 1, totalEpisodes);
for(let i = start; i <= end; i++) {
createEpisodeButton(i, grid);
}
updatePaginationControls();
}
function createEpisodeButton(num, container) {
const btn = document.createElement('div');
btn.className = 'episode-btn';
btn.innerText = `Ep ${num}`;
btn.onclick = () =>
window.location.href = `/watch/${animeId}/${num}` + (extensionName ? `?${extensionName}` : "");
container.appendChild(btn);
}
function updatePaginationControls() {
const totalPages = Math.ceil(totalEpisodes / itemsPerPage);
document.getElementById('page-info').innerText = `Page ${currentPage} of ${totalPages}`;
document.getElementById('prev-page').disabled = currentPage === 1;
document.getElementById('next-page').disabled = currentPage === totalPages;
document.getElementById('pagination-controls').style.display = 'flex';
}
function changePage(delta) {
currentPage += delta;
renderEpisodes();
}
const searchInput = document.getElementById('ep-search');
searchInput.addEventListener('input', (e) => {
const val = parseInt(e.target.value);
const grid = document.getElementById('episodes-grid');
if (val > 0 && val <= totalEpisodes) {
grid.innerHTML = '';
createEpisodeButton(val, grid);
document.getElementById('pagination-controls').style.display = 'none';
} else if (!e.target.value) {
renderEpisodes();
} else {
grid.innerHTML = '<div style="color:#666; width:100%; text-align:center;">Episode not found</div>';
document.getElementById('pagination-controls').style.display = 'none';
}
});
loadAnime();