From 071cdbf6972c4f4d8f2b0c74627fef4768628978 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 22 Oct 2024 16:23:08 +0200 Subject: [PATCH 1/5] Port AllManga.to (AllAnimesite) --- .../engine/transformers/BookmarkConverter.ts | 3 +- .../transformers/BookmarkConverter_test.ts | 3 +- web/src/engine/websites/AllMangaTo.ts | 149 ++++++++++++++++++ web/src/engine/websites/AllMangaTo_e2e.ts | 26 +++ web/src/engine/websites/_index.ts | 1 + 5 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 web/src/engine/websites/AllMangaTo.ts create mode 100644 web/src/engine/websites/AllMangaTo_e2e.ts diff --git a/web/src/engine/transformers/BookmarkConverter.ts b/web/src/engine/transformers/BookmarkConverter.ts index 80debb6c28..ac1d146e14 100644 --- a/web/src/engine/transformers/BookmarkConverter.ts +++ b/web/src/engine/transformers/BookmarkConverter.ts @@ -7,9 +7,10 @@ import type { BookmarkSerialized } from '../providers/Bookmark'; * @remarks Only exported for testing */ export const legacyWebsiteIdentifierMap = new Map([ + [ 'allanimesite', 'allmangato' ], + [ 'apolltoons', 'mundomanhwa' ], [ 'aresnov', 'scarmanga' ], [ 'azoramanga', 'azoraworld' ], - [ 'apolltoons', 'mundomanhwa' ], [ 'bacamangaorg', 'bacamanga' ], [ 'bananascan', 'harmonyscan' ], [ 'blogtruyen', 'blogtruyenmoi' ], diff --git a/web/src/engine/transformers/BookmarkConverter_test.ts b/web/src/engine/transformers/BookmarkConverter_test.ts index ce8ce4159a..70fcd16bd1 100644 --- a/web/src/engine/transformers/BookmarkConverter_test.ts +++ b/web/src/engine/transformers/BookmarkConverter_test.ts @@ -9,8 +9,9 @@ import { Key } from '../SettingsGlobal'; import { GetLocale } from '../../i18n/Localization'; const legacyWebsiteIdentifierMapTestCases = [ - { sourceID: 'aresnov', targetID: 'scarmanga' }, + { sourceID: 'allanimesite', targetID: 'allmangato' }, { sourceID: 'apolltoons', targetID: 'mundomanhwa' }, + { sourceID: 'aresnov', targetID: 'scarmanga' }, { sourceID: 'azoramanga', targetID: 'azoraworld' }, { sourceID: 'bacamangaorg', targetID: 'bacamanga' }, { sourceID: 'bananascan', targetID: 'harmonyscan' }, diff --git a/web/src/engine/websites/AllMangaTo.ts b/web/src/engine/websites/AllMangaTo.ts new file mode 100644 index 0000000000..ae403d681a --- /dev/null +++ b/web/src/engine/websites/AllMangaTo.ts @@ -0,0 +1,149 @@ +import { Tags } from '../Tags'; +import icon from './HqNow.webp'; +import { Chapter, Page } from '../providers/MangaPlugin'; +import { DecoratableMangaScraper, Manga, type MangaPlugin } from '../providers/MangaPlugin'; +import * as Common from './decorators/Common'; +import { FetchCSS, FetchJSON } from '../platform/FetchProvider'; + +type GraphQLResult = { + data: T; +}; + +type APIMangas = { + mangas: { + edges: APIManga[], + + } +} + +type APIManga = { + _id: string, + englishName: string | null, + name: string, + availableChaptersDetail: Record +} + +type APIChapters = { + manga: APIManga +} + +type ChapterID = { + id: string, + translation: string +} + +type APIPages = { + chapterPages: { + edges: { + pictureUrlHead: string, + pictureUrls: { + url: string + }[] + }[] + } +} + +@Common.ImageAjax() +export default class extends DecoratableMangaScraper { + + private readonly apiUrl = 'https://api.allanime.day/api'; + + public constructor() { + super('allmangato', `AllManga.to`, 'https://allmanga.to', Tags.Media.Manga, Tags.Media.Manhua, Tags.Media.Manhwa, Tags.Language.Multilingual, Tags.Source.Aggregator); + } + + public override get Icon() { + return icon; + } + + public override ValidateMangaURL(url: string): boolean { + return new RegExpSafe(`^${this.URI.origin}/manga/[^/]+/[^/]+$`).test(url); + } + + public override async FetchManga(provider: MangaPlugin, url: string): Promise { + const title = (await FetchCSS(new Request(new URL(url)), 'ol.breadcrumb li:last-of-type')).shift().textContent.trim(); + return new Manga(this, provider, url.match(/\/manga\/([^/]+)\//)[1], title); + } + + public override async FetchMangas(provider: MangaPlugin): Promise { + const mangaList: Manga[] = []; + for (let page = 1, run = true; run; page++) { + await new Promise(resolve => setTimeout(resolve, 200)); + const mangas = await this.GetMangasFromPage(page, provider); + mangas.length > 0 ? mangaList.push(...mangas) : run = false; + } + return mangaList; + } + + private async GetMangasFromPage(page: number, provider: MangaPlugin): Promise { + const jsonVariables = { + search: { + isManga: true, + allowAdult: true, + allowUnknown: true + }, + limit: 26, //impossible to change + page: page, + translationType: 'sub', + countryOrigin: 'ALL' + }; + const jsonExtensions = { + persistedQuery: { + version: 1, + sha256Hash: 'a27e57ef5de5bae714db701fb7b5cf57e13d57938fc6256f7d5c70a975d11f3d' + } + }; + + const { mangas: { edges } } = await this.FetchGraphQL(jsonVariables, jsonExtensions); + return edges.map(manga => new Manga(this, provider, manga._id, manga.englishName ?? manga.name)); + } + + public override async FetchChapters(manga: Manga): Promise { + const jsonVariables = { + _id: manga.Identifier, + }; + const jsonExtensions = { + persistedQuery: { + version: 1, + sha256Hash: 'a42e1106694628f5e4eaecd8d7ce0c73895a22a3c905c29836e2c220cf26e55f' + } + }; + const { manga: { availableChaptersDetail } } = await this.FetchGraphQL(jsonVariables, jsonExtensions); + return Object.keys(availableChaptersDetail).reduce((accumulator: Chapter[], key) => { + const chapters = availableChaptersDetail[key].map(chapter => new Chapter(this, manga, JSON.stringify({ id: chapter, translation: key }), `Chapter ${chapter} [${key}]`)); + accumulator.push(...chapters); + return accumulator; + }, []); + } + + public override async FetchPages(chapter: Chapter): Promise { + const { id, translation }: ChapterID = JSON.parse(chapter.Identifier); + const jsonVariables = { + mangaId: chapter.Parent.Identifier, + translationType: translation, + chapterString: id, + limit: 10, + offset: 0 + }; + const jsonExtensions = { + persistedQuery: { + version: 1, + sha256Hash: '121996b57011b69386b65ca8fc9e202046fc20bf68b8c8128de0d0e92a681195' + } + }; + const { chapterPages: { edges } } = await this.FetchGraphQL(jsonVariables, jsonExtensions); + const source = edges.find(source => source.pictureUrlHead); + return source.pictureUrls.map(picture => new Page(this, chapter, new URL(picture.url, source.pictureUrlHead))); + } + + private async FetchGraphQL(variables: JSONObject, extensions: JSONObject): Promise { + const url = new URL(`?variables=${JSON.stringify(variables)}&extensions=${JSON.stringify(extensions)}`, this.apiUrl); + const { data } = await FetchJSON>(new Request(url, { + headers: { + Origin: this.URI.origin + } + })); + return data; + } + +} \ No newline at end of file diff --git a/web/src/engine/websites/AllMangaTo_e2e.ts b/web/src/engine/websites/AllMangaTo_e2e.ts new file mode 100644 index 0000000000..8d34a67062 --- /dev/null +++ b/web/src/engine/websites/AllMangaTo_e2e.ts @@ -0,0 +1,26 @@ +import { describe } from 'vitest'; +import { TestFixture } from '../../../test/WebsitesFixture'; + +const config = { + plugin: { + id: 'allmangato', + title: 'AllManga.to' + }, + container: { + url: 'https://allmanga.to/manga/kFvrdRcbubPjrhr63/yuan-zun', + id: 'kFvrdRcbubPjrhr63', + title: 'Yuan Zun' + }, + child: { + id: JSON.stringify({id: '643.5', translation: 'sub'}), + title: 'Chapter 643.5 [sub]' + }, + entry: { + index: 0, + size: 175_254, + type: 'image/webp' + } +}; + +const fixture = new TestFixture(config); +describe(fixture.Name, async () => (await fixture.Connect()).AssertWebsite()); \ No newline at end of file diff --git a/web/src/engine/websites/_index.ts b/web/src/engine/websites/_index.ts index 50eb00b932..732f0153d6 100755 --- a/web/src/engine/websites/_index.ts +++ b/web/src/engine/websites/_index.ts @@ -5,6 +5,7 @@ export { default as AGCScanlation } from './AGCScanlation'; export { default as AGS } from './AGS'; export { default as Ainzscans } from './Ainzscans'; export { default as Akuma } from './Akuma'; +export { default as AllMangaTo } from './AllMangaTo'; export { default as AllPornComic } from './AllPornComic'; export { default as Alphapolis } from './Alphapolis'; export { default as AmuyScan } from './AmuyScan'; From 988b91939ddf2b87dedbd9540192c26b87d9d98d Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Sun, 27 Oct 2024 12:26:32 +0100 Subject: [PATCH 2/5] update tests --- web/src/engine/websites/AllMangaTo_e2e.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/src/engine/websites/AllMangaTo_e2e.ts b/web/src/engine/websites/AllMangaTo_e2e.ts index 8d34a67062..93fd0dc2d0 100644 --- a/web/src/engine/websites/AllMangaTo_e2e.ts +++ b/web/src/engine/websites/AllMangaTo_e2e.ts @@ -1,4 +1,3 @@ -import { describe } from 'vitest'; import { TestFixture } from '../../../test/WebsitesFixture'; const config = { @@ -22,5 +21,4 @@ const config = { } }; -const fixture = new TestFixture(config); -describe(fixture.Name, async () => (await fixture.Connect()).AssertWebsite()); \ No newline at end of file +new TestFixture(config).AssertWebsite(); \ No newline at end of file From dd33da5697aa6d9fd2e6378ca7bc8556dd320854 Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 19 Nov 2024 12:01:44 +0100 Subject: [PATCH 3/5] update icon --- web/src/engine/websites/AllMangaTo.ts | 2 +- web/src/engine/websites/AllMangaTo.webp | Bin 0 -> 1700 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 web/src/engine/websites/AllMangaTo.webp diff --git a/web/src/engine/websites/AllMangaTo.ts b/web/src/engine/websites/AllMangaTo.ts index ae403d681a..66a2f86947 100644 --- a/web/src/engine/websites/AllMangaTo.ts +++ b/web/src/engine/websites/AllMangaTo.ts @@ -1,5 +1,5 @@ import { Tags } from '../Tags'; -import icon from './HqNow.webp'; +import icon from './AllMangaTo.webp'; import { Chapter, Page } from '../providers/MangaPlugin'; import { DecoratableMangaScraper, Manga, type MangaPlugin } from '../providers/MangaPlugin'; import * as Common from './decorators/Common'; diff --git a/web/src/engine/websites/AllMangaTo.webp b/web/src/engine/websites/AllMangaTo.webp new file mode 100644 index 0000000000000000000000000000000000000000..73d8c641bba180d586be1cebab2a168e90960817 GIT binary patch literal 1700 zcmV;V23z@3Nk>1^@t8MM6+kP&il$0000G0000#002J#06|PpNQeUf00E$F+qNOu znrp2%wr$(E#+z;1wr$(CZQI7NZS%(7dp=|aGU5j!VggWIG!io{wjgWcx*{m3D6%U? zFM1Sa#ZWYPvbcb0L zH4%(^X~jD`*MXRzh-TRS2Y(h|Q4}fA_~;*I-rgG`k)WVfM{w}suXcn0=zGz`3Ac2D zmhEV28hg}$V{aMVv(Rypcgn#I?t+c%qN-)5;clc z@(>dM&3lYG;F9}3n?jO-ba5QJKzc6u`RfO6Kl#8Nx*ICZG>Zxv>~`7vi`TPi2q7@F zXX5&Q-ah&0aRwmLRD!0bPi7!VAIHu!dvGp}V;5K%Pk4Bz_7F*-IA}40k6j3XH4ff6 z7Z|wo!7-2oL7VxEJ&PNB>;so}hE_A|!I(u4n|LOoq+3eBm{%qVzN0I{wF!cTGwI84 zXM$p=XDGw%35K2MS%#ZZ>|2ss5+aIX(+TqTxWgqIvEEOHvGs8HyFSt_L(pfM4i~QD#@Z6q5G5-7sFIgT4~x$L{M%O zlv)Hu6s<0KB&(`s0#;BsAnpPH0Pqa}odGI906+jfwOXxKrzE5!rWIM(;1+-Y)U|ei zpjdY+h1G!EZPKyYM|f7a0(rwW<--+!&P5A70cJ=|m=bel@q=$c|33qF=m*w3W2NJ( z%$1x;SU~mO@QD$iw)h_!tKR|ZndCb@97!|*$#krofF}NG0092j^;7kBPJPWX@NDq@ zRV3f>>n35uA1*zIeWW(NKNE||)b%hA#WV08jtW%^L8Q*qwncD#$pT4CiBO4NtxL6m zci*Y$Ea0E*BGTqF!erk4Tu4Ml{6+<3HO!n2Mj9z`JM?OwTxtED|5!YIA@f6Ez-M9w zdx^G8<+{p)JhRK+^CwtkA>IAj#(kLR?%R#S9wchZ>J4e3pYJa%kdn=2E^fcUKh3hq zA1C%cM>9GemEp^0IYhTvrGMdRz|jjkk}oO-|FnMI$yANwUL$1@t_f zC!OWE12^6B{?r_nnAy42)Omq|@Rk(uZ*!O1DF{9mOcSg$dZ>G>tyKbj&oX&`W`c=bf?*MSlT^@nBh=zKjBOwS@T&J)H{mq2pgx6) zOK`S&p>6&W4Fk%y6rOu*_F3ID?s{eH9*m5VZOngYa&WaQb_X9A4^7)N1Hx&-sq3ps u;UzUCUDKl)2U=jw#?54;Nd!EIc{c#ex))>nFhfvOp4xYF>c=kF00010WFpf5 literal 0 HcmV?d00001 From 0bf560bc0aa3687b999c1c7e02d0edbb5cee850e Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 19 Nov 2024 12:23:33 +0100 Subject: [PATCH 4/5] fix manga list & tests --- web/src/engine/websites/AllMangaTo.ts | 7 +++---- web/src/engine/websites/AllMangaTo_e2e.ts | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/web/src/engine/websites/AllMangaTo.ts b/web/src/engine/websites/AllMangaTo.ts index 66a2f86947..0d63fa9900 100644 --- a/web/src/engine/websites/AllMangaTo.ts +++ b/web/src/engine/websites/AllMangaTo.ts @@ -6,13 +6,12 @@ import * as Common from './decorators/Common'; import { FetchCSS, FetchJSON } from '../platform/FetchProvider'; type GraphQLResult = { - data: T; + data: T }; type APIMangas = { mangas: { edges: APIManga[], - } } @@ -94,8 +93,8 @@ export default class extends DecoratableMangaScraper { } }; - const { mangas: { edges } } = await this.FetchGraphQL(jsonVariables, jsonExtensions); - return edges.map(manga => new Manga(this, provider, manga._id, manga.englishName ?? manga.name)); + const data = await this.FetchGraphQL(jsonVariables, jsonExtensions); + return data?.mangas?.edges ? data.mangas.edges.map(manga => new Manga(this, provider, manga._id, manga.englishName ?? manga.name)) : []; } public override async FetchChapters(manga: Manga): Promise { diff --git a/web/src/engine/websites/AllMangaTo_e2e.ts b/web/src/engine/websites/AllMangaTo_e2e.ts index 93fd0dc2d0..3aa395b181 100644 --- a/web/src/engine/websites/AllMangaTo_e2e.ts +++ b/web/src/engine/websites/AllMangaTo_e2e.ts @@ -16,8 +16,8 @@ const config = { }, entry: { index: 0, - size: 175_254, - type: 'image/webp' + size: 714_974, + type: 'image/jpeg' } }; From c525eac08c82d4e772be5426a1ee083200f47f5b Mon Sep 17 00:00:00 2001 From: MikeZeDev Date: Tue, 26 Nov 2024 13:30:05 +0100 Subject: [PATCH 5/5] Update _index.ts --- web/src/engine/websites/_index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/engine/websites/_index.ts b/web/src/engine/websites/_index.ts index 07aba17b4b..2c2748b1c7 100755 --- a/web/src/engine/websites/_index.ts +++ b/web/src/engine/websites/_index.ts @@ -6,6 +6,7 @@ export { default as AGS } from './AGS'; export { default as Ainzscans } from './Ainzscans'; export { default as Akuma } from './Akuma'; export { default as AllHentai } from './AllHentai'; +export { default as AllMangaTo } from './AllMangaTo'; export { default as AllPornComic } from './AllPornComic'; export { default as Alphapolis } from './Alphapolis'; export { default as AmuyScan } from './AmuyScan';