Add more replacement suspend functions for source APIs
These are basically 1-to-1 replacements for the existing RxJava APIs. This will make the initial migration off of RxJava simpler. We'll revisit the actual call flows in followup versions of the API.
This commit is contained in:
parent
1668be8587
commit
26c5d761da
12 changed files with 137 additions and 128 deletions
|
@ -43,7 +43,6 @@ import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
||||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||||
import tachiyomi.core.util.lang.awaitSingle
|
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
import tachiyomi.core.util.lang.launchNow
|
import tachiyomi.core.util.lang.launchNow
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
|
@ -363,7 +362,7 @@ class Downloader(
|
||||||
if (page.imageUrl.isNullOrEmpty()) {
|
if (page.imageUrl.isNullOrEmpty()) {
|
||||||
page.status = Page.State.LOAD_PAGE
|
page.status = Page.State.LOAD_PAGE
|
||||||
try {
|
try {
|
||||||
page.imageUrl = download.source.fetchImageUrl(page).awaitSingle()
|
page.imageUrl = download.source.getImageUrl(page)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
page.status = Page.State.ERROR
|
page.status = Page.State.ERROR
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import tachiyomi.core.util.lang.awaitSingle
|
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
@ -140,7 +139,7 @@ abstract class SearchScreenModel(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val page = withContext(coroutineDispatcher) {
|
val page = withContext(coroutineDispatcher) {
|
||||||
source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
|
source.getSearchManga(1, query, source.getFilterList())
|
||||||
}
|
}
|
||||||
|
|
||||||
val titles = page.mangas.map {
|
val titles = page.mangas.map {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import tachiyomi.core.util.lang.awaitSingle
|
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -170,7 +169,7 @@ internal class HttpPageLoader(
|
||||||
try {
|
try {
|
||||||
if (page.imageUrl.isNullOrEmpty()) {
|
if (page.imageUrl.isNullOrEmpty()) {
|
||||||
page.status = Page.State.LOAD_PAGE
|
page.status = Page.State.LOAD_PAGE
|
||||||
page.imageUrl = source.fetchImageUrl(page).awaitSingle()
|
page.imageUrl = source.getImageUrl(page)
|
||||||
}
|
}
|
||||||
val imageUrl = page.imageUrl!!
|
val imageUrl = page.imageUrl!!
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,15 @@ fun Call.asObservable(): Observable<Response> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Call.asObservableSuccess(): Observable<Response> {
|
||||||
|
return asObservable().doOnNext { response ->
|
||||||
|
if (!response.isSuccessful) {
|
||||||
|
response.close()
|
||||||
|
throw HttpException(response.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
|
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
||||||
|
@ -95,6 +104,9 @@ suspend fun Call.await(): Response {
|
||||||
return await(callStack)
|
return await(callStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since extensions-lib 1.5
|
||||||
|
*/
|
||||||
suspend fun Call.awaitSuccess(): Response {
|
suspend fun Call.awaitSuccess(): Response {
|
||||||
val callStack = Exception().stackTrace.run { copyOfRange(1, size) }
|
val callStack = Exception().stackTrace.run { copyOfRange(1, size) }
|
||||||
val response = await(callStack)
|
val response = await(callStack)
|
||||||
|
@ -105,15 +117,6 @@ suspend fun Call.awaitSuccess(): Response {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Call.asObservableSuccess(): Observable<Response> {
|
|
||||||
return asObservable().doOnNext { response ->
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
response.close()
|
|
||||||
throw HttpException(response.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: ProgressListener): Call {
|
fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: ProgressListener): Call {
|
||||||
val progressClient = newBuilder()
|
val progressClient = newBuilder()
|
||||||
.cache(null)
|
.cache(null)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import tachiyomi.core.util.lang.awaitSingle
|
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||||
|
|
||||||
|
@ -13,19 +12,19 @@ class SourceSearchPagingSource(source: CatalogueSource, val query: String, val f
|
||||||
source,
|
source,
|
||||||
) {
|
) {
|
||||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||||
return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
|
return source.getSearchManga(currentPage, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
||||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||||
return source.fetchPopularManga(currentPage).awaitSingle()
|
return source.getPopularManga(currentPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
|
||||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||||
return source.fetchLatestUpdates(currentPage).awaitSingle()
|
return source.getLatestUpdates(currentPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import rx.Observable
|
|
||||||
|
|
||||||
class StubSource(
|
class StubSource(
|
||||||
override val id: Long,
|
override val id: Long,
|
||||||
|
@ -14,36 +13,16 @@ class StubSource(
|
||||||
|
|
||||||
private val isInvalid: Boolean = name.isBlank() || lang.isBlank()
|
private val isInvalid: Boolean = name.isBlank() || lang.isBlank()
|
||||||
|
|
||||||
override suspend fun getMangaDetails(manga: SManga): SManga {
|
override suspend fun getMangaDetails(manga: SManga): SManga =
|
||||||
throw SourceNotInstalledException()
|
throw SourceNotInstalledException()
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
|
override suspend fun getChapterList(manga: SManga): List<SChapter> =
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
|
||||||
return Observable.error(SourceNotInstalledException())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getChapterList(manga: SManga): List<SChapter> {
|
|
||||||
throw SourceNotInstalledException()
|
throw SourceNotInstalledException()
|
||||||
}
|
override suspend fun getPageList(chapter: SChapter): List<Page> =
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
|
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
|
||||||
return Observable.error(SourceNotInstalledException())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getPageList(chapter: SChapter): List<Page> {
|
|
||||||
throw SourceNotInstalledException()
|
throw SourceNotInstalledException()
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
|
override fun toString(): String =
|
||||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString()
|
||||||
return Observable.error(SourceNotInstalledException())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceNotInstalledException : Exception()
|
class SourceNotInstalledException : Exception()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.source
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import tachiyomi.core.util.lang.awaitSingle
|
||||||
|
|
||||||
interface CatalogueSource : Source {
|
interface CatalogueSource : Source {
|
||||||
|
|
||||||
|
@ -17,30 +18,63 @@ interface CatalogueSource : Source {
|
||||||
val supportsLatest: Boolean
|
val supportsLatest: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable containing a page with a list of manga.
|
* Get a page with a list of manga.
|
||||||
*
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
* @param page the page number to retrieve.
|
* @param page the page number to retrieve.
|
||||||
*/
|
*/
|
||||||
fun fetchPopularManga(page: Int): Observable<MangasPage>
|
@Suppress("DEPRECATION")
|
||||||
|
suspend fun getPopularManga(page: Int): MangasPage {
|
||||||
|
return fetchPopularManga(page).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable containing a page with a list of manga.
|
* Get a page with a list of manga.
|
||||||
*
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
* @param page the page number to retrieve.
|
* @param page the page number to retrieve.
|
||||||
* @param query the search query.
|
* @param query the search query.
|
||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage>
|
@Suppress("DEPRECATION")
|
||||||
|
suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
|
return fetchSearchManga(page, query, filters).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable containing a page with a list of latest manga updates.
|
* Get a page with a list of latest manga updates.
|
||||||
*
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
* @param page the page number to retrieve.
|
* @param page the page number to retrieve.
|
||||||
*/
|
*/
|
||||||
fun fetchLatestUpdates(page: Int): Observable<MangasPage>
|
@Suppress("DEPRECATION")
|
||||||
|
suspend fun getLatestUpdates(page: Int): MangasPage {
|
||||||
|
return fetchLatestUpdates(page).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of filters for the source.
|
* Returns the list of filters for the source.
|
||||||
*/
|
*/
|
||||||
fun getFilterList(): FilterList
|
fun getFilterList(): FilterList
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
"Use the non-RxJava API instead",
|
||||||
|
ReplaceWith("getPopularManga"),
|
||||||
|
)
|
||||||
|
fun fetchPopularManga(page: Int): Observable<MangasPage> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
"Use the non-RxJava API instead",
|
||||||
|
ReplaceWith("getSearchManga"),
|
||||||
|
)
|
||||||
|
fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
"Use the non-RxJava API instead",
|
||||||
|
ReplaceWith("getLatestUpdates"),
|
||||||
|
)
|
||||||
|
fun fetchLatestUpdates(page: Int): Observable<MangasPage> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
package eu.kanade.tachiyomi.source
|
package eu.kanade.tachiyomi.source
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
interface ConfigurableSource : Source {
|
interface ConfigurableSource : Source {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets instance of [SharedPreferences] scoped to the specific source.
|
||||||
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
|
*/
|
||||||
|
fun getPreferences(): SharedPreferences =
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
|
||||||
fun setupPreferenceScreen(screen: PreferenceScreen)
|
fun setupPreferenceScreen(screen: PreferenceScreen)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ interface Source {
|
||||||
/**
|
/**
|
||||||
* Get the updated details for a manga.
|
* Get the updated details for a manga.
|
||||||
*
|
*
|
||||||
* @since extensions-lib 1.4
|
* @since extensions-lib 1.5
|
||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
* @return the updated manga.
|
* @return the updated manga.
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +39,7 @@ interface Source {
|
||||||
/**
|
/**
|
||||||
* Get all the available chapters for a manga.
|
* Get all the available chapters for a manga.
|
||||||
*
|
*
|
||||||
* @since extensions-lib 1.4
|
* @since extensions-lib 1.5
|
||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
* @return the chapters for the manga.
|
* @return the chapters for the manga.
|
||||||
*/
|
*/
|
||||||
|
@ -52,7 +52,7 @@ interface Source {
|
||||||
* Get the list of pages a chapter has. Pages should be returned
|
* Get the list of pages a chapter has. Pages should be returned
|
||||||
* in the expected order; the index is ignored.
|
* in the expected order; the index is ignored.
|
||||||
*
|
*
|
||||||
* @since extensions-lib 1.4
|
* @since extensions-lib 1.5
|
||||||
* @param chapter the chapter.
|
* @param chapter the chapter.
|
||||||
* @return the pages for the chapter.
|
* @return the pages for the chapter.
|
||||||
*/
|
*/
|
||||||
|
@ -61,41 +61,24 @@ interface Source {
|
||||||
return fetchPageList(chapter).awaitSingle()
|
return fetchPageList(chapter).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable with the updated details for a manga.
|
|
||||||
*
|
|
||||||
* @param manga the manga to update.
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getMangaDetails"),
|
ReplaceWith("getMangaDetails"),
|
||||||
)
|
)
|
||||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException(
|
fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||||
"Not used",
|
throw IllegalStateException("Not used")
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable with all the available chapters for a manga.
|
|
||||||
*
|
|
||||||
* @param manga the manga to update.
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getChapterList"),
|
ReplaceWith("getChapterList"),
|
||||||
)
|
)
|
||||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException(
|
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
|
||||||
"Not used",
|
throw IllegalStateException("Not used")
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable with the list of pages a chapter has. Pages should be returned
|
|
||||||
* in the expected order; the index is ignored.
|
|
||||||
*
|
|
||||||
* @param chapter the chapter.
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getPageList"),
|
ReplaceWith("getPageList"),
|
||||||
)
|
)
|
||||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
|
||||||
|
throw IllegalStateException("Not used")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import tachiyomi.core.util.lang.awaitSingle
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
|
@ -24,6 +25,7 @@ import java.security.MessageDigest
|
||||||
/**
|
/**
|
||||||
* A simple implementation for sources from a website.
|
* A simple implementation for sources from a website.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
abstract class HttpSource : CatalogueSource {
|
abstract class HttpSource : CatalogueSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +83,7 @@ abstract class HttpSource : CatalogueSource {
|
||||||
* @param versionId [Int] the version ID of the source
|
* @param versionId [Int] the version ID of the source
|
||||||
* @return a unique ID for the source
|
* @return a unique ID for the source
|
||||||
*/
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
protected fun generateId(name: String, lang: String, versionId: Int): Long {
|
protected fun generateId(name: String, lang: String, versionId: Int): Long {
|
||||||
val key = "${name.lowercase()}/$lang/$versionId"
|
val key = "${name.lowercase()}/$lang/$versionId"
|
||||||
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
||||||
|
@ -202,11 +205,18 @@ abstract class HttpSource : CatalogueSource {
|
||||||
protected abstract fun latestUpdatesParse(response: Response): MangasPage
|
protected abstract fun latestUpdatesParse(response: Response): MangasPage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the updated details for a manga. Normally it's not needed to
|
* Get the updated details for a manga.
|
||||||
* override this method.
|
* Normally it's not needed to override this method.
|
||||||
*
|
*
|
||||||
* @param manga the manga to be updated.
|
* @param manga the manga to update.
|
||||||
|
* @return the updated manga.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override suspend fun getMangaDetails(manga: SManga): SManga {
|
||||||
|
return fetchMangaDetails(manga).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
return client.newCall(mangaDetailsRequest(manga))
|
return client.newCall(mangaDetailsRequest(manga))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
|
@ -233,11 +243,23 @@ abstract class HttpSource : CatalogueSource {
|
||||||
protected abstract fun mangaDetailsParse(response: Response): SManga
|
protected abstract fun mangaDetailsParse(response: Response): SManga
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the updated chapter list for a manga. Normally it's not needed to
|
* Get all the available chapters for a manga.
|
||||||
* override this method. If a manga is licensed an empty chapter list observable is returned
|
* Normally it's not needed to override this method.
|
||||||
*
|
*
|
||||||
* @param manga the manga to look for chapters.
|
* @param manga the manga to update.
|
||||||
|
* @return the chapters for the manga.
|
||||||
|
* @throws LicensedMangaChaptersException if a manga is licensed and therefore no chapters are available.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override suspend fun getChapterList(manga: SManga): List<SChapter> {
|
||||||
|
if (manga.status == SManga.LICENSED) {
|
||||||
|
throw LicensedMangaChaptersException()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchChapterList(manga).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
return if (manga.status != SManga.LICENSED) {
|
return if (manga.status != SManga.LICENSED) {
|
||||||
client.newCall(chapterListRequest(manga))
|
client.newCall(chapterListRequest(manga))
|
||||||
|
@ -268,10 +290,18 @@ abstract class HttpSource : CatalogueSource {
|
||||||
protected abstract fun chapterListParse(response: Response): List<SChapter>
|
protected abstract fun chapterListParse(response: Response): List<SChapter>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with the page list for a chapter.
|
* Get the list of pages a chapter has. Pages should be returned
|
||||||
|
* in the expected order; the index is ignored.
|
||||||
*
|
*
|
||||||
* @param chapter the chapter whose page list has to be fetched.
|
* @param chapter the chapter.
|
||||||
|
* @return the pages for the chapter.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override suspend fun getPageList(chapter: SChapter): List<Page> {
|
||||||
|
return fetchPageList(chapter).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
|
||||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
return client.newCall(pageListRequest(chapter))
|
return client.newCall(pageListRequest(chapter))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
|
@ -301,8 +331,15 @@ abstract class HttpSource : CatalogueSource {
|
||||||
* Returns an observable with the page containing the source url of the image. If there's any
|
* Returns an observable with the page containing the source url of the image. If there's any
|
||||||
* error, it will return null instead of throwing an exception.
|
* error, it will return null instead of throwing an exception.
|
||||||
*
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
* @param page the page whose source image has to be fetched.
|
* @param page the page whose source image has to be fetched.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
open suspend fun getImageUrl(page: Page): String {
|
||||||
|
return fetchImageUrl(page).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
||||||
open fun fetchImageUrl(page: Page): Observable<String> {
|
open fun fetchImageUrl(page: Page): Observable<String> {
|
||||||
return client.newCall(imageUrlRequest(page))
|
return client.newCall(imageUrlRequest(page))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
|
@ -326,24 +363,14 @@ abstract class HttpSource : CatalogueSource {
|
||||||
*/
|
*/
|
||||||
protected abstract fun imageUrlParse(response: Response): String
|
protected abstract fun imageUrlParse(response: Response): String
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an observable with the response of the source image.
|
|
||||||
*
|
|
||||||
* @param page the page whose source image has to be downloaded.
|
|
||||||
*/
|
|
||||||
fun fetchImage(page: Page): Observable<Response> {
|
|
||||||
// images will be cached or saved manually, so don't take up network cache
|
|
||||||
return client.newCachelessCallWithProgress(imageRequest(page), page)
|
|
||||||
.asObservableSuccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the response of the source image.
|
* Returns the response of the source image.
|
||||||
|
* Typically does not need to be overridden.
|
||||||
*
|
*
|
||||||
|
* @since extensions-lib 1.5
|
||||||
* @param page the page whose source image has to be downloaded.
|
* @param page the page whose source image has to be downloaded.
|
||||||
*/
|
*/
|
||||||
suspend fun getImage(page: Page): Response {
|
open suspend fun getImage(page: Page): Response {
|
||||||
// images will be cached or saved manually, so don't take up network cache
|
|
||||||
return client.newCachelessCallWithProgress(imageRequest(page), page)
|
return client.newCachelessCallWithProgress(imageRequest(page), page)
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.source.online
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
|
||||||
import rx.Observable
|
|
||||||
|
|
||||||
fun HttpSource.fetchAllImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
|
||||||
return Observable.from(pages)
|
|
||||||
.filter { !it.imageUrl.isNullOrEmpty() }
|
|
||||||
.mergeWith(fetchRemainingImageUrlsFromPageList(pages))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun HttpSource.fetchRemainingImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
|
||||||
return Observable.from(pages)
|
|
||||||
.filter { it.imageUrl.isNullOrEmpty() }
|
|
||||||
.concatMap { getImageUrl(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun HttpSource.getImageUrl(page: Page): Observable<Page> {
|
|
||||||
page.status = Page.State.LOAD_PAGE
|
|
||||||
return fetchImageUrl(page)
|
|
||||||
.doOnError { page.status = Page.State.ERROR }
|
|
||||||
.onErrorReturn { null }
|
|
||||||
.doOnNext { page.imageUrl = it }
|
|
||||||
.map { page }
|
|
||||||
}
|
|
|
@ -16,7 +16,6 @@ import kotlinx.serialization.json.decodeFromStream
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import nl.adaptivity.xmlutil.AndroidXmlReader
|
import nl.adaptivity.xmlutil.AndroidXmlReader
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import rx.Observable
|
|
||||||
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
||||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||||
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
|
import tachiyomi.core.metadata.comicinfo.copyFromComicInfo
|
||||||
|
@ -66,11 +65,11 @@ actual class LocalSource(
|
||||||
override val supportsLatest: Boolean = true
|
override val supportsLatest: Boolean = true
|
||||||
|
|
||||||
// Browse related
|
// Browse related
|
||||||
override fun fetchPopularManga(page: Int) = fetchSearchManga(page, "", POPULAR_FILTERS)
|
override suspend fun getPopularManga(page: Int) = getSearchManga(page, "", POPULAR_FILTERS)
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
|
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS)
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
||||||
val lastModifiedLimit by lazy { if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L }
|
val lastModifiedLimit by lazy { if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L }
|
||||||
var mangaDirs = baseDirsFiles
|
var mangaDirs = baseDirsFiles
|
||||||
|
@ -143,7 +142,7 @@ actual class LocalSource(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Observable.just(MangasPage(mangas.toList(), false))
|
return MangasPage(mangas.toList(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manga details related
|
// Manga details related
|
||||||
|
|
Reference in a new issue