Skip to content

Commit

Permalink
feat: improve usability and short novel update handling
Browse files Browse the repository at this point in the history
- Add chapter display support
- Fix chapter route radar
- Optimize ranking and search limits
- Use novelupdated_at as pubDate for short novels
  • Loading branch information
SnowAgar25 committed Nov 22, 2024
1 parent 938edc3 commit f11e58a
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 25 deletions.
13 changes: 11 additions & 2 deletions lib/routes/syosetu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export const route: Route = {
radar: [
{
title: 'Novel Updates',
source: ['novel18.syosetu.com/:ncode', 'ncode.syosetu.com/:ncode'],
source: ['ncode.syosetu.com/:ncode', 'ncode.syosetu.com/:ncode/:chapter'],
target: '/:ncode',
},
{
title: 'Novel Updates',
source: ['novel18.syosetu.com/:ncode', 'novel18.syosetu.com/:ncode/:chapter'],
target: '/:ncode',
},
],
Expand All @@ -42,6 +47,10 @@ async function handler(ctx: Context): Promise<Data> {
const chapterUrl = `${baseUrl}/${ncode}`;
const item = await fetchChapterContent(chapterUrl);

// Shorts are updated rather than having new chapters
// Use novelupdated_at as pubDate since RSS 2.0 doesn't have updated field
item.pubDate = novel.novelupdated_at;

return {
title: novel.title,
description: novel.story,
Expand All @@ -61,7 +70,7 @@ async function handler(ctx: Context): Promise<Data> {
const chapterNumber = startChapter + index;
const chapterUrl = `${baseUrl}/${ncode}/${chapterNumber}`;

const item = await fetchChapterContent(chapterUrl);
const item = await fetchChapterContent(chapterUrl, chapterNumber);
return item;
}).reverse()
);
Expand Down
10 changes: 5 additions & 5 deletions lib/routes/syosetu/ranking-isekai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ export function parseIsekaiRankingType(type: string): { period: RankingPeriod; c
return { period, category, novelType };
}

function getIsekaiSearchParams(period, category, novelType): SearchParams {
function getIsekaiSearchParams(period, category, novelType, limit): SearchParams {
const searchParams: SearchParams = {
order: periodToOrder[period],
gzip: 5,
lim: 150,
lim: Math.ceil(limit / 2),
};

if (novelType !== NovelType.TOTAL) {
Expand All @@ -56,9 +56,9 @@ function getIsekaiSearchParams(period, category, novelType): SearchParams {
export async function handleIsekaiRanking(type: string, limit: number): Promise<Data> {
const { period, category, novelType } = parseIsekaiRankingType(type);
const rankingUrl = `https://yomou.syosetu.com/rank/isekailist/type/${type}`;
const rankingTitle = `[${periodToJapanese[period]}] 異世界転生/転移${isekaiCategoryToJapanese[category]}ランキング - ${novelTypeToJapanese[novelType]}`;
const rankingTitle = `[${periodToJapanese[period]}] 異世界転生/転移${isekaiCategoryToJapanese[category]}ランキング - ${novelTypeToJapanese[novelType]} BEST${limit}`;

const searchParams = getIsekaiSearchParams(period, category, novelType);
const searchParams = getIsekaiSearchParams(period, category, novelType, limit);
const api = new NarouNovelFetch();

const [tenseiResult, tenniResult] = await Promise.all([new SearchBuilder({ ...searchParams, istensei: 1 }, api).execute(), new SearchBuilder({ ...searchParams, istenni: 1 }, api).execute()]);
Expand Down Expand Up @@ -86,7 +86,7 @@ export async function handleIsekaiRanking(type: string, limit: number): Promise<
return {
title: `小説家になろう - ${rankingTitle}`,
link: rankingUrl,
item: (items as DataItem[]).slice(0, limit),
item: items as DataItem[],
language: 'ja',
};
}
12 changes: 6 additions & 6 deletions lib/routes/syosetu/ranking-r18.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Route, Data } from '@/types';
import { Route, Data, DataItem } from '@/types';
import { art } from '@/utils/render';
import path from 'node:path';
import { Context } from 'hono';
Expand Down Expand Up @@ -136,9 +136,9 @@ function parseRankingType(type: string): { period: RankingPeriod; novelType: Nov
};
}

function getRankingTitle(type: string): string {
function getRankingTitle(type: string, limit: number): string {
const { period, novelType } = parseRankingType(type);
return `${periodToJapanese[period]}${novelTypeToJapanese[novelType]}ランキング`;
return `${periodToJapanese[period]}${novelTypeToJapanese[novelType]}ランキング BEST${limit}`;
}

async function handler(ctx: Context): Promise<Data> {
Expand All @@ -152,7 +152,7 @@ async function handler(ctx: Context): Promise<Data> {

const searchParams: SearchParams = {
gzip: 5,
lim: 300,
lim: limit,
order: periodToOrder[period],
};

Expand Down Expand Up @@ -180,9 +180,9 @@ async function handler(ctx: Context): Promise<Data> {
}));

return {
title: `小説家になろう (${sub}) - ${getRankingTitle(type)}`,
title: `小説家になろう (${sub}) - ${getRankingTitle(type, limit)}`,
link: rankingUrl,
item: items.slice(0, limit),
item: items as DataItem[],
language: 'ja',
};
}
11 changes: 5 additions & 6 deletions lib/routes/syosetu/ranking.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Route, Data } from '@/types';
import { Route, Data, DataItem } from '@/types';
import { art } from '@/utils/render';
import path from 'node:path';
import { Context } from 'hono';
Expand Down Expand Up @@ -212,7 +212,7 @@ async function handler(ctx: Context): Promise<Data> {
const api = new NarouNovelFetch();
const searchParams: SearchParams = {
gzip: 5,
lim: 300,
lim: limit,
};

let rankingUrl: string;
Expand All @@ -223,7 +223,7 @@ async function handler(ctx: Context): Promise<Data> {
case RankingType.LIST: {
const { period, novelType } = parseGeneralRankingType(type);
rankingUrl = `https://yomou.syosetu.com/rank/list/type/${type}`;
rankingTitle = `[${periodToJapanese[period]}] 総合ランキング - ${novelTypeToJapanese[novelType]}`;
rankingTitle = `[${periodToJapanese[period]}] 総合ランキング - ${novelTypeToJapanese[novelType]} BEST${limit}`;

searchParams.order = periodToOrder[period];
if (novelType !== NovelType.TOTAL) {
Expand All @@ -235,7 +235,7 @@ async function handler(ctx: Context): Promise<Data> {
case RankingType.GENRE: {
const { period, genre, novelType } = parseGenreRankingType(type);
rankingUrl = `https://yomou.syosetu.com/rank/genrelist/type/${type}`;
rankingTitle = `[${periodToJapanese[period]}] ${GenreNotation[genre]}ランキング - ${novelTypeToJapanese[novelType]}`;
rankingTitle = `[${periodToJapanese[period]}] ${GenreNotation[genre]}ランキング - ${novelTypeToJapanese[novelType]} BEST${limit}`;

searchParams.order = periodToOrder[period];
searchParams.genre = genre as Genre;
Expand All @@ -248,7 +248,6 @@ async function handler(ctx: Context): Promise<Data> {
case RankingType.ISEKAI:
return handleIsekaiRanking(type, limit);


default:
throw new InvalidParameterError(`Invalid ranking type: ${type}`);
}
Expand All @@ -269,7 +268,7 @@ async function handler(ctx: Context): Promise<Data> {
return {
title: `小説家になろう - ${rankingTitle}`,
link: rankingUrl,
item: items.slice(0, limit),
item: items as DataItem[],
language: 'ja',
};
}
7 changes: 4 additions & 3 deletions lib/routes/syosetu/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,12 @@ const setIfExists = (value) => value ?? undefined;
* @see https://deflis.github.io/node-narou/index.html
* @see https://dev.syosetu.com/man/api/
*/
function mapToSearchParams(query: string): SearchParams {
function mapToSearchParams(query: string, limit: number): SearchParams {
const params = queryString.parse(query) as NarouSearchParams;

const searchParams: SearchParams = {
gzip: 5,
lim: 40,
lim: limit,
};

searchParams.word = setIfExists(params.word);
Expand Down Expand Up @@ -251,7 +251,8 @@ async function handler(ctx: Context): Promise<Data> {
const { sub, query } = ctx.req.param();
const searchUrl = `https://${sub}.syosetu.com/search/search/search.php?${query}`;

const searchParams = mapToSearchParams(query);
const limit = Math.min(Number(ctx.req.query('limit') ?? 40), 40);
const searchParams = mapToSearchParams(query, limit);
const builder = createNovelSearchBuilder(sub, searchParams);
const result = await builder.execute();

Expand Down
6 changes: 3 additions & 3 deletions lib/routes/syosetu/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { NarouNovelFetch, NarouSearchResult, SearchBuilder, SearchBuilderR18 } f

export async function fetchNovelInfo(ncode: string): Promise<{ baseUrl: string; novel: NarouSearchResult }> {
const api = new NarouNovelFetch();
const [generalRes, r18Res] = await Promise.all([new SearchBuilder({ gzip: 5, of: 't-s-k-ga-nt' }, api).ncode(ncode).execute(), new SearchBuilderR18({ gzip: 5, of: 't-s-k-ga-nt' }, api).ncode(ncode).execute()]);
const [generalRes, r18Res] = await Promise.all([new SearchBuilder({ gzip: 5, of: 't-s-k-ga-nt-nu' }, api).ncode(ncode).execute(), new SearchBuilderR18({ gzip: 5, of: 't-s-k-ga-nt-nu' }, api).ncode(ncode).execute()]);

const isGeneral = generalRes.allcount !== 0;
const novelData = isGeneral ? generalRes : r18Res;
Expand All @@ -24,7 +24,7 @@ export async function fetchNovelInfo(ncode: string): Promise<{ baseUrl: string;
};
}

export async function fetchChapterContent(chapterUrl: string): Promise<DataItem> {
export async function fetchChapterContent(chapterUrl: string, chapter?: number): Promise<DataItem> {
return (await cache.tryGet(chapterUrl, async () => {
const response = await ofetch(chapterUrl, {
headers: {
Expand All @@ -35,7 +35,7 @@ export async function fetchChapterContent(chapterUrl: string): Promise<DataItem>

const $ = load(response);

const title = $('.p-novel__title').html() || '';
const title = `${chapter ? `#${chapter} ` : ''}${$('.p-novel__title').html() || ''}`;
const description = $('.p-novel__body').html() || '';
const pubDate = $('meta[name=WWWC]').attr('content');

Expand Down

0 comments on commit f11e58a

Please sign in to comment.