From 7d2ded59447b7227de978f1613634a4f9c053e32 Mon Sep 17 00:00:00 2001 From: she11sh0cked Date: Sun, 17 May 2020 21:38:38 +0200 Subject: [PATCH] Implement MyAnimeList recommendations (#7) Co-authored-by: she11sh0cked --- .../browse/source/browse/RecommendsPager.kt | 116 ++++++++++++++++-- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt index aae23f5bc..8fabe9743 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/RecommendsPager.kt @@ -10,6 +10,7 @@ import com.google.gson.JsonParser import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.SMangaImpl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request @@ -17,10 +18,81 @@ import okhttp3.RequestBody.Companion.toRequestBody import rx.Observable import rx.schedulers.Schedulers -open class RecommendsPager(val title: String) : Pager() { +open class RecommendsPager(val title: String, val preferredApi: API = API.MYANIMELIST) : Pager() { private val client = OkHttpClient.Builder().build() - override fun requestNext(): Observable { + private fun myAnimeList(): Observable>? { + fun getId(): Observable { + val endpoint = + myAnimeListEndpoint.toHttpUrlOrNull() + ?: throw Exception("Could not convert endpoint url") + val urlBuilder = endpoint.newBuilder() + urlBuilder.addPathSegment("search") + urlBuilder.addPathSegment("manga") + urlBuilder.addQueryParameter("q", title) + val url = urlBuilder.build().toUrl() + + val request = Request.Builder() + .url(url) + .get() + .build() + + return client.newCall(request) + .asObservableSuccess().subscribeOn(Schedulers.io()) + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = JsonParser.parseString(responseBody).obj + val results = response["results"].array + val firstResult = results[0].obj + val id = firstResult["mal_id"].string + if (id.isEmpty()) { + throw Exception("Not found") + } + id + } + } + + return getId().map { id -> + val endpoint = + myAnimeListEndpoint.toHttpUrlOrNull() + ?: throw Exception("Could not convert endpoint url") + val urlBuilder = endpoint.newBuilder() + urlBuilder.addPathSegment("manga") + urlBuilder.addPathSegment(id) + urlBuilder.addPathSegment("recommendations") + val url = urlBuilder.build().toUrl() + + val request = Request.Builder() + .url(url) + .get() + .build() + + client.newCall(request) + .asObservableSuccess().subscribeOn(Schedulers.io()) + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = JsonParser.parseString(responseBody).obj + val recommendations = response["recommendations"].array + recommendations.map { rec -> + Log.d("MYANIMELIST RECOMMEND", "${rec["title"].string}") + SMangaImpl().apply { + this.title = rec["title"].string + this.thumbnail_url = rec["image_url"].string + this.initialized = true + this.url = rec["url"].string + } + } + }.toBlocking().first() + } + } + + private fun anilist(): Observable>? { val query = """ { @@ -53,7 +125,7 @@ open class RecommendsPager(val title: String) : Pager() { ) val body = payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) val request = Request.Builder() - .url(apiUrl) + .url(anilistEndpoint) .post(body) .build() @@ -71,7 +143,7 @@ open class RecommendsPager(val title: String) : Pager() { val edges = recommendations["edges"].array edges.map { val rec = it["node"]["mediaRecommendation"].obj - Log.d("RECOMMEND", "${rec["title"].obj["romaji"].string}") + Log.d("ANILIST RECOMMEND", "${rec["title"].obj["romaji"].string}") SMangaImpl().apply { this.title = rec["title"].obj["romaji"].string this.thumbnail_url = rec["coverImage"].obj["large"].string @@ -79,13 +151,39 @@ open class RecommendsPager(val title: String) : Pager() { this.url = rec["siteUrl"].string } } - }.map { - MangasPage(it, false) - }.doOnNext { - onPageReceived(it) } } + + override fun requestNext(): Observable { + val apiList = API.values().toMutableList() + apiList.removeAt(apiList.indexOf(preferredApi)) + apiList.add(0, preferredApi) + + var recommendations: Observable>? = null + for (api in apiList) { + recommendations = when (api) { + API.MYANIMELIST -> myAnimeList() + API.ANILIST -> anilist() + } + ?: throw Exception("Could not get recommendations") + + val recommendationsBlocking = recommendations.toBlocking().first() + if (recommendationsBlocking.isNotEmpty()) { + break + } + } + + return recommendations!!.map { + MangasPage(it, false) + }.doOnNext { + onPageReceived(it) + } + } + companion object { - const val apiUrl = "https://graphql.anilist.co/" + private const val myAnimeListEndpoint = "https://api.jikan.moe/v3/" + private const val anilistEndpoint = "https://graphql.anilist.co/" + + enum class API { MYANIMELIST, ANILIST } } }