2023-03-05 21:43:22 +00:00
|
|
|
package eu.kanade.tachiyomi.extension.es.mangacrab
|
|
|
|
|
|
|
|
import android.util.Base64
|
|
|
|
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
|
|
|
|
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
2023-05-06 14:17:13 +00:00
|
|
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
2023-03-05 21:43:22 +00:00
|
|
|
import eu.kanade.tachiyomi.source.model.Page
|
|
|
|
import kotlinx.serialization.json.jsonArray
|
|
|
|
import kotlinx.serialization.json.jsonObject
|
|
|
|
import kotlinx.serialization.json.jsonPrimitive
|
2023-05-06 14:17:13 +00:00
|
|
|
import okhttp3.OkHttpClient
|
2023-03-05 21:43:22 +00:00
|
|
|
import org.jsoup.nodes.Document
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
import java.util.Locale
|
2023-05-06 14:17:13 +00:00
|
|
|
import java.util.concurrent.TimeUnit
|
2023-03-05 21:43:22 +00:00
|
|
|
|
|
|
|
class MangaCrab : Madara(
|
|
|
|
"Manga Crab",
|
|
|
|
"https://mangacrab.com",
|
|
|
|
"es",
|
|
|
|
SimpleDateFormat("dd/MM/yyyy", Locale("es")),
|
|
|
|
) {
|
2023-05-06 14:17:13 +00:00
|
|
|
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
|
|
|
.addInterceptor(uaIntercept)
|
|
|
|
.connectTimeout(10, TimeUnit.SECONDS)
|
|
|
|
.readTimeout(30, TimeUnit.SECONDS)
|
|
|
|
.rateLimit(1, 2)
|
|
|
|
.build()
|
|
|
|
|
2023-03-13 14:36:10 +00:00
|
|
|
override fun chapterListSelector() = "div.listing-chapters_wrap > ul > li"
|
2023-03-05 21:43:22 +00:00
|
|
|
override val mangaDetailsSelectorDescription = "div.c-page__content div.contenedor"
|
|
|
|
|
|
|
|
override fun pageListParse(document: Document): List<Page> {
|
|
|
|
val chapterProtector = document.getElementById("chapter-protector-data")?.html()
|
|
|
|
?: return super.pageListParse(document)
|
|
|
|
|
|
|
|
val password = chapterProtector
|
|
|
|
.substringAfter("wpmangaprotectornonce='")
|
|
|
|
.substringBefore("';")
|
|
|
|
val chapterData = json.parseToJsonElement(
|
|
|
|
chapterProtector
|
|
|
|
.substringAfter("chapter_data='")
|
|
|
|
.substringBefore("';")
|
|
|
|
.replace("\\/", "/"),
|
|
|
|
).jsonObject
|
|
|
|
|
|
|
|
val unsaltedCiphertext = Base64.decode(chapterData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT)
|
|
|
|
val salt = chapterData["s"]!!.jsonPrimitive.content.decodeHex()
|
|
|
|
val ciphertext = SALTED + salt + unsaltedCiphertext
|
|
|
|
|
|
|
|
val rawImgArray = CryptoAES.decrypt(Base64.encodeToString(ciphertext, Base64.DEFAULT), password)
|
|
|
|
val imgArrayString = json.parseToJsonElement(rawImgArray).jsonPrimitive.content
|
|
|
|
val imgArray = json.parseToJsonElement(imgArrayString).jsonArray
|
|
|
|
|
|
|
|
return imgArray.mapIndexed { idx, it ->
|
|
|
|
Page(idx, document.location(), it.jsonPrimitive.content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://stackoverflow.com/a/66614516
|
|
|
|
private fun String.decodeHex(): ByteArray {
|
|
|
|
check(length % 2 == 0) { "Must have an even length" }
|
|
|
|
|
|
|
|
return chunked(2)
|
|
|
|
.map { it.toInt(16).toByte() }
|
|
|
|
.toByteArray()
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
|
|
|
|
}
|
|
|
|
}
|