Clean up library download chapters logic

We can probably clean up the same logic in the manga controller at some point too, but that stuff's messy.
Also fixes the spacing issue that the new icon introduced.

(cherry picked from commit 33e90d64497f920be825b803e1342a2e6c937111)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt
This commit is contained in:
arkon 2022-10-30 22:56:07 -04:00 committed by Jobobby04
parent 35bd6233d9
commit b2565c7f8b
12 changed files with 174 additions and 181 deletions

View File

@ -120,7 +120,7 @@ class SYDomainModule : InjektModule {
addFactory { GetMergedManga(get()) } addFactory { GetMergedManga(get()) }
addFactory { GetMergedMangaById(get()) } addFactory { GetMergedMangaById(get()) }
addFactory { GetMergedReferencesById(get()) } addFactory { GetMergedReferencesById(get()) }
addFactory { GetMergedChapterByMangaId(get()) } addFactory { GetMergedChapterByMangaId(get(), get(), get()) }
addFactory { InsertMergedReference(get()) } addFactory { InsertMergedReference(get()) }
addFactory { UpdateMergedSettings(get()) } addFactory { UpdateMergedSettings(get()) }
addFactory { DeleteByMergeId(get()) } addFactory { DeleteByMergeId(get()) }

View File

@ -2,16 +2,39 @@ package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.repository.ChapterRepository import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.merged.sql.models.MergedMangaReference
import exh.source.MERGED_SOURCE_ID
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import logcat.LogPriority import logcat.LogPriority
class GetMergedChapterByMangaId( class GetMergedChapterByMangaId(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val getMergedReferencesById: GetMergedReferencesById,
private val sourceManager: SourceManager,
) { ) {
suspend fun await(mangaId: Long): List<Chapter> { suspend fun await(mangaId: Long, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return transformMergedChapters(mangaId, getFromDatabase(mangaId), editScanlators, dedupe)
}
suspend fun subscribe(mangaId: Long, editScanlators: Boolean = false, dedupe: Boolean = true): Flow<List<Chapter>> {
return try {
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId)
.map {
transformMergedChapters(mangaId, it, editScanlators, dedupe)
}
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
flowOf(emptyList())
}
}
private suspend fun getFromDatabase(mangaId: Long): List<Chapter> {
return try { return try {
chapterRepository.getMergedChapterByMangaId(mangaId) chapterRepository.getMergedChapterByMangaId(mangaId)
} catch (e: Exception) { } catch (e: Exception) {
@ -20,12 +43,54 @@ class GetMergedChapterByMangaId(
} }
} }
suspend fun subscribe(mangaId: Long): Flow<List<Chapter>> { // TODO more chapter dedupe
return try { suspend fun transformMergedChapters(mangaId: Long, chapterList: List<Chapter>, editScanlators: Boolean, dedupe: Boolean): List<Chapter> {
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId) val mangaReferences = getMergedReferencesById.await(mangaId)
} catch (e: Exception) { val chapters = if (editScanlators) {
logcat(LogPriority.ERROR, e) val sources = mangaReferences.map { sourceManager.getOrStub(it.mangaSourceId) to it.mangaId }
flowOf(emptyList()) chapterList.map { chapter ->
val source = sources.firstOrNull { chapter.mangaId == it.second }?.first
if (source != null) {
chapter.copy(
scanlator = if (chapter.scanlator.isNullOrBlank()) {
source.name
} else {
"$source: ${chapter.scanlator}"
},
)
} else {
chapter
}
}
} else {
chapterList
}
return if (dedupe) dedupeChapterList(mangaReferences, chapters) else chapters
}
private fun dedupeChapterList(mangaReferences: List<MergedMangaReference>, chapterList: List<Chapter>): List<Chapter> {
return when (mangaReferences.firstOrNull { it.mangaSourceId == MERGED_SOURCE_ID }?.chapterSortMode) {
MergedMangaReference.CHAPTER_SORT_NO_DEDUPE, MergedMangaReference.CHAPTER_SORT_NONE -> chapterList
MergedMangaReference.CHAPTER_SORT_PRIORITY -> chapterList
MergedMangaReference.CHAPTER_SORT_MOST_CHAPTERS -> {
findSourceWithMostChapters(chapterList)?.let { mangaId ->
chapterList.filter { it.mangaId == mangaId }
} ?: chapterList
}
MergedMangaReference.CHAPTER_SORT_HIGHEST_CHAPTER_NUMBER -> {
findSourceWithHighestChapterNumber(chapterList)?.let { mangaId ->
chapterList.filter { it.mangaId == mangaId }
} ?: chapterList
}
else -> chapterList
} }
} }
private fun findSourceWithMostChapters(chapterList: List<Chapter>): Long? {
return chapterList.groupBy { it.mangaId }.maxByOrNull { it.value.size }?.key
}
private fun findSourceWithHighestChapterNumber(chapterList: List<Chapter>): Long? {
return chapterList.maxByOrNull { it.chapterNumber }?.mangaId
}
} }

