Fix Bangumi class formatting

This commit is contained in:
arkon 2020-01-11 22:06:23 -05:00
parent 427d2fed8c
commit 44f406b4b9
9 changed files with 383 additions and 383 deletions

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
data class Avatar( data class Avatar(
val large: String? = "", val large: String? = "",
val medium: String? = "", val medium: String? = "",
val small: String? = "" val small: String? = ""
) )

View File

@ -13,132 +13,132 @@ import uy.kohesive.injekt.injectLazy
class Bangumi(private val context: Context, id: Int) : TrackService(id) { class Bangumi(private val context: Context, id: Int) : TrackService(id) {
override fun getScoreList(): List<String> { override val name = "Bangumi"
return IntRange(0, 10).map(Int::toString)
}
override fun displayScore(track: Track): String { private val gson: Gson by injectLazy()
return track.score.toInt().toString()
}
override fun add(track: Track): Observable<Track> { private val interceptor by lazy { BangumiInterceptor(this, gson) }
return api.addLibManga(track)
}
override fun update(track: Track): Observable<Track> { private val api by lazy { BangumiApi(client, interceptor) }
if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
track.status = COMPLETED override fun getScoreList(): List<String> {
return IntRange(0, 10).map(Int::toString)
} }
return api.updateLibManga(track)
}
override fun bind(track: Track): Observable<Track> { override fun displayScore(track: Track): String {
return api.statusLibManga(track) return track.score.toInt().toString()
.flatMap { }
api.findLibManga(track).flatMap { remoteTrack ->
if (remoteTrack != null && it != null) { override fun add(track: Track): Observable<Track> {
track.copyPersonalFrom(remoteTrack) return api.addLibManga(track)
track.library_id = remoteTrack.library_id }
track.status = remoteTrack.status
track.last_chapter_read = remoteTrack.last_chapter_read override fun update(track: Track): Observable<Track> {
refresh(track) if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
} else { track.status = COMPLETED
// Set default fields if it's not found in the list
track.score = DEFAULT_SCORE.toFloat()
track.status = DEFAULT_STATUS
add(track)
update(track)
}
} }
} return api.updateLibManga(track)
} }
override fun search(query: String): Observable<List<TrackSearch>> { override fun bind(track: Track): Observable<Track> {
return api.search(query) return api.statusLibManga(track)
} .flatMap {
api.findLibManga(track).flatMap { remoteTrack ->
if (remoteTrack != null && it != null) {
track.copyPersonalFrom(remoteTrack)
track.library_id = remoteTrack.library_id
track.status = remoteTrack.status
track.last_chapter_read = remoteTrack.last_chapter_read
refresh(track)
} else {
// Set default fields if it's not found in the list
track.score = DEFAULT_SCORE.toFloat()
track.status = DEFAULT_STATUS
add(track)
update(track)
}
}
}
}
override fun refresh(track: Track): Observable<Track> { override fun search(query: String): Observable<List<TrackSearch>> {
return api.statusLibManga(track) return api.search(query)
.flatMap { }
track.copyPersonalFrom(it!!)
api.findLibManga(track) override fun refresh(track: Track): Observable<Track> {
.map { remoteTrack -> return api.statusLibManga(track)
if (remoteTrack != null) { .flatMap {
track.total_chapters = remoteTrack.total_chapters track.copyPersonalFrom(it!!)
track.status = remoteTrack.status api.findLibManga(track)
.map { remoteTrack ->
if (remoteTrack != null) {
track.total_chapters = remoteTrack.total_chapters
track.status = remoteTrack.status
}
track
}
}
}
override fun getLogo() = R.drawable.tracker_bangumi
override fun getLogoColor() = Color.rgb(0xF0, 0x91, 0x99)
override fun getStatusList(): List<Int> {
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLANNING)
}
override fun getStatus(status: Int): String = with(context) {
when (status) {
READING -> getString(R.string.reading)
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 -> ""
}
}
override fun login(username: String, password: String) = login(password)
fun login(code: String): Completable {
return api.accessToken(code).map { oauth: OAuth? ->
interceptor.newAuth(oauth)
if (oauth != null) {
saveCredentials(oauth.user_id.toString(), oauth.access_token)
} }
track }.doOnError {
} logout()
} }.toCompletable()
}
companion object {
const val READING = 3
const val COMPLETED = 2
const val ON_HOLD = 4
const val DROPPED = 5
const val PLANNING = 1
const val DEFAULT_STATUS = READING
const val DEFAULT_SCORE = 0
}
override val name = "Bangumi"
private val gson: Gson by injectLazy()
private val interceptor by lazy { BangumiInterceptor(this, gson) }
private val api by lazy { BangumiApi(client, interceptor) }
override fun getLogo() = R.drawable.tracker_bangumi
override fun getLogoColor() = Color.rgb(0xF0, 0x91, 0x99)
override fun getStatusList(): List<Int> {
return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLANNING)
}
override fun getStatus(status: Int): String = with(context) {
when (status) {
READING -> getString(R.string.reading)
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 -> ""
} }
}
override fun login(username: String, password: String) = login(password) fun saveToken(oauth: OAuth?) {
val json = gson.toJson(oauth)
fun login(code: String): Completable { preferences.trackToken(this).set(json)
return api.accessToken(code).map { oauth: OAuth? ->
interceptor.newAuth(oauth)
if (oauth != null) {
saveCredentials(oauth.user_id.toString(), oauth.access_token)
}
}.doOnError {
logout()
}.toCompletable()
}
fun saveToken(oauth: OAuth?) {
val json = gson.toJson(oauth)
preferences.trackToken(this).set(json)
}
fun restoreToken(): OAuth? {
return try {
gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java)
} catch (e: Exception) {
null
} }
}
override fun logout() { fun restoreToken(): OAuth? {
super.logout() return try {
preferences.trackToken(this).set(null) gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java)
interceptor.newAuth(null) } catch (e: Exception) {
} null
}
}
override fun logout() {
super.logout()
preferences.trackToken(this).set(null)
interceptor.newAuth(null)
}
companion object {
const val READING = 3
const val COMPLETED = 2
const val ON_HOLD = 4
const val DROPPED = 5
const val PLANNING = 1
const val DEFAULT_STATUS = READING
const val DEFAULT_SCORE = 0
}
} }

