Remove usage of RxJava from backup/restore

This commit is contained in:
arkon 2021-01-04 10:12:58 -05:00
parent 9b2c22b2d9
commit 990fb22d3e
7 changed files with 115 additions and 153 deletions

View File

@ -12,8 +12,6 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.runAsObservable
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
abstract class AbstractBackupManager(protected val context: Context) { 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() databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
/** /**
* [Observable] that fetches chapter information * Fetches chapter information.
* *
* @param source source of manga * @param source source of manga
* @param manga manga that needs updating * @param manga manga that needs updating
* @param chapters list of chapters in the backup * @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<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> { internal suspend fun restoreChapters(source: Source, manga: Manga, chapters: List<Chapter>): Pair<List<Chapter>, List<Chapter>> {
return runAsObservable({ val fetchedChapters = source.getChapterList(manga.toMangaInfo())
source.getChapterList(manga.toMangaInfo()) .map { it.toSChapter() }
.map { it.toSChapter() } val syncedChapters = syncChaptersWithSource(databaseHelper, fetchedChapters, manga, source)
}) if (syncedChapters.first.isNotEmpty()) {
.map { syncChaptersWithSource(databaseHelper, it, manga, source) } chapters.forEach { it.manga_id = manga.id }
.doOnNext { (first) -> updateChapters(chapters)
if (first.isNotEmpty()) { }
chapters.forEach { it.manga_id = manga.id } return syncedChapters
updateChapters(chapters)
}
}
} }
/** /**

View File

@ -10,9 +10,8 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.NoChaptersException 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 kotlinx.coroutines.Job
import rx.Observable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -59,48 +58,47 @@ abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val co
} }
/** /**
* [Observable] that fetches chapter information * Fetches chapter information.
* *
* @param source source of manga * @param source source of manga
* @param manga manga that needs updating * @param manga manga that needs updating
* @return [Observable] that contains manga * @return Updated manga chapters.
*/ */
internal fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> { internal suspend fun updateChapters(source: Source, manga: Manga, chapters: List<Chapter>): Pair<List<Chapter>, List<Chapter>> {
return backupManager.restoreChapterFetchObservable(source, manga, chapters) return try {
backupManager.restoreChapters(source, manga, chapters)
} catch (e: Exception) {
// If there's any error, return empty update and continue. // If there's any error, return empty update and continue.
.onErrorReturn { val errorMessage = if (e is NoChaptersException) {
val errorMessage = if (it is NoChaptersException) { context.getString(R.string.no_chapters_error)
context.getString(R.string.no_chapters_error) } else {
} else { e.message
it.message
}
errors.add(Date() to "${manga.title} - $errorMessage")
Pair(emptyList(), emptyList())
} }
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 manga manga that needs updating.
* @param tracks list containing tracks from restore file. * @param tracks list containing tracks from restore file.
* @return [Observable] that contains updated track item
*/ */
internal fun trackingFetchObservable(manga: Manga, tracks: List<Track>): Observable<Track> { internal suspend fun updateTracking(manga: Manga, tracks: List<Track>) {
return Observable.from(tracks) tracks.forEach { track ->
.flatMap { track -> val service = trackManager.getService(track.sync_id)
val service = trackManager.getService(track.sync_id) if (service != null && service.isLogged) {
if (service != null && service.isLogged) { try {
runAsObservable({ service.refresh(track) }) val updatedTrack = service.refresh(track)
.doOnNext { db.insertTrack(it).executeAsBlocking() } db.insertTrack(updatedTrack).await()
.onErrorReturn { } catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${it.message}") errors.add(Date() to "${manga.title} - ${e.message}")
track
}
} else {
errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}")
Observable.empty()
} }
} else {
errors.add(Date() to "${manga.title} - ${context.getString(R.string.tracker_not_logged_in, service?.name)}")
} }
}
} }
/** /**

View File

@ -29,13 +29,11 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.runAsObservable
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
import okio.sink import okio.sink
import rx.Observable
import timber.log.Timber import timber.log.Timber
import kotlin.math.max 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 source source of manga
* @param manga manga that needs updating * @param manga manga that needs updating
* @return [Observable] that contains manga * @return Updated manga info.
*/ */
fun restoreMangaFetchObservable(source: Source?, manga: Manga, online: Boolean): Observable<Manga> { suspend fun restoreMangaFetch(source: Source?, manga: Manga, online: Boolean): Manga {
return if (online && source != null) { return if (online && source != null) {
return runAsObservable({ val networkManga = source.getMangaDetails(manga.toMangaInfo())
val networkManga = source.getMangaDetails(manga.toMangaInfo()) manga.also {
manga.copyFrom(networkManga.toSManga()) it.copyFrom(networkManga.toSManga())
manga.favorite = manga.favorite it.favorite = manga.favorite
manga.initialized = true it.initialized = true
manga.id = insertManga(manga) it.id = insertManga(manga)
manga }
})
} else { } else {
Observable.just(manga) manga.also {
.map { it.initialized = it.description != null
it.initialized = it.description != null it.id = insertManga(it)
it.id = insertManga(it) }
it
}
} }
} }

View File

@ -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.Manga
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
import okio.source import okio.source
import rx.Observable
import java.util.Date import java.util.Date
@OptIn(ExperimentalSerializationApi::class) @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 manga manga that needs updating
* @param chapters chapters of 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<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
backupManager.restoreMangaFetchObservable(source, manga, online) launchIO {
.doOnError { try {
errors.add(Date() to "${manga.title} - ${it.message}") val fetchedManga = backupManager.restoreMangaFetch(source, manga, online)
} fetchedManga.id ?: (return@launchIO)
.filter { it.id != null }
.flatMap {
if (online && source != null) { if (online && source != null) {
chapterFetchObservable(source, it, chapters) updateChapters(source, fetchedManga, chapters)
// Convert to the manga that contains new chapters.
.map { manga }
} else { } else {
backupManager.restoreChaptersForMangaOffline(it, chapters) backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters)
Observable.just(manga)
} }
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( private fun restoreMangaNoFetch(
@ -170,27 +166,19 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
Observable.just(backupManga) launchIO {
.flatMap { manga -> if (online && source != null) {
if (online && source != null) { if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(manga, chapters)) { updateChapters(source, backupManga, chapters)
chapterFetchObservable(source, manga, chapters)
.map { manga }
} else {
Observable.just(manga)
}
} else {
backupManager.restoreChaptersForMangaOffline(manga, chapters)
Observable.just(manga)
} }
} else {
backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
} }
.doOnNext {
restoreExtraForManga(it, categories, history, tracks, backupCategories) restoreExtraForManga(backupManga, categories, history, tracks, backupCategories)
}
.flatMap { manga -> updateTracking(backupManga, tracks)
trackingFetchObservable(manga, tracks) }
}
.subscribe()
} }
private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) {

View File

@ -48,8 +48,6 @@ import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.runAsObservable
import rx.Observable
import timber.log.Timber import timber.log.Timber
import kotlin.math.max 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 source source of manga
* @param manga manga that needs updating * @param manga manga that needs updating
* @return [Observable] that contains manga * @return Updated manga.
*/ */
fun restoreMangaFetchObservable(source: Source, manga: Manga): Observable<Manga> { suspend fun fetchManga(source: Source, manga: Manga): Manga {
return runAsObservable({ val networkManga = source.getMangaDetails(manga.toMangaInfo())
val networkManga = source.getMangaDetails(manga.toMangaInfo()) return manga.also {
manga.copyFrom(networkManga.toSManga()) it.copyFrom(networkManga.toSManga())
manga.favorite = true it.favorite = true
manga.initialized = true it.initialized = true
manga.id = insertManga(manga) it.id = insertManga(manga)
manga }
})
} }
/** /**

View File

@ -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.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import rx.Observable import eu.kanade.tachiyomi.util.lang.launchIO
import java.util.Date import java.util.Date
class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) { class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(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 manga manga that needs updating
* @param chapters chapters of 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<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
backupManager.restoreMangaFetchObservable(source, manga) launchIO {
.onErrorReturn { try {
errors.add(Date() to "${manga.title} - ${it.message}") val fetchedManga = backupManager.fetchManga(source, manga)
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( private fun restoreMangaNoFetch(
@ -179,22 +175,15 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
history: List<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
Observable.just(backupManga) launchIO {
.flatMap { manga -> if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(manga, chapters)) { updateChapters(source, backupManga, chapters)
chapterFetchObservable(source, manga, chapters)
.map { manga }
} else {
Observable.just(manga)
}
} }
.doOnNext {
restoreExtraForManga(it, categories, history, tracks) restoreExtraForManga(backupManga, categories, history, tracks)
}
.flatMap { manga -> updateTracking(backupManga, tracks)
trackingFetchObservable(manga, tracks) }
}
.subscribe()
} }
private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) { private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) {

View File

@ -211,7 +211,7 @@ class BackupTest {
networkManga.description = "This is a description" networkManga.description = "This is a description"
`when`(source.fetchMangaDetails(jsonManga)).thenReturn(Observable.just(networkManga)) `when`(source.fetchMangaDetails(jsonManga)).thenReturn(Observable.just(networkManga))
val obs = legacyBackupManager.restoreMangaFetchObservable(source, jsonManga) val obs = legacyBackupManager.fetchManga(source, jsonManga)
val testSubscriber = TestSubscriber<Manga>() val testSubscriber = TestSubscriber<Manga>()
obs.subscribe(testSubscriber) obs.subscribe(testSubscriber)
@ -255,7 +255,7 @@ class BackupTest {
`when`(source.fetchChapterList(manga)).thenReturn(Observable.just(chaptersRemote)) `when`(source.fetchChapterList(manga)).thenReturn(Observable.just(chaptersRemote))
// Call restoreChapterFetchObservable // Call restoreChapterFetchObservable
val obs = legacyBackupManager.restoreChapterFetchObservable(source, manga, restoredChapters) val obs = legacyBackupManager.restoreChapters(source, manga, restoredChapters)
val testSubscriber = TestSubscriber<Pair<List<Chapter>, List<Chapter>>>() val testSubscriber = TestSubscriber<Pair<List<Chapter>, List<Chapter>>>()
obs.subscribe(testSubscriber) obs.subscribe(testSubscriber)