Refactor e-hentai API response parsing
This commit is contained in:
1 parent
7a8ae183da
commit
dac28b9b6a
13 files changed
+146
-130
No files matched your search
@@ -38,11 +38,11 @@ import com.hippo.ehviewer.client.parser.GalleryTokenApiParser
|
||||
import com.hippo.ehviewer.client.parser.HomeParser
|
||||
import com.hippo.ehviewer.client.parser.Limits
|
||||
import com.hippo.ehviewer.client.parser.ProfileParser
|
||||
import com.hippo.ehviewer.client.parser.RateGalleryParser
|
||||
import com.hippo.ehviewer.client.parser.RateGalleryResult
|
||||
import com.hippo.ehviewer.client.parser.SignInParser
|
||||
import com.hippo.ehviewer.client.parser.TorrentParser
|
||||
import com.hippo.ehviewer.client.parser.TorrentResult
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentResult
|
||||
import com.hippo.ehviewer.client.parser.VoteTagParser
|
||||
import com.hippo.ehviewer.dailycheck.showEventNotification
|
||||
import com.hippo.ehviewer.dailycheck.today
|
||||
@@ -347,7 +347,13 @@ object EhEngine {
|
||||
}.executeAndParsingWith(GalleryPageApiParser::parse)
|
||||
}
|
||||
|
||||
suspend fun rateGallery(apiUid: Long, apiKey: String?, gid: Long, token: String?, rating: Float) = ehRequest(EhUrl.apiUrl, EhUrl.getGalleryDetailUrl(gid, token), EhUrl.origin) {
|
||||
suspend fun rateGallery(
|
||||
apiUid: Long,
|
||||
apiKey: String?,
|
||||
gid: Long,
|
||||
token: String?,
|
||||
rating: Float,
|
||||
): RateGalleryResult = ehRequest(EhUrl.apiUrl, EhUrl.getGalleryDetailUrl(gid, token), EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "rategallery")
|
||||
put("apiuid", apiUid)
|
||||
@@ -356,7 +362,7 @@ object EhEngine {
|
||||
put("token", token)
|
||||
put("rating", ceil((rating * 2).toDouble()).toInt())
|
||||
}
|
||||
}.executeAndParsingWith(RateGalleryParser::parse)
|
||||
}.executeAndParsingWith(String::parseAs)
|
||||
|
||||
suspend fun fillGalleryListByApi(galleryInfoList: List<GalleryInfo>, referer: String) = galleryInfoList.chunked(MAX_REQUEST_SIZE).parMap {
|
||||
ehRequest(EhUrl.apiUrl, referer, EhUrl.origin) {
|
||||
@@ -382,7 +388,7 @@ object EhEngine {
|
||||
token: String?,
|
||||
commentId: Long,
|
||||
commentVote: Int,
|
||||
) = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
|
||||
): VoteCommentResult = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "votecomment")
|
||||
put("apiuid", apiUid)
|
||||
@@ -392,7 +398,7 @@ object EhEngine {
|
||||
put("comment_id", commentId)
|
||||
put("comment_vote", commentVote)
|
||||
}
|
||||
}.executeAndParsingWith { VoteCommentParser.parse(this, commentVote) }
|
||||
}.executeAndParsingWith(String::parseAs)
|
||||
|
||||
suspend fun voteTag(
|
||||
apiUid: Long,
|
||||
|
||||
@@ -36,6 +36,7 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.executeAsync
|
||||
import okio.BufferedSource
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@@ -107,7 +108,9 @@ suspend inline fun <R> Request.executeNonCache(crossinline block: suspend Respon
|
||||
|
||||
suspend inline fun <reified T> Request.executeAndParseAs() = execute { parseAs<T>() }
|
||||
|
||||
inline fun <reified T> Response.parseAs(): T = json.decodeFromBufferedSource(body.source())
|
||||
inline fun <reified T> Response.parseAs(): T = body.source().parseAs()
|
||||
inline fun <reified T> BufferedSource.parseAs(): T = json.decodeFromBufferedSource(this)
|
||||
inline fun <reified T> String.parseAs(): T = json.decodeFromString(this)
|
||||
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
|
||||
@@ -32,12 +32,11 @@ import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.okio.decodeFromBufferedSource
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.executeAsync
|
||||
import okio.BufferedSource
|
||||
import okio.HashingSink.Companion.sha1
|
||||
import okio.blackholeSink
|
||||
import okio.buffer
|
||||
@@ -210,7 +209,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
val dir = AppConfig.getFilesDir("tag-translations")
|
||||
val dataFile = File(dir, dataName).takeIf { it.exists() } ?: return
|
||||
runSuspendCatching {
|
||||
tagGroups = dataFile.source().buffer().use { Json.decodeFromBufferedSource(it) }
|
||||
tagGroups = dataFile.source().buffer().use(BufferedSource::parseAs)
|
||||
initialized = true
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Error(val error: String)
|
||||
@@ -19,32 +19,45 @@ import com.hippo.ehviewer.client.EhUtils.getCategory
|
||||
import com.hippo.ehviewer.client.EhUtils.handleThumbUrlResolution
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.getThumbKey
|
||||
import com.hippo.ehviewer.yorozuya.toFloatOrDefault
|
||||
import com.hippo.ehviewer.yorozuya.toIntOrDefault
|
||||
import org.json.JSONObject
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryApiParser {
|
||||
fun parse(body: String, galleryInfoList: List<GalleryInfo>) {
|
||||
val jo = JSONObject(body)
|
||||
val ja = jo.getJSONArray("gmetadata")
|
||||
for (i in 0 until ja.length()) {
|
||||
val g = ja.getJSONObject(i)
|
||||
val gid = g.getLong("gid")
|
||||
val gi = galleryInfoList.find { it.gid == gid } ?: continue
|
||||
gi.title = ParserUtils.trim(g.getString("title"))
|
||||
gi.titleJpn = ParserUtils.trim(g.getString("title_jpn"))
|
||||
gi.category = getCategory(g.getString("category"))
|
||||
gi.thumbKey = getThumbKey(handleThumbUrlResolution(g.getString("thumb"))!!)
|
||||
gi.uploader = g.getString("uploader")
|
||||
gi.posted =
|
||||
ParserUtils.formatDate(ParserUtils.parseLong(g.getString("posted"), 0) * 1000)
|
||||
gi.rating = g.getString("rating").toFloatOrDefault(0.0f)
|
||||
// tags
|
||||
val tagJa = g.getJSONArray("tags")
|
||||
gi.simpleTags = (0 until tagJa.length()).map { tagJa.getString(it) }
|
||||
.let { arrayListOf<String>().apply { addAll(it) } }
|
||||
gi.pages = g.getString("filecount").toIntOrDefault(0)
|
||||
gi.generateSLang()
|
||||
body.parseAs<Result>().items.forEach { item ->
|
||||
val gi = galleryInfoList.find { it.gid == item.gid } ?: return@forEach
|
||||
gi.apply {
|
||||
title = item.title
|
||||
titleJpn = item.titleJpn
|
||||
category = getCategory(item.category)
|
||||
thumbKey = getThumbKey(handleThumbUrlResolution(item.thumb)!!)
|
||||
uploader = item.uploader
|
||||
posted = ParserUtils.formatDate(item.posted * 1000)
|
||||
rating = item.rating
|
||||
simpleTags = item.tags
|
||||
pages = item.pages
|
||||
generateSLang()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Result(@SerialName("gmetadata") val items: List<Item>)
|
||||
|
||||
@Serializable
|
||||
data class Item(
|
||||
val gid: Long,
|
||||
val title: String,
|
||||
@SerialName("title_jpn")
|
||||
val titleJpn: String,
|
||||
val category: String,
|
||||
val thumb: String,
|
||||
val uploader: String,
|
||||
val posted: Long,
|
||||
@SerialName("filecount")
|
||||
val pages: Int,
|
||||
val rating: Float,
|
||||
val tags: ArrayList<String>,
|
||||
)
|
||||
}
|
||||
+13
-9
@@ -17,22 +17,26 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import org.json.JSONArray
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryMultiPageViewerPTokenParser {
|
||||
private const val IMAGE_LIST_STRING = "var imagelist = "
|
||||
|
||||
fun parse(body: String): List<String> {
|
||||
val imagelist = body.substring(
|
||||
body.indexOf("var imagelist = ") + 16,
|
||||
body.indexOf(";", body.indexOf("var imagelist = ")),
|
||||
)
|
||||
val index = body.indexOf(IMAGE_LIST_STRING)
|
||||
val imagelist = body.substring(index + IMAGE_LIST_STRING.length, body.indexOf(";", index))
|
||||
return runCatching {
|
||||
val ja = JSONArray(imagelist)
|
||||
(0 until ja.length()).map { ja.getJSONObject(it).getString("k") }
|
||||
imagelist.parseAs<List<Item>>().map(Item::token)
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
throw EhException(body)
|
||||
throw ParseException("Parse pToken from MPV error", body, it)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Item(@SerialName("k") val token: String)
|
||||
}
|
||||
@@ -16,55 +16,44 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.yorozuya.unescapeXml
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryPageApiParser {
|
||||
private val PATTERN_IMAGE_URL = Pattern.compile("<img[^>]*src=\"([^\"]+)\" style")
|
||||
private val PATTERN_SKIP_HATH_KEY = Pattern.compile("onclick=\"return nl\\('([^)]+)'\\)")
|
||||
private val PATTERN_ORIGIN_IMAGE_URL =
|
||||
Pattern.compile("<a href=\"([^\"]+)fullimg.php([^\"]+)\">")
|
||||
private val PATTERN_IMAGE_URL = Regex("<img[^>]*src=\"([^\"]+)\" style")
|
||||
private val PATTERN_SKIP_HATH_KEY = Regex("onclick=\"return nl\\('([^)]+)'\\)")
|
||||
private val PATTERN_ORIGIN_IMAGE_URL = Regex("<a href=\"([^\"]+)fullimg.php([^\"]+)\">")
|
||||
|
||||
fun parse(body: String): Result {
|
||||
return try {
|
||||
var m: Matcher
|
||||
val jo = JSONObject(body)
|
||||
if (jo.has("error")) {
|
||||
throw ParseException(jo.getString("error"), body)
|
||||
}
|
||||
val i3 = jo.getString("i3")
|
||||
m = PATTERN_IMAGE_URL.matcher(i3)
|
||||
val imageUrl = if (m.find()) {
|
||||
m.group(1)!!.trim().unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val i6 = jo.getString("i6")
|
||||
m = PATTERN_SKIP_HATH_KEY.matcher(i6)
|
||||
val skipHathKey = if (m.find()) {
|
||||
m.group(1)!!.trim().unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val i7 = jo.getString("i7")
|
||||
m = PATTERN_ORIGIN_IMAGE_URL.matcher(i7)
|
||||
val originImageUrl = if (m.find()) {
|
||||
m.group(1)!!.unescapeXml() + "fullimg.php" + m.group(2)!!.unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (!imageUrl.isNullOrEmpty()) {
|
||||
Result(imageUrl, skipHathKey, originImageUrl)
|
||||
} else {
|
||||
throw ParseException("Parse image url and skip hath key error", body)
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
throw ParseException("Can't parse json", body, e)
|
||||
runCatching { body.parseAs<Error>() }.onSuccess { throw ParseException(it.error, body) }
|
||||
val res = body.parseAs<JsonResult>()
|
||||
val imageUrl = PATTERN_IMAGE_URL.find(res.imageUrl)?.run {
|
||||
groupValues[1].trim().unescapeXml()
|
||||
}
|
||||
val skipHathKey = PATTERN_SKIP_HATH_KEY.find(res.skipHathKey)?.run {
|
||||
groupValues[1].trim().unescapeXml()
|
||||
}
|
||||
val originImageUrl = PATTERN_ORIGIN_IMAGE_URL.find(res.originImageUrl)?.run {
|
||||
groupValues[1].unescapeXml() + "fullimg.php" + groupValues[2].unescapeXml()
|
||||
}
|
||||
if (!imageUrl.isNullOrEmpty()) {
|
||||
return Result(imageUrl, skipHathKey, originImageUrl)
|
||||
} else {
|
||||
throw ParseException("Parse image url and skip hath key error", body)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class JsonResult(
|
||||
@SerialName("i3")
|
||||
val imageUrl: String,
|
||||
@SerialName("i6")
|
||||
val skipHathKey: String,
|
||||
@SerialName("i7")
|
||||
val originImageUrl: String,
|
||||
)
|
||||
|
||||
class Result(val imageUrl: String, val skipHathKey: String?, val originImageUrl: String?)
|
||||
}
|
||||
@@ -16,8 +16,10 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import org.json.JSONObject
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryTokenApiParser {
|
||||
/**
|
||||
@@ -31,12 +33,17 @@ object GalleryTokenApiParser {
|
||||
* }
|
||||
*/
|
||||
fun parse(body: String): String {
|
||||
val jo = JSONObject(body).getJSONArray("tokenlist").getJSONObject(0)
|
||||
return runCatching {
|
||||
jo.getString("token")
|
||||
body.parseAs<Result>().tokenList[0].token
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
throw EhException(jo.getString("error"))
|
||||
throw EhException(body.parseAs<Error>().error)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Result(@SerialName("tokenlist") val tokenList: List<Item>)
|
||||
|
||||
@Serializable
|
||||
data class Item(val gid: Long, val token: String)
|
||||
}
|
||||
+9
-17
@@ -15,21 +15,13 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object RateGalleryParser {
|
||||
fun parse(body: String): Result {
|
||||
return try {
|
||||
val jsonObject = JSONObject(body)
|
||||
val rating = jsonObject.getDouble("rating_avg").toFloat()
|
||||
val ratingCount = jsonObject.getInt("rating_cnt")
|
||||
Result(rating, ratingCount)
|
||||
} catch (e: JSONException) {
|
||||
throw ParseException("Can't parse rate gallery", body, e)
|
||||
}
|
||||
}
|
||||
|
||||
class Result(val rating: Float, val ratingCount: Int)
|
||||
}
|
||||
@Serializable
|
||||
data class RateGalleryResult(
|
||||
@SerialName("rating_avg")
|
||||
val rating: Float,
|
||||
@SerialName("rating_cnt")
|
||||
val ratingCount: Int,
|
||||
)
|
||||
+11
-13
@@ -15,17 +15,15 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import org.json.JSONObject
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object VoteCommentParser {
|
||||
// {"comment_id":1253922,"comment_score":-19,"comment_vote":0}
|
||||
fun parse(body: String, expectVote: Int): Result {
|
||||
val jo = JSONObject(body)
|
||||
val id = jo.getLong("comment_id")
|
||||
val score = jo.getInt("comment_score")
|
||||
val vote = jo.getInt("comment_vote")
|
||||
return Result(id, score, vote, expectVote)
|
||||
}
|
||||
|
||||
class Result(val id: Long, val score: Int, val vote: Int, val expectVote: Int)
|
||||
}
|
||||
@Serializable
|
||||
data class VoteCommentResult(
|
||||
@SerialName("comment_id")
|
||||
val id: Long,
|
||||
@SerialName("comment_score")
|
||||
val score: Int,
|
||||
@SerialName("comment_vote")
|
||||
val vote: Int,
|
||||
)
|
||||
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import org.json.JSONObject
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
|
||||
object VoteTagParser {
|
||||
// {"error":"The tag \"neko\" is not allowed. Use character:neko or artist:neko"}
|
||||
fun parse(body: String): String {
|
||||
return JSONObject(body).optString("error")
|
||||
fun parse(body: String): String? {
|
||||
return runCatching { body.parseAs<Error>().error }.getOrNull()
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ import com.hippo.ehviewer.client.data.GalleryComment
|
||||
import com.hippo.ehviewer.client.data.GalleryCommentList
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentResult
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.FilterMode
|
||||
import com.hippo.ehviewer.databinding.ItemDrawerFavoritesBinding
|
||||
@@ -341,11 +341,12 @@ class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefresh
|
||||
runSuspendCatching {
|
||||
EhEngine.voteComment(gd.apiUid, gd.apiKey, gd.gid, gd.token, id, vote)
|
||||
}.onSuccess { result ->
|
||||
val voteUp = vote > 0
|
||||
showTip(
|
||||
if (result.expectVote > 0) (if (0 != result.vote) R.string.vote_up_successfully else R.string.cancel_vote_up_successfully) else if (0 != result.vote) R.string.vote_down_successfully else R.string.cancel_vote_down_successfully,
|
||||
if (voteUp) (if (0 != result.vote) R.string.vote_up_successfully else R.string.cancel_vote_up_successfully) else if (0 != result.vote) R.string.vote_down_successfully else R.string.cancel_vote_down_successfully,
|
||||
LENGTH_SHORT,
|
||||
)
|
||||
withUIContext { onVoteCommentSuccess(result) }
|
||||
withUIContext { onVoteCommentSuccess(result, voteUp) }
|
||||
}.onFailure {
|
||||
showTip(R.string.vote_failed, LENGTH_LONG)
|
||||
}
|
||||
@@ -698,7 +699,7 @@ class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefresh
|
||||
updateView(true)
|
||||
}
|
||||
|
||||
private fun onVoteCommentSuccess(result: VoteCommentParser.Result) {
|
||||
private fun onVoteCommentSuccess(result: VoteCommentResult, voteUp: Boolean) {
|
||||
if (mAdapter == null) {
|
||||
return
|
||||
}
|
||||
@@ -721,7 +722,7 @@ class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefresh
|
||||
// Update comment
|
||||
val comment = mGalleryDetail!!.comments.comments[position]
|
||||
comment.score = result.score
|
||||
if (result.expectVote > 0) {
|
||||
if (voteUp) {
|
||||
comment.voteUpEd = 0 != result.vote
|
||||
comment.voteDownEd = false
|
||||
} else {
|
||||
|
||||
@@ -1193,11 +1193,9 @@ class GalleryDetailScene : BaseScene() {
|
||||
runSuspendCatching {
|
||||
EhEngine.voteTag(apiUid, apiKey, gid, token, tag, vote)
|
||||
}.onSuccess { result ->
|
||||
if (result.isNotEmpty()) {
|
||||
result?.let {
|
||||
showTip(result, LENGTH_SHORT)
|
||||
} else {
|
||||
showTip(R.string.tag_vote_successfully, LENGTH_SHORT)
|
||||
}
|
||||
} ?: showTip(R.string.tag_vote_successfully, LENGTH_SHORT)
|
||||
}.onFailure {
|
||||
showTip(R.string.vote_failed, LENGTH_LONG)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user