rsshub
Version:
Make RSS Great Again!
201 lines (195 loc) • 8.06 kB
JavaScript
import "./esm-shims-CzJ_djXG.mjs";
import { t as config } from "./config-C37vj7VH.mjs";
import { t as ViewType } from "./types-D84BRIt4.mjs";
import "./dist-BInvbO1W.mjs";
import "./logger-Czu8UMNd.mjs";
import "./ofetch-BIyrKU3Y.mjs";
import { t as parseDate } from "./parse-date-BrP7mxXf.mjs";
import { t as cache_default } from "./cache-Bo__VnGm.mjs";
import "./helpers-DxBp0Pty.mjs";
import { t as got_default } from "./got-KxxWdaxq.mjs";
import { t as invalid_parameter_default } from "./invalid-parameter-rr4AgGpp.mjs";
import { t as config_not_found_default } from "./config-not-found-Dyp3RlZZ.mjs";
import { n as queryToBoolean, t as fallback } from "./readable-social-DoIL4WB3.mjs";
import { i as maskHeader, n as getToken, r as pixiv_got_default, t as utils_default } from "./utils-BFdHMhIn.mjs";
import { n as getNSFWNovelContent, r as convertPixivProtocolExtended, t as getSFWNovelContent } from "./sfw-bRjk5cgb.mjs";
import queryString from "query-string";
//#region lib/routes/pixiv/novel-api/user-novels/nsfw.ts
function getNovels(user_id, token) {
return pixiv_got_default("https://app-api.pixiv.net/v1/user/novels", {
headers: {
...maskHeader,
Authorization: "Bearer " + token
},
searchParams: queryString.stringify({
user_id,
filter: "for_ios"
})
});
}
async function getNSFWUserNovels(id, fullContent = false, limit = 100) {
if (!config.pixiv || !config.pixiv.refreshToken) throw new config_not_found_default("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。");
const token = await getToken(cache_default.tryGet);
if (!token) throw new config_not_found_default("pixiv not login");
const response = await getNovels(id, token);
const novels = limit ? response.data.novels.slice(0, limit) : response.data.novels;
if (novels.length === 0) throw new invalid_parameter_default(`${id} is not a valid user ID, or the user has no novels.\n${id} 不是有效的用戶 ID,或者該用戶沒有小說作品。`);
const username = novels[0].user.name;
const items = await Promise.all(novels.map(async (novel) => {
const baseItem = {
title: novel.series?.title ? `${novel.series.title} - ${novel.title}` : novel.title,
description: `
<img src="${utils_default.getProxiedImageUrl(novel.image_urls.large)}" />
<div>
<p>${convertPixivProtocolExtended(novel.caption)}</p>
</div>`,
author: novel.user.name,
pubDate: parseDate(novel.create_date),
link: `https://www.pixiv.net/novel/show.php?id=${novel.id}`,
category: novel.tags.map((t) => t.name)
};
if (!fullContent) return baseItem;
const { content } = await getNSFWNovelContent(novel.id, token);
return {
...baseItem,
description: `${baseItem.description}<hr>${content}`
};
}));
return {
title: `${username}'s novels - pixiv`,
description: `${username} 的 pixiv 最新小说`,
image: utils_default.getProxiedImageUrl(novels[0].user.profile_image_urls.medium),
link: `https://www.pixiv.net/users/${id}/novels`,
item: items
};
}
//#endregion
//#region lib/routes/pixiv/novel-api/user-novels/sfw.ts
const baseUrl = "https://www.pixiv.net";
async function getSFWUserNovels(id, fullContent = false, limit = 100) {
const url = `${baseUrl}/users/${id}/novels`;
const { data: allData } = await got_default(`${baseUrl}/ajax/user/${id}/profile/all`, { headers: { referer: url } });
const novels = Object.keys(allData.body.novels).toSorted((a, b) => Number(b) - Number(a)).slice(0, Number.parseInt(String(limit), 10));
if (novels.length === 0) throw new Error("No novels found for this user, or is an R18 creator, fallback to ConfigNotFoundError");
const searchParams = new URLSearchParams();
for (const novel of novels) searchParams.append("ids[]", novel);
const { data } = await got_default(`${baseUrl}/ajax/user/${id}/profile/novels`, {
headers: { referer: url },
searchParams
});
const items = await Promise.all(Object.values(data.body.works).map(async (item) => {
const baseItem = {
title: item.title,
description: `
<img src=${utils_default.getProxiedImageUrl(item.url)} />
<div>
<p>${item.description}</p>
</div>
`,
link: `${baseUrl}/novel/show.php?id=${item.id}`,
author: item.userName,
pubDate: parseDate(item.createDate),
updated: parseDate(item.updateDate),
category: item.tags
};
if (!fullContent) return baseItem;
const { content } = await getSFWNovelContent(item.id);
return {
...baseItem,
description: `${baseItem.description}<hr>${content}`
};
}));
return {
title: data.body.extraData.meta.title,
description: data.body.extraData.meta.ogp.description,
image: utils_default.getProxiedImageUrl(Object.values(data.body.works)[0].profileImageUrl),
link: url,
item: items
};
}
//#endregion
//#region lib/routes/pixiv/novels.ts
const route = {
path: "/user/novels/:id/:full_content?",
categories: ["social-media"],
view: ViewType.Articles,
example: "/pixiv/user/novels/27104704",
parameters: {
id: "User id, available in user's homepage URL",
full_content: {
description: "Enable or disable the display of full content. ",
options: [{
value: "true",
label: "true"
}, {
value: "false",
label: "false"
}],
default: "false"
}
},
features: {
requireConfig: [{
name: "PIXIV_REFRESHTOKEN",
optional: true,
description: `
Pixiv 登錄後的 refresh_token,用於獲取 R18 小說
refresh_token after Pixiv login, required for accessing R18 novels
[https://docs.rsshub.app/deploy/config#pixiv](https://docs.rsshub.app/deploy/config#pixiv)`
}],
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true
},
radar: [{
title: "User Novels (簡介 Basic info)",
source: [
"www.pixiv.net/users/:id/novels",
"www.pixiv.net/users/:id",
"www.pixiv.net/en/users/:id/novels",
"www.pixiv.net/en/users/:id"
],
target: "/user/novels/:id"
}, {
title: "User Novels (全文 Full text)",
source: [
"www.pixiv.net/users/:id/novels",
"www.pixiv.net/users/:id",
"www.pixiv.net/en/users/:id/novels",
"www.pixiv.net/en/users/:id"
],
target: "/user/novels/:id/true"
}],
name: "User Novels",
maintainers: ["TonyRL", "SnowAgar25"],
handler,
description: `
| 小說類型 Novel Type | full_content | PIXIV_REFRESHTOKEN | 返回內容 Content |
|-------------------|--------------|-------------------|-----------------|
| Non R18 | false | 不需要 Not Required | 簡介 Basic info |
| Non R18 | true | 不需要 Not Required | 全文 Full text |
| R18 | false | 需要 Required | 簡介 Basic info |
| R18 | true | 需要 Required | 全文 Full text |
Default value for \`full_content\` is \`false\` if not specified.
Example:
- \`/pixiv/user/novels/79603797\` → 簡介 Basic info
- \`/pixiv/user/novels/79603797/true\` → 全文 Full text`
};
const hasPixivAuth = () => Boolean(config.pixiv && config.pixiv.refreshToken);
async function handler(ctx) {
const id = ctx.req.param("id");
const fullContent = fallback(void 0, queryToBoolean(ctx.req.param("full_content")), false);
const { limit } = ctx.req.query();
if (hasPixivAuth()) return await getNSFWUserNovels(id, fullContent, limit);
const nonR18Result = await getSFWUserNovels(id, fullContent, limit).catch((error) => {
if (error.name === "Error") return null;
throw error;
});
if (nonR18Result) return nonR18Result;
throw new config_not_found_default("This user may not have any novel works, or is an R18 creator, PIXIV_REFRESHTOKEN is required.\npixiv RSS is disabled due to the lack of relevant config.\n該用戶可能沒有小說作品,或者該用戶爲 R18 創作者,需要 PIXIV_REFRESHTOKEN。");
}
//#endregion
export { route };