We are launching a docker version (server version) today so we want to just organize the repo so its easier to navigate.
301 lines
8.4 KiB
JavaScript
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();
|
|
}; |