Help with favorites sync db locking
This commit is contained in:
parent
963b85f756
commit
9e80f47e9f
@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import exh.log.xLogStack
|
||||
import exh.source.getMainSource
|
||||
import exh.util.executeOnIO
|
||||
import exh.util.maybeRunBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -53,7 +53,8 @@ class GalleryAdder {
|
||||
url: String,
|
||||
fav: Boolean = false,
|
||||
forceSource: UrlImportableSource? = null,
|
||||
throttleFunc: suspend () -> Unit = {}
|
||||
throttleFunc: suspend () -> Unit = {},
|
||||
protectTrans: Boolean = false
|
||||
): GalleryAddEvent {
|
||||
logger.d(context.getString(R.string.gallery_adder_importing_manga, url, fav.toString(), forceSource))
|
||||
try {
|
||||
@ -118,7 +119,7 @@ class GalleryAdder {
|
||||
} ?: return GalleryAddEvent.Fail.UnknownType(url, context)
|
||||
|
||||
// Use manga in DB if possible, otherwise, make a new manga
|
||||
val manga = db.getManga(cleanedMangaUrl, source.id).executeOnIO()
|
||||
val manga = db.getManga(cleanedMangaUrl, source.id).executeAsBlocking()
|
||||
?: Manga.create(source.id).apply {
|
||||
this.url = cleanedMangaUrl
|
||||
title = realMangaUrl
|
||||
@ -146,14 +147,16 @@ class GalleryAdder {
|
||||
|
||||
// Fetch and copy chapters
|
||||
try {
|
||||
val chapterList = if (source is EHentai) {
|
||||
source.getChapterList(manga.toMangaInfo(), throttleFunc)
|
||||
} else {
|
||||
source.getChapterList(manga.toMangaInfo())
|
||||
}.map { it.toSChapter() }
|
||||
maybeRunBlocking(protectTrans) {
|
||||
val chapterList = if (source is EHentai) {
|
||||
source.getChapterList(manga.toMangaInfo(), throttleFunc)
|
||||
} else {
|
||||
source.getChapterList(manga.toMangaInfo())
|
||||
}.map { it.toSChapter() }
|
||||
|
||||
if (chapterList.isNotEmpty()) {
|
||||
syncChaptersWithSource(db, chapterList, manga, source)
|
||||
if (chapterList.isNotEmpty()) {
|
||||
syncChaptersWithSource(db, chapterList, manga, source)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
||||
@ -161,7 +164,7 @@ class GalleryAdder {
|
||||
}
|
||||
|
||||
return if (cleanedChapterUrl != null) {
|
||||
val chapter = db.getChapter(cleanedChapterUrl, manga.id!!).executeOnIO()
|
||||
val chapter = db.getChapter(cleanedChapterUrl, manga.id!!).executeAsBlocking()
|
||||
if (chapter != null) {
|
||||
GalleryAddEvent.Success(url, manga, context, chapter)
|
||||
} else {
|
||||
|
@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.powerManager
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import exh.GalleryAddEvent
|
||||
@ -24,7 +25,6 @@ import exh.log.xLog
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.EXH_SOURCE_ID
|
||||
import exh.source.isEhBasedManga
|
||||
import exh.util.executeOnIO
|
||||
import exh.util.ignore
|
||||
import exh.util.trans
|
||||
import exh.util.wifiManager
|
||||
@ -85,13 +85,13 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
// Validate library state
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
||||
val libraryManga = db.getLibraryMangas().executeOnIO()
|
||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
||||
val seenManga = HashSet<Long>(libraryManga.size)
|
||||
libraryManga.forEach {
|
||||
if (!it.isEhBasedManga()) return@forEach
|
||||
|
||||
if (it.id in seenManga) {
|
||||
val inCategories = db.getCategoriesForManga(it).executeOnIO()
|
||||
val inCategories = db.getCategoriesForManga(it).executeAsBlocking()
|
||||
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(it, inCategories, context)
|
||||
|
||||
logger.w(context.getString(R.string.favorites_sync_manga_multiple_categories_error, it.id))
|
||||
@ -194,8 +194,8 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun applyRemoteCategories(categories: List<String>) {
|
||||
val localCategories = db.getCategories().executeOnIO()
|
||||
private fun applyRemoteCategories(categories: List<String>) {
|
||||
val localCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
val newLocalCategories = localCategories.toMutableList()
|
||||
|
||||
@ -233,7 +233,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
// Only insert categories if changed
|
||||
if (changed) {
|
||||
db.insertCategories(newLocalCategories).executeOnIO()
|
||||
db.insertCategories(newLocalCategories).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,7 +267,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
for (i in 1..retryCount) {
|
||||
try {
|
||||
val resp = exh.client.newCall(request).await()
|
||||
val resp = withIOContext { exh.client.newCall(request).await() }
|
||||
|
||||
if (resp.isSuccessful) {
|
||||
success = true
|
||||
@ -340,7 +340,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
db.getManga(url, EXH_SOURCE_ID),
|
||||
db.getManga(url, EH_SOURCE_ID)
|
||||
).forEach {
|
||||
val manga = it.executeOnIO()
|
||||
val manga = it.executeAsBlocking()
|
||||
|
||||
if (manga?.favorite == true) {
|
||||
manga.favorite = false
|
||||
@ -357,7 +357,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
}
|
||||
|
||||
val insertedMangaCategories = mutableListOf<Pair<MangaCategory, Manga>>()
|
||||
val categories = db.getCategories().executeOnIO()
|
||||
val categories = db.getCategories().executeAsBlocking()
|
||||
|
||||
// Apply additions
|
||||
throttleManager.resetThrottle()
|
||||
@ -376,7 +376,8 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
"${exh.baseUrl}${it.getUrl()}",
|
||||
true,
|
||||
exh,
|
||||
throttleManager::throttle
|
||||
throttleManager::throttle,
|
||||
true
|
||||
)
|
||||
|
||||
if (result is GalleryAddEvent.Fail) {
|
||||
|
@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.source.isEhBasedManga
|
||||
import exh.util.executeOnIO
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -20,13 +19,13 @@ class LocalFavoritesStorage {
|
||||
|
||||
fun getRealm(): Realm = Realm.getInstance(realmConfig)
|
||||
|
||||
suspend fun getChangedDbEntries(realm: Realm) =
|
||||
fun getChangedDbEntries(realm: Realm) =
|
||||
getChangedEntries(
|
||||
realm,
|
||||
parseToFavoriteEntries(
|
||||
loadDbCategories(
|
||||
db.getFavoriteMangas()
|
||||
.executeOnIO()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
)
|
||||
)
|
||||
@ -45,11 +44,11 @@ class LocalFavoritesStorage {
|
||||
)
|
||||
)
|
||||
|
||||
suspend fun snapshotEntries(realm: Realm) {
|
||||
fun snapshotEntries(realm: Realm) {
|
||||
val dbMangas = parseToFavoriteEntries(
|
||||
loadDbCategories(
|
||||
db.getFavoriteMangas()
|
||||
.executeOnIO()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
)
|
||||
)
|
||||
@ -97,8 +96,8 @@ class LocalFavoritesStorage {
|
||||
it.category == entry.category
|
||||
}
|
||||
|
||||
private suspend fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> {
|
||||
val dbCategories = db.getCategories().executeOnIO()
|
||||
private fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> {
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
return manga.filter(this::validateDbManga).mapNotNull {
|
||||
val category = db.getCategoriesForManga(it).executeAsBlocking()
|
||||
|
@ -3,8 +3,25 @@ package exh.util
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
fun <T> Flow<T>.cancellable() = onEach {
|
||||
coroutineContext.ensureActive()
|
||||
}
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
suspend inline fun <T> maybeRunBlocking(runBlocking: Boolean, crossinline block: suspend () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return if (runBlocking) {
|
||||
runBlocking { block() }
|
||||
} else {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user