Remove jsoup usage and clean up

This commit is contained in:
FooIbar committed 2025-06-14 19:21:35 +08:00
1 parent 5dc02c0bd5
commit 95631ef468
17 files changed
+46 -57

No files matched your search

-1
View File
@@ -98,7 +98,6 @@ Here is the libraries
- [Coil](https://coil-kt.github.io/coil/) - [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/) - [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
- [libarchive](https://www.libarchive.org/) - [libarchive](https://www.libarchive.org/)
- [jsoup](https://jsoup.org/)
# License # License
-1
View File
@@ -240,7 +240,6 @@ dependencies {
implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.datetime)
implementation(libs.jsoup)
coreLibraryDesugaring(libs.desugar) coreLibraryDesugaring(libs.desugar)
@@ -178,7 +178,8 @@ object EhEngine {
location.takeIf { "bounce_login" !in it } ?: throw NotLoggedInException() location.takeIf { "bounce_login" !in it } ?: throw NotLoggedInException()
} }
suspend fun getTorrentList(url: String, gid: Long, token: String): TorrentResult { suspend fun getTorrentList(gid: Long, token: String): TorrentResult {
val url = EhUrl.getTorrentUrl(gid, token)
val referer = EhUrl.getGalleryDetailUrl(gid, token) val referer = EhUrl.getGalleryDetailUrl(gid, token)
return ehRequest(url, referer).fetchUsingAsByteBuffer(TorrentParser::parse) return ehRequest(url, referer).fetchUsingAsByteBuffer(TorrentParser::parse)
} }
@@ -188,7 +189,8 @@ object EhEngine {
"${id}x$key" "${id}x$key"
} }
suspend fun getArchiveList(url: String, gid: Long, token: String): ArchiveParser.Result { suspend fun getArchiveList(gid: Long, token: String): ArchiveParser.Result {
val url = EhUrl.getArchiveUrl(gid, token)
val funds = if (EhUtils.isExHentai) getFunds() else null val funds = if (EhUtils.isExHentai) getFunds() else null
return ehRequest(url, EhUrl.getGalleryDetailUrl(gid, token)) return ehRequest(url, EhUrl.getGalleryDetailUrl(gid, token))
.fetchUsingAsByteBuffer(ArchiveParser::parse.partially2(funds)) .fetchUsingAsByteBuffer(ArchiveParser::parse.partially2(funds))
@@ -304,7 +306,7 @@ object EhEngine {
.fetchUsingAsText(FavoritesParser::parseNote) .fetchUsingAsText(FavoritesParser::parseNote)
suspend fun downloadArchive(gid: Long, token: String, res: String, isHath: Boolean): String? { suspend fun downloadArchive(gid: Long, token: String, res: String, isHath: Boolean): String? {
val url = EhUrl.getDownloadArchive(gid, token) val url = EhUrl.getArchiveUrl(gid, token)
val referer = EhUrl.getGalleryDetailUrl(gid, token) val referer = EhUrl.getGalleryDetailUrl(gid, token)
val request = ehRequest(url, referer, EhUrl.origin) { val request = ehRequest(url, referer, EhUrl.origin) {
formBody { formBody {
@@ -125,7 +125,12 @@ object EhUrl {
addQueryParameter("act", "addfav") addQueryParameter("act", "addfav")
}.buildString() }.buildString()
fun getDownloadArchive(gid: Long, token: String) = ehUrl("archiver.php") { fun getArchiveUrl(gid: Long, token: String) = ehUrl("archiver.php") {
addQueryParameter("gid", "$gid")
addQueryParameter("token", token)
}.buildString()
fun getTorrentUrl(gid: Long, token: String) = ehUrl("gallerytorrents.php") {
addQueryParameter("gid", "$gid") addQueryParameter("gid", "$gid")
addQueryParameter("token", token) addQueryParameter("token", token)
}.buildString() }.buildString()
@@ -44,8 +44,6 @@ data class GalleryDetail(
var apiUid: Long = -1L, var apiUid: Long = -1L,
var apiKey: String? = null, var apiKey: String? = null,
var torrentCount: Int = 0, var torrentCount: Int = 0,
var torrentUrl: String? = null,
var archiveUrl: String? = null,
var parent: String? = null, var parent: String? = null,
var newerVersions: List<BaseGalleryInfo> = emptyList(), var newerVersions: List<BaseGalleryInfo> = emptyList(),
var visible: String? = null, var visible: String? = null,
@@ -25,9 +25,9 @@ object HomeParser {
?: throw ParseException("Parse torrent key error") ?: throw ParseException("Parse torrent key error")
fun parseFunds(body: String): Funds { fun parseFunds(body: String): Funds {
FundsRegex.find(body)?.groupValues?.run { FundsRegex.find(body)?.run {
val fundsC = ParserUtils.parseInt(get(1), 0) val fundsC = ParserUtils.parseInt(groupValues[1], 0)
val fundsGP = ParserUtils.parseInt(get(2), 0) val fundsGP = ParserUtils.parseInt(groupValues[2], 0)
return Funds(fundsGP, fundsC) return Funds(fundsGP, fundsC)
} }
throw ParseException("Parse funds error") throw ParseException("Parse funds error")
@@ -18,8 +18,6 @@ package com.hippo.ehviewer.client.parser
import com.hippo.ehviewer.util.toEpochMillis import com.hippo.ehviewer.util.toEpochMillis
import com.hippo.ehviewer.util.toIntOrDefault import com.hippo.ehviewer.util.toIntOrDefault
import com.hippo.ehviewer.util.toLocalDateTime import com.hippo.ehviewer.util.toLocalDateTime
import com.hippo.ehviewer.util.toLongOrDefault
import com.hippo.ehviewer.util.unescapeXml
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.format.char import kotlinx.datetime.format.char
@@ -41,9 +39,5 @@ object ParserUtils {
fun parseDate(str: String): Long = formatter.parse(str).toEpochMillis() fun parseDate(str: String): Long = formatter.parse(str).toEpochMillis()
fun trim(str: String?): String = str?.unescapeXml()?.trim().orEmpty() fun parseInt(str: String, defValue: Int): Int = str.replace(",", "").toIntOrDefault(defValue)
fun parseInt(str: String?, defValue: Int): Int = trim(str).replace(",", "").toIntOrDefault(defValue)
fun parseLong(str: String?, defValue: Long): Long = trim(str).replace(",", "").toLongOrDefault(defValue)
} }
@@ -58,7 +58,7 @@ private fun GalleryDetail.content() = arrayOf(
R.string.key_rating_count to ratingCount.toString(), R.string.key_rating_count to ratingCount.toString(),
R.string.key_rating to rating.toString(), R.string.key_rating to rating.toString(),
R.string.key_torrents to torrentCount.toString(), R.string.key_torrents to torrentCount.toString(),
R.string.key_torrent_url to torrentUrl, R.string.key_torrent_url to EhUrl.getTorrentUrl(gid, token),
R.string.favorite_name to favoriteName, R.string.favorite_name to favoriteName,
) )
@@ -551,7 +551,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) {
val archiveResult = remember(galleryDetail) { val archiveResult = remember(galleryDetail) {
async(Dispatchers.IO + Job(), CoroutineStart.LAZY) { async(Dispatchers.IO + Job(), CoroutineStart.LAZY) {
with(galleryDetail) { with(galleryDetail) {
EhEngine.getArchiveList(archiveUrl!!, gid, token) EhEngine.getArchiveList(gid, token)
} }
} }
} }
@@ -598,7 +598,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) {
val torrentResult = remember(galleryDetail) { val torrentResult = remember(galleryDetail) {
async(Dispatchers.IO + Job(), CoroutineStart.LAZY) { async(Dispatchers.IO + Job(), CoroutineStart.LAZY) {
parZip( parZip(
{ EhEngine.getTorrentList(galleryDetail.torrentUrl!!, galleryDetail.gid, galleryDetail.token) }, { EhEngine.getTorrentList(galleryDetail.gid, galleryDetail.token) },
{ EhEngine.getTorrentKey() }, { EhEngine.getTorrentKey() },
{ list, key -> list to key }, { list, key -> list to key },
) )
@@ -1,15 +1,16 @@
package com.hippo.ehviewer.util package com.hippo.ehviewer.util
import org.jsoup.parser.Parser private val EntityRegex = "&(?:amp|lt|gt|quot|#039);".toRegex()
private val EntityMap = mapOf(
"&amp;" to "&",
"&lt;" to "<",
"&gt;" to ">",
"&quot;" to "\"",
"&#039;" to "'", // HTML uses &#039; instead of &apos;
)
fun String.unescapeXml(): String = Parser.unescapeEntities(this, true) fun String.unescapeXml() = replace(EntityRegex) { EntityMap[it.value]!! }
inline infix fun <T> CharSequence.trimAnd(block: CharSequence.() -> T): T = block(trim())
fun String.toIntOrDefault(defaultValue: Int): Int = toIntOrNull() ?: defaultValue fun String.toIntOrDefault(defaultValue: Int): Int = toIntOrNull() ?: defaultValue
fun String.toLongOrDefault(defaultValue: Long): Long = toLongOrNull() ?: defaultValue
fun String.toFloatOrDefault(defaultValue: Float): Float = toFloatOrNull() ?: defaultValue
fun String?.containsIgnoreCase(other: String) = this?.contains(other, true) == true fun String?.containsIgnoreCase(other: String) = this?.contains(other, true) == true
+7 -15
View File
@@ -14,8 +14,6 @@ pub struct GalleryDetail {
pub apiUid: i64, pub apiUid: i64,
pub apiKey: Option<String>, pub apiKey: Option<String>,
pub torrentCount: i32, pub torrentCount: i32,
pub torrentUrl: Option<String>,
pub archiveUrl: Option<String>,
pub parent: Option<String>, pub parent: Option<String>,
pub newerVersions: Vec<BaseGalleryInfo>, pub newerVersions: Vec<BaseGalleryInfo>,
pub visible: Option<String>, pub visible: Option<String>,
@@ -148,22 +146,16 @@ fn parse_detail(gd: &mut GalleryDetail, dom: &VDom, parser: &Parser, body: &str)
gd.apiKey = Some(caps[4].to_string()); gd.apiKey = Some(caps[4].to_string());
gd.galleryInfo.rating = caps[5].parse().ok()?; gd.galleryInfo.rating = caps[5].parse().ok()?;
let torrent_regex = regex!( let regex = regex!(r#"[^(]+\(([^)]+)"#);
r#"<a[^<>]*onclick="return popUp\('([^']+)'[^)]+\)">Torrent Download \((\d+)\)</a>"# let gd5 = dom.get_element_by_id("gd5")?.get(parser)?.as_tag()?;
); let node = gd5.query_selector(parser, "a[onclick]")?.nth(1)?;
let caps = torrent_regex.captures(body)?; let text = node.get(parser)?.inner_text(parser);
gd.torrentUrl = unescape(&caps[1]).ok().map(|s| s.to_string()); let caps = regex.captures(&text)?;
gd.torrentCount = caps[2].parse().ok()?; gd.torrentCount = caps[1].parse().ok()?;
let archive_regex =
regex!(r#"<a[^<>]*onclick="return popUp\('([^']+)'[^)]+\)">Archive Download</a>"#);
let caps = archive_regex.captures(body)?;
gd.archiveUrl = unescape(&caps[1]).ok().map(|s| s.to_string());
let gd1 = dom.get_element_by_id("gd1")?.get(parser)?.as_tag()?; let gd1 = dom.get_element_by_id("gd1")?.get(parser)?.as_tag()?;
let style = get_tag_attr(get_first_child(gd1, parser)?, "style")?; let style = get_tag_attr(get_first_child(gd1, parser)?, "style")?;
let cover_regex = regex!(r#"[^(]+\(([^)]+)"#); let caps = regex.captures(style)?;
let caps = cover_regex.captures(style)?;
gd.galleryInfo.thumbKey = get_thumb_key(&caps[1]); gd.galleryInfo.thumbKey = get_thumb_key(&caps[1]);
let title = dom.get_element_by_id("gn")?.get(parser)?.inner_text(parser); let title = dom.get_element_by_id("gn")?.get(parser)?.inner_text(parser);
+9 -8
View File
@@ -96,11 +96,8 @@ fn parse_uploader_and_pages(str: &str) -> (Option<String>, bool, i32) {
regex!(r#"<a href="https://e[x-]hentai.org/uploader/.*?">(.*?)</a>|(\(Disowned\))"#) regex!(r#"<a href="https://e[x-]hentai.org/uploader/.*?">(.*?)</a>|(\(Disowned\))"#)
.captures(str) .captures(str)
.map(|grp| { .map(|grp| {
grp.get(1) let str = grp.get(1).or_else(|| grp.get(2)).unwrap().as_str();
.or_else(|| grp.get(2)) unescape(str).as_deref().unwrap_or(str).to_string()
.unwrap()
.as_str()
.to_string()
}); });
let pages = match regex!(r"<div>(\d+) pages</div>").captures(str) { let pages = match regex!(r"<div>(\d+) pages</div>").captures(str) {
None => 0, None => 0,
@@ -155,7 +152,8 @@ fn parse_gallery_info(node: &Node, parser: &Parser) -> Option<BaseGalleryInfo> {
None => ("".to_string(), None), None => ("".to_string(), None),
Some(node) => ( Some(node) => (
node.inner_text(parser).trim().to_string(), node.inner_text(parser).trim().to_string(),
get_node_attr(node, "title").map(str::to_string), get_node_attr(node, "title")
.map(|s| unescape(s).as_deref().unwrap_or(s).to_string()),
), ),
}; };
let ir = get_first_element_by_class_name(node, parser, "ir")? let ir = get_first_element_by_class_name(node, parser, "ir")?
@@ -168,8 +166,11 @@ fn parse_gallery_info(node: &Node, parser: &Parser) -> Option<BaseGalleryInfo> {
None => (None, false, 0), None => (None, false, 0),
Some(node) => parse_uploader_and_pages(&node.get(parser)?.inner_html(parser)), Some(node) => parse_uploader_and_pages(&node.get(parser)?.inner_html(parser)),
}; };
let favorite_note = get_element_by_id(node, parser, format!("favnote_{gid}").as_str()) let favorite_note =
.map(|e| e.inner_text(parser).to_string()); get_element_by_id(node, parser, format!("favnote_{gid}").as_str()).map(|e| {
let str = e.inner_text(parser);
unescape(&str).as_deref().unwrap_or(&str).to_string()
});
Some(BaseGalleryInfo { Some(BaseGalleryInfo {
gid, gid,
token, token,
+3 -1
View File
@@ -1,5 +1,6 @@
use crate::{EhError, get_node_handle_attr, get_tag_attr, select_first}; use crate::{EhError, get_node_handle_attr, get_tag_attr, select_first};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use quick_xml::escape::unescape;
use serde::Serialize; use serde::Serialize;
use tl::{Parser, VDom}; use tl::{Parser, VDom};
@@ -26,7 +27,8 @@ pub fn parse_profile(dom: &VDom, parser: &Parser) -> Result<Profile> {
.next() .next()
.and_then(|n| { .and_then(|n| {
let mut iter = n.get(parser)?.as_tag()?.query_selector(parser, "div")?; let mut iter = n.get(parser)?.as_tag()?.query_selector(parser, "div")?;
let display_name = iter.next()?.get(parser)?.inner_text(parser).to_string(); let str = iter.next()?.get(parser)?.inner_text(parser);
let display_name = unescape(&str).as_deref().unwrap_or(&str).to_string();
let avatar = iter.next().and_then(|n| { let avatar = iter.next().and_then(|n| {
let img = select_first(n.get(parser)?.as_tag()?, parser, "img")?; let img = select_first(n.get(parser)?.as_tag()?, parser, "img")?;
get_tag_attr(img, "src").map(|src| { get_tag_attr(img, "src").map(|src| {
-1
View File
@@ -98,7 +98,6 @@
- [Coil](https://coil-kt.github.io/coil/) - [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/) - [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
- [libarchive](https://www.libarchive.org/) - [libarchive](https://www.libarchive.org/)
- [jsoup](https://jsoup.org/)
# ライセンス # ライセンス
-1
View File
@@ -97,7 +97,6 @@
- [Coil](https://coil-kt.github.io/coil/) - [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/) - [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
- [libarchive](https://www.libarchive.org/) - [libarchive](https://www.libarchive.org/)
- [jsoup](https://jsoup.org/)
# 许可证 # 许可证
-1
View File
@@ -95,7 +95,6 @@
- [Coil](https://coil-kt.github.io/coil/) - [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/) - [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
- [libarchive](https://www.libarchive.org/) - [libarchive](https://www.libarchive.org/)
- [jsoup](https://jsoup.org/)
# 許可證書 # 許可證書
-1
View File
@@ -85,7 +85,6 @@ material-kolor = "com.materialkolor:material-kolor:2.1.1"
desugar = "com.android.tools:desugar_jdk_libs:2.1.5" desugar = "com.android.tools:desugar_jdk_libs:2.1.5"
diff = "io.github.petertrr:kotlin-multiplatform-diff:1.1.0" diff = "io.github.petertrr:kotlin-multiplatform-diff:1.1.0"
jsoup = "org.jsoup:jsoup:1.20.1"
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }