mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 02:34:39 +00:00
refactor: Migrated most manga queries and some chapter queries to SQLDelight
This commit is contained in:
parent
5ed2934b73
commit
f6080cd5eb
26 changed files with 450 additions and 211 deletions
|
@ -28,6 +28,9 @@
|
|||
- Update dependency com.google.gms:google-services to v4.4.2
|
||||
- Add crashlytics integration for Kermit
|
||||
- Replace ProgressBar with ProgressIndicator from Material3 to improve UI consistency
|
||||
- More StorIO to SQLDelight migrations
|
||||
- Merge lastFetch and lastRead query into library_view VIEW
|
||||
- Migrated a few more chapter related queries
|
||||
- Migrated most of manga related queries
|
||||
- Update Japenese translation
|
||||
- Update dependency com.github.tachiyomiorg:unifile to a9de196cc7
|
||||
|
|
|
@ -4,16 +4,17 @@ import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
|
||||
class CategoriesBackupRestorer(
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
) {
|
||||
@Suppress("RedundantSuspendModifier")
|
||||
suspend fun restoreCategories(backupCategories: List<BackupCategory>, onComplete: () -> Unit) {
|
||||
db.inTransaction {
|
||||
// Get categories from file and from db
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
// Do it outside of transaction because StorIO might hang because we're using SQLDelight
|
||||
val dbCategories = getCategories.await()
|
||||
db.inTransaction {
|
||||
// Iterate over them
|
||||
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
||||
// Used to know if the category is already in the db
|
||||
|
|
|
@ -17,14 +17,24 @@ import eu.kanade.tachiyomi.util.manga.MangaUtil
|
|||
import eu.kanade.tachiyomi.util.system.launchNow
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
import yokai.domain.chapter.interactor.GetChapters
|
||||
import yokai.domain.library.custom.model.CustomMangaInfo
|
||||
import yokai.domain.manga.interactor.GetManga
|
||||
import yokai.domain.manga.interactor.InsertManga
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
import kotlin.math.max
|
||||
|
||||
class MangaBackupRestorer(
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val customMangaManager: CustomMangaManager = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
private val getChapters: GetChapters = Injekt.get(),
|
||||
private val getManga: GetManga = Injekt.get(),
|
||||
private val insertManga: InsertManga = Injekt.get(),
|
||||
private val updateManga: UpdateManga = Injekt.get(),
|
||||
) {
|
||||
fun restoreManga(
|
||||
suspend fun restoreManga(
|
||||
backupManga: BackupManga,
|
||||
backupCategories: List<BackupCategory>,
|
||||
onComplete: (Manga) -> Unit,
|
||||
|
@ -40,19 +50,19 @@ class MangaBackupRestorer(
|
|||
val filteredScanlators = backupManga.excludedScanlators
|
||||
|
||||
try {
|
||||
val dbManga = db.getManga(manga.url, manga.source).executeAsBlocking()
|
||||
val dbManga = getManga.awaitByUrlAndSource(manga.url, manga.source)
|
||||
if (dbManga == null) {
|
||||
// Manga not in database
|
||||
restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
restoreNewManga(manga, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
} else {
|
||||
// Manga in database
|
||||
// Copy information from manga already in database
|
||||
manga.id = dbManga.id
|
||||
manga.filtered_scanlators = dbManga.filtered_scanlators
|
||||
manga.copyFrom(dbManga)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
updateManga.await(manga.toMangaUpdate())
|
||||
// Fetch rest of manga information
|
||||
restoreNewManga(manga, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
onError(manga, e)
|
||||
|
@ -69,7 +79,7 @@ class MangaBackupRestorer(
|
|||
* @param chapters chapters of manga that needs updating
|
||||
* @param categories categories that need updating
|
||||
*/
|
||||
private fun restoreExistingManga(
|
||||
private suspend fun restoreNewManga(
|
||||
manga: Manga,
|
||||
chapters: List<Chapter>,
|
||||
categories: List<Int>,
|
||||
|
@ -81,7 +91,7 @@ class MangaBackupRestorer(
|
|||
) {
|
||||
val fetchedManga = manga.also {
|
||||
it.initialized = it.description != null
|
||||
it.id = db.insertManga(it).executeAsBlocking().insertedId()
|
||||
it.id = insertManga.await(it)
|
||||
}
|
||||
fetchedManga.id ?: return
|
||||
|
||||
|
@ -89,7 +99,7 @@ class MangaBackupRestorer(
|
|||
restoreExtras(fetchedManga, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
}
|
||||
|
||||
private fun restoreNewManga(
|
||||
private suspend fun restoreExistingManga(
|
||||
backupManga: Manga,
|
||||
chapters: List<Chapter>,
|
||||
categories: List<Int>,
|
||||
|
@ -103,8 +113,8 @@ class MangaBackupRestorer(
|
|||
restoreExtras(backupManga, categories, history, tracks, backupCategories, filteredScanlators, customManga)
|
||||
}
|
||||
|
||||
private fun restoreChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
private suspend fun restoreChapters(manga: Manga, chapters: List<Chapter>) {
|
||||
val dbChapters = getChapters.await(manga)
|
||||
|
||||
chapters.forEach { chapter ->
|
||||
val dbChapter = dbChapters.find { it.url == chapter.url }
|
||||
|
@ -130,7 +140,7 @@ class MangaBackupRestorer(
|
|||
newChapters[false]?.let { db.insertChapters(it).executeAsBlocking() }
|
||||
}
|
||||
|
||||
private fun restoreExtras(
|
||||
private suspend fun restoreExtras(
|
||||
manga: Manga,
|
||||
categories: List<Int>,
|
||||
history: List<BackupHistory>,
|
||||
|
@ -157,8 +167,8 @@ class MangaBackupRestorer(
|
|||
* @param manga the manga whose categories have to be restored.
|
||||
* @param categories the categories to restore.
|
||||
*/
|
||||
private fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
private suspend fun restoreCategories(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
|
||||
val dbCategories = getCategories.await()
|
||||
val mangaCategoriesToUpdate = ArrayList<MangaCategory>(categories.size)
|
||||
categories.forEach { backupCategoryOrder ->
|
||||
backupCategories.firstOrNull {
|
||||
|
@ -184,7 +194,7 @@ class MangaBackupRestorer(
|
|||
*
|
||||
* @param history list containing history to be restored
|
||||
*/
|
||||
internal fun restoreHistoryForManga(history: List<BackupHistory>) {
|
||||
internal suspend fun restoreHistoryForManga(history: List<BackupHistory>) {
|
||||
// List containing history to be updated
|
||||
val historyToBeUpdated = ArrayList<History>(history.size)
|
||||
for ((url, lastRead, readDuration) in history) {
|
||||
|
@ -216,7 +226,7 @@ class MangaBackupRestorer(
|
|||
* @param manga the manga whose sync have to be restored.
|
||||
* @param tracks the track list to restore.
|
||||
*/
|
||||
private fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
|
||||
private suspend fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
|
||||
// Fix foreign keys with the current manga id
|
||||
tracks.map { it.manga_id = manga.id!! }
|
||||
|
||||
|
@ -253,8 +263,8 @@ class MangaBackupRestorer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun restoreFilteredScanlatorsForManga(manga: Manga, filteredScanlators: List<String>) {
|
||||
private suspend fun restoreFilteredScanlatorsForManga(manga: Manga, filteredScanlators: List<String>) {
|
||||
val actualList = ChapterUtil.getScanlators(manga.filtered_scanlators) + filteredScanlators
|
||||
MangaUtil.setScanlatorFilter(db, manga, actualList.toSet())
|
||||
MangaUtil.setScanlatorFilter(updateManga, manga, actualList.toSet())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
|||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.data.updateStrategyAdapter
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
import java.util.*
|
||||
|
||||
// TODO: Transform into data class
|
||||
|
@ -327,6 +328,30 @@ interface Manga : SManga {
|
|||
MangaCoverMetadata.addCoverColor(this, value.first, value.second)
|
||||
}
|
||||
|
||||
fun toMangaUpdate(): MangaUpdate {
|
||||
return MangaUpdate(
|
||||
id = id!!,
|
||||
source = source,
|
||||
url = url,
|
||||
artist = artist,
|
||||
author = author,
|
||||
description = description,
|
||||
genres = genre?.split(", ").orEmpty(),
|
||||
title = title,
|
||||
status = status,
|
||||
thumbnailUrl = thumbnail_url,
|
||||
favorite = favorite,
|
||||
lastUpdate = last_update,
|
||||
initialized = initialized,
|
||||
viewerFlags = viewer_flags,
|
||||
hideTitle = hide_title,
|
||||
chapterFlags = chapter_flags,
|
||||
dateAdded = date_added,
|
||||
filteredScanlators = filtered_scanlators,
|
||||
updateStrategy = update_strategy,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// Generic filter that does not filter anything
|
||||
|
|
|
@ -6,10 +6,8 @@ import eu.kanade.tachiyomi.data.database.DbProvider
|
|||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.ChapterBackupPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.ChapterKnownBackupPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.ChapterSourceOrderPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
import eu.kanade.tachiyomi.util.lang.sqLite
|
||||
|
@ -88,15 +86,6 @@ interface ChapterQueries : DbProvider {
|
|||
|
||||
fun insertChapters(chapters: List<Chapter>) = db.put().objects(chapters).prepare()
|
||||
|
||||
fun deleteChapter(chapter: Chapter) = db.delete().`object`(chapter).prepare()
|
||||
|
||||
fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare()
|
||||
|
||||
fun updateChaptersBackup(chapters: List<Chapter>) = db.put()
|
||||
.objects(chapters)
|
||||
.withPutResolver(ChapterBackupPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun updateKnownChaptersBackup(chapters: List<Chapter>) = db.put()
|
||||
.objects(chapters)
|
||||
.withPutResolver(ChapterKnownBackupPutResolver())
|
||||
|
@ -111,9 +100,4 @@ interface ChapterQueries : DbProvider {
|
|||
.objects(chapters)
|
||||
.withPutResolver(ChapterProgressPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun fixChaptersSourceOrder(chapters: List<Chapter>) = db.put()
|
||||
.objects(chapters)
|
||||
.withPutResolver(ChapterSourceOrderPutResolver())
|
||||
.prepare()
|
||||
}
|
||||
|
|
|
@ -9,10 +9,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaDateAddedPutResolver
|
||||
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.MangaTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
|
@ -91,55 +87,24 @@ interface MangaQueries : DbProvider {
|
|||
|
||||
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
||||
|
||||
fun updateChapterFlags(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags))
|
||||
.prepare()
|
||||
|
||||
fun updateChapterFlags(manga: List<Manga>) = db.put()
|
||||
.objects(manga)
|
||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags, true))
|
||||
.prepare()
|
||||
|
||||
fun updateViewerFlags(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags))
|
||||
.prepare()
|
||||
|
||||
fun updateViewerFlags(manga: List<Manga>) = db.put()
|
||||
.objects(manga)
|
||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags, true))
|
||||
.prepare()
|
||||
|
||||
fun updateLastUpdated(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaLastUpdatedPutResolver())
|
||||
.prepare()
|
||||
|
||||
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
|
||||
fun updateMangaFavorite(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaFavoritePutResolver())
|
||||
.prepare()
|
||||
|
||||
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
|
||||
fun updateMangaAdded(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaDateAddedPutResolver())
|
||||
.prepare()
|
||||
|
||||
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
|
||||
fun updateMangaTitle(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaTitlePutResolver())
|
||||
.prepare()
|
||||
|
||||
fun updateMangaInfo(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaInfoPutResolver())
|
||||
.prepare()
|
||||
|
||||
fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare()
|
||||
|
||||
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()
|
||||
|
||||
fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
|
@ -166,14 +131,6 @@ interface MangaQueries : DbProvider {
|
|||
)
|
||||
.prepare()
|
||||
|
||||
fun deleteMangas() = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(MangaTable.TABLE)
|
||||
.build(),
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun getReadNotInLibraryMangas() = db.get()
|
||||
.listOfObjects(Manga::class.java)
|
||||
.withQuery(
|
||||
|
@ -182,12 +139,4 @@ interface MangaQueries : DbProvider {
|
|||
.build(),
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun getTotalChapterManga() = db.get().listOfObjects(Manga::class.java)
|
||||
.withQuery(RawQuery.builder().query(getTotalChapterMangaQuery()).observesTables(MangaTable.TABLE).build()).prepare()
|
||||
|
||||
fun updateMangaFilteredScanlators(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaFilteredScanlatorsPutResolver())
|
||||
.prepare()
|
||||
}
|
||||
|
|
|
@ -402,7 +402,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||
val fetchedChapters = source.getChapterList(manga.copy())
|
||||
|
||||
if (fetchedChapters.isNotEmpty()) {
|
||||
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
||||
val newChapters = syncChaptersWithSource(fetchedChapters, manga, source)
|
||||
if (newChapters.first.isNotEmpty()) {
|
||||
if (shouldDownload) {
|
||||
downloadChapters(
|
||||
|
|
|
@ -1136,7 +1136,7 @@ class LibraryPresenter(
|
|||
val mangaToDelete = mangas.distinctBy { it.id }
|
||||
.mapNotNull { if (it.id != null) MangaUpdate(it.id!!, favorite = false) else null }
|
||||
|
||||
withIOContext { updateManga.updateAll(mangaToDelete) }
|
||||
withIOContext { updateManga.awaitAll(mangaToDelete) }
|
||||
getLibrary()
|
||||
}
|
||||
}
|
||||
|
@ -1173,7 +1173,7 @@ class LibraryPresenter(
|
|||
val mangaToAdd = mangas.distinctBy { it.id }
|
||||
.mapNotNull { if (it.id != null) MangaUpdate(it.id!!, favorite = true) else null }
|
||||
|
||||
withIOContext { updateManga.updateAll(mangaToAdd) }
|
||||
withIOContext { updateManga.awaitAll(mangaToAdd) }
|
||||
(view as? FilteredLibraryController)?.updateStatsPage()
|
||||
getLibrary()
|
||||
}
|
||||
|
@ -1504,8 +1504,8 @@ class LibraryPresenter(
|
|||
fun updateDB() {
|
||||
val db: DatabaseHelper = Injekt.get()
|
||||
val getLibraryManga: GetLibraryManga by injectLazy()
|
||||
db.inTransaction {
|
||||
val libraryManga = runBlocking { getLibraryManga.await() }
|
||||
db.inTransaction {
|
||||
libraryManga.forEach { manga ->
|
||||
if (manga.date_added == 0L) {
|
||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||
|
@ -1529,8 +1529,8 @@ class LibraryPresenter(
|
|||
val db: DatabaseHelper = Injekt.get()
|
||||
val cc: CoverCache = Injekt.get()
|
||||
val getLibraryManga: GetLibraryManga by injectLazy()
|
||||
db.inTransaction {
|
||||
val libraryManga = runBlocking { getLibraryManga.await() }
|
||||
db.inTransaction {
|
||||
libraryManga.forEach { manga ->
|
||||
if (manga.thumbnail_url?.startsWith("custom", ignoreCase = true) == true) {
|
||||
val file = cc.getCoverFile(manga)
|
||||
|
|
|
@ -53,10 +53,10 @@ import eu.kanade.tachiyomi.util.manga.MangaUtil
|
|||
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.launchNow
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||
import eu.kanade.tachiyomi.widget.TriStateCheckBox
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -74,6 +74,8 @@ import uy.kohesive.injekt.injectLazy
|
|||
import yokai.domain.chapter.interactor.GetAvailableScanlators
|
||||
import yokai.domain.chapter.interactor.GetChapters
|
||||
import yokai.domain.library.custom.model.CustomMangaInfo
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
import yokai.domain.storage.StorageManager
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
@ -92,6 +94,7 @@ class MangaDetailsPresenter(
|
|||
) : BaseCoroutinePresenter<MangaDetailsController>(), DownloadQueue.DownloadListener {
|
||||
private val getAvailableScanlators: GetAvailableScanlators by injectLazy()
|
||||
private val getChapters: GetChapters by injectLazy()
|
||||
private val updateManga: UpdateManga by injectLazy()
|
||||
|
||||
private val customMangaManager: CustomMangaManager by injectLazy()
|
||||
private val mangaShortcutManager: MangaShortcutManager by injectLazy()
|
||||
|
@ -404,7 +407,7 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
val finChapters = chapters.await()
|
||||
if (finChapters.isNotEmpty()) {
|
||||
val newChapters = syncChaptersWithSource(db, finChapters, manga, source)
|
||||
val newChapters = withIOContext { syncChaptersWithSource(finChapters, manga, source) }
|
||||
if (newChapters.first.isNotEmpty()) {
|
||||
if (manga.shouldDownloadNewChapters(db, preferences)) {
|
||||
downloadChapters(
|
||||
|
@ -469,7 +472,7 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
isLoading = false
|
||||
try {
|
||||
syncChaptersWithSource(db, chapters, manga, source)
|
||||
syncChaptersWithSource(chapters, manga, source)
|
||||
|
||||
getChapters()
|
||||
withContext(Dispatchers.Main) {
|
||||
|
@ -555,14 +558,14 @@ class MangaDetailsPresenter(
|
|||
if (mangaSortMatchesDefault()) {
|
||||
manga.setSortToGlobal()
|
||||
}
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
fun setGlobalChapterSort(sort: Int, descend: Boolean) {
|
||||
preferences.sortChapterOrder().set(sort)
|
||||
preferences.chaptersDescAsDefault().set(descend)
|
||||
manga.setSortToGlobal()
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
fun mangaSortMatchesDefault(): Boolean {
|
||||
|
@ -583,7 +586,7 @@ class MangaDetailsPresenter(
|
|||
|
||||
fun resetSortingToDefault() {
|
||||
manga.setSortToGlobal()
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -613,7 +616,7 @@ class MangaDetailsPresenter(
|
|||
if (mangaFilterMatchesDefault()) {
|
||||
manga.setFilterToGlobal()
|
||||
}
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -623,7 +626,7 @@ class MangaDetailsPresenter(
|
|||
fun hideTitle(hide: Boolean) {
|
||||
manga.displayMode = if (hide) Manga.CHAPTER_DISPLAY_NUMBER else Manga.CHAPTER_DISPLAY_NAME
|
||||
manga.setFilterToLocal()
|
||||
db.updateChapterFlags(manga).executeAsBlocking()
|
||||
presenterScope.launchIO { updateManga.await(MangaUpdate(manga.id!!, chapterFlags = manga.chapter_flags)) }
|
||||
if (mangaFilterMatchesDefault()) {
|
||||
manga.setFilterToGlobal()
|
||||
}
|
||||
|
@ -632,7 +635,7 @@ class MangaDetailsPresenter(
|
|||
|
||||
fun resetFilterToDefault() {
|
||||
manga.setFilterToGlobal()
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
fun setGlobalChapterFilters(
|
||||
|
@ -663,15 +666,13 @@ class MangaDetailsPresenter(
|
|||
)
|
||||
preferences.hideChapterTitlesByDefault().set(manga.hideChapterTitles)
|
||||
manga.setFilterToGlobal()
|
||||
asyncUpdateMangaAndChapters()
|
||||
presenterScope.launchIO { asyncUpdateMangaAndChapters() }
|
||||
}
|
||||
|
||||
private fun asyncUpdateMangaAndChapters(justChapters: Boolean = false) {
|
||||
presenterScope.launch {
|
||||
if (!justChapters) db.updateChapterFlags(manga).executeOnIO()
|
||||
private suspend fun asyncUpdateMangaAndChapters(justChapters: Boolean = false) {
|
||||
if (!justChapters) updateManga.await(MangaUpdate(manga.id!!, chapterFlags = manga.chapter_flags))
|
||||
getChapters()
|
||||
withContext(Dispatchers.Main) { view?.updateChapters(chapters) }
|
||||
}
|
||||
withUIContext { view?.updateChapters(chapters) }
|
||||
}
|
||||
|
||||
private fun isScanlatorFiltered() = manga.filtered_scanlators?.isNotEmpty() == true
|
||||
|
@ -690,14 +691,16 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
|
||||
fun setScanlatorFilter(filteredScanlators: Set<String>) {
|
||||
presenterScope.launchIO {
|
||||
val manga = manga
|
||||
MangaUtil.setScanlatorFilter(
|
||||
db,
|
||||
updateManga,
|
||||
manga,
|
||||
if (filteredScanlators.size == allChapterScanlators.size) emptySet() else filteredScanlators
|
||||
)
|
||||
asyncUpdateMangaAndChapters()
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleFavorite(): Boolean {
|
||||
manga.favorite = !manga.favorite
|
||||
|
@ -802,11 +805,23 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
}
|
||||
manga.viewer_flags = -1
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
presenterScope.launchIO { updateManga.await(MangaUpdate(manga.id!!, viewerFlags = manga.viewer_flags)) }
|
||||
}
|
||||
manga.status = status ?: SManga.UNKNOWN
|
||||
LocalSource(downloadManager.context).updateMangaInfo(manga, lang)
|
||||
db.updateMangaInfo(manga).executeAsBlocking()
|
||||
presenterScope.launchIO {
|
||||
updateManga.await(
|
||||
MangaUpdate(
|
||||
manga.id!!,
|
||||
title = manga.originalTitle,
|
||||
author = manga.originalAuthor,
|
||||
artist = manga.originalArtist,
|
||||
description = manga.originalDescription,
|
||||
genres = manga.originalGenre?.split(", ").orEmpty(),
|
||||
status = manga.originalStatus,
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
var genre = if (!tags.isNullOrEmpty() && tags.joinToString(", ") != manga.originalGenre) {
|
||||
tags.map { tag -> tag.replaceFirstChar { it.titlecase(Locale.getDefault()) } }
|
||||
|
@ -817,7 +832,7 @@ class MangaDetailsPresenter(
|
|||
if (seriesType != null) {
|
||||
genre = setSeriesType(seriesType, genre?.joinToString())
|
||||
manga.viewer_flags = -1
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
presenterScope.launchIO { updateManga.await(MangaUpdate(manga.id!!, viewerFlags = manga.viewer_flags)) }
|
||||
}
|
||||
val manga = CustomMangaInfo(
|
||||
mangaId = manga.id!!,
|
||||
|
|
|
@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
|||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
|
@ -61,7 +62,7 @@ import kotlinx.coroutines.sync.Semaphore
|
|||
import kotlinx.coroutines.sync.withPermit
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class MigrationListController(bundle: Bundle? = null) :
|
||||
|
@ -190,7 +191,6 @@ class MigrationListController(bundle: Bundle? = null) :
|
|||
val chapters = source.getChapterList(localManga)
|
||||
try {
|
||||
syncChaptersWithSource(
|
||||
db,
|
||||
chapters,
|
||||
localManga,
|
||||
source,
|
||||
|
@ -232,7 +232,7 @@ class MigrationListController(bundle: Bundle? = null) :
|
|||
emptyList()
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
syncChaptersWithSource(db, chapters, localManga, source)
|
||||
syncChaptersWithSource(chapters, localManga, source)
|
||||
}
|
||||
localManga
|
||||
} else {
|
||||
|
@ -368,7 +368,7 @@ class MigrationListController(bundle: Bundle? = null) :
|
|||
val localManga = smartSearchEngine.networkToLocalManga(manga, source.id)
|
||||
try {
|
||||
val chapters = source.getChapterList(localManga)
|
||||
syncChaptersWithSource(db, chapters, localManga, source)
|
||||
withIOContext { syncChaptersWithSource(chapters, localManga, source) }
|
||||
} catch (e: Exception) {
|
||||
return@async null
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ import uy.kohesive.injekt.api.get
|
|||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.domain.chapter.interactor.GetChapters
|
||||
import yokai.domain.download.DownloadPreferences
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
import yokai.domain.storage.StorageManager
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
|
@ -94,6 +96,7 @@ class ReaderViewModel(
|
|||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||
) : ViewModel() {
|
||||
private val getChapters: GetChapters by injectLazy()
|
||||
private val updateManga: UpdateManga by injectLazy()
|
||||
|
||||
private val mutableState = MutableStateFlow(State())
|
||||
val state = mutableState.asStateFlow()
|
||||
|
@ -333,7 +336,6 @@ class ReaderViewModel(
|
|||
val chapterId: Long
|
||||
if (chapters.isNotEmpty()) {
|
||||
val newChapters = syncChaptersWithSource(
|
||||
db,
|
||||
chapters,
|
||||
manga,
|
||||
delegatedSource.delegate!!,
|
||||
|
@ -678,7 +680,7 @@ class ReaderViewModel(
|
|||
manga.viewer_flags = 0
|
||||
}
|
||||
manga.readingModeType = if (cantSwitchToLTR) 0 else readerType
|
||||
db.updateViewerFlags(manga).asRxObservable().subscribe()
|
||||
viewModelScope.launchIO { updateManga.await(MangaUpdate(manga.id!!, viewerFlags = manga.viewer_flags)) }
|
||||
}
|
||||
return if (manga.readingModeType == 0) default else manga.readingModeType
|
||||
}
|
||||
|
@ -689,9 +691,9 @@ class ReaderViewModel(
|
|||
fun setMangaReadingMode(readingModeType: Int) {
|
||||
val manga = manga ?: return
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
viewModelScope.launchIO {
|
||||
manga.readingModeType = readingModeType
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
updateManga.await(MangaUpdate(manga.id!!, viewerFlags = manga.viewer_flags))
|
||||
val currChapters = state.value.viewerChapters
|
||||
if (currChapters != null) {
|
||||
// Save current page
|
||||
|
@ -726,12 +728,11 @@ class ReaderViewModel(
|
|||
fun setMangaOrientationType(rotationType: Int) {
|
||||
val manga = manga ?: return
|
||||
this.manga?.orientationType = rotationType
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
|
||||
Logger.i { "Manga orientation is ${manga.orientationType}" }
|
||||
|
||||
viewModelScope.launchIO {
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
updateManga.await(MangaUpdate(manga.id!!, viewerFlags = manga.viewer_flags))
|
||||
val currChapters = state.value.viewerChapters
|
||||
if (currChapters != null) {
|
||||
mutableState.update {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.util.chapter
|
||||
|
||||
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.download.DownloadManager
|
||||
|
@ -8,7 +7,17 @@ import eu.kanade.tachiyomi.source.Source
|
|||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterFilter.Companion.filterChaptersByScanlators
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.data.DatabaseHandler
|
||||
import yokai.domain.chapter.interactor.DeleteChapters
|
||||
import yokai.domain.chapter.interactor.GetChapters
|
||||
import yokai.domain.chapter.interactor.InsertChapters
|
||||
import yokai.domain.chapter.interactor.UpdateChapters
|
||||
import yokai.domain.chapter.models.ChapterUpdate
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -20,11 +29,16 @@ import java.util.*
|
|||
* @param source the source of the chapters.
|
||||
* @return a pair of new insertions and deletions.
|
||||
*/
|
||||
fun syncChaptersWithSource(
|
||||
db: DatabaseHelper,
|
||||
suspend fun syncChaptersWithSource(
|
||||
rawSourceChapters: List<SChapter>,
|
||||
manga: Manga,
|
||||
source: Source,
|
||||
deleteChapters: DeleteChapters = Injekt.get(),
|
||||
getChapters: GetChapters = Injekt.get(),
|
||||
insertChapters: InsertChapters = Injekt.get(),
|
||||
updateChapters: UpdateChapters = Injekt.get(),
|
||||
updateManga: UpdateManga = Injekt.get(),
|
||||
handler: DatabaseHandler = Injekt.get(),
|
||||
): Pair<List<Chapter>, List<Chapter>> {
|
||||
if (rawSourceChapters.isEmpty()) {
|
||||
throw Exception("No chapters found")
|
||||
|
@ -32,7 +46,7 @@ fun syncChaptersWithSource(
|
|||
|
||||
val downloadManager: DownloadManager by injectLazy()
|
||||
// Chapters from db.
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
val dbChapters = getChapters.await(manga)
|
||||
|
||||
val sourceChapters = rawSourceChapters
|
||||
.distinctBy { it.url }
|
||||
|
@ -49,7 +63,7 @@ fun syncChaptersWithSource(
|
|||
val toAdd = mutableListOf<Chapter>()
|
||||
|
||||
// Chapters whose metadata have changed.
|
||||
val toChange = mutableListOf<Chapter>()
|
||||
val toChange = mutableListOf<ChapterUpdate>()
|
||||
|
||||
for (sourceChapter in sourceChapters) {
|
||||
val dbChapter = dbChapters.find { it.url == sourceChapter.url }
|
||||
|
@ -71,12 +85,15 @@ fun syncChaptersWithSource(
|
|||
) {
|
||||
downloadManager.renameChapter(source, manga, dbChapter, sourceChapter)
|
||||
}
|
||||
dbChapter.scanlator = sourceChapter.scanlator
|
||||
dbChapter.name = sourceChapter.name
|
||||
dbChapter.date_upload = sourceChapter.date_upload
|
||||
dbChapter.chapter_number = sourceChapter.chapter_number
|
||||
dbChapter.source_order = sourceChapter.source_order
|
||||
toChange.add(dbChapter)
|
||||
val update = ChapterUpdate(
|
||||
dbChapter.id!!,
|
||||
scanlator = sourceChapter.scanlator,
|
||||
name = sourceChapter.name,
|
||||
dateUpload = sourceChapter.date_upload,
|
||||
chapterNumber = sourceChapter.chapter_number.toDouble(),
|
||||
sourceOrder = sourceChapter.source_order.toLong(),
|
||||
)
|
||||
toChange.add(update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +118,14 @@ fun syncChaptersWithSource(
|
|||
val newestDate = dbChapters.maxOfOrNull { it.date_upload } ?: 0L
|
||||
if (newestDate != 0L && newestDate != manga.last_update) {
|
||||
manga.last_update = newestDate
|
||||
db.updateLastUpdated(manga).executeAsBlocking()
|
||||
val update = MangaUpdate(manga.id!!, lastUpdate = manga.last_update)
|
||||
updateManga.await(update)
|
||||
}
|
||||
return Pair(emptyList(), emptyList())
|
||||
}
|
||||
|
||||
val readded = mutableListOf<Chapter>()
|
||||
|
||||
db.inTransaction {
|
||||
val deletedChapterNumbers = TreeSet<Float>()
|
||||
val deletedReadChapterNumbers = TreeSet<Float>()
|
||||
if (toDelete.isNotEmpty()) {
|
||||
|
@ -118,7 +135,7 @@ fun syncChaptersWithSource(
|
|||
}
|
||||
deletedChapterNumbers.add(c.chapter_number)
|
||||
}
|
||||
db.deleteChapters(toDelete).executeAsBlocking()
|
||||
deleteChapters.awaitAll(toDelete)
|
||||
}
|
||||
|
||||
if (toAdd.isNotEmpty()) {
|
||||
|
@ -143,31 +160,42 @@ fun syncChaptersWithSource(
|
|||
readded.add(chapter)
|
||||
}
|
||||
}
|
||||
val chapters = db.insertChapters(toAdd).executeAsBlocking()
|
||||
toAdd.forEach { chapter ->
|
||||
chapter.id = chapters.results().getValue(chapter).insertedId()
|
||||
chapter.id = insertChapters.await(chapter)
|
||||
}
|
||||
}
|
||||
|
||||
if (toChange.isNotEmpty()) {
|
||||
db.insertChapters(toChange).executeAsBlocking()
|
||||
updateChapters.awaitAll(toChange)
|
||||
}
|
||||
|
||||
// Fix order in source.
|
||||
db.fixChaptersSourceOrder(sourceChapters).executeAsBlocking()
|
||||
handler.await(inTransaction = true) {
|
||||
sourceChapters.forEach { chapter ->
|
||||
if (chapter.manga_id == null) return@forEach
|
||||
chaptersQueries.fixSourceOrder(
|
||||
url = chapter.url,
|
||||
mangaId = chapter.manga_id!!,
|
||||
sourceOrder = chapter.source_order.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var mangaUpdate: MangaUpdate? = null
|
||||
// Set this manga as updated since chapters were changed
|
||||
val newestChapterDate = db.getChapters(manga).executeAsBlocking()
|
||||
val newestChapterDate = getChapters.await(manga)
|
||||
.maxOfOrNull { it.date_upload } ?: 0L
|
||||
if (newestChapterDate == 0L) {
|
||||
if (toAdd.isNotEmpty()) {
|
||||
manga.last_update = Date().time
|
||||
mangaUpdate = MangaUpdate(manga.id!!, lastUpdate = manga.last_update)
|
||||
}
|
||||
} else {
|
||||
manga.last_update = newestChapterDate
|
||||
mangaUpdate = MangaUpdate(manga.id!!, lastUpdate = manga.last_update)
|
||||
}
|
||||
db.updateLastUpdated(manga).executeAsBlocking()
|
||||
}
|
||||
mangaUpdate?.let { updateManga.await(it) }
|
||||
|
||||
val reAddedSet = readded.toSet()
|
||||
return Pair(
|
||||
toAdd.subtract(reAddedSet).toList().filterChaptersByScanlators(manga),
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package eu.kanade.tachiyomi.util.manga
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
|
||||
object MangaUtil {
|
||||
fun setScanlatorFilter(db: DatabaseHelper, manga: Manga, filteredScanlators: Set<String>) {
|
||||
suspend fun setScanlatorFilter(updateManga: UpdateManga, manga: Manga, filteredScanlators: Set<String>) {
|
||||
if (manga.id == null) return
|
||||
|
||||
manga.filtered_scanlators =
|
||||
if (filteredScanlators.isEmpty()) null else ChapterUtil.getScanlatorString(filteredScanlators)
|
||||
db.updateMangaFilteredScanlators(manga).executeAsBlocking()
|
||||
|
||||
updateManga.await(MangaUpdate(manga.id!!, filteredScanlators = manga.filtered_scanlators))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,11 @@ import yokai.data.manga.MangaRepositoryImpl
|
|||
import yokai.domain.category.CategoryRepository
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
import yokai.domain.chapter.interactor.DeleteChapters
|
||||
import yokai.domain.chapter.interactor.GetAvailableScanlators
|
||||
import yokai.domain.chapter.interactor.GetChapters
|
||||
import yokai.domain.chapter.interactor.InsertChapters
|
||||
import yokai.domain.chapter.interactor.UpdateChapters
|
||||
import yokai.domain.extension.interactor.TrustExtension
|
||||
import yokai.domain.extension.repo.ExtensionRepoRepository
|
||||
import yokai.domain.extension.repo.interactor.CreateExtensionRepo
|
||||
|
@ -30,6 +33,7 @@ import yokai.domain.library.custom.interactor.GetCustomManga
|
|||
import yokai.domain.library.custom.interactor.RelinkCustomManga
|
||||
import yokai.domain.manga.MangaRepository
|
||||
import yokai.domain.manga.interactor.GetLibraryManga
|
||||
import yokai.domain.manga.interactor.GetManga
|
||||
import yokai.domain.manga.interactor.InsertManga
|
||||
import yokai.domain.manga.interactor.UpdateManga
|
||||
|
||||
|
@ -52,13 +56,17 @@ class DomainModule : InjektModule {
|
|||
addFactory { RelinkCustomManga(get()) }
|
||||
|
||||
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||
addFactory { GetManga(get()) }
|
||||
addFactory { GetLibraryManga(get()) }
|
||||
addFactory { InsertManga(get()) }
|
||||
addFactory { UpdateManga(get()) }
|
||||
|
||||
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
|
||||
addFactory { DeleteChapters(get()) }
|
||||
addFactory { GetAvailableScanlators(get()) }
|
||||
addFactory { GetChapters(get()) }
|
||||
addFactory { InsertChapters(get()) }
|
||||
addFactory { UpdateChapters(get()) }
|
||||
|
||||
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
||||
addFactory { GetCategories(get()) }
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package yokai.data.chapter
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.util.system.toInt
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import yokai.data.DatabaseHandler
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
import yokai.domain.chapter.models.ChapterUpdate
|
||||
|
||||
class ChapterRepositoryImpl(private val handler: DatabaseHandler) : ChapterRepository {
|
||||
override suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List<Chapter> =
|
||||
|
@ -18,4 +20,93 @@ class ChapterRepositoryImpl(private val handler: DatabaseHandler) : ChapterRepos
|
|||
|
||||
override fun getScanlatorsByChapterAsFlow(mangaId: Long): Flow<List<String>> =
|
||||
handler.subscribeToList { chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() } }
|
||||
|
||||
override suspend fun delete(chapter: Chapter) =
|
||||
try {
|
||||
partialDelete(chapter)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Failed to delete chapter with id '${chapter.id}'" }
|
||||
false
|
||||
}
|
||||
|
||||
override suspend fun deleteAll(chapters: List<Chapter>) =
|
||||
try {
|
||||
partialDelete(*chapters.toTypedArray())
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Failed to bulk delete chapters" }
|
||||
false
|
||||
}
|
||||
|
||||
private suspend fun partialDelete(vararg chapters: Chapter) {
|
||||
handler.await(inTransaction = true) {
|
||||
chapters.forEach { chapter ->
|
||||
if (chapter.id == null) return@forEach
|
||||
chaptersQueries.delete(chapter.id!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(update: ChapterUpdate): Boolean =
|
||||
try {
|
||||
partialUpdate(update)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Failed to update chapter with id '${update.id}'" }
|
||||
false
|
||||
}
|
||||
|
||||
override suspend fun updateAll(updates: List<ChapterUpdate>): Boolean =
|
||||
try {
|
||||
partialUpdate(*updates.toTypedArray())
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Failed to bulk update chapters" }
|
||||
false
|
||||
}
|
||||
|
||||
private suspend fun partialUpdate(vararg updates: ChapterUpdate) {
|
||||
handler.await(inTransaction = true) {
|
||||
updates.forEach { update ->
|
||||
chaptersQueries.update(
|
||||
chapterId = update.id,
|
||||
mangaId = update.mangaId,
|
||||
url = update.url,
|
||||
name = update.name,
|
||||
scanlator = update.scanlator,
|
||||
read = update.read,
|
||||
bookmark = update.bookmark,
|
||||
lastPageRead = update.lastPageRead,
|
||||
pagesLeft = update.pagesLeft,
|
||||
chapterNumber = update.chapterNumber,
|
||||
sourceOrder = update.sourceOrder,
|
||||
dateFetch = update.dateFetch,
|
||||
dateUpload = update.dateUpload
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insert(chapter: Chapter): Long? {
|
||||
if (chapter.manga_id == null) return null
|
||||
|
||||
return handler.awaitOneOrNullExecutable(inTransaction = true) {
|
||||
chaptersQueries.insert(
|
||||
mangaId = chapter.manga_id!!,
|
||||
url = chapter.url,
|
||||
name = chapter.name,
|
||||
scanlator = chapter.scanlator,
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
lastPageRead = chapter.last_page_read.toLong(),
|
||||
pagesLeft = chapter.pages_left.toLong(),
|
||||
chapterNumber = chapter.chapter_number.toDouble(),
|
||||
sourceOrder = chapter.source_order.toLong(),
|
||||
dateFetch = chapter.date_fetch,
|
||||
dateUpload = chapter.date_upload,
|
||||
)
|
||||
chaptersQueries.selectLastInsertedRowId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,16 @@ import yokai.domain.manga.MangaRepository
|
|||
import yokai.domain.manga.models.MangaUpdate
|
||||
|
||||
class MangaRepositoryImpl(private val handler: DatabaseHandler) : MangaRepository {
|
||||
override suspend fun getManga(): List<Manga> =
|
||||
override suspend fun getMangaList(): List<Manga> =
|
||||
handler.awaitList { mangasQueries.findAll(Manga::mapper) }
|
||||
|
||||
override fun getMangaAsFlow(): Flow<List<Manga>> =
|
||||
override suspend fun getMangaByUrlAndSource(url: String, source: Long): Manga? =
|
||||
handler.awaitOneOrNull { mangasQueries.findByUrlAndSource(url, source, Manga::mapper) }
|
||||
|
||||
override suspend fun getMangaById(id: Long): Manga? =
|
||||
handler.awaitOneOrNull { mangasQueries.findById(id, Manga::mapper) }
|
||||
|
||||
override fun getMangaListAsFlow(): Flow<List<Manga>> =
|
||||
handler.subscribeToList { mangasQueries.findAll(Manga::mapper) }
|
||||
|
||||
override suspend fun getLibraryManga(): List<LibraryManga> =
|
||||
|
|
|
@ -2,6 +2,7 @@ package yokai.domain.chapter
|
|||
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import yokai.domain.chapter.models.ChapterUpdate
|
||||
|
||||
interface ChapterRepository {
|
||||
suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List<Chapter>
|
||||
|
@ -9,4 +10,12 @@ interface ChapterRepository {
|
|||
|
||||
suspend fun getScanlatorsByChapter(mangaId: Long): List<String>
|
||||
fun getScanlatorsByChapterAsFlow(mangaId: Long): Flow<List<String>>
|
||||
|
||||
suspend fun delete(chapter: Chapter): Boolean
|
||||
suspend fun deleteAll(chapters: List<Chapter>): Boolean
|
||||
|
||||
suspend fun update(update: ChapterUpdate): Boolean
|
||||
suspend fun updateAll(updates: List<ChapterUpdate>): Boolean
|
||||
|
||||
suspend fun insert(chapter: Chapter): Long?
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package yokai.domain.chapter.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
|
||||
class DeleteChapters(
|
||||
private val chapterRepository: ChapterRepository,
|
||||
) {
|
||||
suspend fun await(chapter: Chapter) = chapterRepository.delete(chapter)
|
||||
suspend fun awaitAll(chapters: List<Chapter>) = chapterRepository.deleteAll(chapters)
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package yokai.domain.chapter.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
|
||||
class InsertChapters(
|
||||
private val chapterRepository: ChapterRepository,
|
||||
) {
|
||||
suspend fun await(chapter: Chapter) = chapterRepository.insert(chapter)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package yokai.domain.chapter.interactor
|
||||
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
import yokai.domain.chapter.models.ChapterUpdate
|
||||
|
||||
class UpdateChapters(
|
||||
private val chapterRepository: ChapterRepository,
|
||||
) {
|
||||
suspend fun await(chapter: ChapterUpdate) = chapterRepository.update(chapter)
|
||||
suspend fun awaitAll(chapters: List<ChapterUpdate>) = chapterRepository.updateAll(chapters)
|
||||
}
|
|
@ -6,8 +6,10 @@ import kotlinx.coroutines.flow.Flow
|
|||
import yokai.domain.manga.models.MangaUpdate
|
||||
|
||||
interface MangaRepository {
|
||||
suspend fun getManga(): List<Manga>
|
||||
fun getMangaAsFlow(): Flow<List<Manga>>
|
||||
suspend fun getMangaList(): List<Manga>
|
||||
suspend fun getMangaByUrlAndSource(url: String, source: Long): Manga?
|
||||
suspend fun getMangaById(id: Long): Manga?
|
||||
fun getMangaListAsFlow(): Flow<List<Manga>>
|
||||
suspend fun getLibraryManga(): List<LibraryManga>
|
||||
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
|
||||
suspend fun update(update: MangaUpdate): Boolean
|
||||
|
|
13
app/src/main/java/yokai/domain/manga/interactor/GetManga.kt
Normal file
13
app/src/main/java/yokai/domain/manga/interactor/GetManga.kt
Normal file
|
@ -0,0 +1,13 @@
|
|||
package yokai.domain.manga.interactor
|
||||
|
||||
import yokai.domain.manga.MangaRepository
|
||||
|
||||
class GetManga (
|
||||
private val mangaRepository: MangaRepository,
|
||||
) {
|
||||
suspend fun awaitAll() = mangaRepository.getMangaList()
|
||||
fun subscribeAll() = mangaRepository.getMangaListAsFlow()
|
||||
|
||||
suspend fun awaitByUrlAndSource(url: String, source: Long) = mangaRepository.getMangaByUrlAndSource(url, source)
|
||||
suspend fun awaitById(id: Long) = mangaRepository.getMangaById(id)
|
||||
}
|
|
@ -6,6 +6,6 @@ import yokai.domain.manga.models.MangaUpdate
|
|||
class UpdateManga (
|
||||
private val mangaRepository: MangaRepository,
|
||||
) {
|
||||
suspend fun update(update: MangaUpdate) = mangaRepository.update(update)
|
||||
suspend fun updateAll(updates: List<MangaUpdate>) = mangaRepository.updateAll(updates)
|
||||
suspend fun await(update: MangaUpdate) = mangaRepository.update(update)
|
||||
suspend fun awaitAll(updates: List<MangaUpdate>) = mangaRepository.updateAll(updates)
|
||||
}
|
||||
|
|
|
@ -36,3 +36,34 @@ getScanlatorsByMangaId:
|
|||
SELECT scanlator
|
||||
FROM chapters
|
||||
WHERE manga_id = :mangaId;
|
||||
|
||||
delete:
|
||||
DELETE FROM chapters
|
||||
WHERE _id = :chapterId;
|
||||
|
||||
update:
|
||||
UPDATE chapters SET
|
||||
manga_id = coalesce(:mangaId, manga_id),
|
||||
url = coalesce(:url, url),
|
||||
name = coalesce(:name, name),
|
||||
scanlator = coalesce(:scanlator, scanlator),
|
||||
read = coalesce(:read, read),
|
||||
bookmark = coalesce(:bookmark, bookmark),
|
||||
last_page_read = coalesce(:lastPageRead, last_page_read),
|
||||
pages_left = coalesce(:pagesLeft, pages_left),
|
||||
chapter_number = coalesce(:chapterNumber, chapter_number),
|
||||
source_order = coalesce(:sourceOrder, source_order),
|
||||
date_fetch = coalesce(:dateFetch, date_fetch),
|
||||
date_upload = coalesce(:dateUpload, date_upload)
|
||||
WHERE _id = :chapterId;
|
||||
|
||||
fixSourceOrder:
|
||||
UPDATE chapters SET source_order = :sourceOrder
|
||||
WHERE url = :url AND manga_id = :mangaId;
|
||||
|
||||
insert:
|
||||
INSERT INTO chapters (manga_id, url, name, scanlator, read, bookmark, last_page_read, pages_left, chapter_number, source_order, date_fetch, date_upload)
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :pagesLeft, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload);
|
||||
|
||||
selectLastInsertedRowId:
|
||||
SELECT last_insert_rowid();
|
||||
|
|
|
@ -30,6 +30,16 @@ findAll:
|
|||
SELECT *
|
||||
FROM mangas;
|
||||
|
||||
findByUrlAndSource:
|
||||
SELECT *
|
||||
FROM mangas
|
||||
WHERE url = :url AND source = :source;
|
||||
|
||||
findById:
|
||||
SELECT *
|
||||
FROM mangas
|
||||
WHERE _id = :mangaId;
|
||||
|
||||
insert:
|
||||
INSERT INTO mangas (source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, initialized, viewer, hide_title, chapter_flags, date_added, filtered_scanlators, update_strategy)
|
||||
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :initialized, :viewer, :hideTitle, :chapterFlags, :dateAdded, :filteredScanlators, :updateStrategy);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package yokai.domain.chapter.models
|
||||
|
||||
data class ChapterUpdate(
|
||||
val id: Long,
|
||||
val mangaId: Long? = null,
|
||||
val url: String? = null,
|
||||
val name: String? = null,
|
||||
val scanlator: String? = null,
|
||||
val read: Boolean? = null,
|
||||
val bookmark: Boolean? = null,
|
||||
val lastPageRead: Long? = null,
|
||||
val pagesLeft: Long? = null,
|
||||
val chapterNumber: Double? = null,
|
||||
val sourceOrder: Long? = null,
|
||||
val dateFetch: Long? = null,
|
||||
val dateUpload: Long? = null,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue