From df8eb1822ed588d3b84b9a4b354c68bb3592a784 Mon Sep 17 00:00:00 2001 From: Swakshan Date: Wed, 29 Jan 2025 11:29:18 +0530 Subject: [PATCH 1/4] extension(weebcentral): Added popular & latest --- javascript/manga/src/en/weebcentral.js | 82 ++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 javascript/manga/src/en/weebcentral.js diff --git a/javascript/manga/src/en/weebcentral.js b/javascript/manga/src/en/weebcentral.js new file mode 100644 index 00000000..91dc2c14 --- /dev/null +++ b/javascript/manga/src/en/weebcentral.js @@ -0,0 +1,82 @@ +const mangayomiSources = [{ + "name": "Weebcentral", + "lang": "en", + "baseUrl": "https://weebcentral.com", + "apiUrl": "", + "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://weebcentral.com", + "typeSource": "single", + "itemType": 0, + "version": "0.0.1", + "pkgPath": "manga/src/en/weebcentral.js" +}]; + +class DefaultExtension extends MProvider { + getHeaders(url) { + throw new Error("getHeaders not implemented"); + } + + async getMangaList(slug,page=0) { + var page = parseInt(page) + var url = `${this.source.baseUrl}/${slug}` + url = page>0?url+`/${page}`:url + + var res = await new Client().get(url); + var doc = new Document(res.body); + var list = []; + var mangaElements = doc.select("article.bg-base-100") + for (var manga of mangaElements) { + var details = manga.selectFirst('a') + if (details.getHref.indexOf("/series/") < 0) continue; + + details = details.selectFirst("img"); + + var imageUrl = details.getSrc; + var urlSplits = imageUrl.split("/") + + var link = urlSplits[urlSplits.length - 1].split(".")[0] + var name = details.attr("alt") + + list.push({ name, imageUrl, link }); + } + var hasNextPage = page < 142? true : false; + return { list, hasNextPage } + } + + async getPopular(page) { + return await this.getMangaList("hot-updates") + } + get supportsLatest() { + throw new Error("supportsLatest not implemented"); + } + async getLatestUpdates(page) { + return await this.getMangaList("latest-updates",page) + } + async search(query, page, filters) { + throw new Error("search not implemented"); + } + async getDetail(url) { + throw new Error("getDetail not implemented"); + } + // For novel html content + async getHtmlContent(url) { + throw new Error("getHtmlContent not implemented"); + } + // Clean html up for reader + async cleanHtmlContent(html) { + throw new Error("cleanHtmlContent not implemented"); + } + // For anime episode video list + async getVideoList(url) { + throw new Error("getVideoList not implemented"); + } + // For manga chapter pages + async getPageList(url) { + throw new Error("getPageList not implemented"); + } + getFilterList() { + throw new Error("getFilterList not implemented"); + } + getSourcePreferences() { + throw new Error("getSourcePreferences not implemented"); + } +} From 3a78c966b811945d77a9d7b6652304c502589fcb Mon Sep 17 00:00:00 2001 From: Swakshan Date: Wed, 29 Jan 2025 17:13:56 +0530 Subject: [PATCH 2/4] extension(weebcentral): Added details --- javascript/manga/src/en/weebcentral.js | 69 ++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/javascript/manga/src/en/weebcentral.js b/javascript/manga/src/en/weebcentral.js index 91dc2c14..3d8e7eb7 100644 --- a/javascript/manga/src/en/weebcentral.js +++ b/javascript/manga/src/en/weebcentral.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://weebcentral.com", "typeSource": "single", "itemType": 0, - "version": "0.0.1", + "version": "0.0.2", "pkgPath": "manga/src/en/weebcentral.js" }]; @@ -15,13 +15,18 @@ class DefaultExtension extends MProvider { throw new Error("getHeaders not implemented"); } - async getMangaList(slug,page=0) { - var page = parseInt(page) + async request(slug) { var url = `${this.source.baseUrl}/${slug}` - url = page>0?url+`/${page}`:url - var res = await new Client().get(url); - var doc = new Document(res.body); + return new Document(res.body); + } + + async getMangaList(slug, page = 0) { + var page = parseInt(page) + + slug = page > 0 ? `${slug}/${page}` : slug + + var doc = await this.request(slug); var list = []; var mangaElements = doc.select("article.bg-base-100") for (var manga of mangaElements) { @@ -38,7 +43,7 @@ class DefaultExtension extends MProvider { list.push({ name, imageUrl, link }); } - var hasNextPage = page < 142? true : false; + var hasNextPage = page < 142 ? true : false; return { list, hasNextPage } } @@ -49,13 +54,59 @@ class DefaultExtension extends MProvider { throw new Error("supportsLatest not implemented"); } async getLatestUpdates(page) { - return await this.getMangaList("latest-updates",page) + return await this.getMangaList("latest-updates", page) } async search(query, page, filters) { throw new Error("search not implemented"); } + + getImageUrl(id) { return `https://temp.compsci88.com/cover/normal/${id}.webp`; } + + statusCode(status) { + return { + "Ongoing": 0, + "Complete": 1, + "Hiatus": 2, + "Canceled": 3, + }[status] ?? 5; + } + async getDetail(url) { - throw new Error("getDetail not implemented"); + var slug = `series/${url}` + var link = `${this.source.baseUrl}/${slug}` + var doc = await this.request(slug); + var imageUrl = this.getImageUrl(url) + var title = doc.selectFirst("h1").text + var description = doc.selectFirst("p.whitespace-pre-wrap.break-words").text + + var chapters = [] + var ul = doc.select("ul.flex.flex-col.gap-4 > li") + var author = "" + var genre = [] + var status = 5 + for (var li of ul) { + var strongTxt = li.selectFirst("strong").text + if (strongTxt.indexOf("Author(s):") != -1) { + author = li.selectFirst("a").text + } else if (strongTxt.indexOf("Tags(s):") != -1) { + li.select("a").forEach(a => genre.push(a.text)) + } else if (strongTxt.indexOf("Status:") != -1) { + status = this.statusCode(li.selectFirst("a").text) + } + + } + + var chapSlug = `${slug}/full-chapter-list` + doc = await this.request(chapSlug); + var chapList = doc.select("div.flex.items-center"); + for (var chap of chapList) { + var name = chap.selectFirst("span.grow.flex.items-center.gap-2").selectFirst("span").text + var dateUpload = new Date(chap.selectFirst("time.text-datetime").text).valueOf().toString() + var url = chap.selectFirst("input").attr("value") + chapters.push({ name, url, dateUpload }) + } + return { name: title, description, link, imageUrl, author, genre, status, chapters } + } // For novel html content async getHtmlContent(url) { From 836cbfc734229c073c502d6daa2f559c8a24a8e6 Mon Sep 17 00:00:00 2001 From: Swakshan Date: Wed, 29 Jan 2025 21:56:16 +0530 Subject: [PATCH 3/4] extension(weebcentral): Extract manga pages --- javascript/manga/src/en/weebcentral.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/javascript/manga/src/en/weebcentral.js b/javascript/manga/src/en/weebcentral.js index 3d8e7eb7..78ebafa9 100644 --- a/javascript/manga/src/en/weebcentral.js +++ b/javascript/manga/src/en/weebcentral.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://weebcentral.com", "typeSource": "single", "itemType": 0, - "version": "0.0.2", + "version": "0.0.3", "pkgPath": "manga/src/en/weebcentral.js" }]; @@ -122,8 +122,16 @@ class DefaultExtension extends MProvider { } // For manga chapter pages async getPageList(url) { - throw new Error("getPageList not implemented"); + var slug = `/chapters/${url}/images?current_page=1&reading_style=long_strip` + var doc = await this.request(slug); + + var urls = []; + + doc.select("section > img").forEach(page=>urls.push(page.attr("src"))) + + return urls } + getFilterList() { throw new Error("getFilterList not implemented"); } From 22e069c0fd1a7ec3f20615d54c5f6ba28c3184ee Mon Sep 17 00:00:00 2001 From: Swakshan Date: Wed, 29 Jan 2025 23:56:20 +0530 Subject: [PATCH 4/4] extension(weebcentral): Added search & filter --- javascript/manga/src/en/weebcentral.js | 147 +++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 8 deletions(-) diff --git a/javascript/manga/src/en/weebcentral.js b/javascript/manga/src/en/weebcentral.js index 78ebafa9..35153a15 100644 --- a/javascript/manga/src/en/weebcentral.js +++ b/javascript/manga/src/en/weebcentral.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://weebcentral.com", "typeSource": "single", "itemType": 0, - "version": "0.0.3", + "version": "0.0.5", "pkgPath": "manga/src/en/weebcentral.js" }]; @@ -56,12 +56,52 @@ class DefaultExtension extends MProvider { async getLatestUpdates(page) { return await this.getMangaList("latest-updates", page) } - async search(query, page, filters) { - throw new Error("search not implemented"); - } getImageUrl(id) { return `https://temp.compsci88.com/cover/normal/${id}.webp`; } + async search(query, page, filters) { + var offset = 32 * (parseInt(page) - 1) + var sort = filters[0].values[filters[0].state].value + var order = filters[1].values[filters[1].state].value + var translation = filters[2].values[filters[2].state].value + var status = "" + for (var filter of filters[3].state) { + if (filter.state == true) + status += `&included_status=${filter.value}` + } + var type = "" + for (var filter of filters[4].state) { + if (filter.state == true) + type += `&included_type=${filter.value}` + } + var tags = "" + for (var filter of filters[5].state) { + if (filter.state == true) + tags += `&included_tag=${filter.value}` + } + + var slug = `search/data?limit=32&offset=${offset}&author=&text=${query}&sort=${sort}&order=${order}&official=${translation}${status}${type}${tags}&display_mode=Minimal%20Display` + var doc = await this.request(slug); + var list = [] + var mangaElements = doc.select("article.bg-base-300") + for (var manga of mangaElements) { + var details = manga.selectFirst('a') + + var mangaLink = details.getHref; + var urlSplits = mangaLink.split("/") + + var link = urlSplits[urlSplits.length - 2] + var name = details.selectFirst("h2").text + var imageUrl = this.getImageUrl(link) + + list.push({ name, imageUrl, link }); + } + + var hasNextPage = doc.selectFirst("button").text.length > 0; + return { list, hasNextPage } + + } + statusCode(status) { return { "Ongoing": 0, @@ -122,18 +162,109 @@ class DefaultExtension extends MProvider { } // For manga chapter pages async getPageList(url) { - var slug = `/chapters/${url}/images?current_page=1&reading_style=long_strip` + var slug = `chapters/${url}/images?current_page=1&reading_style=long_strip` var doc = await this.request(slug); var urls = []; - doc.select("section > img").forEach(page=>urls.push(page.attr("src"))) + doc.select("section > img").forEach(page => urls.push(page.attr("src"))) return urls } - + getFilterList() { - throw new Error("getFilterList not implemented"); + return [ + { + type_name: "SelectFilter", + name: "Sort", + state: 0, + values: [ + ["Best Match", "Best Match"], + ["Alphabet", "Alphabet"], + ["Popularity", "Popularity"], + ["Subscribers", "Subscribers"], + ["Recently Added", "Recently Added"], + ["Latest Updates", "Latest Updates"] + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, { + type_name: "SelectFilter", + name: "Order", + state: 0, + values: [ + ["Ascending", "Ascending"], + ["Descending", "Descending"] + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, { + type_name: "SelectFilter", + name: "Official Translation", + state: 0, + values: [ + ["Any", "Any"], + ["True", "True"], + ["False", "False"], + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, { + type_name: "GroupFilter", + name: "Series Status", + state: [ + ["Ongoing", "Ongoing"], + ["Complete", "Complete"], + ["Hiatus", "Hiatus"], + ["Canceled", "Canceled"], + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, { + type_name: "GroupFilter", + name: "Series Type", + state: [ + ["Manga", "Manga"], + ["Manhwa", "Manhwa"], + ["Manhua", "Manhua"], + ["OEL", "OEL"], + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, { + type_name: "GroupFilter", + name: "Tags", + state: [ + ["Action", "Action"], + ["Adventure", "Adventure"], + ["Adult", "Adult"], + ["Comedy", "Comedy"], + ["Doujinshi", "Doujinshi"], + ["Drama", "Drama"], + ["Ecchi", "Ecchi"], + ["Fantasy", "Fantasy"], + ["Gender Bender", "Gender Bender"], + ["Harem", "Harem"], + ["Hentai", "Hentai"], + ["Historical", "Historical"], + ["Horror", "Horror"], + ["Isekai", "Isekai"], + ["Josei", "Josei"], + ["Lolicon", "Lolicon"], + ["Martial Arts", "Martial Arts"], + ["Mature", "Mature"], + ["Mecha", "Mecha"], + ["Mystery", "Mystery"], + ["Psychological", "Psychological"], + ["Romance", "Romance"], + ["School Life", "School Life"], + ["Sci-Fi", "Sci-Fi"], + ["Seinen", "Seinen"], + ["Shotacon", "Shotacon"], + ["Shoujo", "Shoujo"], + ["Shoujo Ai", "Shoujo Ai"], + ["Shounen", "Shounen"], + ["Slice of Life", "Slice of Life"], + ["Smut", "Smut"], + ["Sports", "Sports"], + ["Supernatural", "Supernatural"], + ["Tragedy", "Tragedy"], + ["Yaoi", "Yaoi"], + ["Yuri", "Yuri"], + ["Other", "Other"] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + } + ] } getSourcePreferences() { throw new Error("getSourcePreferences not implemented");