refactor(db): Migrate getManga queries to SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-11-29 14:21:16 +07:00
parent 312f9e197b
commit aae9a68c8b
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
14 changed files with 65 additions and 67 deletions

View file

@ -4,12 +4,9 @@ import android.content.Context
import android.text.format.Formatter
import co.touchlab.kermit.Logger
import coil3.imageLoader
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.withUIContext
@ -19,8 +16,8 @@ import java.io.InputStream
import java.util.concurrent.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.i18n.MR
import yokai.util.lang.getString
@ -40,6 +37,8 @@ class CoverCache(val context: Context) {
private const val ONLINE_COVERS_DIR = "online_covers"
}
private val getManga: GetManga by injectLazy()
/** Cache directory used for cache management.*/
private val cacheDir = getCacheDir(COVERS_DIR)
@ -68,9 +67,8 @@ class CoverCache(val context: Context) {
}
suspend fun deleteOldCovers() {
val db = Injekt.get<DatabaseHelper>()
var deletedSize = 0L
val urls = db.getFavoriteMangas().executeOnIO().mapNotNull {
val urls = getManga.awaitFavorites().mapNotNull {
it.thumbnail_url?.let { url ->
it.updateCoverLastModified()
return@mapNotNull DiskUtil.hashKeyForDisk(url)

View file

@ -10,29 +10,6 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
interface MangaQueries : DbProvider {
fun getFavoriteMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ?")
.whereArgs(1)
.orderBy(MangaTable.COL_TITLE)
.build(),
)
.prepare()
fun getManga(url: String, sourceId: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_URL} = ? AND ${MangaTable.COL_SOURCE} = ?")
.whereArgs(url, sourceId)
.build(),
)
.prepare()
fun getManga(id: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(

View file

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.source.Source
@ -118,6 +117,7 @@ class DownloadProvider(private val context: Context) {
* @param manga the manga of the chapter.
* @param source the source of the chapter.
*/
/*
fun renameChapters() {
val db by injectLazy<DatabaseHelper>()
val sourceManager by injectLazy<SourceManager>()
@ -136,6 +136,7 @@ class DownloadProvider(private val context: Context) {
}
}
}
*/
fun renameMangaFolder(from: String, to: String, sourceId: Long) {
val sourceManager by injectLazy<SourceManager>()

View file

@ -1,18 +1,19 @@
package eu.kanade.tachiyomi.smartsearch
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.create
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.toNormalized
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.supervisorScope
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.util.normalizedLevenshteinSimilarity
import kotlin.coroutines.CoroutineContext
class SmartSearchEngine(
parentContext: CoroutineContext,
@ -20,7 +21,8 @@ class SmartSearchEngine(
) : CoroutineScope {
override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default
private val db: DatabaseHelper by injectLazy()
private val getManga: GetManga by injectLazy()
private val insertManga: InsertManga by injectLazy()
/*suspend fun smartSearch(source: CatalogueSource, title: String): SManga? {
val cleanedTitle = cleanSmartSearchTitle(title)
@ -129,12 +131,11 @@ class SmartSearchEngine(
* @return a manga from the database.
*/
suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
var localManga = getManga.awaitByUrlAndSource(sManga.url, sourceId)
if (localManga == null) {
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
newManga.copyFrom(sManga)
val result = db.insertManga(newManga).executeAsBlocking()
newManga.id = result.insertedId()
newManga.id = insertManga.await(newManga)
localManga = newManga
}
return localManga

View file

@ -9,6 +9,8 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.model.SChapter
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.GetManga
abstract class DelegatedHttpSource {
@ -16,6 +18,8 @@ abstract class DelegatedHttpSource {
abstract val domainName: String
protected val db: DatabaseHelper by injectLazy()
protected val getChapter: GetChapter by injectLazy()
protected val getManga: GetManga by injectLazy()
protected val network: NetworkHelper by injectLazy()

View file

@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.DelegatedHttpSource
import eu.kanade.tachiyomi.util.system.executeOnIO
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
@ -15,7 +15,6 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.i18n.MR
import yokai.util.lang.getString
import java.util.*
class Cubari : DelegatedHttpSource() {
override val domainName: String = "cubari"
@ -32,11 +31,11 @@ class Cubari : DelegatedHttpSource() {
val mangaUrl = "/read/$cubariType/$cubariPath"
return withContext(Dispatchers.IO) {
val deferredManga = async {
db.getManga(mangaUrl, delegate?.id!!).executeAsBlocking() ?: getMangaInfo(mangaUrl)
getManga.awaitByUrlAndSource(mangaUrl, delegate?.id!!) ?: getMangaInfo(mangaUrl)
}
val deferredChapters = async {
db.getManga(mangaUrl, delegate?.id!!).executeAsBlocking()?.let { manga ->
val chapters = db.getChapters(manga).executeOnIO()
getManga.awaitByUrlAndSource(mangaUrl, delegate?.id!!)?.let { manga ->
val chapters = getChapter.awaitAll(manga, false)
val chapter = findChapter(chapters, cubariType, chapterNumber)
if (chapter != null) {
return@async chapters

View file

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.DelegatedHttpSource
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
@ -21,7 +22,6 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.i18n.MR
import yokai.util.lang.getString
import java.util.*
class MangaDex : DelegatedHttpSource() {
@ -64,7 +64,7 @@ class MangaDex : DelegatedHttpSource() {
val mangaUrl = "/manga/$mangaId/"
return withContext(Dispatchers.IO) {
val deferredManga = async {
db.getManga(mangaUrl, delegate?.id!!).executeAsBlocking() ?: getMangaInfo(mangaUrl)
getManga.awaitByUrlAndSource(mangaUrl, delegate?.id!!) ?: getMangaInfo(mangaUrl)
}
val deferredChapters = async { getChapters(mangaUrl) }
val manga = deferredManga.await()

View file

@ -63,9 +63,8 @@ open class FoolSlide(override val domainName: String, private val urlModifier: S
return withContext(Dispatchers.IO) {
val mangaUrl = "$urlModifier/series/$mangaName/"
val sourceId = delegate?.id ?: return@withContext null
val dbManga = db.getManga(mangaUrl, sourceId).executeAsBlocking()
val deferredManga = async {
dbManga ?: getManga(mangaUrl)
getManga.awaitByUrlAndSource(mangaUrl, sourceId) ?: getManga(mangaUrl)
}
val chapterUrl = chapterUrl(uri)
val deferredChapters = async { getChapters(mangaUrl) }

View file

@ -53,7 +53,7 @@ class MangaPlus : DelegatedHttpSource() {
val trimmedTitle = title.substring(0, title.length - 1)
val mangaUrl = "#/titles/$titleId"
val deferredManga = async {
db.getManga(mangaUrl, delegate?.id!!).executeAsBlocking() ?: getMangaInfo(mangaUrl)
getManga.awaitByUrlAndSource(mangaUrl, delegate?.id!!) ?: getMangaInfo(mangaUrl)
}
val deferredChapters = async { getChapters(mangaUrl) }
val manga = deferredManga.await()

View file

@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.withUIContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -17,6 +16,7 @@ import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.domain.ui.UiPreferences
abstract class BaseMigrationPresenter<T : BaseMigrationInterface>(
@ -25,6 +25,8 @@ abstract class BaseMigrationPresenter<T : BaseMigrationInterface>(
val uiPreferences: UiPreferences = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(),
) : BaseCoroutinePresenter<T>() {
private val getManga: GetManga by injectLazy()
private var selectedSource: Pair<String, Long>? = null
var sourceItems = emptyList<SourceItem>()
protected set
@ -35,7 +37,7 @@ abstract class BaseMigrationPresenter<T : BaseMigrationInterface>(
fun refreshMigrations() {
presenterScope.launch {
val favs = db.getFavoriteMangas().executeOnIO()
val favs = getManga.awaitFavorites()
sourceItems = findSourcesWithManga(favs)
mangaItems = HashMap(
sourceItems.associate {
@ -92,7 +94,7 @@ abstract class BaseMigrationPresenter<T : BaseMigrationInterface>(
}
protected suspend fun firstTimeMigration() {
val favs = db.getFavoriteMangas().executeOnIO()
val favs = getManga.awaitFavorites()
sourceItems = findSourcesWithManga(favs)
mangaItems = HashMap(
sourceItems.associate {

View file

@ -9,10 +9,6 @@ import androidx.core.view.doOnNextLayout
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationControllerBinding
@ -28,6 +24,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.manga.interactor.GetManga
import yokai.i18n.MR
import yokai.util.lang.getString
class MigrationController :
BaseCoroutineController<MigrationControllerBinding, MigrationPresenter>(),
@ -95,7 +94,7 @@ class MigrationController :
val item = adapter?.getItem(position) as? SourceItem ?: return
launchUI {
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().executeAsBlocking()
val manga = Injekt.get<GetManga>().awaitFavorites()
val sourceMangas =
manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList()
withContext(Dispatchers.Main) {

View file

@ -39,6 +39,11 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.MangaUpdate
import yokai.domain.ui.UiPreferences
/**
@ -54,6 +59,9 @@ open class BrowseSourcePresenter(
val preferences: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
) : BaseCoroutinePresenter<BrowseSourceController>() {
private val getManga: GetManga by injectLazy()
private val insertManga: InsertManga by injectLazy()
private val updateManga: UpdateManga by injectLazy()
/**
* Selected source.
@ -225,17 +233,21 @@ open class BrowseSourcePresenter(
* @param sManga the manga from the source.
* @return a manga from the database.
*/
private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
private suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = getManga.awaitByUrlAndSource(sManga.url, sourceId)
if (localManga == null) {
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
newManga.copyFrom(sManga)
val result = db.insertManga(newManga).executeAsBlocking()
newManga.id = result.insertedId()
newManga.id = insertManga.await(newManga)
localManga = newManga
} else if (localManga.title.isBlank()) {
localManga.title = sManga.title
db.insertManga(localManga).executeAsBlocking()
updateManga.await(
MangaUpdate(
id = localManga.id!!,
title = sManga.title,
)
)
} else if (!localManga.favorite) {
// if the manga isn't a favorite, set its display title from source
// if it later becomes a favorite, updated title will go to db
@ -273,7 +285,7 @@ open class BrowseSourcePresenter(
val networkManga = source.getMangaDetails(manga.copy())
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
updateManga.await(manga.toMangaUpdate())
} catch (e: Exception) {
Logger.e(e) { "Something went wrong while trying to initialize manga" }
}

View file

@ -29,6 +29,9 @@ import kotlinx.coroutines.sync.withPermit
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
/**
* Presenter of [GlobalSearchController]
@ -47,6 +50,9 @@ open class GlobalSearchPresenter(
private val preferences: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
) : BaseCoroutinePresenter<GlobalSearchController>() {
private val getManga: GetManga by injectLazy()
private val insertManga: InsertManga by injectLazy()
private val updateManga: UpdateManga by injectLazy()
/**
* Enabled sources.
@ -256,7 +262,7 @@ open class GlobalSearchPresenter(
val networkManga = source.getMangaDetails(manga.copy())
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
updateManga.await(manga.toMangaUpdate())
return manga
}
@ -267,13 +273,12 @@ open class GlobalSearchPresenter(
* @param sManga the manga from the source.
* @return a manga from the database.
*/
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
protected open suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
var localManga = getManga.awaitByUrlAndSource(sManga.url, sourceId)
if (localManga == null) {
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
newManga.copyFrom(sManga)
val result = db.insertManga(newManga).executeAsBlocking()
newManga.id = result.insertedId()
newManga.id = insertManga.await(newManga)
localManga = newManga
} else if (!localManga.favorite) {
// if the manga isn't a favorite, set its display title from source

View file

@ -48,7 +48,8 @@ WHERE favorite = 1 AND lower(title) = :title AND source != :source;
findFavorites:
SELECT *
FROM mangas
WHERE favorite = 1;
WHERE favorite = 1
ORDER BY title;
findReadNotFavorites:
SELECT *