Skip to content

Commit

Permalink
Merge pull request #634 from DIYgod/master
Browse files Browse the repository at this point in the history
[pull] master from diygod:master
  • Loading branch information
pull[bot] authored Nov 25, 2024
2 parents 0433ad0 + 4c3dffa commit 793da61
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 198 deletions.
12 changes: 0 additions & 12 deletions lib/routes/pixiv/novel-api/content/common.ts

This file was deleted.

5 changes: 0 additions & 5 deletions lib/routes/pixiv/novel-api/content/nsfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import queryString from 'query-string';
import { parseNovelContent } from './utils';
import type { NovelContent, NSFWNovelDetail } from './types';
import { parseDate } from '@/utils/parse-date';
import { getNovelLanguage } from './common';

export async function getNSFWNovelContent(novelId: string, token: string): Promise<NovelContent> {
return (await cache.tryGet(`https://app-api.pixiv.net/webview/v2/novel:${novelId}`, async () => {
Expand Down Expand Up @@ -44,8 +43,6 @@ export async function getNSFWNovelContent(novelId: string, token: string): Promi

const parsedContent = await parseNovelContent(novelDetail.text, images, token);

const language = await getNovelLanguage(novelId);

return {
id: novelDetail.id,
title: novelDetail.title,
Expand All @@ -71,8 +68,6 @@ export async function getNSFWNovelContent(novelId: string, token: string): Promi

seriesId: novelDetail.seriesId || null,
seriesTitle: novelDetail.seriesTitle || null,

language,
};
})) as NovelContent;
}
5 changes: 0 additions & 5 deletions lib/routes/pixiv/novel-api/content/sfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import pixivUtils from '../../utils';
import { parseNovelContent } from './utils';
import { NovelContent, SFWNovelDetail } from './types';
import { parseDate } from '@/utils/parse-date';
import { getNovelLanguage } from './common';

const baseUrl = 'https://www.pixiv.net';

Expand Down Expand Up @@ -34,8 +33,6 @@ export async function getSFWNovelContent(novelId: string): Promise<NovelContent>

const parsedContent = await parseNovelContent(novelDetail.body.content, images);

const language = await getNovelLanguage(novelId);

return {
id: body.id,
title: body.title,
Expand All @@ -61,8 +58,6 @@ export async function getSFWNovelContent(novelId: string): Promise<NovelContent>

seriesId: body.seriesNavData?.seriesId?.toString() || null,
seriesTitle: body.seriesNavData?.title || null,

language,
};
})) as NovelContent;
}
2 changes: 0 additions & 2 deletions lib/routes/pixiv/novel-api/content/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ export interface NovelContent {

seriesId: string | null;
seriesTitle: string | null;

language: string | null;
}

export interface SFWNovelDetail {
Expand Down
63 changes: 33 additions & 30 deletions lib/routes/pixiv/novel-api/series/nsfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,34 @@ import got from '../../pixiv-got';
import { maskHeader } from '../../constants';
import { getNSFWNovelContent } from '../content/nsfw';
import pixivUtils from '../../utils';
import { SeriesContentResponse, SeriesDetail, SeriesFeed } from './types';
import { AppNovelSeries, SeriesDetail, SeriesFeed } from './types';
import ConfigNotFoundError from '@/errors/types/config-not-found';
import { getToken } from '../../token';
import { config } from '@/config';
import cache from '@/utils/cache';
import queryString from 'query-string';

const baseUrl = 'https://www.pixiv.net';

async function getNovelSeries(seriesId: string, offset: number, token: string): Promise<AppNovelSeries> {
const rsp = await got('https://app-api.pixiv.net/v2/novel/series', {
headers: {
...maskHeader,
Authorization: 'Bearer ' + token,
},
searchParams: queryString.stringify({
series_id: seriesId,
last_order: offset,
}),
});
return rsp.data as AppNovelSeries;
}

export async function getNSFWSeriesNovels(seriesId: string, limit: number = 10): Promise<SeriesFeed> {
if (limit > 30) {
limit = 30;
}

if (!config.pixiv || !config.pixiv.refreshToken) {
throw new ConfigNotFoundError('This user is an R18 creator, PIXIV_REFRESHTOKEN is required.\npixiv RSS is disabled due to the lack of relevant config.\n該用戶爲 R18 創作者,需要 PIXIV_REFRESHTOKEN。');
}
Expand All @@ -26,54 +45,38 @@ export async function getNSFWSeriesNovels(seriesId: string, limit: number = 10):
Authorization: 'Bearer ' + token,
},
});

const seriesData = seriesResponse.data as SeriesDetail;

