added local manga, todo: novels

This commit is contained in:
2025-12-27 21:59:03 +01:00
parent 295cab93f3
commit d49f739565
16 changed files with 759 additions and 53 deletions

View File

@@ -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();
@@ -173,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";
@@ -208,7 +256,6 @@ async function loadChapters(targetProvider = null) {
if (totalEl) totalEl.innerText = `${allChapters.length} Found`;
setupReadButton();
chapterPagination.setTotalItems(filteredChapters.length);
chapterPagination.reset();
renderChapterTable();
@@ -235,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;
@@ -252,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];
@@ -314,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() {