Fix InManga pages not load & MangaMx search (#7744)

* InManga replace Gson with kotlinx.serialization, fix pages not load.

* MangaMx: Fix search bug.
This commit is contained in:
Edgar Mejía 2021-06-20 06:40:21 -06:00 committed by GitHub
parent 7f9223fefd
commit 8585b5cd81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 163 additions and 119 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'InManga'
pkgNameSuffix = 'es.inmanga'
extClass = '.InManga'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -1,26 +1,20 @@
package eu.kanade.tachiyomi.extension.es.inmanga
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
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.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
@ -41,46 +35,43 @@ class InManga : ParsedHttpSource() {
.add("X-Requested-With", "XMLHttpRequest")
.build()
private val gson = Gson()
private val json: Json by injectLazy()
// Popular
private val imageCDN = "https://pack-yak.intomanga.com/"
override fun popularMangaRequest(page: Int): Request {
val skip = (page - 1) * 10
val body =
"filter%5Bgeneres%5D%5B%5D=-1&filter%5BqueryString%5D=&filter%5Bskip%5D=$skip&filter%5Btake%5D=10&filter%5Bsortby%5D=1&filter%5BbroadcastStatus%5D=0&filter%5BonlyFavorites%5D=false&d=".toRequestBody(
null
/**
* Returns RequestBody to retrieve latest or populars Manga.
*
* @param page Current page number.
* @param isPopular If is true filter sortby = 1 else sortby = 3
* sortby = 1: Populars
* sortby = 3: Latest
*/
private fun requestBodyBuilder(page: Int, isPopular: Boolean): RequestBody = "filter%5Bgeneres%5D%5B%5D=-1&filter%5BqueryString%5D=&filter%5Bskip%5D=${(page - 1) * 10}&filter%5Btake%5D=10&filter%5Bsortby%5D=${if (isPopular) "1" else "3"}&filter%5BbroadcastStatus%5D=0&filter%5BonlyFavorites%5D=false&d=".toRequestBody(null)
override fun popularMangaRequest(page: Int) = POST(
url = "$baseUrl/manga/getMangasConsultResult",
headers = postHeaders,
body = requestBodyBuilder(page, true)
)
return POST("$baseUrl/manga/getMangasConsultResult", postHeaders, body)
}
override fun popularMangaSelector() = searchMangaSelector()
override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun popularMangaNextPageSelector() = "body"
// Latest
// Search filtered by "Recién actualizado"
override fun latestUpdatesRequest(page: Int): Request {
val skip = (page - 1) * 10
val body =
"filter%5Bgeneres%5D%5B%5D=-1&filter%5BqueryString%5D=&filter%5Bskip%5D=$skip&filter%5Btake%5D=10&filter%5Bsortby%5D=3&filter%5BbroadcastStatus%5D=0&filter%5BonlyFavorites%5D=false&d=".toRequestBody(
null
override fun latestUpdatesRequest(page: Int) = POST(
url = "$baseUrl/manga/getMangasConsultResult",
headers = postHeaders,
body = requestBodyBuilder(page, false)
)
return POST("$baseUrl/manga/getMangasConsultResult", postHeaders, body)
}
override fun latestUpdatesSelector() = searchMangaSelector()
override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element)
override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element = element)
override fun latestUpdatesNextPageSelector() = "body"
// Search
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val skip = (page - 1) * 10
@ -95,7 +86,6 @@ class InManga : ParsedHttpSource() {
override fun searchMangaParse(response: Response): MangasPage {
val mangas = mutableListOf<SManga>()
val document = response.asJsoup()
document.select(searchMangaSelector()).map { mangas.add(searchMangaFromElement(it)) }
return MangasPage(mangas, document.select(searchMangaSelector()).count() == 10)
@ -103,33 +93,23 @@ class InManga : ParsedHttpSource() {
override fun searchMangaSelector() = "body > a"
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.attr("href"))
manga.title = element.select("h4.m0").text()
manga.thumbnail_url = element.select("img").attr("abs:data-src")
return manga
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.attr("href"))
title = element.select("h4.m0").text()
thumbnail_url = element.select("img").attr("abs:data-src")
}
override fun searchMangaNextPageSelector(): String? = null
// Manga summary page
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.select("div.col-md-3 div.panel.widget").let { info ->
manga.thumbnail_url = info.select("img").attr("abs:src")
manga.status = parseStatus(info.select(" a.list-group-item:contains(estado) span").text())
thumbnail_url = info.select("img").attr("abs:src")
status = parseStatus(info.select(" a.list-group-item:contains(estado) span").text())
}
document.select("div.col-md-9").let { info ->
manga.title = info.select("h1").text()
manga.description = info.select("div.panel-body").text()
title = info.select("h1").text()
description = info.select("div.panel-body").text()
}
return manga
}
private fun parseStatus(status: String?) = when {
@ -139,64 +119,56 @@ class InManga : ParsedHttpSource() {
else -> SManga.UNKNOWN
}
// Chapters
override fun chapterListRequest(manga: SManga): Request {
return GET("$baseUrl/chapter/getall?mangaIdentification=${manga.url.substringAfterLast("/")}", headers)
}
override fun chapterListRequest(manga: SManga) = GET(
url = "$baseUrl/chapter/getall?mangaIdentification=${manga.url.substringAfterLast("/")}",
headers = headers
)
override fun chapterListParse(response: Response): List<SChapter> {
val chapters = mutableListOf<SChapter>()
val data = response.body!!.string().substringAfter("{\"data\":\"").substringBeforeLast("\"}")
.replace("\\", "")
// The server returns a JSON with data property that contains a string with the JSON,
// so is necessary to decode twice.
val data = json.decodeFromString<InMangaResultDto>(response.body!!.string())
if (data.data.isNullOrEmpty())
return emptyList()
gson.fromJson<JsonObject>(data)["result"].asJsonArray.forEach { chapters.add(chapterFromJson(it)) }
val result = json.decodeFromString<InMangaResultObjectDto<InMangaChapterDto>>(data.data)
if (!result.success)
return emptyList()
return chapters.sortedBy { it.chapter_number.toInt() }.reversed()
return result.result
.map { chap -> chapterFromObject(chap) }
.sortedBy { it.chapter_number.toInt() }.reversed()
}
override fun chapterListSelector() = "not using"
private fun chapterFromJson(json: JsonElement): SChapter {
val chapter = SChapter.create()
chapter.url = "/chapter/chapterIndexControls?identification=${json["Identification"].string}"
json["FriendlyChapterNumberUrl"].string.replace("-", ".").let { num ->
chapter.name = "Chapter $num"
chapter.chapter_number = num.toFloat()
}
chapter.date_upload = parseChapterDate(json["RegistrationDate"].string) ?: 0
return chapter
private fun chapterFromObject(chapter: InMangaChapterDto) = SChapter.create().apply {
url = "/chapter/chapterIndexControls?identification=${chapter.identification}"
name = "Chapter ${chapter.friendlyChapterNumber}"
chapter_number = chapter.number!!.toFloat()
date_upload = parseChapterDate(chapter.registrationDate)
}
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
companion object {
val dateFormat by lazy {
SimpleDateFormat("yyyy-MM-dd", Locale.US)
}
private fun parseChapterDate(string: String): Long {
return DATE_FORMATTER.parse(string)?.time ?: 0L
}
private fun parseChapterDate(string: String): Long? {
return dateFormat.parse(string)?.time ?: 0L
}
// Pages
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
val ch = document.select("[id=\"FriendlyChapterNumberUrl\"]").attr("value")
val title = document.select("[id=\"FriendlyMangaName\"]").attr("value")
document.select("img.ImageContainer").forEachIndexed { i, img ->
pages.add(Page(i, "", "$baseUrl/images/manga/$title/chapter/$ch/page/${i + 1}/${img.attr("id")}"))
add(Page(i, "", "$imageCDN/images/manga/$title/chapter/$ch/page/${i + 1}/${img.attr("id")}"))
}
return pages
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
override fun getFilterList() = FilterList()
companion object {
val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.US) }
}
}

View File

@ -0,0 +1,35 @@
package eu.kanade.tachiyomi.extension.es.inmanga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class InMangaResultDto(
val data: String?
)
@Serializable
data class InMangaResultObjectDto<T>(
val message: String = "",
val success: Boolean,
val result: List<T>
)
@Serializable
data class InMangaChapterDto(
@SerialName("PagesCount") val pagesCount: Int = 0,
@SerialName("Watched") val watched: Boolean? = false,
@SerialName("MangaIdentification") val mangaIdentification: String? = "",
@SerialName("MangaName") val mangaName: String? = "",
@SerialName("FriendlyMangaName") val friendlyMangaName: String? = "",
@SerialName("Id") val id: Int? = 0,
@SerialName("MangaId") val mangaId: Int? = 0,
@SerialName("Number") val number: Double? = null,
@SerialName("RegistrationDate") val registrationDate: String = "",
@SerialName("Description") val description: String? = "",
@SerialName("Pages") val pages: List<Int> = emptyList(),
@SerialName("Identification") val identification: String? = "",
@SerialName("FeaturedChapter") val featuredChapter: Boolean = false,
@SerialName("FriendlyChapterNumber") val friendlyChapterNumber: String? = "",
@SerialName("FriendlyChapterNumberUrl") val friendlyChapterNumberUrl: String? = "",
)

View File

@ -6,7 +6,7 @@ ext {
extName = 'MangaMx'
pkgNameSuffix = 'es.mangamx'
extClass = '.MangaMx'
extVersionCode = 11
extVersionCode = 12
libVersion = '1.2'
}

View File

@ -15,11 +15,8 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
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.Request
import okhttp3.Response
@ -49,9 +46,8 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int) = GET(
"$baseUrl/directorio?genero=false" +
"&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page",
headers
url = "$baseUrl/directorio?genero=false&estado=false&filtro=visitas&tipo=false&adulto=${if (hideNSFWContent()) "0" else "false"}&orden=desc&p=$page",
headers = headers
)
override fun popularMangaNextPageSelector() = ".page-item a[rel=next]"
@ -99,16 +95,13 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
.add("buscar", query)
.add("_token", csrfToken)
.build()
val searchHeaders = headers.newBuilder().add("X-Requested-With", "XMLHttpRequest")
val searchHeaders = headers.newBuilder()
.add("X-Requested-With", "XMLHttpRequest")
.add("Referer", baseUrl).build()
return POST("$baseUrl/buscar", searchHeaders, formBody)
return POST(url = "$baseUrl/buscar", headers = searchHeaders, body = formBody)
} else {
val uri = Uri.parse("$baseUrl/directorio").buildUpon()
uri.appendQueryParameter(
"adulto",
if (hideNSFWContent()) { "0" } else { "1" }
)
uri.appendQueryParameter("adulto", if (hideNSFWContent()) { "0" } else { "1" })
for (filter in filters) {
when (filter) {
@ -146,6 +139,7 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
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 ->
@ -158,22 +152,20 @@ open class MangaMx : ConfigurableSource, ParsedHttpSource() {
return MangasPage(mangas, hasNextPage)
} else {
val jsonResult = json.parseToJsonElement(response.body!!.string()).jsonArray
val result = json.decodeFromString<ResponseDto>(response.body!!.string())
if (result.mangaList.isEmpty()) throw Exception("Término de búsqueda demasiado corto")
if (jsonResult.isEmpty()) {
throw Exception("Término de búsqueda demasiado corto")
}
val mangaList = jsonResult.map { jsonEl -> searchMangaFromJson(jsonEl.jsonObject) }
val mangaList = result.mangaList
.map(::searchMangaFromObject)
return MangasPage(mangaList, hasNextPage = false)
}
}
private fun searchMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply {
title = jsonObj["nombre"]!!.jsonPrimitive.content
thumbnail_url = jsonObj["img"]!!.jsonPrimitive.content.replace("/thumb", "/cover")
setUrlWithoutDomain(jsonObj["url"]!!.jsonPrimitive.content)
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 {

View File

@ -0,0 +1,44 @@
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 = "",
)