Refactor Kitsu API to remove Retrofit usage
This commit is contained in:
parent
07e76f35fa
commit
17b70ab38c
5 changed files with 187 additions and 180 deletions
|
@ -170,11 +170,6 @@ dependencies {
|
||||||
// TLS 1.3 support for Android < 10
|
// TLS 1.3 support for Android < 10
|
||||||
implementation("org.conscrypt:conscrypt-android:2.5.1")
|
implementation("org.conscrypt:conscrypt-android:2.5.1")
|
||||||
|
|
||||||
// REST
|
|
||||||
val retrofitVersion = "2.9.0"
|
|
||||||
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
|
|
||||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0")
|
|
||||||
|
|
||||||
// JSON
|
// JSON
|
||||||
val kotlinSerializationVersion = "1.0.1"
|
val kotlinSerializationVersion = "1.0.1"
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion")
|
||||||
|
|
|
@ -6,10 +6,10 @@ 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 eu.kanade.tachiyomi.network.jsonMime
|
||||||
import eu.kanade.tachiyomi.network.parseAs
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
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
|
||||||
import kotlinx.serialization.json.contentOrNull
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
@ -21,17 +21,12 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
import kotlinx.serialization.json.long
|
import kotlinx.serialization.json.long
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonObject
|
import kotlinx.serialization.json.putJsonObject
|
||||||
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
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
||||||
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
|
||||||
|
|
||||||
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): Track {
|
suspend fun addLibManga(track: Track): Track {
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package eu.kanade.tachiyomi.data.track.kitsu
|
package eu.kanade.tachiyomi.data.track.kitsu
|
||||||
|
|
||||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
import androidx.core.net.toUri
|
||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
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.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import kotlinx.serialization.json.Json
|
import eu.kanade.tachiyomi.network.await
|
||||||
|
import eu.kanade.tachiyomi.network.jsonMime
|
||||||
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import kotlinx.serialization.json.int
|
import kotlinx.serialization.json.int
|
||||||
|
@ -14,226 +19,236 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonObject
|
import kotlinx.serialization.json.putJsonObject
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.Headers.Companion.headersOf
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import okhttp3.Request
|
||||||
import retrofit2.http.Body
|
import okhttp3.RequestBody
|
||||||
import retrofit2.http.Field
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import retrofit2.http.FormUrlEncoded
|
|
||||||
import retrofit2.http.GET
|
|
||||||
import retrofit2.http.Header
|
|
||||||
import retrofit2.http.Headers
|
|
||||||
import retrofit2.http.PATCH
|
|
||||||
import retrofit2.http.POST
|
|
||||||
import retrofit2.http.Path
|
|
||||||
import retrofit2.http.Query
|
|
||||||
|
|
||||||
class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) {
|
class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) {
|
||||||
|
|
||||||
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||||
|
|
||||||
private val rest = Retrofit.Builder()
|
|
||||||
.baseUrl(baseUrl)
|
|
||||||
.client(authClient)
|
|
||||||
.addConverterFactory(jsonConverter)
|
|
||||||
.build()
|
|
||||||
.create(Rest::class.java)
|
|
||||||
|
|
||||||
private val searchRest = Retrofit.Builder()
|
|
||||||
.baseUrl(algoliaKeyUrl)
|
|
||||||
.client(authClient)
|
|
||||||
.addConverterFactory(jsonConverter)
|
|
||||||
.build()
|
|
||||||
.create(SearchKeyRest::class.java)
|
|
||||||
|
|
||||||
private val algoliaRest = Retrofit.Builder()
|
|
||||||
.baseUrl(algoliaUrl)
|
|
||||||
.client(client)
|
|
||||||
.addConverterFactory(jsonConverter)
|
|
||||||
.build()
|
|
||||||
.create(AgoliaSearchRest::class.java)
|
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track, userId: String): Track {
|
suspend fun addLibManga(track: Track, userId: String): Track {
|
||||||
val data = buildJsonObject {
|
return withContext(Dispatchers.IO) {
|
||||||
putJsonObject("data") {
|
val data = buildJsonObject {
|
||||||
put("type", "libraryEntries")
|
putJsonObject("data") {
|
||||||
putJsonObject("attributes") {
|
put("type", "libraryEntries")
|
||||||
put("status", track.toKitsuStatus())
|
putJsonObject("attributes") {
|
||||||
put("progress", track.last_chapter_read)
|
put("status", track.toKitsuStatus())
|
||||||
}
|
put("progress", track.last_chapter_read)
|
||||||
putJsonObject("relationships") {
|
|
||||||
putJsonObject("user") {
|
|
||||||
putJsonObject("data") {
|
|
||||||
put("id", userId)
|
|
||||||
put("type", "users")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
putJsonObject("media") {
|
putJsonObject("relationships") {
|
||||||
putJsonObject("data") {
|
putJsonObject("user") {
|
||||||
put("id", track.media_id)
|
putJsonObject("data") {
|
||||||
put("type", "manga")
|
put("id", userId)
|
||||||
|
put("type", "users")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putJsonObject("media") {
|
||||||
|
putJsonObject("data") {
|
||||||
|
put("id", track.media_id)
|
||||||
|
put("type", "manga")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val json = rest.addLibManga(data)
|
authClient.newCall(
|
||||||
track.media_id = json["data"]!!.jsonObject["id"]!!.jsonPrimitive.int
|
POST(
|
||||||
return track
|
"${baseUrl}library-entries",
|
||||||
|
headers = headersOf(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.api+json"
|
||||||
|
),
|
||||||
|
body = data.toString().toRequestBody("application/vnd.api+json".toMediaType())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
track.media_id = it["data"]!!.jsonObject["id"]!!.jsonPrimitive.int
|
||||||
|
track
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateLibManga(track: Track): Track {
|
suspend fun updateLibManga(track: Track): Track {
|
||||||
val data = buildJsonObject {
|
return withContext(Dispatchers.IO) {
|
||||||
putJsonObject("data") {
|
val data = buildJsonObject {
|
||||||
put("type", "libraryEntries")
|
putJsonObject("data") {
|
||||||
put("id", track.media_id)
|
put("type", "libraryEntries")
|
||||||
putJsonObject("attributes") {
|
put("id", track.media_id)
|
||||||
put("status", track.toKitsuStatus())
|
putJsonObject("attributes") {
|
||||||
put("progress", track.last_chapter_read)
|
put("status", track.toKitsuStatus())
|
||||||
put("ratingTwenty", track.toKitsuScore())
|
put("progress", track.last_chapter_read)
|
||||||
|
put("ratingTwenty", track.toKitsuScore())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
rest.updateLibManga(track.media_id, data)
|
authClient.newCall(
|
||||||
return track
|
Request.Builder()
|
||||||
|
.url("${baseUrl}library-entries/${track.media_id}")
|
||||||
|
.headers(
|
||||||
|
headersOf(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.api+json"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.patch(data.toString().toRequestBody("application/vnd.api+json".toMediaType()))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
track
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(query: String): List<TrackSearch> {
|
suspend fun search(query: String): List<TrackSearch> {
|
||||||
val json = searchRest.getKey()
|
return withContext(Dispatchers.IO) {
|
||||||
val key = json["media"]!!.jsonObject["key"]!!.jsonPrimitive.content
|
authClient.newCall(GET(algoliaKeyUrl))
|
||||||
return algoliaSearch(key, query)
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
val key = it["media"]!!.jsonObject["key"]!!.jsonPrimitive.content
|
||||||
|
algoliaSearch(key, query)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
|
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
|
||||||
val jsonObject = buildJsonObject {
|
return withContext(Dispatchers.IO) {
|
||||||
put("params", "query=$query$algoliaFilter")
|
val jsonObject = buildJsonObject {
|
||||||
|
put("params", "query=$query$algoliaFilter")
|
||||||
|
}
|
||||||
|
|
||||||
|
client.newCall(
|
||||||
|
POST(
|
||||||
|
algoliaUrl,
|
||||||
|
headers = headersOf(
|
||||||
|
"X-Algolia-Application-Id",
|
||||||
|
algoliaAppId,
|
||||||
|
"X-Algolia-API-Key",
|
||||||
|
key,
|
||||||
|
),
|
||||||
|
body = jsonObject.toString().toRequestBody(jsonMime)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
it["hits"]!!.jsonArray
|
||||||
|
.map { KitsuSearchManga(it.jsonObject) }
|
||||||
|
.filter { it.subType != "novel" }
|
||||||
|
.map { it.toTrack() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val json = algoliaRest.getSearchQuery(algoliaAppId, key, jsonObject)
|
|
||||||
val data = json["hits"]!!.jsonArray
|
|
||||||
return data.map { KitsuSearchManga(it.jsonObject) }
|
|
||||||
.filter { it.subType != "novel" }
|
|
||||||
.map { it.toTrack() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track, userId: String): Track? {
|
suspend fun findLibManga(track: Track, userId: String): Track? {
|
||||||
val json = rest.findLibManga(track.media_id, userId)
|
return withContext(Dispatchers.IO) {
|
||||||
val data = json["data"]!!.jsonArray
|
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
||||||
return if (data.size > 0) {
|
.encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId")
|
||||||
val manga = json["included"]!!.jsonArray[0].jsonObject
|
.appendQueryParameter("include", "manga")
|
||||||
KitsuLibManga(data[0].jsonObject, manga).toTrack()
|
.build()
|
||||||
} else {
|
authClient.newCall(GET(url.toString()))
|
||||||
null
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
val data = it["data"]!!.jsonArray
|
||||||
|
if (data.size > 0) {
|
||||||
|
val manga = it["included"]!!.jsonArray[0].jsonObject
|
||||||
|
KitsuLibManga(data[0].jsonObject, manga).toTrack()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getLibManga(track: Track): Track {
|
suspend fun getLibManga(track: Track): Track {
|
||||||
val json = rest.getLibManga(track.media_id)
|
return withContext(Dispatchers.IO) {
|
||||||
val data = json["data"]!!.jsonArray
|
val url = "${baseUrl}library-entries".toUri().buildUpon()
|
||||||
return if (data.size > 0) {
|
.encodedQuery("filter[id]=${track.media_id}")
|
||||||
val manga = json["included"]!!.jsonArray[0].jsonObject
|
.appendQueryParameter("include", "manga")
|
||||||
KitsuLibManga(data[0].jsonObject, manga).toTrack()
|
.build()
|
||||||
} else {
|
authClient.newCall(GET(url.toString()))
|
||||||
throw Exception("Could not find manga")
|
.await()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let {
|
||||||
|
val data = it["data"]!!.jsonArray
|
||||||
|
if (data.size > 0) {
|
||||||
|
val manga = it["included"]!!.jsonArray[0].jsonObject
|
||||||
|
KitsuLibManga(data[0].jsonObject, manga).toTrack()
|
||||||
|
} else {
|
||||||
|
throw Exception("Could not find manga")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun login(username: String, password: String): OAuth {
|
suspend fun login(username: String, password: String): OAuth {
|
||||||
return Retrofit.Builder()
|
return withContext(Dispatchers.IO) {
|
||||||
.baseUrl(loginUrl)
|
val formBody: RequestBody = FormBody.Builder()
|
||||||
.client(client)
|
.add("username", username)
|
||||||
.addConverterFactory(jsonConverter)
|
.add("password", password)
|
||||||
.build()
|
.add("grant_type", "password")
|
||||||
.create(LoginRest::class.java)
|
.add("client_id", clientId)
|
||||||
.requestAccessToken(username, password)
|
.add("client_secret", clientSecret)
|
||||||
|
.build()
|
||||||
|
client.newCall(POST(loginUrl, body = formBody))
|
||||||
|
.await()
|
||||||
|
.parseAs()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentUser(): String {
|
suspend fun getCurrentUser(): String {
|
||||||
return rest.getCurrentUser()["data"]!!.jsonArray[0].jsonObject["id"]!!.jsonPrimitive.content
|
return withContext(Dispatchers.IO) {
|
||||||
}
|
val url = "${baseUrl}users".toUri().buildUpon()
|
||||||
|
.encodedQuery("filter[self]=true")
|
||||||
private interface Rest {
|
.build()
|
||||||
|
authClient.newCall(GET(url.toString()))
|
||||||
@Headers("Content-Type: application/vnd.api+json")
|
.await()
|
||||||
@POST("library-entries")
|
.parseAs<JsonObject>()
|
||||||
suspend fun addLibManga(
|
.let {
|
||||||
@Body data: JsonObject
|
it["data"]!!.jsonArray[0].jsonObject["id"]!!.jsonPrimitive.content
|
||||||
): JsonObject
|
}
|
||||||
|
}
|
||||||
@Headers("Content-Type: application/vnd.api+json")
|
|
||||||
@PATCH("library-entries/{id}")
|
|
||||||
suspend fun updateLibManga(
|
|
||||||
@Path("id") remoteId: Int,
|
|
||||||
@Body data: JsonObject
|
|
||||||
): JsonObject
|
|
||||||
|
|
||||||
@GET("library-entries")
|
|
||||||
suspend fun findLibManga(
|
|
||||||
@Query("filter[manga_id]", encoded = true) remoteId: Int,
|
|
||||||
@Query("filter[user_id]", encoded = true) userId: String,
|
|
||||||
@Query("include") includes: String = "manga"
|
|
||||||
): JsonObject
|
|
||||||
|
|
||||||
@GET("library-entries")
|
|
||||||
suspend fun getLibManga(
|
|
||||||
@Query("filter[id]", encoded = true) remoteId: Int,
|
|
||||||
@Query("include") includes: String = "manga"
|
|
||||||
): JsonObject
|
|
||||||
|
|
||||||
@GET("users")
|
|
||||||
suspend fun getCurrentUser(
|
|
||||||
@Query("filter[self]", encoded = true) self: Boolean = true
|
|
||||||
): JsonObject
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface SearchKeyRest {
|
|
||||||
@GET("media/")
|
|
||||||
suspend fun getKey(): JsonObject
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface AgoliaSearchRest {
|
|
||||||
@POST("query/")
|
|
||||||
suspend fun getSearchQuery(@Header("X-Algolia-Application-Id") appid: String, @Header("X-Algolia-API-Key") key: String, @Body json: JsonObject): JsonObject
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface LoginRest {
|
|
||||||
|
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("oauth/token")
|
|
||||||
suspend fun requestAccessToken(
|
|
||||||
@Field("username") username: String,
|
|
||||||
@Field("password") password: String,
|
|
||||||
@Field("grant_type") grantType: String = "password",
|
|
||||||
@Field("client_id") client_id: String = clientId,
|
|
||||||
@Field("client_secret") client_secret: String = clientSecret
|
|
||||||
): OAuth
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId = "dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
|
private const val clientId =
|
||||||
private const val clientSecret = "54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
|
"dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
|
||||||
private const val baseUrl = "https://kitsu.io/api/edge/"
|
private const val clientSecret =
|
||||||
private const val loginUrl = "https://kitsu.io/api/"
|
"54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
|
||||||
private const val baseMangaUrl = "https://kitsu.io/manga/"
|
|
||||||
private const val algoliaKeyUrl = "https://kitsu.io/api/edge/algolia-keys/"
|
|
||||||
private const val algoliaUrl = "https://AWQO5J657S-dsn.algolia.net/1/indexes/production_media/"
|
|
||||||
private const val algoliaAppId = "AWQO5J657S"
|
|
||||||
private const val algoliaFilter = "&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=%5B%22synopsis%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
|
|
||||||
|
|
||||||
private val jsonConverter = Json { ignoreUnknownKeys = true }.asConverterFactory("application/json".toMediaType())
|
private const val baseUrl = "https://kitsu.io/api/edge/"
|
||||||
|
private const val loginUrl = "https://kitsu.io/api/oauth/token"
|
||||||
|
private const val baseMangaUrl = "https://kitsu.io/manga/"
|
||||||
|
private const val algoliaKeyUrl = "https://kitsu.io/api/edge/algolia-keys/media/"
|
||||||
|
|
||||||
|
private const val algoliaUrl =
|
||||||
|
"https://AWQO5J657S-dsn.algolia.net/1/indexes/production_media/query/"
|
||||||
|
private const val algoliaAppId = "AWQO5J657S"
|
||||||
|
private const val algoliaFilter =
|
||||||
|
"&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=%5B%22synopsis%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
|
||||||
|
|
||||||
fun mangaUrl(remoteId: Int): String {
|
fun mangaUrl(remoteId: Int): String {
|
||||||
return baseMangaUrl + remoteId
|
return baseMangaUrl + remoteId
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshTokenRequest(token: String) = POST(
|
fun refreshTokenRequest(token: String) = POST(
|
||||||
"${loginUrl}oauth/token",
|
loginUrl,
|
||||||
body = FormBody.Builder()
|
body = FormBody.Builder()
|
||||||
.add("grant_type", "refresh_token")
|
.add("grant_type", "refresh_token")
|
||||||
|
.add("refresh_token", token)
|
||||||
.add("client_id", clientId)
|
.add("client_id", clientId)
|
||||||
.add("client_secret", clientSecret)
|
.add("client_secret", clientSecret)
|
||||||
.add("refresh_token", token)
|
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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.jsonMime
|
||||||
import eu.kanade.tachiyomi.network.parseAs
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -21,13 +22,11 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonObject
|
import kotlinx.serialization.json.putJsonObject
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
|
||||||
class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
|
class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Callback
|
import okhttp3.Callback
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
@ -19,6 +20,8 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
|
val jsonMime = "application/json; charset=utf-8".toMediaType()
|
||||||
|
|
||||||
fun Call.asObservable(): Observable<Response> {
|
fun Call.asObservable(): Observable<Response> {
|
||||||
return Observable.unsafeCreate { subscriber ->
|
return Observable.unsafeCreate { subscriber ->
|
||||||
// Since Call is a one-shot type, clone it for each new subscriber.
|
// Since Call is a one-shot type, clone it for each new subscriber.
|
||||||
|
|
Reference in a new issue