From 48f2c2c72c24dcb78d6fb93f68b74dea64d6f395 Mon Sep 17 00:00:00 2001 From: Dredd1987 <102831270+Dredd1987@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:39:06 +0300 Subject: [PATCH] Added New Arabic Source: rewayat.club (#1128) * Add files via upload * 2nd * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * another change with image * clean up * clean up * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories * fix handling single chapter stories and minor cleanup * fix handling single chapter stories and minor cleanup * New source test * New source test * fix bugs * fix bugs * fix bugs * bugs fix * New arabic source rewayat.club * New arabic source rewayat.club * New arabic source rewayat.club * New arabic source rewayat.club * chapter release dates improvment for ao3 * edits * add chapter dates to ao3 * add chapter dates to ao3 * improve RewayatClub --- public/static/src/ar/rewayatclub/icon.png | Bin 0 -> 4691 bytes src/plugins/arabic/rewayatclub.ts | 250 ++++++++++++++++++++++ src/plugins/english/ao3.ts | 29 ++- 3 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 public/static/src/ar/rewayatclub/icon.png create mode 100644 src/plugins/arabic/rewayatclub.ts diff --git a/public/static/src/ar/rewayatclub/icon.png b/public/static/src/ar/rewayatclub/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cb76ac425845ffe9658535b8cf616376dbd39c07 GIT binary patch literal 4691 zcma)AS6CD4)(#y5(xfRw6FNu&p%ZB#NC)W~P?11FQ3w%2S0o@^q)1V^6lo%Y6zRPq zML~*4ks?)4qy$h-(EYg2{-1O4U(9^7)_T{wX0^GP2qQzS)4+2;003}WM_a>$d|IDe z^pxa#b?Y;pXXX zkNw4P;`BEO${mX)U@5+oRY-T_7l#+3PN~7Tz zsEne56jVW05et=qV=!n%tQ|(t&i)V4Z{!g3mp6)Kc zMgU_kfpx*UVsV6%Cc*!162=}ua3;85|7j7@h4Akdk!KI#g2p*23;RGZSO>JH3qe@L z9QVr&1IA!IoE>psC>Z`f68oD=N#ei4{ExJLtH?u49%qR^Lr)g|tPK`N-W7N9QdE6v z!H}2kj*bS>%*ST&+9|x*BIw>{8^!K)7W&xx)RGY1pe#!<5v|xziwcV<+ppOs357|8 zhRP1S`J>M1)=<-svWMm`ZOye=7L0)WHX1Kk*tEg4^a2pBQxkU&2YxnwOb*7#V`8f7 z8JnzqkuiGN?=Lmd1~A5$8B^5ITre# zlDW35ZucM1>^eGHZlwK;Be6OlvgqecA9FksXEd0@E*4I{~_}|a~p+EwANawp z7i6sUM@qh%>lpi-5GmxjD>B{60t3|NYe^f$rjG(|*+EY=UEHzruKZTaHb&CJKTppp zyj*iMLa^9S!6LXX(>;GyVN-K2b`tw$0sg~%DN znZ&12d)bNQq>Ki0Ylm@#@r*d`)+8Fzl)W7JR8~)T^OpFyuF@o+RR8VSr-d~+<36c6 zZ#wV@z<%7AUdN=Dos=!4Q#a2rYM%RL;J2pBub1x9DV$B!E-#H2n@CHd)Cgx1X?rlYzF@|=vO=^DX-cAkno$@M zcFf#WYbp$#2{)fLP_dsvfREbEyiCL{7LR<;(!V-&MnmJ0(`uP0=S4kp8Bw}H(Ky0i z#MZ5{7PU?RNz<@$1(6pfwUO2yt#}0WlUje?FupvUAvtqxhtr}6#(e$>s>@gkaMT)}mMY*X5KX@qt@eN+>fby3~ugLirQsRFbhw+L^3v zn2uqX&Id~&BKvlm8iGAKC1pVKs#C(Yc7tCTQPrS8ywiHypv`ib9w;a8UfM?rM4e^T zONA=SMly9Xb;V`&&vv)GTQJnhUr^9s`t~WNBxl~>uUhVBAej!hj2QrgX@A?p^uq@J z8h>$eu!6HuI%n?K{LnOX-hn9i}h27wq>Iw+E+ON?a_AKb~7v zRlA#fEY!AQHWZLn3%R-oc*}3FQU4*1qbtjQKfIP{qnk!hJ|m=jwk@oxZufm`G+Yy4 zmxF!?sVif1d61rC1pg`y)lSJW+FCQTLWxdPd*mzw*c1IcAC`#FX`+4#NY9@YN_dv7 z13d^_fK0Cd8HP?-pTR%}G_Po*9UbuRLg6}I=V~LqGmm85jAw6EuPYe!cFG(T;faah zN;|g+%A})xOA)gDgPQPBGCE*-jLdpUd z^yxxp)&USFU2aIcOJtY?&@cp3ubdn(Hj?CumJQ+`&5gS#J#@m*_ zu_O_hN4EJ~F>^`L)y?Rf2_g^p2~RX)I`_pmB+XTL5B(soE$VAe|B(v}8cB3WGSk6w zwLWsSjh}mbA=Qp#1BfL$w>(K=y1;g!8Rf!*q;bmg7 zPebp2Cz8YJ%};6F;O*7r(7*QcaTQ047FSyV36weOg&o(!KGOa4Xwr*c8cWQ+v0wDY zTY;sA>)x``xo6AVBD_jFt@`nkHt;Kuws+mql!?&QuR?j*R>dwRlNv=RlQ{251uS96jZ(3c8DN&fEaO=6N%1cJuBXlAl7S1mcv&QCo-;uAkYVP8i2>OQDnX4GBM*JY> zSea&g*=05sp_Ke-sQUtu;YS*?no_}S&*FnIw~oPE3wmsd5fN)hb+;^8`ry5Fg{94H z7wxOf^e?>oM&7a|$LMtB4fQ9EZEJfr7MmL5cBH|W3UP1lb~KvX|lTNiy`DV`SUsP zvO54(d&8UfwSs<9n6`IY}!IdcMS6QTNRL=Q~?hn4T z^~RzlQOogS-#K^2uQIsMZ~ed5=u_`FxAs$`g?;`ffI zRw)wps@yEBphfR6m5)D`RnG;Z9(e&K1El#{l>S0>-NQTlpywq(_v?W1k5fU@1WRrp z^88(t>?(gHF4S2knolnNv}$Q)9V53^NSE5~NNm1)G|woOkd;cBnCgZ+$Z~oqsFBnS zeuFH%Sm8+eM2QMoo4Kv4Ss#|pQ&RGgkR|;3o1ia7XGcGkc3!5|O@ie(Zg&4c;cROi z+G!E?^bIH?y(ZaNt33TY=t@Taq$xi?G$Jq$)86bonH8f_)68vi(EH;P*OzeD<;`bQ z@LNmtOy;08UASFjiw6aTR;SdOTXoD<8_gY($6?4$n9egryqx-XuaAUS6IVt$)a6=D zIi0oq(-fU|uJdh|@?A<87UoLU*jQ=!Vz7vzTWzM%P!8H+2SRE`mN^!sb(M3yg;Us3 z(#92xoJOgqqe4?xPv4pR^4?;TGqM>Bp= zj5GxfD<5{=H=~vHCwG6K%DFExUE~~9B`aX&tv5!CAh@4nl~ibEG2$4rtF_M$ zk=*q7&U!>w=!0i&ZOdOVQMDE0=I=P`b+dCHr*=T(GB@dEJx1?#EmOM(x^vR<7Vxam z#p3-CbzT`D)6b6>1Qhi2u6#uHMJd}&Lo#|wpocLP(M!6d2qtxa*R>jn%w&~y^z*=B+*M;uJc9xY5>c`ODxt~fO z5ba~7%(hcqsiTYMVd@DKGZ_iJYK-ppE-gd|=n|`3&WBNmwZ}$r+yPOGOU&NLz(!T= zFMYblHG=JcJ+2K9lUc}rX?r}C_l1pjgLbT<%X{c!war~dRkh}7Rq3{a^*3^v{#;ps zc{@Ot`|FPw=!QDNx$8Z>L%g~KrD`%bg4K`~El)wyPC?(_4}_TM2M7IytAzG^R{SV> zoz`h)&TlbpV`1sCPxsc4?P7No>&P{yGxyZfWZxY=FF(zQ8NpyO@{BHOby%%{H6u6G zN9qNZRJ|2|uM{)QsbKq!NZ+k9koqgpY$Rp&DY?ktRfuwfa%x|MqhaML^J_Y4JAwTa z{qv}7qgHI^F!HE^OVeXdAz+^G(l@RfW-L64W~JgYn%~t`IfSwlH`iKD9YsbY%@|Z4 zIVZUupRH+o#m6NQ?iDyJXB<=d?yZ$?a|_LlmdbG5@MjYJ6>MEAS`xPW%x+w;&_T`G zK-xL2VW|4Jsl3(shH1*SnE(?eobi(>@fLiHJS)@Ca=kcAc5;t#j~dp@Wy|jUV6JLv z<*{8&!tSPtm8mGBlem3`4-XKq~37Y> z3HSJet%f{^g;w|HJ`BC-eAdb|D^LjB3Y}Ty^<#qs_yU?$ zuU9FC6!+5IEM3}tH#Fy8oR*kpaiwzlQCG)SJ9__2tfb}_;i5IBh|T0ji{Z#3_Ul`9 z9xOMd5E(YoZxDj%M}YzfG=?L6490W|O6ROX#;?H|d@Xr%GqNR)-cqQ!?2pZreeY+) z`62wTvqgPtA25!FotAz3<>sQ^V9dGG4)d>h*>~=B-eABatC~MJ`Lm;Q*-+z!x^2*Z E0688~ { + novels.push({ + name: item.arabic, + path: `novel/${item.slug}`, + cover: `https://api.rewayat.club/${item.poster_url.slice(1)}`, + }); + }); + return novels; + } + + async popularNovels( + page: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + let link = `https://api.rewayat.club/api/novels/`; + + if (filters) { + if (filters.categories.value !== '') { + link += `?type=${filters.categories.value}`; + } + if (filters.sortOptions.value !== '') { + link += `&ordering=${filters.sortOptions.value}`; + } + if (filters.genre.value.length > 0) { + filters.genre.value.forEach((genre: string) => { + link += `&genre=${genre}`; + }); + } + } + link += `&page=${page}`; + const body = await fetchApi(link).then(r => r.json()); + const loadedCheerio = parseHTML(body); + return this.parseNovels(body); + } + + async parseNovel( + novelUrl: string, + ): Promise { + const result = await fetchApi(new URL(novelUrl, this.site).toString()); + const body = await result.text(); + const loadedCheerio = parseHTML(body); + const novel: Plugin.SourceNovel & { totalPages: number } = { + path: novelUrl, + name: loadedCheerio('h1.primary--text span').text().trim() || 'Untitled', + author: loadedCheerio('.novel-author').text().trim(), + summary: loadedCheerio('div.text-pre-line span').text().trim(), + totalPages: 1, + chapters: [], + }; + const statusWords = new Set(['مكتملة', 'متوقفة', 'مستمرة']); + const mainGenres = Array.from(loadedCheerio('.v-slide-group__content a')) + .map(el => loadedCheerio(el).text().trim()) + .join(','); + const statusGenre = Array.from( + loadedCheerio('div.v-slide-group__content span.v-chip__content'), + ) + .map(el => loadedCheerio(el).text().trim()) + .filter(text => statusWords.has(text)); + novel.genres = `${statusGenre},${mainGenres}`; + const statusText = Array.from( + loadedCheerio('div.v-slide-group__content span.v-chip__content'), + ) + .map(el => loadedCheerio(el).text().trim()) + .filter(text => statusWords.has(text)) + .join(); + novel.status = + { + 'متوقفة': 'On Hiatus', + 'مكتملة': 'Completed', + 'مستمرة': 'Ongoing', + }[statusText] || 'Unknown'; + const imageRaw = loadedCheerio('body script:contains("__NUXT__")') + .first() + .text(); + const imageUrlRegex = /poster_url:"(\\u002F[^"]+)"/; + const imageUrlMatch = imageRaw?.match(imageUrlRegex); + const ImageUrlShort = imageUrlMatch + ? imageUrlMatch[1].replace(/\\u002F/g, '/').replace(/^\/*/, '') + : defaultCover; + const imageUrl = `https://api.rewayat.club/${ImageUrlShort}`; + novel.cover = imageUrl; + const chapterNumberStr = loadedCheerio('div.v-tab--active span.mr-1') + .text() + .replace(/[^\d]/g, ''); + const chapterNumber = parseInt(chapterNumberStr, 10); + const pageNumber = Math.ceil(chapterNumber / 24); + novel.totalPages = pageNumber; + + return novel; + } + parseChapters(data: ChapterData, novelPath: string) { + const chapter: Plugin.ChapterItem[] = []; + data.results.map((item: ChapterEntry) => { + chapter.push({ + name: item.title, + releaseTime: new Date(item.date).toISOString(), + path: `${novelPath}/${item.number}`, + chapterNumber: item.number, + }); + }); + return chapter; + } + async parsePage(novelPath: string, page: string): Promise { + const pagePath = novelPath.slice(6); + const pageUrl = `https://api.rewayat.club/api/chapters/${pagePath}/?ordering=number&page=${page}`; + const dataJson = await fetchApi(pageUrl).then(r => r.json()); + const chapters = this.parseChapters(dataJson, novelPath); + return { + chapters, + }; + } + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(new URL(chapterUrl, this.site).toString()); + const body = await result.text(); + const loadedCheerio = parseHTML(body); + let chapterText = ''; + loadedCheerio('div.v-card--flat').each((idx, ele) => { + loadedCheerio(ele) + .find('div.v-card__text') + .each((idx, textEle) => { + chapterText += + loadedCheerio(textEle) + .find('p') + .map((_, pEle) => loadedCheerio(pEle).text().trim()) + .get() + .join(' ') + ' '; + }); + }); + chapterText = chapterText.trim(); + return chapterText; + } + + async searchNovels( + searchTerm: string, + page: number, + ): Promise { + const searchUrl = `https://api.rewayat.club/api/novels/?type=0&ordering=-num_chapters&page=${page}&search=${searchTerm}`; + + const result = await fetchApi(searchUrl).then(r => r.json()); + // const body = await result.text(); + // const loadedCheerio = parseHTML(body); + return this.parseNovels(result); + } + + filters = { + genre: { + value: [], + label: 'Genres', + options: [ + { label: 'كوميديا', value: '1' }, // Comedy + { label: 'أكشن', value: '2' }, // Action + { label: 'دراما', value: '3' }, // Drama + { label: 'فانتازيا', value: '4' }, // Fantasy + { label: 'مهارات القتال', value: '5' }, // Combat Skills + { label: 'مغامرة', value: '6' }, // Adventure + { label: 'رومانسي', value: '7' }, // Romance + { label: 'خيال علمي', value: '8' }, // Science Fiction + { label: 'الحياة المدرسية', value: '9' }, // School Life + { label: 'قوى خارقة', value: '10' }, // Super Powers + { label: 'سحر', value: '11' }, // Magic + { label: 'رياضة', value: '12' }, // Sports + { label: 'رعب', value: '13' }, // Horror + { label: 'حريم', value: '14' }, // Harem + ], + type: FilterTypes.CheckboxGroup, + }, + categories: { + value: '0', + label: 'الفئات', + options: [ + { label: 'جميع الروايات', value: '0' }, + { label: 'مترجمة', value: '1' }, + { label: 'مؤلفة', value: '2' }, + { label: 'مكتملة', value: '3' }, + ], + type: FilterTypes.Picker, + }, + sortOptions: { + value: '-num_chapters', + label: 'الترتيب', + options: [ + { label: 'عدد الفصول - من أقل ﻷعلى', value: 'num_chapters' }, + { label: 'عدد الفصول - من أعلى ﻷقل', value: '-num_chapters' }, + { label: 'الاسم - من أقل ﻷعلى', value: 'english' }, + { label: 'الاسم - من أعلى ﻷقل', value: '-english' }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new RewayatClub(); + +interface NovelEntry { + arabic: string; + english: string; + about: string; + poster_url: string; + slug: string; + original: boolean; + complete: boolean; + num_chapters: number; + genre: { + id: number; + arabic: string; + english: string; + }; +} +interface NovelData { + count: number; + next: string; + previous: string; + results: NovelEntry[]; +} +interface ChapterEntry { + number: number; + title: string; + date: string; + uploader: { + username: string; + id: number; + }; + hitcounts: { + hits: number; + id: number; + }; + read: any[]; +} + +interface ChapterData { + count: number; + next: string; + previous: string; + results: ChapterEntry[]; +} diff --git a/src/plugins/english/ao3.ts b/src/plugins/english/ao3.ts index a59f50349..7353e10e1 100644 --- a/src/plugins/english/ao3.ts +++ b/src/plugins/english/ao3.ts @@ -7,7 +7,7 @@ import { defaultCover } from '@libs/defaultCover'; class ArchiveOfOurOwn implements Plugin.PluginBase { id = 'archiveofourown'; name = 'Archive Of Our Own'; - version = '1.0.1'; + version = '1.0.2'; icon = 'src/en/ao3/icon.png'; site = 'https://archiveofourown.org/'; @@ -100,7 +100,11 @@ class ArchiveOfOurOwn implements Plugin.PluginBase { async parseNovel(novelUrl: string): Promise { const result = await fetchApi(new URL(novelUrl, this.site).toString()); + const urlchapter = novelUrl + '/navigate'; + const chapters = await fetchApi(new URL(urlchapter, this.site).toString()); const body = await result.text(); + const chapterlisttext = await chapters.text(); + const chapterlistload = parseHTML(chapterlisttext); const loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { @@ -144,12 +148,31 @@ class ArchiveOfOurOwn implements Plugin.PluginBase { .join(','); novel.summary = `Fandom:\n${fandom}\n\nRating:\n${rating}\n\nWarning:\n${warning}\n\nSummary:\n${summary}\n\nSeries:\n${series}\n\nRelationships:\n${relation}\n\nCharacters:\n${character}\n\nStats:\n${stats}`; const chapterItems: Plugin.ChapterItem[] = []; + const longReleaseDate: string[] = []; + let match: RegExpExecArray | null; + chapterlistload('ol.index').each((i, ele) => { + chapterlistload(ele) + .find('li') + .each((i, el) => { + const chapterNameMatch = chapterlistload(el).find('a').text().trim(); + const releaseTimeText = chapterlistload(el) + .find('span.datetime') + .text() + .replace(/\(([^)]+)\)/g, '$1') + .trim(); + const releaseTime = releaseTimeText + ? new Date(releaseTimeText).toISOString() + : ''; + longReleaseDate.push(releaseTime); + }); + }); const releaseTimeText = loadedCheerio('.wrapper dd.published') .text() .trim(); const releaseTime = releaseTimeText ? new Date(releaseTimeText).toISOString() : ''; + let dateCounter: number = 0; if (loadedCheerio('#chapter_index select').length > 0) { loadedCheerio('#chapter_index select').each((i, selectEl) => { loadedCheerio(selectEl) @@ -158,11 +181,13 @@ class ArchiveOfOurOwn implements Plugin.PluginBase { const chapterName = loadedCheerio(el).text().trim(); const chapterUrlCode = loadedCheerio(el).attr('value')?.trim(); const chapterUrl = `${novelUrl}/chapters/${chapterUrlCode}`; - + const releaseDate: string = longReleaseDate[dateCounter]; + dateCounter++; if (chapterUrl) { chapterItems.push({ name: chapterName, path: chapterUrl, + releaseTime: releaseDate, }); } });