mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Convert Bangumi to kotlinx.serialization
This commit is contained in:
parent
07f5056b45
commit
d2c1582a48
9 changed files with 226 additions and 170 deletions
|
@ -0,0 +1,10 @@
|
||||||
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Avatar(
|
||||||
|
val large: String? = "",
|
||||||
|
val medium: String? = "",
|
||||||
|
val small: String? = "",
|
||||||
|
)
|
|
@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
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 eu.kanade.tachiyomi.data.track.updateNewTrackInfo
|
import eu.kanade.tachiyomi.data.track.updateNewTrackInfo
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
@ -17,9 +19,9 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
@StringRes
|
@StringRes
|
||||||
override fun nameRes() = R.string.bangumi
|
override fun nameRes() = R.string.bangumi
|
||||||
|
|
||||||
private val gson: Gson by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
private val interceptor by lazy { BangumiInterceptor(this, gson) }
|
private val interceptor by lazy { BangumiInterceptor(this) }
|
||||||
|
|
||||||
private val api by lazy { BangumiApi(client, interceptor) }
|
private val api by lazy { BangumiApi(client, interceptor) }
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
override suspend fun add(track: Track): Track {
|
override suspend fun add(track: Track): Track {
|
||||||
track.score = DEFAULT_SCORE.toFloat()
|
track.score = DEFAULT_SCORE.toFloat()
|
||||||
track.status = DEFAULT_STATUS
|
track.status = DEFAULT_STATUS
|
||||||
updateNewTrackInfo(track, PLANNING)
|
updateNewTrackInfo(track, PLAN_TO_READ)
|
||||||
api.addLibManga(track)
|
api.addLibManga(track)
|
||||||
return update(track)
|
return update(track)
|
||||||
}
|
}
|
||||||
|
@ -78,22 +80,22 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
override fun getLogoColor() = Color.rgb(240, 145, 153)
|
override fun getLogoColor() = Color.rgb(240, 145, 153)
|
||||||
|
|
||||||
override fun getStatusList(): List<Int> {
|
override fun getStatusList(): List<Int> {
|
||||||
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLANNING)
|
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCompletedStatus(index: Int) = getStatusList()[index] == COMPLETED
|
override fun isCompletedStatus(index: Int) = getStatusList()[index] == COMPLETED
|
||||||
|
|
||||||
override fun completedStatus(): Int = COMPLETED
|
override fun completedStatus(): Int = COMPLETED
|
||||||
override fun readingStatus() = READING
|
override fun readingStatus() = READING
|
||||||
override fun planningStatus() = PLANNING
|
override fun planningStatus() = PLAN_TO_READ
|
||||||
|
|
||||||
override fun getStatus(status: Int): String = with(context) {
|
override fun getStatus(status: Int): String = with(context) {
|
||||||
when (status) {
|
when (status) {
|
||||||
READING -> getString(R.string.reading)
|
READING -> getString(R.string.reading)
|
||||||
|
PLAN_TO_READ -> getString(R.string.plan_to_read)
|
||||||
COMPLETED -> getString(R.string.completed)
|
COMPLETED -> getString(R.string.completed)
|
||||||
ON_HOLD -> getString(R.string.on_hold)
|
ON_HOLD -> getString(R.string.on_hold)
|
||||||
DROPPED -> getString(R.string.dropped)
|
DROPPED -> getString(R.string.dropped)
|
||||||
PLANNING -> getString(R.string.plan_to_read)
|
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +103,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
override fun getGlobalStatus(status: Int): String = with(context) {
|
override fun getGlobalStatus(status: Int): String = with(context) {
|
||||||
when (status) {
|
when (status) {
|
||||||
READING -> getString(R.string.reading)
|
READING -> getString(R.string.reading)
|
||||||
PLANNING -> getString(R.string.plan_to_read)
|
PLAN_TO_READ -> getString(R.string.plan_to_read)
|
||||||
COMPLETED -> getString(R.string.completed)
|
COMPLETED -> getString(R.string.completed)
|
||||||
ON_HOLD -> getString(R.string.on_hold)
|
ON_HOLD -> getString(R.string.on_hold)
|
||||||
DROPPED -> getString(R.string.dropped)
|
DROPPED -> getString(R.string.dropped)
|
||||||
|
@ -109,7 +111,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun login(username: String, password: String): Boolean = login(password)
|
override suspend fun login(username: String, password: String) = login(password)
|
||||||
|
|
||||||
suspend fun login(code: String): Boolean {
|
suspend fun login(code: String): Boolean {
|
||||||
try {
|
try {
|
||||||
|
@ -125,13 +127,12 @@ class Bangumi(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
|
||||||
}
|
}
|
||||||
|
@ -140,10 +141,11 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
||||||
override fun logout() {
|
override fun logout() {
|
||||||
super.logout()
|
super.logout()
|
||||||
preferences.trackToken(this).delete()
|
preferences.trackToken(this).delete()
|
||||||
|
interceptor.newAuth(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PLANNING = 1
|
const val PLAN_TO_READ = 1
|
||||||
const val COMPLETED = 2
|
const val COMPLETED = 2
|
||||||
const val READING = 3
|
const val READING = 3
|
||||||
const val ON_HOLD = 4
|
const val ON_HOLD = 4
|
||||||
|
|
|
@ -1,152 +1,180 @@
|
||||||
package eu.kanade.tachiyomi.data.track.bangumi
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.github.salomonbrys.kotson.array
|
|
||||||
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.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import kotlinx.coroutines.Dispatchers
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
import kotlinx.coroutines.withContext
|
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
import kotlinx.serialization.json.int
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
|
class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
|
||||||
|
|
||||||
private val gson: Gson by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
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().add("rating", track.score.toInt().toString())
|
return withIOContext {
|
||||||
.add("status", track.toBangumiStatus()).build()
|
val body = FormBody.Builder()
|
||||||
val request =
|
.add("rating", track.score.toInt().toString())
|
||||||
Request.Builder().url("$apiUrl/collection/${track.media_id}/update").post(body).build()
|
.add("status", track.toBangumiStatus())
|
||||||
val response = authClient.newCall(request).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 {
|
||||||
// chapter update
|
return withIOContext {
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val body =
|
|
||||||
FormBody.Builder().add("watched_eps", track.last_chapter_read.toInt().toString()).build()
|
|
||||||
val request =
|
|
||||||
Request.Builder().url("$apiUrl/subject/${track.media_id}/update/watched_eps")
|
|
||||||
.post(body).build()
|
|
||||||
|
|
||||||
// read status update
|
// read status update
|
||||||
val sbody = FormBody.Builder().add("status", track.toBangumiStatus()).build()
|
val sbody = FormBody.Builder()
|
||||||
val srequest =
|
.add("rating", track.score.toInt().toString())
|
||||||
Request.Builder().url("$apiUrl/collection/${track.media_id}/update").post(sbody)
|
.add("status", track.toBangumiStatus())
|
||||||
.build()
|
.build()
|
||||||
authClient.newCall(srequest).execute()
|
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody))
|
||||||
authClient.newCall(request).execute()
|
.await()
|
||||||
|
|
||||||
|
// chapter update
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("watched_eps", track.last_chapter_read.toInt().toString())
|
||||||
|
.build()
|
||||||
|
authClient.newCall(
|
||||||
|
POST(
|
||||||
|
"$apiUrl/subject/${track.media_id}/update/watched_eps",
|
||||||
|
body = body,
|
||||||
|
),
|
||||||
|
).await()
|
||||||
|
|
||||||
track
|
track
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(search: String): List<TrackSearch> {
|
suspend fun search(search: String): List<TrackSearch> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withIOContext {
|
||||||
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
|
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}"
|
||||||
.toUri().buildUpon().appendQueryParameter("max_results", "20").build()
|
.toUri()
|
||||||
val request = Request.Builder().url(url.toString()).get().build()
|
.buildUpon()
|
||||||
|
.appendQueryParameter("max_results", "20")
|
||||||
val netResponse = authClient.newCall(request).await()
|
.build()
|
||||||
var responseBody = netResponse.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 = JsonParser.parseString(responseBody).obj["list"]?.array
|
if (responseBody.contains("\"code\":404")) {
|
||||||
if (response != null) {
|
responseBody = "{\"results\":0,\"list\":[]}"
|
||||||
response.filter { it.obj["type"].asInt == 1 }?.map { jsonToSearch(it.obj) }
|
}
|
||||||
} else {
|
val response = json.decodeFromString<JsonObject>(responseBody)["list"]?.jsonArray
|
||||||
listOf()
|
response?.filter { it.jsonObject["type"]?.jsonPrimitive?.int == 1 }
|
||||||
}
|
?.map { jsonToSearch(it.jsonObject) }.orEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun jsonToSearch(obj: JsonObject): TrackSearch {
|
private fun jsonToSearch(obj: JsonObject): TrackSearch {
|
||||||
return TrackSearch.create(TrackManager.BANGUMI).apply {
|
val coverUrl = if (obj["images"] is JsonObject) {
|
||||||
media_id = obj["id"].asInt
|
obj["images"]?.jsonObject?.get("common")?.jsonPrimitive?.contentOrNull ?: ""
|
||||||
title = obj["name_cn"].asString
|
} else {
|
||||||
cover_url = obj["images"].obj["common"].asString
|
// Sometimes JsonNull
|
||||||
summary = obj["name"].asString
|
""
|
||||||
tracking_url = obj["url"].asString
|
|
||||||
}
|
}
|
||||||
}
|
val totalChapters = if (obj["eps_count"] != null) {
|
||||||
|
obj["eps_count"]!!.jsonPrimitive.int
|
||||||
private fun jsonToTrack(mangas: JsonObject): Track {
|
} else {
|
||||||
return Track.create(TrackManager.BANGUMI).apply {
|
0
|
||||||
title = mangas["name"].asString
|
}
|
||||||
media_id = mangas["id"].asInt
|
return TrackSearch.create(TrackManager.BANGUMI).apply {
|
||||||
score =
|
media_id = obj["id"]!!.jsonPrimitive.int
|
||||||
if (mangas["rating"] != null) (if (mangas["rating"].isJsonObject) mangas["rating"].obj["score"].asFloat else 0f)
|
title = obj["name_cn"]!!.jsonPrimitive.content
|
||||||
else 0f
|
cover_url = coverUrl
|
||||||
status = Bangumi.DEFAULT_STATUS
|
summary = obj["name"]!!.jsonPrimitive.content
|
||||||
tracking_url = mangas["url"].asString
|
tracking_url = obj["url"]!!.jsonPrimitive.content
|
||||||
|
total_chapters = totalChapters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findLibManga(track: Track): Track? {
|
suspend fun findLibManga(track: Track): Track? {
|
||||||
return withContext(Dispatchers.IO) {
|
return withIOContext {
|
||||||
val urlMangas = "$apiUrl/subject/${track.media_id}"
|
authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
|
||||||
val requestMangas = Request.Builder().url(urlMangas).get().build()
|
.await()
|
||||||
val netResponse = authClient.newCall(requestMangas).execute()
|
.parseAs<JsonObject>()
|
||||||
val responseBody = netResponse.body?.string().orEmpty()
|
.let { jsonToSearch(it) }
|
||||||
jsonToTrack(JsonParser.parseString(responseBody).obj)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun statusLibManga(track: Track): Track? {
|
suspend fun statusLibManga(track: Track): Track? {
|
||||||
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
return withIOContext {
|
||||||
val requestUserRead =
|
val urlUserRead = "$apiUrl/collection/${track.media_id}"
|
||||||
Request.Builder().url(urlUserRead).cacheControl(CacheControl.FORCE_NETWORK).get()
|
val requestUserRead = Request.Builder()
|
||||||
|
.url(urlUserRead)
|
||||||
|
.cacheControl(CacheControl.FORCE_NETWORK)
|
||||||
|
.get()
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
// todo get user readed chapter here
|
// TODO: get user readed chapter here
|
||||||
val response = authClient.newCall(requestUserRead).await()
|
var response = authClient.newCall(requestUserRead).await()
|
||||||
val resp = response.body?.toString()
|
var responseBody = response.body?.string().orEmpty()
|
||||||
val coll = gson.fromJson(resp, Collection::class.java)
|
|
||||||
track.status = coll.status?.id!!
|
|
||||||
track.last_chapter_read = coll.ep_status!!.toFloat()
|
|
||||||
return track
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun accessToken(code: String): OAuth {
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val netResponse = client.newCall(accessTokenRequest(code)).execute()
|
|
||||||
val responseBody = netResponse.body?.string().orEmpty()
|
|
||||||
if (responseBody.isEmpty()) {
|
if (responseBody.isEmpty()) {
|
||||||
throw Exception("Null Response")
|
throw Exception("Null Response")
|
||||||
}
|
}
|
||||||
gson.fromJson(responseBody, OAuth::class.java)
|
if (responseBody.contains("\"code\":400")) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
json.decodeFromString<Collection>(responseBody).let {
|
||||||
|
track.status = it.status?.id!!
|
||||||
|
track.last_chapter_read = it.ep_status!!.toFloat()
|
||||||
|
track.score = it.rating!!
|
||||||
|
track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun accessToken(code: String): OAuth {
|
||||||
|
return withIOContext {
|
||||||
|
client.newCall(accessTokenRequest(code))
|
||||||
|
.await()
|
||||||
|
.parseAs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun accessTokenRequest(code: String) = POST(
|
private fun accessTokenRequest(code: String) = POST(
|
||||||
oauthUrl,
|
oauthUrl,
|
||||||
body = FormBody.Builder().add("grant_type", "authorization_code").add("client_id", clientId)
|
body = FormBody.Builder()
|
||||||
.add("client_secret", clientSecret).add("code", code).add("redirect_uri", redirectUrl)
|
.add("grant_type", "authorization_code")
|
||||||
.build()
|
.add("client_id", clientId)
|
||||||
|
.add("client_secret", clientSecret)
|
||||||
|
.add("code", code)
|
||||||
|
.add("redirect_uri", redirectUrl)
|
||||||
|
.build(),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val clientId = "bgm10555cda0762e80ca"
|
private const val clientId = "bgm10555cda0762e80ca"
|
||||||
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
|
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
|
||||||
|
|
||||||
private const val baseUrl = "https://bangumi.org"
|
|
||||||
private const val apiUrl = "https://api.bgm.tv"
|
private const val apiUrl = "https://api.bgm.tv"
|
||||||
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
|
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
|
||||||
private const val loginUrl = "https://bgm.tv/oauth/authorize"
|
private const val loginUrl = "https://bgm.tv/oauth/authorize"
|
||||||
|
@ -158,15 +186,22 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
||||||
return "$baseMangaUrl/$remoteId"
|
return "$baseMangaUrl/$remoteId"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authUrl() = loginUrl.toUri().buildUpon().appendQueryParameter("client_id", clientId)
|
fun authUrl(): Uri =
|
||||||
.appendQueryParameter("response_type", "code")
|
loginUrl.toUri().buildUpon()
|
||||||
.appendQueryParameter("redirect_uri", redirectUrl).build()
|
.appendQueryParameter("client_id", clientId)
|
||||||
|
.appendQueryParameter("response_type", "code")
|
||||||
|
.appendQueryParameter("redirect_uri", redirectUrl)
|
||||||
|
.build()
|
||||||
|
|
||||||
fun refreshTokenRequest(token: String) = POST(
|
fun refreshTokenRequest(token: String) = POST(
|
||||||
oauthUrl,
|
oauthUrl,
|
||||||
body = FormBody.Builder().add("grant_type", "refresh_token").add("client_id", clientId)
|
body = FormBody.Builder()
|
||||||
.add("client_secret", clientSecret).add("refresh_token", token)
|
.add("grant_type", "refresh_token")
|
||||||
.add("redirect_uri", redirectUrl).build()
|
.add("client_id", clientId)
|
||||||
|
.add("client_secret", clientSecret)
|
||||||
|
.add("refresh_token", token)
|
||||||
|
.add("redirect_uri", redirectUrl)
|
||||||
|
.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
package eu.kanade.tachiyomi.data.track.bangumi
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
import com.google.gson.Gson
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
|
class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth object used for authenticated requests.
|
* OAuth object used for authenticated requests.
|
||||||
*/
|
*/
|
||||||
private var oauth: OAuth? = bangumi.restoreToken()
|
private var oauth: OAuth? = bangumi.restoreToken()
|
||||||
|
|
||||||
fun addTocken(tocken: String, oidFormBody: FormBody): FormBody {
|
|
||||||
val newFormBody = FormBody.Builder()
|
|
||||||
for (i in 0 until oidFormBody.size) {
|
|
||||||
newFormBody.add(oidFormBody.name(i), oidFormBody.value(i))
|
|
||||||
}
|
|
||||||
newFormBody.add("access_token", tocken)
|
|
||||||
return newFormBody.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
|
|
||||||
|
@ -29,7 +24,7 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
|
||||||
if (currAuth.isExpired()) {
|
if (currAuth.isExpired()) {
|
||||||
val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!))
|
val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!))
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
@ -39,30 +34,35 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
|
||||||
.header("User-Agent", "Tachiyomi")
|
.header("User-Agent", "Tachiyomi")
|
||||||
.url(
|
.url(
|
||||||
originalRequest.url.newBuilder()
|
originalRequest.url.newBuilder()
|
||||||
.addQueryParameter("access_token", currAuth.access_token).build()
|
.addQueryParameter("access_token", currAuth.access_token).build(),
|
||||||
)
|
)
|
||||||
.build() else originalRequest.newBuilder()
|
.build() else originalRequest.newBuilder()
|
||||||
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
|
.post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||||
.header("User-Agent", "Tachiyomi")
|
.header("User-Agent", "Tachiyomi")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return chain.proceed(authRequest)
|
return chain.proceed(authRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newAuth(oauth: OAuth) {
|
fun newAuth(oauth: OAuth?) {
|
||||||
this.oauth = OAuth(
|
this.oauth = if (oauth == null) null else OAuth(
|
||||||
oauth.access_token,
|
oauth.access_token,
|
||||||
oauth.token_type,
|
oauth.token_type,
|
||||||
System.currentTimeMillis() / 1000,
|
System.currentTimeMillis() / 1000,
|
||||||
oauth.expires_in,
|
oauth.expires_in,
|
||||||
oauth.refresh_token,
|
oauth.refresh_token,
|
||||||
this.oauth?.user_id
|
this.oauth?.user_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
bangumi.saveToken(oauth)
|
bangumi.saveToken(oauth)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearOauth() {
|
private fun addToken(token: String, oidFormBody: FormBody): FormBody {
|
||||||
bangumi.saveToken(null)
|
val newFormBody = FormBody.Builder()
|
||||||
|
for (i in 0 until oidFormBody.size) {
|
||||||
|
newFormBody.add(oidFormBody.name(i), oidFormBody.value(i))
|
||||||
|
}
|
||||||
|
newFormBody.add("access_token", token)
|
||||||
|
return newFormBody.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ fun Track.toBangumiStatus() = when (status) {
|
||||||
Bangumi.COMPLETED -> "collect"
|
Bangumi.COMPLETED -> "collect"
|
||||||
Bangumi.ON_HOLD -> "on_hold"
|
Bangumi.ON_HOLD -> "on_hold"
|
||||||
Bangumi.DROPPED -> "dropped"
|
Bangumi.DROPPED -> "dropped"
|
||||||
Bangumi.PLANNING -> "wish"
|
Bangumi.PLAN_TO_READ -> "wish"
|
||||||
else -> throw NotImplementedError("Unknown status")
|
else -> throw NotImplementedError("Unknown status: $status")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toTrackStatus(status: String) = when (status) {
|
fun toTrackStatus(status: String) = when (status) {
|
||||||
|
@ -16,7 +16,6 @@ fun toTrackStatus(status: String) = when (status) {
|
||||||
"collect" -> Bangumi.COMPLETED
|
"collect" -> Bangumi.COMPLETED
|
||||||
"on_hold" -> Bangumi.ON_HOLD
|
"on_hold" -> Bangumi.ON_HOLD
|
||||||
"dropped" -> Bangumi.DROPPED
|
"dropped" -> Bangumi.DROPPED
|
||||||
"wish" -> Bangumi.PLANNING
|
"wish" -> Bangumi.PLAN_TO_READ
|
||||||
|
else -> throw NotImplementedError("Unknown status: $status")
|
||||||
else -> throw Exception("Unknown status")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,16 @@
|
||||||
package eu.kanade.tachiyomi.data.track.bangumi
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class Collection(
|
data class Collection(
|
||||||
val `private`: Int? = 0,
|
val `private`: Int? = 0,
|
||||||
val comment: String? = "",
|
val comment: String? = "",
|
||||||
val ep_status: Int? = 0,
|
val ep_status: Int? = 0,
|
||||||
val lasttouch: Int? = 0,
|
val lasttouch: Int? = 0,
|
||||||
val rating: Int? = 0,
|
val rating: Float? = 0f,
|
||||||
val status: Status? = Status(),
|
val status: Status? = Status(),
|
||||||
val tag: List<String?>? = listOf(),
|
val tag: List<String?>? = listOf(),
|
||||||
val user: User? = User(),
|
val user: User? = User(),
|
||||||
val vol_status: Int? = 0
|
val vol_status: Int? = 0,
|
||||||
)
|
|
||||||
|
|
||||||
data class OAuth(
|
|
||||||
val access_token: String,
|
|
||||||
val token_type: String,
|
|
||||||
val created_at: Long,
|
|
||||||
val expires_in: Long,
|
|
||||||
val refresh_token: String?,
|
|
||||||
val user_id: Long?
|
|
||||||
) {
|
|
||||||
// Access token refresh before expired
|
|
||||||
fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Status(
|
|
||||||
val id: Int? = 0,
|
|
||||||
val name: String? = "",
|
|
||||||
val type: String? = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
data class User(
|
|
||||||
val avatar: Avatar? = Avatar(),
|
|
||||||
val id: Int? = 0,
|
|
||||||
val nickname: String? = "",
|
|
||||||
val sign: String? = "",
|
|
||||||
val url: String? = "",
|
|
||||||
val usergroup: Int? = 0,
|
|
||||||
val username: String? = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Avatar(
|
|
||||||
val large: String? = "",
|
|
||||||
val medium: String? = "",
|
|
||||||
val small: String? = ""
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OAuth(
|
||||||
|
val access_token: String,
|
||||||
|
val token_type: String,
|
||||||
|
val created_at: Long = System.currentTimeMillis() / 1000,
|
||||||
|
val expires_in: Long,
|
||||||
|
val refresh_token: String?,
|
||||||
|
val user_id: Long?,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Access token refresh before expired
|
||||||
|
fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Status(
|
||||||
|
val id: Int? = 0,
|
||||||
|
val name: String? = "",
|
||||||
|
val type: String? = "",
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package eu.kanade.tachiyomi.data.track.bangumi
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class User(
|
||||||
|
val avatar: Avatar? = Avatar(),
|
||||||
|
val id: Int? = 0,
|
||||||
|
val nickname: String? = "",
|
||||||
|
val sign: String? = "",
|
||||||
|
val url: String? = "",
|
||||||
|
val usergroup: Int? = 0,
|
||||||
|
val username: String? = "",
|
||||||
|
)
|
Loading…
Add table
Add a link
Reference in a new issue