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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 beerpsi
						beerpsi