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>
(cherry picked from commit 5bb78eb77f2f68c40079548d5fe3b75a11f2bf71)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/manga/model/Manga.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaLastUpdatedPutResolver.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt
This commit is contained in:
AntsyLich 2022-06-12 20:21:45 +06:00 committed by Jobobby04
parent 0888a7cd72
commit e3f577a6e2
18 changed files with 270 additions and 126 deletions

View File

@ -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
}
}
}

View File

@ -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
@ -48,9 +49,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()) }

View File

@ -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
@ -25,7 +25,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(
@ -188,7 +188,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())

View File

@ -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
}
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}

View File

@ -1,7 +1,12 @@
package eu.kanade.domain.manga.model
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.library.CustomMangaManager
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 uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.database.models.Manga as DbManga
@ -88,7 +93,7 @@ data class Manga(
}
// TODO: Remove when all deps are migrated
fun Manga.toDbManga(): DbManga = DbManga.create(url, title, source).also {
fun Manga.toDbManga(): DbManga = DbManga.create(url, ogTitle, source).also {
it.id = id
it.favorite = favorite
it.last_update = lastUpdate
@ -97,3 +102,22 @@ 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(
// SY -->
artist = ogArtist ?: "",
author = ogAuthor ?: "",
cover = thumbnailUrl ?: "",
description = ogDescription ?: "",
genres = ogGenre ?: emptyList(),
key = url,
status = ogStatus.toInt(),
title = ogTitle,
// SY <--
)
fun Manga.isLocal(): Boolean = source == LocalSource.ID
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}

View File

@ -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,
)

View File

@ -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
}

View File

@ -151,14 +151,18 @@ fun Manga.toDomainManga(): DomainManga? {
chapterFlags = chapter_flags.toLong(),
coverLastModified = cover_last_modified,
url = url,
ogTitle = title,
ogArtist = artist,
ogAuthor = author,
ogDescription = description,
ogGenre = getGenres(),
ogStatus = status.toLong(),
// SY -->
ogTitle = originalTitle,
ogArtist = originalArtist,
ogAuthor = originalAuthor,
ogDescription = originalDescription,
ogGenre = getOriginalGenres(),
ogStatus = originalStatus.toLong(),
// SY <--
thumbnailUrl = thumbnail_url,
initialized = initialized,
// SY -->
filteredScanlators = MdUtil.getScanlators(filtered_scanlators).toList(),
// SY <--
)
}

View File

@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFilteredScanlatorsPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaInfoPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaMigrationPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaThumbnailPutResolver
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
@ -146,11 +145,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())

View File

@ -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,
)
}

View File

