refactor(db): Migrate getChapters query to SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-11-29 15:08:42 +07:00
parent 83cb898068
commit e25f330118
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
10 changed files with 54 additions and 43 deletions

View file

@ -8,15 +8,14 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
interface ChapterQueries : DbProvider { interface ChapterQueries : DbProvider {
fun getChapters(manga: Manga) = getChapters(manga.id) // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
fun getChapters(manga: Manga) = db.get()
fun getChapters(mangaId: Long?) = db.get()
.listOfObjects(Chapter::class.java) .listOfObjects(Chapter::class.java)
.withQuery( .withQuery(
Query.builder() Query.builder()
.table(ChapterTable.TABLE) .table(ChapterTable.TABLE)
.where("${ChapterTable.COL_MANGA_ID} = ?") .where("${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId) .whereArgs(manga.id)
.build(), .build(),
) )
.prepare() .prepare()

View file

@ -79,18 +79,6 @@ interface HistoryQueries : DbProvider {
) )
.prepare() .prepare()
fun getTotalReadDuration(): Long {
val cursor = db.lowLevel()
.rawQuery(
RawQuery.builder()
.query("SELECT SUM(${HistoryTable.COL_TIME_READ}) FROM ${HistoryTable.TABLE}")
.observesTables(HistoryTable.TABLE)
.build(),
)
cursor.moveToFirst()
return cursor.getLong(0)
}
/** /**
* Updates the history last read. * Updates the history last read.
* Inserts history object if not yet in database * Inserts history object if not yet in database

View file

@ -8,16 +8,22 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.isOneShotOrCompleted import eu.kanade.tachiyomi.data.database.models.isOneShotOrCompleted
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.system.executeOnIO
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.history.interactor.GetHistory
import yokai.domain.manga.interactor.GetManga
abstract class TrackService(val id: Long) { abstract class TrackService(val id: Long) {
val trackPreferences: TrackPreferences by injectLazy() val trackPreferences: TrackPreferences by injectLazy()
val networkService: NetworkHelper by injectLazy() val networkService: NetworkHelper by injectLazy()
val db: DatabaseHelper by injectLazy() val db: DatabaseHelper by injectLazy()
val getChapter: GetChapter by injectLazy()
val getManga: GetManga by injectLazy()
val getHistory: GetHistory by injectLazy()
open fun canRemoveFromService() = false open fun canRemoveFromService() = false
open val client: OkHttpClient open val client: OkHttpClient
get() = networkService.client get() = networkService.client
@ -110,9 +116,9 @@ abstract class TrackService(val id: Long) {
} }
suspend fun TrackService.updateNewTrackInfo(track: Track) { suspend fun TrackService.updateNewTrackInfo(track: Track) {
val manga = db.getManga(track.manga_id).executeOnIO() val manga = getManga.awaitById(track.manga_id)
val allRead = manga?.isOneShotOrCompleted() == true && val allRead = manga?.isOneShotOrCompleted() == true &&
db.getChapters(track.manga_id).executeOnIO().all { it.read } getChapter.awaitAll(track.manga_id, false).all { it.read }
if (supportsReadingDates) { if (supportsReadingDates) {
track.started_reading_date = getStartDate(track) track.started_reading_date = getStartDate(track)
track.finished_reading_date = getCompletedDate(track, allRead) track.finished_reading_date = getCompletedDate(track, allRead)
@ -129,8 +135,8 @@ suspend fun TrackService.updateNewTrackInfo(track: Track) {
} }
suspend fun TrackService.getStartDate(track: Track): Long { suspend fun TrackService.getStartDate(track: Track): Long {
if (db.getChapters(track.manga_id).executeOnIO().any { it.read }) { if (getChapter.awaitAll(track.manga_id, false).any { it.read }) {
val chapters = db.getHistoryByMangaId(track.manga_id).executeOnIO().filter { it.last_read > 0 } val chapters = getHistory.awaitAllByMangaId(track.manga_id).filter { it.last_read > 0 }
val date = chapters.minOfOrNull { it.last_read } ?: return 0L val date = chapters.minOfOrNull { it.last_read } ?: return 0L
return if (date <= 0L) 0L else date return if (date <= 0L) 0L else date
} }
@ -139,7 +145,7 @@ suspend fun TrackService.getStartDate(track: Track): Long {
suspend fun TrackService.getCompletedDate(track: Track, allRead: Boolean): Long { suspend fun TrackService.getCompletedDate(track: Track, allRead: Boolean): Long {
if (allRead) { if (allRead) {
val chapters = db.getHistoryByMangaId(track.manga_id).executeOnIO() val chapters = getHistory.awaitAllByMangaId(track.manga_id)
val date = chapters.maxOfOrNull { it.last_read } ?: return 0L val date = chapters.maxOfOrNull { it.last_read } ?: return 0L
return if (date <= 0L) 0L else date return if (date <= 0L) 0L else date
} }
@ -147,7 +153,7 @@ suspend fun TrackService.getCompletedDate(track: Track, allRead: Boolean): Long
} }
suspend fun TrackService.getLastChapterRead(track: Track): Float { suspend fun TrackService.getLastChapterRead(track: Track): Float {
val chapters = db.getChapters(track.manga_id).executeOnIO() val chapters = getChapter.awaitAll(track.manga_id, false)
val lastChapterRead = chapters.filter { it.read }.minByOrNull { it.source_order } val lastChapterRead = chapters.filter { it.read }.minByOrNull { it.source_order }
return lastChapterRead?.takeIf { it.isRecognizedNumber }?.chapter_number ?: 0f return lastChapterRead?.takeIf { it.isRecognizedNumber }?.chapter_number ?: 0f
} }

View file

@ -70,6 +70,7 @@ import yokai.domain.category.models.CategoryUpdate
import yokai.domain.chapter.interactor.GetChapter import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.UpdateChapter import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.chapter.models.ChapterUpdate import yokai.domain.chapter.models.ChapterUpdate
import yokai.domain.history.interactor.GetHistory
import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.GetManga import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.UpdateManga import yokai.domain.manga.interactor.UpdateManga
@ -98,6 +99,7 @@ class LibraryPresenter(
private val updateChapter: UpdateChapter by injectLazy() private val updateChapter: UpdateChapter by injectLazy()
private val updateManga: UpdateManga by injectLazy() private val updateManga: UpdateManga by injectLazy()
private val getTrack: GetTrack by injectLazy() private val getTrack: GetTrack by injectLazy()
private val getHistory: GetHistory by injectLazy()
private val context = preferences.context private val context = preferences.context
private val viewContext private val viewContext
@ -544,9 +546,9 @@ class LibraryPresenter(
return if (scoresList.isEmpty()) -1 else scoresList.average().roundToInt().coerceIn(1..10) return if (scoresList.isEmpty()) -1 else scoresList.average().roundToInt().coerceIn(1..10)
} }
private fun LibraryManga.getStartYear(): Int { private suspend fun LibraryManga.getStartYear(): Int {
if (db.getChapters(id).executeAsBlocking().any { it.read }) { if (getChapter.awaitAll(id!!, false).any { it.read }) {
val chapters = db.getHistoryByMangaId(id!!).executeAsBlocking().filter { it.last_read > 0 } val chapters = getHistory.awaitAllByMangaId(id!!).filter { it.last_read > 0 }
val date = chapters.minOfOrNull { it.last_read } ?: return -1 val date = chapters.minOfOrNull { it.last_read } ?: return -1
val cal = Calendar.getInstance().apply { timeInMillis = date } val cal = Calendar.getInstance().apply { timeInMillis = date }
return if (date <= 0L) -1 else cal.get(Calendar.YEAR) return if (date <= 0L) -1 else cal.get(Calendar.YEAR)

View file

@ -24,6 +24,7 @@ import java.text.DecimalFormat
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.models.cover import yokai.domain.manga.models.cover
import yokai.i18n.MR import yokai.i18n.MR
import yokai.presentation.core.util.coil.loadManga import yokai.presentation.core.util.coil.loadManga
@ -35,6 +36,8 @@ class MigrationProcessHolder(
) : BaseFlexibleViewHolder(view, adapter) { ) : BaseFlexibleViewHolder(view, adapter) {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val getChapter: GetChapter by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private var item: MigrationProcessItem? = null private var item: MigrationProcessItem? = null
private val binding = MigrationProcessItemBinding.bind(view) private val binding = MigrationProcessItemBinding.bind(view)
@ -142,7 +145,7 @@ class MigrationProcessHolder(
root.setOnClickListener(null) root.setOnClickListener(null)
} }
private fun MangaGridItemBinding.attachManga(manga: Manga, source: Source) { private suspend fun MangaGridItemBinding.attachManga(manga: Manga, source: Source) {
(root.layoutParams as ConstraintLayout.LayoutParams).verticalBias = 1f (root.layoutParams as ConstraintLayout.LayoutParams).verticalBias = 1f
progress.isVisible = false progress.isVisible = false
@ -159,7 +162,7 @@ class MigrationProcessHolder(
gradient.isVisible = true gradient.isVisible = true
title.text = source.toString() title.text = source.toString()
val mangaChapters = db.getChapters(manga).executeAsBlocking() val mangaChapters = getChapter.awaitAll(manga, false)
unreadDownloadBadge.badgeView.setChapters(mangaChapters.size) unreadDownloadBadge.badgeView.setChapters(mangaChapters.size)
val latestChapter = mangaChapters.maxOfOrNull { it.chapter_number } ?: -1f val latestChapter = mangaChapters.maxOfOrNull { it.chapter_number } ?: -1f

View file

@ -80,7 +80,9 @@ object StatsHelper {
201..Int.MAX_VALUE, 201..Int.MAX_VALUE,
) )
fun Long.getReadDuration(blankValue: String = "0"): String { fun Long?.getReadDuration(blankValue: String = "0"): String {
if (this == null) return blankValue
val days = TimeUnit.MILLISECONDS.toDays(this) val days = TimeUnit.MILLISECONDS.toDays(this)
val hours = TimeUnit.MILLISECONDS.toHours(this) % 24 val hours = TimeUnit.MILLISECONDS.toHours(this) % 24
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60 val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60

View file

@ -20,6 +20,7 @@ import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.data.DatabaseHandler
import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.track.interactor.GetTrack import yokai.domain.track.interactor.GetTrack
import yokai.i18n.MR import yokai.i18n.MR
@ -35,6 +36,7 @@ class StatsPresenter(
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
): BaseCoroutinePresenter<StatsController>() { ): BaseCoroutinePresenter<StatsController>() {
private val handler: DatabaseHandler by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy() private val getLibraryManga: GetLibraryManga by injectLazy()
private val getTrack: GetTrack by injectLazy() private val getTrack: GetTrack by injectLazy()
@ -86,7 +88,9 @@ class StatsPresenter(
} }
fun getReadDuration(): String { fun getReadDuration(): String {
val chaptersTime = db.getTotalReadDuration() val chaptersTime = runBlocking {
handler.awaitOneOrNull { historyQueries.getTotalReadDuration() }?.sum?.toLong()
}
return chaptersTime.getReadDuration(prefs.context.getString(MR.strings.none)) return chaptersTime.getReadDuration(prefs.context.getString(MR.strings.none))
} }
} }

View file

@ -29,18 +29,21 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.roundToTwoDecimal import eu.kanade.tachiyomi.util.system.roundToTwoDecimal
import eu.kanade.tachiyomi.util.system.withUIContext import eu.kanade.tachiyomi.util.system.withUIContext
import java.util.Calendar
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.history.interactor.GetHistory
import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.track.interactor.GetTrack
import yokai.i18n.MR import yokai.i18n.MR
import yokai.util.lang.getString import yokai.util.lang.getString
import java.util.*
import java.util.concurrent.*
import kotlin.math.roundToInt
import yokai.domain.category.interactor.GetCategories
import yokai.domain.track.interactor.GetTrack
class StatsDetailsPresenter( class StatsDetailsPresenter(
private val db: DatabaseHelper = Injekt.get(), private val db: DatabaseHelper = Injekt.get(),
@ -49,8 +52,10 @@ class StatsDetailsPresenter(
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
) : BaseCoroutinePresenter<StatsDetailsController>() { ) : BaseCoroutinePresenter<StatsDetailsController>() {
private val getCategories: GetCategories by injectLazy() private val getCategories: GetCategories by injectLazy()
private val getChapter: GetChapter by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy() private val getLibraryManga: GetLibraryManga by injectLazy()
private val getTrack: GetTrack by injectLazy() private val getTrack: GetTrack by injectLazy()
private val getHistory: GetHistory by injectLazy()
private val context private val context
get() = view?.view?.context ?: prefs.context get() = view?.view?.context ?: prefs.context
@ -357,7 +362,7 @@ class StatsDetailsPresenter(
currentStats = currentStats?.take(100)?.let { ArrayList(it) } currentStats = currentStats?.take(100)?.let { ArrayList(it) }
} }
private fun setupStartYear() { private suspend fun setupStartYear() {
currentStats = ArrayList() currentStats = ArrayList()
val libraryFormat = mangasDistinct.filterByChip().groupBy { it.getStartYear() } val libraryFormat = mangasDistinct.filterByChip().groupBy { it.getStartYear() }
@ -537,9 +542,9 @@ class StatsDetailsPresenter(
return service?.get10PointScore(this.score) return service?.get10PointScore(this.score)
} }
private fun LibraryManga.getStartYear(): Int? { private suspend fun LibraryManga.getStartYear(): Int? {
if (db.getChapters(id).executeAsBlocking().any { it.read }) { if (getChapter.awaitAll(id!!, false).any { it.read }) {
val chapters = db.getHistoryByMangaId(id!!).executeAsBlocking().filter { it.last_read > 0 } val chapters = getHistory.awaitAllByMangaId(id!!).filter { it.last_read > 0 }
val date = chapters.minOfOrNull { it.last_read } ?: return null val date = chapters.minOfOrNull { it.last_read } ?: return null
val cal = Calendar.getInstance().apply { timeInMillis = date } val cal = Calendar.getInstance().apply { timeInMillis = date }
return if (date <= 0L) null else cal.get(Calendar.YEAR) return if (date <= 0L) null else cal.get(Calendar.YEAR)

View file

@ -18,7 +18,6 @@ import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
@ -87,6 +86,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import yokai.domain.base.BasePreferences.ExtensionInstaller import yokai.domain.base.BasePreferences.ExtensionInstaller
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.extension.interactor.TrustExtension import yokai.domain.extension.interactor.TrustExtension
import yokai.domain.manga.interactor.GetManga import yokai.domain.manga.interactor.GetManga
import yokai.domain.source.SourcePreferences import yokai.domain.source.SourcePreferences
@ -104,8 +104,7 @@ class SettingsAdvancedController : SettingsLegacyController() {
private val readerPreferences: ReaderPreferences by injectLazy() private val readerPreferences: ReaderPreferences by injectLazy()
private val sourcePreferences: SourcePreferences by injectLazy() private val sourcePreferences: SourcePreferences by injectLazy()
private val db: DatabaseHelper by injectLazy() private val getChapter: GetChapter by injectLazy()
private val getManga: GetManga by injectLazy() private val getManga: GetManga by injectLazy()
private val downloadManager: DownloadManager by injectLazy() private val downloadManager: DownloadManager by injectLazy()
@ -511,7 +510,7 @@ class SettingsAdvancedController : SettingsLegacyController() {
} }
continue continue
} }
val chapterList = db.getChapters(manga).executeAsBlocking() val chapterList = getChapter.awaitAll(manga, false)
foldersCleared += downloadManager.cleanupChapters(chapterList, manga, source, removeRead, removeNonFavorite) foldersCleared += downloadManager.cleanupChapters(chapterList, manga, source, removeRead, removeNonFavorite)
} }
} }

View file

@ -42,6 +42,9 @@ JOIN chapters
ON history.history_chapter_id = chapters._id ON history.history_chapter_id = chapters._id
WHERE chapters.url = :chapterUrl AND history.history_chapter_id = chapters._id; WHERE chapters.url = :chapterUrl AND history.history_chapter_id = chapters._id;
getTotalReadDuration:
SELECT sum(history_time_read) FROM history;
getRecentsUngrouped: getRecentsUngrouped:
SELECT SELECT
M.*, M.*,