Changed Kitsu to use Algoria search directly (#1514)

* Changed Kitsu to use Algoria search directly, was recommended by the Kitsu Dev team

* remove extra line

* fixed end date bug
added filtering out novel back in

* save the retrofit instances locally for search.
This commit is contained in:
Carlos 2018-06-30 06:07:37 -04:00 committed by inorichi
parent 136e90638a
commit fd825b1049
2 changed files with 86 additions and 26 deletions

View File

@ -24,6 +24,22 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.build() .build()
.create(KitsuApi.Rest::class.java) .create(KitsuApi.Rest::class.java)
private val searchRest = Retrofit.Builder()
.baseUrl(algoliaKeyUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(KitsuApi.SearchKeyRest::class.java)
private val algoliaRest = Retrofit.Builder()
.baseUrl(algoliaUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(KitsuApi.AgoliaSearchRest::class.java)
fun addLibManga(track: Track, userId: String): Observable<Track> { fun addLibManga(track: Track, userId: String): Observable<Track> {
return Observable.defer { return Observable.defer {
// @formatter:off // @formatter:off
@ -48,7 +64,6 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
) )
) )
) )
// @formatter:on
rest.addLibManga(jsonObject("data" to data)) rest.addLibManga(jsonObject("data" to data))
.map { json -> .map { json ->
@ -77,12 +92,25 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
} }
fun search(query: String): Observable<List<TrackSearch>> { fun search(query: String): Observable<List<TrackSearch>> {
return rest.search(query) return searchRest
.getKey().map { json ->
json["media"].asJsonObject["key"].string
}.flatMap { key ->
algoliaSearch(key, query)
}
}
private fun algoliaSearch(key: String, query: String): Observable<List<TrackSearch>> {
val jsonObject = jsonObject("params" to "query=$query$algoliaFilter")
return algoliaRest
.getSearchQuery(algoliaAppId, key, jsonObject)
.map { json -> .map { json ->
val data = json["data"].array val data = json["hits"].array
data.map { KitsuManga(it.obj) } data.map { KitsuSearchManga(it.obj) }
.filter { it.type != "novel" } .filter { it.subType != "novel" }
.map { it.toTrack() } .map { it.toTrack() }
} }
} }
@ -143,10 +171,6 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
@Body data: JsonObject @Body data: JsonObject
): Observable<JsonObject> ): Observable<JsonObject>
@GET("manga")
fun search(
@Query("filter[text]", encoded = true) query: String
): Observable<JsonObject>
@GET("library-entries") @GET("library-entries")
fun findLibManga( fun findLibManga(
@ -168,6 +192,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
private interface SearchKeyRest {
@GET("media/")
fun getKey(): Observable<JsonObject>
}
private interface AgoliaSearchRest {
@POST("query/")
fun getSearchQuery(@Header("X-Algolia-Application-Id") appid: String, @Header("X-Algolia-API-Key") key: String, @Body json: JsonObject): Observable<JsonObject>
}
private interface LoginRest { private interface LoginRest {
@FormUrlEncoded @FormUrlEncoded
@ -188,6 +222,11 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
private const val baseUrl = "https://kitsu.io/api/edge/" private const val baseUrl = "https://kitsu.io/api/edge/"
private const val loginUrl = "https://kitsu.io/api/" private const val loginUrl = "https://kitsu.io/api/"
private const val baseMangaUrl = "https://kitsu.io/manga/" private const val baseMangaUrl = "https://kitsu.io/manga/"
private const val algoliaKeyUrl = "https://kitsu.io/api/edge/algolia-keys/"
private const val algoliaUrl = "https://AWQO5J657S-dsn.algolia.net/1/indexes/production_media/"
private const val algoliaAppId = "AWQO5J657S"
private const val algoliaFilter = "&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=%5B%22synopsis%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
fun mangaUrl(remoteId: Int): String { fun mangaUrl(remoteId: Int): String {
return baseMangaUrl + remoteId return baseMangaUrl + remoteId

View File

@ -7,38 +7,59 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
open class KitsuManga(obj: JsonObject) { class KitsuSearchManga(obj: JsonObject) {
val id by obj.byInt val id by obj.byInt
val canonicalTitle by obj["attributes"].byString private val canonicalTitle by obj.byString
val chapterCount = obj["attributes"].obj.get("chapterCount").nullInt private val chapterCount = obj.get("chapterCount").nullInt
val type = obj["attributes"].obj.get("mangaType").nullString.orEmpty() val subType = obj.get("subType").nullString
val original by obj["attributes"].obj["posterImage"].byString val original by obj["posterImage"].byString
val synopsis by obj["attributes"].byString private val synopsis by obj.byString
val startDate = obj["attributes"].obj.get("startDate").nullString.orEmpty() private val startDate = obj.get("startDate").nullString
open val status = obj["attributes"].obj.get("status").nullString.orEmpty() private val endDate = obj.get("endDate").nullString
@CallSuper @CallSuper
open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply { open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
media_id = this@KitsuManga.id media_id = this@KitsuSearchManga.id
title = canonicalTitle title = canonicalTitle
total_chapters = chapterCount ?: 0 total_chapters = chapterCount ?: 0
cover_url = original cover_url = original
summary = synopsis summary = synopsis
tracking_url = KitsuApi.mangaUrl(media_id) tracking_url = KitsuApi.mangaUrl(media_id)
publishing_status = this@KitsuManga.status
publishing_type = type if (endDate == null) {
publishing_status = "Publishing"
} else {
publishing_status = "Finished"
}
publishing_type = subType ?: ""
start_date = startDate.orEmpty() start_date = startDate.orEmpty()
} }
} }
class KitsuLibManga(obj: JsonObject, manga: JsonObject) : KitsuManga(manga) {
val libraryId by obj.byInt("id") class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
override val status by obj["attributes"].byString val id by manga.byInt
val ratingTwenty = obj["attributes"].obj.get("ratingTwenty").nullString private val canonicalTitle by manga["attributes"].byString
private val chapterCount = manga["attributes"].obj.get("chapterCount").nullInt
val type = manga["attributes"].obj.get("mangaType").nullString.orEmpty()
val original by manga["attributes"].obj["posterImage"].byString
private val synopsis by manga["attributes"].byString
private val startDate = manga["attributes"].obj.get("startDate").nullString.orEmpty()
private val libraryId by obj.byInt("id")
val status by obj["attributes"].byString
private val ratingTwenty = obj["attributes"].obj.get("ratingTwenty").nullString
val progress by obj["attributes"].byInt val progress by obj["attributes"].byInt
override fun toTrack() = super.toTrack().apply { open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
media_id = libraryId // TODO migrate media ids to library ids media_id = libraryId
title = canonicalTitle
total_chapters = chapterCount ?: 0
cover_url = original
summary = synopsis
tracking_url = KitsuApi.mangaUrl(media_id)
publishing_status = this@KitsuLibManga.status
publishing_type = type
start_date = startDate
status = toTrackStatus() status = toTrackStatus()
score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f
last_chapter_read = progress last_chapter_read = progress