refactor(chapter): Migrate more queries to SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-08-17 07:59:39 +07:00
parent fac21dbab7
commit 1e68e55cf7
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
14 changed files with 160 additions and 95 deletions

View file

@ -10,12 +10,14 @@ import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.domain.manga.models.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.chapter.interactor.GetChapter
class MangaBackupCreator(
private val db: DatabaseHelper = Injekt.get(),
private val customMangaManager: CustomMangaManager = Injekt.get(),
private val getChapter: GetChapter = Injekt.get(),
) {
operator fun invoke(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
suspend operator fun invoke(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
if (!options.libraryEntries) return emptyList()
return mangas.map {
@ -30,14 +32,14 @@ class MangaBackupCreator(
* @param options options for the backup
* @return [BackupManga] containing manga in a serializable form
*/
private fun backupManga(manga: Manga, options: BackupOptions): BackupManga {
private suspend fun backupManga(manga: Manga, options: BackupOptions): BackupManga {
// Entry for this manga
val mangaObject = BackupManga.copyFrom(manga, if (options.customInfo) customMangaManager else null)
// Check if user wants chapter information in backup
if (options.chapters) {
// Backup all the chapters
val chapters = db.getChapters(manga).executeAsBlocking()
val chapters = manga.id?.let { getChapter.awaitAll(it, false) }.orEmpty()
if (chapters.isNotEmpty()) {
mangaObject.chapters = chapters.map { BackupChapter.copyFrom(it) }
}
@ -65,7 +67,7 @@ class MangaBackupCreator(
val historyForManga = db.getHistoryByMangaId(manga.id!!).executeAsBlocking()
if (historyForManga.isNotEmpty()) {
val history = historyForManga.mapNotNull { history ->
val url = db.getChapter(history.chapter_id).executeAsBlocking()?.url
val url = getChapter.awaitById(history.chapter_id)?.url
url?.let { BackupHistory(url, history.last_read, history.time_read) }
}
if (history.isNotEmpty()) {

View file

@ -21,6 +21,7 @@ import uy.kohesive.injekt.api.get
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.InsertChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.library.custom.model.CustomMangaInfo
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
@ -32,6 +33,7 @@ class MangaBackupRestorer(
private val getCategories: GetCategories = Injekt.get(),
private val getChapter: GetChapter = Injekt.get(),
private val insertChapter: InsertChapter = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val insertManga: InsertManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
@ -138,7 +140,7 @@ class MangaBackupRestorer(
}
val newChapters = chapters.groupBy { it.id != null }
newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() }
newChapters[true]?.let { updateChapter.awaitAll(it.map(Chapter::toProgressUpdate)) }
newChapters[false]?.let { insertChapter.awaitBulk(it) }
}
@ -210,7 +212,7 @@ class MangaBackupRestorer(
historyToBeUpdated.add(dbHistory)
} else {
// If not in database create
db.getChapter(url).executeAsBlocking()?.let {
getChapter.awaitByUrl(url, false)?.let {
val historyToAdd = History.create(it).apply {
last_read = lastRead
time_read = readDuration

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.source.model.SChapter
import java.io.Serializable
import yokai.domain.chapter.models.ChapterUpdate
interface Chapter : SChapter, Serializable {
@ -24,6 +25,15 @@ interface Chapter : SChapter, Serializable {
val isRecognizedNumber: Boolean
get() = chapter_number >= 0f
fun toProgressUpdate() =
ChapterUpdate(
id = this.id!!,
read = this.read,
bookmark = this.bookmark,
lastPageRead = this.last_page_read.toLong(),
pagesLeft = this.pages_left.toLong(),
)
companion object {
fun create(): Chapter = ChapterImpl().apply {

View file

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.resolvers.ChapterKnownBackupPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
@ -23,6 +22,7 @@ interface ChapterQueries : DbProvider {
)
.prepare()
// FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
fun getChapter(id: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
@ -34,54 +34,12 @@ interface ChapterQueries : DbProvider {
)
.prepare()
fun getChapter(url: String) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build(),
)
.prepare()
fun getChapters(url: String) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build(),
)
.prepare()
fun getChapter(url: String, mangaId: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(url, mangaId)
.build(),
)
.prepare()
// FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
fun insertChapters(chapters: List<Chapter>) = db.put().objects(chapters).prepare()
fun updateKnownChaptersBackup(chapters: List<Chapter>) = db.put()
.objects(chapters)
.withPutResolver(ChapterKnownBackupPutResolver())
.prepare()
// FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
fun updateChapterProgress(chapter: Chapter) = db.put()
.`object`(chapter)
.withPutResolver(ChapterProgressPutResolver())
.prepare()
fun updateChaptersProgress(chapters: List<Chapter>) = db.put()
.objects(chapters)
.withPutResolver(ChapterProgressPutResolver())
.prepare()
}

View file

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context
import androidx.core.content.edit
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.source.SourceManager
@ -11,6 +10,8 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.GetManga
/**
* This class is used to persist active downloads across application restarts.
@ -28,7 +29,8 @@ class DownloadStore(
private val preferences = context.getSharedPreferences("active_downloads", Context.MODE_PRIVATE)
private val json: Json by injectLazy()
private val db: DatabaseHelper by injectLazy()
private val getChapter: GetChapter by injectLazy()
private val getManga: GetManga by injectLazy()
/**
* Counter used to keep the queue order.
@ -78,7 +80,7 @@ class DownloadStore(
/**
* Returns the list of downloads to restore. It should be called in a background thread.
*/
fun restore(): List<Download> {
suspend fun restore(): List<Download> {
val objs = preferences.all
.mapNotNull { it.value as? String }
.mapNotNull { deserialize(it) }
@ -89,10 +91,10 @@ class DownloadStore(
val cachedManga = mutableMapOf<Long, Manga?>()
for ((mangaId, chapterId) in objs) {
val manga = cachedManga.getOrPut(mangaId) {
db.getManga(mangaId).executeAsBlocking()
getManga.awaitById(mangaId)
} ?: continue
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
val chapter = db.getChapter(chapterId).executeAsBlocking() ?: continue
val chapter = getChapter.awaitById(chapterId) ?: continue
downloads.add(Download(source, manga, chapter))
}
}

View file

@ -8,7 +8,6 @@ import android.content.Intent
import android.net.Uri
import androidx.core.content.IntentCompat
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.download.DownloadJob
import eu.kanade.tachiyomi.data.download.DownloadManager
@ -28,11 +27,15 @@ import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.getParcelableCompat
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.notificationManager
import java.io.File
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.manga.interactor.GetManga
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
/**
@ -41,6 +44,10 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
* NOTE: Use local broadcasts if possible.
*/
class NotificationReceiver : BroadcastReceiver() {
private val getChapter: GetChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private val getManga: GetManga by injectLazy()
/**
* Download manager.
*/
@ -204,23 +211,25 @@ class NotificationReceiver : BroadcastReceiver() {
* @param notificationId id of notification
*/
private fun markAsRead(chapterUrls: Array<String>, mangaId: Long) {
val db: DatabaseHelper = Injekt.get()
val preferences: PreferencesHelper = Injekt.get()
val manga = db.getManga(mangaId).executeAsBlocking() ?: return
val chapters = chapterUrls.map {
val chapter = db.getChapter(it, mangaId).executeAsBlocking() ?: return
chapter.read = true
db.updateChapterProgress(chapter).executeAsBlocking()
if (preferences.removeAfterMarkedAsRead().get()) {
val sourceManager: SourceManager = Injekt.get()
val source = sourceManager.get(manga.source) ?: return
downloadManager.deleteChapters(listOf(chapter), manga, source)
launchIO {
val manga = getManga.awaitById(mangaId) ?: return@launchIO
val chapters = chapterUrls.map {
val chapter = getChapter.awaitByUrlAndMangaId(it, mangaId, false) ?: return@launchIO
chapter.read = true
updateChapter.await(chapter.toProgressUpdate())
if (preferences.removeAfterMarkedAsRead().get()) {
val sourceManager: SourceManager = Injekt.get()
val source = sourceManager.get(manga.source) ?: return@launchIO
downloadManager.deleteChapters(listOf(chapter), manga, source)
}
return@map chapter
}
return@map chapter
val newLastChapter = chapters.maxByOrNull { it.chapter_number.toInt() }
LibraryUpdateJob.updateMutableFlow.tryEmit(manga.id)
updateTrackChapterMarkedAsRead(Injekt.get(), preferences, newLastChapter, mangaId, 0)
}
val newLastChapter = chapters.maxByOrNull { it.chapter_number.toInt() }
LibraryUpdateJob.updateMutableFlow.tryEmit(manga.id)
updateTrackChapterMarkedAsRead(db, preferences, newLastChapter, mangaId, 0)
}
/** Method called when user wants to stop a restore

View file

@ -64,6 +64,11 @@ import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.widget.TriStateCheckBox
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -78,16 +83,13 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetAvailableScanlators
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.UpdateChapter
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.i18n.MR
import yokai.util.lang.getString
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.util.*
class MangaDetailsPresenter(
val manga: Manga,
@ -101,6 +103,7 @@ class MangaDetailsPresenter(
) : BaseCoroutinePresenter<MangaDetailsController>(), DownloadQueue.DownloadListener {
private val getAvailableScanlators: GetAvailableScanlators by injectLazy()
private val getChapter: GetChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private val updateManga: UpdateManga by injectLazy()
private val customMangaManager: CustomMangaManager by injectLazy()
@ -508,10 +511,11 @@ class MangaDetailsPresenter(
*/
fun bookmarkChapters(selectedChapters: List<ChapterItem>, bookmarked: Boolean) {
presenterScope.launch(Dispatchers.IO) {
selectedChapters.forEach {
val updates = selectedChapters.map {
it.bookmark = bookmarked
it.toProgressUpdate()
}
db.updateChaptersProgress(selectedChapters).executeAsBlocking()
updateChapter.awaitAll(updates)
getChapters()
withContext(Dispatchers.Main) { view?.updateChapters(chapters) }
}
@ -529,15 +533,16 @@ class MangaDetailsPresenter(
lastRead: Int? = null,
pagesLeft: Int? = null,
) {
presenterScope.launch(Dispatchers.IO) {
selectedChapters.forEach {
presenterScope.launchIO {
val updates = selectedChapters.map {
it.read = read
if (!read) {
it.last_page_read = lastRead ?: 0
it.pages_left = pagesLeft ?: 0
}
it.toProgressUpdate()
}
db.updateChaptersProgress(selectedChapters).executeAsBlocking()
updateChapter.awaitAll(updates)
if (read && deleteNow && preferences.removeAfterMarkedAsRead().get()) {
deleteChapters(selectedChapters, false)
}

View file

@ -78,6 +78,8 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.InsertChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.chapter.models.ChapterUpdate
import yokai.domain.download.DownloadPreferences
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
@ -103,6 +105,7 @@ class ReaderViewModel(
) : ViewModel() {
private val getChapter: GetChapter by injectLazy()
private val insertChapter: InsertChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private val getManga: GetManga by injectLazy()
private val insertManga: InsertManga by injectLazy()
private val updateManga: UpdateManga by injectLazy()
@ -444,7 +447,16 @@ class ReaderViewModel(
fun toggleBookmark(chapter: Chapter) {
chapter.bookmark = !chapter.bookmark
db.updateChapterProgress(chapter).executeAsBlocking()
viewModelScope.launchNonCancellableIO {
updateChapter.await(
ChapterUpdate(
id = chapter.id!!,
bookmark = chapter.bookmark,
lastPageRead = chapter.last_page_read.toLong(),
pagesLeft = chapter.pages_left.toLong(),
)
)
}
}
/**
@ -617,6 +629,7 @@ class ReaderViewModel(
* Saves this [readerChapter]'s progress (last read page and whether it's read).
* If incognito mode isn't on or has at least 1 tracker
*/
// FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
private fun saveChapterProgress(readerChapter: ReaderChapter) {
readerChapter.requestedPage = readerChapter.chapter.last_page_read
db.getChapter(readerChapter.chapter.id!!).executeAsBlocking()?.let { dbChapter ->
@ -630,6 +643,7 @@ class ReaderViewModel(
/**
* Saves this [readerChapter] last read history.
*/
// FIXME: Migrate to SQLDelight, on halt: in StorIO transaction
private fun saveChapterHistory(readerChapter: ReaderChapter) {
if (!preferences.incognitoMode().get()) {
val readAt = Date().time

View file

@ -42,6 +42,7 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.RecentChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.recents.RecentsPreferences
import yokai.domain.ui.UiPreferences
import yokai.i18n.MR
@ -56,6 +57,7 @@ class RecentsPresenter(
) : BaseCoroutinePresenter<RecentsController>(), DownloadQueue.DownloadListener {
private val getChapter: GetChapter by injectLazy()
private val recentChapter: RecentChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
private var recentsJob: Job? = null
var recentItems = listOf<RecentMangaItem>()
@ -639,7 +641,7 @@ class RecentsPresenter(
pages_left = pagesLeft ?: 0
}
}
db.updateChaptersProgress(listOf(chapter)).executeAsBlocking()
updateChapter.await(chapter.toProgressUpdate())
getRecents()
}
}

View file

@ -16,6 +16,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.chapter.interactor.UpdateChapter
/**
* Helper method for syncing a remote track with the local chapters, and back
@ -25,20 +26,26 @@ import uy.kohesive.injekt.api.get
* @param remoteTrack the remote Track object.
* @param service the tracker service.
*/
fun syncChaptersWithTrackServiceTwoWay(db: DatabaseHelper, chapters: List<Chapter>, remoteTrack: Track, service: TrackService) {
val sortedChapters = chapters.sortedBy { it.chapter_number }
sortedChapters
.filter { chapter -> chapter.chapter_number <= remoteTrack.last_chapter_read && !chapter.read }
.forEach { it.read = true }
db.updateChaptersProgress(sortedChapters).executeAsBlocking()
// only take into account continuous reading
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapter_number ?: 0F
// update remote
remoteTrack.last_chapter_read = localLastRead
fun syncChaptersWithTrackServiceTwoWay(
db: DatabaseHelper,
chapters: List<Chapter>,
remoteTrack: Track,
service: TrackService,
updateChapter: UpdateChapter = Injekt.get(),
) {
launchIO {
val sortedChapters = chapters.sortedBy { it.chapter_number }
sortedChapters
.filter { chapter -> chapter.chapter_number <= remoteTrack.last_chapter_read && !chapter.read }
.forEach { it.read = true }
updateChapter.awaitAll(sortedChapters.map(Chapter::toProgressUpdate))
// only take into account continuous reading
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapter_number ?: 0F
// update remote
remoteTrack.last_chapter_read = localLastRead
try {
service.update(remoteTrack)
db.insertTrack(remoteTrack).executeAsBlocking()

View file

@ -16,9 +16,33 @@ class ChapterRepositoryImpl(private val handler: DatabaseHandler) : ChapterRepos
override fun getChaptersAsFlow(mangaId: Long, filterScanlators: Boolean): Flow<List<Chapter>> =
handler.subscribeToList { chaptersQueries.getChaptersByMangaId(mangaId, filterScanlators.toInt().toLong(), Chapter::mapper) }
override suspend fun getChapterById(id: Long): Chapter? =
handler.awaitOneOrNull { chaptersQueries.getChaptersById(id, Chapter::mapper) }
override suspend fun getChaptersByUrl(url: String, filterScanlators: Boolean): List<Chapter> =
handler.awaitList { chaptersQueries.getChaptersByUrl(url, filterScanlators.toInt().toLong(), Chapter::mapper) }
override suspend fun getChapterByUrl(url: String, filterScanlators: Boolean): Chapter? =
handler.awaitOneOrNull { chaptersQueries.getChaptersByUrl(url, filterScanlators.toInt().toLong(), Chapter::mapper) }
override suspend fun getChaptersByUrlAndMangaId(
url: String,
mangaId: Long,
filterScanlators: Boolean
): List<Chapter> =
handler.awaitList {
chaptersQueries.getChaptersByUrlAndMangaId(url, mangaId, filterScanlators.toInt().toLong(), Chapter::mapper)
}
override suspend fun getChapterByUrlAndMangaId(
url: String,
mangaId: Long,
filterScanlators: Boolean
): Chapter? =
handler.awaitOneOrNull {
chaptersQueries.getChaptersByUrlAndMangaId(url, mangaId, filterScanlators.toInt().toLong(), Chapter::mapper)
}
override suspend fun getRecents(filterScanlators: Boolean, search: String, limit: Long, offset: Long): List<MangaChapter> =
handler.awaitList { chaptersQueries.getRecents(search, filterScanlators.toInt().toLong(), limit, offset, MangaChapter::mapper) }

View file

@ -9,7 +9,13 @@ interface ChapterRepository {
suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List<Chapter>
fun getChaptersAsFlow(mangaId: Long, filterScanlators: Boolean): Flow<List<Chapter>>
suspend fun getChapterById(id: Long): Chapter?
suspend fun getChaptersByUrl(url: String, filterScanlators: Boolean): List<Chapter>
suspend fun getChapterByUrl(url: String, filterScanlators: Boolean): Chapter?
suspend fun getChaptersByUrlAndMangaId(url: String, mangaId: Long, filterScanlators: Boolean): List<Chapter>
suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long, filterScanlators: Boolean): Chapter?
suspend fun getRecents(filterScanlators: Boolean, search: String = "", limit: Long = 25L, offset: Long = 0L): List<MangaChapter>

View file

@ -11,8 +11,17 @@ class GetChapter(
suspend fun awaitAll(manga: Manga, filterScanlators: Boolean? = null) =
awaitAll(manga.id!!, filterScanlators ?: (manga.filtered_scanlators?.isNotEmpty() == true))
suspend fun awaitById(id: Long) = chapterRepository.getChapterById(id)
suspend fun awaitAllByUrl(chapterUrl: String, filterScanlators: Boolean) =
chapterRepository.getChaptersByUrl(chapterUrl, filterScanlators)
suspend fun awaitByUrl(chapterUrl: String, filterScanlators: Boolean) =
chapterRepository.getChapterByUrl(chapterUrl, filterScanlators)
suspend fun awaitAllByUrlAndMangaId(chapterUrl: String, mangaId: Long, filterScanlators: Boolean) =
chapterRepository.getChaptersByUrlAndMangaId(chapterUrl, mangaId, filterScanlators)
suspend fun awaitByUrlAndMangaId(chapterUrl: String, mangaId: Long, filterScanlators: Boolean) =
chapterRepository.getChapterByUrlAndMangaId(chapterUrl, mangaId, filterScanlators)
fun subscribeAll(mangaId: Long, filterScanlators: Boolean) = chapterRepository.getChaptersAsFlow(mangaId, filterScanlators)
}