From 751a89c1070d4b103327be2ef0b35076567092cc Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 20 Jun 2023 21:19:14 +0800 Subject: [PATCH] Refine rust gallery list parser --- .../client/parser/GalleryListParser.kt | 29 ++++-- app/src/main/rust/catch_panic/src/handler.rs | 2 +- app/src/main/rust/src/parser/list.rs | 98 +++++++++++++------ 3 files changed, 92 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt index 340e4892c..4e5bce2c2 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.kt @@ -17,6 +17,7 @@ package com.hippo.ehviewer.client.parser import android.util.Log import com.hippo.ehviewer.EhDB +import com.hippo.ehviewer.R import com.hippo.ehviewer.client.EhUtils import com.hippo.ehviewer.client.data.BaseGalleryInfo import com.hippo.ehviewer.client.data.GalleryInfo @@ -28,6 +29,7 @@ import com.hippo.ehviewer.yorozuya.NumberUtils import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import splitties.init.appCtx import java.util.regex.Pattern object GalleryListParser { @@ -263,6 +265,7 @@ object GalleryListParser { fun parse(body: String): Result { val d = Jsoup.parse(body) + d.outputSettings().prettyPrint(false) return parse(d, body) } @@ -307,18 +310,26 @@ object GalleryListParser { try { val itg = d.getElementsByClass("itg").first() val es = if ("table".equals(itg!!.tagName(), ignoreCase = true)) { - itg.child(0).children() + itg.child(0).children().let { + // First one is table header in non-extended mode, skip it + if (!itg.hasClass("glte")) it.drop(1) else it + } } else { + // Thumbnail mode itg.children() } val list = result.galleryInfoList - // First one is table header, skip it - for (i in es.indices) { - val gi = parseGalleryInfo(es[i]) - if (null != gi) { - list.add(gi) - } - } + list.addAll( + es.mapNotNull { + parseGalleryInfo(it.toString())?.apply { + if (favoriteSlot == -2 && EhDB.containLocalFavorites(gid)) { + favoriteSlot = -1 + favoriteName = appCtx.getString(R.string.local_favorites) + } + generateSLang() + } + }, + ) if (list.isEmpty()) { if (es.size < 2 || NO_UNFILTERED_TEXT != es[1].text()) { Log.d(TAG, "No gallery found") @@ -341,3 +352,5 @@ object GalleryListParser { val galleryInfoList = mutableListOf() } } + +private external fun parseGalleryInfo(e: String): GalleryInfo? diff --git a/app/src/main/rust/catch_panic/src/handler.rs b/app/src/main/rust/catch_panic/src/handler.rs index cf88b42da..2fbf603e6 100644 --- a/app/src/main/rust/catch_panic/src/handler.rs +++ b/app/src/main/rust/catch_panic/src/handler.rs @@ -26,7 +26,7 @@ where /// `#[catch_panic]`'s default panic handler. This /// will rethrow all caught panics as java `RuntimeException`s /// with the message passed to `panic!`. -pub fn default_handler(mut env: JNIEnv, err: Box) { +pub fn default_handler(env: JNIEnv, err: Box) { let msg = match err.downcast_ref::<&'static str>() { Some(s) => *s, None => match err.downcast_ref::() { diff --git a/app/src/main/rust/src/parser/list.rs b/app/src/main/rust/src/parser/list.rs index ec1b0b013..ac727a10f 100644 --- a/app/src/main/rust/src/parser/list.rs +++ b/app/src/main/rust/src/parser/list.rs @@ -1,16 +1,18 @@ +use std::borrow::Cow; + use catch_panic::catch_panic; use html_escape::decode_html_entities; use jni_fn::jni_fn; +use jnix::{IntoJava, JnixEnv}; +use jnix::jni::JNIEnv; use jnix::jni::objects::{JClass, JString}; use jnix::jni::sys::jobject; -use jnix::jni::JNIEnv; -use jnix::{IntoJava, JnixEnv}; use jnix_macros::IntoJava; -use query_childs_first_match_attr; -use std::borrow::Cow; -use {get_node_handle_attr, regex}; -use {parse_jni_string, Anon}; + +use {get_node_attr, get_node_handle_attr, regex}; +use {Anon, parse_jni_string}; use {EHGT_PREFIX, EX_PREFIX}; +use query_childs_first_match_attr; #[derive(Default, IntoJava)] #[allow(non_snake_case)] @@ -31,7 +33,7 @@ pub struct BaseGalleryInfo { pages: i32, thumbWidth: i32, thumbHeight: i32, - simpleLanguage: String, + simpleLanguage: Option, favoriteSlot: i32, favoriteName: Option, } @@ -95,6 +97,32 @@ fn parse_token_and_gid(str: &str) -> (i64, String) { (gid, token.to_string()) } +fn parse_uploader_and_pages(str: &str) -> (Option, bool, i32) { + let reg_uploader = regex!( + r#""# + ); + let reg_pages = regex!( + r"
(\d+) pages
" + ); + let uploader = reg_uploader.captures(str).map(|grp| grp[1].to_string()); + let pages = match reg_pages.captures(str) { + None => 0, + Some(grp) => grp[1].parse().unwrap(), + }; + (uploader, str.contains("style=\"opacity:0.5\""), pages) +} + +fn parse_thumb_resolution(str: &str) -> (i32, i32) { + let reg = regex!( + r"height:(\d+)px;width:(\d+)px" + ); + match reg.captures(str) { + None => panic!("regex err".to_owned() + str), + //None => (0, 0), + Some(grp) => (grp[1].parse().unwrap(), grp[2].parse().unwrap()), + } +} + #[no_mangle] #[catch_panic(default = "std::ptr::null_mut()")] #[allow(non_snake_case)] @@ -114,12 +142,21 @@ pub fn parseGalleryInfo(env: JNIEnv, _class: JClass, input: JString) -> jobject }, }; let (gid, token) = parse_token_and_gid(gdlink); - let thumb = match dom.query_selector("[data-src]")?.next() { + let simpleTags = dom.query_selector(".gt, .gtl")?.filter_map(|tag| { + get_node_handle_attr(&tag, parser, "title").map(str::to_string) + }).collect(); + let (thumb, (thumbHeight, thumbWidth)) = match dom.query_selector("[data-src]")?.next() { None => match dom.query_selector("[src]")?.next() { None => panic!("No thumb found"), - Some(thumb) => get_node_handle_attr(&thumb, parser, "src")?, + Some(thumb) => ( + get_node_handle_attr(&thumb, parser, "src")?, + parse_thumb_resolution(get_node_handle_attr(&thumb, parser, "style")?), + ) }, - Some(thumb) => get_node_handle_attr(&thumb, parser, "data-src")?, + Some(thumb) => ( + get_node_handle_attr(&thumb, parser, "data-src")?, + parse_thumb_resolution(get_node_handle_attr(&thumb, parser, "style")?), + ) }; let category = match dom.get_first_element_by_class_name("cn") { None => match dom.get_first_element_by_class_name("cs") { @@ -128,8 +165,17 @@ pub fn parseGalleryInfo(env: JNIEnv, _class: JClass, input: JString) -> jobject }, Some(cn) => cn.inner_text(parser), }; - let ir = dom.get_first_element_by_class_name("ir")?; - let rating = ir.as_tag()?.attributes().get("style")??.try_as_utf8_str()?; + let (posted, favoriteName) = match dom.get_element_by_id(format!("posted_{gid}").as_str())?.get(parser) { + None => ("".to_string(), None), + Some(node) => (node.inner_text(parser).trim().to_string(), get_node_attr(&node, "title").map(str::to_string)), + }; + let ir = dom.get_first_element_by_class_name("ir")?.as_tag()?.attributes(); + let ir_c = ir.class()?.try_as_utf8_str()?; + let rating = ir.get("style")??.try_as_utf8_str()?; + let (uploader, disowned, pages) = match dom.query_selector(".glhide, .gl3e, .gl5t")?.next() { + None => (None, false, 0), + Some(node) => parse_uploader_and_pages(&node.get(parser)?.inner_html(parser)), + }; Some(BaseGalleryInfo { gid, token, @@ -137,22 +183,18 @@ pub fn parseGalleryInfo(env: JNIEnv, _class: JClass, input: JString) -> jobject titleJpn: None, thumbKey: get_thumb_key(thumb), category: to_category_i32(&category.trim().to_lowercase()), - posted: "".to_string(), - uploader: None, - disowned: false, + posted, + uploader, + disowned, rating: parse_rating(rating), - rated: false, - simpleTags: vec![], - pages: 0, - thumbWidth: 0, - thumbHeight: 0, - simpleLanguage: "".to_string(), - favoriteSlot: 0, - favoriteName: None, + rated: ir_c.contains("irr") || ir_c.contains("irg") || ir_c.contains("irb"), + simpleTags, + pages, + thumbWidth, + thumbHeight, + simpleLanguage: None, + favoriteSlot: if favoriteName.is_some() { 0 } else { -2 }, + favoriteName, }) - }) - .unwrap() - .into_java(&env) - .forget() - .into_raw() + }).unwrap().into_java(&env).forget().into_raw() }