From 4a9f16c5aeb7c1cf7f91850015e8254c76ac7711 Mon Sep 17 00:00:00 2001 From: RndDev123 Date: Thu, 28 Nov 2024 07:04:26 +0100 Subject: [PATCH] New Source: MangaWorld (IT) + Fixes New Source: - Manga World JKAnime, TioAnime, ,AnimeFenix and AnimeWorld: - Add await before this.parseAnimeList for readability --- javascript/anime/src/es/animefenix.js | 8 +- javascript/anime/src/es/jkanime.js | 6 +- javascript/anime/src/es/tioanime.js | 8 +- javascript/anime/src/it/animeworld.js | 12 +- javascript/manga/src/it/mangaworld.js | 280 ++++++++++++++++++++++++++ 5 files changed, 297 insertions(+), 17 deletions(-) create mode 100644 javascript/manga/src/it/mangaworld.js diff --git a/javascript/anime/src/es/animefenix.js b/javascript/anime/src/es/animefenix.js index 14fa4e9b..2d6dc699 100644 --- a/javascript/anime/src/es/animefenix.js +++ b/javascript/anime/src/es/animefenix.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://www3.animefenix.tv/themes/fenix-neo/images/AveFenix.png", "typeSource": "single", "isManga": false, - "version": "0.1.11", + "version": "0.1.12", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/es/animefenix.js" @@ -44,10 +44,10 @@ class DefaultExtension extends MProvider { }[status.toLowerCase()] ?? 5; } async getPopular(page) { - return this.parseAnimeList(`${this.source.baseUrl}/animes?order=visits&page=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/animes?order=visits&page=${page}`); } async getLatestUpdates(page) { - return this.parseAnimeList(`${this.source.baseUrl}/animes?order=updated&page=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/animes?order=updated&page=${page}`); } async search(query, page, filters) { query = query.trim().replaceAll(/\ +/g, "+"); @@ -72,7 +72,7 @@ class DefaultExtension extends MProvider { url += `&order=${filters[3].values[filters[3].state].value}`; url += `&page=${page}`; - return this.parseAnimeList(url); + return await this.parseAnimeList(url); } async getDetail(url) { const detail = {}; diff --git a/javascript/anime/src/es/jkanime.js b/javascript/anime/src/es/jkanime.js index a95d04f7..cc070395 100644 --- a/javascript/anime/src/es/jkanime.js +++ b/javascript/anime/src/es/jkanime.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://cdn.jkanime.net/logo_jk.png", "typeSource": "single", "isManga": false, - "version": "0.1.11", + "version": "0.1.12", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/es/jkanime.js" @@ -61,7 +61,7 @@ class DefaultExtension extends MProvider { return { "list": list, "hasNextPage": false }; } async getLatestUpdates(page) { - return this.parseAnimeList(`${this.source.baseUrl}/directorio/${page}/`); + return await this.parseAnimeList(`${this.source.baseUrl}/directorio/${page}/`); } async search(query, page, filters) { query = query.trim().replaceAll(/\ +/g, "_"); @@ -85,7 +85,7 @@ class DefaultExtension extends MProvider { url += `/${filters[7].values[filters[7].state].value}`; url += `/${filters[8].values[filters[8].state].value}`; } - return this.parseAnimeList(url); + return await this.parseAnimeList(url); } async getDetail(url) { let res = await this.client.get(url); diff --git a/javascript/anime/src/es/tioanime.js b/javascript/anime/src/es/tioanime.js index 514154e9..14beccb1 100644 --- a/javascript/anime/src/es/tioanime.js +++ b/javascript/anime/src/es/tioanime.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://tioanime.com/assets/img/tio_fb.jpg", "typeSource": "single", "isManga": false, - "version": "0.1.1", + "version": "0.1.11", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/es/tioanime.js" @@ -42,15 +42,15 @@ class DefaultExtension extends MProvider { }[status] ?? 5; } async getPopular(page) { - return this.parseAnimeList(`${this.source.baseUrl}/directorio?p=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/directorio?p=${page}`); } async getLatestUpdates(page) { - return this.parseAnimeList(`${this.source.baseUrl}/directorio?p=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/directorio?p=${page}`); } async search(query, page, filters) { query = query.trim().replaceAll(/\ +/g, "+"); let url = `${this.source.baseUrl}/directorio?q=${query}&p=${page}`; - return this.parseAnimeList(url); + return await this.parseAnimeList(url); } async getDetail(url) { const res = await this.client.get(this.source.baseUrl + url); diff --git a/javascript/anime/src/it/animeworld.js b/javascript/anime/src/it/animeworld.js index c4964a61..47fedf3c 100644 --- a/javascript/anime/src/it/animeworld.js +++ b/javascript/anime/src/it/animeworld.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://i.postimg.cc/RFRGfBvP/FVLyB1I.png", "typeSource": "single", "isManga": false, - "version": "0.0.1", + "version": "0.0.11", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/it/animeworld.js" @@ -23,7 +23,7 @@ class DefaultExtension extends MProvider { async parseAnimeList(url) { const res = await this.client.get(url); const doc = new Document(res.body); - const elements = doc.select("div.film-list div.item"); + const elements = doc.select("div#main div.film-list div.item"); const list = []; for (const element of elements) { @@ -62,14 +62,14 @@ class DefaultExtension extends MProvider { return { "list": list, "hasNextPage": false }; } async getLatestUpdates(page) { - return this.parseAnimeList(`${this.source.baseUrl}/filter?sort=1&page=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/filter?sort=1&page=${page}`); } async search(query, page, filters) { query = query.trim().replaceAll(/\ +/g, "+"); // Search sometimes failed because filters were empty. I experienced this mostly on android... if (!filters || filters.length == 0) { - return this.parseAnimeList(`${this.source.baseUrl}/search?keyword=${query}&page=${page}`); + return await this.parseAnimeList(`${this.source.baseUrl}/search?keyword=${query}&page=${page}`); } let url = `${this.source.baseUrl}/filter?sort=${filters[5].values[filters[5].state].value}&keyword=${query}`; @@ -94,7 +94,7 @@ class DefaultExtension extends MProvider { if (filter.state == true) url += `&language=${filter.value}`; } - return this.parseAnimeList(url + `&page=${page}`); + return await this.parseAnimeList(url + `&page=${page}`); } async getDetail(url) { const res = await this.client.get(this.source.baseUrl + url); @@ -258,7 +258,7 @@ class DefaultExtension extends MProvider { ["Più recenti", "5"], ["Più visti", "6"] ].map(x => ({type_name: 'SelectOption', name: x[0], value: x[1] })) - }, + } ]; } getSourcePreferences() { diff --git a/javascript/manga/src/it/mangaworld.js b/javascript/manga/src/it/mangaworld.js new file mode 100644 index 00000000..ba6bfe00 --- /dev/null +++ b/javascript/manga/src/it/mangaworld.js @@ -0,0 +1,280 @@ +const mangayomiSources = [{ + "name": "MangaWorld", + "lang": "it", + "baseUrl": "https://www.mangaworld.ac", + "apiUrl": "", + "iconUrl": "https://www.mangaworld.ac/public/assets/images/MangaWorldSquareLogo.png", + "typeSource": "single", + "isManga": true, + "version": "0.0.1", + "dateFormat": "", + "dateFormatLocale": "", + "pkgPath": "manga/src/it/mangaworld.js" +}]; + +class DefaultExtension extends MProvider { + constructor () { + super(); + this.client = new Client(); + this.getPageCount = /totalPages["']:\s*(\d+).*?page["']:\s*(\d+)/i; // totalPages:\s*(\d+).*?page:\s*(\d+) + this.getMangas = /['"]mangas['"]:([\s\S]+?])\s*\}/i; // mangas:([\s\S]+?])\s*\} + this.getManga = /manga['"]:\s*(\{[\S\s]*?\}),\s*['"]image/i; // manga:\s*(\{[\S\s]*?\}),\s*image + this.getPages = /pages['"]:\s*(\{[\S\s]*?\})\s*\}/ // pages: ({[\S\s]*?})\s*} + this.getChapter = /chapter['"]:\s*(\{[\S\s]*?\}),\s*['"]image/i; // chapter:\s*(\{[\S\s]*?\}),\s*image + this.getCdn = /cdn_url["']:\s*["'](.+?)["']/i; + } + parseStatus(status) { + return { + 'In corso': 0, + 'Finito': 1, + 'In pausa': 2, + 'Droppato': 3, + 'Cancellato': 3 + }[status] ?? 5; + } + async parseMangaList(url) { + const res = await this.client.get(url); + const json = this.getMangas.exec(res.body)[1]; + let mangas = JSON.parse(json).map(manga => ({ + name: manga.title, + imageUrl: manga.imageT, + author: manga.author.join(', '), + artist: manga.artist.join(', '), + status: this.parseStatus(manga.statusT), + description: manga.trama, + genre: manga.genres.map(g => g.name), + link: `${this.source.baseUrl}/manga/${manga.linkId}/${manga.slug}`, + })); + const pageNums = this.getPageCount.exec(res.body); + return { "list": mangas, "hasNextPage": pageNums[1] > pageNums[2] }; + } + async getPopular(page) { + return await this.parseMangaList(this.source.baseUrl + `/archive?sort=most_read&page=${page}`); + } + async getLatestUpdates(page) { + return await this.parseMangaList(this.source.baseUrl + `/archive?sort=newest&page=${page}`); + } + async search(query, page, filters) { + let url = `${this.source.baseUrl}/archive?keyword=${query}` + // Search sometimes failed because filters were empty. I experienced this mostly on android... + if (!filters || filters.length == 0) { + return await this.parseMangaList(url + `&page=${page}`); + } + + // type + for (const filter of filters[0].state) { + if (filter.state == true) + url += `&type=${filter.value}`; + } + // genre + for (const filter of filters[1].state) { + if (filter.state == true) + url += `&genre=${filter.value}`; + } + // status + for (const filter of filters[2].state) { + if (filter.state == true) + url += `&status=${filter.value}`; + } + // year + for (const filter of filters[3].state) { + if (filter.state == true) + url += `&year=${filter.value}`; + } + // sort + url += `&sort=${filters[4].values[filters[4].state].value}`; + return await this.parseMangaList(url + `&page=${page}`); + } + async getDetail(url) { + const res = await this.client.get(url); + const chapters = []; + + const manga = JSON.parse(this.getManga.exec(res.body)[1]); + const pages = JSON.parse(this.getPages.exec(res.body)[1]); + const baseUrl = `${this.source.baseUrl}/manga/${manga.linkId}/${manga.slug}/read/`; + + for (const v of pages.volumes) { + for (const c of v.chapters) { + chapters.push({ + name: c.name, + url: baseUrl + c.id, + dateUpload: new Date(c.createdAt).valueOf().toString(), + }); + } + } + for (const c of pages.singleChapters) { + chapters.push({ + name: c.name, + url: baseUrl + c.id, + dateUpload: new Date(c.createdAt).valueOf().toString(), + }); + } + return { + name: manga.title, + imageUrl: manga.imageT, + author: manga.author.join(', '), + artist: manga.artist.join(', '), + status: this.parseStatus(manga.statusT), + description: manga.trama, + genre: manga.genres.map(g => g.name), + chapters: chapters + }; + } + // For manga chapter pages + async getPageList(url) { + const res = await new Client().get(url); + const cdn = this.getCdn.exec(res.body)[1]; + const chapter = JSON.parse(this.getChapter.exec(res.body)[1]); + const manga = chapter.manga; + const volume = chapter.volume; + const baseUrl = volume ? + `${cdn}/chapters/${manga.slugFolder}-${manga.id}/${volume.slugFolder}-${volume.id}/${chapter.slugFolder}-${chapter.id}/` : + `${cdn}/chapters/${manga.slugFolder}-${manga.id}/${chapter.slugFolder}-${chapter.id}/`; + return chapter.pages.map(img => ({url: baseUrl + img})); + } + getFilterList() { + return [ + { + type_name: "GroupFilter", + name: "Tipo", + state: [ + ['Manga', 'manga'], + ['Manhua', 'manhua'], + ['Manhwa', 'manhwa'], + ['Oneshot', 'oneshot'] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, + { + type_name: "GroupFilter", + name: "Generi", + state: [ + ['Adulti', 'adulti'], + ['Arti Marziali', 'arti-marziali'], + ['Avventura', 'avventura'], + ['Azione', 'azione'], + ['Commedia', 'commedia'], + ['Doujinshi', 'doujinshi'], + ['Drammatico', 'drammatico'], + ['Ecchi', 'ecchi'], + ['Fantasy', 'fantasy'], + ['Gender Bender', 'gender-bender'], + ['Harem', 'harem'], + ['Hentai', 'hentai'], + ['Horror', 'horror'], + ['Josei', 'josei'], + ['Lolicon', 'lolicon'], + ['Maturo', 'maturo'], + ['Mecha', 'mecha'], + ['Mistero', 'mistero'], + ['Psicologico', 'psicologico'], + ['Romantico', 'romantico'], + ['Sci-fi', 'sci-fi'], + ['Scolastico', 'scolastico'], + ['Seinen', 'seinen'], + ['Shotacon', 'shotacon'], + ['Shoujo', 'shoujo'], + ['Shoujo Ai', 'shoujo-ai'], + ['Shounen', 'shounen'], + ['Shounen Ai', 'shounen-ai'], + ['Slice of Life', 'slice-of-life'], + ['Smut', 'smut'], + ['Soprannaturale', 'soprannaturale'], + ['Sport', 'sport'], + ['Storico', 'storico'], + ['Tragico', 'tragico'], + ['Yaoi', 'yaoi'], + ['Yuri', 'yuri'] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, + { + type_name: "GroupFilter", + name: "Stato", + state: [ + ['In corso', 'ongoing'], + ['Finito', 'completed'], + ['Droppato', 'dropped'], + ['In pausa', 'paused'], + ['Cancellato', 'canceled'] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, + { + type_name: "GroupFilter", + name: "Anno", + state: [ + ['Sconosciuto', 'Sconosciuto'], + ['1968', '1968'], + ['1970', '1970'], + ['1972', '1972'], + ['1973', '1973'], + ['1974', '1974'], + ['1975', '1975'], + ['1976', '1976'], + ['1977', '1977'], + ['1979', '1979'], + ['1980', '1980'], + ['1981', '1981'], + ['1982', '1982'], + ['1983', '1983'], + ['1984', '1984'], + ['1985', '1985'], + ['1986', '1986'], + ['1987', '1987'], + ['1988', '1988'], + ['1989', '1989'], + ['1990', '1990'], + ['1991', '1991'], + ['1992', '1992'], + ['1993', '1993'], + ['1994', '1994'], + ['1995', '1995'], + ['1996', '1996'], + ['1997', '1997'], + ['1998', '1998'], + ['1999', '1999'], + ['2000', '2000'], + ['2001', '2001'], + ['2002', '2002'], + ['2003', '2003'], + ['2004', '2004'], + ['2005', '2005'], + ['2006', '2006'], + ['2007', '2007'], + ['2008', '2008'], + ['2009', '2009'], + ['2010', '2010'], + ['2011', '2011'], + ['2012', '2012'], + ['2013', '2013'], + ['2014', '2014'], + ['2015', '2015'], + ['2016', '2016'], + ['2017', '2017'], + ['2018', '2018'], + ['2019', '2019'], + ['2020', '2020'], + ['2021', '2021'], + ['2022', '2022'], + ['2023', '2023'], + ['2024', '2024'] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, + { + type_name: "SelectFilter", + type: "sort", + name: "Ordina per", + state: 4, + values: [ + ['Più letti', 'most_read'], + ['Meno letti', 'less_read'], + ['Più recenti', 'newest'], + ['Meno recenti', 'oldest'], + ['A-Z', 'a-z'], + ['Z-A', 'z-a'] + ].map(x => ({type_name: 'SelectOption', name: x[0], value: x[1] })) + } + ]; + } + getSourcePreferences() { + throw new Error("getSourcePreferences not implemented"); + } +}