diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt index 6050dd930..1a358736f 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt @@ -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, 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, diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt b/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt index 3de2abc95..7cbc43c82 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt @@ -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 Request.executeNonCache(crossinline block: suspend Respon suspend inline fun Request.executeAndParseAs() = execute { parseAs() } -inline fun Response.parseAs(): T = json.decodeFromBufferedSource(body.source()) +inline fun Response.parseAs(): T = body.source().parseAs() +inline fun BufferedSource.parseAs(): T = json.decodeFromBufferedSource(this) +inline fun String.parseAs(): T = json.decodeFromString(this) val json = Json { ignoreUnknownKeys = true } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt index c016fb02c..cf56be878 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt @@ -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() diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/Error.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/Error.kt new file mode 100644 index 000000000..255057ac3 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/Error.kt @@ -0,0 +1,6 @@ +package com.hippo.ehviewer.client.parser + +import kotlinx.serialization.Serializable + +@Serializable +data class Error(val error: String) diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.kt index abbc8562f..d566451fe 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.kt @@ -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) { - 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().apply { addAll(it) } } - gi.pages = g.getString("filecount").toIntOrDefault(0) - gi.generateSLang() + body.parseAs().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) + + @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, + ) } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryMultiPageViewerPTokenParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryMultiPageViewerPTokenParser.kt index 9fe2f1e35..845fa5d3e 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryMultiPageViewerPTokenParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryMultiPageViewerPTokenParser.kt @@ -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 { - 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>().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) } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.kt index 59cc74da0..a6ab9eac7 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.kt @@ -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("]*src=\"([^\"]+)\" style") - private val PATTERN_SKIP_HATH_KEY = Pattern.compile("onclick=\"return nl\\('([^)]+)'\\)") - private val PATTERN_ORIGIN_IMAGE_URL = - Pattern.compile("") + private val PATTERN_IMAGE_URL = Regex("]*src=\"([^\"]+)\" style") + private val PATTERN_SKIP_HATH_KEY = Regex("onclick=\"return nl\\('([^)]+)'\\)") + private val PATTERN_ORIGIN_IMAGE_URL = Regex("") 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() }.onSuccess { throw ParseException(it.error, body) } + val res = body.parseAs() + 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?) } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt index d3bd2627d..ed1c0f163 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt @@ -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().tokenList[0].token }.getOrElse { ExceptionUtils.throwIfFatal(it) - throw EhException(jo.getString("error")) + throw EhException(body.parseAs().error) } } + + @Serializable + data class Result(@SerialName("tokenlist") val tokenList: List) + + @Serializable + data class Item(val gid: Long, val token: String) } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryResult.kt similarity index 51% rename from app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.kt rename to app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryResult.kt index 9e8ad8e3d..c0eba1c7a 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryResult.kt @@ -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, +) diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentResult.kt similarity index 56% rename from app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.kt rename to app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentResult.kt index 9a339d168..346087bfc 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentResult.kt @@ -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, +) diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteTagParser.kt b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteTagParser.kt index 01f369457..6afbc230b 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteTagParser.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteTagParser.kt @@ -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 }.getOrNull() } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.kt index 9226be207..b2182a844 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.kt @@ -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 { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt index 452a213d3..9988b8e17 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt @@ -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) }