Manhwaxxl: Fix source (#7066)

* fix

* fix comment

* fix comment

* add all genre and remove import
This commit is contained in:
dngonz 2025-01-09 15:30:55 +01:00 committed by Draff
parent 677d9d17c0
commit c9b85253ed
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
2 changed files with 62 additions and 43 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = "Manhwa XXL" extName = "Manhwa XXL"
extClass = ".ManhwaXXL" extClass = ".ManhwaXXL"
extVersionCode = 2 extVersionCode = 3
isNsfw = true isNsfw = true
} }

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.en.manhwaxxl package eu.kanade.tachiyomi.extension.en.manhwaxxl
import eu.kanade.tachiyomi.network.GET 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.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page 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.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup 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.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
class ManhwaXXL : ParsedHttpSource() { class ManhwaXXL : ParsedHttpSource() {
@ -20,25 +30,26 @@ class ManhwaXXL : ParsedHttpSource() {
override val lang = "en" override val lang = "en"
override val baseUrl = "https://manhwaxxl.com" override val baseUrl = "https://hentaitnt.net"
override val supportsLatest = true override val supportsLatest = true
// Site changed from BakaManga // Site changed from BakaManga
override val versionId = 2 override val versionId = 2
override fun headersBuilder() = super.headersBuilder() private val json: Json by injectLazy()
.add("Referer", "$baseUrl/")
override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int) = 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 { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("span.manga-name a")!!.attr("href")) setUrlWithoutDomain(element.attr("href"))
title = element.selectFirst("span.manga-name h2")!!.text() title = element.attr("title")
thumbnail_url = element.selectFirst("img")?.absUrl("src") thumbnail_url = element.selectFirst("img")?.absUrl("data-src")
} }
override fun popularMangaNextPageSelector() = "ul.pagination li.active:not(:last-child)" override fun popularMangaNextPageSelector() = "ul.pagination li.active:not(:last-child)"
@ -57,18 +68,11 @@ class ManhwaXXL : ParsedHttpSource() {
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
addQueryParameter("s", query) addQueryParameter("s", query)
} else { } else {
val filterList = if (filters.isEmpty()) getFilterList() else filters val genreFilter = filters.find { it is GenreFilter } as GenreFilter
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
val genreId = genreFilter.genres[genreFilter.state].id val genreId = genreFilter.genres[genreFilter.state].id
addPathSegment("genre")
if (genreId.isEmpty()) { addPathSegment(genreId)
addPathSegment("popular")
} else {
addPathSegment("category")
addPathSegment(genreId)
}
} }
if (page > 1) { if (page > 1) {
addPathSegment("page") addPathSegment("page")
addPathSegment(page.toString()) addPathSegment(page.toString())
@ -85,42 +89,56 @@ class ManhwaXXL : ParsedHttpSource() {
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val statusBadge = document.selectFirst("span.card-title i")?.classNames() ?: emptySet() author = document.selectFirst("div.comic-details__item_links span")?.ownText()
description = document.selectFirst("section#post-decription h2 + p")?.text()
title = document.selectFirst("span.card-title h1")!!.text() genre = document.select("div.comic-details__item a[href*=genre]").joinToString { it.text() }
author = document.selectFirst("div:has(> i.fa-user)")?.ownText() status = when (document.selectFirst("div.card h5")?.text()) {
description = document.selectFirst("div.manga-info")?.text() "Completed" -> SManga.ONGOING
genre = document.select("ul.post-categories li").joinToString { it.text() } "Ongoing" -> SManga.COMPLETED
status = when {
statusBadge.contains("fa-circle-check") -> SManga.COMPLETED
statusBadge.contains("fa-rotate") -> SManga.ONGOING
else -> SManga.UNKNOWN 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` // 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. // but we save a bunch of calls, since each page is like 12 chapters.
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val detailsDocument = response.asJsoup() val detailsDocument = response.asJsoup()
val firstChapter = detailsDocument.selectFirst("ul.chapters-list li.item-chapter a")?.absUrl("href") val script = detailsDocument.selectFirst("script:containsData(post_id)")?.data()
?: return emptyList() ?: throw Exception("Failed to get chapter id")
val document = client.newCall(GET(firstChapter, headers)).execute().asJsoup() 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<JsonObject>(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" private fun JsonElement?.getContent(key: String): String {
return this?.jsonObject?.get(key)?.jsonPrimitive?.content
override fun chapterFromElement(element: Element) = SChapter.create().apply { ?: throw Exception("Key $key not found")
setUrlWithoutDomain(element.attr("href"))
name = element.text()
} }
override fun pageListParse(document: Document) = override fun chapterListSelector() = throw UnsupportedOperationException()
document.select("div#viewer img").mapIndexed { i, it ->
override fun chapterFromElement(element: Element) = throw UnsupportedOperationException()
override fun pageListParse(document: Document): List<Page> {
return document.select("section#viewer img").mapIndexed { i, it ->
Page(i, imageUrl = it.absUrl("src")) Page(i, imageUrl = it.absUrl("src"))
} }
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
@ -133,10 +151,11 @@ class ManhwaXXL : ParsedHttpSource() {
override fun toString() = name override fun toString() = name
} }
private class GenreFilter(val genres: Array<Genre>) : Filter.Select<String>("Genre", genres.map { it.id }.toTypedArray()) private class GenreFilter(val genres: Array<Genre>) :
Filter.Select<String>("Genre", genres.map { it.name }.toTypedArray())
// https://manhwaxxl.com/genres // If you want to add new genres just add the name and id. (eg. https://hentaitnt.net/genre/action) action is the id
// 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")) // You can search more here: https://hentaitnt.net/genres
private fun getGenreList() = arrayOf( private fun getGenreList() = arrayOf(
Genre("All", ""), Genre("All", ""),
Genre("Action", "action"), Genre("Action", "action"),