mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-07 20:31:02 -05:00
Refactor tracker response parsing
This commit is contained in:
parent
0e2b8b10d1
commit
2e8791a101
5 changed files with 313 additions and 255 deletions
|
@ -6,7 +6,9 @@ import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.await
|
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.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
@ -33,8 +35,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track): Track {
|
suspend fun addLibManga(track: Track): Track {
|
||||||
val query =
|
return withContext(Dispatchers.IO) {
|
||||||
"""
|
val query =
|
||||||
|
"""
|
||||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||||
| id
|
| id
|
||||||
|
@ -42,28 +45,34 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
val payload = buildJsonObject {
|
val payload = buildJsonObject {
|
||||||
put("query", query)
|
put("query", query)
|
||||||
putJsonObject("variables") {
|
putJsonObject("variables") {
|
||||||
put("mangaId", track.media_id)
|
put("mangaId", track.media_id)
|
||||||
put("progress", track.last_chapter_read)
|
put("progress", track.last_chapter_read)
|
||||||
put("status", track.toAnilistStatus())
|
put("status", track.toAnilistStatus())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
authClient.newCall(
|
||||||
return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
|
POST(
|
||||||
val responseBody = it.body?.string().orEmpty()
|
apiUrl,
|
||||||
if (responseBody.isEmpty()) {
|
body = payload.toString().toRequestBody(jsonMime)
|
||||||
throw Exception("Null Response")
|
)
|
||||||
}
|
)
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.await()
|
||||||
track.library_id = response["data"]!!.jsonObject["SaveMediaListEntry"]!!.jsonObject["id"]!!.jsonPrimitive.long
|
.parseAs<JsonObject>()
|
||||||
track
|
.let {
|
||||||
|
track.library_id =
|
||||||
|
it["data"]!!.jsonObject["SaveMediaListEntry"]!!.jsonObject["id"]!!.jsonPrimitive.long
|
||||||
|
track
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateLibManga(track: Track): 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) {
|
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|
||||||
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
||||||
|id
|
|id
|
||||||
|
@ -72,22 +81,25 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
val payload = buildJsonObject {
|
val payload = buildJsonObject {
|
||||||
put("query", query)
|
put("query", query)
|
||||||
putJsonObject("variables") {
|
putJsonObject("variables") {
|
||||||
put("listId", track.library_id)
|
put("listId", track.library_id)
|
||||||
put("progress", track.last_chapter_read)
|
put("progress", track.last_chapter_read)
|
||||||
put("status", track.toAnilistStatus())
|
put("status", track.toAnilistStatus())
|
||||||
put("score", track.score.toInt())
|
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> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
val query =
|
return withContext(Dispatchers.IO) {
|
||||||
"""
|
val query =
|
||||||
|
"""
|
||||||
|query Search(${'$'}query: String) {
|
|query Search(${'$'}query: String) {
|
||||||
|Page (perPage: 50) {
|
|Page (perPage: 50) {
|
||||||
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
||||||
|
@ -111,29 +123,34 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
val payload = buildJsonObject {
|
val payload = buildJsonObject {
|
||||||
put("query", query)
|
put("query", query)
|
||||||
putJsonObject("variables") {
|
putJsonObject("variables") {
|
||||||
put("query", search)
|
put("query", search)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
authClient.newCall(
|
||||||
return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
|
POST(
|
||||||
val responseBody = it.body?.string().orEmpty()
|
apiUrl,
|
||||||
if (responseBody.isEmpty()) {
|
body = payload.toString().toRequestBody(jsonMime)
|
||||||
throw Exception("Null Response")
|
)
|
||||||
}
|
)
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.await()
|
||||||
val data = response["data"]!!.jsonObject
|
.parseAs<JsonObject>()
|
||||||
val page = data["Page"]!!.jsonObject
|
.let { response ->
|
||||||
val media = page["media"]!!.jsonArray
|
val data = response["data"]!!.jsonObject
|
||||||
val entries = media.map { jsonToALManga(it.jsonObject) }
|
val page = data["Page"]!!.jsonObject
|
||||||
entries.map { it.toTrack() }
|
val media = page["media"]!!.jsonArray
|
||||||
|
val entries = media.map { jsonToALManga(it.jsonObject) }
|
||||||
|
entries.map { it.toTrack() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track, userid: Int): Track? {
|
suspend fun findLibManga(track: Track, userid: Int): Track? {
|
||||||
val query =
|
return withContext(Dispatchers.IO) {
|
||||||
"""
|
val query =
|
||||||
|
"""
|
||||||
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
||||||
|Page {
|
|Page {
|
||||||
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
||||||
|
@ -163,24 +180,28 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
val payload = buildJsonObject {
|
val payload = buildJsonObject {
|
||||||
put("query", query)
|
put("query", query)
|
||||||
putJsonObject("variables") {
|
putJsonObject("variables") {
|
||||||
put("id", userid)
|
put("id", userid)
|
||||||
put("manga_id", track.media_id)
|
put("manga_id", track.media_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
authClient.newCall(
|
||||||
return authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))).await().use {
|
POST(
|
||||||
val responseBody = it.body?.string().orEmpty()
|
apiUrl,
|
||||||
if (responseBody.isEmpty()) {
|
body = payload.toString().toRequestBody(jsonMime)
|
||||||
throw Exception("Null Response")
|
)
|
||||||
}
|
)
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.await()
|
||||||
val data = response["data"]!!.jsonObject
|
.parseAs<JsonObject>()
|
||||||
val page = data["Page"]!!.jsonObject
|
.let { response ->
|
||||||
val media = page["mediaList"]!!.jsonArray
|
val data = response["data"]!!.jsonObject
|
||||||
val entries = media.map { jsonToALUserManga(it.jsonObject) }
|
val page = data["Page"]!!.jsonObject
|
||||||
entries.firstOrNull()?.toTrack()
|
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> {
|
suspend fun getCurrentUser(): Pair<Int, String> {
|
||||||
val query =
|
return withContext(Dispatchers.IO) {
|
||||||
"""
|
val query =
|
||||||
|
"""
|
||||||
|query User {
|
|query User {
|
||||||
|Viewer {
|
|Viewer {
|
||||||
|id
|
|id
|
||||||
|
@ -204,21 +226,25 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|}
|
|}
|
||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
val payload = buildJsonObject {
|
val payload = buildJsonObject {
|
||||||
put("query", query)
|
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 response = json.decodeFromString<JsonObject>(responseBody)
|
authClient.newCall(
|
||||||
val data = response["data"]!!.jsonObject
|
POST(
|
||||||
val viewer = data["Viewer"]!!.jsonObject
|
apiUrl,
|
||||||
Pair(
|
body = payload.toString().toRequestBody(jsonMime)
|
||||||
viewer["id"]!!.jsonPrimitive.int,
|
)
|
||||||
viewer["mediaListOptions"]!!.jsonObject["scoreFormat"]!!.jsonPrimitive.content
|
|
||||||
)
|
)
|
||||||
|
.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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@ 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.await
|
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.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
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()
|
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track): Track {
|
suspend fun addLibManga(track: Track): Track {
|
||||||
val body = FormBody.Builder()
|
return withContext(Dispatchers.IO) {
|
||||||
.add("rating", track.score.toInt().toString())
|
val body = FormBody.Builder()
|
||||||
.add("status", track.toBangumiStatus())
|
.add("rating", track.score.toInt().toString())
|
||||||
.build()
|
.add("status", track.toBangumiStatus())
|
||||||
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body)).await()
|
.build()
|
||||||
return track
|
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body))
|
||||||
|
.await()
|
||||||
|
track
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateLibManga(track: Track): Track {
|
suspend fun updateLibManga(track: Track): Track {
|
||||||
// read status update
|
return withContext(Dispatchers.IO) {
|
||||||
val sbody = FormBody.Builder()
|
// read status update
|
||||||
.add("status", track.toBangumiStatus())
|
val sbody = FormBody.Builder()
|
||||||
.build()
|
.add("status", track.toBangumiStatus())
|
||||||
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody)).await()
|
.build()
|
||||||
|
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody))
|
||||||
|
.await()
|
||||||
|
|
||||||
// chapter update
|
// chapter update
|
||||||
val body = FormBody.Builder()
|
val body = FormBody.Builder()
|
||||||
.add("watched_eps", track.last_chapter_read.toString())
|
.add("watched_eps", track.last_chapter_read.toString())
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(POST("$apiUrl/subject/${track.media_id}/update/watched_eps", body = body)).await()
|
authClient.newCall(
|
||||||
|
POST(
|
||||||
|
"$apiUrl/subject/${track.media_id}/update/watched_eps",
|
||||||
|
body = body
|
||||||
|
)
|
||||||
|
).await()
|
||||||
|
|
||||||
return track
|
track
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(search: String): List<TrackSearch> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
|
return withContext(Dispatchers.IO) {
|
||||||
.toUri()
|
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
|
||||||
.buildUpon()
|
.toUri()
|
||||||
.appendQueryParameter("max_results", "20")
|
.buildUpon()
|
||||||
.build()
|
.appendQueryParameter("max_results", "20")
|
||||||
return authClient.newCall(GET(url.toString())).await().use {
|
.build()
|
||||||
var responseBody = it.body?.string().orEmpty()
|
authClient.newCall(GET(url.toString()))
|
||||||
if (responseBody.isEmpty()) {
|
.await()
|
||||||
throw Exception("Null Response")
|
.use {
|
||||||
}
|
var responseBody = it.body?.string().orEmpty()
|
||||||
if (responseBody.contains("\"code\":404")) {
|
if (responseBody.isEmpty()) {
|
||||||
responseBody = "{\"results\":0,\"list\":[]}"
|
throw Exception("Null Response")
|
||||||
}
|
}
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)["list"]?.jsonArray
|
if (responseBody.contains("\"code\":404")) {
|
||||||
response?.filter { it.jsonObject["type"]?.jsonPrimitive?.int == 1 }?.map { jsonToSearch(it.jsonObject) }.orEmpty()
|
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? {
|
suspend fun findLibManga(track: Track): Track? {
|
||||||
return authClient.newCall(GET("$apiUrl/subject/${track.media_id}")).await().use {
|
return withContext(Dispatchers.IO) {
|
||||||
// get comic info
|
authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
jsonToTrack(json.decodeFromString(responseBody))
|
.parseAs<JsonObject>()
|
||||||
|
.let { jsonToSearch(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun statusLibManga(track: Track): Track? {
|
suspend fun statusLibManga(track: Track): Track? {
|
||||||
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
return withContext(Dispatchers.IO) {
|
||||||
val requestUserRead = Request.Builder()
|
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
||||||
.url(urlUserRead)
|
val requestUserRead = Request.Builder()
|
||||||
.cacheControl(CacheControl.FORCE_NETWORK)
|
.url(urlUserRead)
|
||||||
.get()
|
.cacheControl(CacheControl.FORCE_NETWORK)
|
||||||
.build()
|
.get()
|
||||||
|
.build()
|
||||||
|
|
||||||
// TODO: get user readed chapter here
|
// TODO: get user readed chapter here
|
||||||
return authClient.newCall(requestUserRead).await().use {
|
authClient.newCall(requestUserRead)
|
||||||
val resp = it.body?.string()
|
.await()
|
||||||
val coll = json.decodeFromString<Collection>(resp!!)
|
.parseAs<Collection>()
|
||||||
track.status = coll.status?.id!!
|
.let {
|
||||||
track.last_chapter_read = coll.ep_status!!
|
track.status = it.status?.id!!
|
||||||
track
|
track.last_chapter_read = it.ep_status!!
|
||||||
|
track
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun accessToken(code: String): OAuth {
|
suspend fun accessToken(code: String): OAuth {
|
||||||
return client.newCall(accessTokenRequest(code)).await().use {
|
return withContext(Dispatchers.IO) {
|
||||||
val responseBody = it.body?.string().orEmpty()
|
client.newCall(accessTokenRequest(code))
|
||||||
if (responseBody.isEmpty()) {
|
.await()
|
||||||
throw Exception("Null Response")
|
.parseAs()
|
||||||
}
|
|
||||||
json.decodeFromString<OAuth>(responseBody)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,12 @@ 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.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import eu.kanade.tachiyomi.util.PkceUtil
|
import eu.kanade.tachiyomi.util.PkceUtil
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.boolean
|
import kotlinx.serialization.json.boolean
|
||||||
import kotlinx.serialization.json.int
|
import kotlinx.serialization.json.int
|
||||||
|
@ -25,15 +24,11 @@ import okhttp3.FormBody
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import okhttp3.Response
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) {
|
class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
|
||||||
|
|
||||||
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
suspend fun getAccessToken(authCode: String): OAuth {
|
suspend fun getAccessToken(authCode: String): OAuth {
|
||||||
|
@ -44,10 +39,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||||
.add("code_verifier", codeVerifier)
|
.add("code_verifier", codeVerifier)
|
||||||
.add("grant_type", "authorization_code")
|
.add("grant_type", "authorization_code")
|
||||||
.build()
|
.build()
|
||||||
client.newCall(POST("$baseOAuthUrl/token", body = formBody)).await().use {
|
client.newCall(POST("$baseOAuthUrl/token", body = formBody))
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
json.decodeFromString(responseBody)
|
.parseAs()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,11 +51,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||||
.url("$baseApiUrl/users/@me")
|
.url("$baseApiUrl/users/@me")
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(request).await().use {
|
authClient.newCall(request)
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.parseAs<JsonObject>()
|
||||||
response["name"]!!.jsonPrimitive.content
|
.let { it["name"]!!.jsonPrimitive.content }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,18 +63,19 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||||
.appendQueryParameter("q", query)
|
.appendQueryParameter("q", query)
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(GET(url.toString())).await().use {
|
authClient.newCall(GET(url.toString()))
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.parseAs<JsonObject>()
|
||||||
response["data"]!!.jsonArray
|
.let {
|
||||||
.map { data -> data.jsonObject["node"]!!.jsonObject }
|
it["data"]!!.jsonArray
|
||||||
.map { node ->
|
.map { data -> data.jsonObject["node"]!!.jsonObject }
|
||||||
val id = node["id"]!!.jsonPrimitive.int
|
.map { node ->
|
||||||
async { getMangaDetails(id) }
|
val id = node["id"]!!.jsonPrimitive.int
|
||||||
}
|
async { getMangaDetails(id) }
|
||||||
.awaitAll()
|
}
|
||||||
.filter { trackSearch -> trackSearch.publishing_type != "novel" }
|
.awaitAll()
|
||||||
}
|
.filter { trackSearch -> trackSearch.publishing_type != "novel" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,27 +85,28 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||||
.appendPath(id.toString())
|
.appendPath(id.toString())
|
||||||
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
|
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(GET(url.toString())).await().use {
|
authClient.newCall(GET(url.toString()))
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
val response = json.decodeFromString<JsonObject>(responseBody)
|
.parseAs<JsonObject>()
|
||||||
val obj = response.jsonObject
|
.let {
|
||||||
TrackSearch.create(TrackManager.MYANIMELIST).apply {
|
val obj = it.jsonObject
|
||||||
media_id = obj["id"]!!.jsonPrimitive.int
|
TrackSearch.create(TrackManager.MYANIMELIST).apply {
|
||||||
title = obj["title"]!!.jsonPrimitive.content
|
media_id = obj["id"]!!.jsonPrimitive.int
|
||||||
summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
|
title = obj["title"]!!.jsonPrimitive.content
|
||||||
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
|
summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
|
||||||
cover_url = obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content ?: ""
|
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
|
||||||
tracking_url = "https://myanimelist.net/manga/$media_id"
|
cover_url = obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content ?: ""
|
||||||
publishing_status = obj["status"]!!.jsonPrimitive.content.replace("_", " ")
|
tracking_url = "https://myanimelist.net/manga/$media_id"
|
||||||
publishing_type = obj["media_type"]!!.jsonPrimitive.content.replace("_", " ")
|
publishing_status = obj["status"]!!.jsonPrimitive.content.replace("_", " ")
|
||||||
start_date = try {
|
publishing_type = obj["media_type"]!!.jsonPrimitive.content.replace("_", " ")
|
||||||
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
start_date = try {
|
||||||
outputDf.format(obj["start_date"]!!)
|
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||||
} catch (e: Exception) {
|
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())
|
.url(mangaUrl(track.media_id).toString())
|
||||||
.put(formBody)
|
.put(formBody)
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(request).await().use {
|
authClient.newCall(request)
|
||||||
parseMangaItem(it, track)
|
.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())
|
.url(mangaUrl(track.media_id).toString())
|
||||||
.put(formBody)
|
.put(formBody)
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(request).await().use {
|
authClient.newCall(request)
|
||||||
parseMangaItem(it, track)
|
.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())
|
.url(mangaUrl(track.media_id).toString())
|
||||||
.put(formBody)
|
.put(formBody)
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(request).await().use {
|
authClient.newCall(request)
|
||||||
parseMangaItem(it, track)
|
.await()
|
||||||
}
|
.parseAs<JsonObject>()
|
||||||
|
.let { parseMangaItem(it, track) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMangaItem(response: Response, track: Track): Track {
|
private fun parseMangaItem(response: JsonObject, track: Track): Track {
|
||||||
val responseBody = response.body?.string().orEmpty()
|
val obj = response.jsonObject
|
||||||
val obj = json.decodeFromString<JsonObject>(responseBody).jsonObject
|
|
||||||
return track.apply {
|
return track.apply {
|
||||||
val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean
|
val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean
|
||||||
status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]!!.jsonPrimitive.content)
|
status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]!!.jsonPrimitive.content)
|
||||||
|
|
|
@ -7,8 +7,10 @@ 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.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import kotlinx.serialization.decodeFromString
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
@ -22,45 +24,51 @@ import okhttp3.FormBody
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
|
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 jsonMime = "application/json; charset=utf-8".toMediaType()
|
||||||
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track, user_id: String): Track {
|
suspend fun addLibManga(track: Track, user_id: String): Track {
|
||||||
val payload = buildJsonObject {
|
return withContext(Dispatchers.IO) {
|
||||||
putJsonObject("user_rate") {
|
val payload = buildJsonObject {
|
||||||
put("user_id", user_id)
|
putJsonObject("user_rate") {
|
||||||
put("target_id", track.media_id)
|
put("user_id", user_id)
|
||||||
put("target_type", "Manga")
|
put("target_id", track.media_id)
|
||||||
put("chapters", track.last_chapter_read)
|
put("target_type", "Manga")
|
||||||
put("score", track.score.toInt())
|
put("chapters", track.last_chapter_read)
|
||||||
put("status", track.toShikimoriStatus())
|
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 updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id)
|
||||||
|
|
||||||
suspend fun search(search: String): List<TrackSearch> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
val url = "$apiUrl/mangas".toUri().buildUpon()
|
return withContext(Dispatchers.IO) {
|
||||||
.appendQueryParameter("order", "popularity")
|
val url = "$apiUrl/mangas".toUri().buildUpon()
|
||||||
.appendQueryParameter("search", search)
|
.appendQueryParameter("order", "popularity")
|
||||||
.appendQueryParameter("limit", "20")
|
.appendQueryParameter("search", search)
|
||||||
.build()
|
.appendQueryParameter("limit", "20")
|
||||||
return authClient.newCall(GET(url.toString())).await().use {
|
.build()
|
||||||
val responseBody = it.body?.string().orEmpty()
|
authClient.newCall(GET(url.toString()))
|
||||||
if (responseBody.isEmpty()) {
|
.await()
|
||||||
throw Exception("Null Response")
|
.parseAs<JsonArray>()
|
||||||
}
|
.let { response ->
|
||||||
val response = json.decodeFromString<JsonArray>(responseBody)
|
response.map {
|
||||||
response.map { jsonToSearch(it.jsonObject) }
|
jsonToSearch(it.jsonObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,47 +99,50 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track, user_id: String): Track? {
|
suspend fun findLibManga(track: Track, user_id: String): Track? {
|
||||||
val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
|
return withContext(Dispatchers.IO) {
|
||||||
.appendPath(track.media_id.toString())
|
val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
|
||||||
.build()
|
.appendPath(track.media_id.toString())
|
||||||
val mangas = authClient.newCall(GET(urlMangas.toString())).await().use {
|
.build()
|
||||||
val responseBody = it.body?.string().orEmpty()
|
val mangas = authClient.newCall(GET(urlMangas.toString()))
|
||||||
json.decodeFromString<JsonObject>(responseBody)
|
.await()
|
||||||
}
|
.parseAs<JsonObject>()
|
||||||
|
|
||||||
val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
|
val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
|
||||||
.appendQueryParameter("user_id", user_id)
|
.appendQueryParameter("user_id", user_id)
|
||||||
.appendQueryParameter("target_id", track.media_id.toString())
|
.appendQueryParameter("target_id", track.media_id.toString())
|
||||||
.appendQueryParameter("target_type", "Manga")
|
.appendQueryParameter("target_type", "Manga")
|
||||||
.build()
|
.build()
|
||||||
return authClient.newCall(GET(url.toString())).await().use {
|
authClient.newCall(GET(url.toString()))
|
||||||
val responseBody = it.body?.string().orEmpty()
|
.await()
|
||||||
if (responseBody.isEmpty()) {
|
.parseAs<JsonArray>()
|
||||||
throw Exception("Null Response")
|
.let { response ->
|
||||||
}
|
if (response.size > 1) {
|
||||||
val response = json.decodeFromString<JsonArray>(responseBody)
|
throw Exception("Too much mangas in response")
|
||||||
if (response.size > 1) {
|
}
|
||||||
throw Exception("Too much mangas in response")
|
val entry = response.map {
|
||||||
}
|
jsonToTrack(it.jsonObject, mangas)
|
||||||
val entry = response.map {
|
}
|
||||||
jsonToTrack(it.jsonObject, mangas)
|
entry.firstOrNull()
|
||||||
}
|
}
|
||||||
entry.firstOrNull()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentUser(): Int {
|
fun getCurrentUser(): Int {
|
||||||
val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body?.string()!!
|
return runBlocking {
|
||||||
return json.decodeFromString<JsonObject>(user)["id"]!!.jsonPrimitive.int
|
authClient.newCall(GET("$apiUrl/users/whoami"))
|
||||||
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
it["id"]!!.jsonPrimitive.int
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun accessToken(code: String): OAuth {
|
suspend fun accessToken(code: String): OAuth {
|
||||||
return client.newCall(accessTokenRequest(code)).await().use {
|
return withContext(Dispatchers.IO) {
|
||||||
val responseBody = it.body?.string().orEmpty()
|
client.newCall(accessTokenRequest(code))
|
||||||
if (responseBody.isEmpty()) {
|
.await()
|
||||||
throw Exception("Null Response")
|
.parseAs()
|
||||||
}
|
|
||||||
json.decodeFromString(responseBody)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import rx.Observable
|
||||||
import rx.Producer
|
import rx.Producer
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.fullType
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
@ -111,8 +112,10 @@ fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListene
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T> Response.parseAs(): T {
|
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 {
|
this.use {
|
||||||
val responseBody = it.body?.string().orEmpty()
|
val responseBody = it.body?.string().orEmpty()
|
||||||
return Injekt.get<Json>().decodeFromString<T>(responseBody)
|
return json.decodeFromString(responseBody)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue