From bf27d173e9a8e502cd37dfbf0c67a41aebc7cd5e Mon Sep 17 00:00:00 2001 From: lenafx Date: Wed, 3 Dec 2025 21:43:19 +0100 Subject: [PATCH] enhanced book backend --- src/api/books/books.controller.ts | 29 ++++++++++++++--------------- src/api/books/books.service.ts | 12 ++++++------ src/api/types.ts | 1 + src/scripts/books/book.js | 30 ++++++++++++++++++++---------- src/scripts/books/reader.js | 18 ++++++++++++------ 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/api/books/books.controller.ts b/src/api/books/books.controller.ts index 21634aa..5d07549 100644 --- a/src/api/books/books.controller.ts +++ b/src/api/books/books.controller.ts @@ -3,29 +3,27 @@ import * as booksService from './books.service'; import {getExtension} from '../../shared/extensions'; import {BookRequest, ChapterRequest, SearchRequest} from '../types'; -export async function getBook(req: BookRequest, reply: FastifyReply) { +export async function getBook(req: any, reply: FastifyReply) { try { const { id } = req.params; - const source = req.query.ext || 'anilist'; + const source = req.query.source; let book; if (source === 'anilist') { book = await booksService.getBookById(id); } else { const ext = getExtension(source); - const result = await booksService.getBookInfoExtension(ext, id); book = result || null; } return book; - } catch (err) { - const error = err as Error; - return { error: error.toString() }; + return { error: (err as Error).message }; } } + export async function getTrending(req: FastifyRequest, reply: FastifyReply) { try { const results = await booksService.getTrendingBooks(); @@ -88,30 +86,31 @@ export async function searchBooksInExtension(req: any, reply: FastifyReply) { export async function getChapters(req: any, reply: FastifyReply) { try { const { id } = req.params; - const { ext } = req.query; - return await booksService.getChaptersForBook(id, Boolean(ext)); - } catch (err) { + const source = req.query.source || 'anilist'; + + const isExternal = source !== 'anilist'; + return await booksService.getChaptersForBook(id, isExternal); + } catch { return { chapters: [] }; } } + export async function getChapterContent(req: any, reply: FastifyReply) { try { const { bookId, chapter, provider } = req.params; - const { ext } = req.query; + const source = req.query.source || 'anilist'; const content = await booksService.getChapterContent( bookId, chapter, provider, - ext + source ); return reply.send(content); } catch (err) { - const error = err as Error; - console.error("getChapterContent error:", error.message); - + console.error("getChapterContent error:", (err as Error).message); return reply.code(500).send({ error: "Error loading chapter" }); } -} \ No newline at end of file +} diff --git a/src/api/books/books.service.ts b/src/api/books/books.service.ts index 26bafe8..6e18fa9 100644 --- a/src/api/books/books.service.ts +++ b/src/api/books/books.service.ts @@ -255,7 +255,8 @@ async function searchChaptersInExtension(ext: Extension, name: string, searchTit number: parseFloat(ch.number.toString()), title: ch.title, date: ch.releaseDate, - provider: name + provider: name, + index: ch.index })); await setCache(cacheKey, result, CACHE_TTL_MS); @@ -311,17 +312,15 @@ export async function getChaptersForBook(id: string, ext: Boolean): Promise<{ ch }; } -export async function getChapterContent(bookId: string, chapterIndex: string, providerName: string, name: string): Promise { +export async function getChapterContent(bookId: string, chapterIndex: string, providerName: string, source: string): Promise { const extensions = getAllExtensions(); const ext = extensions.get(providerName); if (!ext) { throw new Error("Provider not found"); } - let exts = "anilist"; - if (name) exts = "ext"; - const contentCacheKey = `content:${providerName}:${exts}:${bookId}:${chapterIndex}`; + const contentCacheKey = `content:${providerName}:${source}:${bookId}:${chapterIndex}`; const cachedContent = await getCache(contentCacheKey); if (cachedContent) { @@ -340,7 +339,8 @@ export async function getChapterContent(bookId: string, chapterIndex: string, pr } } - const chapterList = await getChaptersForBook(bookId, Boolean(name)); + const isExternal = source !== 'anilist'; + const chapterList = await getChaptersForBook(bookId, isExternal); if (!chapterList?.chapters || chapterList.chapters.length === 0) { throw new Error("Chapters not found"); diff --git a/src/api/types.ts b/src/api/types.ts index e33742e..1e5d40a 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -78,6 +78,7 @@ export interface Episode { } export interface Chapter { + index: number; id: string; number: string | number; title?: string; diff --git a/src/scripts/books/book.js b/src/scripts/books/book.js index 7e89c62..82eb074 100644 --- a/src/scripts/books/book.js +++ b/src/scripts/books/book.js @@ -6,6 +6,14 @@ const itemsPerPage = 12; let extensionName = null; let bookSlug = null; +function getBookUrl(id, source = 'anilist') { + return `/api/book/${id}?source=${source}`; +} + +function getChaptersUrl(id, source = 'anilist') { + return `/api/book/${id}/chapters?source=${source}`; +} + async function init() { try { const path = window.location.pathname; @@ -24,9 +32,10 @@ async function init() { const idForFetch = currentBookId; - const fetchUrl = extensionName - ? `/api/book/${idForFetch}?ext=${extensionName}` - : `/api/book/${idForFetch}`; + const fetchUrl = getBookUrl( + idForFetch, + extensionName || 'anilist' + ); const res = await fetch(fetchUrl); const data = await res.json(); @@ -129,9 +138,10 @@ async function loadChapters(idForFetch) { try { - const fetchUrl = extensionName - ? `/api/book/${idForFetch}/chapters?ext=${extensionName}` - : `/api/book/${idForFetch}/chapters`; + const fetchUrl = getChaptersUrl( + idForFetch, + extensionName || 'anilist' + ); const res = await fetch(fetchUrl); const data = await res.json(); @@ -174,7 +184,7 @@ function populateProviderFilter() { if (providers.length > 0) { select.style.display = 'inline-block'; - select.innerHTML = ''; + select.innerHTML = ''; providers.forEach(prov => { const opt = document.createElement('option'); @@ -236,7 +246,7 @@ function renderTable(idForFetch) { ${ch.title || 'Chapter ' + ch.number} ${ch.provider} - @@ -276,8 +286,8 @@ function updatePagination() { function openReader(bookId, chapterId, provider) { const c = encodeURIComponent(chapterId); const p = encodeURIComponent(provider); - let extension = ""; - if (extensionName) extension = "?" + extensionName; + let extension = "?source=anilist"; + if (extensionName) extension = "?source=" + extensionName; window.location.href = `/read/${p}/${c}/${bookId}${extension}`; } diff --git a/src/scripts/books/reader.js b/src/scripts/books/reader.js index 358d167..251c668 100644 --- a/src/scripts/books/reader.js +++ b/src/scripts/books/reader.js @@ -126,10 +126,15 @@ async function loadChapter() { `; + const urlParams = new URLSearchParams(window.location.search); + let source = urlParams.get('source'); + if (!source) { + source = 'anilist'; + } + const newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`; + try { - let ext = "" - if(hasQuery) ext = "?ext=yes" - const res = await fetch(`/api/book/${bookId}/${chapter}/${provider}${ext}`); + const res = await fetch(newEndpoint); const data = await res.json(); if (data.title) { @@ -480,12 +485,13 @@ document.getElementById('back-btn').addEventListener('click', () => { const provider = parts[2]; const mangaId = parts[4]; - const isInt = Number.isInteger(Number(mangaId)); + const urlParams = new URLSearchParams(window.location.search); + let source = urlParams.get('source'); - if (isInt) { + if (!source) { window.location.href = `/book/${mangaId}`; } else { - window.location.href = `/book/${provider}/${mangaId}`; + window.location.href = `/book/${source}/${mangaId}`; } });