Fix MangaMx (#2409)

Fix MangaMx
This commit is contained in:
happywillow0 2020-03-11 23:18:59 -04:00 committed by GitHub
parent 0f2c985b19
commit 98ccbbde25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 176 additions and 378 deletions

View File

@ -4,8 +4,8 @@ apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: MangaMx & Doujin-Yang'
pkgNameSuffix = 'es.mangamx'
extClass = '.MangaMxFactory'
extVersionCode = 5
extClass = '.MangaMx'
extVersionCode = 6
libVersion = '1.2'
}

View File

@ -21,123 +21,145 @@ import java.util.Locale
open class MangaMx : ParsedHttpSource() {
//Info
override val name = "MangaMx"
override val baseUrl = "https://manga-mx.com"
override val lang = "es"
override val supportsLatest = true
private var csrfToken = ""
override fun popularMangaSelector() = "article[id=item]"
override fun latestUpdatesSelector() = "div.manga-item"
override fun searchMangaSelector() = "article[id=item]"
override fun chapterListSelector() = throw Exception ("Not Used")
//Popular
override fun popularMangaNextPageSelector() = "a[href*=directorio]:containsOwn(Última)"
override fun latestUpdatesNextPageSelector() = "a[href*=reciente]:containsOwn(Última)"
override fun searchMangaNextPageSelector() = "a[href*=/?s]:containsOwn(Última), a[href*=directorio]:containsOwn(Última)"
override fun popularMangaRequest(page: Int) =
GET("$baseUrl/directorio?filtro=visitas&p=$page", headers)
override fun popularMangaRequest(page: Int) = GET("$baseUrl/directorio/?orden=visitas&p=$page", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/reciente/capitulos?p=$page", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = if (query.isNotBlank()) {
Uri.parse(baseUrl).buildUpon()
.appendQueryParameter("s", query)
} else {
val uri = Uri.parse("$baseUrl/directorio").buildUpon()
//Append uri filters
filters.forEach {
if (it is UriFilter)
it.addToUri(uri)
}
uri.appendQueryParameter("p", page.toString())
}
return GET(uri.toString(), headers)
override fun popularMangaNextPageSelector() = ".page-item a[rel=next]"
override fun popularMangaSelector() = "#article-div a"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
setUrlWithoutDomain(element.attr("href"))
thumbnail_url = element.select("img").attr("src")
title = element.select("div:eq(1)").text().trim()
}
//override fun mangaDetailsRequest(manga: SManga) = GET(baseUrl + manga.url, headers)
//override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
override fun chapterListRequest(manga: SManga): Request {
val body = FormBody.Builder()
.addEncoded("cap_list","")
.build()
val headers = headersBuilder()
.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.build()
return POST(baseUrl + manga.url, headers, body)
}
override fun latestUpdatesParse(response: Response): MangasPage {
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select(latestUpdatesSelector())
.distinctBy { it.select("a").first().attr("abs:href") }
.map { latestUpdatesFromElement(it) }
val hasNextPage = latestUpdatesNextPageSelector().let { selector ->
csrfToken = document.select("meta[name=csrf-token]").attr("content")
val mangas = document.select(popularMangaSelector()).map { element ->
popularMangaFromElement(element)
}
val hasNextPage = popularMangaNextPageSelector().let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
}
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.select("a").first().attr("abs:href"))
manga.title = element.select("a").first().text().trim()
return manga
}
override fun searchMangaFromElement(element: Element)= mangaFromElement(element)
//Latest
private fun mangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.select("a").first().attr("abs:href"))
manga.title = element.select("h2").text().trim()
//manga.thumbnail_url = "https:" + element.select("img").attr("src")
manga.thumbnail_url = element.select("img").attr("abs:src")
return manga
}
override fun chapterListParse(response: Response): List<SChapter> {
val jsonData = response.body()!!.string()
val results = JsonParser().parse(jsonData).asJsonArray
val chapters = mutableListOf<SChapter>()
val url = "https:" + results[0].string
for (i in 1 until results.size()) {
val obj = results[i]
chapters.add(chapterFromJson(obj, url))
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/recientes?p=$page", headers)
override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
override fun latestUpdatesSelector() = "div._1bJU3"
override fun latestUpdatesFromElement(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"))
}
return chapters
}
private fun chapterFromJson (obj: JsonElement, url: String): SChapter {
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(url + obj["id"].string)
chapter.name = obj["tc"].string + obj["titulo"].string
chapter.chapter_number = obj["numero"].string.toFloat()
chapter.date_upload = parseDate(obj["datetime"].string)
return chapter
//Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotBlank()) {
val formBody = FormBody.Builder()
.add("buscar", query)
.add("_token", csrfToken)
.build()
val searchHeaders = headers.newBuilder().add("X-Requested-With", "XMLHttpRequest")
.add("Referer", baseUrl).build()
return POST("$baseUrl/buscar", searchHeaders, formBody)
} else {
val uri = Uri.parse("$baseUrl/directorio").buildUpon()
//Append uri filters
for (filter in filters) {
when (filter) {
is StatusFilter -> uri.appendQueryParameter(
filter.name.toLowerCase(Locale.ROOT),
statusArray[filter.state].second
)
is FilterFilter -> uri.appendQueryParameter(
filter.name.toLowerCase(Locale.ROOT),
filterArray[filter.state].second
)
is TypeFilter -> uri.appendQueryParameter(
filter.name.toLowerCase(Locale.ROOT),
typedArray[filter.state].second
)
is AdultFilter -> uri.appendQueryParameter(
filter.name.toLowerCase(Locale.ROOT),
adultArray[filter.state].second
)
is OrderFilter -> uri.appendQueryParameter(
filter.name.toLowerCase(Locale.ROOT),
orderArray[filter.state].second
)
}
}
uri.appendQueryParameter("p", page.toString())
return GET(uri.toString(), headers)
}
}
override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector()
override fun searchMangaSelector(): String = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun searchMangaParse(response: Response): MangasPage {
if (!response.isSuccessful) throw Exception("Búsqueda fallida ${response.code()}")
if ("directorio" in response.request().url().toString()) {
val document = response.asJsoup()
val mangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element)
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.US ).parse(date).time
val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
} else {
val body = response.body()!!.string()
if (body == "[]") throw Exception("Término de búsqueda demasiado corto")
val json = JsonParser().parse(body)["mangas"].asJsonArray
val mangas = json.map { jsonElement -> searchMangaFromJson(jsonElement) }
val hasNextPage = false
return MangasPage(mangas, hasNextPage)
}
}
override fun chapterFromElement(element: Element)= throw Exception("Not used")
private fun searchMangaFromJson(jsonElement: JsonElement): SManga = SManga.create().apply {
title = jsonElement["nombre"].string
setUrlWithoutDomain(jsonElement["url"].string)
thumbnail_url = jsonElement["img"].string.replace("/thumb", "/cover")
}
//Details
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
manga.thumbnail_url = document.select("img[src*=cover]").attr("abs:src")
manga.description = document.select("div[id=sinopsis]").last().ownText()
manga.author = document.select("div[id=info-i]").text().let {
manga.description = document.select("div#sinopsis").last().ownText()
manga.author = document.select("div#info-i").text().let {
if (it.contains("Autor", true)) {
it.substringAfter("Autor:").substringBefore("Fecha:").trim()
} else "N/A"
}
manga.artist = manga.author
val glist = document.select("div[id=categ] a[href*=genero]").map { it.text() }
manga.genre = glist.joinToString(", ")
manga.status = when (document.select("span[id=desarrollo]")?.first()?.text()) {
manga.genre = document.select("div#categ a").joinToString(", ") { it.text() }
manga.status = when (document.select("span#desarrollo")?.first()?.text()) {
"En desarrollo" -> SManga.ONGOING
//"Completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN
@ -145,156 +167,88 @@ open class MangaMx : ParsedHttpSource() {
return manga
}
override fun pageListParse(response: Response): List<Page> {
val body = response.asJsoup()
val script = body.select("script:containsData(cap_info)").html()
val jsonData = script.substringAfter("var cap_info = ").substringBeforeLast(";")
val results = JsonParser().parse(jsonData).asJsonArray
val jsonImg = results[1].asJsonArray
val url = "https:" + jsonImg[0].string
val pages = mutableListOf<Page>()
for (i in 1 until jsonImg.size()) {
pages.add(Page(i, "",url + jsonImg[i].string))
}
return pages
//Chapters
override fun chapterListSelector(): String = "div#c_list a"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
name = element.text().trim()
setUrlWithoutDomain(element.attr("href"))
chapter_number = element.select("span").attr("data-num").toFloat()
date_upload = parseDate(element.select("span").attr("datetime"))
}
private fun parseDate(date: String): Long {
return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).parse(date)?.time ?: 0
}
//Pages
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
val script = document.select("script:containsData(hojas)").html()
val dir = script.substringAfter("var dir = '").substringBefore("';")
val imgList =
script.substringAfter("var hojas = [\"").substringBefore("\"];").split("\",\"")
imgList.forEach {
add(Page(size, "", dir + it))
}
}
override fun pageListParse(document: Document)= throw Exception("Not Used")
override fun imageUrlParse(document: Document) = throw Exception("Not Used")
//Filters
override fun getFilterList() = FilterList(
Filter.Header("NOTA: ¡Ignorado si usa la búsqueda de texto!"),
Filter.Separator(),
GenreFilter(),
LetterFilter(),
StatusFilter(),
TypeFilter(),
AdultFilter(),
SortFilter()
StatusFilter("Estado", statusArray),
FilterFilter("Filtro", filterArray),
TypeFilter("Tipo", typedArray),
AdultFilter("Adulto", adultArray),
OrderFilter("Orden", orderArray)
)
class GenreFilter : UriPartFilter("Género", "genero", arrayOf(
Pair("all","All"),
Pair("3","Acción"),
Pair("35","Artes Marciales"),
Pair("7","Aventura"),
Pair("31","Ciencia ficción"),
Pair("1","Comedia"),
Pair("37","Demonios"),
Pair("10","Deportes"),
Pair("2","Drama"),
Pair("6","Ecchi"),
Pair("42","Eroge"),
Pair("4","Escolar"),
Pair("12","Fantasía"),
Pair("20","Ficción"),
Pair("14","Gore"),
Pair("21","Harem"),
Pair("27","Histórico"),
Pair("36","Horror"),
Pair("43","Isekai"),
Pair("33","Josei"),
Pair("34","Magia"),
Pair("13","Mecha"),
Pair("41","Militar"),
Pair("17","Misterio"),
Pair("30","Músical"),
Pair("11","Psicológico"),
Pair("39","Recuentos de la vida"),
Pair("5","Romance"),
Pair("19","Seinen"),
Pair("9","Shōjo"),
Pair("32","Shōjo-ai"),
Pair("8","Shōnen"),
Pair("40","Shōnen ai"),
Pair("18","Sobrenatural"),
Pair("38","Supervivencia"),
Pair("25","Webtoon"),
Pair("15","Yaoi"),
Pair("16","Yuri")
))
class LetterFilter : UriPartFilter("Letra","letra", arrayOf(
Pair("all","All"),
Pair("a","A"),
Pair("b","B"),
Pair("c","C"),
Pair("d","D"),
Pair("e","E"),
Pair("f","F"),
Pair("g","G"),
Pair("h","H"),
Pair("i","I"),
Pair("j","J"),
Pair("k","K"),
Pair("l","L"),
Pair("m","M"),
Pair("n","N"),
Pair("o","O"),
Pair("p","P"),
Pair("q","Q"),
Pair("r","R"),
Pair("s","S"),
Pair("t","T"),
Pair("u","U"),
Pair("v","V"),
Pair("w","W"),
Pair("x","X"),
Pair("y","Y"),
Pair("z","Z")
))
private class StatusFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
class StatusFilter : UriPartFilter("Estado", "estado", arrayOf(
Pair("all","All"),Pair("1","En desarrollo"), Pair("0","Finalizado")))
private class FilterFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
private class TypeFilter : UriPartFilter("Tipo", "tipo", arrayOf(
Pair("all","All"),
Pair("0","Manga"),
Pair("1","Manhwa"),
Pair("2","One Shot"),
Pair("3","Manhua"),
Pair("4","Novela")
))
private class TypeFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
private class AdultFilter : UriPartFilter("Filtro adulto", "adulto", arrayOf(
Pair("all","All"),Pair("0","Mostrar solo +18"), Pair("1","No mostrar +18")))
private class AdultFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
class SortFilter : UriPartFilterreq("Sort", "orden", arrayOf(
Pair("visitas","Visitas"),
Pair("desc","Descendente"),
Pair("asc","Ascendente"),
Pair("lanzamiento","Lanzamiento"),
Pair("nombre","Nombre")
))
private class OrderFilter(name: String, values: Array<Pair<String, String>>) :
Filter.Select<String>(name, values.map { it.first }.toTypedArray())
/**
* Class that creates a select filter. Each entry in the dropdown has a name and a display name.
* If an entry is selected it is appended as a query parameter onto the end of the URI.
* If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI.
*/
//vals: <name, display>
open class UriPartFilter(displayName: String, private val uriParam: String, private val vals: Array<Pair<String, String>>,
private val firstIsUnspecified: Boolean = true,
defaultValue: Int = 0) :
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter {
override fun addToUri(uri: Uri.Builder) {
if (state != 0 || !firstIsUnspecified)
uri.appendQueryParameter(uriParam, vals[state].first)
}
}
open class UriPartFilterreq(displayName: String, private val uriParam: String, private val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray()), UriFilter {
override fun addToUri(uri: Uri.Builder) {
uri.appendQueryParameter(uriParam, vals[state].first)
}
}
/**
* Represents a filter that is able to modify a URI.
*/
private interface UriFilter {
fun addToUri(uri: Uri.Builder)
}
private val statusArray = arrayOf(
Pair("Estado", "false"),
Pair("En desarrollo", "1"),
Pair("Completo", "0")
)
private val filterArray = arrayOf(
Pair("Visitas", "visitas"),
Pair("Recientes", "id"),
Pair("Alfabético", "nombre")
)
private val typedArray = arrayOf(
Pair("Todo", "false"),
Pair("Mangas", "0"),
Pair("Manhwas", "1"),
Pair("One Shot", "2"),
Pair("Manhuas", "3"),
Pair("Novelas", "4")
)
private val adultArray = arrayOf(
Pair("Filtro adulto", "false"),
Pair("No mostrar +18", "0"),
Pair("Mostrar +18", "1")
)
private val orderArray = arrayOf(
Pair("Descendente", "desc"),
Pair("Ascendente", "asc")
)
}

