diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/HttpException.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/HttpException.kt new file mode 100644 index 000000000..55f4b00d6 --- /dev/null +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/HttpException.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.network + +import okhttp3.Response + +/** + * Exception that handles HTTP codes considered not successful by OkHttp. + * Use it to have a standardized error message in the app across the extensions. + * + * @see Response.isSuccessful + * @since tachiyomix 1.6 + * @param code [Int] the HTTP status code + */ +class HttpException(val code: Int) : IllegalStateException("HTTP error $code") diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt index 37dc1e480..090099820 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -82,8 +82,6 @@ import kotlin.random.Random } } - val nonCloudflareClient /* KMK --> */ by lazy /* KMK <-- */ { clientBuilder().build() } - /* SY --> */ open /* SY <-- */ val client /* KMK --> */ by lazy /* KMK <-- */ { clientBuilder() .addInterceptor( diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt index 0a860d069..fd7917278 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.network -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.json.Json @@ -23,6 +22,7 @@ import kotlin.coroutines.resumeWithException val jsonMime = "application/json; charset=utf-8".toMediaType() @OptIn(ExperimentalAtomicApi::class) +@Deprecated("Use suspend APIs instead") fun Call.asObservable(): Observable { return Observable.unsafeCreate { subscriber -> // Since Call is a one-shot type, clone it for each new subscriber. @@ -61,6 +61,7 @@ fun Call.asObservable(): Observable { } } +@Deprecated("Use suspend APIs instead") fun Call.asObservableSuccess(): Observable { return asObservable().doOnNext { response -> if (!response.isSuccessful) { @@ -70,35 +71,31 @@ fun Call.asObservableSuccess(): Observable { } } -// Based on https://github.com/gildor/kotlin-coroutines-okhttp -@OptIn(ExperimentalCoroutinesApi::class) +// Based on https://github.com/square/okhttp/blob/master/okhttp-coroutines/src/main/kotlin/okhttp3/coroutines/ExecuteAsync.kt +// and https://github.com/gildor/kotlin-coroutines-okhttp private suspend fun Call.await(callStack: Array): Response { return suspendCancellableCoroutine { continuation -> - val callback = - object : Callback { - override fun onResponse(call: Call, response: Response) { - continuation.resume(response) { - response.body.close() - } - } - - override fun onFailure(call: Call, e: IOException) { - // Don't bother with resuming the continuation if it is already cancelled. - if (continuation.isCancelled) return - val exception = IOException(e.message, e).apply { stackTrace = callStack } - continuation.resumeWithException(exception) - } - } - - enqueue(callback) - continuation.invokeOnCancellation { try { - cancel() - } catch (ex: Throwable) { - // Ignore cancel exception + this.cancel() + } catch (_: Throwable) { + // ignore } } + + this.enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + if (continuation.isCancelled) return + val exception = IOException(e.message, e).apply { stackTrace = callStack } + continuation.resumeWithException(exception) + } + + override fun onResponse(call: Call, response: Response) { + continuation.resume(response) { _, value, _ -> + value.close() + } + } + }) } } @@ -108,7 +105,7 @@ suspend fun Call.await(): Response { } /** - * @since extensions-lib 1.5 + * Similar to [await] but throws [HttpException] if [Response.isSuccessful] returns false */ suspend fun Call.awaitSuccess(): Response { val callStack = Exception().stackTrace.run { copyOfRange(1, size) } @@ -148,12 +145,3 @@ fun decodeFromJsonResponse( json.decodeFromBufferedSource(deserializer, it) } } - -/** - * Exception that handles HTTP codes considered not successful by OkHttp. - * Use it to have a standardized error message in the app across the extensions. - * - * @since extensions-lib 1.5 - * @param code [Int] the HTTP status code - */ -class HttpException(val code: Int) : IllegalStateException("HTTP error $code") diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt index 399ad9fad..762efb2c7 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -158,6 +158,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page number to retrieve. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga(page)")) open fun fetchPopularManga(page: Int): Observable { return client.newCall(popularMangaRequest(page)) @@ -203,6 +204,7 @@ abstract class HttpSource : CatalogueSource { * @param query the search query. * @param filters the list of filters to apply. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga(page, query, filters)")) open fun fetchSearchManga( page: Int, @@ -261,6 +263,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page number to retrieve. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates(page)")) open fun fetchLatestUpdates(page: Int): Observable { return client.newCall(latestUpdatesRequest(page)) @@ -305,6 +308,7 @@ abstract class HttpSource : CatalogueSource { * * @param manga the manga to be updated. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails(manga)")) open fun fetchMangaDetails(manga: SManga): Observable { return client.newCall(mangaDetailsRequest(manga)) @@ -400,6 +404,7 @@ abstract class HttpSource : CatalogueSource { * * @param manga the manga to look for chapters. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList(manga)")) open fun fetchChapterList(manga: SManga): Observable> { return client.newCall(chapterListRequest(manga)) @@ -453,6 +458,7 @@ abstract class HttpSource : CatalogueSource { * * @param chapter the chapter whose page list has to be fetched. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList(chapter)")) open fun fetchPageList(chapter: SChapter): Observable> { return client.newCall(pageListRequest(chapter)) @@ -499,6 +505,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the page whose source image has to be fetched. */ + @Suppress("DEPRECATION") @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl(page)")) open fun fetchImageUrl(page: Page): Observable { return client.newCall(imageUrlRequest(page))