Comikey: Fix page list (#1161)
This commit is contained in:
parent
712c3a75be
commit
f71938e357
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = "Comikey"
|
extName = "Comikey"
|
||||||
extClass = ".ComikeyFactory"
|
extClass = ".ComikeyFactory"
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -7,6 +7,8 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
|
import android.webkit.WebResourceRequest
|
||||||
|
import android.webkit.WebResourceResponse
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
|
@ -24,6 +26,7 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
@ -267,17 +270,80 @@ open class Comikey(
|
||||||
innerWv.settings.domStorageEnabled = true
|
innerWv.settings.domStorageEnabled = true
|
||||||
innerWv.settings.javaScriptEnabled = true
|
innerWv.settings.javaScriptEnabled = true
|
||||||
innerWv.settings.blockNetworkImage = true
|
innerWv.settings.blockNetworkImage = true
|
||||||
|
innerWv.settings.userAgentString = headers["User-Agent"]
|
||||||
innerWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
innerWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
||||||
innerWv.addJavascriptInterface(jsInterface, interfaceName)
|
innerWv.addJavascriptInterface(jsInterface, interfaceName)
|
||||||
|
|
||||||
|
// Somewhat useful if you need to debug WebView issues. Don't delete.
|
||||||
|
//
|
||||||
|
/*innerWv.webChromeClient = object : WebChromeClient() {
|
||||||
|
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
|
||||||
|
if (consoleMessage == null) { return false }
|
||||||
|
val logContent = "wv: ${consoleMessage.message()} (${consoleMessage.sourceId()}, line ${consoleMessage.lineNumber()})"
|
||||||
|
when (consoleMessage.messageLevel()) {
|
||||||
|
ConsoleMessage.MessageLevel.DEBUG -> Log.d("comikey", logContent)
|
||||||
|
ConsoleMessage.MessageLevel.ERROR -> Log.e("comikey", logContent)
|
||||||
|
ConsoleMessage.MessageLevel.LOG -> Log.i("comikey", logContent)
|
||||||
|
ConsoleMessage.MessageLevel.TIP -> Log.i("comikey", logContent)
|
||||||
|
ConsoleMessage.MessageLevel.WARNING -> Log.w("comikey", logContent)
|
||||||
|
else -> Log.d("comikey", logContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
innerWv.webViewClient = object : WebViewClient() {
|
innerWv.webViewClient = object : WebViewClient() {
|
||||||
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
||||||
super.onPageStarted(view, url, favicon)
|
super.onPageStarted(view, url, favicon)
|
||||||
view?.evaluateJavascript(webviewScript.replace("__interface__", interfaceName)) {}
|
view?.evaluateJavascript(webviewScript.replace("__interface__", interfaceName)) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you're logged in, the manifest URL sent to the client is not a direct link;
|
||||||
|
// it only redirects to the real one when you call it.
|
||||||
|
//
|
||||||
|
// In order to avoid a later call and remove an avenue for sniffing out users,
|
||||||
|
// we intercept said request so we can grab the real manifest URL.
|
||||||
|
override fun shouldInterceptRequest(
|
||||||
|
view: WebView?,
|
||||||
|
request: WebResourceRequest?,
|
||||||
|
): WebResourceResponse? {
|
||||||
|
val url = request?.url
|
||||||
|
?: return super.shouldInterceptRequest(view, request)
|
||||||
|
|
||||||
|
if (url.host != "relay-us.epub.rocks" || url.path?.endsWith("/manifest") != true) {
|
||||||
|
return super.shouldInterceptRequest(view, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
innerWv.loadUrl("$baseUrl${chapter.url}")
|
val requestHeaders = headers.newBuilder().apply {
|
||||||
|
request.requestHeaders.entries.forEach {
|
||||||
|
set(it.key, it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAll("X-Requested-With")
|
||||||
|
}.build()
|
||||||
|
val response = client.newCall(GET(url.toString(), requestHeaders)).execute()
|
||||||
|
|
||||||
|
jsInterface.manifestUrl = response.request.url
|
||||||
|
|
||||||
|
return WebResourceResponse(
|
||||||
|
response.headers["Content-Type"] ?: "application/divina+json+vnd.e4p.drm",
|
||||||
|
null,
|
||||||
|
response.code,
|
||||||
|
response.message,
|
||||||
|
response.headers.toMap(),
|
||||||
|
response.body.byteStream(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
innerWv.loadUrl(
|
||||||
|
"$baseUrl${chapter.url}",
|
||||||
|
buildMap {
|
||||||
|
putAll(headers.toMap())
|
||||||
|
put("X-Requested-With", randomString())
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
latch.await(30, TimeUnit.SECONDS)
|
latch.await(30, TimeUnit.SECONDS)
|
||||||
|
@ -291,18 +357,30 @@ open class Comikey(
|
||||||
throw Exception(jsInterface.error)
|
throw Exception(jsInterface.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
val manifestUrl = jsInterface.manifestUrl.toHttpUrl()
|
val manifestUrl = jsInterface.manifestUrl!!
|
||||||
|
val manifest = jsInterface.manifest!!
|
||||||
|
val webtoon = manifest.metadata.readingProgression == "ttb"
|
||||||
|
|
||||||
return jsInterface.images.mapIndexed { i, it ->
|
return manifest.readingOrder.mapIndexed { i, it ->
|
||||||
val href = it.alternate.firstOrNull { it.type == "image/webp" }?.href
|
|
||||||
?: it.href
|
|
||||||
val url = manifestUrl.newBuilder().apply {
|
val url = manifestUrl.newBuilder().apply {
|
||||||
removePathSegment(manifestUrl.pathSegments.size - 1)
|
removePathSegment(manifestUrl.pathSize - 1)
|
||||||
addPathSegments(href)
|
|
||||||
addQueryParameter("act", jsInterface.act)
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
Page(i, imageUrl = url.toString())
|
if (it.alternate.isNotEmpty() && it.height == 2048 && it.type == "image/jpeg") {
|
||||||
|
addPathSegments(
|
||||||
|
it.alternate.first {
|
||||||
|
val dimension = if (webtoon) it.width else it.height
|
||||||
|
|
||||||
|
dimension <= 1536 && it.type == "image/webp"
|
||||||
|
}.href,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
addPathSegments(it.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
addQueryParameter("act", jsInterface.act)
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
Page(i, imageUrl = url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,13 +410,17 @@ open class Comikey(
|
||||||
?: throw Exception(intl["error_webview_script_not_found"])
|
?: throw Exception(intl["error_webview_script_not_found"])
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun randomString() = buildString(15) {
|
private fun randomString(): String {
|
||||||
|
val length = (10..20).random()
|
||||||
|
|
||||||
|
return buildString(length) {
|
||||||
val charPool = ('a'..'z') + ('A'..'Z')
|
val charPool = ('a'..'z') + ('A'..'Z')
|
||||||
|
|
||||||
for (i in 0 until 15) {
|
for (i in 0 until length) {
|
||||||
append(charPool.random())
|
append(charPool.random())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeEpisodeSlug(episode: ComikeyEpisode, defaultChapterPrefix: String): String {
|
private fun makeEpisodeSlug(episode: ComikeyEpisode, defaultChapterPrefix: String): String {
|
||||||
val e4pid = episode.id.split("-", limit = 2).last()
|
val e4pid = episode.id.split("-", limit = 2).last()
|
||||||
|
@ -354,7 +436,7 @@ open class Comikey(
|
||||||
defaultChapterPrefix
|
defaultChapterPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
return "$e4pid/$chapterPrefix-${episode.number.toString().replace(".", "-")}"
|
return "$e4pid/$chapterPrefix-${episode.number.toString().removeSuffix(".0").replace(".", "-")}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private class JsInterface(
|
private class JsInterface(
|
||||||
|
@ -362,11 +444,10 @@ open class Comikey(
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
private val intl: Intl,
|
private val intl: Intl,
|
||||||
) {
|
) {
|
||||||
var images: List<ComikeyPage> = emptyList()
|
var manifest: ComikeyEpisodeManifest? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var manifestUrl: String = ""
|
var manifestUrl: HttpUrl? = null
|
||||||
private set
|
|
||||||
|
|
||||||
var act: String = ""
|
var act: String = ""
|
||||||
private set
|
private set
|
||||||
|
@ -390,9 +471,12 @@ open class Comikey(
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
fun passPayload(manifestUrl: String, act: String, rawData: String) {
|
fun passPayload(manifestUrl: String, act: String, rawData: String) {
|
||||||
this.manifestUrl = manifestUrl
|
if (this.manifestUrl == null) {
|
||||||
|
this.manifestUrl = manifestUrl.toHttpUrl()
|
||||||
|
}
|
||||||
|
|
||||||
this.act = act
|
this.act = act
|
||||||
images = json.decodeFromString<ComikeyEpisodeManifest>(rawData).readingOrder
|
manifest = json.decodeFromString<ComikeyEpisodeManifest>(rawData)
|
||||||
|
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,15 @@ data class ComikeyEpisode(
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ComikeyEpisodeManifest(
|
data class ComikeyEpisodeManifest(
|
||||||
|
val metadata: ComikeyEpisodeManifestMetadata,
|
||||||
val readingOrder: List<ComikeyPage>,
|
val readingOrder: List<ComikeyPage>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ComikeyEpisodeManifestMetadata(
|
||||||
|
val readingProgression: String,
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ComikeyPage(
|
data class ComikeyPage(
|
||||||
val href: String,
|
val href: String,
|
||||||
|
|
Loading…
Reference in New Issue