View File

@ -21,191 +21,191 @@ import java.net.URLEncoder
class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) { class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
private val gson: Gson by injectLazy() private val gson: Gson by injectLazy()
private val parser = JsonParser() private val parser = JsonParser()
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
fun addLibManga(track: Track): Observable<Track> { fun addLibManga(track: Track): Observable<Track> {
val body = FormBody.Builder() val body = FormBody.Builder()
.add("rating", track.score.toInt().toString()) .add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
.build() .build()
val request = Request.Builder() val request = Request.Builder()
.url("$apiUrl/collection/${track.media_id}/update") .url("$apiUrl/collection/${track.media_id}/update")
.post(body) .post(body)
.build() .build()
return authClient.newCall(request) return authClient.newCall(request)
.asObservableSuccess() .asObservableSuccess()
.map { .map {
track track
} }
} }
fun updateLibManga(track: Track): Observable<Track> { fun updateLibManga(track: Track): Observable<Track> {
// 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()
val request = Request.Builder() val request = Request.Builder()
.url("$apiUrl/subject/${track.media_id}/update/watched_eps") .url("$apiUrl/subject/${track.media_id}/update/watched_eps")
.post(body) .post(body)
.build() .build()
// read status update // read status update
val sbody = FormBody.Builder() val sbody = FormBody.Builder()
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
.build() .build()
val srequest = Request.Builder() val srequest = Request.Builder()
.url("$apiUrl/collection/${track.media_id}/update") .url("$apiUrl/collection/${track.media_id}/update")
.post(sbody) .post(sbody)
.build() .build()
return authClient.newCall(srequest) return authClient.newCall(srequest)
.asObservableSuccess() .asObservableSuccess()
.map { .map {
track track
}.flatMap { }.flatMap {
authClient.newCall(request) authClient.newCall(request)
.asObservableSuccess() .asObservableSuccess()
.map { .map {
track track
} }
} }
} }
fun search(search: String): Observable<List<TrackSearch>> { fun search(search: String): Observable<List<TrackSearch>> {
val url = Uri.parse( val url = Uri.parse(
"$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon() "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon()
.appendQueryParameter("max_results", "20") .appendQueryParameter("max_results", "20")
.build() .build()
val request = Request.Builder() val request = Request.Builder()
.url(url.toString()) .url(url.toString())
.get() .get()
.build() .build()
return authClient.newCall(request) return authClient.newCall(request)
.asObservableSuccess() .asObservableSuccess()
.map { netResponse -> .map { netResponse ->
var responseBody = netResponse.body?.string().orEmpty() var responseBody = netResponse.body?.string().orEmpty()
if (responseBody.isEmpty()) { if (responseBody.isEmpty()) {
throw Exception("Null Response") throw Exception("Null Response")
}
if (responseBody.contains("\"code\":404")) {
responseBody = "{\"results\":0,\"list\":[]}"
}
val response = parser.parse(responseBody).obj["list"]?.array
response?.filter { it.obj["type"].asInt == 1 }?.map { jsonToSearch(it.obj) }
}
}
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
} }
if(responseBody.contains("\"code\":404")){ }
responseBody = "{\"results\":0,\"list\":[]}"
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 response = parser.parse(responseBody).obj["list"]?.array
response?.filter { it.obj["type"].asInt == 1 }?.map { jsonToSearch(it.obj) }
}
}
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
}
}
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
}
}
fun findLibManga(track: Track): Observable<Track?> {
val urlMangas = "$apiUrl/subject/${track.media_id}"
val requestMangas = Request.Builder()
.url(urlMangas)
.get()
.build()
return authClient.newCall(requestMangas)
.asObservableSuccess()
.map { netResponse ->
// get comic info
val responseBody = netResponse.body?.string().orEmpty()
jsonToTrack(parser.parse(responseBody).obj)
}
}
fun statusLibManga(track: Track): Observable<Track?> {
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
return authClient.newCall(requestUserRead)
.asObservableSuccess()
.map { netResponse ->
val resp = netResponse.body?.string()
val coll = gson.fromJson(resp, Collection::class.java)
track.status = coll.status?.id!!
track.last_chapter_read = coll.ep_status!!
track
}
}
fun accessToken(code: String): Observable<OAuth> {
return client.newCall(accessTokenRequest(code)).asObservableSuccess().map { netResponse ->
val responseBody = netResponse.body?.string().orEmpty()
if (responseBody.isEmpty()) {
throw Exception("Null Response")
}
gson.fromJson(responseBody, OAuth::class.java)
}
}
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()
)
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"
private const val redirectUrl = "tachiyomi://bangumi-auth"
private const val baseMangaUrl = "$apiUrl/mangas"
fun mangaUrl(remoteId: Int): String {
return "$baseMangaUrl/$remoteId"
} }
fun authUrl() = fun findLibManga(track: Track): Observable<Track?> {
Uri.parse(loginUrl).buildUpon() val urlMangas = "$apiUrl/subject/${track.media_id}"
.appendQueryParameter("client_id", clientId) val requestMangas = Request.Builder()
.appendQueryParameter("response_type", "code") .url(urlMangas)
.appendQueryParameter("redirect_uri", redirectUrl) .get()
.build() .build()
fun refreshTokenRequest(token: String) = POST(oauthUrl, return authClient.newCall(requestMangas)
body = FormBody.Builder() .asObservableSuccess()
.add("grant_type", "refresh_token") .map { netResponse ->
.add("client_id", clientId) // get comic info
.add("client_secret", clientSecret) val responseBody = netResponse.body?.string().orEmpty()
.add("refresh_token", token) jsonToTrack(parser.parse(responseBody).obj)
.add("redirect_uri", redirectUrl) }
.build()) }
}
fun statusLibManga(track: Track): Observable<Track?> {
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
return authClient.newCall(requestUserRead)
.asObservableSuccess()
.map { netResponse ->
val resp = netResponse.body?.string()
val coll = gson.fromJson(resp, Collection::class.java)
track.status = coll.status?.id!!
track.last_chapter_read = coll.ep_status!!
track
}
}
fun accessToken(code: String): Observable<OAuth> {
return client.newCall(accessTokenRequest(code)).asObservableSuccess().map { netResponse ->
val responseBody = netResponse.body?.string().orEmpty()
if (responseBody.isEmpty()) {
throw Exception("Null Response")
}
gson.fromJson(responseBody, OAuth::class.java)
}
}
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()
)
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"
private const val redirectUrl = "tachiyomi://bangumi-auth"
private const val baseMangaUrl = "$apiUrl/mangas"
fun mangaUrl(remoteId: Int): String {
return "$baseMangaUrl/$remoteId"
}
fun authUrl() =
Uri.parse(loginUrl).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())
}
} }

