From 76c803ce731cfcaedf6f3fa17db49d139a16dae5 Mon Sep 17 00:00:00 2001 From: K1ngfish3r Date: Wed, 9 Apr 2025 18:19:53 +0500 Subject: [PATCH] RoyalRoad: Replace regex with Parser (#1622) * dom-serialize * prettier fix * use Parser only * prettier fix pt 2 * use URLSearchParams for popular and fix default cover * prettier trilogy (missing ;) --- src/plugins/english/royalroad.ts | 163 ++++++++++++++++++++++++------- 1 file changed, 125 insertions(+), 38 deletions(-) diff --git a/src/plugins/english/royalroad.ts b/src/plugins/english/royalroad.ts index eec6d7b..8439b62 100644 --- a/src/plugins/english/royalroad.ts +++ b/src/plugins/english/royalroad.ts @@ -3,20 +3,21 @@ import { fetchApi } from '@libs/fetch'; import { Plugin } from '@typings/plugin'; import { Filters, FilterTypes } from '@libs/filterInputs'; import { NovelStatus } from '@libs/novelStatus'; +import { isUrlAbsolute } from '@libs/isAbsoluteUrl'; class RoyalRoad implements Plugin.PluginBase { id = 'royalroad'; name = 'Royal Road'; - version = '2.1.4'; + version = '2.2.0'; icon = 'src/en/royalroad/icon.png'; site = 'https://www.royalroad.com/'; parseNovels(html: string) { const novels: Plugin.NovelItem[] = []; let tempNovel = {} as Plugin.NovelItem; + const baseUrl = this.site; tempNovel.name = ''; let isParsingNovel = false; - let isNovelName = false; const parser = new Parser({ onopentag(name, attribs) { if (attribs['class']?.includes('fiction-list-item')) { @@ -25,10 +26,13 @@ class RoyalRoad implements Plugin.PluginBase { if (isParsingNovel) { if (name === 'a' && attribs['class']?.includes('bold')) { tempNovel.path = attribs['href'].slice(1); - isNovelName = true; } if (name === 'img') { + tempNovel.name = attribs['alt']; tempNovel.cover = attribs['src']; + if (tempNovel.cover && !isUrlAbsolute(tempNovel.cover)) { + tempNovel.cover = baseUrl + tempNovel.cover.slice(1); + } } if (tempNovel.path && tempNovel.name) { novels.push(tempNovel); @@ -37,14 +41,8 @@ class RoyalRoad implements Plugin.PluginBase { } } }, - ontext(data) { - if (isNovelName) { - tempNovel.name += data; - } - }, onclosetag(name) { - if (name === 'i') { - isNovelName = false; + if (name === 'h2') { isParsingNovel = false; } }, @@ -61,25 +59,32 @@ class RoyalRoad implements Plugin.PluginBase { showLatestNovels, }: Plugin.PopularNovelsOptions, ): Promise { - let link = `${this.site}fictions/search`; - link += `?page=${page}`; + const params = new URLSearchParams({ + page: page.toString(), + }); + if (showLatestNovels) { + params.append('orderBy', 'last_update'); + } if (!filters) filters = this.filters || {}; - if (showLatestNovels) link += '&orderBy=last_update'; for (const key in filters) { if (filters[key as keyof typeof filters].value === '') continue; if (key === 'genres' || key === 'tags' || key === 'content_warnings') { - if (filters[key].value.include) + if (filters[key].value.include) { for (const include of filters[key].value.include) { - link += `&tagsAdd=${include}`; + params.append('tagsAdd', include); } - if (filters[key].value.exclude) + } + if (filters[key].value.exclude) { for (const exclude of filters[key].value.exclude) { - link += `&tagsRemove=${exclude}`; + params.append('tagsRemove', exclude); } - } else if (typeof filters[key as keyof typeof filters] === 'object') - link += `&${key}=${filters[key as keyof typeof filters].value}`; + } + } else { + params.append(key, String(filters[key as keyof typeof filters].value)); + } } + const link = `${this.site}fictions/search?${params.toString()}`; const body = await fetchApi(link).then(r => r.text()); return this.parseNovels(body); @@ -94,6 +99,7 @@ class RoyalRoad implements Plugin.PluginBase { summary: '', chapters: [], }; + const baseUrl = this.site; let isNovelName = false; let isAuthorName = false; let isDescription = false; @@ -110,6 +116,9 @@ class RoyalRoad implements Plugin.PluginBase { onopentag(name, attribs) { if (name === 'img' && attribs['class']?.includes('thumbnail')) { novel.cover = attribs['src']; + if (novel.cover && !isUrlAbsolute(novel.cover)) { + novel.cover = baseUrl + novel.cover.slice(1); + } } if (name === 'span' && attribs['class']?.includes('label-sm')) { isSpan++; @@ -232,26 +241,104 @@ class RoyalRoad implements Plugin.PluginBase { async parseChapter(chapterPath: string): Promise { const result = await fetchApi(this.site + chapterPath); const html = await result.text(); - const parts: string[] = []; - const regexPatterns: RegExp[] = [ - /