diff --git a/lib/routes/fanbox/namespace.ts b/lib/routes/fanbox/namespace.ts index 0244a7afd5fd..1ea8d1ee3190 100644 --- a/lib/routes/fanbox/namespace.ts +++ b/lib/routes/fanbox/namespace.ts @@ -2,5 +2,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'fanbox', - url: 'https://www.fanbox.cc', + url: 'www.fanbox.cc', }; diff --git a/lib/routes/fanqienovel/namespace.ts b/lib/routes/fanqienovel/namespace.ts new file mode 100644 index 000000000000..45456cca871f --- /dev/null +++ b/lib/routes/fanqienovel/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '番茄小说', + url: 'fanqienovel.com', + categories: ['reading'], +}; diff --git a/lib/routes/fanqienovel/page.ts b/lib/routes/fanqienovel/page.ts new file mode 100644 index 000000000000..91c63326e217 --- /dev/null +++ b/lib/routes/fanqienovel/page.ts @@ -0,0 +1,100 @@ +import type { Data, Route } from '@/types'; +import type { Context } from 'hono'; +import ofetch from '@/utils/ofetch'; +import * as cheerio from 'cheerio'; +import { parseDate } from '@/utils/parse-date'; + +interface Chapter { + itemId: string; + needPay: number; + title: string; + isChapterLock: boolean; + isPaidPublication: boolean; + isPaidStory: boolean; + volume_name: string; + firstPassTime: string; +} + +interface Page { + hasFetch: boolean; + author: string; + authorId: string; + bookId: string; + mediaId: string; + bookName: string; + status: number; + category: string; + categoryV2: string; + abstract: string; + thumbUri: string; + creationStatus: number; + wordNumber: number; + readCount: number; + description: string; + avatarUri: string; + creatorId: string; + lastPublishTime: string; + lastChapterItemId: string; + lastChapterTitle: string; + volumeNameList: string[]; + chapterListWithVolume: Chapter[][]; + chapterTotal: number; + followStatus: number; + itemIds: string[]; + hasFetchDirectory: boolean; + chapterList: any[]; + serverRendered: boolean; + genre: string; + platform: string; + type: string; + originalAuthors: string; + completeCategory: string; +} + +export const route: Route = { + path: '/page/:bookId', + example: '/fanqienovel/page/6621052928482348040', + parameters: { bookId: '小说 ID,可在 URL 中找到' }, + maintainers: ['TonyRL'], + name: '小说更新', + handler, + radar: [ + { + source: ['fanqienovel.com/page/:bookId'], + }, + ], +}; + +async function handler(ctx: Context): Promise { + const { bookId } = ctx.req.param(); + const link = `https://fanqienovel.com/page/${bookId}`; + + const response = await ofetch(link); + const $ = cheerio.load(response); + + const initialState = JSON.parse( + $('script:contains("window.__INITIAL_STATE__")') + .text() + .match(/window\.__INITIAL_STATE__\s*=\s*(.*);/)?.[1] ?? '{}' + ); + + const page = initialState.page as Page; + const items = page.chapterListWithVolume.flatMap((volume) => + volume.map((chapter) => ({ + title: chapter.title, + link: `https://fanqienovel.com/reader/${chapter.itemId}`, + description: chapter.volume_name, + pubDate: parseDate(chapter.firstPassTime, 'X'), + author: page.author, + })) + ); + + return { + title: `${page.bookName} - ${page.author}`, + description: page.abstract, + link, + language: 'zh-CN', + image: page.thumbUri, + item: items, + }; +}