Use SQLDelight for mass migration
This commit is contained in:
parent
664f9b1484
commit
e71c9e2775
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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,12 +78,10 @@ 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 =
|
val toMangaObj = getMangaById.await(manga.searchResult.get() ?: return@forEach)
|
||||||
db.getManga(manga.searchResult.get() ?: return@forEach).executeAsBlocking()
|
|
||||||
?: return@forEach
|
?: return@forEach
|
||||||
migrateMangaInternal(
|
migrateMangaInternal(
|
||||||
manga.manga() ?: return@forEach,
|
manga.manga() ?: return@forEach,
|
||||||
@ -71,21 +92,19 @@ 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user