Migrate to kotlinx.serialization for Shikimori

This commit is contained in:
arkon 2020-10-11 15:44:18 -04:00
parent 980feb6c96
commit f8d82cb052
4 changed files with 58 additions and 54 deletions

View File

@ -1,5 +1,8 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import kotlinx.serialization.Serializable
@Serializable
data class OAuth( data class OAuth(
val access_token: String, val access_token: String,
val token_type: String, val token_type: String,

View File

@ -2,14 +2,15 @@ package eu.kanade.tachiyomi.data.track.shikimori
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import com.google.gson.Gson
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import rx.Completable import rx.Completable
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
class Shikimori(private val context: Context, id: Int) : TrackService(id) { class Shikimori(private val context: Context, id: Int) : TrackService(id) {
@ -27,9 +28,7 @@ class Shikimori(private val context: Context, id: Int) : TrackService(id) {
override val name = "Shikimori" override val name = "Shikimori"
private val gson: Gson by injectLazy() private val interceptor by lazy { ShikimoriInterceptor(this) }
private val interceptor by lazy { ShikimoriInterceptor(this, gson) }
private val api by lazy { ShikimoriApi(client, interceptor) } private val api by lazy { ShikimoriApi(client, interceptor) }
@ -117,13 +116,12 @@ class Shikimori(private val context: Context, id: Int) : TrackService(id) {
} }
fun saveToken(oauth: OAuth?) { fun saveToken(oauth: OAuth?) {
val json = gson.toJson(oauth) preferences.trackToken(this).set(Json.encodeToString(oauth))
preferences.trackToken(this).set(json)
} }
fun restoreToken(): OAuth? { fun restoreToken(): OAuth? {
return try { return try {
gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java) Json.decodeFromString<OAuth>(preferences.trackToken(this).get())
} catch (e: Exception) { } catch (e: Exception) {
null null
} }

View File

@ -1,44 +1,46 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import androidx.core.net.toUri import androidx.core.net.toUri
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.obj
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) { class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
private val gson: Gson by injectLazy()
private val jsonime = "application/json; charset=utf-8".toMediaTypeOrNull() private val jsonime = "application/json; charset=utf-8".toMediaTypeOrNull()
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
fun addLibManga(track: Track, user_id: String): Observable<Track> { fun addLibManga(track: Track, user_id: String): Observable<Track> {
val payload = jsonObject( val payload = buildJsonObject {
"user_rate" to jsonObject( putJsonObject("user_rate") {
"user_id" to user_id, put("user_id", user_id)
"target_id" to track.media_id, put("target_id", track.media_id)
"target_type" to "Manga", put("target_type", "Manga")
"chapters" to track.last_chapter_read, put("chapters", track.last_chapter_read)
"score" to track.score.toInt(), put("score", track.score.toInt())
"status" to track.toShikimoriStatus() put("status", track.toShikimoriStatus())
) }
) }
val body = payload.toString().toRequestBody(jsonime) val body = payload.toString().toRequestBody(jsonime)
val request = Request.Builder() val request = Request.Builder()
.url("$apiUrl/v2/user_rates") .url("$apiUrl/v2/user_rates")
@ -70,34 +72,34 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
if (responseBody.isEmpty()) { if (responseBody.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val response = JsonParser.parseString(responseBody).array val response = Json.decodeFromString<JsonArray>(responseBody)
response.map { jsonToSearch(it.obj) } response.map { jsonToSearch(it.jsonObject) }
} }
} }
private fun jsonToSearch(obj: JsonObject): TrackSearch { private fun jsonToSearch(obj: JsonObject): TrackSearch {
return TrackSearch.create(TrackManager.SHIKIMORI).apply { return TrackSearch.create(TrackManager.SHIKIMORI).apply {
media_id = obj["id"].asInt media_id = obj["id"]!!.jsonPrimitive.int
title = obj["name"].asString title = obj["name"]!!.jsonPrimitive.content
total_chapters = obj["chapters"].asInt total_chapters = obj["chapters"]!!.jsonPrimitive.int
cover_url = baseUrl + obj["image"].obj["preview"].asString cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
summary = "" summary = ""
tracking_url = baseUrl + obj["url"].asString tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
publishing_status = obj["status"].asString publishing_status = obj["status"]!!.jsonPrimitive.content
publishing_type = obj["kind"].asString publishing_type = obj["kind"]!!.jsonPrimitive.content
start_date = obj.get("aired_on").nullString.orEmpty() start_date = obj.get("aired_on")!!.jsonPrimitive.contentOrNull ?: ""
} }
} }
private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track { private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track {
return Track.create(TrackManager.SHIKIMORI).apply { return Track.create(TrackManager.SHIKIMORI).apply {
title = mangas["name"].asString title = mangas["name"]!!.jsonPrimitive.content
media_id = obj["id"].asInt media_id = obj["id"]!!.jsonPrimitive.int
total_chapters = mangas["chapters"].asInt total_chapters = mangas["chapters"]!!.jsonPrimitive.int
last_chapter_read = obj["chapters"].asInt last_chapter_read = obj["chapters"]!!.jsonPrimitive.int
score = (obj["score"].asInt).toFloat() score = (obj["score"]!!.jsonPrimitive.int).toFloat()
status = toTrackStatus(obj["status"].asString) status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
tracking_url = baseUrl + mangas["url"].asString tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content
} }
} }
@ -123,7 +125,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
.asObservableSuccess() .asObservableSuccess()
.map { netResponse -> .map { netResponse ->
val responseBody = netResponse.body?.string().orEmpty() val responseBody = netResponse.body?.string().orEmpty()
JsonParser.parseString(responseBody).obj Json.decodeFromString<JsonObject>(responseBody)
}.flatMap { mangas -> }.flatMap { mangas ->
authClient.newCall(request) authClient.newCall(request)
.asObservableSuccess() .asObservableSuccess()
@ -132,12 +134,12 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
if (responseBody.isEmpty()) { if (responseBody.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
val response = JsonParser.parseString(responseBody).array val response = Json.decodeFromString<JsonArray>(responseBody)
if (response.size() > 1) { if (response.size > 1) {
throw Exception("Too much mangas in response") throw Exception("Too much mangas in response")
} }
val entry = response.map { val entry = response.map {
jsonToTrack(it.obj, mangas) jsonToTrack(it.jsonObject, mangas)
} }
entry.firstOrNull() entry.firstOrNull()
} }
@ -145,8 +147,8 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
} }
fun getCurrentUser(): Int { fun getCurrentUser(): Int {
val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body?.string() val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body?.string()!!
return JsonParser.parseString(user).obj["id"].asInt return Json.decodeFromString<JsonObject>(user)["id"]!!.jsonPrimitive.int
} }
fun accessToken(code: String): Observable<OAuth> { fun accessToken(code: String): Observable<OAuth> {
@ -155,7 +157,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
if (responseBody.isEmpty()) { if (responseBody.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
} }
gson.fromJson(responseBody, OAuth::class.java) Json.decodeFromString<OAuth>(responseBody)
} }
} }

View File

@ -1,10 +1,11 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import com.google.gson.Gson import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
class ShikimoriInterceptor(val shikimori: Shikimori, val gson: Gson) : Interceptor { class ShikimoriInterceptor(val shikimori: Shikimori) : Interceptor {
/** /**
* OAuth object used for authenticated requests. * OAuth object used for authenticated requests.
@ -22,7 +23,7 @@ class ShikimoriInterceptor(val shikimori: Shikimori, val gson: Gson) : Intercept
if (currAuth.isExpired()) { if (currAuth.isExpired()) {
val response = chain.proceed(ShikimoriApi.refreshTokenRequest(refreshToken)) val response = chain.proceed(ShikimoriApi.refreshTokenRequest(refreshToken))
if (response.isSuccessful) { if (response.isSuccessful) {
newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java)) newAuth(Json.decodeFromString<OAuth>(response.body!!.string()))
} else { } else {
response.close() response.close()
} }