View File

@ -7,55 +7,55 @@ import okhttp3.Response
class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
/** /**
* 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 { fun addTocken(tocken: String, oidFormBody: FormBody): FormBody {
val newFormBody = FormBody.Builder() val newFormBody = FormBody.Builder()
for (i in 0 until oidFormBody.size) { for (i in 0 until oidFormBody.size) {
newFormBody.add(oidFormBody.name(i), oidFormBody.value(i)) newFormBody.add(oidFormBody.name(i), oidFormBody.value(i))
} }
newFormBody.add("access_token", tocken) newFormBody.add("access_token", tocken)
return newFormBody.build() return newFormBody.build()
}
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val currAuth = oauth ?: throw Exception("Not authenticated with Bangumi")
if (currAuth.isExpired()) {
val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!))
if (response.isSuccessful) {
newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java))
} else {
response.close()
}
} }
val authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder() override fun intercept(chain: Interceptor.Chain): Response {
.header("User-Agent", "Tachiyomi") val originalRequest = chain.request()
.url(originalRequest.url.newBuilder()
.addQueryParameter("access_token", currAuth.access_token).build())
.build() else originalRequest.newBuilder()
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
.header("User-Agent", "Tachiyomi")
.build()
return chain.proceed(authRequest) val currAuth = oauth ?: throw Exception("Not authenticated with Bangumi")
}
fun newAuth(oauth: OAuth?) { if (currAuth.isExpired()) {
this.oauth = if (oauth == null) null else OAuth( val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!))
oauth.access_token, if (response.isSuccessful) {
oauth.token_type, newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java))
System.currentTimeMillis() / 1000, } else {
oauth.expires_in, response.close()
oauth.refresh_token, }
this.oauth?.user_id) }
bangumi.saveToken(oauth) val authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder()
} .header("User-Agent", "Tachiyomi")
.url(originalRequest.url.newBuilder()
.addQueryParameter("access_token", currAuth.access_token).build())
.build() else originalRequest.newBuilder()
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
.header("User-Agent", "Tachiyomi")
.build()
return chain.proceed(authRequest)
}
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)
bangumi.saveToken(oauth)
}
} }

View File

@ -3,20 +3,20 @@ package eu.kanade.tachiyomi.data.track.bangumi
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
fun Track.toBangumiStatus() = when (status) { fun Track.toBangumiStatus() = when (status) {
Bangumi.READING -> "do" Bangumi.READING -> "do"
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.PLANNING -> "wish"
else -> throw NotImplementedError("Unknown status") else -> throw NotImplementedError("Unknown status")
} }
fun toTrackStatus(status: String) = when (status) { fun toTrackStatus(status: String) = when (status) {
"do" -> Bangumi.READING "do" -> Bangumi.READING
"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.PLANNING
else -> throw Exception("Unknown status") else -> throw Exception("Unknown status")
} }

View File

@ -1,13 +1,13 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
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: Int? = 0,
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
) )

View File

@ -1,16 +1,16 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
data class OAuth( data class OAuth(
val access_token: String, val access_token: String,
val token_type: String, val token_type: String,
val created_at: Long, val created_at: Long,
val expires_in: Long, val expires_in: Long,
val refresh_token: String?, val refresh_token: String?,
val user_id: Long? val user_id: Long?
) { ) {
// Access token refresh before expired // Access token refresh before expired
fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600) fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
} }

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
data class Status( data class Status(
val id: Int? = 0, val id: Int? = 0,
val name: String? = "", val name: String? = "",
val type: String? = "" val type: String? = ""
) )

View File

@ -1,11 +1,11 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
data class User( data class User(
val avatar: Avatar? = Avatar(), val avatar: Avatar? = Avatar(),
val id: Int? = 0, val id: Int? = 0,
val nickname: String? = "", val nickname: String? = "",
val sign: String? = "", val sign: String? = "",
val url: String? = "", val url: String? = "",
val usergroup: Int? = 0, val usergroup: Int? = 0,
val username: String? = "" val username: String? = ""
) )