mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Update manga metadata on library update with sqldelight (#7293)
				
					
				
			Co-Authored-By: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
This commit is contained in:
		@@ -1,7 +1,10 @@
 | 
			
		||||
package eu.kanade.data.manga
 | 
			
		||||
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.data.listOfStringsAdapter
 | 
			
		||||
import eu.kanade.data.toLong
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.model.MangaUpdate
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
@@ -11,6 +14,10 @@ class MangaRepositoryImpl(
 | 
			
		||||
    private val handler: DatabaseHandler,
 | 
			
		||||
) : MangaRepository {
 | 
			
		||||
 | 
			
		||||
    override suspend fun getMangaById(id: Long): Manga {
 | 
			
		||||
        return handler.awaitOne { mangasQueries.getMangaById(id, mangaMapper) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>> {
 | 
			
		||||
        return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
 | 
			
		||||
    }
 | 
			
		||||
@@ -25,11 +32,33 @@ class MangaRepositoryImpl(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun updateLastUpdate(mangaId: Long, lastUpdate: Long) {
 | 
			
		||||
        try {
 | 
			
		||||
            handler.await { mangasQueries.updateLastUpdate(lastUpdate, mangaId) }
 | 
			
		||||
    override suspend fun update(update: MangaUpdate): Boolean {
 | 
			
		||||
        return try {
 | 
			
		||||
            handler.await {
 | 
			
		||||
                mangasQueries.update(
 | 
			
		||||
                    source = update.source,
 | 
			
		||||
                    url = update.url,
 | 
			
		||||
                    artist = update.artist,
 | 
			
		||||
                    author = update.author,
 | 
			
		||||
                    description = update.description,
 | 
			
		||||
                    genre = update.genre?.let(listOfStringsAdapter::encode),
 | 
			
		||||
                    title = update.title,
 | 
			
		||||
                    status = update.status,
 | 
			
		||||
                    thumbnailUrl = update.thumbnailUrl,
 | 
			
		||||
                    favorite = update.favorite?.toLong(),
 | 
			
		||||
                    lastUpdate = update.lastUpdate,
 | 
			
		||||
                    initialized = update.initialized?.toLong(),
 | 
			
		||||
                    viewer = update.viewerFlags,
 | 
			
		||||
                    chapterFlags = update.chapterFlags,
 | 
			
		||||
                    coverLastModified = update.coverLastModified,
 | 
			
		||||
                    dateAdded = update.dateAdded,
 | 
			
		||||
                    mangaId = update.id,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            true
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            logcat(LogPriority.ERROR, e)
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,9 @@ import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
 | 
			
		||||
import eu.kanade.domain.history.interactor.UpsertHistory
 | 
			
		||||
import eu.kanade.domain.history.repository.HistoryRepository
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetFavoritesBySourceId
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetMangaById
 | 
			
		||||
import eu.kanade.domain.manga.interactor.ResetViewerFlags
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateMangaLastUpdate
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateManga
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetEnabledSources
 | 
			
		||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
 | 
			
		||||
@@ -43,9 +44,10 @@ class DomainModule : InjektModule {
 | 
			
		||||
    override fun InjektRegistrar.registerInjectables() {
 | 
			
		||||
        addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
 | 
			
		||||
        addFactory { GetFavoritesBySourceId(get()) }
 | 
			
		||||
        addFactory { GetMangaById(get()) }
 | 
			
		||||
        addFactory { GetNextChapter(get()) }
 | 
			
		||||
        addFactory { ResetViewerFlags(get()) }
 | 
			
		||||
        addFactory { UpdateMangaLastUpdate(get()) }
 | 
			
		||||
        addFactory { UpdateManga(get()) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
 | 
			
		||||
        addFactory { UpdateChapter(get()) }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import eu.kanade.domain.chapter.model.Chapter
 | 
			
		||||
import eu.kanade.domain.chapter.model.toChapterUpdate
 | 
			
		||||
import eu.kanade.domain.chapter.model.toDbChapter
 | 
			
		||||
import eu.kanade.domain.chapter.repository.ChapterRepository
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateMangaLastUpdate
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateManga
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.model.toDbManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadManager
 | 
			
		||||
@@ -24,7 +24,7 @@ class SyncChaptersWithSource(
 | 
			
		||||
    private val downloadManager: DownloadManager = Injekt.get(),
 | 
			
		||||
    private val chapterRepository: ChapterRepository = Injekt.get(),
 | 
			
		||||
    private val shouldUpdateDbChapter: ShouldUpdateDbChapter = Injekt.get(),
 | 
			
		||||
    private val updateMangaLastUpdate: UpdateMangaLastUpdate = Injekt.get(),
 | 
			
		||||
    private val updateManga: UpdateManga = Injekt.get(),
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun await(
 | 
			
		||||
@@ -171,7 +171,7 @@ class SyncChaptersWithSource(
 | 
			
		||||
 | 
			
		||||
        // Set this manga as updated since chapters were changed
 | 
			
		||||
        // Note that last_update actually represents last time the chapter list changed at all
 | 
			
		||||
        updateMangaLastUpdate.await(manga.id, Date().time)
 | 
			
		||||
        updateManga.awaitUpdateLastUpdate(manga.id)
 | 
			
		||||
 | 
			
		||||
        @Suppress("ConvertArgumentToSet") // See tachiyomiorg/tachiyomi#6372.
 | 
			
		||||
        return Pair(updatedToAdd.subtract(reAdded).toList(), toDelete.subtract(reAdded).toList())
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
package eu.kanade.domain.manga.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
 | 
			
		||||
class GetMangaById(
 | 
			
		||||
    private val mangaRepository: MangaRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun await(id: Long): Manga? {
 | 
			
		||||
        return try {
 | 
			
		||||
            mangaRepository.getMangaById(id)
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            logcat(LogPriority.ERROR, e)
 | 
			
		||||
            null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
package eu.kanade.domain.manga.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.model.MangaUpdate
 | 
			
		||||
import eu.kanade.domain.manga.model.hasCustomCover
 | 
			
		||||
import eu.kanade.domain.manga.model.isLocal
 | 
			
		||||
import eu.kanade.domain.manga.model.toDbManga
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.CoverCache
 | 
			
		||||
import tachiyomi.source.model.MangaInfo
 | 
			
		||||
import java.util.Date
 | 
			
		||||
 | 
			
		||||
class UpdateManga(
 | 
			
		||||
    private val mangaRepository: MangaRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun awaitUpdateFromSource(
 | 
			
		||||
        localManga: Manga,
 | 
			
		||||
        remoteManga: MangaInfo,
 | 
			
		||||
        manualFetch: Boolean,
 | 
			
		||||
        coverCache: CoverCache,
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        // if the manga isn't a favorite, set its title from source and update in db
 | 
			
		||||
        val title = if (!localManga.favorite) remoteManga.title else null
 | 
			
		||||
 | 
			
		||||
        // Never refresh covers if the url is empty to avoid "losing" existing covers
 | 
			
		||||
        val updateCover = remoteManga.cover.isNotEmpty() && (manualFetch || localManga.thumbnailUrl != remoteManga.cover)
 | 
			
		||||
        val coverLastModified = if (updateCover) {
 | 
			
		||||
            when {
 | 
			
		||||
                localManga.isLocal() -> Date().time
 | 
			
		||||
                localManga.hasCustomCover(coverCache) -> {
 | 
			
		||||
                    coverCache.deleteFromCache(localManga.toDbManga(), false)
 | 
			
		||||
                    null
 | 
			
		||||
                }
 | 
			
		||||
                else -> {
 | 
			
		||||
                    coverCache.deleteFromCache(localManga.toDbManga(), false)
 | 
			
		||||
                    Date().time
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else null
 | 
			
		||||
 | 
			
		||||
        return mangaRepository.update(
 | 
			
		||||
            MangaUpdate(
 | 
			
		||||
                id = localManga.id,
 | 
			
		||||
                title = title?.takeIf { it.isNotEmpty() },
 | 
			
		||||
                coverLastModified = coverLastModified,
 | 
			
		||||
                author = remoteManga.author,
 | 
			
		||||
                artist = remoteManga.artist,
 | 
			
		||||
                description = remoteManga.description,
 | 
			
		||||
                genre = remoteManga.genres,
 | 
			
		||||
                thumbnailUrl = remoteManga.cover.takeIf { it.isNotEmpty() },
 | 
			
		||||
                status = remoteManga.status.toLong(),
 | 
			
		||||
                initialized = true,
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean {
 | 
			
		||||
        return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Date().time))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
package eu.kanade.domain.manga.interactor
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.manga.repository.MangaRepository
 | 
			
		||||
 | 
			
		||||
class UpdateMangaLastUpdate(
 | 
			
		||||
    private val mangaRepository: MangaRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    suspend fun await(mangaId: Long, lastUpdate: Long) {
 | 
			
		||||
        mangaRepository.updateLastUpdate(mangaId, lastUpdate)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
package eu.kanade.domain.manga.model
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.CoverCache
 | 
			
		||||
import eu.kanade.tachiyomi.source.LocalSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import tachiyomi.source.model.MangaInfo
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga as DbManga
 | 
			
		||||
 | 
			
		||||
data class Manga(
 | 
			
		||||
@@ -62,3 +67,20 @@ fun Manga.toDbManga(): DbManga = DbManga.create(url, title, source).also {
 | 
			
		||||
    it.chapter_flags = chapterFlags.toInt()
 | 
			
		||||
    it.cover_last_modified = coverLastModified
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Manga.toMangaInfo(): MangaInfo = MangaInfo(
 | 
			
		||||
    artist = artist ?: "",
 | 
			
		||||
    author = author ?: "",
 | 
			
		||||
    cover = thumbnailUrl ?: "",
 | 
			
		||||
    description = description ?: "",
 | 
			
		||||
    genres = genre ?: emptyList(),
 | 
			
		||||
    key = url,
 | 
			
		||||
    status = status.toInt(),
 | 
			
		||||
    title = title,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
fun Manga.isLocal(): Boolean = source == LocalSource.ID
 | 
			
		||||
 | 
			
		||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
 | 
			
		||||
    return coverCache.getCustomCoverFile(id).exists()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
package eu.kanade.domain.manga.model
 | 
			
		||||
 | 
			
		||||
data class MangaUpdate(
 | 
			
		||||
    val id: Long,
 | 
			
		||||
    val source: Long? = null,
 | 
			
		||||
    val favorite: Boolean? = null,
 | 
			
		||||
    val lastUpdate: Long? = null,
 | 
			
		||||
    val dateAdded: Long? = null,
 | 
			
		||||
    val viewerFlags: Long? = null,
 | 
			
		||||
    val chapterFlags: Long? = null,
 | 
			
		||||
    val coverLastModified: Long? = null,
 | 
			
		||||
    val url: String? = null,
 | 
			
		||||
    val title: String? = null,
 | 
			
		||||
    val artist: String? = null,
 | 
			
		||||
    val author: String? = null,
 | 
			
		||||
    val description: String? = null,
 | 
			
		||||
    val genre: List<String>? = null,
 | 
			
		||||
    val status: Long? = null,
 | 
			
		||||
    val thumbnailUrl: String? = null,
 | 
			
		||||
    val initialized: Boolean? = null,
 | 
			
		||||
)
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
package eu.kanade.domain.manga.repository
 | 
			
		||||
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.domain.manga.model.MangaUpdate
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
interface MangaRepository {
 | 
			
		||||
 | 
			
		||||
    suspend fun getMangaById(id: Long): Manga
 | 
			
		||||
 | 
			
		||||
    fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
 | 
			
		||||
 | 
			
		||||
    suspend fun resetViewerFlags(): Boolean
 | 
			
		||||
 | 
			
		||||
    suspend fun updateLastUpdate(mangaId: Long, lastUpdate: Long)
 | 
			
		||||
    suspend fun update(update: MangaUpdate): Boolean
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaCoverLastModifiedPutResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
 | 
			
		||||
@@ -101,11 +100,6 @@ interface MangaQueries : DbProvider {
 | 
			
		||||
        .withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags))
 | 
			
		||||
        .prepare()
 | 
			
		||||
 | 
			
		||||
    fun updateLastUpdated(manga: Manga) = db.put()
 | 
			
		||||
        .`object`(manga)
 | 
			
		||||
        .withPutResolver(MangaLastUpdatedPutResolver())
 | 
			
		||||
        .prepare()
 | 
			
		||||
 | 
			
		||||
    fun updateMangaFavorite(manga: Manga) = db.put()
 | 
			
		||||
        .`object`(manga)
 | 
			
		||||
        .withPutResolver(MangaFavoritePutResolver())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.database.resolvers
 | 
			
		||||
 | 
			
		||||
import androidx.core.content.contentValuesOf
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
 | 
			
		||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
 | 
			
		||||
 | 
			
		||||
class MangaLastUpdatedPutResolver : PutResolver<Manga>() {
 | 
			
		||||
 | 
			
		||||
    override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
 | 
			
		||||
        val updateQuery = mapToUpdateQuery(manga)
 | 
			
		||||
        val contentValues = mapToContentValues(manga)
 | 
			
		||||
 | 
			
		||||
        val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
 | 
			
		||||
        PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
 | 
			
		||||
        .table(MangaTable.TABLE)
 | 
			
		||||
        .where("${MangaTable.COL_ID} = ?")
 | 
			
		||||
        .whereArgs(manga.id)
 | 
			
		||||
        .build()
 | 
			
		||||
 | 
			
		||||
    fun mapToContentValues(manga: Manga) =
 | 
			
		||||
        contentValuesOf(
 | 
			
		||||
            MangaTable.COL_LAST_UPDATE to manga.last_update,
 | 
			
		||||
        )
 | 
			
		||||
}
 | 
			
		||||
@@ -7,6 +7,11 @@ import android.os.IBinder
 | 
			
		||||
import android.os.PowerManager
 | 
			
		||||
import androidx.core.content.ContextCompat
 | 
			
		||||
import eu.kanade.data.chapter.NoChaptersException
 | 
			
		||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
 | 
			
		||||
import eu.kanade.domain.chapter.model.toDbChapter
 | 
			
		||||
import eu.kanade.domain.manga.interactor.GetMangaById
 | 
			
		||||
import eu.kanade.domain.manga.interactor.UpdateManga
 | 
			
		||||
import eu.kanade.domain.manga.model.toMangaInfo
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.CoverCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
@@ -14,6 +19,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
 | 
			
		||||
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.toDomainManga
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadService
 | 
			
		||||
@@ -29,10 +35,8 @@ import eu.kanade.tachiyomi.data.track.TrackService
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.UnmeteredSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.toMangaInfo
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.toSChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.toSManga
 | 
			
		||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
 | 
			
		||||
import eu.kanade.tachiyomi.util.lang.withIOContext
 | 
			
		||||
import eu.kanade.tachiyomi.util.prepUpdateCover
 | 
			
		||||
@@ -55,12 +59,15 @@ import kotlinx.coroutines.supervisorScope
 | 
			
		||||
import kotlinx.coroutines.sync.Semaphore
 | 
			
		||||
import kotlinx.coroutines.sync.withPermit
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
import tachiyomi.source.model.MangaInfo
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import java.io.File
 | 
			
		||||
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
 | 
			
		||||
@@ -77,6 +84,9 @@ class LibraryUpdateService(
 | 
			
		||||
    val downloadManager: DownloadManager = Injekt.get(),
 | 
			
		||||
    val trackManager: TrackManager = Injekt.get(),
 | 
			
		||||
    val coverCache: CoverCache = Injekt.get(),
 | 
			
		||||
    private val getMangaById: GetMangaById = Injekt.get(),
 | 
			
		||||
    private val updateManga: UpdateManga = Injekt.get(),
 | 
			
		||||
    private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
 | 
			
		||||
) : Service() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var wakeLock: PowerManager.WakeLock
 | 
			
		||||
@@ -302,7 +312,7 @@ class LibraryUpdateService(
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                // Don't continue to update if manga not in library
 | 
			
		||||
                                db.getManga(manga.id!!).executeAsBlocking() ?: return@forEach
 | 
			
		||||
                                manga.id?.let { getMangaById.await(it) } ?: return@forEach
 | 
			
		||||
 | 
			
		||||
                                withUpdateNotification(
 | 
			
		||||
                                    currentlyUpdatingManga,
 | 
			
		||||
@@ -322,19 +332,22 @@ class LibraryUpdateService(
 | 
			
		||||
 | 
			
		||||
                                            else -> {
 | 
			
		||||
                                                // Convert to the manga that contains new chapters
 | 
			
		||||
                                                val (newChapters, _) = updateManga(mangaWithNotif)
 | 
			
		||||
                                                mangaWithNotif.toDomainManga()?.let { domainManga ->
 | 
			
		||||
                                                    val (newChapters, _) = updateManga(domainManga)
 | 
			
		||||
                                                    val newDbChapters = newChapters.map { it.toDbChapter() }
 | 
			
		||||
 | 
			
		||||
                                                if (newChapters.isNotEmpty()) {
 | 
			
		||||
                                                    if (mangaWithNotif.shouldDownloadNewChapters(db, preferences)) {
 | 
			
		||||
                                                        downloadChapters(mangaWithNotif, newChapters)
 | 
			
		||||
                                                        hasDownloads.set(true)
 | 
			
		||||
                                                    if (newChapters.isNotEmpty()) {
 | 
			
		||||
                                                        if (mangaWithNotif.shouldDownloadNewChapters(db, preferences)) {
 | 
			
		||||
                                                            downloadChapters(mangaWithNotif, newDbChapters)
 | 
			
		||||
                                                            hasDownloads.set(true)
 | 
			
		||||
                                                        }
 | 
			
		||||
 | 
			
		||||
                                                        // Convert to the manga that contains new chapters
 | 
			
		||||
                                                        newUpdates.add(
 | 
			
		||||
                                                            mangaWithNotif to newDbChapters.sortedByDescending { ch -> ch.source_order }
 | 
			
		||||
                                                                .toTypedArray(),
 | 
			
		||||
                                                        )
 | 
			
		||||
                                                    }
 | 
			
		||||
 | 
			
		||||
                                                    // Convert to the manga that contains new chapters
 | 
			
		||||
                                                    newUpdates.add(
 | 
			
		||||
                                                        mangaWithNotif to newChapters.sortedByDescending { ch -> ch.source_order }
 | 
			
		||||
                                                            .toTypedArray(),
 | 
			
		||||
                                                    )
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
@@ -394,39 +407,27 @@ class LibraryUpdateService(
 | 
			
		||||
     * @param manga the manga to update.
 | 
			
		||||
     * @return a pair of the inserted and removed chapters.
 | 
			
		||||
     */
 | 
			
		||||
    private suspend fun updateManga(manga: Manga): Pair<List<Chapter>, List<Chapter>> {
 | 
			
		||||
    private suspend fun updateManga(manga: DomainManga): Pair<List<DomainChapter>, List<DomainChapter>> {
 | 
			
		||||
        val source = sourceManager.getOrStub(manga.source)
 | 
			
		||||
 | 
			
		||||
        var updatedManga: SManga = manga
 | 
			
		||||
        val mangaInfo: MangaInfo = manga.toMangaInfo()
 | 
			
		||||
 | 
			
		||||
        // Update manga details metadata
 | 
			
		||||
        // Update manga metadata if needed
 | 
			
		||||
        if (preferences.autoUpdateMetadata()) {
 | 
			
		||||
            val updatedMangaDetails = source.getMangaDetails(manga.toMangaInfo())
 | 
			
		||||
            val sManga = updatedMangaDetails.toSManga()
 | 
			
		||||
            // Avoid "losing" existing cover
 | 
			
		||||
            if (!sManga.thumbnail_url.isNullOrEmpty()) {
 | 
			
		||||
                manga.prepUpdateCover(coverCache, sManga, false)
 | 
			
		||||
            } else {
 | 
			
		||||
                sManga.thumbnail_url = manga.thumbnail_url
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            updatedManga = sManga
 | 
			
		||||
            val updatedMangaInfo = source.getMangaDetails(manga.toMangaInfo())
 | 
			
		||||
            updateManga.awaitUpdateFromSource(manga, updatedMangaInfo, manualFetch = false, coverCache)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val chapters = source.getChapterList(updatedManga.toMangaInfo())
 | 
			
		||||
        val chapters = source.getChapterList(mangaInfo)
 | 
			
		||||
            .map { it.toSChapter() }
 | 
			
		||||
 | 
			
		||||
        // Get manga from database to account for if it was removed during the update
 | 
			
		||||
        val dbManga = db.getManga(manga.id!!).executeAsBlocking()
 | 
			
		||||
        val dbManga = getMangaById.await(manga.id)
 | 
			
		||||
            ?: return Pair(emptyList(), emptyList())
 | 
			
		||||
 | 
			
		||||
        // Copy into [dbManga] to retain favourite value
 | 
			
		||||
        dbManga.copyFrom(updatedManga)
 | 
			
		||||
        db.insertManga(dbManga).executeAsBlocking()
 | 
			
		||||
 | 
			
		||||
        // [dbmanga] was used so that manga data doesn't get overwritten
 | 
			
		||||
        // in case manga gets new chapter
 | 
			
		||||
        return syncChaptersWithSource(chapters, dbManga, source)
 | 
			
		||||
        return syncChaptersWithSource.await(chapters, dbManga, source)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun updateCovers() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user