From 0ef804c931f2bd43e95c8f91fb436bf7620828e9 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 28 Mar 2026 19:46:34 +0800 Subject: [PATCH] fix(route/nhentai): fetch failed issue --- lib/routes/nhentai/index.ts | 3 ++- lib/routes/nhentai/search.ts | 3 ++- lib/routes/nhentai/util.tsx | 39 +++++++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/routes/nhentai/index.ts b/lib/routes/nhentai/index.ts index 043f0fe10..63f87caf5 100644 --- a/lib/routes/nhentai/index.ts +++ b/lib/routes/nhentai/index.ts @@ -15,6 +15,7 @@ export const route: Route = { mode: 'mode, `simple` to only show cover, `detail` to show all pages, `torrent` to include Magnet URI, need login, refer to [Route-specific Configurations](https://docs.rsshub.app/deploy/config#route-specific-configurations), default to `simple`', }, features: { + requirePuppeteer: false, antiCrawler: true, supportBT: true, nsfw: true, @@ -26,7 +27,7 @@ export const route: Route = { }, ], name: 'Filter', - maintainers: ['MegrezZhu', 'hoilc'], + maintainers: ['MegrezZhu', 'hoilc', 'pseudoyu'], handler, }; diff --git a/lib/routes/nhentai/search.ts b/lib/routes/nhentai/search.ts index 9d354e225..a55035673 100644 --- a/lib/routes/nhentai/search.ts +++ b/lib/routes/nhentai/search.ts @@ -11,6 +11,7 @@ export const route: Route = { mode: 'mode, `simple` to only show cover, `detail` to show all pages, `torrent` to include Magnet URI, need login, refer to [Route-specific Configurations](https://docs.rsshub.app/deploy/config#route-specific-configurations), default to `simple`', }, features: { + requirePuppeteer: false, antiCrawler: true, supportBT: true, nsfw: true, @@ -22,7 +23,7 @@ export const route: Route = { }, ], name: 'Advanced Search', - maintainers: ['MegrezZhu', 'hoilc'], + maintainers: ['MegrezZhu', 'hoilc', 'pseudoyu'], handler, }; diff --git a/lib/routes/nhentai/util.tsx b/lib/routes/nhentai/util.tsx index 3201a4158..16f84cd23 100644 --- a/lib/routes/nhentai/util.tsx +++ b/lib/routes/nhentai/util.tsx @@ -6,6 +6,7 @@ import ConfigNotFoundError from '@/errors/types/config-not-found'; import got from '@/utils/got'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { getPuppeteerPage } from '@/utils/puppeteer'; const baseUrl = 'https://nhentai.net'; @@ -65,16 +66,32 @@ const getCookie = async (username, password, cache) => { return userTokenCookie; }; -const oFetch = (url, ...options) => - ofetch(url, { - ...options, - headers: { - host: 'nhentai.net', - }, - }); +// Reason: try ofetch first for speed, fall back to puppeteer on 403 (anti-bot protection) +const fetchPage = async (url: string): Promise => { + try { + return await ofetch(url); + } catch (error: unknown) { + const status = (error as { status?: number; statusCode?: number }).status ?? (error as { status?: number; statusCode?: number }).statusCode; + if (status === 403) { + const { page, destory } = await getPuppeteerPage(url, { + onBeforeLoad: async (page) => { + const allowedTypes = new Set(['document', 'script', 'xhr', 'fetch']); + await page.setRequestInterception(true); + page.on('request', (request) => { + allowedTypes.has(request.resourceType()) ? request.continue() : request.abort(); + }); + }, + }); + const content = await page.content(); + await destory(); + return content; + } + throw error; + } +}; const getSimple = async (url) => { - const data = await oFetch(url); + const data = await fetchPage(url); const $ = load(data); return $('.gallery a.cover') @@ -113,7 +130,9 @@ const parseSimpleDetail = ($ele) => { const getTorrent = async (simple, cookie) => { const { link } = simple; - const response = await oFetch(link + 'download', { followRedirect: false, responseType: 'buffer', headers: { Cookie: cookie } }); + const response = await ofetch(link + 'download', { + headers: { Cookie: cookie }, + }); return { ...simple, enclosure_url: response, @@ -123,7 +142,7 @@ const getTorrent = async (simple, cookie) => { const getDetail = async (simple) => { const { link } = simple; - const data = await oFetch(link); + const data = await fetchPage(link); const $ = load(data); const galleryImgs = $('.gallerythumb img')