mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 20:01:53 +01:00
Do library updates from up to 5 sources concurrently (but in coroutines and queue system)
0 help from arkon on this one
This commit is contained in:
parent
7a8f9373ba
commit
f3d4e87542
@ -43,7 +43,12 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
@ -100,11 +105,28 @@ class LibraryUpdateService(
|
||||
|
||||
private val mangaToUpdate = mutableListOf<LibraryManga>()
|
||||
|
||||
private val mangaToUpdateMap = mutableMapOf<Long, List<LibraryManga>>()
|
||||
|
||||
private val categoryIds = mutableSetOf<Int>()
|
||||
|
||||
// List containing new updates
|
||||
private val newUpdates = mutableMapOf<LibraryManga, Array<Chapter>>()
|
||||
|
||||
val count = AtomicInteger(0)
|
||||
val jobCount = AtomicInteger(0)
|
||||
|
||||
// List containing categories that get included in downloads.
|
||||
private val categoriesToDownload =
|
||||
preferences.downloadNewCategories().getOrDefault().map(String::toInt)
|
||||
|
||||
// Boolean to determine if user wants to automatically download new chapters.
|
||||
private val downloadNew: Boolean = preferences.downloadNew().getOrDefault()
|
||||
|
||||
// Boolean to determine if DownloadManager has downloads
|
||||
private var hasDownloads = false
|
||||
|
||||
private val requestSemaphore = Semaphore(5)
|
||||
|
||||
// For updates delete removed chapters if not preference is set as well
|
||||
private val deleteRemoved by lazy {
|
||||
preferences.deleteRemovedChapters().get() != 1
|
||||
@ -116,15 +138,10 @@ class LibraryUpdateService(
|
||||
private val progressNotification by lazy {
|
||||
NotificationCompat.Builder(this, Notifications.CHANNEL_LIBRARY)
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
||||
.setLargeIcon(notificationBitmap)
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
||||
.addAction(
|
||||
R.drawable.ic_clear_grey_24dp_img,
|
||||
getString(android.R.string.cancel),
|
||||
cancelIntent
|
||||
.setSmallIcon(R.drawable.ic_refresh_white_24dp_img).setLargeIcon(notificationBitmap)
|
||||
.setOngoing(true).setOnlyAlertOnce(true)
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorAccent)).addAction(
|
||||
R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent
|
||||
)
|
||||
}
|
||||
|
||||
@ -132,6 +149,7 @@ class LibraryUpdateService(
|
||||
* Defines what should be updated within a service execution.
|
||||
*/
|
||||
enum class Target {
|
||||
|
||||
CHAPTERS, // Manga chapters
|
||||
DETAILS, // Manga metadata
|
||||
TRACKING // Tracking metadata
|
||||
@ -206,14 +224,38 @@ class LibraryUpdateService(
|
||||
}
|
||||
|
||||
fun removeListener(listener: LibraryServiceListener) {
|
||||
if (this.listener == listener)
|
||||
this.listener = null
|
||||
if (this.listener == listener) this.listener = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun addManga(mangaToAdd: List<LibraryManga>) {
|
||||
for (manga in mangaToAdd) {
|
||||
if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga)
|
||||
val distinctManga = mangaToAdd.filter { it !in mangaToUpdate }
|
||||
mangaToUpdate.addAll(distinctManga)
|
||||
distinctManga.groupBy { it.source }.forEach {
|
||||
// if added queue items is a new source not in the async list or an async list has
|
||||
// finished running
|
||||
if (mangaToUpdateMap[it.key].isNullOrEmpty()) {
|
||||
mangaToUpdateMap[it.key] = it.value
|
||||
jobCount.andIncrement
|
||||
val handler = CoroutineExceptionHandler { _, exception ->
|
||||
Timber.e(exception)
|
||||
}
|
||||
GlobalScope.launch(handler) {
|
||||
val hasDLs = requestSemaphore.withPermit {
|
||||
updateMangaInSource(
|
||||
it.key, downloadNew, categoriesToDownload
|
||||
)
|
||||
}
|
||||
hasDownloads = hasDownloads || hasDLs
|
||||
jobCount.andDecrement
|
||||
if (job?.isCancelled != false) {
|
||||
finishUpdates()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val list = mangaToUpdateMap[it.key] ?: emptyList()
|
||||
mangaToUpdateMap[it.key] = (list + it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,50 +397,73 @@ class LibraryUpdateService(
|
||||
}
|
||||
|
||||
private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) {
|
||||
// List containing categories that get included in downloads.
|
||||
val categoriesToDownload =
|
||||
preferences.downloadNewCategories().getOrDefault().map(String::toInt)
|
||||
// Boolean to determine if user wants to automatically download new chapters.
|
||||
val downloadNew = preferences.downloadNew().getOrDefault()
|
||||
// Boolean to determine if DownloadManager has downloads
|
||||
var hasDownloads = false
|
||||
// Initialize the variables holding the progress of the updates.
|
||||
var count = 0
|
||||
|
||||
mangaToUpdate.addAll(mangaToAdd)
|
||||
while (count < mangaToUpdate.size) {
|
||||
val shouldDownload = (downloadNew && (categoriesToDownload.isEmpty() ||
|
||||
mangaToUpdate[count].category in categoriesToDownload ||
|
||||
db.getCategoriesForManga(mangaToUpdate[count]).executeOnIO()
|
||||
.any { (it.id ?: -1) in categoriesToDownload }))
|
||||
if (updateMangaChapters(mangaToUpdate[count], count, shouldDownload)) {
|
||||
hasDownloads = true
|
||||
mangaToUpdateMap.putAll(mangaToAdd.groupBy { it.source })
|
||||
coroutineScope {
|
||||
jobCount.andIncrement
|
||||
val list = mangaToUpdateMap.keys.map { source ->
|
||||
async {
|
||||
requestSemaphore.withPermit {
|
||||
updateMangaInSource(source, downloadNew, categoriesToDownload)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
val results = list.awaitAll()
|
||||
hasDownloads = hasDownloads || results.any { it }
|
||||
jobCount.andDecrement
|
||||
finishUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishUpdates() {
|
||||
if (jobCount.get() != 0) return
|
||||
if (newUpdates.isNotEmpty()) {
|
||||
showResultNotification(newUpdates)
|
||||
|
||||
if (preferences.refreshCoversToo().getOrDefault() && job?.isCancelled == false) {
|
||||
updateDetails(newUpdates.map { it.key }).observeOn(Schedulers.io())
|
||||
.doOnCompleted {
|
||||
updateDetails(newUpdates.map { it.key }).observeOn(Schedulers.io()).doOnCompleted {
|
||||
cancelProgressNotification()
|
||||
if (downloadNew && hasDownloads) {
|
||||
DownloadService.start(this)
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io()).subscribe {}
|
||||
}.subscribeOn(Schedulers.io()).subscribe {}
|
||||
} else if (downloadNew && hasDownloads) {
|
||||
DownloadService.start(this)
|
||||
}
|
||||
}
|
||||
|
||||
cancelProgressNotification()
|
||||
}
|
||||
|
||||
private suspend fun updateMangaInSource(
|
||||
source: Long,
|
||||
downloadNew: Boolean,
|
||||
categoriesToDownload: List<Int>
|
||||
): Boolean {
|
||||
if (mangaToUpdateMap[source] == null) return false
|
||||
var count = 0
|
||||
var hasDownloads = false
|
||||
while (count < mangaToUpdateMap[source]!!.size) {
|
||||
val shouldDownload =
|
||||
(downloadNew && (categoriesToDownload.isEmpty() || mangaToUpdateMap[source]!![count].category in categoriesToDownload || db.getCategoriesForManga(
|
||||
mangaToUpdateMap[source]!![count]
|
||||
).executeOnIO().any { (it.id ?: -1) in categoriesToDownload }))
|
||||
if (updateMangaChapters(
|
||||
mangaToUpdateMap[source]!![count], this.count.andIncrement, shouldDownload
|
||||
)
|
||||
) {
|
||||
hasDownloads = true
|
||||
}
|
||||
count++
|
||||
}
|
||||
mangaToUpdateMap[source] = emptyList()
|
||||
return hasDownloads
|
||||
}
|
||||
|
||||
private suspend fun updateMangaChapters(
|
||||
manga: LibraryManga,
|
||||
progess: Int,
|
||||
progress: Int,
|
||||
shouldDownload: Boolean
|
||||
):
|
||||
Boolean {
|
||||
@ -407,7 +472,7 @@ class LibraryUpdateService(
|
||||
if (job?.isCancelled == true) {
|
||||
throw java.lang.Exception("Job was cancelled")
|
||||
}
|
||||
showProgressNotification(manga, progess, mangaToUpdate.size)
|
||||
showProgressNotification(manga, progress, mangaToUpdate.size)
|
||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return false
|
||||
val fetchedChapters = withContext(Dispatchers.IO) {
|
||||
source.fetchChapterList(manga).toBlocking().single()
|
||||
@ -441,7 +506,7 @@ class LibraryUpdateService(
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
// we need to get the chapters from the db so we have chapter ids
|
||||
val mangaChapters = db.getChapters(manga).executeAsBlocking()
|
||||
val dbChapters = chapters.map {
|
||||
|
Loading…
Reference in New Issue
Block a user