mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-03-13 10:30:18 +08:00
feat(comic-walker.com): 新添一个路由 (#21310)
* add route * style: auto format * Update manga.ts * Update manga.ts * ts -> tsx * Update manga.tsx * 没法了,这是我最后的手段了( * Update manga.ts * Update manga.ts * Update manga.ts * Update manga.ts --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b55864eed0
commit
e2c991a0ed
122
lib/routes/comic-walker/manga.ts
Normal file
122
lib/routes/comic-walker/manga.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { load } from 'cheerio';
|
||||
|
||||
import type { Route } from '@/types';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
|
||||
const getEpisodes = (obj: any) => obj?.result || obj?.items || (Array.isArray(obj) ? obj : []);
|
||||
|
||||
export const route: Route = {
|
||||
path: '/manga/:id',
|
||||
categories: ['anime'],
|
||||
example: '/comic-walker/manga/KC_006778_S',
|
||||
parameters: { id: 'カドコミ(Kadocomi)中对应的作品workCode,例如 KC_006778_S' },
|
||||
features: {
|
||||
requireConfig: false,
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
supportBT: false,
|
||||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: ['comic-walker.com/detail/:id'],
|
||||
target: '/manga/:id',
|
||||
},
|
||||
],
|
||||
name: 'カドコミ(Kadocomi)漫画详情',
|
||||
maintainers: ['xiaobailoves'],
|
||||
|
||||
handler: async (ctx) => {
|
||||
const { id } = ctx.req.param();
|
||||
const baseUrl = 'https://comic-walker.com';
|
||||
|
||||
const fetchUrl = `${baseUrl}/detail/${id}?episodeType=first`;
|
||||
const openUrl = `${baseUrl}/detail/${id}`;
|
||||
|
||||
const response = await ofetch<string>(fetchUrl, {
|
||||
headers: {
|
||||
Referer: baseUrl,
|
||||
'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8',
|
||||
},
|
||||
});
|
||||
|
||||
const $ = load(response);
|
||||
const nextDataText = $('#__NEXT_DATA__').text();
|
||||
|
||||
if (!nextDataText) {
|
||||
throw new Error('无法解析页面 HTML 数据,可能触发了反爬策略或页面结构巨变');
|
||||
}
|
||||
|
||||
const nextData = JSON.parse(nextDataText);
|
||||
const queries = nextData.props?.pageProps?.dehydratedState?.queries || [];
|
||||
|
||||
const workQuery = queries.find((q: any) => q.queryKey?.includes('/api/contents/details/work') || (Array.isArray(q.queryKey) && q.queryKey.some((k: any) => typeof k === 'string' && k.includes('work'))));
|
||||
|
||||
if (!workQuery || !workQuery.state?.data) {
|
||||
throw new Error('无法在 HTML 缓存中提取核心数据对象');
|
||||
}
|
||||
|
||||
const data = workQuery.state.data;
|
||||
const work = data.work;
|
||||
|
||||
if (!work) {
|
||||
throw new Error('成功获取数据对象,但未找到作品基本信息');
|
||||
}
|
||||
|
||||
const mangaTitle = work.title || $('title').text().trim();
|
||||
const mangaAuthor = work.authors?.map((author: any) => author.name).join(', ') || '';
|
||||
const mangaDescription = work.summary || '';
|
||||
const coverImage = work.bookCover || work.thumbnail;
|
||||
|
||||
const firstEpisodes = getEpisodes(data.firstEpisodes);
|
||||
const latestEpisodes = getEpisodes(data.latestEpisodes);
|
||||
const extraEpisodes = getEpisodes(data.episodes);
|
||||
|
||||
const seenCodes = new Set<string>();
|
||||
const allChapters = [...firstEpisodes, ...latestEpisodes, ...extraEpisodes]
|
||||
.filter((ep) => {
|
||||
if (ep?.code && !seenCodes.has(ep.code)) {
|
||||
seenCodes.add(ep.code);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.toSorted((a: any, b: any) => (b.internal?.episodeNo || 0) - (a.internal?.episodeNo || 0));
|
||||
|
||||
if (allChapters.length === 0) {
|
||||
throw new Error('HTML 缓存中无章节!');
|
||||
}
|
||||
|
||||
const items = allChapters.map((chapter: any) => {
|
||||
const epType = chapter.type === 'normal' ? '正篇' : '特别篇/PR';
|
||||
const isReadStatus = chapter.isActive ? '' : ' (未解锁/仍需等待)';
|
||||
const fullTitle = `${chapter.title}${chapter.subTitle ? ` - ${chapter.subTitle}` : ''}${isReadStatus}`;
|
||||
const thumb = chapter.originalThumbnail || chapter.thumbnail;
|
||||
|
||||
const currentPubDate = chapter.updateDate ? parseDate(chapter.updateDate) : undefined;
|
||||
|
||||
return {
|
||||
title: fullTitle,
|
||||
link: `${baseUrl}/detail/${id}/episodes/${chapter.code}`,
|
||||
description: `
|
||||
${thumb ? `<img src="${thumb}" style="max-width: 100%;"><br>` : ''}
|
||||
`,
|
||||
guid: `Kadocomi-manga-${chapter.code}`,
|
||||
category: epType,
|
||||
author: mangaAuthor,
|
||||
pubDate: currentPubDate,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
title: `Kadocomi - ${mangaTitle}`,
|
||||
link: openUrl,
|
||||
description: mangaDescription,
|
||||
image: coverImage,
|
||||
item: items,
|
||||
language: 'ja',
|
||||
};
|
||||
},
|
||||
};
|
||||
7
lib/routes/comic-walker/namespace.ts
Normal file
7
lib/routes/comic-walker/namespace.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'カドコミ(Kadocomi)',
|
||||
url: 'comic-walker.com',
|
||||
lang: 'ja',
|
||||
};
|
||||
Reference in New Issue
Block a user