diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 09cb8412cc..568c9bd0c7 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -151,7 +151,7 @@ class DomainModule : InjektModule { addFactory { UpdateChapter(get()) } addFactory { SetReadStatus(get(), get(), get(), get()) } addFactory { ShouldUpdateDbChapter() } - addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) } + addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } addFactory { GetAvailableScanlators(get()) } addFactory { GetReadChapterCountByMangaIdAndChapterNumber(get()) } diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index 80b6438ad6..31a6c78558 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.online.HttpSource +import mihon.domain.chapter.interactor.GetReadChapterCountByMangaIdAndChapterNumber import tachiyomi.data.chapter.ChapterSanitizer import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter @@ -19,6 +20,7 @@ import tachiyomi.domain.chapter.model.NoChaptersException import tachiyomi.domain.chapter.model.toChapterUpdate import tachiyomi.domain.chapter.repository.ChapterRepository import tachiyomi.domain.chapter.service.ChapterRecognition +import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.source.local.isLocal import java.lang.Long.max @@ -34,6 +36,8 @@ class SyncChaptersWithSource( private val updateChapter: UpdateChapter, private val getChaptersByMangaId: GetChaptersByMangaId, private val getExcludedScanlators: GetExcludedScanlators, + private val downloadPreferences: DownloadPreferences, + private val getReadChapterCount: GetReadChapterCountByMangaIdAndChapterNumber, ) { /** @@ -42,7 +46,12 @@ class SyncChaptersWithSource( * @param rawSourceChapters the chapters from the source. * @param manga the manga the chapters belong to. * @param source the source the manga belongs to. - * @return Newly added chapters + * @param manualFetch A flag indicating if this fetch was manually triggered. Defaults to `false`. + * @param fetchWindow A time window to determine if a fetch should be performed. Defaults to `(0, 0)`. + * @return A pair of lists: + * - First: List of chapters that were added or updated after filtering out re-added or excluded scanlator chapters. + * - Second: List of chapters that are eligible for download, based on the unread chapters-only preference. + * @throws NoChaptersException If the source is not local and there are no chapters in `rawSourceChapters`. */ suspend fun await( rawSourceChapters: List, @@ -50,7 +59,7 @@ class SyncChaptersWithSource( source: Source, manualFetch: Boolean = false, fetchWindow: Pair = Pair(0, 0), - ): List { + ): Pair, List> { if (rawSourceChapters.isEmpty() && !source.isLocal()) { throw NoChaptersException() } @@ -142,7 +151,7 @@ class SyncChaptersWithSource( fetchWindow, ) } - return emptyList() + return Pair(emptyList(), emptyList()) } val reAdded = mutableListOf() @@ -206,8 +215,16 @@ class SyncChaptersWithSource( val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet() - return updatedToAdd.filterNot { + val nonExcludedChapters = updatedToAdd.filterNot { it.url in reAddedUrls || it.scanlator in excludedScanlators } + + val chaptersToDownload = if (downloadPreferences.downloadUnreadChaptersOnly().get()) { + nonExcludedChapters.filter { getReadChapterCount.await(manga.id, it.chapterNumber) == 0L } + } else { + nonExcludedChapters + } + + return Pair(nonExcludedChapters, chaptersToDownload) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index 9e38fb7dd2..7db4a94d5c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -39,8 +39,6 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import logcat.LogPriority -import mihon.core.chapters.utils.filterChaptersToDownload -import mihon.domain.chapter.interactor.GetReadChapterCountByMangaIdAndChapterNumber import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.util.lang.withIOContext @@ -90,7 +88,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet private val getCategories: GetCategories = Injekt.get() private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get() private val fetchInterval: FetchInterval = Injekt.get() - private val getReadChapterCount: GetReadChapterCountByMangaIdAndChapterNumber = Injekt.get() private val notifier = LibraryUpdateNotifier(context) @@ -269,11 +266,16 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet manga, ) { try { - val newChapters = updateManga(manga, fetchWindow) - .sortedByDescending { it.sourceOrder } + val (newChapters, chaptersToDownload) = updateManga(manga, fetchWindow) if (newChapters.isNotEmpty()) { - handleNewChapters(manga, newChapters, newUpdates, hasDownloads) + handleNewChapters( + manga, + newChapters, + chaptersToDownload, + newUpdates, + hasDownloads + ) } } catch (e: Throwable) { val errorMessage = when (e) { @@ -315,35 +317,33 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet } /** - * This function handles the new chapters for a given manga, checks the download preferences, - * and initiates the download based on the user preferences. It also adds the new chapters to the new updates list - * and updates the hasDownloads flag. + * Processes new chapters for a given manga and handles related updates, such as downloading + * new chapters and updating the notification states. * * @param manga The manga for which new chapters are being handled. * @param newChapters A list of new chapters for the manga. + * @param chaptersToDownload A list of chapters to be downloaded based on user preferences. * @param newUpdates A thread-safe list that stores pairs of manga and their respective new chapters. * @param hasDownloads A flag indicating whether there are any chapters to download. */ private suspend fun handleNewChapters( manga: Manga, newChapters: List, + chaptersToDownload: List, newUpdates: CopyOnWriteArrayList>>, hasDownloads: AtomicBoolean ) { val categoryIds = getCategories.await(manga.id).map { it.id } - - if (manga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) { - val chaptersToDownload = newChapters - .filterChaptersToDownload(manga, getReadChapterCount, downloadPreferences) - - if (chaptersToDownload.isNotEmpty()) { - downloadChapters(manga, chaptersToDownload) - hasDownloads.set(true) - } + if (manga.shouldDownloadNewChapters(categoryIds, downloadPreferences) && chaptersToDownload.isNotEmpty()) { + downloadChapters(manga, chaptersToDownload) + hasDownloads.set(true) } libraryPreferences.newUpdatesCount().getAndSet { it + newChapters.size } - newUpdates.add(manga to newChapters.toTypedArray()) + val sortedChapters = newChapters.sortedByDescending { it.sourceOrder } + + // Convert to the manga that contains new chapters + newUpdates.add(manga to sortedChapters.toTypedArray()) } private fun downloadChapters(manga: Manga, chapters: List) { @@ -355,10 +355,13 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet /** * Updates the chapters for the given manga and adds them to the database. * - * @param manga the manga to update. - * @return a pair of the inserted and removed chapters. + * @param manga The manga to be updated. + * @param fetchWindow A time window defining the interval for fetching chapters, represented as a pair of timestamps. + * @return A pair of lists: + * - First: List of chapters that were added or updated after filtering out re-added or excluded scanlator chapters. + * - Second: List of chapters that are eligible for download, based on the unread chapters-only preference. */ - private suspend fun updateManga(manga: Manga, fetchWindow: Pair): List { + private suspend fun updateManga(manga: Manga, fetchWindow: Pair): Pair, List> { val source = sourceManager.getOrStub(manga.source) // Update manga metadata if needed @@ -371,7 +374,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet // Get manga from database to account for if it was removed during the update and // to get latest data so it doesn't get overwritten later on - val dbManga = getManga.await(manga.id)?.takeIf { it.favorite } ?: return emptyList() + val dbManga = getManga.await(manga.id)?.takeIf { it.favorite } ?: return Pair(emptyList(), emptyList()) return syncChaptersWithSource.await(chapters, dbManga, source, false, fetchWindow) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt index 5bef14675b..a156113a82 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt @@ -66,7 +66,7 @@ class DeepLinkScreenModel( return if (localChapter == null) { val sourceChapters = source.getChapterList(manga.toSManga()) - val newChapters = syncChaptersWithSource.await(sourceChapters, manga, source, false) + val (newChapters, _) = syncChaptersWithSource.await(sourceChapters, manga, source, false) newChapters.find { it.url == sChapter.url } } else { localChapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 0c5e5833fe..299ea1cde4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -48,8 +48,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import logcat.LogPriority -import mihon.core.chapters.utils.filterChaptersToDownload -import mihon.domain.chapter.interactor.GetReadChapterCountByMangaIdAndChapterNumber import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.preference.CheckboxState import tachiyomi.core.common.preference.TriState @@ -111,7 +109,6 @@ class MangaScreenModel( private val addTracks: AddTracks = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(), private val mangaRepository: MangaRepository = Injekt.get(), - private val getReadChapterCount: GetReadChapterCountByMangaIdAndChapterNumber = Injekt.get(), val snackbarHostState: SnackbarHostState = SnackbarHostState(), ) : StateScreenModel(State.Loading) { @@ -538,7 +535,7 @@ class MangaScreenModel( withIOContext { val chapters = state.source.getChapterList(state.manga.toSManga()) - val newChapters = syncChaptersWithSource.await( + val (_, chaptersToDownload) = syncChaptersWithSource.await( chapters, state.manga, state.source, @@ -546,7 +543,7 @@ class MangaScreenModel( ) if (manualFetch) { - downloadNewChapters(newChapters) + downloadNewChapters(chaptersToDownload) } } } catch (e: Throwable) { @@ -782,8 +779,7 @@ class MangaScreenModel( return@launchNonCancellable } - val chaptersToDownload = chapters.filterChaptersToDownload(manga, getReadChapterCount, downloadPreferences) - downloadChapters(chaptersToDownload) + downloadChapters(chapters) } } diff --git a/app/src/main/java/mihon/core/chapters/utils/FilterChaptersToDownload.kt b/app/src/main/java/mihon/core/chapters/utils/FilterChaptersToDownload.kt deleted file mode 100644 index bcaf0caa3d..0000000000 --- a/app/src/main/java/mihon/core/chapters/utils/FilterChaptersToDownload.kt +++ /dev/null @@ -1,33 +0,0 @@ -package mihon.core.chapters.utils - -import mihon.domain.chapter.interactor.GetReadChapterCountByMangaIdAndChapterNumber -import tachiyomi.domain.chapter.model.Chapter -import tachiyomi.domain.download.service.DownloadPreferences -import tachiyomi.domain.manga.model.Manga - -/** - * Filters the list of chapters based on the user's download preferences. - * - * This function checks the user's preference for downloading only unread chapters. If the - * preference is enabled, it filters the chapters to include only those that haven't been read. - * Otherwise, it returns the original list of chapters. - * - * @param manga The manga to which the chapters belong. - * @param getReadChapterCount A function to get the count of times a chapter has been read. - * @param downloadPreferences User preferences related to downloading chapters. - * @return A list of chapters filtered according to the download preferences. If the user prefers to download only - * unread chapters, the list will contain only unread chapters. Otherwise, it will return the original list of chapters. - */ -suspend fun List.filterChaptersToDownload( - manga: Manga, - getReadChapterCount: GetReadChapterCountByMangaIdAndChapterNumber, - downloadPreferences: DownloadPreferences, -): List { - val onlyDownloadUnreadChapters = downloadPreferences.downloadUnreadChaptersOnly().get() - - return if (onlyDownloadUnreadChapters) { - this.filter { getReadChapterCount.await(manga.id, it.chapterNumber) == 0L } - } else { - this - } -}