210 lines
7.1 KiB
JavaScript
210 lines
7.1 KiB
JavaScript
const bookId = window.location.pathname.split('/').pop();
|
|
let allChapters = []; // Stores all fetched chapters
|
|
let filteredChapters = []; // Stores currently displayed chapters (filtered)
|
|
let currentPage = 1;
|
|
const itemsPerPage = 12;
|
|
|
|
async function init() {
|
|
try {
|
|
// 1. Load Metadata
|
|
const res = await fetch(`/api/book/${bookId}`);
|
|
const data = await res.json();
|
|
|
|
if (data.error) {
|
|
const titleEl = document.getElementById('title');
|
|
if (titleEl) titleEl.innerText = "Book Not Found";
|
|
return;
|
|
}
|
|
|
|
// Populate Hero Elements
|
|
const title = data.title.english || data.title.romaji;
|
|
document.title = `${title} | StreamFlow Books`;
|
|
|
|
const titleEl = document.getElementById('title');
|
|
if (titleEl) titleEl.innerText = title;
|
|
|
|
const descEl = document.getElementById('description');
|
|
if (descEl) descEl.innerHTML = data.description || "No description available.";
|
|
|
|
const scoreEl = document.getElementById('score');
|
|
if (scoreEl) scoreEl.innerText = (data.averageScore || '?') + '% Score';
|
|
|
|
const pubEl = document.getElementById('published-date');
|
|
if (pubEl) {
|
|
if (data.startDate && data.startDate.year) {
|
|
const y = data.startDate.year;
|
|
const m = data.startDate.month ? `-${data.startDate.month.toString().padStart(2, '0')}` : '';
|
|
const d = data.startDate.day ? `-${data.startDate.day.toString().padStart(2, '0')}` : '';
|
|
pubEl.innerText = `${y}${m}${d}`;
|
|
} else {
|
|
pubEl.innerText = '????';
|
|
}
|
|
}
|
|
|
|
const statusEl = document.getElementById('status');
|
|
if (statusEl) statusEl.innerText = data.status || 'Unknown';
|
|
|
|
const formatEl = document.getElementById('format');
|
|
if (formatEl) formatEl.innerText = data.format || 'MANGA';
|
|
|
|
const chaptersEl = document.getElementById('chapters');
|
|
if (chaptersEl) chaptersEl.innerText = data.chapters || '?';
|
|
|
|
const genresEl = document.getElementById('genres');
|
|
if(genresEl && data.genres) {
|
|
genresEl.innerText = data.genres.slice(0, 3).join(' • ');
|
|
}
|
|
|
|
const img = data.coverImage.extraLarge || data.coverImage.large;
|
|
|
|
const posterEl = document.getElementById('poster');
|
|
if (posterEl) posterEl.src = img;
|
|
|
|
const heroBgEl = document.getElementById('hero-bg');
|
|
if (heroBgEl) heroBgEl.src = data.bannerImage || img;
|
|
|
|
// 2. Load Chapters
|
|
loadChapters();
|
|
|
|
} catch (err) {
|
|
console.error("Metadata Error:", err);
|
|
}
|
|
}
|
|
|
|
async function loadChapters() {
|
|
const tbody = document.getElementById('chapters-body');
|
|
if (!tbody) return;
|
|
|
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding: 2rem;">Searching extensions for chapters...</td></tr>';
|
|
|
|
try {
|
|
const res = await fetch(`/api/book/${bookId}/chapters`);
|
|
const data = await res.json();
|
|
|
|
allChapters = data.chapters || [];
|
|
filteredChapters = [...allChapters]; // Initially, show all
|
|
|
|
const totalEl = document.getElementById('total-chapters');
|
|
|
|
if (allChapters.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding: 2rem;">No chapters found on loaded extensions.</td></tr>';
|
|
if (totalEl) totalEl.innerText = "0 Found";
|
|
return;
|
|
}
|
|
|
|
if (totalEl) totalEl.innerText = `${allChapters.length} Found`;
|
|
|
|
// Populate Provider Filter
|
|
populateProviderFilter();
|
|
|
|
// Read Button Action (Start at filtered Ch 1)
|
|
const readBtn = document.getElementById('read-start-btn');
|
|
if (readBtn && filteredChapters.length > 0) {
|
|
readBtn.onclick = () => openReader(filteredChapters[0].id);
|
|
}
|
|
|
|
renderTable();
|
|
|
|
} catch (err) {
|
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; color: #ef4444;">Error loading chapters.</td></tr>';
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
function populateProviderFilter() {
|
|
const select = document.getElementById('provider-filter');
|
|
if (!select) return;
|
|
|
|
// Extract unique providers
|
|
const providers = [...new Set(allChapters.map(ch => ch.provider))];
|
|
|
|
// Only show filter if there are actual providers found
|
|
if (providers.length > 0) {
|
|
select.style.display = 'inline-block';
|
|
|
|
// Clear existing options except "All"
|
|
select.innerHTML = '<option value="all">All Providers</option>';
|
|
|
|
providers.forEach(prov => {
|
|
const opt = document.createElement('option');
|
|
opt.value = prov;
|
|
opt.innerText = prov;
|
|
select.appendChild(opt);
|
|
});
|
|
|
|
// Attach Event Listener
|
|
select.onchange = (e) => {
|
|
const selected = e.target.value;
|
|
if (selected === 'all') {
|
|
filteredChapters = [...allChapters];
|
|
} else {
|
|
filteredChapters = allChapters.filter(ch => ch.provider === selected);
|
|
}
|
|
currentPage = 1; // Reset to page 1 on filter change
|
|
renderTable();
|
|
};
|
|
}
|
|
}
|
|
|
|
function renderTable() {
|
|
const tbody = document.getElementById('chapters-body');
|
|
if (!tbody) return;
|
|
|
|
tbody.innerHTML = '';
|
|
|
|
if (filteredChapters.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding: 2rem;">No chapters match this filter.</td></tr>';
|
|
updatePagination(); // Update to hide buttons
|
|
return;
|
|
}
|
|
|
|
const start = (currentPage - 1) * itemsPerPage;
|
|
const end = start + itemsPerPage;
|
|
const pageItems = filteredChapters.slice(start, end);
|
|
|
|
pageItems.forEach(ch => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${ch.number}</td>
|
|
<td>${ch.title || `Chapter ${ch.number}`}</td>
|
|
<td><span class="pill" style="font-size:0.75rem;">${ch.provider}</span></td>
|
|
<td>
|
|
<button class="read-btn-small" onclick="openReader('${ch.id}')">Read</button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
updatePagination();
|
|
}
|
|
|
|
function updatePagination() {
|
|
const totalPages = Math.ceil(filteredChapters.length / itemsPerPage);
|
|
const pagination = document.getElementById('pagination');
|
|
|
|
if (!pagination) return;
|
|
|
|
if (totalPages <= 1) {
|
|
pagination.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
pagination.style.display = 'flex';
|
|
document.getElementById('page-info').innerText = `Page ${currentPage} of ${totalPages}`;
|
|
|
|
const prevBtn = document.getElementById('prev-page');
|
|
const nextBtn = document.getElementById('next-page');
|
|
|
|
prevBtn.disabled = currentPage === 1;
|
|
nextBtn.disabled = currentPage >= totalPages;
|
|
|
|
prevBtn.onclick = () => { currentPage--; renderTable(); };
|
|
nextBtn.onclick = () => { currentPage++; renderTable(); };
|
|
}
|
|
|
|
function openReader(chapterId) {
|
|
alert("Opening Reader for Chapter ID: " + chapterId);
|
|
// window.location.href = `/read/${bookId}/${chapterId}`;
|
|
}
|
|
|
|
init(); |