View File

@ -7,8 +7,6 @@ import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.download.service.DownloadPreferences import eu.kanade.domain.download.service.DownloadPreferences
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
@ -19,7 +17,9 @@ class SetReadStatus(
private val deleteDownload: DeleteDownload, private val deleteDownload: DeleteDownload,
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,
private val sourceManager: SourceManager, // SY -->
private val getMergedChapterByMangaId: GetMergedChapterByMangaId,
// SY <--
) { ) {
private val mapper = { chapter: Chapter, read: Boolean -> private val mapper = { chapter: Chapter, read: Boolean ->
@ -75,11 +75,10 @@ class SetReadStatus(
// SY --> // SY -->
private suspend fun awaitMerged(mangaId: Long, read: Boolean) = withNonCancellableContext f@{ private suspend fun awaitMerged(mangaId: Long, read: Boolean) = withNonCancellableContext f@{
val mergedSource = sourceManager.get(MERGED_SOURCE_ID) as MergedSource
return@f await( return@f await(
read = read, read = read,
chapters = mergedSource chapters = getMergedChapterByMangaId
.getChapters(mangaId, dedupe = false) .await(mangaId, dedupe = false)
.toTypedArray(), .toTypedArray(),
) )
} }

View File

@ -1,15 +1,19 @@
package eu.kanade.domain.history.interactor package eu.kanade.domain.history.interactor
import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.history.repository.HistoryRepository import eu.kanade.domain.history.repository.HistoryRepository
import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.tachiyomi.util.chapter.getChapterSort import eu.kanade.tachiyomi.util.chapter.getChapterSort
import exh.source.MERGED_SOURCE_ID
import kotlin.math.max
class GetNextUnreadChapters( class GetNextUnreadChapters(
private val getChapter: GetChapter,
private val getChapterByMangaId: GetChapterByMangaId, private val getChapterByMangaId: GetChapterByMangaId,
// SY -->
private val getMergedChapterByMangaId: GetMergedChapterByMangaId,
// SY <--
private val getManga: GetManga, private val getManga: GetManga,
private val historyRepository: HistoryRepository, private val historyRepository: HistoryRepository,
) { ) {
@ -19,15 +23,23 @@ class GetNextUnreadChapters(
return await(history.mangaId, history.chapterId).firstOrNull() return await(history.mangaId, history.chapterId).firstOrNull()
} }
suspend fun await(mangaId: Long, chapterId: Long): List<Chapter> { suspend fun await(mangaId: Long): List<Chapter> {
val chapter = getChapter.await(chapterId) ?: return emptyList()
val manga = getManga.await(mangaId) ?: return emptyList() val manga = getManga.await(mangaId) ?: return emptyList()
// SY -->
val chapters = getChapterByMangaId.await(mangaId) if (manga.source == MERGED_SOURCE_ID) {
return getMergedChapterByMangaId.await(mangaId)
.sortedWith(getChapterSort(manga, sortDescending = false))
.filterNot { it.read }
}
// SY <--
return getChapterByMangaId.await(mangaId)
.sortedWith(getChapterSort(manga, sortDescending = false)) .sortedWith(getChapterSort(manga, sortDescending = false))
val currChapterIndex = chapters.indexOfFirst { chapter.id == it.id }
return chapters
.subList(currChapterIndex, chapters.size)
.filterNot { it.read } .filterNot { it.read }
} }
suspend fun await(mangaId: Long, fromChapterId: Long): List<Chapter> {
val unreadChapters = await(mangaId)
val currChapterIndex = unreadChapters.indexOfFirst { it.id == fromChapterId }
return unreadChapters.subList(max(0, currChapterIndex), unreadChapters.size)
}
} }

View File

@ -9,7 +9,6 @@ import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
@ -179,6 +178,7 @@ private fun RowScope.Button(
toConfirm: Boolean, toConfirm: Boolean,
onLongClick: () -> Unit, onLongClick: () -> Unit,
onClick: () -> Unit, onClick: () -> Unit,
content: (@Composable () -> Unit)? = null,
) { ) {
val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f) val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f)
Column( Column(
@ -210,6 +210,7 @@ private fun RowScope.Button(
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
) )
} }
content?.invoke()
} }
} }
@ -272,15 +273,14 @@ fun LibraryBottomActionMenu(
) )
} }
if (onDownloadClicked != null) { if (onDownloadClicked != null) {
Box { var downloadExpanded by remember { mutableStateOf(false) }
var downloadExpanded by remember { mutableStateOf(false) } Button(
this@Row.Button( title = stringResource(R.string.action_download),
title = stringResource(R.string.action_download), icon = Icons.Outlined.Download,
icon = Icons.Outlined.Download, toConfirm = confirm[3],
toConfirm = confirm[3], onLongClick = { onLongClickItem(3) },
onLongClick = { onLongClickItem(3) }, onClick = { downloadExpanded = !downloadExpanded },
onClick = { downloadExpanded = !downloadExpanded }, ) {
)
val onDismissRequest = { downloadExpanded = false } val onDismissRequest = { downloadExpanded = false }
DownloadDropdownMenu( DownloadDropdownMenu(
expanded = downloadExpanded, expanded = downloadExpanded,

View File

@ -72,7 +72,7 @@ fun LibraryComfortableGrid(
) )
}, },
// SY --> // SY -->
buttonBottom = if (showStartReadingButton) { buttonBottom = if (showStartReadingButton && libraryItem.unreadCount > 0) {
{ StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) } { StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) }
} else { } else {
null null

View File

@ -73,12 +73,12 @@ fun LibraryCompactGrid(
) )
}, },
// SY --> // SY -->
buttonTop = if (showStartReadingButton && showTitle) { buttonTop = if (showStartReadingButton && showTitle && libraryItem.unreadCount > 0) {
{ StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) } { StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) }
} else { } else {
null null
}, },
buttonBottom = if (showStartReadingButton && !showTitle) { buttonBottom = if (showStartReadingButton && !showTitle && libraryItem.unreadCount > 0) {
{ StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) } { StartReadingButton(onOpenReader = { onOpenReader(libraryItem.libraryManga) }) }
} else { } else {
null null

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.source.online.all package eu.kanade.tachiyomi.source.online.all
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
@ -27,19 +26,16 @@ import exh.source.MERGED_SOURCE_ID
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class MergedSource : HttpSource() { class MergedSource : HttpSource() {
private val getManga: GetManga by injectLazy() private val getManga: GetManga by injectLazy()
private val getMergedReferencesById: GetMergedReferencesById by injectLazy() private val getMergedReferencesById: GetMergedReferencesById by injectLazy()
private val getMergedChaptersByMangaId: GetMergedChapterByMangaId by injectLazy() private val syncChaptersWithSource: SyncChaptersWithSource by injectLazy()
private val networkToLocalManga: NetworkToLocalManga by injectLazy() private val networkToLocalManga: NetworkToLocalManga by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
@ -76,18 +72,12 @@ class MergedSource : HttpSource() {
override suspend fun getMangaDetails(manga: SManga): SManga { override suspend fun getMangaDetails(manga: SManga): SManga {
return withIOContext { return withIOContext {
val mergedManga = getManga.await(manga.url, id) ?: throw Exception("merged manga not in db") val mergedManga = requireNotNull(getManga.await(manga.url, id)) { "merged manga not in db" }
val mangaReferences = getMergedReferencesById.await(mergedManga.id) val mangaReferences = getMergedReferencesById.await(mergedManga.id)
.apply { .apply {
if (isEmpty()) { require(isNotEmpty()) { "Manga references are empty, info unavailable, merge is likely corrupted" }
throw IllegalArgumentException( require(!(size == 1 && first().mangaSourceId == MERGED_SOURCE_ID)) {
"Manga references are empty, info unavailable, merge is likely corrupted", "Manga references contain only the merged reference, merge is likely corrupted"
)
}
if (size == 1 && first().mangaSourceId == MERGED_SOURCE_ID) {
throw IllegalArgumentException(
"Manga references contain only the merged reference, merge is likely corrupted",
)
} }
} }
@ -102,77 +92,14 @@ class MergedSource : HttpSource() {
} }
} }
// TODO more chapter dedupe suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true) {
suspend fun transformMergedChapters(mangaId: Long, chapterList: List<Chapter>, editScanlators: Boolean, dedupe: Boolean): List<Chapter> { fetchChaptersAndSync(manga, downloadChapters)
val mangaReferences = getMergedReferencesById.await(mangaId)
val chapters = if (editScanlators) {
val sources = mangaReferences.map { sourceManager.getOrStub(it.mangaSourceId) to it.mangaId }
chapterList.map { chapter ->
val source = sources.firstOrNull { chapter.mangaId == it.second }?.first
if (source != null) {
chapter.copy(
scanlator = if (chapter.scanlator.isNullOrBlank()) {
source.name
} else {
"$source: ${chapter.scanlator}"
},
)
} else {
chapter
}
}
} else {
chapterList
}
return if (dedupe) dedupeChapterList(mangaReferences, chapters) else chapters
}
fun getChaptersAsBlocking(mangaId: Long, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return runBlocking { getChapters(mangaId, editScanlators, dedupe) }
}
suspend fun getChapters(mangaId: Long, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return transformMergedChapters(mangaId, getMergedChaptersByMangaId.await(mangaId), editScanlators, dedupe)
}
private fun dedupeChapterList(mangaReferences: List<MergedMangaReference>, chapterList: List<Chapter>): List<Chapter> {
return when (mangaReferences.firstOrNull { it.mangaSourceId == MERGED_SOURCE_ID }?.chapterSortMode) {
MergedMangaReference.CHAPTER_SORT_NO_DEDUPE, MergedMangaReference.CHAPTER_SORT_NONE -> chapterList
MergedMangaReference.CHAPTER_SORT_PRIORITY -> chapterList
MergedMangaReference.CHAPTER_SORT_MOST_CHAPTERS -> {
findSourceWithMostChapters(chapterList)?.let { mangaId ->
chapterList.filter { it.mangaId == mangaId }
} ?: chapterList
}
MergedMangaReference.CHAPTER_SORT_HIGHEST_CHAPTER_NUMBER -> {
findSourceWithHighestChapterNumber(chapterList)?.let { mangaId ->
chapterList.filter { it.mangaId == mangaId }
} ?: chapterList
}
else -> chapterList
}
}
private fun findSourceWithMostChapters(chapterList: List<Chapter>): Long? {
return chapterList.groupBy { it.mangaId }.maxByOrNull { it.value.size }?.key
}
private fun findSourceWithHighestChapterNumber(chapterList: List<Chapter>): Long? {
return chapterList.maxByOrNull { it.chapterNumber }?.mangaId
}
suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return withIOContext {
fetchChaptersAndSync(manga, downloadChapters)
getChapters(manga.id, editScanlators, dedupe)
}
} }
suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): List<Chapter> { suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): List<Chapter> {
val syncChaptersWithSource = Injekt.get<SyncChaptersWithSource>()
val mangaReferences = getMergedReferencesById.await(manga.id) val mangaReferences = getMergedReferencesById.await(manga.id)
if (mangaReferences.isEmpty()) { require(mangaReferences.isNotEmpty()) {
throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted") "Manga references are empty, chapters unavailable, merge is likely corrupted"
} }
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(getCategories.await(manga.id).map { it.id }, downloadPreferences) val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(getCategories.await(manga.id).map { it.id }, downloadPreferences)

View File

@ -35,6 +35,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.favorites.FavoritesIntroDialog import exh.favorites.FavoritesIntroDialog
import exh.favorites.FavoritesSyncStatus import exh.favorites.FavoritesSyncStatus
@ -509,10 +510,14 @@ class LibraryController(
private fun startReading(manga: Manga) { private fun startReading(manga: Manga) {
val activity = activity ?: return val activity = activity ?: return
val chapter = presenter.getFirstUnread(manga) ?: return viewScope.launchIO {
val intent = ReaderActivity.newIntent(activity, manga.id, chapter.id) val chapter = presenter.getFirstUnread(manga) ?: return@launchIO
presenter.clearSelection() val intent = ReaderActivity.newIntent(activity, manga.id, chapter.id)
startActivity(intent) presenter.clearSelection()
withUIContext {
startActivity(intent)
}
}
} }
// <-- EXH // <-- EXH
} }

View File

@ -20,11 +20,11 @@ import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.history.interactor.GetNextUnreadChapters
import eu.kanade.domain.library.model.LibraryDisplayMode import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.library.model.LibraryGroup import eu.kanade.domain.library.model.LibraryGroup
import eu.kanade.domain.library.model.LibraryManga import eu.kanade.domain.library.model.LibraryManga
@ -54,7 +54,6 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.toDomainManga import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.library.CustomMangaManager import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackStatus import eu.kanade.tachiyomi.data.track.TrackStatus
@ -65,7 +64,6 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.chapter.getChapterSort
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
@ -82,7 +80,6 @@ import exh.search.SearchEngine
import exh.search.Text import exh.search.Text
import exh.source.EH_SOURCE_ID import exh.source.EH_SOURCE_ID
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.isEhBasedManga
import exh.source.isMetadataSource import exh.source.isMetadataSource
import exh.util.cancellable import exh.util.cancellable
import exh.util.isLewd import exh.util.isLewd
@ -123,7 +120,7 @@ class LibraryPresenter(
private val getLibraryManga: GetLibraryManga = Injekt.get(), private val getLibraryManga: GetLibraryManga = Injekt.get(),
private val getTracksPerManga: GetTracksPerManga = Injekt.get(), private val getTracksPerManga: GetTracksPerManga = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories = Injekt.get(),
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val getNextUnreadChapters: GetNextUnreadChapters = Injekt.get(),
private val setReadStatus: SetReadStatus = Injekt.get(), private val setReadStatus: SetReadStatus = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(),
@ -602,25 +599,6 @@ class LibraryPresenter(
return mangaCategories.flatten().distinct().subtract(common) return mangaCategories.flatten().distinct().subtract(common)
} }
fun shouldDownloadChapter(manga: Manga, chapter: Chapter): Boolean {
val activeDownload = downloadManager.queue.find { chapter.id == it.chapter.id }
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source)
val state = when {
activeDownload != null -> activeDownload.status
downloaded -> Download.State.DOWNLOADED
else -> Download.State.NOT_DOWNLOADED
}
return state == Download.State.NOT_DOWNLOADED
}
suspend fun getNotDownloadedUnreadChapters(manga: Manga): List<Chapter> {
return getChapterByMangaId.await(manga.id)
.filter { chapter ->
!chapter.read && shouldDownloadChapter(manga, chapter)
}
.sortedWith(getChapterSort(manga, sortDescending = false))
}
/** /**
* Queues the amount specified of unread chapters from the list of mangas given. * Queues the amount specified of unread chapters from the list of mangas given.
* *
@ -630,31 +608,48 @@ class LibraryPresenter(
fun downloadUnreadChapters(mangas: List<Manga>, amount: Int?) { fun downloadUnreadChapters(mangas: List<Manga>, amount: Int?) {
presenterScope.launchNonCancellable { presenterScope.launchNonCancellable {
mangas.forEach { manga -> mangas.forEach { manga ->
// SY -->
if (manga.source == MERGED_SOURCE_ID) { if (manga.source == MERGED_SOURCE_ID) {
val mergedSource = sourceManager.get(MERGED_SOURCE_ID) as MergedSource
val mergedMangas = getMergedMangaById.await(manga.id) val mergedMangas = getMergedMangaById.await(manga.id)
mergedSource .associateBy { it.id }
.getChapters(manga.id) getNextUnreadChapters.await(manga.id)
.filter { !it.read }
.let { if (amount != null) it.take(amount) else it } .let { if (amount != null) it.take(amount) else it }
.groupBy { it.mangaId } .groupBy { it.mangaId }
.forEach ab@{ (mangaId, chapters) -> .forEach ab@{ (mangaId, chapters) ->
val mergedManga = mergedMangas.firstOrNull { it.id == mangaId } ?: return@ab val mergedManga = mergedMangas[mangaId] ?: return@ab
downloadManager.downloadChapters(mergedManga, chapters.map(Chapter::toDbChapter)) val downloadChapters = chapters.filterNot { chapter ->
} downloadManager.queue.any { chapter.id == it.chapter.id } ||
} else { downloadManager.isChapterDownloaded(
/* SY --> */ chapter.name,
val chapters = if (manga.isEhBasedManga()) { chapter.scanlator,
getChapterByMangaId.await(manga.id).minByOrNull { it.sourceOrder } mergedManga.ogTitle,
?.takeUnless { it.read } mergedManga.source,
.let(::listOfNotNull) )
} else { }
/* SY <-- */ getNotDownloadedUnreadChapters(manga)
.let { if (amount != null) it.take(amount) else it }
}
downloadManager.downloadChapters(manga, chapters.map { it.toDbChapter() }) downloadManager.downloadChapters(mergedManga, downloadChapters.map(Chapter::toDbChapter))
}
return@forEach
} }
// SY <--
val chapters = getNextUnreadChapters.await(manga.id)
.filterNot { chapter ->
downloadManager.queue.any { chapter.id == it.chapter.id } ||
downloadManager.isChapterDownloaded(
chapter.name,
chapter.scanlator,
// SY -->
manga.ogTitle,
// SY <--
manga.source,
)
}
.let { if (amount != null) it.take(amount) else it }
downloadManager.downloadChapters(manga, chapters.map { it.toDbChapter() })
} }
} }
} }
@ -1082,18 +1077,8 @@ class LibraryPresenter(
// SY --> // SY -->
/** Returns first unread chapter of a manga */ /** Returns first unread chapter of a manga */
fun getFirstUnread(manga: Manga): Chapter? { suspend fun getFirstUnread(manga: Manga): Chapter? {
val chapters = if (manga.source == MERGED_SOURCE_ID) { return getNextUnreadChapters.await(manga.id).firstOrNull()
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource).getChaptersAsBlocking(manga.id)
} else {
runBlocking { getChapterByMangaId.await(manga.id) }
}
return if (manga.isEhBasedManga()) {
val chapter = chapters.sortedBy { it.sourceOrder }.getOrNull(0)
if (chapter?.read == false) chapter else null
} else {
chapters.sortedByDescending { it.sourceOrder }.find { !it.read }
}
} }
private fun getGroupedMangaItems(groupType: Int, libraryManga: List<LibraryItem>): Pair<LibraryMap, List<Category>> { private fun getGroupedMangaItems(groupType: Int, libraryManga: List<LibraryItem>): Pair<LibraryMap, List<Category>> {

View File

@ -266,7 +266,7 @@ class MangaPresenter(
.distinctUntilChanged() .distinctUntilChanged()
// SY --> // SY -->
.combine( .combine(
getMergedChapterByMangaId.subscribe(mangaId) getMergedChapterByMangaId.subscribe(mangaId, true)
.distinctUntilChanged(), .distinctUntilChanged(),
) { (manga, chapters), mergedChapters -> ) { (manga, chapters), mergedChapters ->
if (manga.source == MERGED_SOURCE_ID) { if (manga.source == MERGED_SOURCE_ID) {
@ -351,7 +351,7 @@ class MangaPresenter(
presenterScope.launchIO { presenterScope.launchIO {
val manga = getMangaAndChapters.awaitManga(mangaId) val manga = getMangaAndChapters.awaitManga(mangaId)
// SY --> // SY -->
val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChapterByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId)) val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChapterByMangaId.await(mangaId, true) else getMangaAndChapters.awaitChapters(mangaId))
.toChapterItemsParams(manga, null) .toChapterItemsParams(manga, null)
val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references -> val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
MergedMangaData( MergedMangaData(
@ -1041,7 +1041,6 @@ class MangaPresenter(
} }
} else { } else {
successState.source.fetchChaptersForMergedManga(successState.manga, manualFetch, true, dedupe) successState.source.fetchChaptersForMergedManga(successState.manga, manualFetch, true, dedupe)
Unit
} }
} }
} catch (e: Throwable) { } catch (e: Throwable) {

View File

@ -8,6 +8,7 @@ import androidx.annotation.ColorInt
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
import eu.kanade.domain.chapter.interactor.UpdateChapter import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.chapter.model.ChapterUpdate import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
@ -121,6 +122,7 @@ class ReaderPresenter(
private val getFlatMetadataById: GetFlatMetadataById = Injekt.get(), private val getFlatMetadataById: GetFlatMetadataById = Injekt.get(),
private val getMergedManga: GetMergedManga = Injekt.get(), private val getMergedManga: GetMergedManga = Injekt.get(),
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(), private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(),
// SY <-- // SY <--
) : BasePresenter<ReaderActivity>() { ) : BasePresenter<ReaderActivity>() {
@ -183,8 +185,7 @@ class ReaderPresenter(
// SY <-- // SY <--
val chapters = runBlocking { val chapters = runBlocking {
/* SY --> */ if (manga.source == MERGED_SOURCE_ID) { /* SY --> */ if (manga.source == MERGED_SOURCE_ID) {
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource) getMergedChapterByMangaId.await(manga.id!!)
.getChapters(manga.id!!)
} else { } else {
/* SY <-- */ getChapterByMangaId.await(manga.id!!) /* SY <-- */ getChapterByMangaId.await(manga.id!!)
} }