2023-02-11 17:30:31 -05:00

158 lines
6.5 KiB
Kotlin

package eu.kanade.tachiyomi.extension.en.constellarscans
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.View
import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import android.webkit.WebView
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.Request
import org.jsoup.nodes.Document
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.CountDownLatch
class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarscans.com", "en") {
override val client = super.client.newBuilder()
.addInterceptor(DataImageInterceptor())
.rateLimit(1, 3)
.build()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", "$baseUrl/")
.add("Accept-Language", "en-US,en;q=0.9")
.add("DNT", "1")
.add("User-Agent", mobileUserAgent)
.add("Upgrade-Insecure-Requests", "1")
override val seriesStatusSelector = ".status"
private val mobileUserAgent by lazy {
val req = GET(UA_DB_URL)
val data = client.newCall(req).execute().body.use {
json.parseToJsonElement(it.string()).jsonArray
}.mapNotNull {
it.jsonObject["user-agent"]?.jsonPrimitive?.content?.takeIf { ua ->
ua.startsWith("Mozilla/5.0") &&
(
ua.contains("iPhone") &&
(ua.contains("FxiOS") || ua.contains("CriOS")) ||
ua.contains("Android") &&
(ua.contains("EdgA") || ua.contains("Chrome") || ua.contains("Firefox"))
)
}
}
data.random()
}
override fun pageListRequest(chapter: SChapter): Request =
super.pageListRequest(chapter).newBuilder()
.header(
"Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
)
.header("Sec-Fetch-Site", "same-origin")
.header("Sec-Fetch-Mode", "navigate")
.header("Sec-Fetch-Dest", "document")
.header("Sec-Fetch-User", "?1")
.cacheControl(CacheControl.FORCE_NETWORK)
.build()
internal class JsObject(val imageList: MutableList<String> = mutableListOf()) {
@JavascriptInterface
fun passSingleImage(url: String) {
Log.d("constellarscans", "received image: $url")
imageList.add(url)
}
}
private fun randomString(length: Int = 10): String {
val charPool = ('a'..'z') + ('A'..'Z')
return List(length) { charPool.random() }.joinToString("")
}
private val funkyScript by lazy {
client.newCall(GET(FUNKY_SCRIPT_URL)).execute().body.string()
}
@SuppressLint("SetJavaScriptEnabled")
override fun pageListParse(document: Document): List<Page> {
val interfaceName = randomString()
document.body().prepend("<script>${funkyScript.replace("\$interfaceName", interfaceName)}</script>")
val handler = Handler(Looper.getMainLooper())
val latch = CountDownLatch(1)
val jsInterface = JsObject()
var webView: WebView? = null
handler.post {
val webview = WebView(Injekt.get<Application>())
webView = webview
webview.settings.javaScriptEnabled = true
webview.settings.domStorageEnabled = true
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
webview.settings.useWideViewPort = false
webview.settings.loadWithOverviewMode = false
webview.settings.userAgentString = mobileUserAgent
webview.addJavascriptInterface(jsInterface, interfaceName)
webview.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
if (newProgress == 100) {
latch.countDown()
}
}
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("constellarscans", logContent)
ConsoleMessage.MessageLevel.ERROR -> Log.e("constellarscans", logContent)
ConsoleMessage.MessageLevel.LOG -> Log.i("constellarscans", logContent)
ConsoleMessage.MessageLevel.TIP -> Log.i("constellarscans", logContent)
ConsoleMessage.MessageLevel.WARNING -> Log.w("constellarscans", logContent)
else -> Log.d("constellarscans", logContent)
}
return true
}
}
Log.d("constellarscans", "starting webview shenanigans")
webview.loadDataWithBaseURL(baseUrl, document.toString(), "text/html", "UTF-8", null)
}
latch.await()
handler.post { webView?.destroy() }
return jsInterface.imageList.mapIndexed { idx, it -> Page(idx, imageUrl = it) }
}
override fun imageRequest(page: Page): Request = super.imageRequest(page).newBuilder()
.header("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
.header("Sec-Fetch-Dest", "image")
.header("Sec-Fetch-Mode", "no-cors")
.header("Sec-Fetch-Site", "same-origin")
.build()
companion object {
const val UA_DB_URL =
"https://cdn.jsdelivr.net/gh/mimmi20/browscap-helper@30a83c095688f40b9eaca0165a479c661e5a7fbe/tests/0002999.json"
val FUNKY_SCRIPT_URL = "https://cdn.jsdelivr.net/npm/@beerpsi/funky-script@latest/constellar.js"
}
}