mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Move LibraryManga to domain layer (#8126)
				
					
				
			This commit is contained in:
		@@ -1,35 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.models
 | 
			
		||||
 | 
			
		||||
class LibraryManga : MangaImpl() {
 | 
			
		||||
 | 
			
		||||
    var unreadCount: Int = 0
 | 
			
		||||
    var readCount: Int = 0
 | 
			
		||||
 | 
			
		||||
    val totalChapters
 | 
			
		||||
        get() = readCount + unreadCount
 | 
			
		||||
 | 
			
		||||
    val hasStarted
 | 
			
		||||
        get() = readCount > 0
 | 
			
		||||
 | 
			
		||||
    var category: Int = 0
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (this === other) return true
 | 
			
		||||
        if (other !is LibraryManga) return false
 | 
			
		||||
        if (!super.equals(other)) return false
 | 
			
		||||
 | 
			
		||||
        if (unreadCount != other.unreadCount) return false
 | 
			
		||||
        if (readCount != other.readCount) return false
 | 
			
		||||
        if (category != other.category) return false
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int {
 | 
			
		||||
        var result = super.hashCode()
 | 
			
		||||
        result = 31 * result + unreadCount
 | 
			
		||||
        result = 31 * result + readCount
 | 
			
		||||
        result = 31 * result + category
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,10 +14,12 @@ import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
 | 
			
		||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
 | 
			
		||||
import eu.kanade.domain.chapter.model.toDbChapter
 | 
			
		||||
import eu.kanade.domain.download.service.DownloadPreferences
 | 
			
		||||
import eu.kanade.domain.library.model.LibraryManga
 | 
			
		||||
import eu.kanade.domain.library.service.LibraryPreferences
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetLibraryManga
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetManga
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateManga
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.model.toMangaUpdate
 | 
			
		||||
import eu.kanade.domain.track.interactor.GetTracks
 | 
			
		||||
import eu.kanade.domain.track.interactor.InsertTrack
 | 
			
		||||
@@ -26,10 +28,7 @@ import eu.kanade.domain.track.model.toDomainTrack
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.CoverCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadService
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
 | 
			
		||||
@@ -75,7 +74,6 @@ import java.util.concurrent.CopyOnWriteArrayList
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger
 | 
			
		||||
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga as DomainManga
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class will take care of updating the chapters of the manga from the library. It can be
 | 
			
		||||
@@ -261,20 +259,20 @@ class LibraryUpdateService(
 | 
			
		||||
     *
 | 
			
		||||
     * @param categoryId the ID of the category to update, or -1 if no category specified.
 | 
			
		||||
     */
 | 
			
		||||
    fun addMangaToQueue(categoryId: Long) {
 | 
			
		||||
    private fun addMangaToQueue(categoryId: Long) {
 | 
			
		||||
        val libraryManga = runBlocking { getLibraryManga.await() }
 | 
			
		||||
 | 
			
		||||
        val listToUpdate = if (categoryId != -1L) {
 | 
			
		||||
            libraryManga.filter { it.category.toLong() == categoryId }
 | 
			
		||||
            libraryManga.filter { it.category == categoryId }
 | 
			
		||||
        } else {
 | 
			
		||||
            val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map(String::toInt)
 | 
			
		||||
            val categoriesToUpdate = libraryPreferences.libraryUpdateCategories().get().map { it.toLong() }
 | 
			
		||||
            val listToInclude = if (categoriesToUpdate.isNotEmpty()) {
 | 
			
		||||
                libraryManga.filter { it.category in categoriesToUpdate }
 | 
			
		||||
            } else {
 | 
			
		||||
                libraryManga
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map(String::toInt)
 | 
			
		||||
            val categoriesToExclude = libraryPreferences.libraryUpdateCategoriesExclude().get().map { it.toLong() }
 | 
			
		||||
            val listToExclude = if (categoriesToExclude.isNotEmpty()) {
 | 
			
		||||
                libraryManga.filter { it.category in categoriesToExclude }
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -285,12 +283,12 @@ class LibraryUpdateService(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mangaToUpdate = listToUpdate
 | 
			
		||||
            .distinctBy { it.id }
 | 
			
		||||
            .sortedBy { it.title }
 | 
			
		||||
            .distinctBy { it.manga.id }
 | 
			
		||||
            .sortedBy { it.manga.title }
 | 
			
		||||
 | 
			
		||||
        // Warn when excessively checking a single source
 | 
			
		||||
        val maxUpdatesFromSource = mangaToUpdate
 | 
			
		||||
            .groupBy { it.source }
 | 
			
		||||
            .groupBy { it.manga.source }
 | 
			
		||||
            .filterKeys { sourceManager.get(it) !is UnmeteredSource }
 | 
			
		||||
            .maxOfOrNull { it.value.size } ?: 0
 | 
			
		||||
        if (maxUpdatesFromSource > MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD) {
 | 
			
		||||
@@ -309,8 +307,8 @@ class LibraryUpdateService(
 | 
			
		||||
    private suspend fun updateChapterList() {
 | 
			
		||||
        val semaphore = Semaphore(5)
 | 
			
		||||
        val progressCount = AtomicInteger(0)
 | 
			
		||||
        val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>()
 | 
			
		||||
        val newUpdates = CopyOnWriteArrayList<Pair<DomainManga, Array<DomainChapter>>>()
 | 
			
		||||
        val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
 | 
			
		||||
        val newUpdates = CopyOnWriteArrayList<Pair<Manga, Array<DomainChapter>>>()
 | 
			
		||||
        val skippedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
 | 
			
		||||
        val failedUpdates = CopyOnWriteArrayList<Pair<Manga, String?>>()
 | 
			
		||||
        val hasDownloads = AtomicBoolean(false)
 | 
			
		||||
@@ -319,60 +317,58 @@ class LibraryUpdateService(
 | 
			
		||||
        val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get()
 | 
			
		||||
 | 
			
		||||
        withIOContext {
 | 
			
		||||
            mangaToUpdate.groupBy { it.source }
 | 
			
		||||
            mangaToUpdate.groupBy { it.manga.source }
 | 
			
		||||
                .values
 | 
			
		||||
                .map { mangaInSource ->
 | 
			
		||||
                    async {
 | 
			
		||||
                        semaphore.withPermit {
 | 
			
		||||
                            mangaInSource.forEach { manga ->
 | 
			
		||||
                            mangaInSource.forEach { libraryManga ->
 | 
			
		||||
                                val manga = libraryManga.manga
 | 
			
		||||
                                if (updateJob?.isActive != true) {
 | 
			
		||||
                                    return@async
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                // Don't continue to update if manga not in library
 | 
			
		||||
                                manga.id?.let { getManga.await(it) } ?: return@forEach
 | 
			
		||||
                                // Don't continue to update if manga is not in library
 | 
			
		||||
                                manga.id.let { getManga.await(it) } ?: return@forEach
 | 
			
		||||
 | 
			
		||||
                                withUpdateNotification(
 | 
			
		||||
                                    currentlyUpdatingManga,
 | 
			
		||||
                                    progressCount,
 | 
			
		||||
                                    manga,
 | 
			
		||||
                                ) { mangaWithNotif ->
 | 
			
		||||
                                ) {
 | 
			
		||||
                                    try {
 | 
			
		||||
                                        when {
 | 
			
		||||
                                            MANGA_NON_COMPLETED in restrictions && mangaWithNotif.status == SManga.COMPLETED ->
 | 
			
		||||
                                                skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_completed))
 | 
			
		||||
                                            MANGA_NON_COMPLETED in restrictions && manga.status.toInt() == SManga.COMPLETED ->
 | 
			
		||||
                                                skippedUpdates.add(manga to getString(R.string.skipped_reason_completed))
 | 
			
		||||
 | 
			
		||||
                                            MANGA_HAS_UNREAD in restrictions && mangaWithNotif.unreadCount != 0 ->
 | 
			
		||||
                                                skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_caught_up))
 | 
			
		||||
                                            MANGA_HAS_UNREAD in restrictions && libraryManga.unreadCount != 0L ->
 | 
			
		||||
                                                skippedUpdates.add(manga to getString(R.string.skipped_reason_not_caught_up))
 | 
			
		||||
 | 
			
		||||
                                            MANGA_NON_READ in restrictions && mangaWithNotif.totalChapters > 0 && !mangaWithNotif.hasStarted ->
 | 
			
		||||
                                                skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_started))
 | 
			
		||||
                                            MANGA_NON_READ in restrictions && libraryManga.totalChapters > 0L && !libraryManga.hasStarted ->
 | 
			
		||||
                                                skippedUpdates.add(manga to getString(R.string.skipped_reason_not_started))
 | 
			
		||||
 | 
			
		||||
                                            mangaWithNotif.update_strategy != UpdateStrategy.ALWAYS_UPDATE ->
 | 
			
		||||
                                                skippedUpdates.add(mangaWithNotif to getString(R.string.skipped_reason_not_always_update))
 | 
			
		||||
                                            manga.updateStrategy != UpdateStrategy.ALWAYS_UPDATE ->
 | 
			
		||||
                                                skippedUpdates.add(manga to getString(R.string.skipped_reason_not_always_update))
 | 
			
		||||
 | 
			
		||||
                                            else -> {
 | 
			
		||||
                                                // Convert to the manga that contains new chapters
 | 
			
		||||
                                                mangaWithNotif.toDomainManga()?.let { domainManga ->
 | 
			
		||||
                                                    val newChapters = updateManga(domainManga)
 | 
			
		||||
                                                    val newDbChapters = newChapters.map { it.toDbChapter() }
 | 
			
		||||
                                                val newChapters = updateManga(manga)
 | 
			
		||||
                                                val newDbChapters = newChapters.map { it.toDbChapter() }
 | 
			
		||||
 | 
			
		||||
                                                    if (newChapters.isNotEmpty()) {
 | 
			
		||||
                                                        val categoryIds = getCategories.await(domainManga.id).map { it.id }
 | 
			
		||||
                                                        if (domainManga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) {
 | 
			
		||||
                                                            downloadChapters(mangaWithNotif, newDbChapters)
 | 
			
		||||
                                                            hasDownloads.set(true)
 | 
			
		||||
                                                        }
 | 
			
		||||
 | 
			
		||||
                                                        // Convert to the manga that contains new chapters
 | 
			
		||||
                                                        newUpdates.add(
 | 
			
		||||
                                                            mangaWithNotif.toDomainManga()!! to
 | 
			
		||||
                                                                newDbChapters
 | 
			
		||||
                                                                    .map { it.toDomainChapter()!! }
 | 
			
		||||
                                                                    .sortedByDescending { it.sourceOrder }
 | 
			
		||||
                                                                    .toTypedArray(),
 | 
			
		||||
                                                        )
 | 
			
		||||
                                                if (newChapters.isNotEmpty()) {
 | 
			
		||||
                                                    val categoryIds = getCategories.await(manga.id).map { it.id }
 | 
			
		||||
                                                    if (manga.shouldDownloadNewChapters(categoryIds, downloadPreferences)) {
 | 
			
		||||
                                                        downloadChapters(manga, newDbChapters)
 | 
			
		||||
                                                        hasDownloads.set(true)
 | 
			
		||||
                                                    }
 | 
			
		||||
 | 
			
		||||
                                                    // Convert to the manga that contains new chapters
 | 
			
		||||
                                                    newUpdates.add(
 | 
			
		||||
                                                        manga to
 | 
			
		||||
                                                            newDbChapters
 | 
			
		||||
                                                                .map { it.toDomainChapter()!! }
 | 
			
		||||
                                                                .sortedByDescending { it.sourceOrder }
 | 
			
		||||
                                                                .toTypedArray(),
 | 
			
		||||
                                                    )
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
@@ -383,11 +379,11 @@ class LibraryUpdateService(
 | 
			
		||||
                                            is SourceManager.SourceNotInstalledException -> getString(R.string.loader_not_implemented_error)
 | 
			
		||||
                                            else -> e.message
 | 
			
		||||
                                        }
 | 
			
		||||
                                        failedUpdates.add(mangaWithNotif to errorMessage)
 | 
			
		||||
                                        failedUpdates.add(manga to errorMessage)
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                    if (libraryPreferences.autoUpdateTrackers().get()) {
 | 
			
		||||
                                        updateTrackings(mangaWithNotif, loggedServices)
 | 
			
		||||
                                        updateTrackings(manga, loggedServices)
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
@@ -423,7 +419,7 @@ class LibraryUpdateService(
 | 
			
		||||
    private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
 | 
			
		||||
        // We don't want to start downloading while the library is updating, because websites
 | 
			
		||||
        // may don't like it and they could ban the user.
 | 
			
		||||
        downloadManager.downloadChapters(manga.toDomainManga()!!, chapters, false)
 | 
			
		||||
        downloadManager.downloadChapters(manga, chapters, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -432,7 +428,7 @@ class LibraryUpdateService(
 | 
			
		||||
     * @param manga the manga to update.
 | 
			
		||||
     * @return a pair of the inserted and removed chapters.
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun updateManga(manga: DomainManga): List<DomainChapter> {
 | 
			
		||||
    private suspend fun updateManga(manga: Manga): List<DomainChapter> {
 | 
			
		||||
        val source = sourceManager.getOrStub(manga.source)
 | 
			
		||||
 | 
			
		||||
        // Update manga metadata if needed
 | 
			
		||||
@@ -455,15 +451,16 @@ class LibraryUpdateService(
 | 
			
		||||
    private suspend fun updateCovers() {
 | 
			
		||||
        val semaphore = Semaphore(5)
 | 
			
		||||
        val progressCount = AtomicInteger(0)
 | 
			
		||||
        val currentlyUpdatingManga = CopyOnWriteArrayList<LibraryManga>()
 | 
			
		||||
        val currentlyUpdatingManga = CopyOnWriteArrayList<Manga>()
 | 
			
		||||
 | 
			
		||||
        withIOContext {
 | 
			
		||||
            mangaToUpdate.groupBy { it.source }
 | 
			
		||||
            mangaToUpdate.groupBy { it.manga.source }
 | 
			
		||||
                .values
 | 
			
		||||
                .map { mangaInSource ->
 | 
			
		||||
                    async {
 | 
			
		||||
                        semaphore.withPermit {
 | 
			
		||||
                            mangaInSource.forEach { manga ->
 | 
			
		||||
                            mangaInSource.forEach { libraryManga ->
 | 
			
		||||
                                val manga = libraryManga.manga
 | 
			
		||||
                                if (updateJob?.isActive != true) {
 | 
			
		||||
                                    return@async
 | 
			
		||||
                                }
 | 
			
		||||
@@ -472,14 +469,14 @@ class LibraryUpdateService(
 | 
			
		||||
                                    currentlyUpdatingManga,
 | 
			
		||||
                                    progressCount,
 | 
			
		||||
                                    manga,
 | 
			
		||||
                                ) { mangaWithNotif ->
 | 
			
		||||
                                    val source = sourceManager.get(mangaWithNotif.source) ?: return@withUpdateNotification
 | 
			
		||||
                                ) {
 | 
			
		||||
                                    val source = sourceManager.get(manga.source) ?: return@withUpdateNotification
 | 
			
		||||
                                    try {
 | 
			
		||||
                                        val networkManga = source.getMangaDetails(mangaWithNotif.copy())
 | 
			
		||||
                                        mangaWithNotif.prepUpdateCover(coverCache, networkManga, true)
 | 
			
		||||
                                        mangaWithNotif.copyFrom(networkManga)
 | 
			
		||||
                                        val networkManga = source.getMangaDetails(manga.toSManga())
 | 
			
		||||
                                        val updatedManga = manga.prepUpdateCover(coverCache, networkManga, true)
 | 
			
		||||
                                            .copyFrom(networkManga)
 | 
			
		||||
                                        try {
 | 
			
		||||
                                            updateManga.await(mangaWithNotif.toDomainManga()!!.toMangaUpdate())
 | 
			
		||||
                                            updateManga.await(updatedManga.toMangaUpdate())
 | 
			
		||||
                                        } catch (e: Exception) {
 | 
			
		||||
                                            logcat(LogPriority.ERROR) { "Manga doesn't exist anymore" }
 | 
			
		||||
                                        }
 | 
			
		||||
@@ -506,12 +503,13 @@ class LibraryUpdateService(
 | 
			
		||||
        var progressCount = 0
 | 
			
		||||
        val loggedServices = trackManager.services.filter { it.isLogged }
 | 
			
		||||
 | 
			
		||||
        mangaToUpdate.forEach { manga ->
 | 
			
		||||
        mangaToUpdate.forEach { libraryManga ->
 | 
			
		||||
            val manga = libraryManga.manga
 | 
			
		||||
            if (updateJob?.isActive != true) {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            notifier.showProgressNotification(listOf(manga.toDomainManga()!!), progressCount++, mangaToUpdate.size)
 | 
			
		||||
            notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size)
 | 
			
		||||
 | 
			
		||||
            // Update the tracking details.
 | 
			
		||||
            updateTrackings(manga, loggedServices)
 | 
			
		||||
@@ -520,8 +518,8 @@ class LibraryUpdateService(
 | 
			
		||||
        notifier.cancelProgressNotification()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun updateTrackings(manga: LibraryManga, loggedServices: List<TrackService>) {
 | 
			
		||||
        getTracks.await(manga.id!!)
 | 
			
		||||
    private suspend fun updateTrackings(manga: Manga, loggedServices: List<TrackService>) {
 | 
			
		||||
        getTracks.await(manga.id)
 | 
			
		||||
            .map { track ->
 | 
			
		||||
                supervisorScope {
 | 
			
		||||
                    async {
 | 
			
		||||
@@ -532,7 +530,7 @@ class LibraryUpdateService(
 | 
			
		||||
                                insertTrack.await(updatedTrack.toDomainTrack()!!)
 | 
			
		||||
 | 
			
		||||
                                if (service is EnhancedTrackService) {
 | 
			
		||||
                                    val chapters = getChapterByMangaId.await(manga.id!!)
 | 
			
		||||
                                    val chapters = getChapterByMangaId.await(manga.id)
 | 
			
		||||
                                    syncChaptersWithTrackServiceTwoWay.await(chapters, track, service)
 | 
			
		||||
                                }
 | 
			
		||||
                            } catch (e: Throwable) {
 | 
			
		||||
@@ -547,10 +545,10 @@ class LibraryUpdateService(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun withUpdateNotification(
 | 
			
		||||
        updatingManga: CopyOnWriteArrayList<LibraryManga>,
 | 
			
		||||
        updatingManga: CopyOnWriteArrayList<Manga>,
 | 
			
		||||
        completed: AtomicInteger,
 | 
			
		||||
        manga: LibraryManga,
 | 
			
		||||
        block: suspend (LibraryManga) -> Unit,
 | 
			
		||||
        manga: Manga,
 | 
			
		||||
        block: suspend () -> Unit,
 | 
			
		||||
    ) {
 | 
			
		||||
        if (updateJob?.isActive != true) {
 | 
			
		||||
            return
 | 
			
		||||
@@ -558,12 +556,12 @@ class LibraryUpdateService(
 | 
			
		||||
 | 
			
		||||
        updatingManga.add(manga)
 | 
			
		||||
        notifier.showProgressNotification(
 | 
			
		||||
            updatingManga.map { it.toDomainManga()!! },
 | 
			
		||||
            updatingManga,
 | 
			
		||||
            completed.get(),
 | 
			
		||||
            mangaToUpdate.size,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        block(manga)
 | 
			
		||||
        block()
 | 
			
		||||
 | 
			
		||||
        if (updateJob?.isActive != true) {
 | 
			
		||||
            return
 | 
			
		||||
@@ -572,7 +570,7 @@ class LibraryUpdateService(
 | 
			
		||||
        updatingManga.remove(manga)
 | 
			
		||||
        completed.getAndIncrement()
 | 
			
		||||
        notifier.showProgressNotification(
 | 
			
		||||
            updatingManga.map { it.toDomainManga()!! },
 | 
			
		||||
            updatingManga,
 | 
			
		||||
            completed.get(),
 | 
			
		||||
            mangaToUpdate.size,
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user