DragonTea: Fix page list (#17366)
This commit is contained in:
parent
11eb5915ca
commit
59f296a02d
|
@ -1,24 +1,15 @@
|
|||
package eu.kanade.tachiyomi.extension.en.dragontea
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import org.jsoup.nodes.Document
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -29,7 +20,6 @@ class DragonTea : Madara(
|
|||
dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.US),
|
||||
) {
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.addInterceptor(::begonepeconIntercept)
|
||||
.rateLimit(1)
|
||||
.build()
|
||||
|
||||
|
@ -45,109 +35,46 @@ class DragonTea : Madara(
|
|||
}
|
||||
}
|
||||
|
||||
private val begonepeconSelector: String = "div.begonepecon"
|
||||
|
||||
private val peconholderSelector: String = "div.peconholder"
|
||||
private val pageIndexRegex = Regex("""image-(\d+)[a-z]+""", RegexOption.IGNORE_CASE)
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
countViews(document)
|
||||
val dataId = document.selectFirst(".entry-header.header")?.attr("data-id")?.toInt()
|
||||
?: return super.pageListParse(document)
|
||||
val elements = document.select(".reading-content .page-break img")
|
||||
val pageCount = elements.size
|
||||
|
||||
val hasSplitImages = document
|
||||
.select(begonepeconSelector)
|
||||
.firstOrNull() != null
|
||||
|
||||
if (!hasSplitImages) {
|
||||
return document.select(pageListParseSelector).mapIndexed { index, element ->
|
||||
val imageUrl = element.selectFirst("img")?.let {
|
||||
val src = when {
|
||||
it.hasAttr("data-src") -> it.attr("data-src")
|
||||
it.hasAttr("data-lazy-src") -> it.attr("data-lazy-src")
|
||||
it.hasAttr("srcset") -> it.attr("srcset").substringBefore(" ")
|
||||
else -> it.attr("src")
|
||||
}.trim()
|
||||
|
||||
if (!src.startsWith("{\"")) {
|
||||
return@let imageFromElement(it)
|
||||
}
|
||||
|
||||
val srcData = json.parseToJsonElement(src).jsonObject
|
||||
|
||||
val unsaltedCiphertext = Base64.decode(srcData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT)
|
||||
val salt = srcData["s"]!!.jsonPrimitive.content.decodeHex()
|
||||
val ciphertext = SALTED + salt + unsaltedCiphertext
|
||||
|
||||
val plaintext = CryptoAES.decrypt(Base64.encodeToString(ciphertext, Base64.DEFAULT), PASSWORD)
|
||||
json.parseToJsonElement(plaintext).jsonPrimitive.content
|
||||
}
|
||||
|
||||
Page(index, document.location(), imageUrl)
|
||||
}
|
||||
val idKey = "8" + ((dataId + 1306) * 3 - pageCount).toString()
|
||||
elements.forEach {
|
||||
val decryptedId = decryptAesJson(it.attr("id"), idKey).jsonPrimitive.content
|
||||
it.attr("id", decryptedId)
|
||||
}
|
||||
|
||||
return document.select("div.page-break, li.blocks-gallery-item, $begonepeconSelector")
|
||||
.mapIndexed { index, element ->
|
||||
val imageUrl = if (element.select(peconholderSelector).firstOrNull() == null) {
|
||||
element.select("img").first()?.let { it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src") }
|
||||
} else {
|
||||
element.select("img").joinToString("|") { it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src") } + BEGONEPECON_SUFFIX
|
||||
}
|
||||
Page(index, document.location(), imageUrl)
|
||||
}
|
||||
val orderedElements = elements.sortedBy {
|
||||
pageIndexRegex.find(it.attr("id"))?.groupValues?.get(1)?.toInt() ?: 0
|
||||
}
|
||||
val dtaKey = "13" + orderedElements.joinToString("") { it.attr("id").takeLast(1) } + (((dataId + 88) * 2) - pageCount - 4).toString()
|
||||
|
||||
val srcKey = (dataId + 20).toString() + orderedElements.joinToString("") {
|
||||
decryptAesJson(it.attr("dta"), dtaKey).jsonPrimitive.content.takeLast(2)
|
||||
} + (pageCount * 2).toString()
|
||||
|
||||
return orderedElements.mapIndexed { i, element ->
|
||||
val src = decryptAesJson(element.attr("data-src"), srcKey).jsonPrimitive.content
|
||||
Page(i, document.location(), src)
|
||||
}
|
||||
}
|
||||
|
||||
private fun begonepeconIntercept(chain: Interceptor.Chain): Response {
|
||||
if (!chain.request().url.toString().endsWith(BEGONEPECON_SUFFIX)) {
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
private fun decryptAesJson(ciphertext: String, key: String): JsonElement {
|
||||
val cipherData = json.parseToJsonElement(ciphertext).jsonObject
|
||||
|
||||
val imageUrls = chain.request().url.toString()
|
||||
.removeSuffix(BEGONEPECON_SUFFIX)
|
||||
.split("%7C")
|
||||
val unsaltedCiphertext = Base64.decode(cipherData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT)
|
||||
val salt = cipherData["s"]!!.jsonPrimitive.content.decodeHex()
|
||||
val saltedCiphertext = SALTED + salt + unsaltedCiphertext
|
||||
|
||||
var width = 0
|
||||
var height = 0
|
||||
|
||||
val imageBitmaps = imageUrls.map { imageUrl ->
|
||||
val request = chain.request().newBuilder().url(imageUrl).build()
|
||||
val response = chain.proceed(request)
|
||||
|
||||
val bitmap = BitmapFactory.decodeStream(response.body.byteStream())
|
||||
|
||||
width += bitmap.width
|
||||
height = bitmap.height
|
||||
|
||||
bitmap
|
||||
}
|
||||
|
||||
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(result)
|
||||
|
||||
var left = 0
|
||||
|
||||
imageBitmaps.forEach { bitmap ->
|
||||
val srcRect = Rect(0, 0, bitmap.width, bitmap.height)
|
||||
val dstRect = Rect(left, 0, left + bitmap.width, bitmap.height)
|
||||
|
||||
canvas.drawBitmap(bitmap, srcRect, dstRect, null)
|
||||
|
||||
left += bitmap.width
|
||||
}
|
||||
|
||||
val output = ByteArrayOutputStream()
|
||||
result.compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||
|
||||
val responseBody = output.toByteArray().toResponseBody(PNG_MEDIA_TYPE)
|
||||
|
||||
return Response.Builder()
|
||||
.code(200)
|
||||
.protocol(Protocol.HTTP_1_1)
|
||||
.request(chain.request())
|
||||
.message("OK")
|
||||
.body(responseBody)
|
||||
.build()
|
||||
return json.parseToJsonElement(CryptoAES.decrypt(Base64.encodeToString(saltedCiphertext, Base64.DEFAULT), key))
|
||||
}
|
||||
|
||||
fun String.decodeHex(): ByteArray {
|
||||
private fun String.decodeHex(): ByteArray {
|
||||
check(length % 2 == 0) { "Must have an even length" }
|
||||
|
||||
return chunked(2)
|
||||
|
@ -156,10 +83,6 @@ class DragonTea : Madara(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val BEGONEPECON_SUFFIX = "?begonepecon"
|
||||
private val PNG_MEDIA_TYPE = "image/png".toMediaType()
|
||||
|
||||
private val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
|
||||
private val PASSWORD = "releasethestormy888"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("DokkoManga", "https://dokkomanga.com", "es", overrideVersionCode = 1),
|
||||
SingleLang("Doodmanga", "https://www.doodmanga.com", "th"),
|
||||
SingleLang("DoujinHentai", "https://doujinhentai.net", "es", isNsfw = true, overrideVersionCode = 1),
|
||||
SingleLang("DragonTea", "https://dragontea.ink", "en", overrideVersionCode = 1),
|
||||
SingleLang("DragonTea", "https://dragontea.ink", "en", overrideVersionCode = 2),
|
||||
SingleLang("DragonTranslation.net", "https://dragontranslation.net", "es", isNsfw = true, className = "DragonTranslationNet"),
|
||||
SingleLang("Drake Scans", "https://drakescans.com", "en", overrideVersionCode = 3),
|
||||
SingleLang("Dream Manga", "https://www.swarmmanga.com", "en", overrideVersionCode = 3),
|
||||
|
|
Loading…
Reference in New Issue