From 216065fb38b7200275f299aa5599d2f1a70d0e95 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 5 Jun 2021 18:02:59 -0400 Subject: [PATCH] Use coroutine job for fetching next source page (cherry picked from commit e6f3cd03bbc8b9e0ea2203f981cda94062d5186c) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt --- .../source/browse/BrowseSourcePresenter.kt | 20 +++--- .../ui/browse/source/browse/Pager.kt | 2 +- .../ui/browse/source/browse/SourcePager.kt | 24 +++---- .../source/latest/LatestUpdatesPager.kt | 16 ++--- .../source/latest/LatestUpdatesPresenter.kt | 3 - .../exh/md/follows/MangaDexFollowsPager.kt | 12 +--- .../exh/md/similar/MangaDexSimilarPager.kt | 24 +++---- app/src/main/java/exh/recs/RecommendsPager.kt | 69 ++++++++----------- 8 files changed, 61 insertions(+), 109 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index 1d22b4503..258ba4ba6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.removeCovers import exh.savedsearches.EXHSavedSearch import exh.savedsearches.JsonSavedSearch +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.catch @@ -52,7 +53,6 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray -import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -117,7 +117,7 @@ open class BrowseSourcePresenter( /** * Subscription for one request from the pager. */ - private var pageSubscription: Subscription? = null + private var nextPageJob: Job? = null private val loggedServices by lazy { Injekt.get().services.filter { it.isLogged } } @@ -204,14 +204,14 @@ open class BrowseSourcePresenter( fun requestNext() { if (!hasNextPage()) return - pageSubscription?.let { remove(it) } - pageSubscription = Observable.defer { pager.requestNext() } - .subscribeFirst( - { _, _ -> - // Nothing to do when onNext is emitted. - }, - BrowseSourceController::onAddPageError - ) + nextPageJob?.cancel() + nextPageJob = launchIO { + try { + pager.requestNextPage() + } catch (e: Throwable) { + view?.onAddPageError(e) + } + } } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt index 267ffea7c..6db4bbbff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt @@ -21,7 +21,7 @@ abstract class Pager(var currentPage: Int = 1) { return results.asObservable() } - abstract fun requestNext(): Observable + abstract suspend fun requestNextPage() fun onPageReceived(mangasPage: MangasPage) { val page = currentPage diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourcePager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourcePager.kt index d771098c2..3d7970172 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourcePager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourcePager.kt @@ -2,14 +2,11 @@ package eu.kanade.tachiyomi.ui.browse.source.browse import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers +import eu.kanade.tachiyomi.util.lang.awaitSingle open class SourcePager(val source: CatalogueSource, val query: String, val filters: FilterList) : Pager() { - override fun requestNext(): Observable { + override suspend fun requestNextPage() { val page = currentPage val observable = if (query.isBlank() && filters.isEmpty()) { @@ -18,15 +15,12 @@ open class SourcePager(val source: CatalogueSource, val query: String, val filte source.fetchSearchManga(page, query, filters) } - return observable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { - if (it.mangas.isNotEmpty()) { - onPageReceived(it) - } else { - throw NoResultsException() - } - } + val mangasPage = observable.awaitSingle() + + if (mangasPage.mangas.isNotEmpty()) { + onPageReceived(mangasPage) + } else { + throw NoResultsException() + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPager.kt index 97eccfc14..fa07a3462 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPager.kt @@ -1,21 +1,13 @@ package eu.kanade.tachiyomi.ui.browse.source.latest import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.ui.browse.source.browse.Pager -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers +import eu.kanade.tachiyomi.util.lang.awaitSingle -/** - * LatestUpdatesPager inherited from the general Pager. - */ class LatestUpdatesPager(val source: CatalogueSource) : Pager() { - override fun requestNext(): Observable { - return source.fetchLatestUpdates(currentPage) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { onPageReceived(it) } + override suspend fun requestNextPage() { + val mangasPage = source.fetchLatestUpdates(currentPage).awaitSingle() + onPageReceived(mangasPage) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPresenter.kt index 964c0b3c6..968d3635d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPresenter.kt @@ -4,9 +4,6 @@ import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter import eu.kanade.tachiyomi.ui.browse.source.browse.Pager -/** - * Presenter of [LatestUpdatesController]. Inherit BrowseCataloguePresenter. - */ class LatestUpdatesPresenter(sourceId: Long) : BrowseSourcePresenter(sourceId) { override fun createPager(query: String, filters: FilterList): Pager { diff --git a/app/src/main/java/exh/md/follows/MangaDexFollowsPager.kt b/app/src/main/java/exh/md/follows/MangaDexFollowsPager.kt index aa9857b4f..84794a12b 100644 --- a/app/src/main/java/exh/md/follows/MangaDexFollowsPager.kt +++ b/app/src/main/java/exh/md/follows/MangaDexFollowsPager.kt @@ -1,22 +1,14 @@ package exh.md.follows -import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.ui.browse.source.browse.Pager -import eu.kanade.tachiyomi.util.lang.runAsObservable -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers /** * LatestUpdatesPager inherited from the general Pager. */ class MangaDexFollowsPager(val source: MangaDex) : Pager() { - override fun requestNext(): Observable { - return runAsObservable({ source.fetchFollows(currentPage) }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { onPageReceived(it) } + override suspend fun requestNextPage() { + onPageReceived(source.fetchFollows(currentPage)) } } diff --git a/app/src/main/java/exh/md/similar/MangaDexSimilarPager.kt b/app/src/main/java/exh/md/similar/MangaDexSimilarPager.kt index dfe63e63c..93fe6804b 100644 --- a/app/src/main/java/exh/md/similar/MangaDexSimilarPager.kt +++ b/app/src/main/java/exh/md/similar/MangaDexSimilarPager.kt @@ -2,30 +2,22 @@ package exh.md.similar import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.toMangaInfo -import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException import eu.kanade.tachiyomi.ui.browse.source.browse.Pager -import eu.kanade.tachiyomi.util.lang.runAsObservable -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers /** * MangaDexSimilarPager inherited from the general Pager. */ class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() { - override fun requestNext(): Observable { - return runAsObservable({ source.getMangaSimilar(manga.toMangaInfo()) }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { - if (it.mangas.isNotEmpty()) { - onPageReceived(it) - } else { - throw NoResultsException() - } - } + override suspend fun requestNextPage() { + val mangasPage = source.getMangaSimilar(manga.toMangaInfo()) + + if (mangasPage.mangas.isNotEmpty()) { + onPageReceived(mangasPage) + } else { + throw NoResultsException() + } } } diff --git a/app/src/main/java/exh/recs/RecommendsPager.kt b/app/src/main/java/exh/recs/RecommendsPager.kt index 1cdd2787d..aafb82401 100644 --- a/app/src/main/java/exh/recs/RecommendsPager.kt +++ b/app/src/main/java/exh/recs/RecommendsPager.kt @@ -5,16 +5,13 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.SMangaImpl +import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException import eu.kanade.tachiyomi.ui.browse.source.browse.Pager -import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.withIOContext import exh.log.maybeInjectEHLogger import exh.util.MangaType import exh.util.mangaType -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray @@ -29,9 +26,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers import timber.log.Timber abstract class API(_endpoint: String) { @@ -40,11 +34,11 @@ abstract class API(_endpoint: String) { .maybeInjectEHLogger() .build() - abstract suspend fun getRecsBySearch(search: String): List + abstract suspend fun getRecsBySearch(search: String): List } class MyAnimeList : API("https://api.jikan.moe/v3/") { - private suspend fun getRecsById(id: String): List { + private suspend fun getRecsById(id: String): List { val httpUrl = endpoint.toHttpUrlOrNull() ?: throw Exception("Could not convert endpoint url") val apiUrl = httpUrl.newBuilder() .addPathSegment("manga") @@ -59,7 +53,7 @@ class MyAnimeList : API("https://api.jikan.moe/v3/") { val recommendations = data["recommendations"] as? JsonArray return recommendations?.filterIsInstance()?.map { rec -> Timber.tag("RECOMMENDATIONS").d("MYANIMELIST > RECOMMENDATION: %s", rec["title"]?.jsonPrimitive?.content.orEmpty()) - SMangaImpl().apply { + SManga.create().apply { title = rec["title"]!!.jsonPrimitive.content thumbnail_url = rec["image_url"]!!.jsonPrimitive.content initialized = true @@ -68,7 +62,7 @@ class MyAnimeList : API("https://api.jikan.moe/v3/") { }.orEmpty() } - override suspend fun getRecsBySearch(search: String): List { + override suspend fun getRecsBySearch(search: String): List { val httpUrl = endpoint.toHttpUrlOrNull() ?: throw Exception("Could not convert endpoint url") val url = httpUrl.newBuilder() .addPathSegment("search") @@ -121,7 +115,7 @@ class Anilist : API("https://graphql.anilist.co/") { } } - override suspend fun getRecsBySearch(search: String): List { + override suspend fun getRecsBySearch(search: String): List { val query = """ |query Recommendations(${'$'}search: String!) { @@ -186,7 +180,7 @@ class Anilist : API("https://graphql.anilist.co/") { val rec = it.jsonObject["node"]!!.jsonObject["mediaRecommendation"]!!.jsonObject val recTitle = getTitle(rec) Timber.tag("RECOMMENDATIONS").d("ANILIST > RECOMMENDATION: %s", recTitle) - SMangaImpl().apply { + SManga.create().apply { title = recTitle thumbnail_url = rec["coverImage"]!!.jsonObject["large"]!!.jsonPrimitive.content initialized = true @@ -201,38 +195,29 @@ open class RecommendsPager( private val smart: Boolean = true, private var preferredApi: API = API.MYANIMELIST ) : Pager() { - override fun requestNext(): Observable { - return runAsObservable({ - if (smart) preferredApi = if (manga.mangaType() != MangaType.TYPE_MANGA) API.ANILIST else preferredApi + override suspend fun requestNextPage() { + if (smart) preferredApi = if (manga.mangaType() != MangaType.TYPE_MANGA) API.ANILIST else preferredApi - val apiList = API_MAP.toList().sortedByDescending { it.first == preferredApi } + val apiList = API_MAP.toList().sortedByDescending { it.first == preferredApi } - val recs = apiList - .asSequence() - .map { (key, api) -> - try { - val recs = runBlocking(Dispatchers.IO) { api.getRecsBySearch(manga.originalTitle) } - Timber.tag("RECOMMENDATIONS").d("%s > Results: %s", key, recs.count()) - recs - } catch (e: Exception) { - Timber.tag("RECOMMENDATIONS").e("%s > Error: %s", key, e.message) - listOf() - } - } - .firstOrNull { it.isNotEmpty() } - .orEmpty() - - MangasPage(recs, false) - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { - if (it.mangas.isNotEmpty()) { - onPageReceived(it) - } else { - throw NoResultsException() - } + val recs = apiList.firstNotNullOfOrNull { (key, api) -> + try { + val recs = api.getRecsBySearch(manga.originalTitle) + Timber.tag("RECOMMENDATIONS").d("%s > Results: %s", key, recs.count()) + recs + } catch (e: Exception) { + Timber.tag("RECOMMENDATIONS").e("%s > Error: %s", key, e.message) + null } + }.orEmpty() + + val mangasPage = MangasPage(recs, false) + + if (mangasPage.mangas.isNotEmpty()) { + onPageReceived(mangasPage) + } else { + throw NoResultsException() + } } companion object {