mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-10-16 21:02:57 +08:00
8aeca4cf73
The app webview page defines its data with
Object.defineProperty(window, 'pixiv', { value: { novel, ... } }).
JSDOM ran that call, but the script data parser does not execute calls,
so window.pixiv was never set and the novel series route failed with
"Unsupported script data expression: CallExpression". Read the value from
the defineProperty call arguments instead.
Closes #23592
78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { load } from 'cheerio';
|
|
import queryString from 'query-string';
|
|
|
|
import cache from '@/utils/cache';
|
|
import { evaluateScriptCallback } from '@/utils/evaluate-script';
|
|
import { parseDate } from '@/utils/parse-date';
|
|
|
|
import { maskHeader } from '../../constants';
|
|
import got from '../../pixiv-got';
|
|
import type { NovelContent, NSFWNovelDetail } from './types';
|
|
import { parseNovelContent } from './utils';
|
|
|
|
export async function getNSFWNovelContent(novelId: string, token: string): Promise<NovelContent> {
|
|
return await cache.tryGet(`https://app-api.pixiv.net/webview/v2/novel:${novelId}`, async () => {
|
|
const response = await got('https://app-api.pixiv.net/webview/v2/novel', {
|
|
headers: {
|
|
...maskHeader,
|
|
Authorization: 'Bearer ' + token,
|
|
},
|
|
searchParams: queryString.stringify({
|
|
id: novelId,
|
|
viewer_version: '20221031_ai',
|
|
}),
|
|
});
|
|
|
|
const $ = load(response.data);
|
|
const script = $('script')
|
|
.toArray()
|
|
.map((element) => $(element).text())
|
|
.filter((source) => source.includes('pixiv'))
|
|
.join('\n');
|
|
if (!script) {
|
|
throw new Error('No novel data found');
|
|
}
|
|
// The page defines its data with Object.defineProperty(window, 'pixiv', { value: { novel } })
|
|
const novelDetail = (await evaluateScriptCallback<{ value?: { novel?: NSFWNovelDetail } }>(script, 'Object.defineProperty')).value?.novel;
|
|
|
|
if (!novelDetail) {
|
|
throw new Error('No novel data found');
|
|
}
|
|
|
|
const images = Object.fromEntries(
|
|
Object.entries(novelDetail.images)
|
|
.filter(([, image]) => image?.urls?.original)
|
|
.map(([id, image]) => [id, image.urls.original])
|
|
);
|
|
|
|
const parsedContent = await parseNovelContent(novelDetail.text, images, token);
|
|
|
|
return {
|
|
id: novelDetail.id,
|
|
title: novelDetail.title,
|
|
description: novelDetail.caption,
|
|
content: parsedContent,
|
|
|
|
userId: novelDetail.userId,
|
|
userName: null, // Not provided in NSFW API
|
|
|
|
bookmarkCount: novelDetail.rating.bookmark,
|
|
viewCount: novelDetail.rating.view,
|
|
likeCount: novelDetail.rating.like,
|
|
|
|
createDate: parseDate(novelDetail.cdate),
|
|
updateDate: null, // Not provided in NSFW API
|
|
|
|
isOriginal: novelDetail.isOriginal,
|
|
aiType: novelDetail.aiType,
|
|
tags: novelDetail.tags,
|
|
|
|
coverUrl: novelDetail.coverUrl,
|
|
images,
|
|
|
|
seriesId: novelDetail.seriesId || null,
|
|
seriesTitle: novelDetail.seriesTitle || null,
|
|
};
|
|
});
|
|
}
|