Files
WaifuBoard/desktop/src/scripts/anime/anime.js
itsskaiya 28ff6ccc68 Organized the differences between server and docker versions.
We are launching a docker version (server version) today so we want to just organize the repo
so its easier to navigate.
2025-12-16 21:50:22 -05:00

301 lines
8.4 KiB
JavaScript

let animeData = null;
let extensionName = null;
let animeId = null;
const episodePagination = Object.create(PaginationManager);
episodePagination.init(12, renderEpisodes);
YouTubePlayerUtils.init('player');
document.addEventListener('DOMContentLoaded', () => {
loadAnime();
setupDescriptionModal();
setupEpisodeSearch();
});
async function loadAnime() {
try {
const urlData = URLUtils.parseEntityPath('anime');
if (!urlData) {
showError("Invalid URL");
return;
}
extensionName = urlData.extensionName;
animeId = urlData.entityId;
const fetchUrl = extensionName
? `/api/anime/${animeId}?source=${extensionName}`
: `/api/anime/${animeId}?source=anilist`;
const res = await fetch(fetchUrl, { headers: AuthUtils.getSimpleAuthHeaders() });
const data = await res.json();
if (data.error) {
showError("Anime Not Found");
return;
}
animeData = data;
const metadata = MediaMetadataUtils.formatAnimeData(data, !!extensionName);
updatePageTitle(metadata.title);
updateMetadata(metadata);
updateDescription(data.description || data.summary);
updateCharacters(metadata.characters);
updateExtensionPill();
setupWatchButton();
const hasTrailer = YouTubePlayerUtils.playTrailer(
metadata.trailer,
'player',
metadata.banner
);
setupEpisodes(metadata.episodes);
await setupAddToListButton();
} catch (err) {
console.error('Error loading anime:', err);
showError("Error loading anime");
}
}
function updatePageTitle(title) {
document.title = `${title} | WaifuBoard`;
document.getElementById('title').innerText = title;
}
function updateMetadata(metadata) {
if (metadata.poster) {
document.getElementById('poster').src = metadata.poster;
}
document.getElementById('score').innerText = `${metadata.score}% Score`;
document.getElementById('year').innerText = metadata.year;
document.getElementById('genres').innerText = metadata.genres;
document.getElementById('format').innerText = metadata.format;
document.getElementById('status').innerText = metadata.status;
document.getElementById('season').innerText = metadata.season;
document.getElementById('studio').innerText = metadata.studio;
document.getElementById('episodes').innerText = metadata.episodes;
}
function updateDescription(rawDescription) {
const desc = MediaMetadataUtils.truncateDescription(rawDescription, 4);
document.getElementById('description-preview').innerHTML = desc.short;
document.getElementById('full-description').innerHTML = desc.full;
const readMoreBtn = document.getElementById('read-more-btn');
if (desc.isTruncated) {
readMoreBtn.style.display = 'inline-flex';
} else {
readMoreBtn.style.display = 'none';
}
}
function updateCharacters(characters) {
const container = document.getElementById('char-list');
container.innerHTML = '';
if (characters.length > 0) {
characters.forEach(char => {
container.innerHTML += `
<div class="character-item">
<div class="char-dot"></div> ${char.name}
</div>`;
});
} else {
container.innerHTML = `
<div class="character-item" style="color: #666;">
No character data available
</div>`;
}
}
function updateExtensionPill() {
const pill = document.getElementById('extension-pill');
if (!pill) return;
if (extensionName) {
pill.textContent = extensionName.charAt(0).toUpperCase() + extensionName.slice(1).toLowerCase();
pill.style.display = 'inline-flex';
} else {
pill.style.display = 'none';
}
}
function setupWatchButton() {
const watchBtn = document.getElementById('watch-btn');
if (watchBtn) {
watchBtn.onclick = () => {
const url = URLUtils.buildWatchUrl(animeId, 1, extensionName);
window.location.href = url;
};
}
}
async function setupAddToListButton() {
const btn = document.getElementById('add-to-list-btn');
if (!btn || !animeData) return;
ListModalManager.currentData = animeData;
const entryType = ListModalManager.getEntryType(animeData);
await ListModalManager.checkIfInList(animeId, extensionName || 'anilist', entryType);
const tempBtn = document.querySelector('.hero-buttons .btn-blur');
if (tempBtn) {
ListModalManager.updateButton('.hero-buttons .btn-blur');
} else {
updateCustomAddButton();
}
btn.onclick = () => ListModalManager.open(animeData, extensionName || 'anilist');
}
function updateCustomAddButton() {
const btn = document.getElementById('add-to-list-btn');
if (!btn) return;
if (ListModalManager.isInList) {
btn.innerHTML = `
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
In Your List
`;
btn.style.background = 'rgba(34, 197, 94, 0.2)';
btn.style.color = '#22c55e';
btn.style.borderColor = 'rgba(34, 197, 94, 0.3)';
} else {
btn.innerHTML = '+ Add to List';
btn.style.background = null;
btn.style.color = null;
btn.style.borderColor = null;
}
}
function setupEpisodes(totalEpisodes) {
const limitedTotal = Math.min(Math.max(totalEpisodes, 1), 5000);
episodePagination.setTotalItems(limitedTotal);
renderEpisodes();
}
function renderEpisodes() {
const grid = document.getElementById('episodes-grid');
if (!grid) return;
grid.innerHTML = '';
const range = episodePagination.getPageRange();
const start = range.start + 1;
const end = range.end;
for (let i = start; i <= end; i++) {
createEpisodeButton(i, grid);
}
episodePagination.renderControls(
'pagination-controls',
'page-info',
'prev-page',
'next-page'
);
}
function createEpisodeButton(num, container) {
const btn = document.createElement('div');
btn.className = 'episode-btn';
btn.innerText = `Ep ${num}`;
btn.onclick = () => {
const url = URLUtils.buildWatchUrl(animeId, num, extensionName);
window.location.href = url;
};
container.appendChild(btn);
}
function setupDescriptionModal() {
const modal = document.getElementById('desc-modal');
if (!modal) return;
modal.addEventListener('click', (e) => {
if (e.target.id === 'desc-modal') {
closeDescriptionModal();
}
});
}
function openDescriptionModal() {
document.getElementById('desc-modal').classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeDescriptionModal() {
document.getElementById('desc-modal').classList.remove('active');
document.body.style.overflow = '';
}
function setupEpisodeSearch() {
const searchInput = document.getElementById('ep-search');
if (!searchInput) return;
searchInput.addEventListener('input', (e) => {
const val = parseInt(e.target.value);
const grid = document.getElementById('episodes-grid');
const totalEpisodes = episodePagination.totalItems;
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';
}
});
}
function showError(message) {
document.getElementById('title').innerText = message;
}
function saveToList() {
if (!animeId) return;
ListModalManager.save(animeId, extensionName || 'anilist');
}
function deleteFromList() {
if (!animeId) return;
ListModalManager.delete(animeId, extensionName || 'anilist');
}
function closeAddToListModal() {
ListModalManager.close();
}
window.openDescriptionModal = openDescriptionModal;
window.closeDescriptionModal = closeDescriptionModal;
window.changePage = (delta) => {
if (delta > 0) episodePagination.nextPage();
else episodePagination.prevPage();
};