diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt index cd101484eb..8afc1eaea7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt @@ -12,8 +12,6 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource -import eu.kanade.tachiyomi.util.lang.runAsObservable -import rx.Observable import uy.kohesive.injekt.injectLazy abstract class AbstractBackupManager(protected val context: Context) { @@ -34,25 +32,22 @@ abstract class AbstractBackupManager(protected val context: Context) { databaseHelper.getManga(manga.url, manga.source).executeAsBlocking() /** - * [Observable] that fetches chapter information + * Fetches chapter information. * * @param source source of manga * @param manga manga that needs updating * @param chapters list of chapters in the backup - * @return [Observable] that contains manga + * @return Updated manga chapters. */ - internal fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List): Observable, List>> { - return runAsObservable({ - source.getChapterList(manga.toMangaInfo()) - .map { it.toSChapter() } - }) - .map { syncChaptersWithSource(databaseHelper, it, manga, source) } - .doOnNext { (first) -> - if (first.isNotEmpty()) { - chapters.forEach { it.manga_id = manga.id } - updateChapters(chapters) - } - } + internal suspend fun restoreChapters(source: Source, manga: Manga, chapters: List): Pair, List> { + val fetchedChapters = source.getChapterList(manga.toMangaInfo()) + .map { it.toSChapter() } + val syncedChapters = syncChaptersWithSource(databaseHelper, fetchedChapters, manga, source) + if (syncedChapters.first.isNotEmpty()) { + chapters.forEach { it.manga_id = manga.id } + updateChapters(chapters) + } + return syncedChapters } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestore.kt index c0219b40f4..5a7b08c3fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupRestore.kt @@ -10,9 +10,8 @@ import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.util.chapter.NoChaptersException -import eu.kanade.tachiyomi.util.lang.runAsObservable +import eu.kanade.tachiyomi.util.lang.await import kotlinx.coroutines.Job -import rx.Observable import uy.kohesive.injekt.injectLazy import java.io.File import java.text.SimpleDateFormat @@ -59,48 +58,47 @@ abstract class AbstractBackupRestore(protected val co } /** - * [Observable] that fetches chapter information + * Fetches chapter information. * * @param source source of manga * @param manga manga that needs updating - * @return [Observable] that contains manga + * @return Updated manga chapters. */ - internal fun chapterFetchObservable(source: Source, manga: Manga, chapters: List): Observable, List>> { - return backupManager.restoreChapterFetchObservable(source, manga, chapters) + internal suspend fun updateChapters(source: Source, manga: Manga, chapters: List): Pair, List> { + return try { + backupManager.restoreChapters(source, manga, chapters) + } catch (e: Exception) { // If there's any error, return empty update and continue. - .onErrorReturn { - val errorMessage = if (it is NoChaptersException) { - context.getString(R.string.no_chapters_error) - } else { - it.message - } - errors.add(Date() to "${manga.title} - $errorMessage") - Pair(emptyList(), emptyList()) + val errorMessage = if (e is NoChaptersException) { + context.getString(R.string.no_chapters_error) + } else { + e.message } + errors.add(Date() to "${manga.title} - $errorMessage") + Pair(emptyList(), emptyList()) + } } /** - * [Observable] that refreshes tracking information + * Refreshes tracking information. + * * @param manga manga that needs updating. * @param tracks list containing tracks from restore file. - * @return [Observable] that contains updated track item */ - internal fun trackingFetchObservable(manga: Manga, tracks: List): Observable { - return Observable.from(tracks) - .flatMap { track -> - val service = trackManager.getService(track.sync_id) - if (service != null && service.isLogged) { - runAsObservable({ service.refresh(track) }) - .doOnNext { db.insertTrack(it).executeAsBlocking() } - .onErrorReturn { - errors.add(Date() to "${manga.title} - ${it.message}") - track - } - } else { - errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}") - Observable.empty() + internal suspend fun updateTracking(manga: Manga, tracks: List) { + tracks.forEach { track -> + val service = trackManager.getService(track.sync_id) + if (service != null && service.isLogged) { + try { + val updatedTrack = service.refresh(track) + db.insertTrack(updatedTrack).await() + } catch (e: Exception) { + errors.add(Date() to "${manga.title} - ${e.message}") } + } else { + errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}") } + } } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt index 8b243327e3..a56b63691a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt @@ -29,13 +29,11 @@ import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.toSManga -import eu.kanade.tachiyomi.util.lang.runAsObservable import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.protobuf.ProtoBuf import okio.buffer import okio.gzip import okio.sink -import rx.Observable import timber.log.Timber import kotlin.math.max @@ -185,29 +183,26 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) { } /** - * [Observable] that fetches manga information + * Fetches manga information * * @param source source of manga * @param manga manga that needs updating - * @return [Observable] that contains manga + * @return Updated manga info. */ - fun restoreMangaFetchObservable(source: Source?, manga: Manga, online: Boolean): Observable { + suspend fun restoreMangaFetch(source: Source?, manga: Manga, online: Boolean): Manga { return if (online && source != null) { - return runAsObservable({ - val networkManga = source.getMangaDetails(manga.toMangaInfo()) - manga.copyFrom(networkManga.toSManga()) - manga.favorite = manga.favorite - manga.initialized = true - manga.id = insertManga(manga) - manga - }) + val networkManga = source.getMangaDetails(manga.toMangaInfo()) + manga.also { + it.copyFrom(networkManga.toSManga()) + it.favorite = manga.favorite + it.initialized = true + it.id = insertManga(manga) + } } else { - Observable.just(manga) - .map { - it.initialized = it.description != null - it.id = insertManga(it) - it - } + manga.also { + it.initialized = it.description != null + it.id = insertManga(it) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt index 3e6ad20770..3fe4b0a909 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupRestore.kt @@ -13,11 +13,11 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.util.lang.launchIO import kotlinx.serialization.ExperimentalSerializationApi import okio.buffer import okio.gzip import okio.source -import rx.Observable import java.util.Date @OptIn(ExperimentalSerializationApi::class) @@ -120,7 +120,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val } /** - * [Observable] that fetches manga information + * Fetches manga information * * @param manga manga that needs updating * @param chapters chapters of manga that needs updating @@ -136,28 +136,24 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val backupCategories: List, online: Boolean ) { - backupManager.restoreMangaFetchObservable(source, manga, online) - .doOnError { - errors.add(Date() to "${manga.title} - ${it.message}") - } - .filter { it.id != null } - .flatMap { + launchIO { + try { + val fetchedManga = backupManager.restoreMangaFetch(source, manga, online) + fetchedManga.id ?: (return@launchIO) + if (online && source != null) { - chapterFetchObservable(source, it, chapters) - // Convert to the manga that contains new chapters. - .map { manga } + updateChapters(source, fetchedManga, chapters) } else { - backupManager.restoreChaptersForMangaOffline(it, chapters) - Observable.just(manga) + backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters) } + + restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories) + + updateTracking(fetchedManga, tracks) + } catch (e: Exception) { + errors.add(Date() to "${manga.title} - ${e.message}") } - .doOnNext { - restoreExtraForManga(it, categories, history, tracks, backupCategories) - } - .flatMap { - trackingFetchObservable(it, tracks) - } - .subscribe() + } } private fun restoreMangaNoFetch( @@ -170,27 +166,19 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val backupCategories: List, online: Boolean ) { - Observable.just(backupManga) - .flatMap { manga -> - if (online && source != null) { - if (!backupManager.restoreChaptersForManga(manga, chapters)) { - chapterFetchObservable(source, manga, chapters) - .map { manga } - } else { - Observable.just(manga) - } - } else { - backupManager.restoreChaptersForMangaOffline(manga, chapters) - Observable.just(manga) + launchIO { + if (online && source != null) { + if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { + updateChapters(source, backupManga, chapters) } + } else { + backupManager.restoreChaptersForMangaOffline(backupManga, chapters) } - .doOnNext { - restoreExtraForManga(it, categories, history, tracks, backupCategories) - } - .flatMap { manga -> - trackingFetchObservable(manga, tracks) - } - .subscribe() + + restoreExtraForManga(backupManga, categories, history, tracks, backupCategories) + + updateTracking(backupManga, tracks) + } } private fun restoreExtraForManga(manga: Manga, categories: List, history: List, tracks: List, backupCategories: List) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt index 53dc47ba0c..ff154eeb4c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt @@ -48,8 +48,6 @@ import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.toSManga -import eu.kanade.tachiyomi.util.lang.runAsObservable -import rx.Observable import timber.log.Timber import kotlin.math.max @@ -252,21 +250,20 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab } /** - * [Observable] that fetches manga information + * Fetches manga information * * @param source source of manga * @param manga manga that needs updating - * @return [Observable] that contains manga + * @return Updated manga. */ - fun restoreMangaFetchObservable(source: Source, manga: Manga): Observable { - return runAsObservable({ - val networkManga = source.getMangaDetails(manga.toMangaInfo()) - manga.copyFrom(networkManga.toSManga()) - manga.favorite = true - manga.initialized = true - manga.id = insertManga(manga) - manga - }) + suspend fun fetchManga(source: Source, manga: Manga): Manga { + val networkManga = source.getMangaDetails(manga.toMangaInfo()) + return manga.also { + it.copyFrom(networkManga.toSManga()) + it.favorite = true + it.initialized = true + it.id = insertManga(manga) + } } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt index e0e448f6f4..b910155c85 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupRestore.kt @@ -21,7 +21,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.source.Source -import rx.Observable +import eu.kanade.tachiyomi.util.lang.launchIO import java.util.Date class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore(context, notifier) { @@ -137,7 +137,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract } /** - * [Observable] that fetches manga information + * Fetches manga information. * * @param manga manga that needs updating * @param chapters chapters of manga that needs updating @@ -151,24 +151,20 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract history: List, tracks: List ) { - backupManager.restoreMangaFetchObservable(source, manga) - .onErrorReturn { - errors.add(Date() to "${manga.title} - ${it.message}") - manga + launchIO { + try { + val fetchedManga = backupManager.fetchManga(source, manga) + fetchedManga.id ?: (return@launchIO) + + updateChapters(source, fetchedManga, chapters) + + restoreExtraForManga(fetchedManga, categories, history, tracks) + + updateTracking(fetchedManga, tracks) + } catch (e: Exception) { + errors.add(Date() to "${manga.title} - ${e.message}") } - .filter { it.id != null } - .flatMap { - chapterFetchObservable(source, it, chapters) - // Convert to the manga that contains new chapters. - .map { manga } - } - .doOnNext { - restoreExtraForManga(it, categories, history, tracks) - } - .flatMap { - trackingFetchObservable(it, tracks) - } - .subscribe() + } } private fun restoreMangaNoFetch( @@ -179,22 +175,15 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract history: List, tracks: List ) { - Observable.just(backupManga) - .flatMap { manga -> - if (!backupManager.restoreChaptersForManga(manga, chapters)) { - chapterFetchObservable(source, manga, chapters) - .map { manga } - } else { - Observable.just(manga) - } + launchIO { + if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { + updateChapters(source, backupManga, chapters) } - .doOnNext { - restoreExtraForManga(it, categories, history, tracks) - } - .flatMap { manga -> - trackingFetchObservable(manga, tracks) - } - .subscribe() + + restoreExtraForManga(backupManga, categories, history, tracks) + + updateTracking(backupManga, tracks) + } } private fun restoreExtraForManga(manga: Manga, categories: List, history: List, tracks: List) { diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt index d550718169..a74b6e502b 100644 --- a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt +++ b/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt @@ -211,7 +211,7 @@ class BackupTest { networkManga.description = "This is a description" `when`(source.fetchMangaDetails(jsonManga)).thenReturn(Observable.just(networkManga)) - val obs = legacyBackupManager.restoreMangaFetchObservable(source, jsonManga) + val obs = legacyBackupManager.fetchManga(source, jsonManga) val testSubscriber = TestSubscriber() obs.subscribe(testSubscriber) @@ -255,7 +255,7 @@ class BackupTest { `when`(source.fetchChapterList(manga)).thenReturn(Observable.just(chaptersRemote)) // Call restoreChapterFetchObservable - val obs = legacyBackupManager.restoreChapterFetchObservable(source, manga, restoredChapters) + val obs = legacyBackupManager.restoreChapters(source, manga, restoredChapters) val testSubscriber = TestSubscriber, List>>() obs.subscribe(testSubscriber)