mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 16:18:55 +01:00 
			
		
		
		
	Fix Bangumi class formatting
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
 | 
			
		||||
data class Avatar(
 | 
			
		||||
  val large: String? = "",
 | 
			
		||||
  val medium: String? = "",
 | 
			
		||||
  val small: String? = ""
 | 
			
		||||
)
 | 
			
		||||
        val large: String? = "",
 | 
			
		||||
        val medium: String? = "",
 | 
			
		||||
        val small: String? = ""
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,132 +13,132 @@ import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class Bangumi(private val context: Context, id: Int) : TrackService(id) {
 | 
			
		||||
 | 
			
		||||
  override fun getScoreList(): List<String> {
 | 
			
		||||
    return IntRange(0, 10).map(Int::toString)
 | 
			
		||||
  }
 | 
			
		||||
    override val name = "Bangumi"
 | 
			
		||||
 | 
			
		||||
  override fun displayScore(track: Track): String {
 | 
			
		||||
    return track.score.toInt().toString()
 | 
			
		||||
  }
 | 
			
		||||
    private val gson: Gson by injectLazy()
 | 
			
		||||
 | 
			
		||||
  override fun add(track: Track): Observable<Track> {
 | 
			
		||||
    return api.addLibManga(track)
 | 
			
		||||
  }
 | 
			
		||||
    private val interceptor by lazy { BangumiInterceptor(this, gson) }
 | 
			
		||||
 | 
			
		||||
  override fun update(track: Track): Observable<Track> {
 | 
			
		||||
    if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
 | 
			
		||||
      track.status = COMPLETED
 | 
			
		||||
    private val api by lazy { BangumiApi(client, interceptor) }
 | 
			
		||||
 | 
			
		||||
    override fun getScoreList(): List<String> {
 | 
			
		||||
        return IntRange(0, 10).map(Int::toString)
 | 
			
		||||
    }
 | 
			
		||||
    return api.updateLibManga(track)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun bind(track: Track): Observable<Track> {
 | 
			
		||||
    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 displayScore(track: Track): String {
 | 
			
		||||
        return track.score.toInt().toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun add(track: Track): Observable<Track> {
 | 
			
		||||
        return api.addLibManga(track)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun update(track: Track): Observable<Track> {
 | 
			
		||||
        if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) {
 | 
			
		||||
            track.status = COMPLETED
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
        return api.updateLibManga(track)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  override fun search(query: String): Observable<List<TrackSearch>> {
 | 
			
		||||
    return api.search(query)
 | 
			
		||||
  }
 | 
			
		||||
    override fun bind(track: Track): Observable<Track> {
 | 
			
		||||
        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> {
 | 
			
		||||
    return api.statusLibManga(track)
 | 
			
		||||
      .flatMap {
 | 
			
		||||
        track.copyPersonalFrom(it!!)
 | 
			
		||||
        api.findLibManga(track)
 | 
			
		||||
          .map { remoteTrack ->
 | 
			
		||||
            if (remoteTrack != null) {
 | 
			
		||||
              track.total_chapters = remoteTrack.total_chapters
 | 
			
		||||
              track.status = remoteTrack.status
 | 
			
		||||
    override fun search(query: String): Observable<List<TrackSearch>> {
 | 
			
		||||
        return api.search(query)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun refresh(track: Track): Observable<Track> {
 | 
			
		||||
        return api.statusLibManga(track)
 | 
			
		||||
                .flatMap {
 | 
			
		||||
                    track.copyPersonalFrom(it!!)
 | 
			
		||||
                    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
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 -> ""
 | 
			
		||||
        }.doOnError {
 | 
			
		||||
            logout()
 | 
			
		||||
        }.toCompletable()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
      }
 | 
			
		||||
    }.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
 | 
			
		||||
    fun saveToken(oauth: OAuth?) {
 | 
			
		||||
        val json = gson.toJson(oauth)
 | 
			
		||||
        preferences.trackToken(this).set(json)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun logout() {
 | 
			
		||||
    super.logout()
 | 
			
		||||
    preferences.trackToken(this).set(null)
 | 
			
		||||
    interceptor.newAuth(null)
 | 
			
		||||
  }
 | 
			
		||||
    fun restoreToken(): OAuth? {
 | 
			
		||||
        return try {
 | 
			
		||||
            gson.fromJson(preferences.trackToken(this).get(), OAuth::class.java)
 | 
			
		||||
        } 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,191 +21,191 @@ import java.net.URLEncoder
 | 
			
		||||
 | 
			
		||||
class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
 | 
			
		||||
 | 
			
		||||
  private val gson: Gson by injectLazy()
 | 
			
		||||
  private val parser = JsonParser()
 | 
			
		||||
  private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 | 
			
		||||
    private val gson: Gson by injectLazy()
 | 
			
		||||
    private val parser = JsonParser()
 | 
			
		||||
    private val authClient = client.newBuilder().addInterceptor(interceptor).build()
 | 
			
		||||
 | 
			
		||||
  fun addLibManga(track: Track): Observable<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()
 | 
			
		||||
    return authClient.newCall(request)
 | 
			
		||||
      .asObservableSuccess()
 | 
			
		||||
      .map {
 | 
			
		||||
        track
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
    fun addLibManga(track: Track): Observable<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()
 | 
			
		||||
        return authClient.newCall(request)
 | 
			
		||||
                .asObservableSuccess()
 | 
			
		||||
                .map {
 | 
			
		||||
                    track
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  fun updateLibManga(track: Track): Observable<Track> {
 | 
			
		||||
    // chapter update
 | 
			
		||||
    val body = FormBody.Builder()
 | 
			
		||||
      .add("watched_eps", track.last_chapter_read.toString())
 | 
			
		||||
      .build()
 | 
			
		||||
    val request = Request.Builder()
 | 
			
		||||
      .url("$apiUrl/subject/${track.media_id}/update/watched_eps")
 | 
			
		||||
      .post(body)
 | 
			
		||||
      .build()
 | 
			
		||||
    fun updateLibManga(track: Track): Observable<Track> {
 | 
			
		||||
        // chapter update
 | 
			
		||||
        val body = FormBody.Builder()
 | 
			
		||||
                .add("watched_eps", track.last_chapter_read.toString())
 | 
			
		||||
                .build()
 | 
			
		||||
        val request = Request.Builder()
 | 
			
		||||
                .url("$apiUrl/subject/${track.media_id}/update/watched_eps")
 | 
			
		||||
                .post(body)
 | 
			
		||||
                .build()
 | 
			
		||||
 | 
			
		||||
    // 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()
 | 
			
		||||
    return authClient.newCall(srequest)
 | 
			
		||||
      .asObservableSuccess()
 | 
			
		||||
      .map {
 | 
			
		||||
        track
 | 
			
		||||
      }.flatMap {
 | 
			
		||||
        authClient.newCall(request)
 | 
			
		||||
          .asObservableSuccess()
 | 
			
		||||
          .map {
 | 
			
		||||
            track
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
        // 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()
 | 
			
		||||
        return authClient.newCall(srequest)
 | 
			
		||||
                .asObservableSuccess()
 | 
			
		||||
                .map {
 | 
			
		||||
                    track
 | 
			
		||||
                }.flatMap {
 | 
			
		||||
                    authClient.newCall(request)
 | 
			
		||||
                            .asObservableSuccess()
 | 
			
		||||
                            .map {
 | 
			
		||||
                                track
 | 
			
		||||
                            }
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  fun search(search: String): Observable<List<TrackSearch>> {
 | 
			
		||||
    val url = Uri.parse(
 | 
			
		||||
      "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon()
 | 
			
		||||
      .appendQueryParameter("max_results", "20")
 | 
			
		||||
      .build()
 | 
			
		||||
    val request = Request.Builder()
 | 
			
		||||
      .url(url.toString())
 | 
			
		||||
      .get()
 | 
			
		||||
      .build()
 | 
			
		||||
    return authClient.newCall(request)
 | 
			
		||||
      .asObservableSuccess()
 | 
			
		||||
      .map { netResponse ->
 | 
			
		||||
        var responseBody = netResponse.body?.string().orEmpty()
 | 
			
		||||
        if (responseBody.isEmpty()) {
 | 
			
		||||
          throw Exception("Null Response")
 | 
			
		||||
    fun search(search: String): Observable<List<TrackSearch>> {
 | 
			
		||||
        val url = Uri.parse(
 | 
			
		||||
                "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon()
 | 
			
		||||
                .appendQueryParameter("max_results", "20")
 | 
			
		||||
                .build()
 | 
			
		||||
        val request = Request.Builder()
 | 
			
		||||
                .url(url.toString())
 | 
			
		||||
                .get()
 | 
			
		||||
                .build()
 | 
			
		||||
        return authClient.newCall(request)
 | 
			
		||||
                .asObservableSuccess()
 | 
			
		||||
                .map { netResponse ->
 | 
			
		||||
                    var responseBody = netResponse.body?.string().orEmpty()
 | 
			
		||||
                    if (responseBody.isEmpty()) {
 | 
			
		||||
                        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() =
 | 
			
		||||
      Uri.parse(loginUrl).buildUpon()
 | 
			
		||||
        .appendQueryParameter("client_id", clientId)
 | 
			
		||||
        .appendQueryParameter("response_type", "code")
 | 
			
		||||
        .appendQueryParameter("redirect_uri", redirectUrl)
 | 
			
		||||
        .build()
 | 
			
		||||
    fun findLibManga(track: Track): Observable<Track?> {
 | 
			
		||||
        val urlMangas = "$apiUrl/subject/${track.media_id}"
 | 
			
		||||
        val requestMangas = Request.Builder()
 | 
			
		||||
                .url(urlMangas)
 | 
			
		||||
                .get()
 | 
			
		||||
                .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())
 | 
			
		||||
  }
 | 
			
		||||
        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() =
 | 
			
		||||
                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())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,55 +7,55 @@ import okhttp3.Response
 | 
			
		||||
 | 
			
		||||
class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * OAuth object used for authenticated requests.
 | 
			
		||||
   */
 | 
			
		||||
  private var oauth: OAuth? = bangumi.restoreToken()
 | 
			
		||||
    /**
 | 
			
		||||
     * 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()
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
      }
 | 
			
		||||
    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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
    override fun intercept(chain: Interceptor.Chain): Response {
 | 
			
		||||
        val originalRequest = chain.request()
 | 
			
		||||
 | 
			
		||||
    return chain.proceed(authRequest)
 | 
			
		||||
  }
 | 
			
		||||
        val currAuth = oauth ?: throw Exception("Not authenticated with Bangumi")
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
        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()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,20 +3,20 @@ package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Track
 | 
			
		||||
 | 
			
		||||
fun Track.toBangumiStatus() = when (status) {
 | 
			
		||||
  Bangumi.READING -> "do"
 | 
			
		||||
  Bangumi.COMPLETED -> "collect"
 | 
			
		||||
  Bangumi.ON_HOLD -> "on_hold"
 | 
			
		||||
  Bangumi.DROPPED -> "dropped"
 | 
			
		||||
  Bangumi.PLANNING -> "wish"
 | 
			
		||||
  else -> throw NotImplementedError("Unknown status")
 | 
			
		||||
    Bangumi.READING -> "do"
 | 
			
		||||
    Bangumi.COMPLETED -> "collect"
 | 
			
		||||
    Bangumi.ON_HOLD -> "on_hold"
 | 
			
		||||
    Bangumi.DROPPED -> "dropped"
 | 
			
		||||
    Bangumi.PLANNING -> "wish"
 | 
			
		||||
    else -> throw NotImplementedError("Unknown status")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun toTrackStatus(status: String) = when (status) {
 | 
			
		||||
  "do" -> Bangumi.READING
 | 
			
		||||
  "collect" -> Bangumi.COMPLETED
 | 
			
		||||
  "on_hold" -> Bangumi.ON_HOLD
 | 
			
		||||
  "dropped" -> Bangumi.DROPPED
 | 
			
		||||
  "wish" -> Bangumi.PLANNING
 | 
			
		||||
    "do" -> Bangumi.READING
 | 
			
		||||
    "collect" -> Bangumi.COMPLETED
 | 
			
		||||
    "on_hold" -> Bangumi.ON_HOLD
 | 
			
		||||
    "dropped" -> Bangumi.DROPPED
 | 
			
		||||
    "wish" -> Bangumi.PLANNING
 | 
			
		||||
 | 
			
		||||
  else -> throw Exception("Unknown status")
 | 
			
		||||
    else -> throw Exception("Unknown status")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
 | 
			
		||||
data class Collection(
 | 
			
		||||
  val `private`: Int? = 0,
 | 
			
		||||
  val comment: String? = "",
 | 
			
		||||
  val ep_status: Int? = 0,
 | 
			
		||||
  val lasttouch: Int? = 0,
 | 
			
		||||
  val rating: Int? = 0,
 | 
			
		||||
  val status: Status? = Status(),
 | 
			
		||||
  val tag: List<String?>? = listOf(),
 | 
			
		||||
  val user: User? = User(),
 | 
			
		||||
  val vol_status: Int? = 0
 | 
			
		||||
)
 | 
			
		||||
        val `private`: Int? = 0,
 | 
			
		||||
        val comment: String? = "",
 | 
			
		||||
        val ep_status: Int? = 0,
 | 
			
		||||
        val lasttouch: Int? = 0,
 | 
			
		||||
        val rating: Int? = 0,
 | 
			
		||||
        val status: Status? = Status(),
 | 
			
		||||
        val tag: List<String?>? = listOf(),
 | 
			
		||||
        val user: User? = User(),
 | 
			
		||||
        val vol_status: Int? = 0
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
 | 
			
		||||
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?
 | 
			
		||||
        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)
 | 
			
		||||
    // Access token refresh before expired
 | 
			
		||||
    fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
 | 
			
		||||
data class Status(
 | 
			
		||||
  val id: Int? = 0,
 | 
			
		||||
  val name: String? = "",
 | 
			
		||||
  val type: String? = ""
 | 
			
		||||
)
 | 
			
		||||
        val id: Int? = 0,
 | 
			
		||||
        val name: String? = "",
 | 
			
		||||
        val type: String? = ""
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.track.bangumi
 | 
			
		||||
 | 
			
		||||
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? = ""
 | 
			
		||||
)
 | 
			
		||||
        val avatar: Avatar? = Avatar(),
 | 
			
		||||
        val id: Int? = 0,
 | 
			
		||||
        val nickname: String? = "",
 | 
			
		||||
        val sign: String? = "",
 | 
			
		||||
        val url: String? = "",
 | 
			
		||||
        val usergroup: Int? = 0,
 | 
			
		||||
        val username: String? = ""
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user