Use SQLDelight for mass migration

This commit is contained in:
Jobobby04 2022-07-03 00:33:03 -04:00
parent 664f9b1484
commit e71c9e2775
4 changed files with 148 additions and 113 deletions

View File

@ -1,18 +1,17 @@
package eu.kanade.tachiyomi.ui.browse.migration.advanced.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.domain.manga.interactor.GetMangaById
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import exh.util.DeferredField import exh.util.DeferredField
import exh.util.executeOnIO
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class MigratingManga( class MigratingManga(
private val db: DatabaseHelper, private val getMangaById: GetMangaById,
private val sourceManager: SourceManager, private val sourceManager: SourceManager,
val mangaId: Long, val mangaId: Long,
parentContext: CoroutineContext, parentContext: CoroutineContext,
@ -29,7 +28,7 @@ class MigratingManga(
@Volatile @Volatile
private var manga: Manga? = null private var manga: Manga? = null
suspend fun manga(): Manga? { suspend fun manga(): Manga? {
if (manga == null) manga = db.getManga(mangaId).executeOnIO() if (manga == null) manga = getMangaById.await(mangaId)
return manga return manga
} }

View File

@ -15,17 +15,20 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
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.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toDomainManga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationListControllerBinding import eu.kanade.tachiyomi.databinding.MigrationListControllerBinding
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.toSChapter import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.ui.base.changehandler.OneWayFadeChangeHandler import eu.kanade.tachiyomi.ui.base.changehandler.OneWayFadeChangeHandler
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
@ -35,15 +38,12 @@ import eu.kanade.tachiyomi.ui.browse.migration.MigrationMangaDialog
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.eh.EHentaiThrottleManager import exh.eh.EHentaiThrottleManager
import exh.smartsearch.SmartSearchEngine import exh.smartsearch.SmartSearchEngine
import exh.util.executeOnIO
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -70,11 +70,13 @@ class MigrationListController(bundle: Bundle? = null) :
val config: MigrationProcedureConfig? = args.getParcelable(CONFIG_EXTRA) val config: MigrationProcedureConfig? = args.getParcelable(CONFIG_EXTRA)
private val db: DatabaseHelper by injectLazy()
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val smartSearchEngine = SmartSearchEngine(config?.extraSearchParams) private val smartSearchEngine = SmartSearchEngine(config?.extraSearchParams)
private val syncChaptersWithSource: SyncChaptersWithSource by injectLazy()
private val getMangaById: GetMangaById by injectLazy()
private val updateManga: UpdateManga by injectLazy()
private val migrationScope = CoroutineScope(Job() + Dispatchers.IO) private val migrationScope = CoroutineScope(Job() + Dispatchers.IO)
var migrationsJob: Job? = null var migrationsJob: Job? = null
@ -107,7 +109,7 @@ class MigrationListController(bundle: Bundle? = null) :
val newMigratingManga = migratingManga ?: run { val newMigratingManga = migratingManga ?: run {
val new = config.mangaIds.map { val new = config.mangaIds.map {
MigratingManga(db, sourceManager, it, migrationScope.coroutineContext) MigratingManga(getMangaById, sourceManager, it, migrationScope.coroutineContext)
} }
migratingManga = new.toMutableList() migratingManga = new.toMutableList()
new new
@ -172,16 +174,16 @@ class MigrationListController(bundle: Bundle? = null) :
sourceSemaphore.withPermit { sourceSemaphore.withPermit {
try { try {
val searchResult = if (useSmartSearch) { val searchResult = if (useSmartSearch) {
smartSearchEngine.smartSearch(source, mangaObj.originalTitle) smartSearchEngine.smartSearch(source, mangaObj.ogTitle)
} else { } else {
smartSearchEngine.normalSearch(source, mangaObj.originalTitle) smartSearchEngine.normalSearch(source, mangaObj.ogTitle)
} }
if (searchResult != null && !(searchResult.url == mangaObj.url && source.id == mangaObj.source)) { if (searchResult != null && !(searchResult.url == mangaObj.url && source.id == mangaObj.source)) {
val localManga = smartSearchEngine.networkToLocalManga( val localManga = smartSearchEngine.networkToLocalManga(
searchResult, searchResult,
source.id, source.id,
) ).toDomainManga()!!
val chapters = if (source is EHentai) { val chapters = if (source is EHentai) {
source.getChapterList(localManga.toMangaInfo(), throttleManager::throttle) source.getChapterList(localManga.toMangaInfo(), throttleManager::throttle)
@ -190,7 +192,7 @@ class MigrationListController(bundle: Bundle? = null) :
} }
try { try {
syncChaptersWithSource(chapters.map { it.toSChapter() }, localManga, source) syncChaptersWithSource.await(chapters.map { it.toSChapter() }, localManga, source)
} catch (e: Exception) { } catch (e: Exception) {
return@async2 null return@async2 null
} }
@ -212,13 +214,13 @@ class MigrationListController(bundle: Bundle? = null) :
validSources.forEachIndexed { index, source -> validSources.forEachIndexed { index, source ->
val searchResult = try { val searchResult = try {
val searchResult = if (useSmartSearch) { val searchResult = if (useSmartSearch) {
smartSearchEngine.smartSearch(source, mangaObj.originalTitle) smartSearchEngine.smartSearch(source, mangaObj.ogTitle)
} else { } else {
smartSearchEngine.normalSearch(source, mangaObj.originalTitle) smartSearchEngine.normalSearch(source, mangaObj.ogTitle)
} }
if (searchResult != null) { if (searchResult != null) {
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id) val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id).toDomainManga()!!
val chapters = try { val chapters = try {
if (source is EHentai) { if (source is EHentai) {
source.getChapterList(localManga.toMangaInfo(), throttleManager::throttle) source.getChapterList(localManga.toMangaInfo(), throttleManager::throttle)
@ -229,9 +231,7 @@ class MigrationListController(bundle: Bundle? = null) :
this@MigrationListController.logcat(LogPriority.ERROR, e) this@MigrationListController.logcat(LogPriority.ERROR, e)
emptyList() emptyList()
} }
withIOContext { syncChaptersWithSource.await(chapters, localManga, source)
syncChaptersWithSource(chapters, localManga, source)
}
localManga localManga
} else null } else null
} catch (e: CancellationException) { } catch (e: CancellationException) {
@ -252,12 +252,10 @@ class MigrationListController(bundle: Bundle? = null) :
continue continue
} }
if (result != null && result.thumbnail_url == null) { if (result != null && result.thumbnailUrl == null) {
try { try {
val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo()) val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo())
result.copyFrom(newManga.toSManga()) updateManga.awaitUpdateFromSource(result, newManga, true)
db.insertManga(result).executeOnIO()
} catch (e: CancellationException) { } catch (e: CancellationException) {
// Ignore cancellations // Ignore cancellations
throw e throw e
@ -335,7 +333,7 @@ class MigrationListController(bundle: Bundle? = null) :
} else { } else {
sources.filter { it.id != manga.source } sources.filter { it.id != manga.source }
} }
val searchController = SearchController(manga, validSources) val searchController = SearchController(manga.toDbManga(), validSources)
searchController.targetController = this@MigrationListController searchController.targetController = this@MigrationListController
router.pushController(searchController) router.pushController(searchController)
} }
@ -359,11 +357,11 @@ class MigrationListController(bundle: Bundle? = null) :
adapter?.notifyItemChanged(firstIndex) adapter?.notifyItemChanged(firstIndex)
launchUI { launchUI {
val result = CoroutineScope(migratingManga.manga.migrationJob).async { val result = CoroutineScope(migratingManga.manga.migrationJob).async {
val localManga = smartSearchEngine.networkToLocalManga(manga, source.id) val localManga = smartSearchEngine.networkToLocalManga(manga, source.id).toDomainManga()!!
try { try {
val chapters = source.getChapterList(localManga.toMangaInfo()) val chapters = source.getChapterList(localManga.toMangaInfo())
.map { it.toSChapter() } .map { it.toSChapter() }
syncChaptersWithSource(chapters, localManga, source) syncChaptersWithSource.await(chapters, localManga, source)
} catch (e: Exception) { } catch (e: Exception) {
return@async null return@async null
} }
@ -373,9 +371,7 @@ class MigrationListController(bundle: Bundle? = null) :
if (result != null) { if (result != null) {
try { try {
val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo()) val newManga = sourceManager.getOrStub(result.source).getMangaDetails(result.toMangaInfo())
result.copyFrom(newManga.toSManga()) updateManga.awaitUpdateFromSource(result, newManga, true)
db.insertManga(result).executeOnIO()
} catch (e: CancellationException) { } catch (e: CancellationException) {
// Ignore cancellations // Ignore cancellations
throw e throw e
@ -413,14 +409,14 @@ class MigrationListController(bundle: Bundle? = null) :
val hasDetails = router.backstack.any { it.controller is MangaController } val hasDetails = router.backstack.any { it.controller is MangaController }
if (hasDetails) { if (hasDetails) {
val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let { val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let {
db.getManga(it).executeOnIO() getMangaById.await(it)
} }
if (manga != null) { if (manga != null) {
val newStack = router.backstack.filter { val newStack = router.backstack.filter {
it.controller !is MangaController && it.controller !is MangaController &&
it.controller !is MigrationListController && it.controller !is MigrationListController &&
it.controller !is PreMigrationController it.controller !is PreMigrationController
} + MangaController(manga.id!!).withFadeTransaction() } + MangaController(manga.id).withFadeTransaction()
router.setBackstack(newStack, OneWayFadeChangeHandler()) router.setBackstack(newStack, OneWayFadeChangeHandler())
return@launchUI return@launchUI
} }

View File

@ -2,15 +2,28 @@ package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.view.MenuItem import android.view.MenuItem
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.history.historyMapper
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.model.HistoryUpdate
import eu.kanade.domain.manga.interactor.GetMangaById
import eu.kanade.domain.manga.interactor.UpdateManga
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.toDbManga
import eu.kanade.domain.track.interactor.DeleteTrack
import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.tachiyomi.data.cache.CoverCache 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.History
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.util.hasCustomCover
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
@ -19,9 +32,19 @@ import uy.kohesive.injekt.injectLazy
class MigrationProcessAdapter( class MigrationProcessAdapter(
val controller: MigrationListController, val controller: MigrationListController,
) : FlexibleAdapter<MigrationProcessItem>(null, controller, true) { ) : FlexibleAdapter<MigrationProcessItem>(null, controller, true) {
private val db: DatabaseHelper by injectLazy() private val handler: DatabaseHandler by injectLazy()
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private val coverCache: CoverCache by injectLazy() private val coverCache: CoverCache by injectLazy()
private val getMangaById: GetMangaById by injectLazy()
private val updateManga: UpdateManga by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
private val upsertHistory: UpsertHistory by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val setMangaCategories: SetMangaCategories by injectLazy()
private val getTracks: GetTracks by injectLazy()
private val insertTrack: InsertTrack by injectLazy()
private val deleteTrack: DeleteTrack by injectLazy()
var items: List<MigrationProcessItem> = emptyList() var items: List<MigrationProcessItem> = emptyList()
@ -55,19 +78,16 @@ class MigrationProcessAdapter(
suspend fun performMigrations(copy: Boolean) { suspend fun performMigrations(copy: Boolean) {
withIOContext { withIOContext {
db.inTransaction { currentItems.forEach { migratingManga ->
currentItems.forEach { migratingManga -> val manga = migratingManga.manga
val manga = migratingManga.manga if (manga.searchResult.initialized) {
if (manga.searchResult.initialized) { val toMangaObj = getMangaById.await(manga.searchResult.get() ?: return@forEach)
val toMangaObj = ?: return@forEach
db.getManga(manga.searchResult.get() ?: return@forEach).executeAsBlocking() migrateMangaInternal(
?: return@forEach manga.manga() ?: return@forEach,
migrateMangaInternal( toMangaObj,
manga.manga() ?: return@forEach, !copy,
toMangaObj, )
!copy,
)
}
} }
} }
} }
@ -76,16 +96,15 @@ class MigrationProcessAdapter(
fun migrateManga(position: Int, copy: Boolean) { fun migrateManga(position: Int, copy: Boolean) {
launchUI { launchUI {
val manga = getItem(position)?.manga ?: return@launchUI val manga = getItem(position)?.manga ?: return@launchUI
db.inTransaction {
val toMangaObj = val toMangaObj = getMangaById.await(manga.searchResult.get() ?: return@launchUI)
db.getManga(manga.searchResult.get() ?: return@launchUI).executeAsBlocking() ?: return@launchUI
?: return@launchUI migrateMangaInternal(
migrateMangaInternal( manga.manga() ?: return@launchUI,
manga.manga() ?: return@launchUI, toMangaObj,
toMangaObj, !copy,
!copy, )
)
}
removeManga(position) removeManga(position)
} }
} }
@ -107,7 +126,7 @@ class MigrationProcessAdapter(
sourceFinished() sourceFinished()
} }
private fun migrateMangaInternal( private suspend fun migrateMangaInternal(
prevManga: Manga, prevManga: Manga,
manga: Manga, manga: Manga,
replace: Boolean, replace: Boolean,
@ -116,69 +135,87 @@ class MigrationProcessAdapter(
val flags = preferences.migrateFlags().get() val flags = preferences.migrateFlags().get()
// Update chapters read // Update chapters read
if (MigrationFlags.hasChapters(flags)) { if (MigrationFlags.hasChapters(flags)) {
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking() val prevMangaChapters = getChapterByMangaId.await(prevManga.id)
val maxChapterRead = val maxChapterRead =
prevMangaChapters.filter(Chapter::read).maxOfOrNull(Chapter::chapter_number) prevMangaChapters.filter(Chapter::read).maxOfOrNull(Chapter::chapterNumber)
val dbChapters = db.getChapters(manga).executeAsBlocking() val dbChapters = getChapterByMangaId.await(manga.id)
val prevHistoryList = db.getHistoryByMangaId(prevManga.id!!).executeAsBlocking() val prevHistoryList = handler.awaitList { historyQueries.getHistoryByMangaId(prevManga.id, historyMapper) }
val historyList = mutableListOf<History>()
val chapterUpdates = mutableListOf<ChapterUpdate>()
val historyUpdates = mutableListOf<HistoryUpdate>()
dbChapters.forEach { chapter -> dbChapters.forEach { chapter ->
if (chapter.isRecognizedNumber) { if (chapter.isRecognizedNumber) {
val prevChapter = val prevChapter = prevMangaChapters.find { it.isRecognizedNumber && it.chapterNumber == chapter.chapterNumber }
prevMangaChapters.find { it.isRecognizedNumber && it.chapter_number == chapter.chapter_number }
if (prevChapter != null) { if (prevChapter != null) {
chapter.bookmark = prevChapter.bookmark chapterUpdates += ChapterUpdate(
chapter.read = prevChapter.read id = chapter.id,
chapter.date_fetch = prevChapter.date_fetch bookmark = prevChapter.bookmark,
prevHistoryList.find { it.chapter_id == prevChapter.id }?.let { prevHistory -> read = prevChapter.read,
val history = History.create(chapter).apply { last_read = prevHistory.last_read } dateFetch = prevChapter.dateFetch,
historyList.add(history) )
prevHistoryList.find { it.chapterId == prevChapter.id }?.let { prevHistory ->
historyUpdates += HistoryUpdate(
chapter.id,
prevHistory.readAt ?: return@let,
prevHistory.readDuration,
)
} }
} else if (maxChapterRead != null && chapter.chapter_number <= maxChapterRead) { } else if (maxChapterRead != null && chapter.chapterNumber <= maxChapterRead) {
chapter.read = true chapterUpdates += ChapterUpdate(
id = chapter.id,
read = true,
)
} }
} }
} }
db.insertChapters(dbChapters).executeAsBlocking()
db.upsertHistoryLastRead(historyList).executeAsBlocking() updateChapter.awaitAll(chapterUpdates)
historyUpdates.forEach {
upsertHistory.await(it)
}
} }
// Update categories // Update categories
if (MigrationFlags.hasCategories(flags)) { if (MigrationFlags.hasCategories(flags)) {
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking() val categories = getCategories.await(prevManga.id)
val mangaCategories = categories.map { MangaCategory.create(manga, it) } setMangaCategories.await(manga.id, categories.map { it.id })
db.setMangaCategories(mangaCategories, listOf(manga))
} }
// Update track // Update track
if (MigrationFlags.hasTracks(flags)) { if (MigrationFlags.hasTracks(flags)) {
val tracks = db.getTracks(prevManga.id).executeAsBlocking() val tracks = getTracks.await(prevManga.id)
if (tracks.isNotEmpty()) { if (tracks.isNotEmpty()) {
tracks.forEach { track -> getTracks.await(manga.id).forEach {
track.id = null deleteTrack.await(manga.id, it.syncId)
track.manga_id = manga.id!!
} }
db.insertTracks(tracks).executeAsBlocking() insertTrack.awaitAll(tracks.map { it.copy(mangaId = manga.id) })
} }
} }
// Update custom cover // Update custom cover
if (MigrationFlags.hasCustomCover(flags) && prevManga.hasCustomCover(coverCache)) { if (MigrationFlags.hasCustomCover(flags) && prevManga.hasCustomCover(coverCache)) {
coverCache.setCustomCoverToCache(manga, coverCache.getCustomCoverFile(prevManga.id).inputStream()) coverCache.setCustomCoverToCache(manga.toDbManga(), coverCache.getCustomCoverFile(prevManga.id).inputStream())
} }
var mangaUpdate = MangaUpdate(manga.id, favorite = true, dateAdded = System.currentTimeMillis())
var prevMangaUpdate: MangaUpdate? = null
// Update extras // Update extras
if (MigrationFlags.hasExtra(flags)) { if (MigrationFlags.hasExtra(flags)) {
manga.chapter_flags = prevManga.chapter_flags mangaUpdate = mangaUpdate.copy(
manga.viewer_flags = prevManga.viewer_flags chapterFlags = prevManga.chapterFlags,
viewerFlags = prevManga.viewerFlags,
)
} }
// Update favorite status // Update favorite status
if (replace) { if (replace) {
prevManga.favorite = false prevMangaUpdate = MangaUpdate(
manga.date_added = prevManga.date_added id = prevManga.id,
prevManga.date_added = 0 favorite = false,
db.updateMangaFavorite(prevManga).executeAsBlocking() dateAdded = 0,
} else { )
manga.date_added = System.currentTimeMillis() mangaUpdate = mangaUpdate.copy(
dateAdded = prevManga.dateAdded,
)
} }
manga.favorite = true
db.updateMangaMigrate(manga).executeAsBlocking() updateManga.awaitAll(listOfNotNull(mangaUpdate, prevMangaUpdate))
} }
} }

View File

@ -6,9 +6,11 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.dispose import coil.dispose
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.manga.interactor.GetMangaById
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.MigrationMangaCardBinding import eu.kanade.tachiyomi.databinding.MigrationMangaCardBinding
import eu.kanade.tachiyomi.databinding.MigrationProcessItemBinding import eu.kanade.tachiyomi.databinding.MigrationProcessItemBinding
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
@ -19,7 +21,6 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.view.loadAutoPause import eu.kanade.tachiyomi.util.view.loadAutoPause
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.util.executeOnIO
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
@ -30,8 +31,10 @@ class MigrationProcessHolder(
private val view: View, private val view: View,
private val adapter: MigrationProcessAdapter, private val adapter: MigrationProcessAdapter,
) : FlexibleViewHolder(view, adapter) { ) : FlexibleViewHolder(view, adapter) {
private val db: DatabaseHelper by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val getMangaById: GetMangaById by injectLazy()
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
private val getMergedReferencesById: GetMergedReferencesById by injectLazy()
private var item: MigrationProcessItem? = null private var item: MigrationProcessItem? = null
private val binding = MigrationProcessItemBinding.bind(view) private val binding = MigrationProcessItemBinding.bind(view)
@ -67,7 +70,7 @@ class MigrationProcessHolder(
.onEach { .onEach {
adapter.controller.router.pushController( adapter.controller.router.pushController(
MangaController( MangaController(
manga.id!!, manga.id,
true, true,
), ),
) )
@ -86,7 +89,7 @@ class MigrationProcessHolder(
}*/ }*/
val searchResult = item.manga.searchResult.get()?.let { val searchResult = item.manga.searchResult.get()?.let {
db.getManga(it).executeOnIO() getMangaById.await(it)
} }
val resultSource = searchResult?.source?.let { val resultSource = searchResult?.source?.let {
sourceManager.get(it) sourceManager.get(it)
@ -103,7 +106,7 @@ class MigrationProcessHolder(
.onEach { .onEach {
adapter.controller.router.pushController( adapter.controller.router.pushController(
MangaController( MangaController(
searchResult.id!!, searchResult.id,
true, true,
), ),
) )
@ -143,24 +146,24 @@ class MigrationProcessHolder(
title.text = if (manga.title.isBlank()) { title.text = if (manga.title.isBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
} else { } else {
manga.originalTitle manga.ogTitle
} }
mangaSourceLabel.text = if (source.id == MERGED_SOURCE_ID) { mangaSourceLabel.text = if (source.id == MERGED_SOURCE_ID) {
db.getMergedMangaReferences(manga.id!!).executeOnIO().map { getMergedReferencesById.await(manga.id).map {
sourceManager.getOrStub(it.mangaSourceId).toString() sourceManager.getOrStub(it.mangaSourceId).toString()
}.distinct().joinToString() }.distinct().joinToString()
} else { } else {
source.toString() source.toString()
} }
val chapters = db.getChapters(manga).executeOnIO() val chapters = getChapterByMangaId.await(manga.id)
// For rounded corners // For rounded corners
badges.leftBadges.clipToOutline = true badges.leftBadges.clipToOutline = true
badges.rightBadges.clipToOutline = true badges.rightBadges.clipToOutline = true
badges.unreadText.isVisible = true badges.unreadText.isVisible = true
badges.unreadText.text = chapters.size.toString() badges.unreadText.text = chapters.size.toString()
val latestChapter = chapters.maxByOrNull { it.chapter_number }?.chapter_number ?: -1f val latestChapter = chapters.maxOfOrNull { it.chapterNumber } ?: -1f
if (latestChapter > 0f) { if (latestChapter > 0f) {
mangaLastChapterLabel.text = root.context.getString( mangaLastChapterLabel.text = root.context.getString(