support for novels and lot of formats for books
This commit is contained in:
@@ -32,12 +32,12 @@ let localEntryId = null;
|
||||
async function checkLocal() {
|
||||
try {
|
||||
const res = await fetch(`/api/library/anime/${animeId}`);
|
||||
if (!res.ok) return;
|
||||
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
localEntryId = data.id; // ← ID interna
|
||||
|
||||
} catch {}
|
||||
return data.id;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAniSkip(malId, episode, duration) {
|
||||
@@ -53,7 +53,7 @@ async function loadAniSkip(malId, episode, duration) {
|
||||
}
|
||||
|
||||
async function loadMetadata() {
|
||||
checkLocal();
|
||||
localEntryId = await checkLocal();
|
||||
try {
|
||||
const sourceQuery = (extName === 'local' || !extName) ? "source=anilist" : `source=${extName}`;
|
||||
const res = await fetch(`/api/anime/${animeId}?${sourceQuery}`);
|
||||
@@ -133,6 +133,7 @@ async function loadMetadata() {
|
||||
} catch (error) {
|
||||
console.error('Error loading metadata:', error);
|
||||
}
|
||||
await loadExtensions();
|
||||
}
|
||||
|
||||
async function applyAniSkip(video) {
|
||||
@@ -484,4 +485,3 @@ setInterval(() => {
|
||||
}, 60000);
|
||||
|
||||
loadMetadata();
|
||||
loadExtensions();
|
||||
@@ -18,7 +18,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
async function checkLocalLibraryEntry() {
|
||||
try {
|
||||
const res = await fetch(`/api/library/manga/${bookId}`);
|
||||
const libraryType =
|
||||
bookData?.entry_type === 'NOVEL' ? 'novels' : 'manga';
|
||||
|
||||
const res = await fetch(`/api/library/${libraryType}/${bookId}`);
|
||||
if (!res.ok) return;
|
||||
|
||||
const data = await res.json();
|
||||
@@ -38,18 +41,6 @@ async function checkLocalLibraryEntry() {
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
@@ -61,8 +52,8 @@ async function init() {
|
||||
extensionName = urlData.extensionName;
|
||||
bookId = urlData.entityId;
|
||||
bookSlug = urlData.slug;
|
||||
await checkLocalLibraryEntry();
|
||||
await loadBookMetadata();
|
||||
await checkLocalLibraryEntry();
|
||||
|
||||
await loadAvailableExtensions();
|
||||
await loadChapters();
|
||||
@@ -220,7 +211,7 @@ async function loadChapters(targetProvider = null) {
|
||||
|
||||
if (isLocalRequest) {
|
||||
// Nuevo endpoint para archivos locales
|
||||
fetchUrl = `/api/library/manga/${bookId}/units`;
|
||||
fetchUrl = `/api/library/${bookId}/units`;
|
||||
} else {
|
||||
const source = extensionName || 'anilist';
|
||||
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
||||
|
||||
@@ -132,7 +132,7 @@ async function loadChapter() {
|
||||
let newEndpoint;
|
||||
|
||||
if (provider === 'local') {
|
||||
newEndpoint = `/api/library/manga/${bookId}/units`;
|
||||
newEndpoint = `/api/library/${bookId}/units`;
|
||||
} else {
|
||||
newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`;
|
||||
}
|
||||
@@ -142,29 +142,39 @@ async function loadChapter() {
|
||||
const data = await res.json();
|
||||
if (provider === 'local') {
|
||||
const unit = data.units[Number(chapter)];
|
||||
if (!unit) return;
|
||||
|
||||
if (!unit) {
|
||||
reader.innerHTML = '<div class="loading-container"><span>Chapter not found</span></div>';
|
||||
return;
|
||||
}
|
||||
chapterLabel.textContent = unit.name;
|
||||
document.title = unit.name;
|
||||
|
||||
if (unit.format === 'cbz') {
|
||||
chapterLabel.textContent = unit.name; // ✅
|
||||
document.title = unit.name;
|
||||
const pagesRes = await fetch(
|
||||
`/api/library/manga/cbz/${unit.id}/pages`
|
||||
);
|
||||
const pagesData = await pagesRes.json();
|
||||
const manifestRes = await fetch(`/api/library/${unit.id}/manifest`);
|
||||
const manifest = await manifestRes.json();
|
||||
|
||||
reader.innerHTML = '';
|
||||
|
||||
// ===== MANGA =====
|
||||
if (manifest.type === 'manga') {
|
||||
currentType = 'manga';
|
||||
updateSettingsVisibility();
|
||||
applyStyles();
|
||||
|
||||
currentPages = pagesData.pages.map(url => ({ url }));
|
||||
reader.innerHTML = '';
|
||||
currentPages = manifest.pages;
|
||||
loadManga(currentPages);
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== LN =====
|
||||
if (manifest.type === 'ln') {
|
||||
currentType = 'ln';
|
||||
updateSettingsVisibility();
|
||||
applyStyles();
|
||||
|
||||
const contentRes = await fetch(manifest.url);
|
||||
const html = await contentRes.text();
|
||||
|
||||
loadLN(html);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,13 +215,8 @@ async function loadChapter() {
|
||||
reader.innerHTML = '';
|
||||
|
||||
if (data.type === 'manga') {
|
||||
if (provider === 'local' && data.format === 'cbz') {
|
||||
currentPages = data.pages.map(url => ({ url }));
|
||||
loadManga(currentPages);
|
||||
} else {
|
||||
currentPages = data.pages || [];
|
||||
loadManga(currentPages);
|
||||
}
|
||||
currentPages = data.pages || [];
|
||||
loadManga(currentPages);
|
||||
} else if (data.type === 'ln') {
|
||||
loadLN(data.content);
|
||||
}
|
||||
|
||||
@@ -26,21 +26,37 @@ async function loadLocalEntries() {
|
||||
grid.innerHTML = '<div class="skeleton-card"></div>'.repeat(6);
|
||||
|
||||
try {
|
||||
// Cambiado a endpoint de libros
|
||||
const response = await fetch('/api/library/manga');
|
||||
const entries = await response.json();
|
||||
localEntries = entries;
|
||||
const [mangaRes, novelRes] = await Promise.all([
|
||||
fetch('/api/library/manga'),
|
||||
fetch('/api/library/novels')
|
||||
]);
|
||||
|
||||
if (entries.length === 0) {
|
||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-text-secondary); padding: 3rem;">No books found in your local library.</p>';
|
||||
const [manga, novel] = await Promise.all([
|
||||
mangaRes.json(),
|
||||
novelRes.json()
|
||||
]);
|
||||
|
||||
localEntries = [
|
||||
...manga.map(e => ({ ...e, type: 'manga' })),
|
||||
...novel.map(e => ({ ...e, type: 'novel' }))
|
||||
];
|
||||
|
||||
if (localEntries.length === 0) {
|
||||
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;padding:3rem;">No books found.</p>';
|
||||
return;
|
||||
}
|
||||
renderLocalEntries(entries);
|
||||
} catch (err) {
|
||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-danger); padding: 3rem;">Error loading local books.</p>';
|
||||
|
||||
renderLocalEntries(localEntries);
|
||||
} catch {
|
||||
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;color:var(--color-danger);padding:3rem;">Error loading library.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function filterLocal(type) {
|
||||
if (type === 'all') renderLocalEntries(localEntries);
|
||||
else renderLocalEntries(localEntries.filter(e => e.type === type));
|
||||
}
|
||||
|
||||
function renderLocalEntries(entries) {
|
||||
const grid = document.getElementById('local-entries-grid');
|
||||
grid.innerHTML = entries.map(entry => {
|
||||
@@ -58,6 +74,7 @@ function renderLocalEntries(entries) {
|
||||
<p style="font-size: 0.85rem; color: var(--color-text-secondary); margin: 0;">
|
||||
${chapters} Chapters
|
||||
</p>
|
||||
<div class="badge">${entry.type}</div>
|
||||
<div class="match-status ${entry.matched ? 'status-linked' : 'status-unlinked'}">
|
||||
${entry.matched ? '● Linked' : '○ Unlinked'}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user