UNPKG

rsshub

Version:
208 lines (203 loc) 7.25 kB
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 };