feat(Anilist): Parse GraphQL error responses for downtime and token expiry (#1591)
* Create ALError.kt * Update AnilistApi.kt * Update AnilistApi.kt * Update AnilistApi.kt * Add error handling for AniList API responses * Update AnilistApi.kt * Update ALError.kt * Update AnilistApi.kt * fix: improve error handling and resource management in AniList API calls * Fix interpolation sign & add comment --------- Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
parent
9a7d1b6143
commit
ca26501aef
2 changed files with 67 additions and 8 deletions
|
|
@ -5,6 +5,7 @@ import androidx.core.net.toUri
|
|||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALAddMangaResult
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALCurrentUserResult
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALError
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALIdSearchResult
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALMangaMetadata
|
||||
import eu.kanade.tachiyomi.data.track.anilist.dto.ALOAuth
|
||||
|
|
@ -26,6 +27,7 @@ import kotlinx.serialization.json.put
|
|||
import kotlinx.serialization.json.putJsonObject
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.time.Instant
|
||||
|
|
@ -43,6 +45,25 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
.rateLimit(permits = 85, period = 1.minutes)
|
||||
.build()
|
||||
|
||||
// KMK -->
|
||||
private fun Response.parseALError() {
|
||||
val bodyString = peekBody(1024 * 1024).string()
|
||||
val errorObj = try {
|
||||
json.decodeFromString<ALError>(bodyString)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
errorObj?.errors?.firstOrNull()?.let {
|
||||
val msg = it.message
|
||||
if (msg.contains("Invalid token") || it.status == 401) {
|
||||
throw Exception("AniList token expired, please login again")
|
||||
}
|
||||
throw Exception(msg)
|
||||
}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
suspend fun addLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
val query = $$"""
|
||||
|
|
@ -71,6 +92,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALAddMangaResult>()
|
||||
.let {
|
||||
track.library_id = it.data.entry.id
|
||||
|
|
@ -112,6 +136,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
}
|
||||
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.use { it.parseALError() }
|
||||
// KMK <--
|
||||
track
|
||||
}
|
||||
}
|
||||
|
|
@ -134,6 +161,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
}
|
||||
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.use { it.parseALError() }
|
||||
// KMK <--
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,6 +222,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALSearchResult>()
|
||||
.data.page.media
|
||||
.map { it.toALManga().toTrack() }
|
||||
|
|
@ -271,6 +304,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALUserListMangaQueryResult>()
|
||||
.data.page.mediaList
|
||||
.map { it.toALUserManga() }
|
||||
|
|
@ -312,6 +348,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALCurrentUserResult>()
|
||||
.let {
|
||||
val viewer = it.data.viewer
|
||||
|
|
@ -323,9 +362,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
|
||||
suspend fun getMangaMetadata(track: DomainTrack): TrackMangaMetadata {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|query (${'$'}mangaId: Int!) {
|
||||
|Media (id: ${'$'}mangaId) {
|
||||
val query = $$"""
|
||||
|query ($mangaId: Int!) {
|
||||
|Media (id: $mangaId) {
|
||||
|id
|
||||
|title {
|
||||
|userPreferred
|
||||
|
|
@ -365,9 +404,12 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALMangaMetadata>()
|
||||
.let {
|
||||
val media = it.data.media
|
||||
.let { metadata ->
|
||||
val media = metadata.data.media
|
||||
TrackMangaMetadata(
|
||||
remoteId = media.id,
|
||||
title = media.title.userPreferred,
|
||||
|
|
@ -392,9 +434,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
// SY -->
|
||||
suspend fun searchById(id: String): TrackSearch {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|query (${'$'}mangaId: Int!) {
|
||||
|Media (id: ${'$'}mangaId) {
|
||||
val query = $$"""
|
||||
|query ($mangaId: Int!) {
|
||||
|Media (id: $mangaId) {
|
||||
|id
|
||||
|title {
|
||||
|userPreferred
|
||||
|
|
@ -430,6 +472,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
// KMK -->
|
||||
.also { it.parseALError() }
|
||||
// KMK <--
|
||||
.parseAs<ALIdSearchResult>()
|
||||
.data.media
|
||||
.toALManga()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package eu.kanade.tachiyomi.data.track.anilist.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ALError(
|
||||
val errors: List<ALErrorItem>? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ALErrorItem(
|
||||
val message: String,
|
||||
val status: Int? = null,
|
||||
)
|
||||
Loading…
Reference in a new issue