rsshub
Version:
Make RSS Great Again!
208 lines (203 loc) • 7.25 kB
JavaScript
import { n as init_esm_shims, t as __dirname } from "./esm-shims-CzJ_djXG.mjs";
import { t as art } from "./render-BQo6B4tL.mjs";
import { t as invalid_parameter_default } from "./invalid-parameter-rr4AgGpp.mjs";
import path from "node:path";
import { NarouNovelFetch, R18Site, SearchBuilderR18 } from "narou";
//#region lib/routes/syosetu/types/ranking-r18.ts
init_esm_shims();
let SyosetuSub = /* @__PURE__ */ function(SyosetuSub$1) {
SyosetuSub$1["NOCTURNE"] = "noc";
SyosetuSub$1["MOONLIGHT"] = "mnlt";
SyosetuSub$1["MIDNIGHT"] = "mid";
SyosetuSub$1["MOONLIGHT_BL"] = "mnlt-bl";
return SyosetuSub$1;
}({});
let RankingPeriod = /* @__PURE__ */ function(RankingPeriod$1) {
RankingPeriod$1["DAILY"] = "daily";
RankingPeriod$1["WEEKLY"] = "weekly";
RankingPeriod$1["MONTHLY"] = "monthly";
RankingPeriod$1["QUARTER"] = "quarter";
RankingPeriod$1["YEARLY"] = "yearly";
return RankingPeriod$1;
}({});
let NovelType$1 = /* @__PURE__ */ function(NovelType$2) {
NovelType$2["TOTAL"] = "total";
NovelType$2["SHORT"] = "t";
NovelType$2["ONGOING"] = "r";
NovelType$2["COMPLETE"] = "er";
return NovelType$2;
}({});
const syosetuSubToNocgenre = {
[SyosetuSub.NOCTURNE]: R18Site.Nocturne,
[SyosetuSub.MOONLIGHT]: R18Site.MoonLight,
[SyosetuSub.MOONLIGHT_BL]: R18Site.MoonLightBL,
[SyosetuSub.MIDNIGHT]: R18Site.Midnight
};
const syosetuSubToJapanese = {
[SyosetuSub.NOCTURNE]: "ノクターン",
[SyosetuSub.MOONLIGHT]: "ムーンライト",
[SyosetuSub.MOONLIGHT_BL]: "ムーンライト BL",
[SyosetuSub.MIDNIGHT]: "ミッドナイト"
};
const periodToOrder = {
[RankingPeriod.DAILY]: "dailypoint",
[RankingPeriod.WEEKLY]: "weeklypoint",
[RankingPeriod.MONTHLY]: "monthlypoint",
[RankingPeriod.QUARTER]: "quarterpoint",
[RankingPeriod.YEARLY]: "yearlypoint"
};
const periodToJapanese = {
[RankingPeriod.DAILY]: "日間",
[RankingPeriod.WEEKLY]: "週間",
[RankingPeriod.MONTHLY]: "月間",
[RankingPeriod.QUARTER]: "四半期",
[RankingPeriod.YEARLY]: "年間"
};
const novelTypeToJapanese = {
[NovelType$1.TOTAL]: "総合",
[NovelType$1.SHORT]: "短編",
[NovelType$1.ONGOING]: "連載中",
[NovelType$1.COMPLETE]: "完結済"
};
//#endregion
//#region lib/routes/syosetu/ranking-r18.ts
/**
* Implementation of "Syosetu" R18 Rankings
*
* While "Syosetu" only provides ranking API for "Syosetu o yomou" (general audience),
* equivalent ranking functionality can be achieved using the point-based sorting in the search API.
*
* This implementation utilizes the 'order' parameter (e.g., dailypoint, weeklypoint)
* of the search API to replicate ranking functionality across all Syosetu subsidiary sites.
*/
const getParameters = () => {
const subOptions = Object.entries(SyosetuSub).map(([, value]) => ({
value,
label: syosetuSubToJapanese[value]
}));
const periodOptions = Object.entries(RankingPeriod).map(([key, value]) => ({
value,
label: `${periodToJapanese[value]} (${key})`
}));
const novelTypeOptions = Object.entries(NovelType$1).map(([key, value]) => ({
value,
label: `${novelTypeToJapanese[value]} (${key})`
}));
return {
sub: {
description: "Target site for R18 rankings",
options: subOptions
},
type: {
description: "Detailed ranking type (format: period_noveltype)",
options: periodOptions.flatMap((period) => novelTypeOptions.map((type) => ({
value: `${period.value}_${type.value}`,
label: `${period.label} ${type.label}`
})))
}
};
};
const getBest5RadarItems = () => Object.entries(SyosetuSub).flatMap(([, domain]) => Object.values(RankingPeriod).map((period) => ({
title: `${syosetuSubToJapanese[domain]} ${periodToJapanese[period]}ランキング BEST5`,
source: [`${domain === SyosetuSub.MOONLIGHT_BL ? SyosetuSub.MOONLIGHT : domain}.syosetu.com/rank/${domain === SyosetuSub.MOONLIGHT_BL ? "bltop" : "top"}`],
target: `/rankingr18/${domain}/${period}_${NovelType$1.TOTAL}?limit=5`
})));
const route = {
path: "/rankingr18/:sub/:type",
categories: ["reading"],
example: "/syosetu/rankingr18/noc/daily_total?limit=50",
parameters: getParameters(),
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false
},
name: "R18 Rankings",
url: "syosetu.com/site/group",
maintainers: ["SnowAgar25"],
handler,
description: `
| Period | Description | 説明 |
| --- | --- | --- |
| daily | Daily Ranking | 日間ランキング |
| weekly | Weekly Ranking | 週間ランキング |
| monthly | Monthly Ranking | 月間ランキング |
| quarter | Quarterly Ranking | 四半期ランキング |
| yearly | Yearly Ranking | 年間ランキング |
| Novel Type | Description | 説明 |
| --- | --- | --- |
| total | All Works | 総合 |
| t | Short Stories | 短編 |
| r | Ongoing Series | 連載中 |
| er | Completed Series | 完結済 |
::: tip
Combine Period and Novel Type with \`_\`.
For example: \`daily_total\`, \`weekly_r\`, \`monthly_er\`
:::`,
radar: [
{
source: ["noc.syosetu.com/rank/list/type/:type"],
target: "/rankingr18/noc/:type"
},
{
source: ["mid.syosetu.com/rank/list/type/:type"],
target: "/rankingr18/mid/:type"
},
{
source: ["mnlt.syosetu.com/rank/list/type/:type"],
target: "/rankingr18/mnlt/:type"
},
{
source: ["mnlt.syosetu.com/rank/bllist/type/:type"],
target: "/rankingr18/mnlt-bl/:type"
},
...getBest5RadarItems()
]
};
function parseRankingType(type) {
const [periodStr, novelTypeStr] = type.split("_");
const period = periodStr;
const novelType = novelTypeStr;
if (![Object.values(RankingPeriod).includes(period), Object.values(NovelType$1).includes(novelType)].every(Boolean)) throw new invalid_parameter_default(`Invalid ranking type: ${type}`);
return {
period: periodStr,
novelType: novelTypeStr
};
}
function getRankingTitle(type, limit) {
const { period, novelType } = parseRankingType(type);
return `${periodToJapanese[period]}${novelTypeToJapanese[novelType]}ランキング BEST${limit}`;
}
async function handler(ctx) {
const { sub, type } = ctx.req.param();
const rankingUrl = `${`https://${sub === SyosetuSub.MOONLIGHT_BL ? SyosetuSub.MOONLIGHT : sub}.syosetu.com`}/rank/list/type/${type}`;
const api = new NarouNovelFetch();
const limit = Math.min(Number(ctx.req.query("limit") ?? 300), 300);
const { period, novelType } = parseRankingType(type);
const searchParams = {
gzip: 5,
lim: limit,
order: periodToOrder[period]
};
if (novelType !== NovelType$1.TOTAL) searchParams.type = novelType;
if (!(sub in syosetuSubToNocgenre)) throw new invalid_parameter_default(`Invalid subsite: ${sub}`);
const nocgenre = syosetuSubToNocgenre[sub];
const items = (await new SearchBuilderR18(searchParams, api).r18Site(nocgenre).execute()).values.map((novel, index) => ({
title: `#${index + 1} ${novel.title}`,
link: `https://novel18.syosetu.com/${String(novel.ncode).toLowerCase()}`,
description: art(path.join(__dirname, "templates/description-e1f33e28.art"), { novel }),
author: novel.writer,
category: novel.keyword.split(/[\s/\uFF0F]/).filter(Boolean)
}));
return {
title: `小説家になろう (${sub}) - ${getRankingTitle(type, limit)}`,
link: rankingUrl,
item: items,
language: "ja"
};
}
//#endregion
export { route };