Treat search warnings as errors

This commit is contained in:
FooIbar committed 2025-09-12 21:16:16 +08:00
1 parent 4db96c7bdb
commit 348d45f2f5
6 files changed
+65 -70

No files matched your search

@@ -41,6 +41,7 @@ import com.hippo.ehviewer.client.parser.FavoritesParser
import com.hippo.ehviewer.client.parser.GalleryApiParser
import com.hippo.ehviewer.client.parser.GalleryDetailParser
import com.hippo.ehviewer.client.parser.GalleryListParser
import com.hippo.ehviewer.client.parser.GalleryMultiPageViewerPTokenParser
import com.hippo.ehviewer.client.parser.GalleryPageParser
import com.hippo.ehviewer.client.parser.GalleryTokenApiParser
import com.hippo.ehviewer.client.parser.HomeParser
@@ -62,6 +63,8 @@ import eu.kanade.tachiyomi.util.system.logcat
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.HttpStatement
import io.ktor.client.statement.bodyAsChannel
import io.ktor.client.statement.request
import io.ktor.http.Url
import io.ktor.utils.io.pool.DirectByteBufferPool
import io.ktor.utils.io.pool.useInstance
import io.ktor.utils.io.readAvailable
@@ -86,7 +89,9 @@ private const val MAX_REQUEST_SIZE = 25
private const val MAX_SEQUENTIAL_REQUESTS = 5
private const val REQUEST_INTERVAL = 5000L
fun Either<String, ByteBuffer>.saveParseError(e: Throwable) {
private val Url.isLogin get() = segments.firstOrNull() == "bounce_login.php"
private fun Either<String, ByteBuffer>.saveParseError(e: Throwable) {
val dir = AppConfig.externalParseErrorDir ?: return
val file = File(dir, ReadableTime.getFilenamableTime() + ".txt")
file.sink().buffer().use { sink ->
@@ -98,7 +103,7 @@ fun Either<String, ByteBuffer>.saveParseError(e: Throwable) {
}
}
fun rethrowExactly(response: HttpResponse, body: Either<String, ByteBuffer>, e: Throwable): Nothing {
private fun rethrowExactly(response: HttpResponse, body: Either<String, ByteBuffer>, e: Throwable): Nothing {
// Don't translate coroutine cancellation
if (e is CancellationException) throw e
@@ -148,9 +153,10 @@ fun rethrowExactly(response: HttpResponse, body: Either<String, ByteBuffer>, e:
throw e
}
val httpContentPool = DirectByteBufferPool(8, 0x80000)
private val httpContentPool = DirectByteBufferPool(8, 0x80000)
suspend inline fun <T> HttpStatement.fetchUsingAsText(crossinline block: suspend String.() -> T) = executeSafely { response ->
private suspend inline fun <T> HttpStatement.fetchUsingAsText(crossinline block: suspend String.() -> T) = executeSafely { response ->
if (response.request.url.isLogin) throw NotLoggedInException()
val body = response.bodyAsUtf8Text()
runSuspendCatching {
block(body)
@@ -159,7 +165,8 @@ suspend inline fun <T> HttpStatement.fetchUsingAsText(crossinline block: suspend
}.getOrThrow()
}
suspend inline fun <T> HttpStatement.fetchUsingAsByteBuffer(crossinline block: suspend ByteBuffer.() -> T) = executeSafely { response ->
private suspend inline fun <T> HttpStatement.fetchUsingAsByteBuffer(crossinline block: suspend ByteBuffer.() -> T) = executeSafely { response ->
if (response.request.url.isLogin) throw NotLoggedInException()
httpContentPool.useInstance { buffer ->
with(response.bodyAsChannel()) { while (!isClosedForRead) readAvailable(buffer) }
buffer.flip()
@@ -175,9 +182,12 @@ suspend inline fun <T> HttpStatement.fetchUsingAsByteBuffer(crossinline block: s
object EhEngine {
suspend fun getOriginalImageUrl(url: String, referer: String?) = noRedirectEhRequest(url, referer).executeSafely { response ->
val location = response.headers["Location"] ?: throw InsufficientFundsException()
location.takeIf { "bounce_login" !in it } ?: throw NotLoggedInException()
location.takeUnless { Url(it).isLogin } ?: throw NotLoggedInException()
}
suspend fun getPTokenListFromMpv(gid: Long, token: String) = ehRequest(EhUrl.getGalleryMultiPageViewerUrl(gid, token), EhUrl.referer)
.fetchUsingAsText(GalleryMultiPageViewerPTokenParser::parse)
suspend fun getTorrentList(gid: Long, token: String): TorrentResult {
val url = EhUrl.getTorrentUrl(gid, token)
val referer = EhUrl.getGalleryDetailUrl(gid, token)
@@ -25,15 +25,11 @@ import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhEngine
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.EhUrl.getGalleryDetailUrl
import com.hippo.ehviewer.client.EhUrl.getGalleryMultiPageViewerUrl
import com.hippo.ehviewer.client.EhUrl.referer
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.client.data.GalleryPreview
import com.hippo.ehviewer.client.ehRequest
import com.hippo.ehviewer.client.exception.QuotaExceededException
import com.hippo.ehviewer.client.fetchUsingAsText
import com.hippo.ehviewer.client.parser.GalleryMultiPageViewerPTokenParser
import com.hippo.ehviewer.util.displayString
import com.hippo.files.find
import eu.kanade.tachiyomi.util.system.logcat
@@ -342,17 +338,11 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
suspend fun getPTokenFromMultiPageViewer(index: Int): String? {
if (!isMpvAvailable) return null
val url = getGalleryMultiPageViewerUrl(
galleryInfo.gid,
galleryInfo.token,
)
return runSuspendCatching {
ehRequest(url, referer).fetchUsingAsText {
GalleryMultiPageViewerPTokenParser.parse(this).forEachIndexed { index, s ->
spiderInfo.pTokenMap[index] = s
}
spiderInfo.pTokenMap[index]
EhEngine.getPTokenListFromMpv(galleryInfo.gid, galleryInfo.token).forEachIndexed { index, s ->
spiderInfo.pTokenMap[index] = s
}
spiderInfo.pTokenMap[index]
}.getOrElse {
logcat(it)
null
+4 -4
View File
@@ -29,8 +29,8 @@ use tl::{ParserOptions, VDom};
#[jni_fn("com.hippo.ehviewer.client.parser.FavoritesParserKt")]
pub fn parseFav(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jint {
parse_marshal_inplace(&mut env, input, limit, |dom, html| {
parse_fav(dom, dom.parser(), html)
parse_marshal_inplace(&mut env, input, limit, |dom, _| {
parse_fav(dom, dom.parser())
})
}
@@ -43,8 +43,8 @@ pub fn parseLimit(mut env: JNIEnv, _class: JClass, input: JByteBuffer, limit: ji
#[jni_fn("com.hippo.ehviewer.client.parser.GalleryListParserKt")]
pub fn parseGalleryInfoList(mut env: JNIEnv, _: JClass, buffer: JByteBuffer, limit: jint) -> jint {
parse_marshal_inplace(&mut env, buffer, limit, |dom, str| {
parse_info_list(dom, dom.parser(), str)
parse_marshal_inplace(&mut env, buffer, limit, |dom, _| {
parse_info_list(dom, dom.parser())
})
}
+2 -6
View File
@@ -1,4 +1,3 @@
use crate::EhError;
use crate::parser::list::{GalleryListResult, parse_info_list};
use anyhow::{Result, bail};
use quick_xml::escape::unescape;
@@ -14,10 +13,7 @@ pub struct FavResult {
galleryListResult: GalleryListResult,
}
pub fn parse_fav(dom: &VDom, parser: &Parser, html: &str) -> Result<FavResult> {
if html.contains("This page requires you to log on.</p>") {
bail!(EhError::NeedLogin)
}
pub fn parse_fav(dom: &VDom, parser: &Parser) -> Result<FavResult> {
let vec: Vec<(String, i32)> = dom
.get_elements_by_class_name("fp")
.enumerate()
@@ -38,7 +34,7 @@ pub fn parse_fav(dom: &VDom, parser: &Parser, html: &str) -> Result<FavResult> {
})
.collect();
if vec.len() == 10 {
let list = parse_info_list(dom, parser, html)?;
let list = parse_info_list(dom, parser)?;
let cat = vec.iter().cloned().unzip();
Ok(FavResult {
catArray: cat.0,
+39 -40
View File
@@ -194,47 +194,46 @@ fn parse_gallery_info(node: &Node, parser: &Parser) -> Option<BaseGalleryInfo> {
})
}
pub fn parse_info_list(dom: &VDom, parser: &Parser, str: &str) -> Result<GalleryListResult> {
if str.contains("<p>You do not have any watched tags") {
bail!(EhError::NoWatched)
}
if str.contains("No hits found</p>") || str.contains("No unfiltered results") {
let e = get_vdom_first_element_by_class_name(dom, "searchwarn")
.map_or(EhError::NoHits, |n| {
EhError::Error(n.inner_text(parser).to_string())
});
pub fn parse_info_list(dom: &VDom, parser: &Parser) -> Result<GalleryListResult> {
if let Some(n) = get_vdom_first_element_by_class_name(dom, "searchwarn") {
let text = n.inner_text(parser);
let e = if text.contains("You do not have any watched tags") {
EhError::NoWatched
} else {
EhError::Error(text.to_string())
};
bail!(e)
}
let f = || {
let itg = get_vdom_first_element_by_class_name(dom, "itg")?;
let children = itg.children()?;
let iter = children.top().iter();
let info: Vec<BaseGalleryInfo> = iter
.filter_map(|x| parse_gallery_info(x.get(parser)?, parser))
.collect();
let prev = dom.get_element_by_id("uprev").and_then(|e| {
let str = get_node_handle_attr(&e, parser, "href")?;
Some(
regex!("prev=(\\d+(-\\d+)?)")
.captures(str)?
.index(1)
.to_string(),
)
});
let next = dom.get_element_by_id("unext").and_then(|e| {
let str = get_node_handle_attr(&e, parser, "href")?;
Some(
regex!("next=(\\d+(-\\d+)?)")
.captures(str)?
.index(1)
.to_string(),
)
});
(!info.is_empty()).then_some(GalleryListResult {
prev,
next,
galleryInfoList: info,
let info = get_vdom_first_element_by_class_name(dom, "itg")
.map(|itg| {
let children = itg.children().unwrap();
let iter = children.top().iter();
iter.filter_map(|x| parse_gallery_info(x.get(parser)?, parser))
.collect::<Vec<_>>()
})
};
f().context("No content")
.filter(|v| !v.is_empty())
.context(EhError::NoHits)?;
let prev = dom.get_element_by_id("uprev").and_then(|e| {
let str = get_node_handle_attr(&e, parser, "href")?;
Some(
regex!("prev=(\\d+(-\\d+)?)")
.captures(str)?
.index(1)
.to_string(),
)
});
let next = dom.get_element_by_id("unext").and_then(|e| {
let str = get_node_handle_attr(&e, parser, "href")?;
Some(
regex!("next=(\\d+(-\\d+)?)")
.captures(str)?
.index(1)
.to_string(),
)
});
Ok(GalleryListResult {
prev,
next,
galleryInfoList: info,
})
}
+1 -1
View File
@@ -14,7 +14,7 @@ mod tests {
let resp = get("https://e-hentai.org/").await.expect("Failed to get!");
let body = resp.text().await.expect("Failed to receive!");
let dom = tl::parse(&body, ParserOptions::default()).expect("Failed to parse html");
let result = parse_info_list(&dom, dom.parser(), &body).expect("Failed to parse info list");
let result = parse_info_list(&dom, dom.parser()).expect("Failed to parse info list");
dbg!(result);
}