From c9b85253ede69d988af2475237e29ddb3e5ada1a Mon Sep 17 00:00:00 2001 From: dngonz Date: Thu, 9 Jan 2025 15:30:55 +0100 Subject: [PATCH] Manhwaxxl: Fix source (#7066) * fix * fix comment * fix comment * add all genre and remove import --- src/en/manhwaxxl/build.gradle | 2 +- .../extension/en/manhwaxxl/ManhwaXXL.kt | 103 +++++++++++------- 2 files changed, 62 insertions(+), 43 deletions(-) diff --git a/src/en/manhwaxxl/build.gradle b/src/en/manhwaxxl/build.gradle index 8ed4b0043..15a45e2cf 100644 --- a/src/en/manhwaxxl/build.gradle +++ b/src/en/manhwaxxl/build.gradle @@ -1,7 +1,7 @@ ext { extName = "Manhwa XXL" extClass = ".ManhwaXXL" - extVersionCode = 2 + extVersionCode = 3 isNsfw = true } diff --git a/src/en/manhwaxxl/src/eu/kanade/tachiyomi/extension/en/manhwaxxl/ManhwaXXL.kt b/src/en/manhwaxxl/src/eu/kanade/tachiyomi/extension/en/manhwaxxl/ManhwaXXL.kt index 794804aaf..86c0f26c9 100644 --- a/src/en/manhwaxxl/src/eu/kanade/tachiyomi/extension/en/manhwaxxl/ManhwaXXL.kt +++ b/src/en/manhwaxxl/src/eu/kanade/tachiyomi/extension/en/manhwaxxl/ManhwaXXL.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.extension.en.manhwaxxl import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.Page @@ -8,11 +9,20 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.FormBody import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import uy.kohesive.injekt.injectLazy class ManhwaXXL : ParsedHttpSource() { @@ -20,25 +30,26 @@ class ManhwaXXL : ParsedHttpSource() { override val lang = "en" - override val baseUrl = "https://manhwaxxl.com" + override val baseUrl = "https://hentaitnt.net" override val supportsLatest = true // Site changed from BakaManga override val versionId = 2 - override fun headersBuilder() = super.headersBuilder() - .add("Referer", "$baseUrl/") + private val json: Json by injectLazy() + + override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int) = - GET("$baseUrl/popular" + (if (page > 1) "/page/$page" else "")) + GET("$baseUrl/hot-releases" + (if (page > 1) "/page/$page" else "")) - override fun popularMangaSelector() = "section#page ul.row li" + override fun popularMangaSelector() = "a.comic-tmb" override fun popularMangaFromElement(element: Element) = SManga.create().apply { - setUrlWithoutDomain(element.selectFirst("span.manga-name a")!!.attr("href")) - title = element.selectFirst("span.manga-name h2")!!.text() - thumbnail_url = element.selectFirst("img")?.absUrl("src") + setUrlWithoutDomain(element.attr("href")) + title = element.attr("title") + thumbnail_url = element.selectFirst("img")?.absUrl("data-src") } override fun popularMangaNextPageSelector() = "ul.pagination li.active:not(:last-child)" @@ -57,18 +68,11 @@ class ManhwaXXL : ParsedHttpSource() { if (query.isNotEmpty()) { addQueryParameter("s", query) } else { - val filterList = if (filters.isEmpty()) getFilterList() else filters - val genreFilter = filterList.find { it is GenreFilter } as GenreFilter + val genreFilter = filters.find { it is GenreFilter } as GenreFilter val genreId = genreFilter.genres[genreFilter.state].id - - if (genreId.isEmpty()) { - addPathSegment("popular") - } else { - addPathSegment("category") - addPathSegment(genreId) - } + addPathSegment("genre") + addPathSegment(genreId) } - if (page > 1) { addPathSegment("page") addPathSegment(page.toString()) @@ -85,42 +89,56 @@ class ManhwaXXL : ParsedHttpSource() { override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun mangaDetailsParse(document: Document) = SManga.create().apply { - val statusBadge = document.selectFirst("span.card-title i")?.classNames() ?: emptySet() - - title = document.selectFirst("span.card-title h1")!!.text() - author = document.selectFirst("div:has(> i.fa-user)")?.ownText() - description = document.selectFirst("div.manga-info")?.text() - genre = document.select("ul.post-categories li").joinToString { it.text() } - status = when { - statusBadge.contains("fa-circle-check") -> SManga.COMPLETED - statusBadge.contains("fa-rotate") -> SManga.ONGOING + author = document.selectFirst("div.comic-details__item_links span")?.ownText() + description = document.selectFirst("section#post-decription h2 + p")?.text() + genre = document.select("div.comic-details__item a[href*=genre]").joinToString { it.text() } + status = when (document.selectFirst("div.card h5")?.text()) { + "Completed" -> SManga.ONGOING + "Ongoing" -> SManga.COMPLETED else -> SManga.UNKNOWN } - thumbnail_url = document.selectFirst("div.card div.manga-avatar img")?.absUrl("src") } // Manga details page have paginated chapter list. We sacrifice `date_upload` // but we save a bunch of calls, since each page is like 12 chapters. override fun chapterListParse(response: Response): List { val detailsDocument = response.asJsoup() - val firstChapter = detailsDocument.selectFirst("ul.chapters-list li.item-chapter a")?.absUrl("href") - ?: return emptyList() - val document = client.newCall(GET(firstChapter, headers)).execute().asJsoup() + val script = detailsDocument.selectFirst("script:containsData(post_id)")?.data() + ?: throw Exception("Failed to get chapter id") + val data = script.substringAfter("viewsCacheL10n = ").substringBefore(";") + .let { json.parseToJsonElement(it).jsonObject } - return document.select(chapterListSelector()).map { chapterFromElement(it) }.reversed() + val form = FormBody.Builder().add("action", "baka_ajax").add("type", "get_chapters_list") + .add("id", data.getContent("post_id")) + .add("chapters_list_nonce", data.getContent("nonce")).build() + + val ajaxResponse = client.newCall( + POST("$baseUrl/wp-admin/admin-ajax.php", headers, form), + ).execute() + + val jsonObject = json.decodeFromString(ajaxResponse.body.string()) + return jsonObject["data"]!!.jsonObject["chapters"]!!.jsonArray.map { + SChapter.create().apply { + setUrlWithoutDomain(it.getContent("link")) + name = it.getContent("title") + } + } } - override fun chapterListSelector() = "ul#slide-out a.chapter-link" - - override fun chapterFromElement(element: Element) = SChapter.create().apply { - setUrlWithoutDomain(element.attr("href")) - name = element.text() + private fun JsonElement?.getContent(key: String): String { + return this?.jsonObject?.get(key)?.jsonPrimitive?.content + ?: throw Exception("Key $key not found") } - override fun pageListParse(document: Document) = - document.select("div#viewer img").mapIndexed { i, it -> + override fun chapterListSelector() = throw UnsupportedOperationException() + + override fun chapterFromElement(element: Element) = throw UnsupportedOperationException() + + override fun pageListParse(document: Document): List { + return document.select("section#viewer img").mapIndexed { i, it -> Page(i, imageUrl = it.absUrl("src")) } + } override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() @@ -133,10 +151,11 @@ class ManhwaXXL : ParsedHttpSource() { override fun toString() = name } - private class GenreFilter(val genres: Array) : Filter.Select("Genre", genres.map { it.id }.toTypedArray()) + private class GenreFilter(val genres: Array) : + Filter.Select("Genre", genres.map { it.name }.toTypedArray()) - // https://manhwaxxl.com/genres - // copy([...document.querySelectorAll("section#page ul li a:not([class])")].map((e) => `Genre("${e.textContent.trim()}", "${e.href.split("/").slice(-1)[0].replace(/#page$/u, "")}"),`).join("\n")) + // If you want to add new genres just add the name and id. (eg. https://hentaitnt.net/genre/action) action is the id + // You can search more here: https://hentaitnt.net/genres private fun getGenreList() = arrayOf( Genre("All", ""), Genre("Action", "action"),