added local manga, todo: novels
This commit is contained in:
@@ -7,7 +7,7 @@ let allChapters = [];
|
||||
let filteredChapters = [];
|
||||
|
||||
let availableExtensions = [];
|
||||
|
||||
let isLocal = false;
|
||||
const chapterPagination = Object.create(PaginationManager);
|
||||
chapterPagination.init(12, () => renderChapterTable());
|
||||
|
||||
@@ -16,6 +16,40 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
setupModalClickOutside();
|
||||
});
|
||||
|
||||
async function checkLocalLibraryEntry() {
|
||||
try {
|
||||
const res = await fetch(`/api/library/manga/${bookId}`);
|
||||
if (!res.ok) return;
|
||||
|
||||
const data = await res.json();
|
||||
if (data.matched) {
|
||||
isLocal = true;
|
||||
const pill = document.getElementById('local-pill');
|
||||
if (pill) {
|
||||
pill.textContent = 'Local';
|
||||
pill.style.display = 'inline-flex';
|
||||
pill.style.background = 'rgba(34, 197, 94, 0.2)';
|
||||
pill.style.color = '#22c55e';
|
||||
pill.style.borderColor = 'rgba(34, 197, 94, 0.3)';
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error checking local status:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function markAsLocal() {
|
||||
isLocal = true;
|
||||
const pill = document.getElementById('local-pill');
|
||||
if (pill) {
|
||||
pill.textContent = 'Local';
|
||||
pill.style.display = 'inline-flex';
|
||||
pill.style.background = 'rgba(34, 197, 94, 0.2)';
|
||||
pill.style.color = '#22c55e';
|
||||
pill.style.borderColor = 'rgba(34, 197, 94, 0.3)';
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const urlData = URLUtils.parseEntityPath('book');
|
||||
@@ -27,7 +61,7 @@ async function init() {
|
||||
extensionName = urlData.extensionName;
|
||||
bookId = urlData.entityId;
|
||||
bookSlug = urlData.slug;
|
||||
|
||||
await checkLocalLibraryEntry();
|
||||
await loadBookMetadata();
|
||||
|
||||
await loadAvailableExtensions();
|
||||
@@ -71,7 +105,6 @@ async function loadBookMetadata() {
|
||||
const metadata = MediaMetadataUtils.formatBookData(raw, !!extensionName);
|
||||
bookData.entry_type =
|
||||
metadata.format === 'MANGA' ? 'MANGA' : 'NOVEL';
|
||||
|
||||
updatePageTitle(metadata.title);
|
||||
updateMetadata(metadata);
|
||||
updateExtensionPill();
|
||||
@@ -174,32 +207,46 @@ async function loadChapters(targetProvider = null) {
|
||||
const tbody = document.getElementById('chapters-body');
|
||||
if (!tbody) return;
|
||||
|
||||
// Si no se pasa provider, intentamos pillar el del select o el primero disponible
|
||||
if (!targetProvider) {
|
||||
const select = document.getElementById('provider-filter');
|
||||
targetProvider = select ? select.value : (availableExtensions[0] || 'all');
|
||||
}
|
||||
|
||||
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding: 2rem;">Searching extension for chapters...</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center; padding: 2rem;">Loading chapters...</td></tr>';
|
||||
|
||||
try {
|
||||
const source = extensionName || 'anilist';
|
||||
// Añadimos el query param 'provider' para que el backend filtre
|
||||
let fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
||||
if (targetProvider !== 'all') {
|
||||
fetchUrl += `&provider=${targetProvider}`;
|
||||
let fetchUrl;
|
||||
let isLocalRequest = targetProvider === 'local';
|
||||
|
||||
if (isLocalRequest) {
|
||||
// Nuevo endpoint para archivos locales
|
||||
fetchUrl = `/api/library/manga/${bookId}/units`;
|
||||
} else {
|
||||
const source = extensionName || 'anilist';
|
||||
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
||||
if (targetProvider !== 'all') fetchUrl += `&provider=${targetProvider}`;
|
||||
}
|
||||
|
||||
const res = await fetch(fetchUrl);
|
||||
const data = await res.json();
|
||||
|
||||
allChapters = data.chapters || [];
|
||||
filteredChapters = [...allChapters];
|
||||
// Mapeo de datos: Si es local usamos 'units', si no, usamos 'chapters'
|
||||
if (isLocalRequest) {
|
||||
allChapters = (data.units || []).map((unit, idx) => ({
|
||||
number: unit.number,
|
||||
title: unit.name,
|
||||
provider: 'local',
|
||||
index: idx, // ✅ índice (0,1,2…)
|
||||
format: unit.format
|
||||
}));
|
||||
} else {
|
||||
allChapters = data.chapters || [];
|
||||
}
|
||||
|
||||
filteredChapters = [...allChapters];
|
||||
applyChapterFilter();
|
||||
|
||||
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.</td></tr>';
|
||||
if (totalEl) totalEl.innerText = "0 Found";
|
||||
@@ -209,7 +256,6 @@ async function loadChapters(targetProvider = null) {
|
||||
if (totalEl) totalEl.innerText = `${allChapters.length} Found`;
|
||||
|
||||
setupReadButton();
|
||||
|
||||
chapterPagination.setTotalItems(filteredChapters.length);
|
||||
chapterPagination.reset();
|
||||
renderChapterTable();
|
||||
@@ -236,16 +282,26 @@ function applyChapterFilter() {
|
||||
|
||||
function setupProviderFilter() {
|
||||
const select = document.getElementById('provider-filter');
|
||||
if (!select || availableExtensions.length === 0) return;
|
||||
if (!select) return;
|
||||
|
||||
select.style.display = 'inline-block';
|
||||
select.innerHTML = '';
|
||||
|
||||
// Opción para cargar todo
|
||||
const allOpt = document.createElement('option');
|
||||
allOpt.value = 'all';
|
||||
allOpt.innerText = 'Load All (Slower)';
|
||||
select.appendChild(allOpt);
|
||||
|
||||
// NUEVO: Si es local, añadimos la opción 'local' al principio
|
||||
if (isLocal) {
|
||||
const localOpt = document.createElement('option');
|
||||
localOpt.value = 'local';
|
||||
localOpt.innerText = 'Local';
|
||||
select.appendChild(localOpt);
|
||||
}
|
||||
|
||||
// Añadir extensiones normales
|
||||
availableExtensions.forEach(ext => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = ext;
|
||||
@@ -253,7 +309,10 @@ function setupProviderFilter() {
|
||||
select.appendChild(opt);
|
||||
});
|
||||
|
||||
if (extensionName && availableExtensions.includes(extensionName)) {
|
||||
// Lógica de selección automática
|
||||
if (isLocal) {
|
||||
select.value = 'local'; // Prioridad si es local
|
||||
} else if (extensionName && availableExtensions.includes(extensionName)) {
|
||||
select.value = extensionName;
|
||||
} else if (availableExtensions.length > 0) {
|
||||
select.value = availableExtensions[0];
|
||||
@@ -315,7 +374,14 @@ function renderChapterTable() {
|
||||
}
|
||||
|
||||
function openReader(chapterId, provider) {
|
||||
window.location.href = URLUtils.buildReadUrl(bookId, chapterId, provider, extensionName);
|
||||
const effectiveExtension = extensionName || 'anilist';
|
||||
|
||||
window.location.href = URLUtils.buildReadUrl(
|
||||
bookId, // SIEMPRE anilist
|
||||
chapterId, // número normal
|
||||
provider, // 'local' o extensión
|
||||
extensionName || 'anilist'
|
||||
);
|
||||
}
|
||||
|
||||
function setupModalClickOutside() {
|
||||
|
||||
Reference in New Issue
Block a user