fix page list

closes  #4093
closes  #4092
This commit is contained in:
AwkwardPeak7 2024-07-18 22:44:30 +05:00 committed by Draff
parent 424021dac5
commit ec59467da4
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
2 changed files with 65 additions and 184 deletions

View File

@ -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"

View File

@ -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()
} }
} }