mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Remove usage of RxJava from LibraryUpdateService
This commit is contained in:
parent
628bd5d6b4
commit
86b9d7e843
1 changed files with 114 additions and 145 deletions
|
@ -27,15 +27,16 @@ 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.util.chapter.NoChaptersException
|
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
import eu.kanade.tachiyomi.util.system.acquireWakeLock
|
||||||
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
import eu.kanade.tachiyomi.util.system.isServiceRunning
|
||||||
import rx.Observable
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import rx.Subscription
|
import kotlinx.coroutines.Job
|
||||||
import rx.schedulers.Schedulers
|
import kotlinx.coroutines.MainScope
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -59,17 +60,11 @@ class LibraryUpdateService(
|
||||||
val coverCache: CoverCache = Injekt.get()
|
val coverCache: CoverCache = Injekt.get()
|
||||||
) : Service() {
|
) : Service() {
|
||||||
|
|
||||||
/**
|
|
||||||
* Wake lock that will be held until the service is destroyed.
|
|
||||||
*/
|
|
||||||
private lateinit var wakeLock: PowerManager.WakeLock
|
private lateinit var wakeLock: PowerManager.WakeLock
|
||||||
|
|
||||||
private lateinit var notifier: LibraryUpdateNotifier
|
private lateinit var notifier: LibraryUpdateNotifier
|
||||||
|
private lateinit var scope: CoroutineScope
|
||||||
|
|
||||||
/**
|
private var updateJob: Job? = null
|
||||||
* Subscription where the update is done.
|
|
||||||
*/
|
|
||||||
private var subscription: Subscription? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines what should be updated within a service execution.
|
* Defines what should be updated within a service execution.
|
||||||
|
@ -144,6 +139,7 @@ class LibraryUpdateService(
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
scope = MainScope()
|
||||||
notifier = LibraryUpdateNotifier(this)
|
notifier = LibraryUpdateNotifier(this)
|
||||||
wakeLock = acquireWakeLock(javaClass.name)
|
wakeLock = acquireWakeLock(javaClass.name)
|
||||||
|
|
||||||
|
@ -155,7 +151,8 @@ class LibraryUpdateService(
|
||||||
* lock.
|
* lock.
|
||||||
*/
|
*/
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
subscription?.unsubscribe()
|
scope?.cancel()
|
||||||
|
updateJob?.cancel()
|
||||||
if (wakeLock.isHeld) {
|
if (wakeLock.isHeld) {
|
||||||
wakeLock.release()
|
wakeLock.release()
|
||||||
}
|
}
|
||||||
|
@ -183,34 +180,27 @@ class LibraryUpdateService(
|
||||||
?: return START_NOT_STICKY
|
?: return START_NOT_STICKY
|
||||||
|
|
||||||
// Unsubscribe from any previous subscription if needed.
|
// Unsubscribe from any previous subscription if needed.
|
||||||
subscription?.unsubscribe()
|
updateJob?.cancel()
|
||||||
|
|
||||||
// Update favorite manga. Destroy service when completed or in case of an error.
|
// Update favorite manga. Destroy service when completed or in case of an error.
|
||||||
subscription = Observable
|
val selectedScheme = preferences.libraryUpdatePrioritization().get()
|
||||||
.defer {
|
val mangaList = getMangaToUpdate(intent, target)
|
||||||
val selectedScheme = preferences.libraryUpdatePrioritization().get()
|
.sortedWith(rankingScheme[selectedScheme])
|
||||||
val mangaList = getMangaToUpdate(intent, target)
|
|
||||||
.sortedWith(rankingScheme[selectedScheme])
|
|
||||||
|
|
||||||
// Update either chapter list or manga details.
|
updateJob = scope.launchIO {
|
||||||
|
try {
|
||||||
when (target) {
|
when (target) {
|
||||||
Target.CHAPTERS -> updateChapterList(mangaList)
|
Target.CHAPTERS -> updateChapterList(mangaList)
|
||||||
Target.COVERS -> updateCovers(mangaList)
|
Target.COVERS -> updateCovers(mangaList)
|
||||||
Target.TRACKING -> updateTrackings(mangaList)
|
Target.TRACKING -> updateTrackings(mangaList)
|
||||||
}
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e)
|
||||||
|
stopSelf(startId)
|
||||||
|
} finally {
|
||||||
|
stopSelf(startId)
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
}
|
||||||
.subscribe(
|
|
||||||
{
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Timber.e(it)
|
|
||||||
stopSelf(startId)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stopSelf(startId)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
}
|
}
|
||||||
|
@ -253,7 +243,7 @@ class LibraryUpdateService(
|
||||||
* @param mangaToUpdate the list to update
|
* @param mangaToUpdate the list to update
|
||||||
* @return an observable delivering the progress of each update.
|
* @return an observable delivering the progress of each update.
|
||||||
*/
|
*/
|
||||||
fun updateChapterList(mangaToUpdate: List<LibraryManga>): Observable<LibraryManga> {
|
suspend fun updateChapterList(mangaToUpdate: List<LibraryManga>) {
|
||||||
// Initialize the variables holding the progress of the updates.
|
// Initialize the variables holding the progress of the updates.
|
||||||
val count = AtomicInteger(0)
|
val count = AtomicInteger(0)
|
||||||
// List containing new updates
|
// List containing new updates
|
||||||
|
@ -263,67 +253,60 @@ class LibraryUpdateService(
|
||||||
// Boolean to determine if DownloadManager has downloads
|
// Boolean to determine if DownloadManager has downloads
|
||||||
var hasDownloads = false
|
var hasDownloads = false
|
||||||
|
|
||||||
// Emit each manga and update it sequentially.
|
mangaToUpdate
|
||||||
return Observable.from(mangaToUpdate)
|
.map { manga ->
|
||||||
// Notify manga that will update.
|
// Notify manga that will update.
|
||||||
.doOnNext { notifier.showProgressNotification(it, count.andIncrement, mangaToUpdate.size) }
|
notifier.showProgressNotification(manga, count.andIncrement, mangaToUpdate.size)
|
||||||
// Update the chapters of the manga
|
|
||||||
.concatMap { manga ->
|
// Update the chapters of the manga
|
||||||
updateManga(manga)
|
try {
|
||||||
|
val newChapters = updateManga(manga).first
|
||||||
|
Pair(manga, newChapters)
|
||||||
|
} catch (e: Throwable) {
|
||||||
// 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) {
|
getString(R.string.no_chapters_error)
|
||||||
getString(R.string.no_chapters_error)
|
} else {
|
||||||
} else {
|
e.message
|
||||||
it.message
|
|
||||||
}
|
|
||||||
failedUpdates.add(Pair(manga, errorMessage))
|
|
||||||
Pair(emptyList(), emptyList())
|
|
||||||
}
|
|
||||||
// Filter out mangas without new chapters (or failed).
|
|
||||||
.filter { (first) -> first.isNotEmpty() }
|
|
||||||
.doOnNext {
|
|
||||||
if (manga.shouldDownloadNewChapters(db, preferences)) {
|
|
||||||
downloadChapters(manga, it.first)
|
|
||||||
hasDownloads = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Convert to the manga that contains new chapters.
|
|
||||||
.map {
|
|
||||||
Pair(
|
|
||||||
manga,
|
|
||||||
(
|
|
||||||
it.first.sortedByDescending { ch -> ch.source_order }
|
|
||||||
.toTypedArray()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
failedUpdates.add(Pair(manga, errorMessage))
|
||||||
|
Pair(manga, emptyList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add manga with new chapters to the list.
|
// Filter out mangas without new chapters (or failed).
|
||||||
.doOnNext { manga ->
|
.filter { (_, newChapters) -> newChapters.isNotEmpty() }
|
||||||
// Add to the list
|
.forEach { (manga, newChapters) ->
|
||||||
newUpdates.add(manga)
|
if (manga.shouldDownloadNewChapters(db, preferences)) {
|
||||||
}
|
downloadChapters(manga, newChapters)
|
||||||
// Notify result of the overall update.
|
hasDownloads = true
|
||||||
.doOnCompleted {
|
|
||||||
notifier.cancelProgressNotification()
|
|
||||||
|
|
||||||
if (newUpdates.isNotEmpty()) {
|
|
||||||
notifier.showUpdateNotifications(newUpdates)
|
|
||||||
if (hasDownloads) {
|
|
||||||
DownloadService.start(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferences.showLibraryUpdateErrors() && failedUpdates.isNotEmpty()) {
|
// Convert to the manga that contains new chapters.
|
||||||
val errorFile = writeErrorFile(failedUpdates)
|
newUpdates.add(
|
||||||
notifier.showUpdateErrorNotification(
|
Pair(
|
||||||
failedUpdates.map { it.first.title },
|
manga,
|
||||||
errorFile.getUriCompat(this)
|
newChapters.sortedByDescending { ch -> ch.source_order }.toTypedArray()
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
.map { (first) -> first }
|
|
||||||
|
// Notify result of the overall update.
|
||||||
|
notifier.cancelProgressNotification()
|
||||||
|
|
||||||
|
if (newUpdates.isNotEmpty()) {
|
||||||
|
notifier.showUpdateNotifications(newUpdates)
|
||||||
|
if (hasDownloads) {
|
||||||
|
DownloadService.start(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferences.showLibraryUpdateErrors() && failedUpdates.isNotEmpty()) {
|
||||||
|
val errorFile = writeErrorFile(failedUpdates)
|
||||||
|
notifier.showUpdateErrorNotification(
|
||||||
|
failedUpdates.map { it.first.title },
|
||||||
|
errorFile.getUriCompat(this)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
||||||
|
@ -338,49 +321,38 @@ class LibraryUpdateService(
|
||||||
* @param manga the manga to update.
|
* @param manga the manga to update.
|
||||||
* @return a pair of the inserted and removed chapters.
|
* @return a pair of the inserted and removed chapters.
|
||||||
*/
|
*/
|
||||||
fun updateManga(manga: Manga): Observable<Pair<List<Chapter>, List<Chapter>>> {
|
suspend fun updateManga(manga: Manga): Pair<List<Chapter>, List<Chapter>> {
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
|
|
||||||
// Update manga details metadata in the background
|
// Update manga details metadata in the background
|
||||||
if (preferences.autoUpdateMetadata()) {
|
if (preferences.autoUpdateMetadata()) {
|
||||||
runAsObservable({
|
val updatedManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
val updatedManga = source.getMangaDetails(manga.toMangaInfo())
|
val sManga = updatedManga.toSManga()
|
||||||
val sManga = updatedManga.toSManga()
|
// Avoid "losing" existing cover
|
||||||
// Avoid "losing" existing cover
|
if (!sManga.thumbnail_url.isNullOrEmpty()) {
|
||||||
if (!sManga.thumbnail_url.isNullOrEmpty()) {
|
manga.prepUpdateCover(coverCache, sManga, false)
|
||||||
manga.prepUpdateCover(coverCache, sManga, false)
|
} else {
|
||||||
} else {
|
sManga.thumbnail_url = manga.thumbnail_url
|
||||||
sManga.thumbnail_url = manga.thumbnail_url
|
}
|
||||||
}
|
|
||||||
|
|
||||||
manga.copyFrom(sManga)
|
manga.copyFrom(sManga)
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeAsBlocking()
|
||||||
manga
|
|
||||||
})
|
|
||||||
.onErrorResumeNext { Observable.just(manga) }
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.subscribe()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return runAsObservable({
|
val chapters = source.getChapterList(manga.toMangaInfo())
|
||||||
source.getChapterList(manga.toMangaInfo())
|
.map { it.toSChapter() }
|
||||||
.map { it.toSChapter() }
|
|
||||||
})
|
return syncChaptersWithSource(db, chapters, manga, source)
|
||||||
.map { syncChaptersWithSource(db, it, manga, source) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCovers(mangaToUpdate: List<LibraryManga>): Observable<LibraryManga> {
|
private suspend fun updateCovers(mangaToUpdate: List<LibraryManga>) {
|
||||||
var count = 0
|
var count = 0
|
||||||
|
|
||||||
return Observable.from(mangaToUpdate)
|
mangaToUpdate.forEach { manga ->
|
||||||
.doOnNext {
|
notifier.showProgressNotification(manga, count++, mangaToUpdate.size)
|
||||||
notifier.showProgressNotification(it, count++, mangaToUpdate.size)
|
|
||||||
}
|
|
||||||
.flatMap { manga ->
|
|
||||||
val source = sourceManager.get(manga.source)
|
|
||||||
?: return@flatMap Observable.empty<LibraryManga>()
|
|
||||||
|
|
||||||
runAsObservable({
|
sourceManager.get(manga.source)?.let { source ->
|
||||||
|
try {
|
||||||
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
val sManga = networkManga.toSManga()
|
val sManga = networkManga.toSManga()
|
||||||
manga.prepUpdateCover(coverCache, sManga, true)
|
manga.prepUpdateCover(coverCache, sManga, true)
|
||||||
|
@ -388,49 +360,46 @@ class LibraryUpdateService(
|
||||||
manga.thumbnail_url = it
|
manga.thumbnail_url = it
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeAsBlocking()
|
||||||
}
|
}
|
||||||
manga
|
} catch (e: Throwable) {
|
||||||
})
|
// Ignore errors and continue
|
||||||
.onErrorReturn { manga }
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
.doOnCompleted {
|
|
||||||
notifier.cancelProgressNotification()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier.cancelProgressNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that updates the metadata of the connected tracking services. It's called in a
|
* Method that updates the metadata of the connected tracking services. It's called in a
|
||||||
* background thread, so it's safe to do heavy operations or network calls here.
|
* background thread, so it's safe to do heavy operations or network calls here.
|
||||||
*/
|
*/
|
||||||
private fun updateTrackings(mangaToUpdate: List<LibraryManga>): Observable<LibraryManga> {
|
private suspend fun updateTrackings(mangaToUpdate: List<LibraryManga>) {
|
||||||
// Initialize the variables holding the progress of the updates.
|
// Initialize the variables holding the progress of the updates.
|
||||||
var count = 0
|
var count = 0
|
||||||
|
|
||||||
val loggedServices = trackManager.services.filter { it.isLogged }
|
val loggedServices = trackManager.services.filter { it.isLogged }
|
||||||
|
|
||||||
// Emit each manga and update it sequentially.
|
mangaToUpdate.forEach { manga ->
|
||||||
return Observable.from(mangaToUpdate)
|
|
||||||
// Notify manga that will update.
|
// Notify manga that will update.
|
||||||
.doOnNext { notifier.showProgressNotification(it, count++, mangaToUpdate.size) }
|
notifier.showProgressNotification(manga, count++, mangaToUpdate.size)
|
||||||
// Update the tracking details.
|
|
||||||
.concatMap { manga ->
|
|
||||||
val tracks = db.getTracks(manga).executeAsBlocking()
|
|
||||||
|
|
||||||
Observable.from(tracks)
|
// Update the tracking details.
|
||||||
.concatMap { track ->
|
db.getTracks(manga).executeAsBlocking().forEach { track ->
|
||||||
val service = trackManager.getService(track.sync_id)
|
val service = trackManager.getService(track.sync_id)
|
||||||
if (service != null && service in loggedServices) {
|
if (service != null && service in loggedServices) {
|
||||||
runAsObservable({ service.refresh(track) })
|
try {
|
||||||
.doOnNext { db.insertTrack(it).executeAsBlocking() }
|
val updatedTrack = service.refresh(track)
|
||||||
.onErrorReturn { track }
|
db.insertTrack(updatedTrack).executeAsBlocking()
|
||||||
} else {
|
} catch (e: Throwable) {
|
||||||
Observable.empty()
|
// Ignore errors and continue
|
||||||
}
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
.map { manga }
|
}
|
||||||
}
|
|
||||||
.doOnCompleted {
|
|
||||||
notifier.cancelProgressNotification()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier.cancelProgressNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue