parent
424021dac5
commit
ec59467da4
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'ReadComicOnline'
|
extName = 'ReadComicOnline'
|
||||||
extClass = '.Readcomiconline'
|
extClass = '.Readcomiconline'
|
||||||
extVersionCode = 24
|
extVersionCode = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.readcomiconline
|
package eu.kanade.tachiyomi.extension.en.readcomiconline
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import app.cash.quickjs.QuickJs
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.view.View
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
|
@ -12,6 +17,8 @@ import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
@ -24,8 +31,10 @@ import org.jsoup.nodes.Element
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
|
class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
|
||||||
|
@ -38,6 +47,8 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||||
.addNetworkInterceptor(::captchaInterceptor)
|
.addNetworkInterceptor(::captchaInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
@ -205,42 +216,69 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
val qualitySuffix = if ((qualitypref() != "lq" && serverpref() != "s2") || (qualitypref() == "lq" && serverpref() == "s2")) "&s=${serverpref()}&quality=${qualitypref()}" else "&s=${serverpref()}"
|
val qualitySuffix = if ((qualitypref() != "lq" && serverpref() != "s2") || (qualitypref() == "lq" && serverpref() == "s2")) {
|
||||||
|
"&s=${serverpref()}&quality=${qualitypref()}&readType=1"
|
||||||
|
} else {
|
||||||
|
"&s=${serverpref()}&readType=1"
|
||||||
|
}
|
||||||
|
|
||||||
return GET(baseUrl + chapter.url + qualitySuffix, headers)
|
return GET(baseUrl + chapter.url + qualitySuffix, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
if (rguardUrl == null) {
|
val script = client.newCall(
|
||||||
rguardUrl = document.selectFirst("script[src*='rguard.min.js']")?.absUrl("src")
|
GET(
|
||||||
}
|
"https://raw.githubusercontent.com/AwkwardPeak7/sources/main/rco",
|
||||||
|
headers,
|
||||||
|
CacheControl.FORCE_NETWORK,
|
||||||
|
),
|
||||||
|
).execute().body.string()
|
||||||
|
|
||||||
val script = document.selectFirst("script:containsData(beau)")?.data()
|
val handler = Handler(Looper.getMainLooper())
|
||||||
?: throw Exception("Failed to find image URLs")
|
val latch = CountDownLatch(1)
|
||||||
|
var webView: WebView? = null
|
||||||
|
var images: List<String> = emptyList()
|
||||||
|
|
||||||
val cleanedScript = removeComments(script)
|
handler.post {
|
||||||
|
val innerWv = WebView(Injekt.get<Application>())
|
||||||
|
|
||||||
val variableName = ARRAY_VAR.findAll(cleanedScript).map { it.groupValues[1] }
|
webView = innerWv
|
||||||
.groupingBy { it }.eachCount()
|
innerWv.settings.javaScriptEnabled = true
|
||||||
.entries.sortedByDescending { it.value }.map { it.key }
|
innerWv.settings.blockNetworkImage = true
|
||||||
|
innerWv.settings.userAgentString = headers["User-Agent"]
|
||||||
|
innerWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
||||||
|
|
||||||
return QuickJs.create().use { qjs ->
|
innerWv.webViewClient = object : WebViewClient() {
|
||||||
qjs.execute(rguardBytecode)
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
qjs.evaluate(script)
|
view?.evaluateJavascript(script) {
|
||||||
|
try {
|
||||||
variableName.forEach {
|
images = json.decodeFromString<List<String>>(it)
|
||||||
val bool = qjs.evaluate(
|
} catch (_: Exception) { }
|
||||||
"""
|
latch.countDown()
|
||||||
var _0x49b8c2=_0x123c;function _0x123c(_0x3a3f70,_0x153a9a){var _0x500ddc=_0x500d();return _0x123c=function(_0x123c39,_0x130bcf){_0x123c39=_0x123c39-0x6c;var _0x40ac71=_0x500ddc[_0x123c39];return _0x40ac71;},_0x123c(_0x3a3f70,_0x153a9a);}function _0x500d(){var _0x30f66a=['12914343dbcuCG','8tTIdhc','39LHaBuI','11272136IRkEic','isArray','828602mqTUSC','7SBvdKh','232292tbxHGL','10035420aRjSoi','664765erNydn','1990674vTmFDr'];_0x500d=function(){return _0x30f66a;};return _0x500d();}(function(_0x214853,_0x338d23){var _0x1dd529=_0x123c,_0x48f320=_0x214853();while(!![]){try{var _0x51e687=parseInt(_0x1dd529(0x72))/0x1+parseInt(_0x1dd529(0x74))/0x2*(-parseInt(_0x1dd529(0x6f))/0x3)+parseInt(_0x1dd529(0x6e))/0x4*(parseInt(_0x1dd529(0x76))/0x5)+parseInt(_0x1dd529(0x6c))/0x6+parseInt(_0x1dd529(0x73))/0x7*(parseInt(_0x1dd529(0x70))/0x8)+-parseInt(_0x1dd529(0x6d))/0x9+parseInt(_0x1dd529(0x75))/0xa;if(_0x51e687===_0x338d23)break;else _0x48f320['push'](_0x48f320['shift']());}catch(_0xe6b639){_0x48f320['push'](_0x48f320['shift']());}}}(_0x500d,0xda445));try{Array[_0x49b8c2(0x71)]($it);}catch(_0x15c758){![];}
|
}
|
||||||
""".trimIndent(),
|
|
||||||
) as Boolean
|
|
||||||
|
|
||||||
if (bool) {
|
|
||||||
return@use (qjs.evaluate(it) as Array<*>).map { it as String }.toList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
|
||||||
|
innerWv.loadDataWithBaseURL(
|
||||||
|
document.location(),
|
||||||
|
document.outerHtml(),
|
||||||
|
"text/html",
|
||||||
|
"UTF-8",
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await(10, TimeUnit.SECONDS)
|
||||||
|
handler.post { webView?.destroy() }
|
||||||
|
|
||||||
|
if (latch.count == 1L) {
|
||||||
|
throw Exception("Timed getting image links")
|
||||||
|
}
|
||||||
|
|
||||||
|
return images.mapIndexed { idx, img ->
|
||||||
|
Page(idx, imageUrl = img)
|
||||||
}
|
}
|
||||||
.mapIndexed { i, imageUrl -> Page(i, "", imageUrl) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document) = ""
|
override fun imageUrlParse(document: Document) = ""
|
||||||
|
@ -377,167 +415,10 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
|
||||||
|
|
||||||
private fun serverpref() = preferences.getString(SERVER_PREF, "")
|
private fun serverpref() = preferences.getString(SERVER_PREF, "")
|
||||||
|
|
||||||
private var rguardUrl: String? = null
|
|
||||||
|
|
||||||
private val rguardBytecode: ByteArray by lazy {
|
|
||||||
val cacheDays = if (rguardUrl == null) 1 else 7
|
|
||||||
val cacheControl = CacheControl.Builder()
|
|
||||||
.maxAge(cacheDays, TimeUnit.DAYS)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val scriptUrl = rguardUrl ?: "$baseUrl/Scripts/rguard.min.js"
|
|
||||||
val scriptRequest = GET(scriptUrl, headers, cache = cacheControl)
|
|
||||||
val scriptResponse = client.newCall(scriptRequest).execute()
|
|
||||||
val scriptBody = scriptResponse.body.string()
|
|
||||||
|
|
||||||
QuickJs.create().use {
|
|
||||||
it.compile(FUNNY_JS_SCRIPT + scriptBody + ATOB_SCRIPT, "?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from https://www.removecomments.com
|
|
||||||
private fun removeComments(input: String): String {
|
|
||||||
val regx = "\\s".toRegex()
|
|
||||||
var full = input
|
|
||||||
var finalText = ""
|
|
||||||
val comment = "//"
|
|
||||||
val bcOpen = "/*"
|
|
||||||
val bcClose = "*/"
|
|
||||||
val bcOpenIndexes = mutableListOf<Int>()
|
|
||||||
val bcCloseIndexes = mutableListOf<Int>()
|
|
||||||
var o = -1
|
|
||||||
var c = -1
|
|
||||||
if (bcOpen.isNotEmpty()) {
|
|
||||||
o = full.indexOf(bcOpen)
|
|
||||||
while (o != -1) {
|
|
||||||
bcOpenIndexes.add(o)
|
|
||||||
o = full.indexOf(bcOpen, o + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bcClose.isNotEmpty()) {
|
|
||||||
c = full.indexOf(bcClose)
|
|
||||||
while (c != -1) {
|
|
||||||
bcCloseIndexes.add(c)
|
|
||||||
c = full.indexOf(bcClose, c + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var d = 0
|
|
||||||
var s = 0
|
|
||||||
var bc = 0
|
|
||||||
var record = 0
|
|
||||||
for (i in full.indices) {
|
|
||||||
if (full[i] == '"' && d == 0 && s == 0) {
|
|
||||||
d++
|
|
||||||
} else if (full[i] == '"' && d == 1 && s == 0) {
|
|
||||||
d--
|
|
||||||
}
|
|
||||||
if (full[i] == '\'' && d == 0 && s == 0) {
|
|
||||||
if (!bcOpenIndexes.contains(i)) {
|
|
||||||
s++
|
|
||||||
}
|
|
||||||
} else if (full[i] == '\'' && d == 0 && s == 1) {
|
|
||||||
if (!bcCloseIndexes.contains(i)) {
|
|
||||||
s--
|
|
||||||
}
|
|
||||||
} else if (full[i] == '\n') {
|
|
||||||
d = 0
|
|
||||||
s = 0
|
|
||||||
}
|
|
||||||
if (bcOpenIndexes.contains(i) && d == 0 && s == 0 && bc == 0) {
|
|
||||||
finalText += full.substring(record, i)
|
|
||||||
bc = 1
|
|
||||||
} else if (bcClose.isNotEmpty() && bcCloseIndexes.contains(i) && bc == 1) {
|
|
||||||
record = i + bcClose.length
|
|
||||||
bc = 0
|
|
||||||
} else if (i == full.length - 1 && bc == 0) {
|
|
||||||
finalText += full.substring(record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (comment.isNotEmpty()) {
|
|
||||||
val lines = finalText.split('\n')
|
|
||||||
val remComArr = mutableListOf<String>()
|
|
||||||
lines.forEach { line ->
|
|
||||||
var rem = line
|
|
||||||
if (line.contains(comment)) {
|
|
||||||
val comIndexes = mutableListOf<Int>()
|
|
||||||
var a = -1
|
|
||||||
a = line.indexOf(comment)
|
|
||||||
while (a != -1) {
|
|
||||||
comIndexes.add(a)
|
|
||||||
a = line.indexOf(comment, a + 1)
|
|
||||||
}
|
|
||||||
var d = 0
|
|
||||||
var s = 0
|
|
||||||
for (i in line.indices) {
|
|
||||||
if (line[i] == '"' && d == 0 && s == 0) {
|
|
||||||
d++
|
|
||||||
} else if (line[i] == '"' && d == 1 && s == 0) {
|
|
||||||
d--
|
|
||||||
}
|
|
||||||
if (line[i] == '\'' && d == 0 && s == 0) {
|
|
||||||
s++
|
|
||||||
} else if (line[i] == '\'' && d == 0 && s == 1) {
|
|
||||||
s--
|
|
||||||
}
|
|
||||||
if (comIndexes.contains(i) && d == 0 && s == 0) {
|
|
||||||
rem = line.substring(0, i)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rem.replace(regx, "").isEmpty()) {
|
|
||||||
rem = "\n"
|
|
||||||
}
|
|
||||||
remComArr.add(rem)
|
|
||||||
}
|
|
||||||
finalText = remComArr.joinToString("\n")
|
|
||||||
}
|
|
||||||
while (finalText.contains("\n\n\n")) {
|
|
||||||
finalText = finalText.replace("\n\n\n", "\n\n")
|
|
||||||
}
|
|
||||||
while (finalText.startsWith("\n")) {
|
|
||||||
finalText = finalText.substring(1)
|
|
||||||
}
|
|
||||||
return finalText
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val QUALITY_PREF_TITLE = "Image Quality Selector"
|
private const val QUALITY_PREF_TITLE = "Image Quality Selector"
|
||||||
private const val QUALITY_PREF = "qualitypref"
|
private const val QUALITY_PREF = "qualitypref"
|
||||||
private const val SERVER_PREF_TITLE = "Server Preference"
|
private const val SERVER_PREF_TITLE = "Server Preference"
|
||||||
private const val SERVER_PREF = "serverpref"
|
private const val SERVER_PREF = "serverpref"
|
||||||
|
|
||||||
private val ARRAY_VAR = Regex("""(\w+)\s*\.\s*push\s*\(""")
|
|
||||||
private val FUNNY_JS_SCRIPT = """(function(_0x55d059,_0x2a4b53){const _0x46f9ed=_0x2c15,_0x5fbbf3=_0x55d059();while(!![]){try{const _0x250626=-parseInt(_0x46f9ed(0x1c9))/0x1+-parseInt(_0x46f9ed(0x1cb))/0x2+parseInt(_0x46f9ed(0x1c1))/0x3+parseInt(_0x46f9ed(0x1bf))/0x4*(-parseInt(_0x46f9ed(0x1c0))/0x5)+parseInt(_0x46f9ed(0x1c5))/0x6*(-parseInt(_0x46f9ed(0x1c7))/0x7)+parseInt(_0x46f9ed(0x1c6))/0x8*(parseInt(_0x46f9ed(0x1be))/0x9)+-parseInt(_0x46f9ed(0x1cc))/0xa*(-parseInt(_0x46f9ed(0x1c8))/0xb);if(_0x250626===_0x2a4b53)break;else _0x5fbbf3['push'](_0x5fbbf3['shift']());}catch(_0x4a075f){_0x5fbbf3['push'](_0x5fbbf3['shift']());}}}(_0x1a8f,0xcadac));function _0x1a8f(){const _0x2ff1a8=['GwhMz','813232hXddIs','6011150dGcjKz','7851879KPoNdN','12uSzWoa','1917785LUgHvu','4699299IvmJAE','eeMSA','lHqYE','href','54wYnYWU','8jCuDDH','430220FnRNoq','22mbOOqO','699775vAmAff'];_0x1a8f=function(){return _0x2ff1a8;};return _0x1a8f();}function _0x2c15(_0x241167,_0x106b3f){const _0x1a8f75=_0x1a8f();return _0x2c15=function(_0x2c15f8,_0x3d6d3c){_0x2c15f8=_0x2c15f8-0x1be;let _0x4aab42=_0x1a8f75[_0x2c15f8];return _0x4aab42;},_0x2c15(_0x241167,_0x106b3f);}const handler={'get':function(_0x463020,_0x343d72){const _0x101204=_0x2c15;if(_0x343d72==_0x101204(0x1c4))return _0x101204(0x1c2)!==_0x101204(0x1c3)?'':new _0xaf0d53(()=>{},_0x122164);if(_0x343d72 in _0x463020)return _0x101204(0x1ca)==='dQvmR'?_0x4db6a4[_0x4cee20]:_0x463020[_0x343d72];return new Proxy(()=>{},handler);},'apply':function(_0x391c1e,_0x1ae889,_0x2daceb){return new Proxy(()=>{},handler);},'set':function(_0x49ec09,_0x4cac39,_0x48e10f){return _0x49ec09[_0x4cac39]=_0x48e10f,!![];},'construct':function(_0x41a3c1,_0x67ea52){return new Proxy(()=>{},handler);}};document=new Proxy({},handler),window=new Proxy({},handler),console=new Proxy({},handler),${'$'}=new Proxy(function(){},handler),location=new Proxy({},handler); """.trimIndent()
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
* Copyright (c) 2014 MaxArt2501
|
|
||||||
*/
|
|
||||||
private val ATOB_SCRIPT = """
|
|
||||||
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
|
||||||
b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
|
|
||||||
|
|
||||||
atob = function(string) {
|
|
||||||
// atob can work with strings with whitespaces, even inside the encoded part,
|
|
||||||
// but only \t, \n, \f, \r and ' ', which can be stripped.
|
|
||||||
string = String(string).replace(/[\t\n\f\r ]+/g, "");
|
|
||||||
if (!b64re.test(string))
|
|
||||||
throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
|
|
||||||
|
|
||||||
// Adding the padding if missing, for semplicity
|
|
||||||
string += "==".slice(2 - (string.length & 3));
|
|
||||||
var bitmap, result = "", r1, r2, i = 0;
|
|
||||||
for (; i < string.length;) {
|
|
||||||
bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12
|
|
||||||
| (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
|
|
||||||
|
|
||||||
result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255)
|
|
||||||
: r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255)
|
|
||||||
: String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue