Deal with SY for the coroutine function changes

This commit is contained in:
Jobobby04 2021-01-10 19:36:24 -05:00
parent 0edff11353
commit a0ac2daad1
27 changed files with 146 additions and 159 deletions

View File

@ -46,6 +46,7 @@ import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.getMainSource import exh.source.getMainSource
import exh.util.executeOnIO
import exh.util.nullIfBlank import exh.util.nullIfBlank
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -456,11 +457,11 @@ class LibraryUpdateService(
// SY --> // SY -->
if (source is MangaDex && trackManager.mdList.isLogged) { if (source is MangaDex && trackManager.mdList.isLogged) {
runAsObservable({ runAsObservable({
val tracks = db.getTracks(manga).await() val tracks = db.getTracks(manga).executeOnIO()
if (tracks.isEmpty() || tracks.none { it.sync_id == TrackManager.MDLIST }) { if (tracks.isEmpty() || tracks.none { it.sync_id == TrackManager.MDLIST }) {
var track = trackManager.mdList.createInitialTracker(manga) var track = trackManager.mdList.createInitialTracker(manga)
track = trackManager.mdList.refresh(track) track = trackManager.mdList.refresh(track)
db.insertTrack(track).await() db.insertTrack(track).executeOnIO()
} }
}) })
.onErrorResumeNext { Observable.just(Unit) } .onErrorResumeNext { Observable.just(Unit) }

View File

@ -16,6 +16,7 @@ import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.executeOnIO
import exh.util.floor import exh.util.floor
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -49,7 +50,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
override suspend fun update(track: Track): Track { override suspend fun update(track: Track): Track {
val mdex = mdex ?: throw Exception("Mangadex not enabled") val mdex = mdex ?: throw Exception("Mangadex not enabled")
val mangaMetadata = db.getFlatMetadataForManga(track.manga_id).await() val mangaMetadata = db.getFlatMetadataForManga(track.manga_id).executeAsBlocking()
?.raise<MangaDexSearchMetadata>() ?.raise<MangaDexSearchMetadata>()
?: throw Exception("Invalid manga metadata") ?: throw Exception("Invalid manga metadata")
val followStatus = FollowStatus.fromInt(track.status) ?: throw Exception("Follow status was not a valid value") val followStatus = FollowStatus.fromInt(track.status) ?: throw Exception("Follow status was not a valid value")
@ -88,8 +89,8 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
val mdex = mdex ?: throw Exception("Mangadex not enabled") val mdex = mdex ?: throw Exception("Mangadex not enabled")
val remoteTrack = mdex.fetchTrackingInfo(track.tracking_url) val remoteTrack = mdex.fetchTrackingInfo(track.tracking_url)
track.copyPersonalFrom(remoteTrack) track.copyPersonalFrom(remoteTrack)
if (track.total_chapters == 0 && db.getManga(track.manga_id).await()?.status == SManga.COMPLETED) { if (track.total_chapters == 0 && db.getManga(track.manga_id).executeOnIO()?.status == SManga.COMPLETED) {
track.total_chapters = db.getChapters(track.manga_id).await().maxOfOrNull { it.chapter_number }?.floor() ?: 0 track.total_chapters = db.getChapters(track.manga_id).executeOnIO().maxOfOrNull { it.chapter_number }?.floor() ?: 0
} }
return track return track
} }

View File

@ -29,7 +29,7 @@ import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.asObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.GalleryAddEvent import exh.GalleryAddEvent
import exh.GalleryAdder import exh.GalleryAdder
import exh.md.MangaDexFabHeaderAdapter import exh.md.MangaDexFabHeaderAdapter
@ -48,7 +48,6 @@ import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import exh.widget.preference.MangadexLoginDialog import exh.widget.preference.MangadexLoginDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.FormBody import okhttp3.FormBody
@ -207,7 +206,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
) )
).await() ).await()
response.body!!.string().let { withContext(Dispatchers.IO) { response.body!!.string() }.let {
if (it.isEmpty()) { if (it.isEmpty()) {
true true
} else { } else {
@ -230,9 +229,9 @@ class MangaDex(delegate: HttpSource, val context: Context) :
} }
val result = client.newCall( val result = client.newCall(
POST("${MdUtil.baseUrl}/ajax/actions.ajax.php?function=logout", headers).newBuilder().addHeader(REMEMBER_ME, token).build() POST("${MdUtil.baseUrl}/ajax/actions.ajax.php?function=logout", headers).newBuilder().addHeader(REMEMBER_ME, token).build()
).execute() ).await()
val resultStr = result.body!!.string() val resultStr = withContext(Dispatchers.IO) { result.body?.string() }
if (resultStr.contains("success", true)) { if (resultStr?.contains("success", true) == true) {
network.cookieManager.remove(httpUrl) network.cookieManager.remove(httpUrl)
trackManager.mdList.logout() trackManager.mdList.logout()
return@withContext true return@withContext true
@ -282,10 +281,9 @@ class MangaDex(delegate: HttpSource, val context: Context) :
private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> = private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when { when {
query.toIntOrNull() != null -> { query.toIntOrNull() != null -> {
flow { runAsObservable({
emit(GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this@MangaDex)) GalleryAdder().addGallery(context, MdUtil.baseUrl + MdUtil.mapMdIdToMangaUrl(query.toInt()), false, this@MangaDex)
} })
.asObservable()
.map { res -> .map { res ->
MangasPage( MangasPage(
( (

View File

@ -17,16 +17,12 @@ import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.SuspendHttpSource import eu.kanade.tachiyomi.source.online.SuspendHttpSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.awaitSingleOrNull import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import kotlinx.coroutines.Dispatchers import exh.util.executeOnIO
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -59,9 +55,9 @@ class MergedSource : SuspendHttpSource() {
override suspend fun fetchPopularMangaSuspended(page: Int) = throw UnsupportedOperationException() override suspend fun fetchPopularMangaSuspended(page: Int) = throw UnsupportedOperationException()
override suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga { override suspend fun fetchMangaDetailsSuspended(manga: SManga): SManga {
return withContext(Dispatchers.IO) { return withIOContext {
val mergedManga = db.getManga(manga.url, id).await() ?: throw Exception("merged manga not in db") val mergedManga = db.getManga(manga.url, id).executeAsBlocking() ?: throw Exception("merged manga not in db")
val mangaReferences = mergedManga.id?.let { withContext(Dispatchers.IO) { db.getMergedMangaReferences(it).await() } } ?: throw Exception("merged manga id is null") val mangaReferences = mergedManga.id?.let { db.getMergedMangaReferences(it).executeOnIO() } ?: throw Exception("merged manga id is null")
if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, info unavailable, merge is likely corrupted") if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, info unavailable, merge is likely corrupted")
if (mangaReferences.size == 1 || run { if (mangaReferences.size == 1 || run {
val mangaReference = mangaReferences.firstOrNull() val mangaReference = mangaReferences.firstOrNull()
@ -71,7 +67,7 @@ class MergedSource : SuspendHttpSource() {
SManga.create().apply { SManga.create().apply {
val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId } val mangaInfoReference = mangaReferences.firstOrNull { it.isInfoManga } ?: mangaReferences.firstOrNull { it.mangaId != it.mergeId }
val dbManga = mangaInfoReference?.let { withContext(Dispatchers.IO) { db.getManga(it.mangaUrl, it.mangaSourceId).await() } } val dbManga = mangaInfoReference?.let { db.getManga(it.mangaUrl, it.mangaSourceId).executeOnIO() }
this.copyFrom(dbManga ?: mergedManga) this.copyFrom(dbManga ?: mergedManga)
url = manga.url url = manga.url
} }
@ -82,7 +78,7 @@ class MergedSource : SuspendHttpSource() {
// TODO more chapter dedupe // TODO more chapter dedupe
return db.getChaptersByMergedMangaId(manga.id!!).asRxObservable() return db.getChaptersByMergedMangaId(manga.id!!).asRxObservable()
.map { chapterList -> .map { chapterList ->
val mangaReferences = runBlocking(Dispatchers.IO) { db.getMergedMangaReferences(manga.id!!).await().orEmpty() } val mangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking()
if (editScanlators) { if (editScanlators) {
val sources = mangaReferences.map { sourceManager.getOrStub(it.mangaSourceId) to it.mangaId } val sources = mangaReferences.map { sourceManager.getOrStub(it.mangaSourceId) to it.mangaId }
chapterList.onEach { chapter -> chapterList.onEach { chapter ->
@ -124,21 +120,21 @@ class MergedSource : SuspendHttpSource() {
} }
suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> { suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return withContext(Dispatchers.IO) { return withIOContext {
fetchChaptersAndSync(manga, downloadChapters) fetchChaptersAndSync(manga, downloadChapters)
getChaptersFromDB(manga, editScanlators, dedupe).awaitSingleOrNull() ?: emptyList() getChaptersFromDB(manga, editScanlators, dedupe).awaitSingle()
} }
} }
suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): Pair<List<Chapter>, List<Chapter>> { suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): Pair<List<Chapter>, List<Chapter>> {
val mangaReferences = db.getMergedMangaReferences(manga.id!!).await() val mangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking()
if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted") if (mangaReferences.isEmpty()) throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted")
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(db, preferences) val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(db, preferences)
return mangaReferences.filter { it.mangaSourceId != MERGED_SOURCE_ID }.map { return mangaReferences.filter { it.mangaSourceId != MERGED_SOURCE_ID }.map {
it.load(db, sourceManager) it.load(db, sourceManager)
}.mapNotNull { loadedManga -> }.mapNotNull { loadedManga ->
withContext(Dispatchers.IO) { withIOContext {
if (loadedManga.manga != null && loadedManga.reference.getChapterUpdates) { if (loadedManga.manga != null && loadedManga.reference.getChapterUpdates) {
loadedManga.source.getChapterList(loadedManga.manga.toMangaInfo()) loadedManga.source.getChapterList(loadedManga.manga.toMangaInfo())
.map { it.toSChapter() } .map { it.toSChapter() }
@ -166,7 +162,7 @@ class MergedSource : SuspendHttpSource() {
} }
suspend fun MergedMangaReference.load(db: DatabaseHelper, sourceManager: SourceManager): LoadedMangaSource { suspend fun MergedMangaReference.load(db: DatabaseHelper, sourceManager: SourceManager): LoadedMangaSource {
var manga = db.getManga(mangaUrl, mangaSourceId).await() var manga = db.getManga(mangaUrl, mangaSourceId).executeOnIO()
val source = sourceManager.getOrStub(manga?.source ?: mangaSourceId) val source = sourceManager.getOrStub(manga?.source ?: mangaSourceId)
if (manga == null) { if (manga == null) {
manga = Manga.create(mangaSourceId).apply { manga = Manga.create(mangaSourceId).apply {
@ -174,9 +170,9 @@ class MergedSource : SuspendHttpSource() {
} }
manga.copyFrom(source.getMangaDetails(manga.toMangaInfo()).toSManga()) manga.copyFrom(source.getMangaDetails(manga.toMangaInfo()).toSManga())
try { try {
manga.id = db.insertManga(manga).await().insertedId() manga.id = db.insertManga(manga).executeOnIO().insertedId()
mangaId = manga.id mangaId = manga.id
db.insertNewMergedMangaId(this).await() db.insertNewMergedMangaId(this).executeOnIO()
} catch (e: Exception) { } catch (e: Exception) {
XLog.tag("MergedSource").enableStackTrace(e.stackTrace.contentToString(), 5) XLog.tag("MergedSource").enableStackTrace(e.stackTrace.contentToString(), 5)
} }

View File

@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.lang.await
import exh.util.DeferredField import exh.util.DeferredField
import exh.util.executeOnIO
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.channels.ConflatedBroadcastChannel
@ -29,7 +29,7 @@ class MigratingManga(
@Volatile @Volatile
private var manga: Manga? = null private var manga: Manga? = null
suspend fun manga(): Manga? { suspend fun manga(): Manga? {
if (manga == null) manga = db.getManga(mangaId).await() if (manga == null) manga = db.getManga(mangaId).executeOnIO()
return manga return manga
} }

View File

@ -250,7 +250,7 @@ class MigrationListController(bundle: Bundle? = null) :
val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo()) val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo())
result.copyFrom(newManga.toSManga()) result.copyFrom(newManga.toSManga())
db.insertManga(result).await() db.insertManga(result).executeOnIO()
} catch (e: CancellationException) { } catch (e: CancellationException) {
// Ignore cancellations // Ignore cancellations
throw e throw e
@ -365,7 +365,7 @@ class MigrationListController(bundle: Bundle? = null) :
val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo()) val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo())
result.copyFrom(newManga.toSManga()) result.copyFrom(newManga.toSManga())
db.insertManga(result).await() db.insertManga(result).executeOnIO()
} catch (e: CancellationException) { } catch (e: CancellationException) {
// Ignore cancellations // Ignore cancellations
throw e throw e

View File

@ -17,11 +17,11 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.util.executeOnIO
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -157,7 +157,7 @@ class MigrationProcessHolder(
gradient.isVisible = true gradient.isVisible = true
mangaSourceLabel.text = if (source.id == MERGED_SOURCE_ID) { mangaSourceLabel.text = if (source.id == MERGED_SOURCE_ID) {
db.getMergedMangaReferences(manga.id!!).await().map { db.getMergedMangaReferences(manga.id!!).executeOnIO().map {
sourceManager.getOrStub(it.mangaSourceId).toString() sourceManager.getOrStub(it.mangaSourceId).toString()
}.distinct().joinToString() }.distinct().joinToString()
} else { } else {

View File

@ -11,7 +11,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.ui.category.CategoryAdapter import eu.kanade.tachiyomi.ui.category.CategoryAdapter
import eu.kanade.tachiyomi.util.lang.await
import exh.isMetadataSource import exh.isMetadataSource
import exh.metadata.sql.models.SearchTag import exh.metadata.sql.models.SearchTag
import exh.metadata.sql.models.SearchTitle import exh.metadata.sql.models.SearchTitle
@ -115,7 +114,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
// Prepare filter object // Prepare filter object
val parsedQuery = searchEngine.parseQuery(savedSearchText) val parsedQuery = searchEngine.parseQuery(savedSearchText)
val mangaWithMetaIdsQuery = db.getIdsOfFavoriteMangaWithMetadata().await() val mangaWithMetaIdsQuery = db.getIdsOfFavoriteMangaWithMetadata().executeAsBlocking()
val mangaWithMetaIds = LongArray(mangaWithMetaIdsQuery.count) val mangaWithMetaIds = LongArray(mangaWithMetaIdsQuery.count)
if (mangaWithMetaIds.isNotEmpty()) { if (mangaWithMetaIds.isNotEmpty()) {
val mangaIdCol = mangaWithMetaIdsQuery.getColumnIndex(MangaTable.COL_ID) val mangaIdCol = mangaWithMetaIdsQuery.getColumnIndex(MangaTable.COL_ID)
@ -138,8 +137,8 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
// No meta? Filter using title // No meta? Filter using title
filterManga(parsedQuery, item.manga) filterManga(parsedQuery, item.manga)
} else { } else {
val tags = db.getSearchTagsForManga(mangaId).await() val tags = db.getSearchTagsForManga(mangaId).executeAsBlocking()
val titles = db.getSearchTitlesForManga(mangaId).await() val titles = db.getSearchTitlesForManga(mangaId).executeAsBlocking()
filterManga(parsedQuery, item.manga, false, tags, titles) filterManga(parsedQuery, item.manga, false, tags, titles)
} }
} else { } else {
@ -167,7 +166,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
private suspend fun filterManga(queries: List<QueryComponent>, manga: LibraryManga, checkGenre: Boolean = true, searchTags: List<SearchTag>? = null, searchTitles: List<SearchTitle>? = null): Boolean { private suspend fun filterManga(queries: List<QueryComponent>, manga: LibraryManga, checkGenre: Boolean = true, searchTags: List<SearchTag>? = null, searchTitles: List<SearchTitle>? = null): Boolean {
val mappedQueries = queries.groupBy { it.excluded } val mappedQueries = queries.groupBy { it.excluded }
val tracks = if (hasLoggedServices) db.getTracks(manga).await().toList() else null val tracks = if (hasLoggedServices) db.getTracks(manga).executeAsBlocking().toList() else null
val source = sourceManager.get(manga.source) val source = sourceManager.get(manga.source)
val genre = if (checkGenre) manga.getGenres().orEmpty() else emptyList() val genre = if (checkGenre) manga.getGenres().orEmpty() else emptyList()
val hasNormalQuery = mappedQueries[false]?.all { queryComponent -> val hasNormalQuery = mappedQueries[false]?.all { queryComponent ->

View File

@ -19,8 +19,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.awaitSingleOrNull
import eu.kanade.tachiyomi.util.lang.combineLatest import eu.kanade.tachiyomi.util.lang.combineLatest
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
@ -32,6 +31,7 @@ import exh.MERGED_SOURCE_ID
import exh.favorites.FavoritesSyncHelper import exh.favorites.FavoritesSyncHelper
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.util.executeOnIO
import exh.util.isLewd import exh.util.isLewd
import exh.util.nullIfBlank import exh.util.nullIfBlank
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -499,10 +499,10 @@ class LibraryPresenter(
mangas.forEach { manga -> mangas.forEach { manga ->
launchIO { launchIO {
/* SY --> */ val chapters = if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) { /* SY --> */ val chapters = if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) {
val chapter = db.getChapters(manga).await().minByOrNull { it.source_order } val chapter = db.getChapters(manga).executeOnIO().minByOrNull { it.source_order }
if (chapter != null && !chapter.read) listOf(chapter) else emptyList() if (chapter != null && !chapter.read) listOf(chapter) else emptyList()
} else if (manga.source == MERGED_SOURCE_ID) { } else if (manga.source == MERGED_SOURCE_ID) {
(sourceManager.getOrStub(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingleOrNull()?.filter { !it.read }.orEmpty() (sourceManager.getOrStub(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingle()?.filter { !it.read }.orEmpty()
} else /* SY <-- */ db.getChapters(manga).executeAsBlocking() } else /* SY <-- */ db.getChapters(manga).executeAsBlocking()
.filter { !it.read } .filter { !it.read }
@ -557,7 +557,7 @@ class LibraryPresenter(
fun markReadStatus(mangas: List<Manga>, read: Boolean) { fun markReadStatus(mangas: List<Manga>, read: Boolean) {
mangas.forEach { manga -> mangas.forEach { manga ->
launchIO { launchIO {
val chapters = if (manga.source == MERGED_SOURCE_ID) (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingleOrNull().orEmpty() else db.getChapters(manga).executeAsBlocking() val chapters = if (manga.source == MERGED_SOURCE_ID) (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingle().orEmpty() else db.getChapters(manga).executeAsBlocking()
chapters.forEach { chapters.forEach {
it.read = read it.read = read
if (!read) { if (!read) {
@ -612,7 +612,7 @@ class LibraryPresenter(
val source = sourceManager.get(manga.source) as? HttpSource val source = sourceManager.get(manga.source) as? HttpSource
if (source != null) { if (source != null) {
if (source is MergedSource) { if (source is MergedSource) {
val mergedMangas = db.getMergedMangas(manga.id!!).await() val mergedMangas = db.getMergedMangas(manga.id!!).executeAsBlocking()
val sources = mergedMangas.distinctBy { it.source }.map { sourceManager.getOrStub(it.source) } val sources = mergedMangas.distinctBy { it.source }.map { sourceManager.getOrStub(it.source) }
mergedMangas.forEach merge@{ mergedManga -> mergedMangas.forEach merge@{ mergedManga ->
val mergedSource = sources.firstOrNull { mergedManga.source == it.id } ?: return@merge val mergedSource = sources.firstOrNull { mergedManga.source == it.id } ?: return@merge
@ -644,7 +644,7 @@ class LibraryPresenter(
// SY --> // SY -->
/** Returns first unread chapter of a manga */ /** Returns first unread chapter of a manga */
fun getFirstUnread(manga: Manga): Chapter? { fun getFirstUnread(manga: Manga): Chapter? {
val chapters = (if (manga.source == MERGED_SOURCE_ID) (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource).let { runBlocking { it?.getChaptersFromDB(manga)?.awaitSingleOrNull().orEmpty() } } else db.getChapters(manga).executeAsBlocking()) val chapters = (if (manga.source == MERGED_SOURCE_ID) (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource).let { runBlocking { it?.getChaptersFromDB(manga)?.awaitSingle().orEmpty() } } else db.getChapters(manga).executeAsBlocking())
return if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) { return if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) {
val chapter = chapters.sortedBy { it.source_order }.getOrNull(0) val chapter = chapters.sortedBy { it.source_order }.getOrNull(0)
if (chapter?.read == false) chapter else null if (chapter?.read == false) chapter else null

View File

@ -143,7 +143,7 @@ class MangaPresenter(
// SY --> // SY -->
if (source is MergedSource) { if (source is MergedSource) {
launchIO { mergedManga = db.getMergedMangas(manga.id!!).await() } launchIO { mergedManga = db.getMergedMangas(manga.id!!).executeAsBlocking() }
} }
// SY <-- // SY <--
@ -379,9 +379,9 @@ class MangaPresenter(
} }
suspend fun smartSearchMerge(manga: Manga, originalMangaId: Long): Manga { suspend fun smartSearchMerge(manga: Manga, originalMangaId: Long): Manga {
val originalManga = db.getManga(originalMangaId).await() ?: throw IllegalArgumentException("Unknown manga ID: $originalMangaId") val originalManga = db.getManga(originalMangaId).executeAsBlocking() ?: throw IllegalArgumentException("Unknown manga ID: $originalMangaId")
if (originalManga.source == MERGED_SOURCE_ID) { if (originalManga.source == MERGED_SOURCE_ID) {
val children = db.getMergedMangaReferences(originalMangaId).await() val children = db.getMergedMangaReferences(originalMangaId).executeAsBlocking()
if (children.any { it.mangaSourceId == manga.source && it.mangaUrl == manga.url }) { if (children.any { it.mangaSourceId == manga.source && it.mangaUrl == manga.url }) {
throw IllegalArgumentException("This manga is already merged with the current manga!") throw IllegalArgumentException("This manga is already merged with the current manga!")
} }
@ -418,7 +418,7 @@ class MangaPresenter(
) )
} }
db.insertMergedMangas(mangaReferences).await() db.insertMergedMangas(mangaReferences).executeAsBlocking()
return originalManga return originalManga
} else { } else {
@ -431,30 +431,30 @@ class MangaPresenter(
sorting = Manga.SORTING_NUMBER sorting = Manga.SORTING_NUMBER
date_added = System.currentTimeMillis() date_added = System.currentTimeMillis()
} }
var existingManga = db.getManga(mergedManga.url, mergedManga.source).await() var existingManga = db.getManga(mergedManga.url, mergedManga.source).executeAsBlocking()
while (existingManga != null) { while (existingManga != null) {
if (existingManga.favorite) { if (existingManga.favorite) {
throw IllegalArgumentException("This merged manga is a duplicate!") throw IllegalArgumentException("This merged manga is a duplicate!")
} else if (!existingManga.favorite) { } else if (!existingManga.favorite) {
withContext(NonCancellable) { withContext(NonCancellable) {
db.deleteManga(existingManga!!).await() db.deleteManga(existingManga!!).executeAsBlocking()
db.deleteMangaForMergedManga(existingManga!!.id!!).await() db.deleteMangaForMergedManga(existingManga!!.id!!).executeAsBlocking()
} }
} }
existingManga = db.getManga(mergedManga.url, mergedManga.source).await() existingManga = db.getManga(mergedManga.url, mergedManga.source).executeAsBlocking()
} }
// Reload chapters immediately // Reload chapters immediately
mergedManga.initialized = false mergedManga.initialized = false
val newId = db.insertManga(mergedManga).await().insertedId() val newId = db.insertManga(mergedManga).executeAsBlocking().insertedId()
if (newId != null) mergedManga.id = newId if (newId != null) mergedManga.id = newId
db.getCategoriesForManga(originalManga) db.getCategoriesForManga(originalManga)
.await() .executeAsBlocking()
.map { MangaCategory.create(mergedManga, it) } .map { MangaCategory.create(mergedManga, it) }
.let { .let {
db.insertMangasCategories(it).await() db.insertMangasCategories(it).executeAsBlocking()
} }
val originalMangaReference = MergedMangaReference( val originalMangaReference = MergedMangaReference(
@ -499,7 +499,7 @@ class MangaPresenter(
mangaSourceId = MERGED_SOURCE_ID mangaSourceId = MERGED_SOURCE_ID
) )
db.insertMergedMangas(listOf(originalMangaReference, newMangaReference, mergedMangaReference)).await() db.insertMergedMangas(listOf(originalMangaReference, newMangaReference, mergedMangaReference)).executeAsBlocking()
return mergedManga return mergedManga
} }
@ -510,9 +510,9 @@ class MangaPresenter(
fun updateMergeSettings(mergeReference: MergedMangaReference?, mergedMangaReferences: List<MergedMangaReference>) { fun updateMergeSettings(mergeReference: MergedMangaReference?, mergedMangaReferences: List<MergedMangaReference>) {
launchIO { launchIO {
mergeReference?.let { mergeReference?.let {
db.updateMergeMangaSettings(it).await() db.updateMergeMangaSettings(it).executeAsBlocking()
} }
if (mergedMangaReferences.isNotEmpty()) db.updateMergedMangaSettings(mergedMangaReferences).await() if (mergedMangaReferences.isNotEmpty()) db.updateMergedMangaSettings(mergedMangaReferences).executeAsBlocking()
} }
} }

View File

@ -26,7 +26,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.awaitSingleOrNull import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.byteSize
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.takeBytes import eu.kanade.tachiyomi.util.lang.takeBytes
@ -119,7 +119,7 @@ class ReaderPresenter(
val meta = meta val meta = meta
val filteredScanlators = meta?.filteredScanlators?.let { MdUtil.getScanlators(it) } val filteredScanlators = meta?.filteredScanlators?.let { MdUtil.getScanlators(it) }
// SY <-- // SY <--
val dbChapters = /* SY --> */if (manga.source == MERGED_SOURCE_ID) runBlocking { (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingleOrNull().orEmpty() } else /* SY <-- */ db.getChapters(manga).executeAsBlocking() val dbChapters = /* SY --> */if (manga.source == MERGED_SOURCE_ID) runBlocking { (sourceManager.get(MERGED_SOURCE_ID) as? MergedSource)?.getChaptersFromDB(manga)?.awaitSingle().orEmpty() } else /* SY <-- */ db.getChapters(manga).executeAsBlocking()
val selectedChapter = dbChapters.find { it.id == chapterId } val selectedChapter = dbChapters.find { it.id == chapterId }
?: error("Requested chapter of id $chapterId not found in chapter list") ?: error("Requested chapter of id $chapterId not found in chapter list")

View File

@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.databinding.EhDialogCategoriesBinding import eu.kanade.tachiyomi.databinding.EhDialogCategoriesBinding
import eu.kanade.tachiyomi.databinding.EhDialogLanguagesBinding import eu.kanade.tachiyomi.databinding.EhDialogLanguagesBinding
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.entriesRes import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference import eu.kanade.tachiyomi.util.preference.intListPreference
@ -641,10 +640,10 @@ class SettingsEhController : SettingsController() {
context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates) context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates)
} else context.getString(R.string.gallery_updater_not_ran_yet) } else context.getString(R.string.gallery_updater_not_ran_yet)
val allMeta = db.getFavoriteMangaWithMetadata().await().filter { val allMeta = db.getFavoriteMangaWithMetadata().executeAsBlocking().filter {
it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID
}.mapNotNull { }.mapNotNull {
db.getFlatMetadataForManga(it.id!!).await() db.getFlatMetadataForManga(it.id!!).executeAsBlocking()
?.raise<EHentaiSearchMetadata>() ?.raise<EHentaiSearchMetadata>()
}.toList() }.toList()

View File

@ -21,6 +21,7 @@ import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.savedsearches.JsonSavedSearch import exh.savedsearches.JsonSavedSearch
import exh.util.cancellable import exh.util.cancellable
import exh.util.executeOnIO
import exh.util.jobScheduler import exh.util.jobScheduler
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
@ -51,7 +52,7 @@ object DebugFunctions {
fun resetAgedFlagInEXHManga() { fun resetAgedFlagInEXHManga() {
runBlocking { runBlocking {
val metadataManga = db.getFavoriteMangaWithMetadata().await() val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga -> val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) { if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
@ -60,7 +61,7 @@ object DebugFunctions {
}.toList() }.toList()
allManga.forEach { manga -> allManga.forEach { manga ->
val meta = db.getFlatMetadataForManga(manga.id!!).await()?.raise<EHentaiSearchMetadata>() ?: return@forEach val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
// remove age flag // remove age flag
meta.aged = false meta.aged = false
db.insertFlatMetadata(meta.flatten()).await() db.insertFlatMetadata(meta.flatten()).await()
@ -74,7 +75,7 @@ object DebugFunctions {
fun resetEHGalleriesForUpdater() { fun resetEHGalleriesForUpdater() {
throttleManager.resetThrottle() throttleManager.resetThrottle()
runBlocking { runBlocking {
val metadataManga = db.getFavoriteMangaWithMetadata().await() val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga -> val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) { if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
@ -104,7 +105,7 @@ object DebugFunctions {
fun getEHMangaListWithAgedFlagInfo(): String { fun getEHMangaListWithAgedFlagInfo(): String {
val galleries = mutableListOf(String()) val galleries = mutableListOf(String())
runBlocking { runBlocking {
val metadataManga = db.getFavoriteMangaWithMetadata().await() val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga -> val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) { if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
@ -113,7 +114,7 @@ object DebugFunctions {
}.toList() }.toList()
allManga.forEach { manga -> allManga.forEach { manga ->
val meta = db.getFlatMetadataForManga(manga.id!!).await()?.raise<EHentaiSearchMetadata>() ?: return@forEach val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
galleries += "Aged: ${meta.aged}\t Title: ${manga.title}" galleries += "Aged: ${meta.aged}\t Title: ${manga.title}"
} }
} }
@ -123,7 +124,7 @@ object DebugFunctions {
fun countAgedFlagInEXHManga(): Int { fun countAgedFlagInEXHManga(): Int {
var agedAmount = 0 var agedAmount = 0
runBlocking { runBlocking {
val metadataManga = db.getFavoriteMangaWithMetadata().await() val metadataManga = db.getFavoriteMangaWithMetadata().executeAsBlocking()
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga -> val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) { if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
@ -132,7 +133,7 @@ object DebugFunctions {
}.toList() }.toList()
allManga.forEach { manga -> allManga.forEach { manga ->
val meta = db.getFlatMetadataForManga(manga.id!!).await()?.raise<EHentaiSearchMetadata>() ?: return@forEach val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
if (meta.aged) { if (meta.aged) {
// remove age flag // remove age flag
agedAmount++ agedAmount++

View File

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.util.lang.await import exh.util.executeOnIO
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -33,13 +33,13 @@ class EHentaiUpdateHelper(context: Context) {
val chainsFlow = flowOf(chapters) val chainsFlow = flowOf(chapters)
.map { chapterList -> .map { chapterList ->
chapterList.flatMap { chapter -> chapterList.flatMap { chapter ->
db.getChapters(chapter.url).await().mapNotNull { it.manga_id } db.getChapters(chapter.url).executeOnIO().mapNotNull { it.manga_id }
}.distinct() }.distinct()
} }
.map { mangaIds -> .map { mangaIds ->
mangaIds mangaIds
.mapNotNull { mangaId -> .mapNotNull { mangaId ->
(db.getManga(mangaId).await() ?: return@mapNotNull null) to db.getChapters(mangaId).await() (db.getManga(mangaId).executeOnIO() ?: return@mapNotNull null) to db.getChapters(mangaId).executeOnIO()
} }
.map { .map {
ChapterChain(it.first, it.second) ChapterChain(it.first, it.second)

View File

@ -28,6 +28,7 @@ import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.cancellable import exh.util.cancellable
import exh.util.executeOnIO
import exh.util.jobScheduler import exh.util.jobScheduler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -136,7 +137,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
logger.d("Finding manga with metadata...") logger.d("Finding manga with metadata...")
val metadataManga = db.getFavoriteMangaWithMetadata().await() val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
logger.d("Filtering manga and raising metadata...") logger.d("Filtering manga and raising metadata...")
val curTime = System.currentTimeMillis() val curTime = System.currentTimeMillis()
@ -145,7 +146,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
return@mapNotNull null return@mapNotNull null
} }
val meta = db.getFlatMetadataForManga(manga.id!!).await() val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()
?: return@mapNotNull null ?: return@mapNotNull null
val raisedMeta = meta.raise<EHentaiSearchMetadata>() val raisedMeta = meta.raise<EHentaiSearchMetadata>()
@ -155,7 +156,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
return@mapNotNull null return@mapNotNull null
} }
val chapter = db.getChapters(manga.id!!).await().minByOrNull { val chapter = db.getChapters(manga.id!!).executeOnIO().minByOrNull {
it.date_upload it.date_upload
} }
@ -265,16 +266,16 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
try { try {
val updatedManga = source.getMangaDetails(manga.toMangaInfo()) val updatedManga = source.getMangaDetails(manga.toMangaInfo())
manga.copyFrom(updatedManga.toSManga()) manga.copyFrom(updatedManga.toSManga())
db.insertManga(manga).await() db.insertManga(manga).executeOnIO()
val newChapters = source.getChapterList(manga.toMangaInfo()) val newChapters = source.getChapterList(manga.toMangaInfo())
.map { it.toSChapter() } .map { it.toSChapter() }
val (new, _) = syncChaptersWithSource(db, newChapters, manga, source) // Not suspending, but does block, maybe fix this? val (new, _) = syncChaptersWithSource(db, newChapters, manga, source) // Not suspending, but does block, maybe fix this?
return new to db.getChapters(manga).await() return new to db.getChapters(manga).executeOnIO()
} catch (t: Throwable) { } catch (t: Throwable) {
if (t is EHentai.GalleryNotFoundException) { if (t is EHentai.GalleryNotFoundException) {
val meta = db.getFlatMetadataForManga(manga.id!!).await()?.raise<EHentaiSearchMetadata>() val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>()
if (meta != null) { if (meta != null) {
// Age dead galleries // Age dead galleries
logger.d("Aged %s - notfound", manga.id) logger.d("Aged %s - notfound", manga.id)

View File

@ -84,13 +84,13 @@ class FavoritesSyncHelper(val context: Context) {
// Validate library state // Validate library state
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context) status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
val libraryManga = db.getLibraryMangas().await() val libraryManga = db.getLibraryMangas().executeOnIO()
val seenManga = HashSet<Long>(libraryManga.size) val seenManga = HashSet<Long>(libraryManga.size)
libraryManga.forEach { libraryManga.forEach {
if (it.source != EXH_SOURCE_ID && it.source != EH_SOURCE_ID) return@forEach if (it.source != EXH_SOURCE_ID && it.source != EH_SOURCE_ID) return@forEach
if (it.id in seenManga) { if (it.id in seenManga) {
val inCategories = db.getCategoriesForManga(it).await() val inCategories = db.getCategoriesForManga(it).executeOnIO()
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it, inCategories, context) status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it, inCategories, context)
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id)) logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id))

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.md.handlers.serializers.FollowPage import exh.md.handlers.serializers.FollowPage
import exh.md.handlers.serializers.FollowsIndividualSerializer import exh.md.handlers.serializers.FollowsIndividualSerializer
import exh.md.handlers.serializers.FollowsPageSerializer import exh.md.handlers.serializers.FollowsPageSerializer
@ -17,8 +18,6 @@ import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.util.floor import exh.util.floor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.FormBody import okhttp3.FormBody
@ -122,7 +121,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
title = manga.title title = manga.title
mdUrl = manga.url mdUrl = manga.url
thumbnail_url = manga.thumbnail_url thumbnail_url = manga.thumbnail_url
follow_status = FollowStatus.fromInt(result.followType)?.int follow_status = FollowStatus.fromInt(result.followType).int
} }
} }
@ -130,7 +129,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
* Change the status of a manga * Change the status of a manga
*/ */
suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean { suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean {
return withContext(Dispatchers.IO) { return withIOContext {
val response: Response = val response: Response =
if (followStatus == FollowStatus.UNFOLLOWED) { if (followStatus == FollowStatus.UNFOLLOWED) {
client.newCall( client.newCall(
@ -153,12 +152,12 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
.await() .await()
} }
withContext(Dispatchers.IO) { response.body?.string().isNullOrEmpty() } withIOContext { response.body?.string().isNullOrEmpty() }
} }
} }
suspend fun updateReadingProgress(track: Track): Boolean { suspend fun updateReadingProgress(track: Track): Boolean {
return withContext(Dispatchers.IO) { return withIOContext {
val mangaID = MdUtil.getMangaId(track.tracking_url) val mangaID = MdUtil.getMangaId(track.tracking_url)
val formBody = FormBody.Builder() val formBody = FormBody.Builder()
.add("chapter", track.last_chapter_read.toString()) .add("chapter", track.last_chapter_read.toString())
@ -178,12 +177,12 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
) )
).await().body?.close() ).await().body?.close()
withContext(Dispatchers.IO) { response.body?.string().isNullOrEmpty() } withIOContext { response.body?.string().isNullOrEmpty() }
} }
} }
suspend fun updateRating(track: Track): Boolean { suspend fun updateRating(track: Track): Boolean {
return withContext(Dispatchers.IO) { return withIOContext {
val mangaID = MdUtil.getMangaId(track.tracking_url) val mangaID = MdUtil.getMangaId(track.tracking_url)
val response = client.newCall( val response = client.newCall(
GET( GET(
@ -193,7 +192,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
) )
.await() .await()
withContext(Dispatchers.IO) { response.body?.string().isNullOrEmpty() } withIOContext { response.body?.string().isNullOrEmpty() }
} }
} }
@ -201,7 +200,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
* fetch all manga from all possible pages * fetch all manga from all possible pages
*/ */
suspend fun fetchAllFollows(forceHd: Boolean): List<Pair<SManga, MangaDexSearchMetadata>> { suspend fun fetchAllFollows(forceHd: Boolean): List<Pair<SManga, MangaDexSearchMetadata>> {
return withContext(Dispatchers.IO) { return withIOContext {
val listManga = mutableListOf<Pair<SManga, MangaDexSearchMetadata>>() val listManga = mutableListOf<Pair<SManga, MangaDexSearchMetadata>>()
val response = client.newCall(followsListRequest()).await() val response = client.newCall(followsListRequest()).await()
val mangasPage = followsParseMangaPage(response, forceHd) val mangasPage = followsParseMangaPage(response, forceHd)
@ -215,7 +214,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
} }
suspend fun fetchTrackingInfo(url: String): Track { suspend fun fetchTrackingInfo(url: String): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val request = GET( val request = GET(
"${MdUtil.apiUrl}${MdUtil.followsMangaApi}" + MdUtil.getMangaId(url), "${MdUtil.apiUrl}${MdUtil.followsMangaApi}" + MdUtil.getMangaId(url),
headers, headers,

View File

@ -10,10 +10,9 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toMangaInfo import eu.kanade.tachiyomi.source.model.toMangaInfo
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.md.handlers.serializers.ApiCovers import exh.md.handlers.serializers.ApiCovers
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -25,12 +24,12 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
// TODO make use of this // TODO make use of this
suspend fun fetchMangaAndChapterDetails(manga: MangaInfo, sourceId: Long): Pair<MangaInfo, List<SChapter>> { suspend fun fetchMangaAndChapterDetails(manga: MangaInfo, sourceId: Long): Pair<MangaInfo, List<SChapter>> {
return withContext(Dispatchers.IO) { return withIOContext {
val response = client.newCall(apiRequest(manga.toSManga())).await() val response = client.newCall(apiRequest(manga.toSManga())).await()
val covers = getCovers(manga, forceLatestCovers) val covers = getCovers(manga, forceLatestCovers)
val parser = ApiMangaParser(lang) val parser = ApiMangaParser(lang)
val jsonData = withContext(Dispatchers.IO) { response.body!!.string() } val jsonData = withIOContext { response.body!!.string() }
if (response.code != 200) { if (response.code != 200) {
XLog.tag("MangaHandler").enableStackTrace(2).e("error from MangaDex with response code ${response.code} \n body: \n$jsonData") XLog.tag("MangaHandler").enableStackTrace(2).e("error from MangaDex with response code ${response.code} \n body: \n$jsonData")
throw Exception("Error from MangaDex Response code ${response.code} ") throw Exception("Error from MangaDex Response code ${response.code} ")
@ -55,7 +54,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
} }
suspend fun getMangaIdFromChapterId(urlChapterId: String): Int { suspend fun getMangaIdFromChapterId(urlChapterId: String): Int {
return withContext(Dispatchers.IO) { return withIOContext {
val request = GET(MdUtil.apiUrl + MdUtil.newApiChapter + urlChapterId + MdUtil.apiChapterSuffix, headers, CacheControl.FORCE_NETWORK) val request = GET(MdUtil.apiUrl + MdUtil.newApiChapter + urlChapterId + MdUtil.apiChapterSuffix, headers, CacheControl.FORCE_NETWORK)
val response = client.newCall(request).await() val response = client.newCall(request).await()
ApiMangaParser(lang).chapterParseForMangaId(response) ApiMangaParser(lang).chapterParseForMangaId(response)
@ -63,7 +62,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
} }
suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long): MangaInfo { suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long): MangaInfo {
return withContext(Dispatchers.IO) { return withIOContext {
val response = client.newCall(apiRequest(manga.toSManga())).await() val response = client.newCall(apiRequest(manga.toSManga())).await()
val covers = getCovers(manga, forceLatestCovers) val covers = getCovers(manga, forceLatestCovers)
ApiMangaParser(lang).parseToManga(manga, response, covers, sourceId) ApiMangaParser(lang).parseToManga(manga, response, covers, sourceId)
@ -100,7 +99,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
} }
suspend fun fetchChapterList(manga: SManga): List<SChapter> { suspend fun fetchChapterList(manga: SManga): List<SChapter> {
return withContext(Dispatchers.IO) { return withIOContext {
val response = client.newCall(apiRequest(manga)).await() val response = client.newCall(apiRequest(manga)).await()
ApiMangaParser(lang).chapterListParse(response) ApiMangaParser(lang).chapterListParse(response)
} }
@ -115,7 +114,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
} }
suspend fun fetchRandomMangaId(): String { suspend fun fetchRandomMangaId(): String {
return withContext(Dispatchers.IO) { return withIOContext {
val response = client.newCall(randomMangaRequest()).await() val response = client.newCall(randomMangaRequest()).await()
ApiMangaParser(lang).randomMangaIdParse(response) ApiMangaParser(lang).randomMangaIdParse(response)
} }

View File

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.isServiceRunning
import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.system.notificationManager
import exh.md.similar.sql.models.MangaSimilarImpl import exh.md.similar.sql.models.MangaSimilarImpl
@ -28,7 +29,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okio.buffer import okio.buffer
import okio.sink import okio.sink
import okio.source import okio.source
@ -149,7 +149,7 @@ class SimilarUpdateService(
/** /**
* Method that updates the similar database for manga * Method that updates the similar database for manga
*/ */
private suspend fun updateSimilar() = withContext(Dispatchers.IO) { private suspend fun updateSimilar() = withIOContext {
val response = client val response = client
.newCall(GET(similarUrl)) .newCall(GET(similarUrl))
.await() .await()
@ -157,7 +157,7 @@ class SimilarUpdateService(
throw Exception("Error trying to download similar file") throw Exception("Error trying to download similar file")
} }
val destinationFile = File(filesDir, "neko-similar.json") val destinationFile = File(filesDir, "neko-similar.json")
val buffer = withContext(Dispatchers.IO) { destinationFile.sink().buffer() } val buffer = withIOContext { destinationFile.sink().buffer() }
// write json to file // write json to file
response.body?.byteStream()?.source()?.use { input -> response.body?.byteStream()?.source()?.use { input ->

View File

@ -8,15 +8,13 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SMangaImpl import eu.kanade.tachiyomi.source.model.SMangaImpl
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import eu.kanade.tachiyomi.util.lang.asObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.log.maybeInjectEHLogger import exh.log.maybeInjectEHLogger
import exh.util.MangaType import exh.util.MangaType
import exh.util.mangaType import exh.util.mangaType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
@ -32,6 +30,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
@ -55,7 +54,7 @@ class MyAnimeList : API("https://api.jikan.moe/v3/") {
.toString() .toString()
val response = client.newCall(GET(apiUrl)).await() val response = client.newCall(GET(apiUrl)).await()
val body = withContext(Dispatchers.IO) { response.body?.string() } ?: throw Exception("Null Response") val body = withIOContext { response.body?.string() } ?: throw Exception("Null Response")
val data = Json.decodeFromString<JsonObject>(body) val data = Json.decodeFromString<JsonObject>(body)
val recommendations = data["recommendations"] as? JsonArray val recommendations = data["recommendations"] as? JsonArray
return recommendations?.filterIsInstance<JsonObject>()?.map { rec -> return recommendations?.filterIsInstance<JsonObject>()?.map { rec ->
@ -79,7 +78,7 @@ class MyAnimeList : API("https://api.jikan.moe/v3/") {
.toString() .toString()
val response = client.newCall(GET(url)).await() val response = client.newCall(GET(url)).await()
val body = withContext(Dispatchers.IO) { response.body?.string() } ?: throw Exception("Null Response") val body = withIOContext { response.body?.string() } ?: throw Exception("Null Response")
val data = Json.decodeFromString<JsonObject>(body) val data = Json.decodeFromString<JsonObject>(body)
val results = data["results"] as? JsonArray val results = data["results"] as? JsonArray
if (results.isNullOrEmpty()) { if (results.isNullOrEmpty()) {
@ -167,7 +166,7 @@ class Anilist : API("https://graphql.anilist.co/") {
val payloadBody = payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) val payloadBody = payload.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val response = client.newCall(POST(endpoint, body = payloadBody)).await() val response = client.newCall(POST(endpoint, body = payloadBody)).await()
val body = withContext(Dispatchers.IO) { response.body?.string() } ?: throw Exception("Null Response") val body = withIOContext { response.body?.string() } ?: throw Exception("Null Response")
val data = Json.decodeFromString<JsonObject>(body)["data"] as? JsonObject ?: throw Exception("Unexpected response") val data = Json.decodeFromString<JsonObject>(body)["data"] as? JsonObject ?: throw Exception("Unexpected response")
val media = data["Page"]?.jsonObject?.get("media")?.jsonArray val media = data["Page"]?.jsonObject?.get("media")?.jsonArray
@ -203,7 +202,7 @@ open class RecommendsPager(
private var preferredApi: API = API.MYANIMELIST private var preferredApi: API = API.MYANIMELIST
) : Pager() { ) : Pager() {
override fun requestNext(): Observable<MangasPage> { override fun requestNext(): Observable<MangasPage> {
return flow { return runAsObservable({
if (smart) preferredApi = if (manga.mangaType() != MangaType.TYPE_MANGA) API.ANILIST else preferredApi 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 }
@ -223,19 +222,17 @@ open class RecommendsPager(
.firstOrNull { it.isNotEmpty() } .firstOrNull { it.isNotEmpty() }
.orEmpty() .orEmpty()
val page = MangasPage(recs, false) MangasPage(recs, false)
emit(page) })
}
.onEach {
withContext(Dispatchers.Main) {
if (it.mangas.isNotEmpty()) {
onPageReceived(it)
} else {
throw NoResultsException()
}
}
}.asObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
if (it.mangas.isNotEmpty()) {
onPageReceived(it)
} else {
throw NoResultsException()
}
}
} }
companion object { companion object {

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.awaitSingle
import exh.util.executeOnIO
import info.debatty.java.stringsimilarity.NormalizedLevenshtein import info.debatty.java.stringsimilarity.NormalizedLevenshtein
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -170,11 +171,11 @@ class SmartSearchEngine(
* @return a manga from the database. * @return a manga from the database.
*/ */
suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = db.getManga(sManga.url, sourceId).await() var localManga = db.getManga(sManga.url, sourceId).executeOnIO()
if (localManga == null) { if (localManga == null) {
val newManga = Manga.create(sManga.url, sManga.title, sourceId) val newManga = Manga.create(sManga.url, sManga.title, sourceId)
newManga.copyFrom(sManga) newManga.copyFrom(sManga)
val result = db.insertManga(newManga).await() val result = db.insertManga(newManga).executeOnIO()
newManga.id = result.insertedId() newManga.id = result.insertedId()
localManga = newManga localManga = newManga
} }

View File

@ -7,29 +7,26 @@ import com.afollestad.materialdialogs.MaterialDialog
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ConfiguringDialogController : DialogController() { class ConfiguringDialogController : DialogController() {
private var materialDialog: MaterialDialog? = null private var materialDialog: MaterialDialog? = null
val scope = CoroutineScope(Job() + Dispatchers.Main) val scope = MainScope()
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
if (savedViewState == null) { if (savedViewState == null) {
scope.launch(Dispatchers.IO) { scope.launchIO {
try { try {
EHConfigurator(activity!!).configureAll() EHConfigurator(activity!!).configureAll()
launchUI { launchUI {
activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded)) activity?.toast(activity?.getString(R.string.eh_settings_successfully_uploaded))
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { launchUI {
activity?.let { activity?.let {
MaterialDialog(it) MaterialDialog(it)
.title(R.string.eh_settings_configuration_failed) .title(R.string.eh_settings_configuration_failed)

View File

@ -1,9 +1,10 @@
package exh.ui.base package exh.ui.base
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
@ -11,14 +12,13 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import nucleus.presenter.Presenter import nucleus.presenter.Presenter
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
@Suppress("DEPRECATION", "unused") @Suppress("DEPRECATION", "unused")
open class CoroutinePresenter<V>( open class CoroutinePresenter<V>(
scope: CoroutineScope = CoroutineScope(Job() + Dispatchers.Main) scope: CoroutineScope = MainScope()
) : Presenter<V>(), CoroutineScope by scope { ) : Presenter<V>(), CoroutineScope by scope {
@Suppress("DeprecatedCallableAddReplaceWith") @Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use launchInView, Flow.inView, Flow.mapView") @Deprecated("Use launchInView, Flow.inView, Flow.mapView")
@ -26,19 +26,19 @@ open class CoroutinePresenter<V>(
return super.getView() return super.getView()
} }
fun launchInView(block: (CoroutineScope, V) -> Unit) = launch(Dispatchers.Main) { fun launchInView(block: (CoroutineScope, V) -> Unit) = launchUI {
view?.let { block.invoke(this, it) } view?.let { block.invoke(this, it) }
} }
inline fun <F> Flow<F>.inView(crossinline block: (V, F) -> Unit) = onEach { inline fun <F> Flow<F>.inView(crossinline block: (V, F) -> Unit) = onEach {
withContext(Dispatchers.Main) { withUIContext {
view?.let { view -> block(view, it) } view?.let { view -> block(view, it) }
} }
} }
inline fun <F, P> Flow<F>.mapView(crossinline block: (V, F) -> P): Flow<P> { inline fun <F, P> Flow<F>.mapView(crossinline block: (V, F) -> P): Flow<P> {
return mapNotNull { return mapNotNull {
withContext(Dispatchers.Main) { withUIContext {
view?.let { view -> block(view, it) } view?.let { view -> block(view, it) }
} }
} }

View File

@ -7,6 +7,7 @@ import com.jakewharton.rxrelay.ReplayRelay
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.GalleryAddEvent import exh.GalleryAddEvent
import exh.GalleryAdder import exh.GalleryAdder
import exh.util.trimOrNull import exh.util.trimOrNull
@ -14,7 +15,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -65,7 +65,7 @@ class BatchAddPresenter : BasePresenter<BatchAddController>() {
splitGalleries.forEachIndexed { i, s -> splitGalleries.forEachIndexed { i, s ->
ensureActive() ensureActive()
val result = withContext(Dispatchers.IO) { galleryAdder.addGallery(context, s, true) } val result = withIOContext { galleryAdder.addGallery(context, s, true) }
if (result is GalleryAddEvent.Success) { if (result is GalleryAddEvent.Success) {
succeeded.add(s) succeeded.add(s)
} else { } else {

View File

@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.setDefaultSettings
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.util.melt import exh.util.melt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -188,7 +187,7 @@ class BrowserActionActivity : AppCompatActivity() {
currentLoopId = null currentLoopId = null
validateCurrentLoopId = null validateCurrentLoopId = null
XLog.tag("BrowserActionActivity").enableStackTrace(2).e(IllegalStateException("Captcha solve failure!")) XLog.tag("BrowserActionActivity").enableStackTrace(2).e(IllegalStateException("Captcha solve failure!"))
withContext(Dispatchers.Main) { withUIContext {
binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null) binding.webview.evaluateJavascript(SOLVE_UI_SCRIPT_HIDE, null)
MaterialDialog(this@BrowserActionActivity) MaterialDialog(this@BrowserActionActivity)
.title(R.string.captcha_solve_failure) .title(R.string.captcha_solve_failure)

View File

@ -12,12 +12,12 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.SourceController import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class SmartSearchController(bundle: Bundle? = null) : NucleusController<EhSmartSearchBinding, SmartSearchPresenter>() { class SmartSearchController(bundle: Bundle? = null) : NucleusController<EhSmartSearchBinding, SmartSearchPresenter>() {
@ -52,7 +52,7 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<EhSmartS
.onEach { results -> .onEach { results ->
if (results is SmartSearchPresenter.SearchResults.Found) { if (results is SmartSearchPresenter.SearchResults.Found) {
val transaction = MangaController(results.manga, true, smartSearchConfig).withFadeTransaction() val transaction = MangaController(results.manga, true, smartSearchConfig).withFadeTransaction()
withContext(Dispatchers.Main) { withUIContext {
router.replaceTopController(transaction) router.replaceTopController(transaction)
} }
} else { } else {
@ -63,7 +63,7 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<EhSmartS
} }
val transaction = BrowseSourceController(source, smartSearchConfig.origTitle, smartSearchConfig).withFadeTransaction() val transaction = BrowseSourceController(source, smartSearchConfig.origTitle, smartSearchConfig).withFadeTransaction()
withContext(Dispatchers.Main) { withUIContext {
router.replaceTopController(transaction) router.replaceTopController(transaction)
} }
} }

View File

@ -11,10 +11,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.source.getMainSource import exh.source.getMainSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -37,7 +36,7 @@ class MangadexLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) {
.positiveButton(R.string.logout) { .positiveButton(R.string.logout) {
launchNow { launchNow {
source?.let { source -> source?.let { source ->
val loggedOut = withContext(Dispatchers.IO) { source.logout() } val loggedOut = withIOContext { source.logout() }
if (loggedOut) { if (loggedOut) {
trackManager.mdList.logout() trackManager.mdList.logout()