From 592bbcc551fd47e626857982f1d1183d57faf053 Mon Sep 17 00:00:00 2001 From: nyagami Date: Mon, 18 Mar 2024 23:02:05 +0700 Subject: [PATCH] Migrate Japanese and Chinese plugins (#838) * add Syosetu * add 69shu * add 69xinshu * add linovelib * clean up --- plugins/chinese/{69shu.broken.ts => 69shu.ts} | 46 ++++++++--------- .../{69xinshu.broken.ts => 69xinshu.ts} | 43 ++++++++-------- .../{linovelib.broken.ts => linovelib.ts} | 26 ++++------ .../{Syosetu.broken.ts => Syosetu.ts} | 50 +++++++++++-------- 4 files changed, 83 insertions(+), 82 deletions(-) rename plugins/chinese/{69shu.broken.ts => 69shu.ts} (87%) rename plugins/chinese/{69xinshu.broken.ts => 69xinshu.ts} (79%) rename plugins/chinese/{linovelib.broken.ts => linovelib.ts} (96%) rename plugins/japanese/{Syosetu.broken.ts => Syosetu.ts} (87%) diff --git a/plugins/chinese/69shu.broken.ts b/plugins/chinese/69shu.ts similarity index 87% rename from plugins/chinese/69shu.broken.ts rename to plugins/chinese/69shu.ts index d2ab6a9..0668063 100644 --- a/plugins/chinese/69shu.broken.ts +++ b/plugins/chinese/69shu.ts @@ -39,14 +39,12 @@ class Shu69 implements Plugin.PluginBase { const novelName = loadedCheerio(el).find('h4.name').text().trim(); const novelCover = loadedCheerio(el).find('a.cover > img').attr('src'); - const novelUrl = this.site + url; - if (!url) return; const novel = { name: novelName, cover: novelCover, - url: novelUrl, + path: url, }; novels.push(novel); @@ -55,8 +53,8 @@ class Shu69 implements Plugin.PluginBase { return novels; } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; + async parseNovel(novelPath: string): Promise { + const url = this.site + novelPath; const body = await fetchText(url); if (body === '') throw Error('无法获取小说内容,请检查网络'); @@ -64,12 +62,11 @@ class Shu69 implements Plugin.PluginBase { let loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { - url, + path: novelPath, chapters: [], + name: loadedCheerio('h1').text().trim() }; - novel.name = loadedCheerio('h1').text().trim(); - novel.cover = loadedCheerio('div.cover > img').attr('src'); novel.summary = loadedCheerio('#bookIntro').text().trim(); @@ -95,23 +92,25 @@ class Shu69 implements Plugin.PluginBase { const chaptersLoadedCheerio = parseHTML(chaptersBody); chaptersLoadedCheerio('dd').each((i, el) => { - const chapterUrl = this.site + chaptersLoadedCheerio(el).find('a').attr('href'); + const chapterUrl = chaptersLoadedCheerio(el).find('a').attr('href'); const chapterName = chaptersLoadedCheerio(el).find('a').text().trim(); - - chapters.push({ - name: chapterName, - url: chapterUrl, - }); + if(chapterUrl){ + chapters.push({ + name: chapterName, + path: chapterUrl, + }); + } }); } else { loadedCheerio('div.panel.hidden-xs > dl.panel-chapterlist:nth-child(2) > dd').each((i, el) => { - const chapterUrl = this.site + loadedCheerio(el).find('a').attr('href'); + const chapterUrl = loadedCheerio(el).find('a').attr('href'); const chapterName = loadedCheerio(el).find('a').text().trim(); - - chapters.push({ - name: chapterName, - url: chapterUrl, - }); + if(chapterUrl){ + chapters.push({ + name: chapterName, + path: chapterUrl, + }); + } }); } @@ -120,8 +119,8 @@ class Shu69 implements Plugin.PluginBase { return novel; } - async parseChapter(chapterUrl: string): Promise { - const body = await fetchText(chapterUrl); + async parseChapter(chapterPath: string): Promise { + const body = await fetchText(this.site + chapterPath); const loadedCheerio = parseHTML(body); @@ -162,14 +161,13 @@ class Shu69 implements Plugin.PluginBase { const novelName = loadedCheerio(el).find('h4.name').text().trim(); const novelCover = loadedCheerio(el).find('a.cover > img').attr('src'); - const novelUrl = this.site + url; if (!url) return; const novel = { name: novelName, cover: novelCover, - url: novelUrl, + path: url, }; novels.push(novel); diff --git a/plugins/chinese/69xinshu.broken.ts b/plugins/chinese/69xinshu.ts similarity index 79% rename from plugins/chinese/69xinshu.broken.ts rename to plugins/chinese/69xinshu.ts index 133a8a8..078fc13 100644 --- a/plugins/chinese/69xinshu.broken.ts +++ b/plugins/chinese/69xinshu.ts @@ -9,7 +9,7 @@ class XinShu69 implements Plugin.PluginBase { id = "69xinshu"; name = "69书吧"; icon = "src/cn/69xinshu/icon.png"; - site = "https://www.69xinshu.com"; + site = "https://www.69shu.pro"; version = "0.1.1"; async popularNovels( @@ -31,19 +31,19 @@ class XinShu69 implements Plugin.PluginBase { const novels: Plugin.NovelItem[] = []; const novelsList = pageNo === 1 ? loadedCheerio('div.newbox > ul > li') : loadedCheerio('li'); - novelsList.each(function () { - const novelUrl = loadedCheerio(this).find('li > div > h3 > a:nth-child(2)').attr('href'); + novelsList.each((i, e) => { + const novelUrl = loadedCheerio(e).find('li > div > h3 > a:nth-child(2)').attr('href'); if (novelUrl) { - const novelName = loadedCheerio(this).find('li > div > h3 > a:nth-child(2)').text(); - const novelCover = loadedCheerio(this) + const novelName = loadedCheerio(e).find('li > div > h3 > a:nth-child(2)').text(); + const novelCover = loadedCheerio(e) .find('li > a > img') .attr('data-src'); const novel = { name: novelName, cover: novelCover, - url: novelUrl, + path: novelUrl.replace(this.site, ''), }; novels.push(novel); @@ -53,8 +53,8 @@ class XinShu69 implements Plugin.PluginBase { return novels; } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; + async parseNovel(novelPath: string): Promise { + const url = this.site + novelPath; const body = await fetchText(url, {}, 'gbk'); if (body === '') throw Error('无法获取小说内容,请检查网络'); @@ -62,12 +62,11 @@ class XinShu69 implements Plugin.PluginBase { let loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { - url, + path: novelPath, chapters: [], + name: loadedCheerio('h1 > a').text() }; - novel.name = loadedCheerio('h1 > a').text(); - novel.cover = loadedCheerio('div.bookimg2 > img').attr( 'src', ); @@ -99,15 +98,15 @@ class XinShu69 implements Plugin.PluginBase { const chaptersLoadedCheerio = parseHTML(chaptersBody); - chaptersLoadedCheerio('li').each(function () { - const chapterUrl = chaptersLoadedCheerio(this).find('a').attr('href'); + chaptersLoadedCheerio('li').each((i, e) => { + const chapterUrl = chaptersLoadedCheerio(e).find('a').attr('href'); if (chapterUrl?.startsWith("https://")) { - const chapterName = chaptersLoadedCheerio(this).find('a').text().trim(); + const chapterName = chaptersLoadedCheerio(e).find('a').text().trim(); chapters.push({ name: chapterName, - url: chapterUrl, + path: chapterUrl.replace(this.site, ''), }); } }); @@ -118,8 +117,8 @@ class XinShu69 implements Plugin.PluginBase { return novel; } - async parseChapter(chapterUrl: string): Promise { - const body = await fetchText(chapterUrl, {}, 'gbk'); + async parseChapter(chapterPath: string): Promise { + const body = await fetchText(this.site + chapterPath, {}, 'gbk'); const loadedCheerio = parseHTML(body); @@ -154,14 +153,14 @@ class XinShu69 implements Plugin.PluginBase { const novels: Plugin.NovelItem[] = []; - loadedCheerio('div.newbox > ul > li').each(function () { - const novelUrl = loadedCheerio(this).find('a').attr('href')!; - const novelName = loadedCheerio(this).find('div.newnav > h3').text(); - const novelCover = loadedCheerio(this).find('img').attr('data-src'); + loadedCheerio('div.newbox > ul > li').each((i, e) => { + const novelUrl = loadedCheerio(e).find('a').attr('href')!; + const novelName = loadedCheerio(e).find('div.newnav > h3').text(); + const novelCover = loadedCheerio(e).find('img').attr('data-src'); const novel = { name: novelName, - url: novelUrl, + path: novelUrl.replace(this.site, ''), cover: novelCover, }; diff --git a/plugins/chinese/linovelib.broken.ts b/plugins/chinese/linovelib.ts similarity index 96% rename from plugins/chinese/linovelib.broken.ts rename to plugins/chinese/linovelib.ts index 50f21e2..ac38281 100644 --- a/plugins/chinese/linovelib.broken.ts +++ b/plugins/chinese/linovelib.ts @@ -35,14 +35,12 @@ class Linovelib implements Plugin.PluginBase { const novelCover = loadedCheerio(el) .find("div.book-cover > img") .attr("data-src"); - const novelUrl = this.site + url; - if (!url) return; const novel = { name: novelName, cover: novelCover, - url: novelUrl, + path: url, }; novels.push(novel); @@ -51,8 +49,8 @@ class Linovelib implements Plugin.PluginBase { return novels; } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; + async parseNovel(novelPath: string): Promise { + const url = this.site + novelPath; const body = await fetchText(url); if (body === '') throw Error('无法获取小说内容,请检查网络'); @@ -60,12 +58,11 @@ class Linovelib implements Plugin.PluginBase { let loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { - url, + path: novelPath, chapters: [], + name: loadedCheerio("#bookDetailWrapper .book-title").text() }; - novel.name = loadedCheerio("#bookDetailWrapper .book-title").text(); - novel.cover = loadedCheerio("#bookDetailWrapper img.book-cover").attr( "src" ); @@ -118,7 +115,7 @@ class Linovelib implements Plugin.PluginBase { } } - const chapterUrl = `${this.site}/novel/${novelId}/${chapterId}.html`; + const chapterUrl = `/novel/${novelId}/${chapterId}.html`; const chapterName = volumeName + " — " + @@ -130,7 +127,7 @@ class Linovelib implements Plugin.PluginBase { chapter.push({ name: chapterName, releaseTime: releaseDate, - url: chapterUrl, + path: chapterUrl, }); }); @@ -139,7 +136,7 @@ class Linovelib implements Plugin.PluginBase { return novel; } - async parseChapter(chapterUrl: string): Promise { + async parseChapter(chapterPath: string): Promise { let chapterName, chapterText = "", hasNextPage, @@ -423,13 +420,13 @@ class Linovelib implements Plugin.PluginBase { return { pageCheerio, pageHasNextPage }; }; - let url = chapterUrl; + let url = this.site + chapterPath; do { const page = await loadPage(url); hasNextPage = page.pageHasNextPage; if (hasNextPage === true) { pageNumber++; - url = chapterUrl.replace( + url = url.replace( /\.html/gi, `_${pageNumber}` + ".html" ); @@ -516,13 +513,12 @@ class Linovelib implements Plugin.PluginBase { const novelCover = pageCheerio(el) .find("div.book-cover > img") .attr("data-src"); - const novelUrl = this.site + nUrl; if (!nUrl) return; novels.push({ name: novelName, - url: novelUrl, + path: nUrl, cover: novelCover, }); }); diff --git a/plugins/japanese/Syosetu.broken.ts b/plugins/japanese/Syosetu.ts similarity index 87% rename from plugins/japanese/Syosetu.broken.ts rename to plugins/japanese/Syosetu.ts index 5bf1abc..87b85c7 100644 --- a/plugins/japanese/Syosetu.broken.ts +++ b/plugins/japanese/Syosetu.ts @@ -13,6 +13,7 @@ class Syosetu implements Plugin.PluginBase { name = "Syosetu"; icon = "src/jp/syosetu/icon.png"; site = "https://yomou.syosetu.com/"; + novelPrefix = "https://ncode.syosetu.com"; version = "1.1.0"; headers = { "User-Agent": @@ -67,7 +68,7 @@ class Syosetu implements Plugin.PluginBase { if (!url) return; const name = anchor.text(); const novel: NovelItem = { - url, + path: url.replace(this.novelPrefix, ''), name, cover: defaultCover, }; @@ -78,15 +79,15 @@ class Syosetu implements Plugin.PluginBase { const novels = await getNovelsFromPage(pageNo); return novels; } - async parseNovelAndChapters(novelUrl: string): Promise { + async parseNovel(novelPath: string): Promise { let chapters: Plugin.ChapterItem[] = []; - const result = await fetch(novelUrl, { headers: this.headers }); + const result = await fetch(this.novelPrefix + novelPath, { headers: this.headers }); const body = await result.text(); const loadedCheerio = loadCheerio(body, { decodeEntities: false }); // create novel object let novel: Plugin.SourceNovel = { - url: novelUrl, + path: novelPath, name: loadedCheerio(".novel_title").text(), author: loadedCheerio(".novel_writername") .text() @@ -111,13 +112,15 @@ class Syosetu implements Plugin.PluginBase { .text() // get text .replace(/(.)/g, "") // remove "(edited)" mark .trim(), // trim spaces - "https://ncode.syosetu.com" + chapterA.attr("href"), + chapterA.attr("href"), ]; - chapters.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); + if(chapterUrl) { + chapters.push({ + name: chapterName, + releaseTime: releaseDate, + path: chapterUrl, + }); + } }); } else { /** @@ -148,14 +151,14 @@ class Syosetu implements Plugin.PluginBase { releaseTime: loadedCheerio("head") .find("meta[name='WWWC']") .attr("content"), // get date from metadata - url: novelUrl, // set chapterUrl to oneshot so that chapterScraper knows it's a one-shot, + path: novelPath, // set chapterUrl to oneshot so that chapterScraper knows it's a one-shot, }); } novel.chapters = chapters; return novel; } - async parseChapter(chapterUrl: string): Promise { - const result = await fetch(chapterUrl, { headers: this.headers }); + async parseChapter(chapterPath: string): Promise { + const result = await fetch(this.novelPrefix + chapterPath, { headers: this.headers }); const body = await result.text(); // create cheerioQuery @@ -178,7 +181,6 @@ class Syosetu implements Plugin.PluginBase { const getNovelsFromPage = async (pagenumber: number) => { // load page const url = this.searchUrl(pagenumber) + `&word=${searchTerm}`; - console.log(url); const result = await fetch(url, { headers: this.headers }); const body = await result.text(); // Cheerio it! @@ -186,17 +188,20 @@ class Syosetu implements Plugin.PluginBase { let pageNovels: Plugin.NovelItem[] = []; // find class=searchkekka_box - cheerioQuery(".searchkekka_box").each(function (i, e) { + cheerioQuery(".searchkekka_box").each((i, e) => { // get div with link and name - const novelDIV = cheerioQuery(this).find(".novel_h"); + const novelDIV = cheerioQuery(e).find(".novel_h"); // get link element const novelA = novelDIV.children()[0]; // add new novel to array - pageNovels.push({ - name: novelDIV.text(), // get the name - url: novelA.attribs.href, // get last part of the link - cover: defaultCover, - }); + const novelPath = novelA.attribs.href.replace(this.novelPrefix, ''); + if(novelPath){ + pageNovels.push({ + name: novelDIV.text(), // get the name + path: novelPath, // get last part of the link + cover: defaultCover, + }); + } }); // return all novels from this page return pageNovels; @@ -223,6 +228,9 @@ class Syosetu implements Plugin.PluginBase { async fetchImage(url: string): Promise { return fetchFile(url); } + resolveUrl(path: string, isNovel?: boolean | undefined): string { + return this.novelPrefix + path; + } filters = { ranking: { type: FilterTypes.Picker,