if (seriesData.error) {
throw new Error(seriesData.message || 'Failed to get series detail');
let offset = seriesData.body.total - limit;
if (offset < 0) {
offset = 0;
}

// Get chapters
const chaptersResponse = await got(`${baseUrl}/ajax/novel/series/${seriesId}/content_titles`, {
headers: {
...maskHeader,
Authorization: 'Bearer ' + token,
},
});

const data = chaptersResponse.data as SeriesContentResponse;

if (data.error) {
throw new Error(data.message || 'Failed to get series data');
}

const chapters = data.body.slice(-Math.abs(limit));
const chapterStartNum = Math.max(data.body.length - limit + 1, 1);
const appSeriesData = await getNovelSeries(seriesId, offset, token);

const items = await Promise.all(
chapters.map(async (chapter, index) => {
const novelContent = await getNSFWNovelContent(chapter.id, token);
appSeriesData.novels.map(async (novel) => {
const novelContent = await getNSFWNovelContent(novel.id, token);
return {
title: `#${chapterStartNum + index} ${novelContent.title}`,
title: novel.title,
description: `
<img src="${pixivUtils.getProxiedImageUrl(novelContent.coverUrl)}" />
<div lang="${novelContent.language}">
<div>
<p>${novelContent.description}</p>
<hr>
${novelContent.content}
</div>
`,
link: `${baseUrl}/novel/show.php?id=${novelContent.id}`,
pubDate: novelContent.createDate,
author: novelContent.userName || `User ID: ${novelContent.userId}`,
link: `${baseUrl}/novel/show.php?id=${novel.id}`,
pubDate: novel.create_date,
author: novel.user.name,
category: novelContent.tags,
};
})
);

return {
title: seriesData.body.title,
description: seriesData.body.caption,
title: appSeriesData.novel_series_detail.title,
description: appSeriesData.novel_series_detail.caption,
link: `${baseUrl}/novel/series/${seriesId}`,
image: pixivUtils.getProxiedImageUrl(seriesData.body.cover.urls.original),
item: items,
Expand Down
2 changes: 1 addition & 1 deletion lib/routes/pixiv/novel-api/series/sfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export async function getSFWSeriesNovels(seriesId: string, limit: number = 10):
title: `#${chapterStartNum + index} ${novelContent.title}`,
description: `
<img src="${pixivUtils.getProxiedImageUrl(novelContent.coverUrl)}" />
<div lang="${novelContent.language}">
<div>
<p>${novelContent.description}</p>
<hr>
${novelContent.content}
Expand Down
26 changes: 26 additions & 0 deletions lib/routes/pixiv/novel-api/series/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface SeriesDetail {
latestNovelId: string;
xRestrict: number;
isOriginal: boolean;
total: number;
cover: {
urls: {
original: string;
Expand Down Expand Up @@ -66,3 +67,28 @@ export interface SeriesFeed {
category?: string[];
}>;
}

export interface AppUser {
id: number;
name: string;
}

export interface AppNovelSeriesDetail {
id: string;
title: string;
caption: string;
content_count: number;
is_concluded: boolean;
is_original: boolean;
user: AppUser;
}

export interface AppNovelSeries {
novel_series_detail: AppNovelSeriesDetail;
novels: {
id: string;
title: string;
create_date: Date;
user: AppUser;
}[];
}
4 changes: 1 addition & 3 deletions lib/routes/pixiv/novel-api/user-novels/nsfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import ConfigNotFoundError from '@/errors/types/config-not-found';
import cache from '@/utils/cache';
import { getToken } from '../../token';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import { getNovelLanguage } from '../content/common';

function getNovels(user_id: string, token: string): Promise<NSFWNovelsResponse> {
return got('https://app-api.pixiv.net/v1/user/novels', {
Expand Down Expand Up @@ -47,12 +46,11 @@ export async function getNSFWUserNovels(id: string, fullContent: boolean = false

const items = await Promise.all(
novels.map(async (novel) => {
const language = await getNovelLanguage(novel.id);
const baseItem = {
title: novel.series?.title ? `${novel.series.title} - ${novel.title}` : novel.title,
description: `
<img src="${pixivUtils.getProxiedImageUrl(novel.image_urls.large)}" />
<div lang="${language}">
<div>
<p>${convertPixivProtocolExtended(novel.caption)}</p>
</div>`,
author: novel.user.name,
Expand Down
4 changes: 1 addition & 3 deletions lib/routes/pixiv/novel-api/user-novels/sfw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { parseDate } from '@/utils/parse-date';
import pixivUtils from '../../utils';
import { getSFWNovelContent } from '../content/sfw';
import type { SFWNovelsResponse, NovelList } from './types';
import { getNovelLanguage } from '../content/common';

const baseUrl = 'https://www.pixiv.net';

Expand Down Expand Up @@ -37,12 +36,11 @@ export async function getSFWUserNovels(id: string, fullContent: boolean = false,

const items = await Promise.all(
Object.values(data.body.works).map(async (item) => {
const language = await getNovelLanguage(item.id);
const baseItem = {
title: item.title,
description: `
<img src=${pixivUtils.getProxiedImageUrl(item.url)} />
<div lang="${language}">
<div>
<p>${item.description}</p>
</div>
`,
Expand Down
2 changes: 1 addition & 1 deletion lib/routes/pixiv/novel-series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Pixiv 登錄後的 refresh_token,用於獲取 R18 小說
supportScihub: false,
},
name: 'Novel Series',
maintainers: ['SnowAgar25'],
maintainers: ['SnowAgar25', 'keocheung'],
handler,
radar: [
{
Expand Down
Loading

0 comments on commit 793da61

Please sign in to comment.