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 { ext {
appName = 'Tachiyomi: MangaMx & Doujin-Yang' appName = 'Tachiyomi: MangaMx & Doujin-Yang'
pkgNameSuffix = 'es.mangamx' pkgNameSuffix = 'es.mangamx'
extClass = '.MangaMxFactory' extClass = '.MangaMx'
extVersionCode = 5 extVersionCode = 6
libVersion = '1.2' libVersion = '1.2'
} }

View File

@ -21,123 +21,145 @@ import java.util.Locale
open class MangaMx : ParsedHttpSource() { open class MangaMx : ParsedHttpSource() {
//Info
override val name = "MangaMx" override val name = "MangaMx"
override val baseUrl = "https://manga-mx.com" override val baseUrl = "https://manga-mx.com"
override val lang = "es" override val lang = "es"
override val supportsLatest = true override val supportsLatest = true
private var csrfToken = ""
override fun popularMangaSelector() = "article[id=item]" //Popular
override fun latestUpdatesSelector() = "div.manga-item"
override fun searchMangaSelector() = "article[id=item]"
override fun chapterListSelector() = throw Exception ("Not Used")
override fun popularMangaNextPageSelector() = "a[href*=directorio]:containsOwn(Última)" override fun popularMangaRequest(page: Int) =
override fun latestUpdatesNextPageSelector() = "a[href*=reciente]:containsOwn(Última)" GET("$baseUrl/directorio?filtro=visitas&p=$page", headers)
override fun searchMangaNextPageSelector() = "a[href*=/?s]:containsOwn(Última), a[href*=directorio]:containsOwn(Última)"
override fun popularMangaNextPageSelector() = ".page-item a[rel=next]"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/directorio/?orden=visitas&p=$page", headers) override fun popularMangaSelector() = "#article-div a"
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/reciente/capitulos?p=$page", headers) override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { setUrlWithoutDomain(element.attr("href"))
val uri = if (query.isNotBlank()) { thumbnail_url = element.select("img").attr("src")
Uri.parse(baseUrl).buildUpon() title = element.select("div:eq(1)").text().trim()
.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 mangaDetailsRequest(manga: SManga) = GET(baseUrl + manga.url, headers) override fun popularMangaParse(response: Response): MangasPage {
//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 {
val document = response.asJsoup() val document = response.asJsoup()
val mangas = document.select(latestUpdatesSelector()) csrfToken = document.select("meta[name=csrf-token]").attr("content")
.distinctBy { it.select("a").first().attr("abs:href") }
.map { latestUpdatesFromElement(it) } val mangas = document.select(popularMangaSelector()).map { element ->
val hasNextPage = latestUpdatesNextPageSelector().let { selector -> popularMangaFromElement(element)
}
val hasNextPage = popularMangaNextPageSelector().let { selector ->
document.select(selector).first() document.select(selector).first()
} != null } != null
return MangasPage(mangas, hasNextPage) return MangasPage(mangas, hasNextPage)
} }
override fun popularMangaFromElement(element: Element) = mangaFromElement(element) //Latest
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)
private fun mangaFromElement(element: Element): SManga { override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/recientes?p=$page", headers)
val manga = SManga.create() override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector()
manga.setUrlWithoutDomain(element.select("a").first().attr("abs:href")) override fun latestUpdatesSelector() = "div._1bJU3"
manga.title = element.select("h2").text().trim() override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
//manga.thumbnail_url = "https:" + element.select("img").attr("src") thumbnail_url = element.select("img").attr("src")
manga.thumbnail_url = element.select("img").attr("abs:src") element.select("div a").apply {
return manga title = this.text().trim()
} setUrlWithoutDomain(this.attr("href"))
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))
} }
return chapters
} }
private fun chapterFromJson (obj: JsonElement, url: String): SChapter { //Search
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(url + obj["id"].string) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
chapter.name = obj["tc"].string + obj["titulo"].string if (query.isNotBlank()) {
chapter.chapter_number = obj["numero"].string.toFloat() val formBody = FormBody.Builder()
chapter.date_upload = parseDate(obj["datetime"].string) .add("buscar", query)
return chapter .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 { val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
return SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.US ).parse(date).time 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 { override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.thumbnail_url = document.select("img[src*=cover]").attr("abs:src") manga.thumbnail_url = document.select("img[src*=cover]").attr("abs:src")
manga.description = document.select("div[id=sinopsis]").last().ownText() manga.description = document.select("div#sinopsis").last().ownText()
manga.author = document.select("div[id=info-i]").text().let { manga.author = document.select("div#info-i").text().let {
if (it.contains("Autor", true)) { if (it.contains("Autor", true)) {
it.substringAfter("Autor:").substringBefore("Fecha:").trim() it.substringAfter("Autor:").substringBefore("Fecha:").trim()
} else "N/A" } else "N/A"
} }
manga.artist = manga.author manga.artist = manga.author
val glist = document.select("div[id=categ] a[href*=genero]").map { it.text() } manga.genre = document.select("div#categ a").joinToString(", ") { it.text() }
manga.genre = glist.joinToString(", ") manga.status = when (document.select("span#desarrollo")?.first()?.text()) {
manga.status = when (document.select("span[id=desarrollo]")?.first()?.text()) {
"En desarrollo" -> SManga.ONGOING "En desarrollo" -> SManga.ONGOING
//"Completed" -> SManga.COMPLETED //"Completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
@ -145,156 +167,88 @@ open class MangaMx : ParsedHttpSource() {
return manga return manga
} }
override fun pageListParse(response: Response): List<Page> { //Chapters
val body = response.asJsoup()
val script = body.select("script:containsData(cap_info)").html() override fun chapterListSelector(): String = "div#c_list a"
val jsonData = script.substringAfter("var cap_info = ").substringBeforeLast(";") override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
val results = JsonParser().parse(jsonData).asJsonArray name = element.text().trim()
val jsonImg = results[1].asJsonArray setUrlWithoutDomain(element.attr("href"))
val url = "https:" + jsonImg[0].string chapter_number = element.select("span").attr("data-num").toFloat()
val pages = mutableListOf<Page>() date_upload = parseDate(element.select("span").attr("datetime"))
for (i in 1 until jsonImg.size()) { }
pages.add(Page(i, "",url + jsonImg[i].string))
} private fun parseDate(date: String): Long {
return pages 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") override fun imageUrlParse(document: Document) = throw Exception("Not Used")
//Filters
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(
Filter.Header("NOTA: ¡Ignorado si usa la búsqueda de texto!"), Filter.Header("NOTA: ¡Ignorado si usa la búsqueda de texto!"),
Filter.Separator(), Filter.Separator(),
GenreFilter(), StatusFilter("Estado", statusArray),
LetterFilter(), FilterFilter("Filtro", filterArray),
StatusFilter(), TypeFilter("Tipo", typedArray),
TypeFilter(), AdultFilter("Adulto", adultArray),
AdultFilter(), OrderFilter("Orden", orderArray)
SortFilter()
) )
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( private class StatusFilter(name: String, values: Array<Pair<String, String>>) :
Pair("all","All"), Filter.Select<String>(name, values.map { it.first }.toTypedArray())
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")
))
class StatusFilter : UriPartFilter("Estado", "estado", arrayOf( private class FilterFilter(name: String, values: Array<Pair<String, String>>) :
Pair("all","All"),Pair("1","En desarrollo"), Pair("0","Finalizado"))) Filter.Select<String>(name, values.map { it.first }.toTypedArray())
private class TypeFilter : UriPartFilter("Tipo", "tipo", arrayOf( private class TypeFilter(name: String, values: Array<Pair<String, String>>) :
Pair("all","All"), Filter.Select<String>(name, values.map { it.first }.toTypedArray())
Pair("0","Manga"),
Pair("1","Manhwa"),
Pair("2","One Shot"),
Pair("3","Manhua"),
Pair("4","Novela")
))
private class AdultFilter : UriPartFilter("Filtro adulto", "adulto", arrayOf( private class AdultFilter(name: String, values: Array<Pair<String, String>>) :
Pair("all","All"),Pair("0","Mostrar solo +18"), Pair("1","No mostrar +18"))) Filter.Select<String>(name, values.map { it.first }.toTypedArray())
class SortFilter : UriPartFilterreq("Sort", "orden", arrayOf( private class OrderFilter(name: String, values: Array<Pair<String, String>>) :
Pair("visitas","Visitas"), Filter.Select<String>(name, values.map { it.first }.toTypedArray())
Pair("desc","Descendente"),
Pair("asc","Ascendente"),
Pair("lanzamiento","Lanzamiento"),
Pair("nombre","Nombre")
))
/** private val statusArray = arrayOf(
* Class that creates a select filter. Each entry in the dropdown has a name and a display name. Pair("Estado", "false"),
* If an entry is selected it is appended as a query parameter onto the end of the URI. Pair("En desarrollo", "1"),
* If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI. Pair("Completo", "0")
*/ )
//vals: <name, display> private val filterArray = arrayOf(
open class UriPartFilter(displayName: String, private val uriParam: String, private val vals: Array<Pair<String, String>>, Pair("Visitas", "visitas"),
private val firstIsUnspecified: Boolean = true, Pair("Recientes", "id"),
defaultValue: Int = 0) : Pair("Alfabético", "nombre")
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter { )
override fun addToUri(uri: Uri.Builder) { private val typedArray = arrayOf(
if (state != 0 || !firstIsUnspecified) Pair("Todo", "false"),
uri.appendQueryParameter(uriParam, vals[state].first) Pair("Mangas", "0"),
} Pair("Manhwas", "1"),
} Pair("One Shot", "2"),
Pair("Manhuas", "3"),
open class UriPartFilterreq(displayName: String, private val uriParam: String, private val vals: Array<Pair<String, String>>) : Pair("Novelas", "4")
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray()), UriFilter { )
override fun addToUri(uri: Uri.Builder) { private val adultArray = arrayOf(
uri.appendQueryParameter(uriParam, vals[state].first) Pair("Filtro adulto", "false"),
} Pair("No mostrar +18", "0"),
} Pair("Mostrar +18", "1")
)
/** private val orderArray = arrayOf(
* Represents a filter that is able to modify a URI. Pair("Descendente", "desc"),
*/ Pair("Ascendente", "asc")
private interface UriFilter { )
fun addToUri(uri: Uri.Builder)
}
} }

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")
))
}