Clean up ExceptionUtils with Either.catch

This commit is contained in:
FooIbar committed 2024-10-23 15:20:43 +08:00
1 parent f301eab5b1
commit ca324995b3
7 files changed
+125 -171

No files matched your search

@@ -15,19 +15,19 @@
*/
package com.hippo.ehviewer.client.parser
import arrow.core.Either
import arrow.core.getOrElse
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.exception.NotLoggedInException
import com.hippo.ehviewer.util.ExceptionUtils
import org.jsoup.Jsoup
object ForumsParser {
fun parse(body: String): String = runCatching {
fun parse(body: String): String = Either.catch {
val d = Jsoup.parse(body, EhUrl.URL_FORUMS)
val userlinks = d.getElementById("userlinks")
val child = userlinks!!.child(0).child(0).child(0)
child.attr("href")
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
throw NotLoggedInException()
}
}
@@ -15,12 +15,13 @@
*/
package com.hippo.ehviewer.client.parser
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.unzip
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhFilter
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.EhUtils.getCategory
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.data.GalleryComment
import com.hippo.ehviewer.client.data.GalleryCommentList
@@ -37,7 +38,6 @@ import com.hippo.ehviewer.client.exception.ParseException
import com.hippo.ehviewer.client.exception.PiningException
import com.hippo.ehviewer.client.getNormalPreviewKey
import com.hippo.ehviewer.client.getThumbKey
import com.hippo.ehviewer.util.ExceptionUtils
import com.hippo.ehviewer.util.toEpochMillis
import com.hippo.ehviewer.util.toFloatOrDefault
import com.hippo.ehviewer.util.toIntOrDefault
@@ -136,7 +136,7 @@ object GalleryDetailParser {
PATTERN_ARCHIVE.find(body)?.run {
gd.archiveUrl = groupValues[1].trim().unescapeXml()
}
try {
Either.catch {
val gm = d.getElementsByClass("gm")[0]
// Thumb url
gm.getElementById("gd1")?.child(0)?.attr("style")?.trim()?.let {
@@ -147,16 +147,15 @@ object GalleryDetailParser {
gd.titleJpn = gm.getElementById("gj")?.text()?.trim()
// Category
try {
gd.category = Either.catch {
val gdc = gm.getElementById("gdc")!!
var ce = gdc.getElementsByClass("cn").first()
if (ce == null) {
ce = gdc.getElementsByClass("cs").first()
}
gd.category = getCategory(ce!!.text())
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
gd.category = EhUtils.UNKNOWN
EhUtils.getCategory(ce!!.text())
}.getOrElse {
EhUtils.UNKNOWN
}
// Uploader
@@ -219,8 +218,7 @@ object GalleryDetailParser {
if (gd.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(gd.gid)) {
gd.favoriteSlot = LOCAL_FAVORITED
}
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
}.getOrElse {
throw ParseException("Can't parse gallery detail")
}
@@ -279,7 +277,7 @@ object GalleryDetailParser {
}
}
private fun parseTagGroup(element: Element): GalleryTagGroup? = try {
private fun parseTagGroup(element: Element): GalleryTagGroup? = Either.catch {
var nameSpace = element.child(0).text()
// Remove last ':'
nameSpace = nameSpace.substring(0, nameSpace.length - 1)
@@ -297,143 +295,134 @@ object GalleryDetailParser {
}
group.add(tag)
}
if (group.size > 0) group else null
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
logcat(e)
group.takeIf { it.isNotEmpty() }
}.getOrElse {
logcat(it)
null
}
/**
* Parse tag groups with html parser
*/
private fun parseTagGroups(document: Document): List<GalleryTagGroup> = try {
private fun parseTagGroups(document: Document): List<GalleryTagGroup> = Either.catch {
val taglist = document.getElementById("taglist")!!
val tagGroups = taglist.child(0).child(0).children()
parseTagGroups(tagGroups)
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
logcat(e)
}.getOrElse {
logcat(it)
emptyList()
}
private fun parseTagGroups(trs: Elements): List<GalleryTagGroup> = try {
private fun parseTagGroups(trs: Elements): List<GalleryTagGroup> = Either.catch {
trs.mapNotNull { parseTagGroup(it) }
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
logcat(e)
}.getOrElse {
logcat(it)
emptyList()
}
private suspend fun parseComment(element: Element): GalleryComment? {
return try {
// Id
val a = element.previousElementSibling()
val name = a!!.attr("name")
val id = name trimAnd { substring(1).toInt().toLong() }
// Editable, vote up and vote down
val c4 = element.getElementsByClass("c4").first()
var voteUpAble = false
var voteUpEd = false
var voteDownAble = false
var voteDownEd = false
var editable = false
var uploader = false
if (null != c4) {
if ("Uploader Comment" == c4.text()) {
uploader = true
}
for (e in c4.children()) {
when (e.text()) {
"Vote+" -> {
voteUpAble = true
voteUpEd = e.attr("style").trim().isNotEmpty()
}
"Vote-" -> {
voteDownAble = true
voteDownEd = e.attr("style").trim().isNotEmpty()
}
"Edit" -> editable = true
private suspend fun parseComment(element: Element): GalleryComment? = Either.catch {
// Id
val a = element.previousElementSibling()
val name = a!!.attr("name")
val id = name trimAnd { substring(1).toInt().toLong() }
// Editable, vote up and vote down
val c4 = element.getElementsByClass("c4").first()
var voteUpAble = false
var voteUpEd = false
var voteDownAble = false
var voteDownEd = false
var editable = false
var uploader = false
if (null != c4) {
if ("Uploader Comment" == c4.text()) {
uploader = true
}
for (e in c4.children()) {
when (e.text()) {
"Vote+" -> {
voteUpAble = true
voteUpEd = e.attr("style").trim().isNotEmpty()
}
"Vote-" -> {
voteDownAble = true
voteDownEd = e.attr("style").trim().isNotEmpty()
}
"Edit" -> editable = true
}
}
// Vote state
val c7 = element.getElementsByClass("c7").first()
val voteState = c7?.text()?.trim()
// Score
val c5 = element.getElementsByClass("c5").first()
val score = c5?.children()?.first()?.text()?.trim()?.toIntOrDefault(0) ?: 0
// time
val c3 = element.getElementsByClass("c3").first()
val temp = c3!!.ownText()
val (timeStr, user) = if (temp.endsWith(':')) {
temp.substring("Posted on ".length, temp.length - " by:".length) to c3.child(0).text()
} else {
temp.substring("Posted on ".length) to null
}
val time = WEB_COMMENT_DATE_FORMAT.parse(timeStr).toEpochMillis()
// comment
val c6 = element.getElementsByClass("c6").first()
// fix underline support
for (e in c6!!.children()) {
if ("span" == e.tagName() && "text-decoration:underline;" == e.attr("style")) {
e.tagName("u")
}
}
val commentHtml = c6.html()
// filter comment
if (!uploader) {
val sEhFilter = EhFilter
if (score <= Settings.commentThreshold || sEhFilter.filterCommenter(user.orEmpty()) || sEhFilter.filterComment(commentHtml)) {
return null
}
}
// last edited
val c8 = element.getElementsByClass("c8").first()
val lastEdited = c8?.children()?.first()?.run { WEB_COMMENT_DATE_FORMAT.parse(text()).toEpochMillis() } ?: 0
GalleryComment(
id, score, editable, voteUpAble, voteUpEd, voteDownAble,
voteDownEd, uploader, voteState, time, user, commentHtml, lastEdited,
)
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
logcat(e)
null
}
// Vote state
val c7 = element.getElementsByClass("c7").first()
val voteState = c7?.text()?.trim()
// Score
val c5 = element.getElementsByClass("c5").first()
val score = c5?.children()?.first()?.text()?.trim()?.toIntOrDefault(0) ?: 0
// time
val c3 = element.getElementsByClass("c3").first()
val temp = c3!!.ownText()
val (timeStr, user) = if (temp.endsWith(':')) {
temp.substring("Posted on ".length, temp.length - " by:".length) to c3.child(0).text()
} else {
temp.substring("Posted on ".length) to null
}
val time = WEB_COMMENT_DATE_FORMAT.parse(timeStr).toEpochMillis()
// comment
val c6 = element.getElementsByClass("c6").first()
// fix underline support
for (e in c6!!.children()) {
if ("span" == e.tagName() && "text-decoration:underline;" == e.attr("style")) {
e.tagName("u")
}
}
val commentHtml = c6.html()
// filter comment
if (!uploader) {
val sEhFilter = EhFilter
if (score <= Settings.commentThreshold || sEhFilter.filterCommenter(user.orEmpty()) || sEhFilter.filterComment(commentHtml)) {
return null
}
}
// last edited
val c8 = element.getElementsByClass("c8").first()
val lastEdited = c8?.children()?.first()?.run { WEB_COMMENT_DATE_FORMAT.parse(text()).toEpochMillis() } ?: 0
GalleryComment(
id, score, editable, voteUpAble, voteUpEd, voteDownAble,
voteDownEd, uploader, voteState, time, user, commentHtml, lastEdited,
)
}.getOrElse {
logcat(it)
null
}
/**
* Parse comments with html parser
*/
suspend fun parseComments(document: Document): GalleryCommentList {
suspend fun parseComments(document: Document): GalleryCommentList = Either.catch {
// Disable pretty print to get comments in raw html
document.outputSettings().prettyPrint(false)
return try {
val cdiv = document.getElementById("cdiv")!!
val c1s = cdiv.getElementsByClass("c1")
val list = c1s.mapNotNull { parseComment(it) }
val chd = cdiv.getElementById("chd")
var hasMore = false
NodeTraversor.traverse(
object : NodeVisitor {
override fun head(node: Node, depth: Int) {
if (node is Element && node.text() == "click to show all") {
hasMore = true
}
val cdiv = document.getElementById("cdiv")!!
val c1s = cdiv.getElementsByClass("c1")
val list = c1s.mapNotNull { parseComment(it) }
val chd = cdiv.getElementById("chd")
var hasMore = false
NodeTraversor.traverse(
object : NodeVisitor {
override fun head(node: Node, depth: Int) {
if (node is Element && node.text() == "click to show all") {
hasMore = true
}
}
override fun tail(node: Node, depth: Int) {}
},
chd!!,
)
GalleryCommentList(list, hasMore)
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
logcat(e)
EMPTY_GALLERY_COMMENT_LIST
}
override fun tail(node: Node, depth: Int) {}
},
chd!!,
)
GalleryCommentList(list, hasMore)
}.getOrElse {
logcat(it)
EMPTY_GALLERY_COMMENT_LIST
}
/**
@@ -17,16 +17,16 @@
*/
package com.hippo.ehviewer.client.parser
import com.hippo.ehviewer.util.ExceptionUtils
import arrow.core.Either
import arrow.core.getOrElse
import eu.kanade.tachiyomi.util.system.logcat
import org.jsoup.Jsoup
object GalleryNotAvailableParser {
fun parse(body: String): String? = runCatching {
fun parse(body: String): String? = Either.catch {
val e = Jsoup.parse(body).getElementsByClass("d").first()
e!!.child(0).html().replace("<br>", "\n")
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
logcat(it)
null
}
@@ -15,9 +15,10 @@
*/
package com.hippo.ehviewer.client.parser
import arrow.core.Either
import arrow.core.getOrElse
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.client.parseAs
import com.hippo.ehviewer.util.ExceptionUtils
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -32,10 +33,9 @@ object GalleryTokenApiParser {
* ]
* }
*/
fun parse(body: String): String = runCatching {
fun parse(body: String): String = Either.catch {
body.parseAs<Result>().tokenList[0].token
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
throw EhException(body.parseAs<Error>().error)
}
@@ -15,18 +15,19 @@
*/
package com.hippo.ehviewer.client.parser
import arrow.core.Either
import arrow.core.getOrElse
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.exception.ParseException
import com.hippo.ehviewer.util.ExceptionUtils
import eu.kanade.tachiyomi.util.system.logcat
import org.jsoup.Jsoup
object ProfileParser {
fun parse(body: String): Result = runCatching {
fun parse(body: String): Result = Either.catch {
val d = Jsoup.parse(body)
val profilename = d.getElementById("profilename")
val displayName = profilename!!.child(0).text()
val avatar = runCatching {
val avatar = Either.catch {
val avatar =
profilename.nextElementSibling()!!.nextElementSibling()!!.child(0).attr("src")
if (avatar.isEmpty()) {
@@ -37,13 +38,11 @@ object ProfileParser {
avatar
}
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
logcat { "No avatar" }
null
}
Result(displayName, avatar)
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
throw ParseException("Parse forums error")
}
@@ -18,6 +18,8 @@ package com.hippo.ehviewer.util
import android.app.Activity
import android.content.Intent
import android.widget.Toast
import arrow.core.Either
import arrow.core.getOrElse
import com.hippo.ehviewer.R
object AppHelper {
@@ -27,11 +29,10 @@ object AppHelper {
sendIntent.putExtra(Intent.EXTRA_TEXT, text)
sendIntent.type = "text/plain"
val chooser = Intent.createChooser(sendIntent, from.getString(R.string.share))
return runCatching {
return Either.catch {
from.startActivity(chooser)
true
}.getOrElse {
ExceptionUtils.throwIfFatal(it)
Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show()
false
}
@@ -17,44 +17,9 @@ package com.hippo.ehviewer.util
import com.hippo.ehviewer.R
import eu.kanade.tachiyomi.util.system.logcat
import java.io.IOException
import java.net.MalformedURLException
import java.net.ProtocolException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLException
import splitties.init.appCtx
fun Throwable.displayString(): String {
logcat(this)
val cause = cause
return when {
cause != null && this is IOException -> getReadableStringInternal(cause)
else -> getReadableStringInternal(this)
}
}
private fun getReadableStringInternal(e: Throwable) = when (e) {
is MalformedURLException -> appCtx.getString(R.string.error_invalid_url)
is SocketTimeoutException -> appCtx.getString(R.string.error_timeout)
is UnknownHostException -> appCtx.getString(R.string.error_unknown_host)
is ProtocolException -> if (e.message!!.startsWith("Too many follow-up requests:")) {
appCtx.getString(R.string.error_redirection)
} else {
appCtx.getString(R.string.error_socket)
}
is SocketException, is SSLException -> appCtx.getString(R.string.error_socket)
else -> e.message ?: appCtx.getString(R.string.error_unknown)
}
object ExceptionUtils {
fun throwIfFatal(t: Throwable) {
// values here derived from https://github.com/ReactiveX/RxJava/issues/748#issuecomment-32471495
when (t) {
is VirtualMachineError, is ThreadDeath, is LinkageError -> {
throw t
}
}
}
return message ?: appCtx.getString(R.string.error_unknown)
}