View File

@ -1,156 +0,0 @@
package eu.kanade.tachiyomi.extension.es.mangamx
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
class MangaMxFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
MangaMx(),
DoujinYang()
)
}
class DoujinYang: MangaMx() {
override val baseUrl = "https://doujin-yang.es"
override val name = "Doujin-Yang"
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/reciente/doujin?p=$page", headers)
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesNextPageSelector() = "nav#paginacion a:contains(Última)"
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun chapterListRequest(manga: SManga): Request {
return GET(baseUrl + manga.url, headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup().select("div#c_list a").map { element ->
SChapter.create().apply {
name = element.select("h3").text()
setUrlWithoutDomain(element.attr("abs:href"))
}
}
}
override fun pageListRequest(chapter: SChapter): Request {
return POST(baseUrl + chapter.url,
headersBuilder().add("Content-Type", "application/x-www-form-urlencoded").build(),
RequestBody.create(null, "info")
)
}
override fun pageListParse(response: Response): List<Page> {
return response.body()!!.string().substringAfter(",[").substringBefore("]")
.replace(Regex("""[\\"]"""), "").split(",").let { list ->
val path = "https:" + list[0]
list.drop(1).mapIndexed { i, img -> Page(i, "", path + img) }
}
}
override fun getFilterList() = FilterList(
Filter.Header("NOTA: ¡La búsqueda de títulos no funciona!"), // "Title search not working"
Filter.Separator(),
GenreFilter(),
LetterFilter(),
StatusFilter(),
SortFilter()
)
class GenreFilter : UriPartFilter("Género", "genero", arrayOf(
Pair("all","All"),
Pair("1","Ahegao"),
Pair("379","Alien"),
Pair("2","Anal"),
Pair("490","Android18"),
Pair("717","Angel"),
Pair("633","Asphyxiation"),
Pair("237","Bandages"),
Pair("77","Bbw"),
Pair("143","Bdsm"),
Pair("23","Blackmail"),
Pair("113","Blindfold"),
Pair("24","Blowjob"),
Pair("166","Blowjobface"),
Pair("25","Body Writing"),
Pair("314","Bodymodification"),
Pair("806","Bodystocking"),
Pair("366","Bodysuit"),
Pair("419","Bodyswap"),
Pair("325","Bodywriting"),
Pair("5","Bondage"),
Pair("51","Bukkake"),
Pair("410","Catgirl"),
Pair("61","Chastitybelt"),
Pair("78","Cheating"),
Pair("293","Cheerleader"),
Pair("62","Collar"),
Pair("120","Compilation"),
Pair("74","Condom"),
Pair("63","Corruption"),
Pair("191","Corset"),
Pair("234","Cosplaying"),
Pair("389","Cowgirl"),
Pair("256","Crossdressing"),
Pair("179","Crotchtattoo"),
Pair("689","Crown"),
Pair("733","Cumflation"),
Pair("385","Cumswap"),
Pair("251","Cunnilingus"),
Pair("75","Darkskin"),
Pair("180","Daughter"),
Pair("52","Deepthroat"),
Pair("28","Defloration"),
Pair("198","Demon"),
Pair("145","Demongirl"),
Pair("64","Drugs"),
Pair("95","Drunk"),
Pair("462","Femalesonly"),
Pair("82","Femdom"),
Pair("139","Ffmthreesome"),
Pair("823","Fftthreesome"),
Pair("55","Full Color"),
Pair("181","Fullbodytattoo"),
Pair("203","Fullcensorship"),
Pair("111","Fullcolor"),
Pair("114","Gag"),
Pair("3","Glasses"),
Pair("515","Gloryhole"),
Pair("116","Humanpet"),
Pair("32","Humiliation"),
Pair("147","Latex"),
Pair("12","Maid"),
Pair("4","Milf"),
Pair("245","Military"),
Pair("414","Milking"),
Pair("34","Mind Control"),
Pair("68","Mindbreak"),
Pair("124","Mindcontrol"),
Pair("645","Nun"),
Pair("312","Nurse"),
Pair("272","Robot"),
Pair("7","Romance"),
Pair("761","Sundress"),
Pair("412","Tailplug"),
Pair("253","Tutor"),
Pair("259","Twins"),
Pair("207","Twintails"),
Pair("840","Valkyrie"),
Pair("530","Vampire"),
Pair("16","Yuri"),
Pair("273","Zombie")
))
}