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