RCO: fix page list (#8510)

fix
This commit is contained in:
AwkwardPeak7 2025-04-17 18:56:59 +05:00 committed by Draff
parent 22ba4be2b6
commit 1393a25fbb
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
3 changed files with 111 additions and 20 deletions

View File

@ -0,0 +1,47 @@
(() => {
const isValidUrl = (str) => {
try {
new URL(str);
return true;
} catch (e) {
return false;
}
}
const arrays = [];
const functions = [];
const results = [];
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const builtInKeys = new Set(Object.getOwnPropertyNames(iframe.contentWindow));
document.body.removeChild(iframe);
for (const [key, value] of Object.entries(window)) {
if (builtInKeys.has(key)) continue;
if (Array.isArray(value)) {
arrays.push(value);
} else if (typeof value === 'function' && value.length >= 1) {
functions.push(value);
}
}
arrays.forEach(arrayValue => {
functions.forEach(funcValue => {
try {
const mapped = arrayValue.map(x => funcValue(x));
if (
Array.isArray(mapped) &&
mapped.length != 0 &&
mapped.every(item => typeof item === 'string' && isValidUrl(item))
) {
results.push(mapped);
}
} catch (err) {}
});
});
return results
})();

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'ReadComicOnline' extName = 'ReadComicOnline'
extClass = '.Readcomiconline' extClass = '.Readcomiconline'
extVersionCode = 31 extVersionCode = 32
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -9,12 +9,15 @@ import android.util.Log
import android.view.View import android.view.View
import android.webkit.ConsoleMessage import android.webkit.ConsoleMessage
import android.webkit.WebChromeClient import android.webkit.WebChromeClient
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 eu.kanade.tachiyomi.lib.randomua.UserAgentType import eu.kanade.tachiyomi.lib.randomua.UserAgentType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
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.network.await
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -23,8 +26,11 @@ 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 keiyoushi.utils.getPreferencesLazy import keiyoushi.utils.getPreferencesLazy
import kotlinx.serialization.decodeFromString import keiyoushi.utils.parseAs
import kotlinx.serialization.json.Json import keiyoushi.utils.tryParse
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -35,7 +41,6 @@ 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.CountDownLatch
@ -51,7 +56,8 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
override val supportsLatest = true override val supportsLatest = true
private val json: Json by injectLazy() override fun headersBuilder() = super.headersBuilder()
.set("Referer", "$baseUrl/")
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.setRandomUserAgent( .setRandomUserAgent(
@ -173,6 +179,7 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
val infoElement = document.select("div.barContent").first()!! val infoElement = document.select("div.barContent").first()!!
val manga = SManga.create() val manga = SManga.create()
manga.title = infoElement.selectFirst("a.bigChar")!!.text()
manga.artist = infoElement.select("p:has(span:contains(Artist:)) > a").first()?.text() manga.artist = infoElement.select("p:has(span:contains(Artist:)) > a").first()?.text()
manga.author = infoElement.select("p:has(span:contains(Writer:)) > a").first()?.text() manga.author = infoElement.select("p:has(span:contains(Writer:)) > a").first()?.text()
manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text() manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text()
@ -211,12 +218,12 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href")) chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text() chapter.name = urlElement.text()
chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let { chapter.date_upload = dateFormat.tryParse(element.selectFirst("td:eq(1)")?.text())
SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0
return chapter return chapter
} }
private val dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault())
override fun pageListRequest(chapter: SChapter): Request { override fun pageListRequest(chapter: SChapter): Request {
val qualitySuffix = if ((qualitypref() != "lq" && serverpref() != "s2") || (qualitypref() == "lq" && serverpref() == "s2")) { val qualitySuffix = if ((qualitypref() != "lq" && serverpref() != "s2") || (qualitypref() == "lq" && serverpref() == "s2")) {
"&s=${serverpref()}&quality=${qualitypref()}&readType=1" "&s=${serverpref()}&quality=${qualitypref()}&readType=1"
@ -232,12 +239,8 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
var webView: WebView? = null var webView: WebView? = null
var images: List<String> = emptyList() var urls: List<List<String>> = emptyList()
val html = document.outerHtml()
val match = KEY_REGEX.find(html)
val key1 = match?.groups?.get(1)?.value ?: throw Exception("Fail to get image links. Logging in via WebView may fix this issue.")
val key2 = match?.groups?.get(2)?.value ?: throw Exception("Fail to get image links. Logging in via WebView may fix this issue.")
handler.post { handler.post {
val innerWv = WebView(Injekt.get<Application>()) val innerWv = WebView(Injekt.get<Application>())
@ -249,14 +252,32 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
innerWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null) innerWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
innerWv.webViewClient = object : WebViewClient() { innerWv.webViewClient = object : WebViewClient() {
override fun onLoadResource(view: WebView?, url: String?) { private val emptyResponse = WebResourceResponse("text/plain", "utf-8", 200, "OK", mapOf(), "".byteInputStream())
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?,
): WebResourceResponse? {
val url = request?.url?.toString()
url ?: return emptyResponse
if (!url.contains("rguard")) {
return emptyResponse
}
return super.shouldInterceptRequest(view, request)
}
override fun onPageFinished(view: WebView?, url: String?) {
view?.evaluateJavascript( view?.evaluateJavascript(
""" Readcomiconline::class.java
window['$key2'].map(i => $key1(i)); .getResourceAsStream("/assets/script.js")!!
""".trimIndent(), .bufferedReader()
.use { it.readText() },
) { ) {
try { try {
images = json.decodeFromString<List<String>>(it) urls = it.parseAs()
latch.countDown() latch.countDown()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("RCO", e.stackTraceToString()) Log.e("RCO", e.stackTraceToString())
@ -286,7 +307,7 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
innerWv.loadDataWithBaseURL( innerWv.loadDataWithBaseURL(
document.location(), document.location(),
html, document.outerHtml(),
"text/html", "text/html",
"UTF-8", "UTF-8",
null, null,
@ -300,6 +321,30 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
throw Exception("Timeout getting image links") throw Exception("Timeout getting image links")
} }
val images = runBlocking {
val valid = urls.map { urlList ->
async {
val request = Request.Builder()
.url(urlList.random())
.method("HEAD", null)
.headers(headers)
.build()
val response = client.newCall(request).await()
val contentType = response.headers["content-type"] ?: ""
val size = response.headers["content-length"]?.toLongOrNull() ?: 0L
response.isSuccessful && contentType.startsWith("image") && size > 100
}
}.awaitAll()
val index = valid.indexOf(true)
if (index == -1) {
throw Exception("No valid image links found")
}
urls[index]
}
return images.mapIndexed { idx, img -> return images.mapIndexed { idx, img ->
Page(idx, imageUrl = img) Page(idx, imageUrl = img)
} }
@ -444,6 +489,5 @@ class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
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 KEY_REGEX = """\.attr\(\s*['"]src['"]\s*,\s*([\w]+)\(\s*([\w]+)\[\s*\w+""".toRegex()
} }
} }