From 50af9d7b23c4ec003679b739f8b5766be5a208e4 Mon Sep 17 00:00:00 2001 From: Adam Kemish <49228220+Soopyboo32@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:24:57 +0800 Subject: [PATCH] Add fictionzone (#1464) --- public/static/src/en/fictionzone/icon.png | Bin 0 -> 2510 bytes src/plugins/english/fictionzone.ts | 199 ++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 public/static/src/en/fictionzone/icon.png create mode 100644 src/plugins/english/fictionzone.ts diff --git a/public/static/src/en/fictionzone/icon.png b/public/static/src/en/fictionzone/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..794f2f7a37627a308c3c848b6dcf36a705e4ad33 GIT binary patch literal 2510 zcmZ{mc{CL47snriF^HLLS<;MpDf`%J8j^i^vu{l#>llRbG8jviv8QCuDWD7e!p|?eeQYgeLmlN&i(62wlFi~;S%Kn0Pq+a=~*3S z)USX+56{4`u+3i=HzO-k0K%jIh>inb_plT_4?qwafUnK~U~>UD=AYkUsd3nVx?VEW z0|&pXsI4sXup&v)$C2D!NO-JU0RAumWwf%IJX%R!`K*nS3ii-dm1WUrEE>(babo7b z0AHfJmq+OT2coJUIUWLM{+&Sb^2G;|Tzvii2c!C*A(yV#Jv(GDzZhMjuP@$@1P*tA zawGZ$;{!-|cT|WM$rI&CBKZem6&3Me1TPN-H=?g%2;S9So(x55!N7=niPzFDNug+da2`5BQ;x^Y!B^#wWn1Mq{*6@T?OKIs~ za0p>yoc)aBF?j`=R#&<3{AR#`ec;zMliW?-RN=Ozc#mbsnxw zw};|39PcUeZ(%)sr`$W$eVbd}7vtg-kFjKVyoBSoG@kdSK$myuy5+*Nd(UGQKHq6? zS-1!(N)7+!RQB9iC6j)>@YaFPRhqiobfpJaj`kzw;&un-ls!%8Lk3UJX{cM!d=7fz zZCYuSfKNZG7>je!$ep&+QIr^~0}j^SaWX?xkj-3$=3;$TR*u$`E9{kll4R%Z$f+QF zp0bX*GI~`1Tx0jRscoO)wd~JP&{=bt*QPmmcz8Px%))G-sJ)hZKi*OJQp#G%ZSVta9uDE+0>^UXVx$wBSn#?bqiYZ3gf61QC% z@)8N^tj^05!TJs2{oTFaXvr!xez!E?*f(4P6K30_hT$CaW}f_XY0iXZqP8g749eF- z^l)>!l^tok&|6&hiOHl7#p=$vVKn!SC%iRGALGM4J5h8c5#F)!W2r)S&T7pCXh~VW z@|3GtljF8k(GAsED}l;NidXSQ&sJql3O37L+0k_AoIJ&Pfn2t!ppbaoy(w?Wy_)t7 zf{CJGxRo9ORDnpuvmfL{SdEBikQmOWC}xi$r7=R{?6;|XRo@UFhh$Vu1v`IG9RD2P z7;m*8Jcw)M)uMWyI&SDH*Tqbk*$-X)OMyIC8WX6oYvwWym)`h#Om?~br`??LY>?67 zr-~d6jj^!(nYGQFhc662%HJx$YG5^SVET{LV4>(XhtvH!`K*<2U4|cwF1NbZk+iQo zjKMyus9)&}*v{w>U0U}QF7fGkSkSiZsIoOfznp9O)0KHDpV&CS7qR}7HsWv}V?F>u z5Rm+f9bQ-;**P&Erzz{knwp~|5v`f*_yE(pVh)k7^_>@47$Dtzjuhb*a*#4qynT=m z8cP$M3mj##*Mnj2xzHyA*5N z^q8FSe?+~g)HZ)OyGYjbl)A}PqGy45k8H9JUT_tNc9POCS8E|lu(tBg(L+D-C z@$7;~M*l@=9t1rubQlh6JI=ow(_n!Sz

(0ldd_Y$L!e(g$@domd6l2j=Y z^}u9gD0wU2uuPLqo~yS%`!_{O$P1!k#D;?A%5|*hO4m^4IbrE!C?^oA6+&%$<3%s`+f2_CrPgi|Y>T0ct zr}@pEO-&#Kt>*~mXxtJAuSuSlB1>VQ`H2{gMqZy`)`bo#2VM3C+=E6_*A-s@f%oco zJKx&EQLysw9O+g}6vY^JGHSDF(`UN2LSXNb4!SN@2xV*qfULsG^CPlzvd4ym1YFOR?`>J0r-QzTNLr>xCD~V8iSP0V*wxOg?CUt>`26crC;+4(ukT zPVPoHGmr}}R)|5c5d>z?(Ynv&w2u=lUH;~Io+0=u+SP^l?a9Z{w!R@D~$y$3Re) zl9AJ7jF5$VhwuSarTSeuL~ZDpR<1-L_oto2jG9VA!WY!Fs0&&%ssjSVBNnls zZ(ZScyI-uu0p!_#E0meqd_j@urDB={sm=WT%ev9s7&VR5YC8k!$ZF(}uroH!RjcQ5 z?fyf7cjl6@ZCzG;D1 = new Map(); + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + return await this.getPage(this.site + '/library?page=' + pageNo); + } + + async getPage(url: string) { + const req = await fetchApi(url); + const body = await req.text(); + const loadedCheerio = loadCheerio(body); + + return loadedCheerio('div.novel-card') + .map((i, el) => { + const novelName = loadedCheerio(el).find('a > div.title > h1').text(); + const novelCover = loadedCheerio(el).find('img').attr('src'); + const novelUrl = loadedCheerio(el).find('a').attr('href'); + + return { + name: novelName, + cover: novelCover, + path: novelUrl!.replace(/^\//, '').replace(/\/$/, ''), + }; + }) + .toArray(); + } + + async parseNovel( + novelPath: string, + ): Promise { + const req = await fetchApi(this.site + '/' + novelPath); + const body = await req.text(); + const loadedCheerio = loadCheerio(body); + + const novel: Plugin.SourceNovel & { totalPages: number } = { + path: novelPath, + name: loadedCheerio('div.novel-title > h1').text(), + totalPages: 1, + }; + + // novel.artist = ''; + novel.author = loadedCheerio('div.novel-author > content').text(); + novel.cover = loadedCheerio('div.novel-img > img').attr('src'); + novel.genres = [ + ...loadedCheerio('div.genres > .items > span') + .map((i, el) => loadedCheerio(el).text()) + .toArray(), + ...loadedCheerio('div.tags > .items > a') + .map((i, el) => loadedCheerio(el).text()) + .toArray(), + ].join(','); + const status = loadedCheerio('div.novel-status > div.content') + .text() + .trim(); + if (status === 'Ongoing') novel.status = NovelStatus.Ongoing; + novel.summary = loadedCheerio('#synopsis > div.content').text(); + + let nuxtData = loadedCheerio('script#__NUXT_DATA__').html(); + let parsed = JSON.parse(nuxtData!); + let last = null; + for (let a of parsed) { + if (typeof a === 'string' && a.startsWith('novel_covers/')) break; + last = a; + } + this.cachedNovelIds.set(novelPath, last.toString()); + novel.chapters = loadedCheerio( + 'div.chapters > div.list-wrapper > div.items > a.chapter', + ) + .map((i, el) => { + const chapterName = loadedCheerio(el).find('span.chapter-title').text(); + const chapterUrl = loadedCheerio(el) + .attr('href')! + .replace(/^\//, '') + .replace(/\/$/, ''); + const uploadTime = this.parseAgoDate( + loadedCheerio(el).find('span.update-date').text(), + ); + + return { + name: chapterName, + releaseTime: uploadTime, + path: chapterUrl!.replace(/^\//, '').replace(/\/$/, ''), + }; + }) + .toArray(); + novel.totalPages = parseInt( + loadedCheerio('div.chapters ul.el-pager > li:last-child').text(), + ); + + return novel; + } + + async parsePage(novelPath: string, page: string): Promise { + let id = this.cachedNovelIds.get(novelPath); + if (!id) { + await this.parseNovel(novelPath); + id = this.cachedNovelIds.get(novelPath); + } + + const data = await fetchApi(this.site + '/api/__api_party/api-v1', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 'path': '/chapter/all/' + id, + 'query': { 'page': parseInt(page) }, + 'headers': { 'content-type': 'application/json' }, + 'method': 'get', + }), + }).then(r => r.json()); + return { + chapters: data._data.map((c: any) => ({ + name: c.title, + releaseTime: new Date(c.created_at).toISOString(), + path: novelPath + '/' + c.slug, + })), + }; + } + + async parseChapter(chapterPath: string): Promise { + const req = await fetchApi(this.site + '/' + chapterPath); + const body = await req.text(); + const loadedCheerio = loadCheerio(body); + return loadedCheerio('div.chapter-content').html() || ''; + } + + async searchNovels( + searchTerm: string, + pageNo: number, + ): Promise { + return await this.getPage( + this.site + + '/library?query=' + + encodeURIComponent(searchTerm) + + '&page=' + + pageNo + + '&sort=views-all', + ); + } + + parseAgoDate(date: string | undefined) { + //parseMadaraDate + if (date?.includes('ago')) { + const dayJSDate = dayjs(new Date()); // today + const timeAgo = date.match(/\d+/)?.[0] || ''; + const timeAgoInt = parseInt(timeAgo, 10); + + if (!timeAgo) return null; // there is no number! + + if (date.includes('hours ago') || date.includes('hour ago')) { + dayJSDate.subtract(timeAgoInt, 'hours'); // go back N hours + } + + if (date.includes('days ago') || date.includes('day ago')) { + dayJSDate.subtract(timeAgoInt, 'days'); // go back N days + } + + if (date.includes('months ago') || date.includes('month ago')) { + dayJSDate.subtract(timeAgoInt, 'months'); // go back N months + } + + if (date.includes('years ago') || date.includes('year ago')) { + dayJSDate.subtract(timeAgoInt, 'years'); // go back N years + } + + return dayJSDate.toISOString(); + } + return null; // there is no "ago" so give up + } + + resolveUrl = (path: string, isNovel?: boolean) => this.site + '/' + path; +} + +export default new FictionZonePlugin();