ManhwaFreakXyz: Migrate theme (#7739)

* Migrate theme

* Fix lint

* Add nonce setting in the popular manga
This commit is contained in:
Chopper 2025-02-23 10:34:17 -03:00 committed by Draff
parent 027d09ffd9
commit d4c94d0972
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
3 changed files with 162 additions and 4 deletions

View File

@ -1,7 +1,7 @@
ext {
extName = 'ManhwaFreak.xyz'
extClass = '.ManhwaFreakXyz'
themePkg = 'mangathemesia'
themePkg = 'madara'
baseUrl = 'https://manhwafreak.xyz'
overrideVersionCode = 0
isNsfw = false

View File

@ -1,11 +1,152 @@
package eu.kanade.tachiyomi.extension.en.manhwafreakxyz
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import android.util.Base64
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.decodeFromStream
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class ManhwaFreakXyz : MangaThemesia(
class ManhwaFreakXyz : Madara(
"ManhwaFreak.xyz",
"https://manhwafreak.xyz",
"en",
) {
override val seriesStatusSelector = ".status-value"
// ===================== Popular ============================
override fun popularMangaRequest(page: Int): Request {
val url = "$baseUrl/$mangaSubString/${searchPage(page)}".toHttpUrl().newBuilder()
.addQueryParameter("post_type", "wp-manga")
.addQueryParameter("s", "")
.addQueryParameter("sort", "most_viewed")
.build()
return GET(url, headers)
}
override fun popularMangaSelector() = "div[class*=unit item]"
override val popularMangaUrlSelector = ".info a"
override fun popularMangaFromElement(element: Element): SManga {
return super.popularMangaFromElement(element).apply {
element.selectFirst("img:not(.flag-icon)")?.let {
thumbnail_url = imageFromElement(it)
}
}
}
override fun popularMangaParse(response: Response): MangasPage {
if (nonce.isBlank()) {
nonce = response.peekBody().let(::findNonceValue)
}
return super.popularMangaParse(response)
}
private fun Response.peekBody(): Document =
Jsoup.parseBodyFragment(peekBody(Long.MAX_VALUE).string())
override fun popularMangaNextPageSelector() = ".navigation .page-item:last-child:not(.disabled)"
// ===================== Latest ============================
override fun latestUpdatesRequest(page: Int): Request {
val request = popularMangaRequest(page)
val url = request.url.newBuilder()
.setQueryParameter("sort", "recently_added")
.build()
return request.newBuilder()
.url(url)
.build()
}
// ===================== Search ============================
override fun searchRequest(page: Int, query: String, filters: FilterList): Request {
if (nonce.isBlank()) {
nonce = findNonceValue()
}
val form = FormBody.Builder()
.add("action", "live_search")
.add("search", query)
.add("nonce", nonce)
.build()
return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, form)
}
override fun searchMangaParse(response: Response): MangasPage {
val searchDto = json.decodeFromStream<SearchDto>(response.body.byteStream())
val mangas = searchDto.mangas.map {
SManga.create().apply {
title = it.title
thumbnail_url = it.thumbnail
setUrlWithoutDomain(it.url)
}
}
return MangasPage(mangas, hasNextPage = false)
}
private var nonce: String = ""
private fun findNonceValue(document: Document? = null): String {
val dom = document ?: client.newCall(popularMangaRequest(1)).execute().asJsoup()
return dom.select("script")
.map(Element::data)
.firstOrNull { it.contains("'nonce','") }
?.substringAfter("'nonce','")
?.substringBefore("'") ?: ""
}
// ===================== Manga Details ============================
override val mangaDetailsSelectorTitle = ".serie-title"
override val mangaDetailsSelectorAuthor = ".stat-label:contains(Author) + .stat-value"
override val mangaDetailsSelectorArtist = ".stat-label:contains(Artist) + .stat-value"
override val mangaDetailsSelectorStatus = ".stat-label:contains(Status) + .manga"
override val mangaDetailsSelectorDescription = ".description-content"
override val mangaDetailsSelectorThumbnail = ".main-cover img.cover"
override val mangaDetailsSelectorGenre = ".genre-list .genre-link"
// ===================== Chapters ============================
override fun chapterListSelector() = ".list-body-hh li"
override fun chapterDateSelector() = "a > span:not(:has(i))"
override fun chapterFromElement(element: Element): SChapter {
return super.chapterFromElement(element).apply {
name = name.split(" ")
.take(2)
.joinToString(" ")
}
}
// ===================== Pages ============================
override fun pageListParse(document: Document): List<Page> {
launchIO { countViews(document) }
return document.select("canvas.manga-canvas").mapIndexed { index, canvas ->
val imageUrl = canvas.attr("data-src")
.let { Base64.decode(it, Base64.DEFAULT).toString(Charsets.UTF_8) }
Page(index, document.location(), imageUrl)
}
}
override fun getFilterList() = FilterList()
}

View File

@ -0,0 +1,17 @@
package eu.kanade.tachiyomi.extension.en.manhwafreakxyz
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class SearchDto(
@SerialName("data")
val mangas: List<MangaDto>,
)
@Serializable
class MangaDto(
val thumbnail: String,
val title: String,
val url: String,
)