@ -7,6 +7,12 @@ 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.toDbManga
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 +20,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
@ -30,13 +37,11 @@ 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.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.library.LibraryGroup
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
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
@ -52,7 +57,6 @@ import exh.md.utils.MdUtil
import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.source.LIBRARY_UPDATE_EXCLUDED_SOURCES
import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource
import exh.source.isMdBasedSource
import exh.source.mangaDexSourceIds
import exh.util.executeOnIO
@ -70,12 +74,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
@ -92,6 +99,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
@ -386,7 +396,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,
@ -406,19 +416,22 @@ class LibraryUpdateService(
else -> {
// Convert to the manga that contains new chapters
val (newChapters, _) = updateManga(mangaWithNotif, loggedServices)
mangaWithNotif.toDomainManga()?.let { domainManga ->
val (newChapters, _) = updateManga(domainManga, loggedServices)
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(),
)
}
}
}
@ -494,23 +507,15 @@ class LibraryUpdateService(
* @param manga the manga to update.
* @return a pair of the inserted and removed chapters.
*/
private suspend fun updateManga(manga: Manga, loggedServices: List<TrackService>): Pair<List<Chapter>, List<Chapter>> {
val source = sourceManager.getOrStub(manga.source).getMainSource()
private suspend fun updateManga(manga: DomainManga, loggedServices: List<TrackService>): 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)
}
// SY -->
@ -521,7 +526,7 @@ class LibraryUpdateService(
ioScope?.launch(handler) {
val tracks = db.getTracks(manga.id).executeAsBlocking()
if (tracks.isEmpty() || tracks.none { it.sync_id == TrackManager.MDLIST }) {
val track = trackManager.mdList.createInitialTracker(manga)
val track = trackManager.mdList.createInitialTracker(manga.toDbManga())
db.insertTrack(trackManager.mdList.refresh(track)).executeAsBlocking()
}
}
@ -532,20 +537,16 @@ class LibraryUpdateService(
}
// SY <--
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() {

View File

@ -1,8 +1,12 @@
package eu.kanade.tachiyomi.source.online.all
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.manga.model.toDbManga
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
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.preference.PreferencesHelper
@ -15,7 +19,6 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import exh.log.xLogW
@ -31,7 +34,11 @@ import okhttp3.Response
import rx.Observable
import tachiyomi.source.model.ChapterInfo
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import eu.kanade.domain.chapter.model.Chapter as DomainChapter
import eu.kanade.domain.manga.model.Manga as DomainManga
class MergedSource : HttpSource() {
private val db: DatabaseHelper by injectLazy()
@ -92,8 +99,8 @@ class MergedSource : HttpSource() {
}
// TODO more chapter dedupe
private fun transformMergedChapters(manga: Manga, chapterList: List<Chapter>, editScanlators: Boolean, dedupe: Boolean): List<Chapter> {
val mangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking()
private fun transformMergedChapters(manga: DomainManga, chapterList: List<Chapter>, editScanlators: Boolean, dedupe: Boolean): List<Chapter> {
val mangaReferences = db.getMergedMangaReferences(manga.id).executeAsBlocking()
if (editScanlators) {
val sources = mangaReferences.map { sourceManager.getOrStub(it.mangaSourceId) to it.mangaId }
chapterList.onEach { chapter ->
@ -107,12 +114,12 @@ class MergedSource : HttpSource() {
return if (dedupe) dedupeChapterList(mangaReferences, chapterList) else chapterList
}
fun getChaptersAsBlocking(manga: Manga, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return transformMergedChapters(manga, db.getChaptersByMergedMangaId(manga.id!!).executeAsBlocking(), editScanlators, dedupe)
fun getChaptersAsBlocking(manga: DomainManga, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return transformMergedChapters(manga, db.getChaptersByMergedMangaId(manga.id).executeAsBlocking(), editScanlators, dedupe)
}
fun getChaptersObservable(manga: Manga, editScanlators: Boolean = false, dedupe: Boolean = true): Observable<List<Chapter>> {
return db.getChaptersByMergedMangaId(manga.id!!).asRxObservable()
fun getChaptersObservable(manga: DomainManga, editScanlators: Boolean = false, dedupe: Boolean = true): Observable<List<Chapter>> {
return db.getChaptersByMergedMangaId(manga.id).asRxObservable()
.map { chapterList ->
transformMergedChapters(manga, chapterList, editScanlators, dedupe)
}
@ -144,20 +151,21 @@ class MergedSource : HttpSource() {
return chapterList.maxByOrNull { it.chapter_number }?.manga_id
}
suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
suspend fun fetchChaptersForMergedManga(manga: DomainManga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true): List<Chapter> {
return withIOContext {
fetchChaptersAndSync(manga, downloadChapters)
getChaptersAsBlocking(manga, editScanlators, dedupe)
}
}
suspend fun fetchChaptersAndSync(manga: Manga, downloadChapters: Boolean = true): Pair<List<Chapter>, List<Chapter>> {
val mangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking()
suspend fun fetchChaptersAndSync(manga: DomainManga, downloadChapters: Boolean = true): Pair<List<DomainChapter>, List<DomainChapter>> {
val syncChaptersWithSource = Injekt.get<SyncChaptersWithSource>()
val mangaReferences = db.getMergedMangaReferences(manga.id).executeAsBlocking()
if (mangaReferences.isEmpty()) {
throw IllegalArgumentException("Manga references are empty, chapters unavailable, merge is likely corrupted")
}
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(db, preferences)
val ifDownloadNewChapters = downloadChapters && manga.toDbManga().shouldDownloadNewChapters(db, preferences)
val semaphore = Semaphore(5)
var exception: Exception? = null
return supervisorScope {
@ -175,11 +183,11 @@ class MergedSource : HttpSource() {
val chapterList = source.getChapterList(loadedManga.toMangaInfo())
.map(ChapterInfo::toSChapter)
val results =
syncChaptersWithSource(chapterList, loadedManga, source)
syncChaptersWithSource.await(chapterList, loadedManga.toDomainManga()!!, source)
if (ifDownloadNewChapters && reference.downloadChapters) {
downloadManager.downloadChapters(
loadedManga,
results.first,
results.first.map(DomainChapter::toDbChapter),
)
}
results

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -641,7 +642,7 @@ class LibraryPresenter(
val mergedSource = sourceManager.get(MERGED_SOURCE_ID) as MergedSource
val mergedMangas = db.getMergedMangas(manga.id!!).executeAsBlocking()
mergedSource
.getChaptersAsBlocking(manga)
.getChaptersAsBlocking(manga.toDomainManga()!!)
.filter { !it.read }
.groupBy { it.manga_id!! }
.forEach ab@{ (mangaId, chapters) ->
@ -710,7 +711,11 @@ class LibraryPresenter(
fun markReadStatus(mangas: List<Manga>, read: Boolean) {
mangas.forEach { manga ->
launchIO {
val chapters = if (manga.source == MERGED_SOURCE_ID) (sourceManager.get(MERGED_SOURCE_ID) as MergedSource).getChaptersAsBlocking(manga) else db.getChapters(manga).executeAsBlocking()
val chapters = if (manga.source == MERGED_SOURCE_ID) {
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource).getChaptersAsBlocking(manga.toDomainManga()!!)
} else {
db.getChapters(manga).executeAsBlocking()
}
chapters.forEach {
it.read = read
if (!read) {
@ -815,7 +820,7 @@ class LibraryPresenter(
/** Returns first unread chapter of a manga */
fun getFirstUnread(manga: Manga): Chapter? {
val chapters = if (manga.source == MERGED_SOURCE_ID) {
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource).getChaptersAsBlocking(manga)
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource).getChaptersAsBlocking(manga.toDomainManga()!!)
} else db.getChapters(manga).executeAsBlocking()
return if (manga.isEhBasedManga()) {
val chapter = chapters.sortedBy { it.source_order }.getOrNull(0)

View File

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track
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.model.Download
@ -222,7 +223,7 @@ class MangaPresenter(
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(
(/* SY --> */if (source is MergedSource) source.getChaptersObservable(manga, true, dedupe) else /* SY <-- */ db.getChapters(manga).asRxObservable())
(/* SY --> */if (source is MergedSource) source.getChaptersObservable(manga.toDomainManga()!!, true, dedupe) else /* SY <-- */ db.getChapters(manga).asRxObservable())
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
@ -761,7 +762,7 @@ class MangaPresenter(
downloadNewChapters(newChapters)
}
} else {
source.fetchChaptersForMergedManga(manga, manualFetch, true, dedupe)
source.fetchChaptersForMergedManga(manga.toDomainManga()!!, manualFetch, true, dedupe)
}
withUIContext { view?.onFetchChaptersDone() }

View File

@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.saver.Image
@ -47,7 +48,6 @@ import eu.kanade.tachiyomi.util.lang.takeBytes
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.cacheImageDir
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.isLTR
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.updateCoverLastModified
@ -146,7 +146,7 @@ class ReaderPresenter(
// SY <--
val dbChapters = /* SY --> */ if (manga.source == MERGED_SOURCE_ID) {
(sourceManager.get(MERGED_SOURCE_ID) as MergedSource)
.getChaptersAsBlocking(manga)
.getChaptersAsBlocking(manga.toDomainManga()!!)
} else /* SY <-- */ db.getChapters(manga).executeAsBlocking()
val selectedChapter = dbChapters.find { it.id == chapterId }

View File

@ -69,7 +69,22 @@ WHERE favorite = 0 AND source IN :sourceIdsAND AND _id NOT IN (
SELECT manga_id FROM chapters WHERE read = 1 OR last_page_read != 0
);
updateLastUpdate:
UPDATE mangas
SET last_update = :lastUpdate
update:
UPDATE mangas SET
source = coalesce(:source, source),
url = coalesce(:url, url),
artist = coalesce(:artist, artist),
author = coalesce(:author, author),
description = coalesce(:description, description),
genre = coalesce(:genre, genre),
title = coalesce(:title, title),
status = coalesce(:status, status),
thumbnail_url = coalesce(:thumbnailUrl, thumbnail_url),
favorite = coalesce(:favorite, favorite),
last_update = coalesce(:lastUpdate, last_update),
initialized = coalesce(:initialized, initialized),
viewer = coalesce(:viewer, viewer),
chapter_flags = coalesce(:chapterFlags, chapter_flags),
cover_last_modified = coalesce(:coverLastModified, cover_last_modified),
date_added = coalesce(:dateAdded, date_added)
WHERE _id = :mangaId;