From d2c1582a4805c7a0b8b66f8964516d2541e3411a Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Mon, 25 Apr 2022 23:09:57 -0400 Subject: [PATCH] Convert Bangumi to kotlinx.serialization --- .../tachiyomi/data/track/bangumi/Avatar.kt | 10 + .../tachiyomi/data/track/bangumi/Bangumi.kt | 28 ++- .../data/track/bangumi/BangumiApi.kt | 229 ++++++++++-------- .../data/track/bangumi/BangumiInterceptor.kt | 38 +-- .../data/track/bangumi/BangumiModels.kt | 9 +- .../data/track/bangumi/Collection.kt | 41 +--- .../tachiyomi/data/track/bangumi/OAuth.kt | 17 ++ .../tachiyomi/data/track/bangumi/Status.kt | 10 + .../tachiyomi/data/track/bangumi/User.kt | 14 ++ 9 files changed, 226 insertions(+), 170 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Avatar.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Avatar.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Avatar.kt new file mode 100644 index 0000000000..1cea3438dc --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Avatar.kt @@ -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? = "", +) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt index 9bdddfbcee..402079a494 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt @@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.data.track.bangumi import android.content.Context import android.graphics.Color import androidx.annotation.StringRes -import com.google.gson.Gson import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.model.TrackSearch 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 uy.kohesive.injekt.injectLazy @@ -17,9 +19,9 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) { @StringRes 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) } @@ -39,7 +41,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) { override suspend fun add(track: Track): Track { track.score = DEFAULT_SCORE.toFloat() track.status = DEFAULT_STATUS - updateNewTrackInfo(track, PLANNING) + updateNewTrackInfo(track, PLAN_TO_READ) api.addLibManga(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 getStatusList(): List { - 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 completedStatus(): Int = COMPLETED override fun readingStatus() = READING - override fun planningStatus() = PLANNING + override fun planningStatus() = PLAN_TO_READ override fun getStatus(status: Int): String = with(context) { when (status) { READING -> getString(R.string.reading) + PLAN_TO_READ -> getString(R.string.plan_to_read) COMPLETED -> getString(R.string.completed) ON_HOLD -> getString(R.string.on_hold) DROPPED -> getString(R.string.dropped) - PLANNING -> getString(R.string.plan_to_read) else -> "" } } @@ -101,7 +103,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) { override fun getGlobalStatus(status: Int): String = with(context) { when (status) { 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) ON_HOLD -> getString(R.string.on_hold) 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 { try { @@ -125,13 +127,12 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) { } fun saveToken(oauth: OAuth?) { - val json = gson.toJson(oauth) - preferences.trackToken(this).set(json) + preferences.trackToken(this).set(json.encodeToString(oauth)) } fun restoreToken(): OAuth? { return try { - gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java) + json.decodeFromString(preferences.trackToken(this).get()) } catch (e: Exception) { null } @@ -140,10 +141,11 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) { override fun logout() { super.logout() preferences.trackToken(this).delete() + interceptor.newAuth(null) } companion object { - const val PLANNING = 1 + const val PLAN_TO_READ = 1 const val COMPLETED = 2 const val READING = 3 const val ON_HOLD = 4 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index 417ef1bca9..93c7afb891 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -1,152 +1,180 @@ package eu.kanade.tachiyomi.data.track.bangumi +import android.net.Uri 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.track.TrackManager 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.await -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import eu.kanade.tachiyomi.network.parseAs +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.FormBody import okhttp3.OkHttpClient import okhttp3.Request import uy.kohesive.injekt.injectLazy import java.net.URLEncoder +import java.nio.charset.StandardCharsets 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() suspend fun addLibManga(track: Track): Track { - val body = FormBody.Builder().add("rating", track.score.toInt().toString()) - .add("status", track.toBangumiStatus()).build() - val request = - Request.Builder().url("$apiUrl/collection/${track.media_id}/update").post(body).build() - val response = authClient.newCall(request).await() - return track + return withIOContext { + val body = FormBody.Builder() + .add("rating", track.score.toInt().toString()) + .add("status", track.toBangumiStatus()) + .build() + authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body)) + .await() + track + } } suspend fun updateLibManga(track: Track): Track { - // chapter update - 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() - + return withIOContext { // read status update - val sbody = FormBody.Builder().add("status", track.toBangumiStatus()).build() - val srequest = - Request.Builder().url("$apiUrl/collection/${track.media_id}/update").post(sbody) - .build() - authClient.newCall(srequest).execute() - authClient.newCall(request).execute() + val sbody = FormBody.Builder() + .add("rating", track.score.toInt().toString()) + .add("status", track.toBangumiStatus()) + .build() + authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody)) + .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 } } suspend fun search(search: String): List { - return withContext(Dispatchers.IO) { - val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}" - .toUri().buildUpon().appendQueryParameter("max_results", "20").build() - val request = Request.Builder().url(url.toString()).get().build() - - val netResponse = authClient.newCall(request).await() - var responseBody = netResponse.body?.string().orEmpty() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - if (responseBody.contains("\"code\":404")) { - responseBody = "{\"results\":0,\"list\":[]}" - } - val response = JsonParser.parseString(responseBody).obj["list"]?.array - if (response != null) { - response.filter { it.obj["type"].asInt == 1 }?.map { jsonToSearch(it.obj) } - } else { - listOf() - } + return withIOContext { + val url = "$apiUrl/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}" + .toUri() + .buildUpon() + .appendQueryParameter("max_results", "20") + .build() + authClient.newCall(GET(url.toString())) + .await() + .use { + var responseBody = it.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + if (responseBody.contains("\"code\":404")) { + responseBody = "{\"results\":0,\"list\":[]}" + } + val response = json.decodeFromString(responseBody)["list"]?.jsonArray + response?.filter { it.jsonObject["type"]?.jsonPrimitive?.int == 1 } + ?.map { jsonToSearch(it.jsonObject) }.orEmpty() + } } } private fun jsonToSearch(obj: JsonObject): TrackSearch { - return TrackSearch.create(TrackManager.BANGUMI).apply { - media_id = obj["id"].asInt - title = obj["name_cn"].asString - cover_url = obj["images"].obj["common"].asString - summary = obj["name"].asString - tracking_url = obj["url"].asString + val coverUrl = if (obj["images"] is JsonObject) { + obj["images"]?.jsonObject?.get("common")?.jsonPrimitive?.contentOrNull ?: "" + } else { + // Sometimes JsonNull + "" } - } - - private fun jsonToTrack(mangas: JsonObject): Track { - return Track.create(TrackManager.BANGUMI).apply { - title = mangas["name"].asString - media_id = mangas["id"].asInt - score = - if (mangas["rating"] != null) (if (mangas["rating"].isJsonObject) mangas["rating"].obj["score"].asFloat else 0f) - else 0f - status = Bangumi.DEFAULT_STATUS - tracking_url = mangas["url"].asString + val totalChapters = if (obj["eps_count"] != null) { + obj["eps_count"]!!.jsonPrimitive.int + } else { + 0 + } + return TrackSearch.create(TrackManager.BANGUMI).apply { + media_id = obj["id"]!!.jsonPrimitive.int + title = obj["name_cn"]!!.jsonPrimitive.content + cover_url = coverUrl + summary = obj["name"]!!.jsonPrimitive.content + tracking_url = obj["url"]!!.jsonPrimitive.content + total_chapters = totalChapters } } suspend fun findLibManga(track: Track): Track? { - return withContext(Dispatchers.IO) { - val urlMangas = "$apiUrl/subject/${track.media_id}" - val requestMangas = Request.Builder().url(urlMangas).get().build() - val netResponse = authClient.newCall(requestMangas).execute() - val responseBody = netResponse.body?.string().orEmpty() - jsonToTrack(JsonParser.parseString(responseBody).obj) + return withIOContext { + authClient.newCall(GET("$apiUrl/subject/${track.media_id}")) + .await() + .parseAs() + .let { jsonToSearch(it) } } } suspend fun statusLibManga(track: Track): Track? { - val urlUserRead = "$apiUrl/collection/${track.media_id}" - val requestUserRead = - Request.Builder().url(urlUserRead).cacheControl(CacheControl.FORCE_NETWORK).get() + return withIOContext { + val urlUserRead = "$apiUrl/collection/${track.media_id}" + val requestUserRead = Request.Builder() + .url(urlUserRead) + .cacheControl(CacheControl.FORCE_NETWORK) + .get() .build() - // todo get user readed chapter here - val response = authClient.newCall(requestUserRead).await() - val resp = response.body?.toString() - 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() + // TODO: get user readed chapter here + var response = authClient.newCall(requestUserRead).await() + var responseBody = response.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } - gson.fromJson(responseBody, OAuth::class.java) + if (responseBody.contains("\"code\":400")) { + null + } else { + json.decodeFromString(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( oauthUrl, - body = FormBody.Builder().add("grant_type", "authorization_code").add("client_id", clientId) - .add("client_secret", clientSecret).add("code", code).add("redirect_uri", redirectUrl) - .build() + body = FormBody.Builder() + .add("grant_type", "authorization_code") + .add("client_id", clientId) + .add("client_secret", clientSecret) + .add("code", code) + .add("redirect_uri", redirectUrl) + .build(), ) companion object { private const val clientId = "bgm10555cda0762e80ca" private const val clientSecret = "8fff394a8627b4c388cbf349ec865775" - private const val baseUrl = "https://bangumi.org" private const val apiUrl = "https://api.bgm.tv" private const val oauthUrl = "https://bgm.tv/oauth/access_token" private const val loginUrl = "https://bgm.tv/oauth/authorize" @@ -158,15 +186,22 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept return "$baseMangaUrl/$remoteId" } - fun authUrl() = loginUrl.toUri().buildUpon().appendQueryParameter("client_id", clientId) - .appendQueryParameter("response_type", "code") - .appendQueryParameter("redirect_uri", redirectUrl).build() + fun authUrl(): Uri = + loginUrl.toUri().buildUpon() + .appendQueryParameter("client_id", clientId) + .appendQueryParameter("response_type", "code") + .appendQueryParameter("redirect_uri", redirectUrl) + .build() fun refreshTokenRequest(token: String) = POST( oauthUrl, - body = FormBody.Builder().add("grant_type", "refresh_token").add("client_id", clientId) - .add("client_secret", clientSecret).add("refresh_token", token) - .add("redirect_uri", redirectUrl).build() + body = FormBody.Builder() + .add("grant_type", "refresh_token") + .add("client_id", clientId) + .add("client_secret", clientSecret) + .add("refresh_token", token) + .add("redirect_uri", redirectUrl) + .build(), ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt index 4f7e19424d..391b5e9e1b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -1,26 +1,21 @@ 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.Interceptor 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. */ 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 { val originalRequest = chain.request() @@ -29,7 +24,7 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { if (currAuth.isExpired()) { val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!)) if (response.isSuccessful) { - newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java)) + newAuth(json.decodeFromString(response.body!!.string())) } else { response.close() } @@ -39,30 +34,35 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { .header("User-Agent", "Tachiyomi") .url( originalRequest.url.newBuilder() - .addQueryParameter("access_token", currAuth.access_token).build() + .addQueryParameter("access_token", currAuth.access_token).build(), ) .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") .build() return chain.proceed(authRequest) } - fun newAuth(oauth: OAuth) { - this.oauth = OAuth( + fun newAuth(oauth: OAuth?) { + this.oauth = if (oauth == null) null else OAuth( oauth.access_token, oauth.token_type, System.currentTimeMillis() / 1000, oauth.expires_in, oauth.refresh_token, - this.oauth?.user_id + this.oauth?.user_id, ) bangumi.saveToken(oauth) } - fun clearOauth() { - bangumi.saveToken(null) + private fun addToken(token: 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", token) + return newFormBody.build() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt index 0b02f2b2ff..e5811d707c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt @@ -7,8 +7,8 @@ fun Track.toBangumiStatus() = when (status) { Bangumi.COMPLETED -> "collect" Bangumi.ON_HOLD -> "on_hold" Bangumi.DROPPED -> "dropped" - Bangumi.PLANNING -> "wish" - else -> throw NotImplementedError("Unknown status") + Bangumi.PLAN_TO_READ -> "wish" + else -> throw NotImplementedError("Unknown status: $status") } fun toTrackStatus(status: String) = when (status) { @@ -16,7 +16,6 @@ fun toTrackStatus(status: String) = when (status) { "collect" -> Bangumi.COMPLETED "on_hold" -> Bangumi.ON_HOLD "dropped" -> Bangumi.DROPPED - "wish" -> Bangumi.PLANNING - - else -> throw Exception("Unknown status") + "wish" -> Bangumi.PLAN_TO_READ + else -> throw NotImplementedError("Unknown status: $status") } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt index 7601630f35..301167366f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Collection.kt @@ -1,47 +1,16 @@ package eu.kanade.tachiyomi.data.track.bangumi +import kotlinx.serialization.Serializable + +@Serializable data class Collection( val `private`: Int? = 0, val comment: String? = "", val ep_status: Int? = 0, val lasttouch: Int? = 0, - val rating: Int? = 0, + val rating: Float? = 0f, val status: Status? = Status(), val tag: List? = listOf(), val user: User? = User(), - 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? = "" + val vol_status: Int? = 0, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt new file mode 100644 index 0000000000..25776d3416 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/OAuth.kt @@ -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) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt new file mode 100644 index 0000000000..f69bac3f5e --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Status.kt @@ -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? = "", +) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt new file mode 100644 index 0000000000..514be8fb0a --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/User.kt @@ -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? = "", +)