MangaOni: Fix global search ()

* Added new filter and removed DTO

* Fix status

* Minor changes
This commit is contained in:
seew3l 2023-03-13 09:37:45 -05:00 committed by GitHub
parent be9525d989
commit 7c5fd399b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 107 deletions
src/es/mangamx
build.gradle
src/eu/kanade/tachiyomi/extension/es/mangamx

@ -6,7 +6,7 @@ ext {
extName = 'MangaOni' extName = 'MangaOni'
pkgNameSuffix = 'es.mangamx' pkgNameSuffix = 'es.mangamx'
extClass = '.MangaOni' extClass = '.MangaOni'
extVersionCode = 14 extVersionCode = 15
isNsfw = true isNsfw = true
} }

@ -5,7 +5,6 @@ import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
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
@ -15,16 +14,12 @@ 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 okhttp3.FormBody
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.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.nio.charset.Charset import java.nio.charset.Charset
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -43,16 +38,12 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
override val client = network.cloudflareClient override val client = network.cloudflareClient
private var csrfToken = ""
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int) = GET( override fun popularMangaRequest(page: Int) = GET(
url = "$baseUrl/directorio?genero=false&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page", url = "$baseUrl/directorio?genero=false&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page",
headers = headers, headers = headers,
) )
override fun popularMangaNextPageSelector() = ".page-item a[rel=next]" override fun popularMangaNextPageSelector() = "ul.pagination a[rel=next]"
override fun popularMangaSelector() = "#article-div a" override fun popularMangaSelector() = "#article-div a"
@ -64,7 +55,6 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup() val document = response.asJsoup()
csrfToken = document.select("meta[name=csrf-token]").attr("content")
val mangas = document.select(popularMangaSelector()).map { element -> val mangas = document.select(popularMangaSelector()).map { element ->
popularMangaFromElement(element) popularMangaFromElement(element)
@ -92,18 +82,12 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = Uri.parse("$baseUrl/${if (query.isNotBlank()) "buscar" else "directorio"}").buildUpon()
if (query.isNotBlank()) { if (query.isNotBlank()) {
val formBody = FormBody.Builder() uri.appendQueryParameter("q", query)
.add("buscar", query)
.add("_token", csrfToken)
.build()
val searchHeaders = headers.newBuilder()
.add("X-Requested-With", "XMLHttpRequest")
.add("Referer", baseUrl).build()
return POST(url = "$baseUrl/buscar", headers = searchHeaders, body = formBody)
} else { } else {
val uri = Uri.parse("$baseUrl/directorio").buildUpon() uri.appendQueryParameter("adulto", if (hideNSFWContent()) { "0" } else { "false" })
uri.appendQueryParameter("adulto", if (hideNSFWContent()) { "0" } else { "1" })
for (filter in filters) { for (filter in filters) {
when (filter) { when (filter) {
@ -126,27 +110,39 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
"genero", "genero",
genresArray[filter.state].second, genresArray[filter.state].second,
) )
is AdultContentFilter -> uri.appendQueryParameter(
"adulto",
adultContentArray[filter.state].second,
)
else -> {} else -> {}
} }
} }
}
uri.appendQueryParameter("p", page.toString()) uri.appendQueryParameter("p", page.toString())
return GET(uri.toString(), headers) return GET(uri.toString(), headers)
} }
}
override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector() override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector()
override fun searchMangaSelector(): String = popularMangaSelector() override fun searchMangaSelector(): String = "#article-div > div"
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply {
thumbnail_url = element.select("img").attr("src")
element.select("div a").apply {
title = this.text().trim()
setUrlWithoutDomain(this.attr("href"))
}
}
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
if (!response.isSuccessful) throw Exception("Búsqueda fallida ${response.code}") if (!response.isSuccessful) throw Exception("Búsqueda fallida ${response.code}")
if ("directorio" in response.request.url.toString()) {
val document = response.asJsoup() val document = response.asJsoup()
val mangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element) val mangas = if (document.location().startsWith("$baseUrl/directorio")) {
document.select(popularMangaSelector()).map { popularMangaFromElement(it) }
} else {
document.select(searchMangaSelector()).map { searchMangaFromElement(it) }
} }
val hasNextPage = searchMangaNextPageSelector()?.let { selector -> val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
@ -154,24 +150,6 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
} != null } != null
return MangasPage(mangas, hasNextPage) return MangasPage(mangas, hasNextPage)
} else {
val jsonString = response.body.string()
val result = json.decodeFromString<ResponseDto>(jsonString)
if (result.mangaList.isEmpty()) {
return MangasPage(emptyList(), hasNextPage = false)
}
val mangaList = result.mangaList
.map(::searchMangaFromObject)
return MangasPage(mangaList, hasNextPage = false)
}
}
private fun searchMangaFromObject(manga: MangaDto): SManga = SManga.create().apply {
title = manga.name
thumbnail_url = manga.img.replace("/thumb", "/cover")
setUrlWithoutDomain(manga.url)
} }
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
@ -187,9 +165,9 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
} }
manga.artist = manga.author manga.artist = manga.author
manga.genre = document.select("div#categ a").joinToString(", ") { it.text() } manga.genre = document.select("div#categ a").joinToString(", ") { it.text() }
manga.status = when (document.select("span#desarrollo").first()?.text()) { manga.status = when (document.select("strong:contains(Estado) + span").first()?.text()) {
"En desarrollo" -> SManga.ONGOING "En desarrollo" -> SManga.ONGOING
// "Completed" -> SManga.COMPLETED "Finalizado" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
return manga return manga
@ -222,7 +200,8 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
override fun imageUrlParse(document: Document) = throw Exception("Not Used") override fun imageUrlParse(document: Document) = throw Exception("Not Used")
override fun getFilterList() = FilterList( override fun getFilterList(): FilterList {
val filterList = mutableListOf(
Filter.Header("NOTA: Se ignoran si se usa el buscador"), Filter.Header("NOTA: Se ignoran si se usa el buscador"),
Filter.Separator(), Filter.Separator(),
SortBy("Ordenar por", sortables), SortBy("Ordenar por", sortables),
@ -231,6 +210,14 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
GenreFilter("Géneros", genresArray), GenreFilter("Géneros", genresArray),
) )
if (!hideNSFWContent()) {
filterList.add(
AdultContentFilter("Contenido +18", adultContentArray),
)
}
return FilterList(filterList)
}
private class StatusFilter(name: String, values: Array<Pair<String, String>>) : private class StatusFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray()) Filter.Select<String>(name, values.map { it.first }.toTypedArray())
@ -240,6 +227,9 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
private class GenreFilter(name: String, values: Array<Pair<String, String>>) : private class GenreFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray()) Filter.Select<String>(name, values.map { it.first }.toTypedArray())
private class AdultContentFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
class SortBy(name: String, values: Array<Pair<String, String>>) : Filter.Sort( class SortBy(name: String, values: Array<Pair<String, String>>) : Filter.Sort(
name, name,
values.map { it.first }.toTypedArray(), values.map { it.first }.toTypedArray(),
@ -267,9 +257,15 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
Pair("Alfabético", "nombre"), Pair("Alfabético", "nombre"),
) )
private val adultContentArray = arrayOf(
Pair("Mostrar todo", "false"),
Pair("Mostrar solo +18", "1"),
Pair("No mostrar +18", "0"),
)
/** /**
* Url: https://manga-mx.com/directorio/ * Url: https://manga-mx.com/directorio/
* Last check: 27/03/2021 * Last check: 12/03/2023
* JS script: Array.from(document.querySelectorAll('select[name="genero"] option')) * JS script: Array.from(document.querySelectorAll('select[name="genero"] option'))
* .map(a => `Pair("${a.innerText}", "${a.value}")`).join(',\n') * .map(a => `Pair("${a.innerText}", "${a.value}")`).join(',\n')
*/ */
@ -339,7 +335,7 @@ open class MangaOni : ConfigurableSource, ParsedHttpSource() {
companion object { companion object {
private const val CONTENT_PREF = "showNSFWContent" private const val CONTENT_PREF = "showNSFWContent"
private const val CONTENT_PREF_TITLE = "Ocultar contenido +18" private const val CONTENT_PREF_TITLE = "Ocultar contenido +18"
private const val CONTENT_PREF_SUMMARY = "Ocultar el contenido erótico en explorar y buscar, no funciona en los mangas recientes." private const val CONTENT_PREF_SUMMARY = "Ocultar el contenido erótico en mangas populares y filtros, no funciona en los mangas recientes ni búsquedas textuales."
private const val CONTENT_PREF_DEFAULT_VALUE = false private const val CONTENT_PREF_DEFAULT_VALUE = false
} }
} }

@ -1,44 +0,0 @@
package eu.kanade.tachiyomi.extension.es.mangamx
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ResponseDto(
@SerialName("mangas") val mangaList: List<MangaDto> = emptyList(),
@SerialName("usuarios") val usersList: List<UserDto>? = emptyList(),
@SerialName("grupos") val groupsList: List<GroupDto>? = emptyList(),
)
@Serializable
data class MangaDto(
@SerialName("nombre") val name: String = "",
@SerialName("alterno") val alternative: String? = "",
@SerialName("tipo") val type: Int = 0,
@SerialName("lanzamiento") val releaseYear: Int = 0,
@SerialName("autor") val author: String? = "",
val visible: Int = 0,
val cover: String? = "",
val slug: String = "",
val url: String = "",
val img: String = "",
)
@Serializable
data class UserDto(
@SerialName("usuario") val username: String = "",
@SerialName("perfil") val profile: String? = "",
@SerialName("genero") val gender: String? = "",
val id: Int? = 0,
val url: String = "",
val img: String = "",
)
@Serializable
data class GroupDto(
@SerialName("nombre") val name: String = "",
val id: Int? = 0,
val cover: String? = "",
val url: String = "",
val img: String = "",
)