komikku/app/src/main/java/exh/md/handlers/MangaPlusHandler.kt
arkon b25e604bc2 Set preview/experimental compiler flags instead of using annotations
(cherry picked from commit 781971ee813b9c3a71558c268365e9495dbc41c2)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
2020-11-25 14:52:06 -05:00

102 lines
3.5 KiB
Kotlin

package exh.md.handlers
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Page
import exh.md.handlers.serializers.MangaPlusSerializer
import kotlinx.serialization.protobuf.ProtoBuf
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.util.UUID
class MangaPlusHandler(currentClient: OkHttpClient) {
val baseUrl = "https://jumpg-webapi.tokyo-cdn.com/api"
val headers = Headers.Builder()
.add("Origin", WEB_URL)
.add("Referer", WEB_URL)
.add("User-Agent", USER_AGENT)
.add("SESSION-TOKEN", UUID.randomUUID().toString()).build()
val client: OkHttpClient = currentClient.newBuilder()
.addInterceptor { imageIntercept(it) }
.build()
fun fetchPageList(chapterId: String): List<Page> {
val response = client.newCall(pageListRequest(chapterId)).execute()
return pageListParse(response)
}
private fun pageListRequest(chapterId: String): Request {
return GET(
"$baseUrl/manga_viewer?chapter_id=$chapterId&split=yes&img_quality=high",
headers
)
}
private fun pageListParse(response: Response): List<Page> {
val result = ProtoBuf.decodeFromByteArray(MangaPlusSerializer, response.body!!.bytes())
if (result.success == null) {
throw Exception("error getting images")
}
return result.success.mangaViewer!!.pages
.mapNotNull { it.page }
.mapIndexed { i, page ->
val encryptionKey =
if (page.encryptionKey == null) "" else "&encryptionKey=${page.encryptionKey}"
Page(i, "", "${page.imageUrl}$encryptionKey")
}
}
private fun imageIntercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if (!request.url.queryParameterNames.contains("encryptionKey")) {
return chain.proceed(request)
}
val encryptionKey = request.url.queryParameter("encryptionKey")!!
// Change the url and remove the encryptionKey to avoid detection.
val newUrl = request.url.newBuilder().removeAllQueryParameters("encryptionKey").build()
request = request.newBuilder().url(newUrl).build()
val response = chain.proceed(request)
val image = decodeImage(encryptionKey, response.body!!.bytes())
val body = image.toResponseBody("image/jpeg".toMediaTypeOrNull())
return response.newBuilder().body(body).build()
}
private fun decodeImage(encryptionKey: String, image: ByteArray): ByteArray {
val keyStream = HEX_GROUP
.findAll(encryptionKey)
.map { it.groupValues[1].toInt(16) }
.toList()
val content = image
.map { it.toInt() }
.toMutableList()
val blockSizeInBytes = keyStream.size
for ((i, value) in content.iterator().withIndex()) {
content[i] = value xor keyStream[i % blockSizeInBytes]
}
return ByteArray(content.size) { pos -> content[pos].toByte() }
}
companion object {
private const val WEB_URL = "https://mangaplus.shueisha.co.jp"
private const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
private val HEX_GROUP = "(.{1,2})".toRegex()
}
}