Переглянути джерело

Refactor tracker response parsing

arkon 4 роки тому
батько
коміт
2e8791a101

+ 107 - 81
app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt

@@ -6,7 +6,9 @@ import eu.kanade.tachiyomi.data.database.models.Track
 import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.await
-import kotlinx.serialization.decodeFromString
+import eu.kanade.tachiyomi.network.parseAs
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonObject
 import kotlinx.serialization.json.buildJsonObject
@@ -33,8 +35,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
     private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 
     suspend fun addLibManga(track: Track): Track {
-        val query =
-            """
+        return withContext(Dispatchers.IO) {
+            val query =
+                """
             |mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
                 |SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) { 
                 |   id 
@@ -42,28 +45,34 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
                 |} 
             |}
             |""".trimMargin()
-        val payload = buildJsonObject {
-            put("query", query)
-            putJsonObject("variables") {
-                put("mangaId", track.media_id)
-                put("progress", track.last_chapter_read)
-                put("status", track.toAnilistStatus())
+            val payload = buildJsonObject {
+                put("query", query)
+                putJsonObject("variables") {
+                    put("mangaId", track.media_id)
+                    put("progress", track.last_chapter_read)
+                    put("status", track.toAnilistStatus())
+                }
             }
-        }
-        return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            val response = json.decodeFromString<JsonObject>(responseBody)
-            track.library_id = response["data"]!!.jsonObject["SaveMediaListEntry"]!!.jsonObject["id"]!!.jsonPrimitive.long
-            track
+            authClient.newCall(
+                POST(
+                    apiUrl,
+                    body = payload.toString().toRequestBody(jsonMime)
+                )
+            )
+                .await()
+                .parseAs<JsonObject>()
+                .let {
+                    track.library_id =
+                        it["data"]!!.jsonObject["SaveMediaListEntry"]!!.jsonObject["id"]!!.jsonPrimitive.long
+                    track
+                }
         }
     }
 
     suspend fun updateLibManga(track: Track): Track {
-        val query =
-            """
+        return withContext(Dispatchers.IO) {
+            val query =
+                """
             |mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
                 |SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
                     |id
@@ -72,22 +81,25 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
                 |}
             |}
             |""".trimMargin()
-        val payload = buildJsonObject {
-            put("query", query)
-            putJsonObject("variables") {
-                put("listId", track.library_id)
-                put("progress", track.last_chapter_read)
-                put("status", track.toAnilistStatus())
-                put("score", track.score.toInt())
+            val payload = buildJsonObject {
+                put("query", query)
+                putJsonObject("variables") {
+                    put("listId", track.library_id)
+                    put("progress", track.last_chapter_read)
+                    put("status", track.toAnilistStatus())
+                    put("score", track.score.toInt())
+                }
             }
+            authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
+                .await()
+            track
         }
-        authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await()
-        return track
     }
 
     suspend fun search(search: String): List<TrackSearch> {
-        val query =
-            """
+        return withContext(Dispatchers.IO) {
+            val query =
+                """
             |query Search(${'$'}query: String) {
                 |Page (perPage: 50) {
                     |media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
@@ -111,29 +123,34 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
                 |}
             |}
             |""".trimMargin()
-        val payload = buildJsonObject {
-            put("query", query)
-            putJsonObject("variables") {
-                put("query", search)
-            }
-        }
-        return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
+            val payload = buildJsonObject {
+                put("query", query)
+                putJsonObject("variables") {
+                    put("query", search)
+                }
             }
-            val response = json.decodeFromString<JsonObject>(responseBody)
-            val data = response["data"]!!.jsonObject
-            val page = data["Page"]!!.jsonObject
-            val media = page["media"]!!.jsonArray
-            val entries = media.map { jsonToALManga(it.jsonObject) }
-            entries.map { it.toTrack() }
+            authClient.newCall(
+                POST(
+                    apiUrl,
+                    body = payload.toString().toRequestBody(jsonMime)
+                )
+            )
+                .await()
+                .parseAs<JsonObject>()
+                .let { response ->
+                    val data = response["data"]!!.jsonObject
+                    val page = data["Page"]!!.jsonObject
+                    val media = page["media"]!!.jsonArray
+                    val entries = media.map { jsonToALManga(it.jsonObject) }
+                    entries.map { it.toTrack() }
+                }
         }
     }
 
     suspend fun findLibManga(track: Track, userid: Int): Track? {
-        val query =
-            """
+        return withContext(Dispatchers.IO) {
+            val query =
+                """
             |query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
                 |Page {
                     |mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
@@ -163,24 +180,28 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
                 |}
             |}
             |""".trimMargin()
-        val payload = buildJsonObject {
-            put("query", query)
-            putJsonObject("variables") {
-                put("id", userid)
-                put("manga_id", track.media_id)
-            }
-        }
-        return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
+            val payload = buildJsonObject {
+                put("query", query)
+                putJsonObject("variables") {
+                    put("id", userid)
+                    put("manga_id", track.media_id)
+                }
             }
-            val response = json.decodeFromString<JsonObject>(responseBody)
-            val data = response["data"]!!.jsonObject
-            val page = data["Page"]!!.jsonObject
-            val media = page["mediaList"]!!.jsonArray
-            val entries = media.map { jsonToALUserManga(it.jsonObject) }
-            entries.firstOrNull()?.toTrack()
+            authClient.newCall(
+                POST(
+                    apiUrl,
+                    body = payload.toString().toRequestBody(jsonMime)
+                )
+            )
+                .await()
+                .parseAs<JsonObject>()
+                .let { response ->
+                    val data = response["data"]!!.jsonObject
+                    val page = data["Page"]!!.jsonObject
+                    val media = page["mediaList"]!!.jsonArray
+                    val entries = media.map { jsonToALUserManga(it.jsonObject) }
+                    entries.firstOrNull()?.toTrack()
+                }
         }
     }
 
@@ -193,8 +214,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
     }
 
     suspend fun getCurrentUser(): Pair<Int, String> {
-        val query =
-            """
+        return withContext(Dispatchers.IO) {
+            val query =
+                """
             |query User {
                 |Viewer {
                     |id
@@ -204,21 +226,25 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
                 |}
             |}
             |""".trimMargin()
-        val payload = buildJsonObject {
-            put("query", query)
-        }
-        return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
+            val payload = buildJsonObject {
+                put("query", query)
             }
-            val response = json.decodeFromString<JsonObject>(responseBody)
-            val data = response["data"]!!.jsonObject
-            val viewer = data["Viewer"]!!.jsonObject
-            Pair(
-                viewer["id"]!!.jsonPrimitive.int,
-                viewer["mediaListOptions"]!!.jsonObject["scoreFormat"]!!.jsonPrimitive.content
+            authClient.newCall(
+                POST(
+                    apiUrl,
+                    body = payload.toString().toRequestBody(jsonMime)
+                )
             )
+                .await()
+                .parseAs<JsonObject>()
+                .let {
+                    val data = it["data"]!!.jsonObject
+                    val viewer = data["Viewer"]!!.jsonObject
+                    Pair(
+                        viewer["id"]!!.jsonPrimitive.int,
+                        viewer["mediaListOptions"]!!.jsonObject["scoreFormat"]!!.jsonPrimitive.content
+                    )
+                }
         }
     }
 

+ 76 - 55
app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt

@@ -8,6 +8,9 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.await
+import eu.kanade.tachiyomi.network.parseAs
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonObject
@@ -30,46 +33,62 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
     private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 
     suspend fun addLibManga(track: Track): Track {
-        val body = FormBody.Builder()
-            .add("rating", track.score.toInt().toString())
-            .add("status", track.toBangumiStatus())
-            .build()
-        authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body)).await()
-        return track
+        return withContext(Dispatchers.IO) {
+            val body = FormBody.Builder()
+                .add("rating", track.score.toInt().toString())
+                .add("status", track.toBangumiStatus())
+                .build()
+            authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body))
+                .await()
+            track
+        }
     }
 
     suspend fun updateLibManga(track: Track): Track {
-        // read status update
-        val sbody = FormBody.Builder()
-            .add("status", track.toBangumiStatus())
-            .build()
-        authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody)).await()
+        return withContext(Dispatchers.IO) {
+            // read status update
+            val sbody = FormBody.Builder()
+                .add("status", track.toBangumiStatus())
+                .build()
+            authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody))
+                .await()
 
-        // chapter update
-        val body = FormBody.Builder()
-            .add("watched_eps", track.last_chapter_read.toString())
-            .build()
-        authClient.newCall(POST("$apiUrl/subject/${track.media_id}/update/watched_eps", body = body)).await()
+            // chapter update
+            val body = FormBody.Builder()
+                .add("watched_eps", track.last_chapter_read.toString())
+                .build()
+            authClient.newCall(
+                POST(
+                    "$apiUrl/subject/${track.media_id}/update/watched_eps",
+                    body = body
+                )
+            ).await()
 
-        return track
+            track
+        }
     }
 
     suspend fun search(search: String): List<TrackSearch> {
-        val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
-            .toUri()
-            .buildUpon()
-            .appendQueryParameter("max_results", "20")
-            .build()
-        return authClient.newCall(GET(url.toString())).await().use {
-            var responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            if (responseBody.contains("\"code\":404")) {
-                responseBody = "{\"results\":0,\"list\":[]}"
-            }
-            val response = json.decodeFromString<JsonObject>(responseBody)["list"]?.jsonArray
-            response?.filter { it.jsonObject["type"]?.jsonPrimitive?.int == 1 }?.map { jsonToSearch(it.jsonObject) }.orEmpty()
+        return withContext(Dispatchers.IO) {
+            val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
+                .toUri()
+                .buildUpon()
+                .appendQueryParameter("max_results", "20")
+                .build()
+            authClient.newCall(GET(url.toString()))
+                .await()
+                .use {
+                    var responseBody = it.body?.string().orEmpty()
+                    if (responseBody.isEmpty()) {
+                        throw Exception("Null Response")
+                    }
+                    if (responseBody.contains("\"code\":404")) {
+                        responseBody = "{\"results\":0,\"list\":[]}"
+                    }
+                    val response = json.decodeFromString<JsonObject>(responseBody)["list"]?.jsonArray
+                    response?.filter { it.jsonObject["type"]?.jsonPrimitive?.int == 1 }
+                        ?.map { jsonToSearch(it.jsonObject) }.orEmpty()
+                }
         }
     }
 
@@ -98,38 +117,40 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
     }
 
     suspend fun findLibManga(track: Track): Track? {
-        return authClient.newCall(GET("$apiUrl/subject/${track.media_id}")).await().use {
-            // get comic info
-            val responseBody = it.body?.string().orEmpty()
-            jsonToTrack(json.decodeFromString(responseBody))
+        return withContext(Dispatchers.IO) {
+            authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
+                .await()
+                .parseAs<JsonObject>()
+                .let { jsonToSearch(it) }
         }
     }
 
     suspend fun statusLibManga(track: Track): Track? {
-        val urlUserRead = "$apiUrl/collection/${track.media_id}"
-        val requestUserRead = Request.Builder()
-            .url(urlUserRead)
-            .cacheControl(CacheControl.FORCE_NETWORK)
-            .get()
-            .build()
+        return withContext(Dispatchers.IO) {
+            val urlUserRead = "$apiUrl/collection/${track.media_id}"
+            val requestUserRead = Request.Builder()
+                .url(urlUserRead)
+                .cacheControl(CacheControl.FORCE_NETWORK)
+                .get()
+                .build()
 
-        // TODO: get user readed chapter here
-        return authClient.newCall(requestUserRead).await().use {
-            val resp = it.body?.string()
-            val coll = json.decodeFromString<Collection>(resp!!)
-            track.status = coll.status?.id!!
-            track.last_chapter_read = coll.ep_status!!
-            track
+            // TODO: get user readed chapter here
+            authClient.newCall(requestUserRead)
+                .await()
+                .parseAs<Collection>()
+                .let {
+                    track.status = it.status?.id!!
+                    track.last_chapter_read = it.ep_status!!
+                    track
+                }
         }
     }
 
     suspend fun accessToken(code: String): OAuth {
-        return client.newCall(accessTokenRequest(code)).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            json.decodeFromString<OAuth>(responseBody)
+        return withContext(Dispatchers.IO) {
+            client.newCall(accessTokenRequest(code))
+                .await()
+                .parseAs()
         }
     }
 

+ 55 - 58
app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt

@@ -8,13 +8,12 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.await
+import eu.kanade.tachiyomi.network.parseAs
 import eu.kanade.tachiyomi.util.PkceUtil
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.withContext
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonObject
 import kotlinx.serialization.json.boolean
 import kotlinx.serialization.json.int
@@ -25,15 +24,11 @@ import okhttp3.FormBody
 import okhttp3.OkHttpClient
 import okhttp3.Request
 import okhttp3.RequestBody
-import okhttp3.Response
-import uy.kohesive.injekt.injectLazy
 import java.text.SimpleDateFormat
 import java.util.Locale
 
 class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) {
 
-    private val json: Json by injectLazy()
-
     private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 
     suspend fun getAccessToken(authCode: String): OAuth {
@@ -44,10 +39,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .add("code_verifier", codeVerifier)
                 .add("grant_type", "authorization_code")
                 .build()
-            client.newCall(POST("$baseOAuthUrl/token", body = formBody)).await().use {
-                val responseBody = it.body?.string().orEmpty()
-                json.decodeFromString(responseBody)
-            }
+            client.newCall(POST("$baseOAuthUrl/token", body = formBody))
+                .await()
+                .parseAs()
         }
     }
 
@@ -57,11 +51,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .url("$baseApiUrl/users/@me")
                 .get()
                 .build()
-            authClient.newCall(request).await().use {
-                val responseBody = it.body?.string().orEmpty()
-                val response = json.decodeFromString<JsonObject>(responseBody)
-                response["name"]!!.jsonPrimitive.content
-            }
+            authClient.newCall(request)
+                .await()
+                .parseAs<JsonObject>()
+                .let { it["name"]!!.jsonPrimitive.content }
         }
     }
 
@@ -70,18 +63,19 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
             val url = "$baseApiUrl/manga".toUri().buildUpon()
                 .appendQueryParameter("q", query)
                 .build()
-            authClient.newCall(GET(url.toString())).await().use {
-                val responseBody = it.body?.string().orEmpty()
-                val response = json.decodeFromString<JsonObject>(responseBody)
-                response["data"]!!.jsonArray
-                    .map { data -> data.jsonObject["node"]!!.jsonObject }
-                    .map { node ->
-                        val id = node["id"]!!.jsonPrimitive.int
-                        async { getMangaDetails(id) }
-                    }
-                    .awaitAll()
-                    .filter { trackSearch -> trackSearch.publishing_type != "novel" }
-            }
+            authClient.newCall(GET(url.toString()))
+                .await()
+                .parseAs<JsonObject>()
+                .let {
+                    it["data"]!!.jsonArray
+                        .map { data -> data.jsonObject["node"]!!.jsonObject }
+                        .map { node ->
+                            val id = node["id"]!!.jsonPrimitive.int
+                            async { getMangaDetails(id) }
+                        }
+                        .awaitAll()
+                        .filter { trackSearch -> trackSearch.publishing_type != "novel" }
+                }
         }
     }
 
@@ -91,27 +85,28 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .appendPath(id.toString())
                 .appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
                 .build()
-            authClient.newCall(GET(url.toString())).await().use {
-                val responseBody = it.body?.string().orEmpty()
-                val response = json.decodeFromString<JsonObject>(responseBody)
-                val obj = response.jsonObject
-                TrackSearch.create(TrackManager.MYANIMELIST).apply {
-                    media_id = obj["id"]!!.jsonPrimitive.int
-                    title = obj["title"]!!.jsonPrimitive.content
-                    summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
-                    total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
-                    cover_url = obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content ?: ""
-                    tracking_url = "https://myanimelist.net/manga/$media_id"
-                    publishing_status = obj["status"]!!.jsonPrimitive.content.replace("_", " ")
-                    publishing_type = obj["media_type"]!!.jsonPrimitive.content.replace("_", " ")
-                    start_date = try {
-                        val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
-                        outputDf.format(obj["start_date"]!!)
-                    } catch (e: Exception) {
-                        ""
+            authClient.newCall(GET(url.toString()))
+                .await()
+                .parseAs<JsonObject>()
+                .let {
+                    val obj = it.jsonObject
+                    TrackSearch.create(TrackManager.MYANIMELIST).apply {
+                        media_id = obj["id"]!!.jsonPrimitive.int
+                        title = obj["title"]!!.jsonPrimitive.content
+                        summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
+                        total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
+                        cover_url = obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content ?: ""
+                        tracking_url = "https://myanimelist.net/manga/$media_id"
+                        publishing_status = obj["status"]!!.jsonPrimitive.content.replace("_", " ")
+                        publishing_type = obj["media_type"]!!.jsonPrimitive.content.replace("_", " ")
+                        start_date = try {
+                            val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+                            outputDf.format(obj["start_date"]!!)
+                        } catch (e: Exception) {
+                            ""
+                        }
                     }
                 }
-            }
         }
     }
 
@@ -124,9 +119,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .url(mangaUrl(track.media_id).toString())
                 .put(formBody)
                 .build()
-            authClient.newCall(request).await().use {
-                parseMangaItem(it, track)
-            }
+            authClient.newCall(request)
+                .await()
+                .parseAs<JsonObject>()
+                .let { parseMangaItem(it, track) }
         }
     }
 
@@ -140,9 +136,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .url(mangaUrl(track.media_id).toString())
                 .put(formBody)
                 .build()
-            authClient.newCall(request).await().use {
-                parseMangaItem(it, track)
-            }
+            authClient.newCall(request)
+                .await()
+                .parseAs<JsonObject>()
+                .let { parseMangaItem(it, track) }
         }
     }
 
@@ -158,15 +155,15 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
                 .url(mangaUrl(track.media_id).toString())
                 .put(formBody)
                 .build()
-            authClient.newCall(request).await().use {
-                parseMangaItem(it, track)
-            }
+            authClient.newCall(request)
+                .await()
+                .parseAs<JsonObject>()
+                .let { parseMangaItem(it, track) }
         }
     }
 
-    private fun parseMangaItem(response: Response, track: Track): Track {
-        val responseBody = response.body?.string().orEmpty()
-        val obj = json.decodeFromString<JsonObject>(responseBody).jsonObject
+    private fun parseMangaItem(response: JsonObject, track: Track): Track {
+        val obj = response.jsonObject
         return track.apply {
             val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean
             status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]!!.jsonPrimitive.content)

+ 72 - 61
app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt

@@ -7,8 +7,10 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.await
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
+import eu.kanade.tachiyomi.network.parseAs
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.JsonArray
 import kotlinx.serialization.json.JsonObject
 import kotlinx.serialization.json.buildJsonObject
@@ -22,45 +24,51 @@ import okhttp3.FormBody
 import okhttp3.MediaType.Companion.toMediaType
 import okhttp3.OkHttpClient
 import okhttp3.RequestBody.Companion.toRequestBody
-import uy.kohesive.injekt.injectLazy
 
 class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
 
-    private val json: Json by injectLazy()
-
     private val jsonMime = "application/json; charset=utf-8".toMediaType()
     private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 
     suspend fun addLibManga(track: Track, user_id: String): Track {
-        val payload = buildJsonObject {
-            putJsonObject("user_rate") {
-                put("user_id", user_id)
-                put("target_id", track.media_id)
-                put("target_type", "Manga")
-                put("chapters", track.last_chapter_read)
-                put("score", track.score.toInt())
-                put("status", track.toShikimoriStatus())
+        return withContext(Dispatchers.IO) {
+            val payload = buildJsonObject {
+                putJsonObject("user_rate") {
+                    put("user_id", user_id)
+                    put("target_id", track.media_id)
+                    put("target_type", "Manga")
+                    put("chapters", track.last_chapter_read)
+                    put("score", track.score.toInt())
+                    put("status", track.toShikimoriStatus())
+                }
             }
+            authClient.newCall(
+                POST(
+                    "$apiUrl/v2/user_rates",
+                    body = payload.toString().toRequestBody(jsonMime)
+                )
+            ).await()
+            track
         }
-        authClient.newCall(POST("$apiUrl/v2/user_rates", body = payload.toString().toRequestBody(jsonMime))).await()
-        return track
     }
 
     suspend fun updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id)
 
     suspend fun search(search: String): List<TrackSearch> {
-        val url = "$apiUrl/mangas".toUri().buildUpon()
-            .appendQueryParameter("order", "popularity")
-            .appendQueryParameter("search", search)
-            .appendQueryParameter("limit", "20")
-            .build()
-        return authClient.newCall(GET(url.toString())).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            val response = json.decodeFromString<JsonArray>(responseBody)
-            response.map { jsonToSearch(it.jsonObject) }
+        return withContext(Dispatchers.IO) {
+            val url = "$apiUrl/mangas".toUri().buildUpon()
+                .appendQueryParameter("order", "popularity")
+                .appendQueryParameter("search", search)
+                .appendQueryParameter("limit", "20")
+                .build()
+            authClient.newCall(GET(url.toString()))
+                .await()
+                .parseAs<JsonArray>()
+                .let { response ->
+                    response.map {
+                        jsonToSearch(it.jsonObject)
+                    }
+                }
         }
     }
 
@@ -91,47 +99,50 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
     }
 
     suspend fun findLibManga(track: Track, user_id: String): Track? {
-        val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
-            .appendPath(track.media_id.toString())
-            .build()
-        val mangas = authClient.newCall(GET(urlMangas.toString())).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            json.decodeFromString<JsonObject>(responseBody)
-        }
-
-        val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
-            .appendQueryParameter("user_id", user_id)
-            .appendQueryParameter("target_id", track.media_id.toString())
-            .appendQueryParameter("target_type", "Manga")
-            .build()
-        return authClient.newCall(GET(url.toString())).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            val response = json.decodeFromString<JsonArray>(responseBody)
-            if (response.size > 1) {
-                throw Exception("Too much mangas in response")
-            }
-            val entry = response.map {
-                jsonToTrack(it.jsonObject, mangas)
-            }
-            entry.firstOrNull()
+        return withContext(Dispatchers.IO) {
+            val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
+                .appendPath(track.media_id.toString())
+                .build()
+            val mangas = authClient.newCall(GET(urlMangas.toString()))
+                .await()
+                .parseAs<JsonObject>()
+
+            val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
+                .appendQueryParameter("user_id", user_id)
+                .appendQueryParameter("target_id", track.media_id.toString())
+                .appendQueryParameter("target_type", "Manga")
+                .build()
+            authClient.newCall(GET(url.toString()))
+                .await()
+                .parseAs<JsonArray>()
+                .let { response ->
+                    if (response.size > 1) {
+                        throw Exception("Too much mangas in response")
+                    }
+                    val entry = response.map {
+                        jsonToTrack(it.jsonObject, mangas)
+                    }
+                    entry.firstOrNull()
+                }
         }
     }
 
     fun getCurrentUser(): Int {
-        val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body?.string()!!
-        return json.decodeFromString<JsonObject>(user)["id"]!!.jsonPrimitive.int
+        return runBlocking {
+            authClient.newCall(GET("$apiUrl/users/whoami"))
+                .await()
+                .parseAs<JsonObject>()
+                .let {
+                    it["id"]!!.jsonPrimitive.int
+                }
+        }
     }
 
     suspend fun accessToken(code: String): OAuth {
-        return client.newCall(accessTokenRequest(code)).await().use {
-            val responseBody = it.body?.string().orEmpty()
-            if (responseBody.isEmpty()) {
-                throw Exception("Null Response")
-            }
-            json.decodeFromString(responseBody)
+        return withContext(Dispatchers.IO) {
+            client.newCall(accessTokenRequest(code))
+                .await()
+                .parseAs()
         }
     }
 

+ 4 - 1
app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt

@@ -12,6 +12,7 @@ import rx.Observable
 import rx.Producer
 import rx.Subscription
 import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.fullType
 import uy.kohesive.injekt.api.get
 import java.io.IOException
 import java.util.concurrent.atomic.AtomicBoolean
@@ -111,8 +112,10 @@ fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListene
 }
 
 inline fun <reified T> Response.parseAs(): T {
+    // Avoiding Injekt.get<Json>() due to compiler issues
+    val json = Injekt.getInstance<Json>(fullType<Json>().type)
     this.use {
         val responseBody = it.body?.string().orEmpty()
-        return Injekt.get<Json>().decodeFromString<T>(responseBody)
+        return json.decodeFromString(responseBody)
     }
 }