Add more shikimori api methods

This commit is contained in:
Koitharu
2022-04-11 13:17:26 +03:00
parent 3b357eb509
commit 9d1c4bd660
5 changed files with 127 additions and 3 deletions

View File

@@ -8,6 +8,6 @@ val detailsModule
get() = module {
viewModel { intent ->
DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get())
DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get(), get())
}
}

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.details.ui
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
@@ -26,6 +27,8 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.shikimori.data.ShikimoriRepository
import org.koitharu.kotatsu.shikimori.data.model.ShikimoriMangaInfo
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.iterator
@@ -39,6 +42,7 @@ class DetailsViewModel(
private val trackingRepository: TrackingRepository,
private val mangaDataRepository: MangaDataRepository,
private val settings: AppSettings,
private val shikimoriRepository: ShikimoriRepository,
) : BaseViewModel() {
private var loadingJob: Job
@@ -85,6 +89,7 @@ class DetailsViewModel(
.asLiveData(viewModelScope.coroutineContext)
val onMangaRemoved = SingleLiveEvent<Manga>()
val shikimoriInfo = MutableLiveData<ShikimoriMangaInfo?>()
val branches = mangaData.map {
it?.chapters?.mapToSet { x -> x.branch }?.sortedBy { x -> x }.orEmpty()
@@ -204,6 +209,7 @@ class DetailsViewModel(
localMangaRepository.findSavedManga(manga)
}
}.getOrNull()
findShikimoriManga(manga)
}
private fun mapChapters(
@@ -312,4 +318,18 @@ class DetailsViewModel(
it.chapter.name.contains(query, ignoreCase = true)
}
}
private fun findShikimoriManga(manga: Manga) {
if (!shikimoriRepository.isAuthorized) {
return
}
launchJob(Dispatchers.Default) {
val data = runCatching {
shikimoriRepository.findMangaInfo(manga)
}.getOrNull()
if (data != null) {
shikimoriInfo.postValue(data)
}
}
}
}

View File

@@ -3,8 +3,11 @@ package org.koitharu.kotatsu.shikimori.data
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.parseJson
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.shikimori.data.model.ShikimoriManga
import org.koitharu.kotatsu.shikimori.data.model.ShikimoriMangaInfo
import org.koitharu.kotatsu.shikimori.data.model.ShikimoriUser
private const val CLIENT_ID = "Mw6F0tPEOgyV7F9U9Twg50Q8SndMY7hzIOfXg0AX_XU"
@@ -49,4 +52,41 @@ class ShikimoriRepository(
val response = okHttp.newCall(request.build()).await().parseJson()
return ShikimoriUser(response)
}
suspend fun findMangaInfo(manga: Manga): ShikimoriMangaInfo? {
val q = manga.title.urlEncoded()
val request = Request.Builder()
.get()
.url("https://shikimori.one/api/mangas?limit=20&search=$q&censored=false")
val response = okHttp.newCall(request.build()).await().parseJsonArray()
val candidates = response.mapJSON { ShikimoriManga(it) }
val bestCandidate = candidates.minByOrNull {
it.name.levenshteinDistance(manga.title)
} ?: return null
return getMangaInfo(bestCandidate.id)
}
suspend fun getRelatedManga(id: Long): List<ShikimoriManga> {
val request = Request.Builder()
.get()
.url("https://shikimori.one/api/mangas/$id/related")
val response = okHttp.newCall(request.build()).await().parseJsonArray()
return response.mapJSON { jo -> ShikimoriManga(jo) }
}
suspend fun getSimilarManga(id: Long): List<ShikimoriManga> {
val request = Request.Builder()
.get()
.url("https://shikimori.one/api/mangas/$id/similar")
val response = okHttp.newCall(request.build()).await().parseJsonArray()
return response.mapJSON { jo -> ShikimoriManga(jo) }
}
suspend fun getMangaInfo(id: Long): ShikimoriMangaInfo {
val request = Request.Builder()
.get()
.url("https://shikimori.one/api/mangas/$id")
val response = okHttp.newCall(request.build()).await().parseJson()
return ShikimoriMangaInfo(response)
}
}

View File

@@ -0,0 +1,44 @@
package org.koitharu.kotatsu.shikimori.data.model
import org.json.JSONObject
class ShikimoriManga(
val id: Long,
val name: String,
val cover: String,
val url: String,
) {
constructor(json: JSONObject) : this(
id = json.getLong("id"),
name = json.getString("name"),
cover = json.getJSONObject("image").getString("preview"),
url = json.getString("url"),
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ShikimoriManga
if (id != other.id) return false
if (name != other.name) return false
if (cover != other.cover) return false
if (url != other.url) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + cover.hashCode()
result = 31 * result + url.hashCode()
return result
}
override fun toString(): String {
return "ShikimoriManga #$id \"$name\" $url"
}
}

View File

@@ -0,0 +1,20 @@
package org.koitharu.kotatsu.shikimori.data.model
import org.json.JSONObject
class ShikimoriMangaInfo(
val id: Long,
val name: String,
val cover: String,
val url: String,
val descriptionHtml: String,
) {
constructor(json: JSONObject) : this(
id = json.getLong("id"),
name = json.getString("name"),
cover = json.getJSONObject("image").getString("preview"),
url = json.getString("url"),
descriptionHtml = json.getString("description_html"),
)
}