diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/SuspendHttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/SuspendHttpSource.kt deleted file mode 100644 index 7faa05fab..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/SuspendHttpSource.kt +++ /dev/null @@ -1,374 +0,0 @@ -package eu.kanade.tachiyomi.source.online - -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.await -import eu.kanade.tachiyomi.network.newCallWithProgress -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.util.lang.runAsObservable -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import okhttp3.Request -import okhttp3.Response -import rx.Observable -import kotlin.jvm.Throws - -/** - * A simple implementation for sources from a website, but for Coroutines. - */ -abstract class SuspendHttpSource : HttpSource() { - - /** - * Returns an observable containing a page with a list of manga. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - */ - final override fun fetchPopularManga(page: Int): Observable { - return runAsObservable({ fetchPopularMangaSuspended(page) }) - } - - open suspend fun fetchPopularMangaSuspended(page: Int): MangasPage { - return withContext(Dispatchers.IO) { - val response = client.newCall(popularMangaRequestSuspended(page)).await() - popularMangaParseSuspended(response) - } - } - - /** - * Returns the request for the popular manga given the page. - * - * @param page the page number to retrieve. - */ - final override fun popularMangaRequest(page: Int): Request { - return runBlocking { popularMangaRequestSuspended(page) } - } - - protected abstract suspend fun popularMangaRequestSuspended(page: Int): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - final override fun popularMangaParse(response: Response): MangasPage { - return runBlocking { popularMangaParseSuspended(response) } - } - - protected abstract suspend fun popularMangaParseSuspended(response: Response): MangasPage - - /** - * Returns an observable containing a page with a list of manga. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - final override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return runAsObservable({ fetchSearchMangaSuspended(page, query, filters) }) - } - - open suspend fun fetchSearchMangaSuspended(page: Int, query: String, filters: FilterList): MangasPage { - return withContext(Dispatchers.IO) { - val response = client.newCall(searchMangaRequestSuspended(page, query, filters)).await() - searchMangaParseSuspended(response) - } - } - - /** - * Returns the request for the search manga given the page. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - final override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return runBlocking { searchMangaRequestSuspended(page, query, filters) } - } - - protected abstract suspend fun searchMangaRequestSuspended(page: Int, query: String, filters: FilterList): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - final override fun searchMangaParse(response: Response): MangasPage { - return runBlocking { searchMangaParseSuspended(response) } - } - - protected abstract suspend fun searchMangaParseSuspended(response: Response): MangasPage - - /** - * Returns an observable containing a page with a list of latest manga updates. - * - * @param page the page number to retrieve. - */ - final override fun fetchLatestUpdates(page: Int): Observable { - return runAsObservable({ fetchLatestUpdatesSuspended(page) }) - } - - open suspend fun fetchLatestUpdatesSuspended(page: Int): MangasPage { - return withContext(Dispatchers.IO) { - val response = client.newCall(latestUpdatesRequestSuspended(page)).await() - latestUpdatesParseSuspended(response) - } - } - - /** - * Returns the request for latest manga given the page. - * - * @param page the page number to retrieve. - */ - final override fun latestUpdatesRequest(page: Int): Request { - return runBlocking { latestUpdatesRequestSuspended(page) } - } - - protected abstract suspend fun latestUpdatesRequestSuspended(page: Int): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - final override fun latestUpdatesParse(response: Response): MangasPage { - return runBlocking { latestUpdatesParseSuspended(response) } - } - - protected abstract suspend fun latestUpdatesParseSuspended(response: Response): MangasPage - - /** - * Returns an observable with the updated details for a manga. Normally it's not needed to - * override this method. - * - * @param manga the manga to be updated. - */ - final override fun fetchMangaDetails(manga: SManga): Observable { - return runAsObservable({ fetchMangaDetailsSuspended(manga) }) - } - - open suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga { - return withContext(Dispatchers.IO) { - val response = client.newCall(mangaDetailsRequestSuspended(manga)).await() - mangaDetailsParseSuspended(response).apply { initialized = true } - } - } - - /** - * Returns the request for the details of a manga. Override only if it's needed to change the - * url, send different headers or request method like POST. - * - * @param manga the manga to be updated. - */ - final override fun mangaDetailsRequest(manga: SManga): Request { - return runBlocking { mangaDetailsRequestSuspended(manga) } - } - - open suspend fun mangaDetailsRequestSuspended(manga: SManga): Request { - return GET(baseUrl + manga.url, headers) - } - - /** - * Parses the response from the site and returns the details of a manga. - * - * @param response the response from the site. - */ - final override fun mangaDetailsParse(response: Response): SManga { - return runBlocking { mangaDetailsParseSuspended(response) } - } - - protected abstract suspend fun mangaDetailsParseSuspended(response: Response): SManga - - /** - * Returns an observable with the updated chapter list for a manga. Normally it's not needed to - * override this method. If a manga is licensed an empty chapter list observable is returned - * - * @param manga the manga to look for chapters. - */ - final override fun fetchChapterList(manga: SManga): Observable> { - return try { - runAsObservable({ fetchChapterListSuspended(manga) }) - } catch (e: LicencedException) { - Observable.error(Exception("Licensed - No chapters to show")) - } - } - - @Throws(LicencedException::class) - open suspend fun fetchChapterListSuspended(manga: SManga): List { - return withContext(Dispatchers.IO) { - if (manga.status != SManga.LICENSED) { - val response = client.newCall(chapterListRequestSuspended(manga)).await() - chapterListParseSuspended(response) - } else { - throw LicencedException("Licensed - No chapters to show") - } - } - } - - /** - * Returns the request for updating the chapter list. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param manga the manga to look for chapters. - */ - final override fun chapterListRequest(manga: SManga): Request { - return runBlocking { chapterListRequestSuspended(manga) } - } - - protected open suspend fun chapterListRequestSuspended(manga: SManga): Request { - return GET(baseUrl + manga.url, headers) - } - - /** - * Parses the response from the site and returns a list of chapters. - * - * @param response the response from the site. - */ - final override fun chapterListParse(response: Response): List { - return runBlocking { chapterListParseSuspended(response) } - } - - protected abstract suspend fun chapterListParseSuspended(response: Response): List - - /** - * Returns an observable with the page list for a chapter. - * - * @param chapter the chapter whose page list has to be fetched. - */ - final override fun fetchPageList(chapter: SChapter): Observable> { - return runAsObservable({ fetchPageListSuspended(chapter) }) - } - - open suspend fun fetchPageListSuspended(chapter: SChapter): List { - return withContext(Dispatchers.IO) { - val response = client.newCall(pageListRequestSuspended(chapter)).await() - pageListParseSuspended(response) - } - } - - /** - * Returns the request for getting the page list. Override only if it's needed to override the - * url, send different headers or request method like POST. - * - * @param chapter the chapter whose page list has to be fetched. - */ - final override fun pageListRequest(chapter: SChapter): Request { - return runBlocking { pageListRequestSuspended(chapter) } - } - - protected open suspend fun pageListRequestSuspended(chapter: SChapter): Request { - return GET(baseUrl + chapter.url, headers) - } - - /** - * Parses the response from the site and returns a list of pages. - * - * @param response the response from the site. - */ - final override fun pageListParse(response: Response): List { - return runBlocking { pageListParseSuspended(response) } - } - - protected abstract suspend fun pageListParseSuspended(response: Response): List - - /** - * 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. - * - * @param page the page whose source image has to be fetched. - */ - final override fun fetchImageUrl(page: Page): Observable { - return runAsObservable({ fetchImageUrlSuspended(page) }) - } - - open suspend fun fetchImageUrlSuspended(page: Page): String { - return withContext(Dispatchers.IO) { - val response = client.newCall(imageUrlRequestSuspended(page)).await() - imageUrlParseSuspended(response) - } - } - - /** - * Returns the request for getting the url to the source image. Override only if it's needed to - * override the url, send different headers or request method like POST. - * - * @param page the chapter whose page list has to be fetched - */ - final override fun imageUrlRequest(page: Page): Request { - return runBlocking { imageUrlRequestSuspended(page) } - } - - protected open suspend fun imageUrlRequestSuspended(page: Page): Request { - return GET(page.url, headers) - } - - /** - * Parses the response from the site and returns the absolute url to the source image. - * - * @param response the response from the site. - */ - final override fun imageUrlParse(response: Response): String { - return runBlocking { imageUrlParseSuspended(response) } - } - - protected abstract suspend fun imageUrlParseSuspended(response: Response): String - - /** - * Returns an observable with the response of the source image. - * - * @param page the page whose source image has to be downloaded. - */ - final override fun fetchImage(page: Page): Observable { - return runAsObservable({ fetchImageSuspended(page) }) - } - - open suspend fun fetchImageSuspended(page: Page): Response { - return withContext(Dispatchers.IO) { - client.newCallWithProgress(imageRequestSuspended(page), page).await() - } - } - - /** - * Returns the request for getting the source image. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param page the chapter whose page list has to be fetched - */ - final override fun imageRequest(page: Page): Request { - return runBlocking { imageRequestSuspended(page) } - } - - protected open suspend fun imageRequestSuspended(page: Page): Request { - return GET(page.imageUrl!!, headers) - } - - /** - * Called before inserting a new chapter into database. Use it if you need to override chapter - * fields, like the title or the chapter number. Do not change anything to [manga]. - * - * @param chapter the chapter to be added. - * @param manga the manga of the chapter. - */ - final override fun prepareNewChapter(chapter: SChapter, manga: SManga) { - runBlocking { prepareNewChapterSuspended(chapter, manga) } - } - - open suspend fun prepareNewChapterSuspended(chapter: SChapter, manga: SManga) { - } - - /** - * Returns the list of filters for the source. - */ - override fun getFilterList() = runBlocking { getFilterListSuspended() } - - open suspend fun getFilterListSuspended() = FilterList() - - companion object { - data class LicencedException(override val message: String?) : Exception() - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt index 1d2ba5447..fe1d419b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt @@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.lang.runAsObservable +import eu.kanade.tachiyomi.util.lang.withIOContext import exh.GalleryAddEvent import exh.GalleryAdder import exh.md.MangaDexFabHeaderAdapter @@ -47,12 +48,10 @@ import exh.source.DelegatedHttpSource import exh.ui.metadata.adapters.MangaDexDescriptionAdapter import exh.util.urlImportFetchSearchManga import exh.widget.preference.MangadexLoginDialog -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import okhttp3.CacheControl import okhttp3.FormBody import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import okhttp3.Response import rx.Observable @@ -184,7 +183,7 @@ class MangaDex(delegate: HttpSource, val context: Context) : } override fun isLogged(): Boolean { - val httpUrl = MdUtil.baseUrl.toHttpUrlOrNull()!! + val httpUrl = MdUtil.baseUrl.toHttpUrl() return trackManager.mdList.isLogged && network.cookieManager.get(httpUrl).any { it.name == REMEMBER_ME } } @@ -193,12 +192,13 @@ class MangaDex(delegate: HttpSource, val context: Context) : password: String, twoFactorCode: String ): Boolean { - return withContext(Dispatchers.IO) { - val formBody = FormBody.Builder() - .add("login_username", username) - .add("login_password", password) - .add("no_js", "1") - .add("remember_me", "1") + return withIOContext { + val formBody = FormBody.Builder().apply { + add("login_username", username) + add("login_password", password) + add("no_js", "1") + add("remember_me", "1") + } twoFactorCode.let { formBody.add("two_factor", it) @@ -212,11 +212,11 @@ class MangaDex(delegate: HttpSource, val context: Context) : ) ).await() - withContext(Dispatchers.IO) { response.body!!.string() }.let { - if (it.isEmpty()) { + withIOContext { response.body?.string() }.let { result -> + if (result != null && result.isEmpty()) { true } else { - val error = HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_COMPACT).toString() + val error = result?.let { HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_COMPACT).toString() } throw Exception(error) } } @@ -224,23 +224,23 @@ class MangaDex(delegate: HttpSource, val context: Context) : } override suspend fun logout(): Boolean { - return withContext(Dispatchers.IO) { + return withIOContext { // https://mangadex.org/ajax/actions.ajax.php?function=logout - val httpUrl = MdUtil.baseUrl.toHttpUrlOrNull()!! + val httpUrl = MdUtil.baseUrl.toHttpUrl() val listOfDexCookies = network.cookieManager.get(httpUrl) val cookie = listOfDexCookies.find { it.name == REMEMBER_ME } val token = cookie?.value if (token.isNullOrEmpty()) { - return@withContext true + return@withIOContext true } val result = client.newCall( POST("${MdUtil.baseUrl}/ajax/actions.ajax.php?function=logout", headers).newBuilder().addHeader(REMEMBER_ME, token).build() ).await() - val resultStr = withContext(Dispatchers.IO) { result.body?.string() } + val resultStr = withIOContext { result.body?.string() } if (resultStr?.contains("success", true) == true) { network.cookieManager.remove(httpUrl) trackManager.mdList.logout() - return@withContext true + return@withIOContext true } false @@ -248,19 +248,19 @@ class MangaDex(delegate: HttpSource, val context: Context) : } override suspend fun fetchAllFollows(forceHd: Boolean): List> { - return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchAllFollows(forceHd) } + return withIOContext { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).fetchAllFollows(forceHd) } } suspend fun updateReadingProgress(track: Track): Boolean { - return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateReadingProgress(track) } + return withIOContext { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateReadingProgress(track) } } suspend fun updateRating(track: Track): Boolean { - return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateRating(track) } + return withIOContext { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateRating(track) } } override suspend fun fetchTrackingInfo(url: String): Track { - return withContext(Dispatchers.IO) { + return withIOContext { if (!isLogged()) { throw Exception("Not Logged in") } @@ -269,7 +269,7 @@ class MangaDex(delegate: HttpSource, val context: Context) : } override suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean { - return withContext(Dispatchers.IO) { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateFollowStatus(mangaID, followStatus) } + return withIOContext { FollowsHandler(client, headers, Injekt.get(), useLowQualityThumbnail()).updateFollowStatus(mangaID, followStatus) } } override fun getFilterHeader(controller: BaseController<*>): MangaDexFabHeaderAdapter { @@ -292,13 +292,11 @@ class MangaDex(delegate: HttpSource, val context: Context) : }) .map { res -> MangasPage( - ( - if (res is GalleryAddEvent.Success) { - listOf(res.manga) - } else { - emptyList() - } - ), + if (res is GalleryAddEvent.Success) { + listOf(res.manga) + } else { + emptyList() + }, false ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt index 8298a00f3..c124e4e63 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MergedSource.kt @@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.source.model.toSManga -import eu.kanade.tachiyomi.source.online.SuspendHttpSource +import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.withIOContext @@ -25,9 +25,11 @@ import exh.merged.sql.models.MergedMangaReference import exh.util.executeOnIO import okhttp3.Response import rx.Observable +import tachiyomi.source.model.ChapterInfo +import tachiyomi.source.model.MangaInfo import uy.kohesive.injekt.injectLazy -class MergedSource : SuspendHttpSource() { +class MergedSource : HttpSource() { private val db: DatabaseHelper by injectLazy() private val sourceManager: SourceManager by injectLazy() private val downloadManager: DownloadManager by injectLazy() @@ -37,40 +39,42 @@ class MergedSource : SuspendHttpSource() { override val baseUrl = "" - override suspend fun popularMangaRequestSuspended(page: Int) = throw UnsupportedOperationException() - override suspend fun popularMangaParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun searchMangaRequestSuspended(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() - override suspend fun searchMangaParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun latestUpdatesRequestSuspended(page: Int) = throw UnsupportedOperationException() - override suspend fun latestUpdatesParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun mangaDetailsParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun chapterListParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun pageListParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun imageUrlParseSuspended(response: Response) = throw UnsupportedOperationException() - override suspend fun fetchChapterListSuspended(manga: SManga) = throw UnsupportedOperationException() - override suspend fun fetchImageSuspended(page: Page) = throw UnsupportedOperationException() - override suspend fun fetchImageUrlSuspended(page: Page) = throw UnsupportedOperationException() - override suspend fun fetchPageListSuspended(chapter: SChapter) = throw UnsupportedOperationException() - override suspend fun fetchLatestUpdatesSuspended(page: Int) = throw UnsupportedOperationException() - override suspend fun fetchPopularMangaSuspended(page: Int) = throw UnsupportedOperationException() + override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException() + override fun popularMangaParse(response: Response) = throw UnsupportedOperationException() + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException() + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() + override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException() + override fun chapterListParse(response: Response) = throw UnsupportedOperationException() + override fun pageListParse(response: Response) = throw UnsupportedOperationException() + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() + override fun fetchChapterList(manga: SManga) = throw UnsupportedOperationException() + override suspend fun getChapterList(manga: MangaInfo) = throw UnsupportedOperationException() + override fun fetchImage(page: Page) = throw UnsupportedOperationException() + override fun fetchImageUrl(page: Page) = throw UnsupportedOperationException() + override fun fetchPageList(chapter: SChapter) = throw UnsupportedOperationException() + override suspend fun getPageList(chapter: ChapterInfo) = throw UnsupportedOperationException() + override fun fetchLatestUpdates(page: Int) = throw UnsupportedOperationException() + override fun fetchPopularManga(page: Int) = throw UnsupportedOperationException() - override suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga { + override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo { return withIOContext { - val mergedManga = db.getManga(manga.url, id).executeAsBlocking() ?: throw Exception("merged manga not in db") - val mangaReferences = mergedManga.id?.let { db.getMergedMangaReferences(it).executeOnIO() } ?: throw Exception("merged manga id is null") + val mergedManga = db.getManga(manga.key, id).executeAsBlocking() ?: throw Exception("merged manga not in db") + val mangaReferences = db.getMergedMangaReferences(mergedManga.id ?: throw Exception("merged manga id is null")).executeOnIO() if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, info unavailable, merge is likely corrupted") - if (mangaReferences.size == 1 || run { - val mangaReference = mangaReferences.firstOrNull() - mangaReference == null || (mangaReference.mangaSourceId == MERGED_SOURCE_ID) - } + if (mangaReferences.size == 1 && + run { + val mangaReference = mangaReferences.firstOrNull() + mangaReference == null || mangaReference.mangaSourceId == MERGED_SOURCE_ID + } ) throw IllegalArgumentException("Manga references contain only the merged reference, merge is likely corrupted") - SManga.create().apply { - val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId } - val dbManga = mangaInfoReference?.let { db.getManga(it.mangaUrl, it.mangaSourceId).executeOnIO() } - this.copyFrom(dbManga ?: mergedManga) - url = manga.url - } + val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId } + val dbManga = mangaInfoReference?.let { db.getManga(it.mangaUrl, it.mangaSourceId).executeOnIO()?.toMangaInfo() } + (dbManga ?: mergedManga.toMangaInfo()).copy( + key = manga.key + ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigratingManga.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigratingManga.kt index 7725ae81f..e81f643b1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigratingManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigratingManga.kt @@ -8,7 +8,7 @@ import exh.util.DeferredField import exh.util.executeOnIO import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.channels.ConflatedBroadcastChannel +import kotlinx.coroutines.flow.MutableStateFlow import kotlin.coroutines.CoroutineContext class MigratingManga( @@ -20,7 +20,7 @@ class MigratingManga( val searchResult = DeferredField() // - val progress = ConflatedBroadcastChannel(1 to 0) + val progress = MutableStateFlow(1 to 0) val migrationJob = parentContext + SupervisorJob() + Dispatchers.Default diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt index a4b82c9b9..def8c9528 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt @@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.toast import exh.eh.EHentaiThrottleManager @@ -51,7 +52,6 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit -import kotlinx.coroutines.withContext import timber.log.Timber import uy.kohesive.injekt.injectLazy import java.util.concurrent.atomic.AtomicInteger @@ -187,7 +187,7 @@ class MigrationListController(bundle: Bundle? = null) : } catch (e: Exception) { return@async2 null } - manga.progress.send(validSources.size to processedSources.incrementAndGet()) + manga.progress.value = validSources.size to processedSources.incrementAndGet() localManga to chapters.size } else { null @@ -222,7 +222,7 @@ class MigrationListController(bundle: Bundle? = null) : Timber.e(e) emptyList() } - withContext(Dispatchers.IO) { + withIOContext { syncChaptersWithSource(db, chapters, localManga, source) } localManga @@ -233,7 +233,7 @@ class MigrationListController(bundle: Bundle? = null) : } catch (e: Exception) { null } - manga.progress.send(validSources.size to (index + 1)) + manga.progress.value = validSources.size to (index + 1) if (searchResult != null) return@async searchResult } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt index 456fcba3d..e491ffc1c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt @@ -8,9 +8,8 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.util.lang.launchUI -import kotlinx.coroutines.Dispatchers +import eu.kanade.tachiyomi.util.lang.withIOContext import kotlinx.coroutines.cancel -import kotlinx.coroutines.withContext import uy.kohesive.injekt.injectLazy class MigrationProcessAdapter( @@ -48,7 +47,7 @@ class MigrationProcessAdapter( fun mangasSkipped() = items.count { it.manga.migrationStatus == MigrationStatus.MANGA_NOT_FOUND } suspend fun performMigrations(copy: Boolean) { - withContext(Dispatchers.IO) { + withIOContext { db.inTransaction { currentItems.forEach { migratingManga -> val manga = migratingManga.manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt index 46cc2e379..520af3599 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessHolder.kt @@ -18,14 +18,13 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.setVectorCompat import exh.MERGED_SOURCE_ID import exh.util.executeOnIO -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.withContext import reactivecircus.flowbinding.android.view.clicks import uy.kohesive.injekt.injectLazy import java.text.DecimalFormat @@ -70,7 +69,7 @@ class MigrationProcessHolder( binding.skipManga.isVisible = true binding.migrationMangaCardTo.resetManga() if (manga != null) { - withContext(Dispatchers.Main) { + withUIContext { binding.migrationMangaCardFrom.attachManga(manga, source) binding.migrationMangaCardFrom.root.clicks() .onEach { @@ -85,7 +84,7 @@ class MigrationProcessHolder( /*launchUI { item.manga.progress.asFlow().collect { (max, progress) -> - withContext(Dispatchers.Main) { + withUIContext { migration_manga_card_to.search_progress.let { progressBar -> progressBar.max = max progressBar.progress = progress @@ -100,11 +99,11 @@ class MigrationProcessHolder( val resultSource = searchResult?.source?.let { sourceManager.get(it) } - withContext(Dispatchers.Main) { + withUIContext { if (item.manga.mangaId != this@MigrationProcessHolder.item?.manga?.mangaId || item.manga.migrationStatus == MigrationStatus.RUNNING ) { - return@withContext + return@withUIContext } if (searchResult != null && resultSource != null) { binding.migrationMangaCardTo.attachManga(searchResult, resultSource) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesController.kt index c429af476..616ce5d9f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesController.kt @@ -19,9 +19,8 @@ import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationContr import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.launchUI +import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.openInBrowser -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -100,7 +99,7 @@ class MigrationSourcesController : launchUI { val manga = Injekt.get().getFavoriteMangas().asRxSingle().await(Schedulers.io()) val sourceMangas = manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList() - withContext(Dispatchers.Main) { + withUIContext { PreMigrationController.navigateToMigration( Injekt.get().skipPreMigration().get(), if (parentController is BrowseController) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt index 8d97598d6..fe5a1d5c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexController.kt @@ -143,7 +143,7 @@ open class IndexController : searchView.maxWidth = Int.MAX_VALUE val query = presenter.query - if (!query.isBlank()) { + if (query.isNotBlank()) { searchItem.expandActionView() searchView.setQuery(query, true) searchView.clearFocus() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt index 15c3edd61..7a10d7820 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/index/IndexPresenter.kt @@ -15,12 +15,12 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems import eu.kanade.tachiyomi.util.lang.asFlow import eu.kanade.tachiyomi.util.lang.runAsObservable +import eu.kanade.tachiyomi.util.lang.withUIContext import exh.savedsearches.EXHSavedSearch import exh.savedsearches.JsonSavedSearch import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.singleOrNull import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import rx.Observable @@ -99,7 +99,7 @@ open class IndexPresenter( initializeFetchImageSubscription() presenterScope.launch(Dispatchers.IO) { - withContext(Dispatchers.Main) { + withUIContext { Observable.just(null).subscribeLatestCache({ view, results -> view.setLatestManga(results) }) @@ -118,7 +118,7 @@ open class IndexPresenter( } fetchImage(results, true) - withContext(Dispatchers.Main) { + withUIContext { Observable.just(results.map { IndexCardItem(it) }).subscribeLatestCache({ view, results -> view.setLatestManga(results) }) @@ -127,7 +127,7 @@ open class IndexPresenter( } presenterScope.launch(Dispatchers.IO) { - withContext(Dispatchers.Main) { + withUIContext { Observable.just(null).subscribeLatestCache({ view, results -> view.setBrowseManga(results) }) @@ -146,7 +146,7 @@ open class IndexPresenter( } fetchImage(results, false) - withContext(Dispatchers.Main) { + withUIContext { Observable.just(results.map { IndexCardItem(it) }).subscribeLatestCache({ view, results -> view.setBrowseManga(results) }) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index b8985a284..171ced2bc 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -36,7 +36,6 @@ import reactivecircus.flowbinding.recyclerview.scrollStateChanges import reactivecircus.flowbinding.swiperefreshlayout.refreshes import rx.android.schedulers.AndroidSchedulers import rx.subscriptions.CompositeSubscription -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index 78ca13cbd..7fc3a4e1d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.takeBytes +import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.updateCoverLastModified @@ -44,11 +45,9 @@ import exh.metadata.metadata.base.getFlatMetadataForManga import exh.source.getMainSource import exh.util.defaultReaderType import exh.util.shouldDeleteChapters -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -310,7 +309,7 @@ class ReaderPresenter( // SY --> suspend fun getChapters(context: Context): List { - return withContext(Dispatchers.IO) { + return withIOContext { val currentChapter = getCurrentChapter() val decimalFormat = DecimalFormat( "#.###", diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index 203331666..9c3f598ae 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -18,12 +18,6 @@ import exh.metadata.metadata.base.insertFlatMetadata import exh.util.await import exh.util.floor import exh.util.nullIfZero -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.intOrNull -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive import okhttp3.Response import rx.Completable import rx.Single diff --git a/app/src/main/java/exh/md/handlers/SearchHandler.kt b/app/src/main/java/exh/md/handlers/SearchHandler.kt index 832a55c00..e2a5a16d9 100644 --- a/app/src/main/java/exh/md/handlers/SearchHandler.kt +++ b/app/src/main/java/exh/md/handlers/SearchHandler.kt @@ -10,7 +10,7 @@ import exh.md.utils.MdUtil import exh.md.utils.setMDUrlWithoutDomain import okhttp3.CacheControl import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -75,7 +75,7 @@ class SearchHandler(val client: OkHttpClient, private val headers: Headers, val val demographics = mutableListOf() // Do traditional search - val url = "${MdUtil.baseUrl}/?page=search".toHttpUrlOrNull()!!.newBuilder() + val url = "${MdUtil.baseUrl}/?page=search".toHttpUrl().newBuilder() .addQueryParameter("p", page.toString()) .addQueryParameter("title", query.replace(WHITESPACE_REGEX, " ")) diff --git a/app/src/main/java/exh/md/handlers/serializers/ApiChapterSerializer.kt b/app/src/main/java/exh/md/handlers/serializers/ApiChapterSerializer.kt index 76058b757..cf1579d54 100644 --- a/app/src/main/java/exh/md/handlers/serializers/ApiChapterSerializer.kt +++ b/app/src/main/java/exh/md/handlers/serializers/ApiChapterSerializer.kt @@ -21,4 +21,4 @@ data class ChapterPageSerializer( val pages: List, val server: String, val mangaId: Int -) \ No newline at end of file +) diff --git a/app/src/main/java/exh/util/SourceTagsUtil.kt b/app/src/main/java/exh/util/SourceTagsUtil.kt index 327bbc3f6..e93757462 100644 --- a/app/src/main/java/exh/util/SourceTagsUtil.kt +++ b/app/src/main/java/exh/util/SourceTagsUtil.kt @@ -49,13 +49,11 @@ object SourceTagsUtil { } fun parseTag(tag: String) = RaisedTag( - ( - if (tag.startsWith("-")) { - tag.substringAfter("-") - } else { - tag - } - ).substringBefore(':', missingDelimiterValue = "").trimOrNull(), + if (tag.startsWith("-")) { + tag.substringAfter("-") + } else { + tag + }.substringBefore(':', missingDelimiterValue = "").trimOrNull(), tag.substringAfter(':', missingDelimiterValue = tag).trim(), if (tag.startsWith("-")) TAG_TYPE_EXCLUDE else TAG_TYPE_DEFAULT ) @@ -94,6 +92,6 @@ object SourceTagsUtil { private const val TAG_TYPE_DEFAULT = 1 } -fun Manga.getRaisedTags(genres: List? = null): List? = (genres ?: this.getGenres())?.map { +fun Manga.getRaisedTags(genres: List? = getGenres()): List? = genres?.map { SourceTagsUtil.parseTag(it) }