mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-26 23:18:17 +03:00
Use SQLDelight for all Manga related queries (#7447)
This commit is contained in:
parent
6d6237e370
commit
17951cfd68
42 changed files with 343 additions and 753 deletions
|
@ -165,6 +165,7 @@ dependencies {
|
||||||
implementation(androidx.paging.runtime)
|
implementation(androidx.paging.runtime)
|
||||||
implementation(androidx.paging.compose)
|
implementation(androidx.paging.compose)
|
||||||
|
|
||||||
|
implementation(libs.bundles.sqlite)
|
||||||
implementation(androidx.sqlite)
|
implementation(androidx.sqlite)
|
||||||
implementation(libs.sqldelight.android.driver)
|
implementation(libs.sqldelight.android.driver)
|
||||||
implementation(libs.sqldelight.coroutines)
|
implementation(libs.sqldelight.coroutines)
|
||||||
|
@ -218,11 +219,6 @@ dependencies {
|
||||||
implementation(libs.unifile)
|
implementation(libs.unifile)
|
||||||
implementation(libs.junrar)
|
implementation(libs.junrar)
|
||||||
|
|
||||||
// Database
|
|
||||||
implementation(libs.bundles.sqlite)
|
|
||||||
implementation("com.github.inorichi.storio:storio-common:8be19de@aar")
|
|
||||||
implementation("com.github.inorichi.storio:storio-sqlite:8be19de@aar")
|
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
implementation(libs.preferencektx)
|
implementation(libs.preferencektx)
|
||||||
implementation(libs.flowpreferences)
|
implementation(libs.flowpreferences)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.data.manga
|
||||||
|
|
||||||
import eu.kanade.domain.chapter.model.Chapter
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
|
|
||||||
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long) -> Manga =
|
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long) -> Manga =
|
||||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, _, initialized, viewer, chapterFlags, coverLastModified, dateAdded ->
|
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, _, initialized, viewer, chapterFlags, coverLastModified, dateAdded ->
|
||||||
|
@ -61,3 +62,29 @@ val mangaChapterMapper: (Long, Long, String, String?, String?, String?, List<Str
|
||||||
scanlator = scanlator,
|
scanlator = scanlator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga =
|
||||||
|
{ _id, source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, unread_count, read_count, category ->
|
||||||
|
LibraryManga().apply {
|
||||||
|
this.id = _id
|
||||||
|
this.source = source
|
||||||
|
this.url = url
|
||||||
|
this.artist = artist
|
||||||
|
this.author = author
|
||||||
|
this.description = description
|
||||||
|
this.genre = genre?.joinToString()
|
||||||
|
this.title = title
|
||||||
|
this.status = status.toInt()
|
||||||
|
this.thumbnail_url = thumbnail_url
|
||||||
|
this.favorite = favorite
|
||||||
|
this.last_update = last_update ?: 0
|
||||||
|
this.initialized = initialized
|
||||||
|
this.viewer_flags = viewer.toInt()
|
||||||
|
this.chapter_flags = chapter_flags.toInt()
|
||||||
|
this.cover_last_modified = cover_last_modified
|
||||||
|
this.date_added = date_added
|
||||||
|
this.unreadCount = unread_count.toInt()
|
||||||
|
this.readCount = read_count.toInt()
|
||||||
|
this.category = category.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import eu.kanade.data.toLong
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
@ -18,18 +19,26 @@ class MangaRepositoryImpl(
|
||||||
return handler.awaitOne { mangasQueries.getMangaById(id, mangaMapper) }
|
return handler.awaitOne { mangasQueries.getMangaById(id, mangaMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun subscribeMangaById(id: Long): Flow<Manga> {
|
|
||||||
return handler.subscribeToOne { mangasQueries.getMangaById(id, mangaMapper) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getMangaByIdAsFlow(id: Long): Flow<Manga> {
|
override suspend fun getMangaByIdAsFlow(id: Long): Flow<Manga> {
|
||||||
return handler.subscribeToOne { mangasQueries.getMangaById(id, mangaMapper) }
|
return handler.subscribeToOne { mangasQueries.getMangaById(id, mangaMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
||||||
|
return handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(url, sourceId, mangaMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getFavorites(): List<Manga> {
|
override suspend fun getFavorites(): List<Manga> {
|
||||||
return handler.awaitList { mangasQueries.getFavorites(mangaMapper) }
|
return handler.awaitList { mangasQueries.getFavorites(mangaMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getLibraryManga(): List<LibraryManga> {
|
||||||
|
return handler.awaitList { mangasQueries.getLibrary(libraryManga) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>> {
|
||||||
|
return handler.subscribeToList { mangasQueries.getLibrary(libraryManga) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>> {
|
override fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>> {
|
||||||
return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
|
return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
|
||||||
}
|
}
|
||||||
|
@ -59,6 +68,31 @@ class MangaRepositoryImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(manga: Manga): Long? {
|
||||||
|
return handler.awaitOneOrNull {
|
||||||
|
mangasQueries.insert(
|
||||||
|
source = manga.source,
|
||||||
|
url = manga.url,
|
||||||
|
artist = manga.artist,
|
||||||
|
author = manga.author,
|
||||||
|
description = manga.description,
|
||||||
|
genre = manga.genre,
|
||||||
|
title = manga.title,
|
||||||
|
status = manga.status,
|
||||||
|
thumbnail_url = manga.thumbnailUrl,
|
||||||
|
favorite = manga.favorite,
|
||||||
|
last_update = manga.lastUpdate,
|
||||||
|
next_update = null,
|
||||||
|
initialized = manga.initialized,
|
||||||
|
viewer = manga.viewerFlags,
|
||||||
|
chapter_flags = manga.chapterFlags,
|
||||||
|
cover_last_modified = manga.coverLastModified,
|
||||||
|
date_added = manga.dateAdded,
|
||||||
|
)
|
||||||
|
mangasQueries.selectLastInsertedRowId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun update(update: MangaUpdate): Boolean {
|
override suspend fun update(update: MangaUpdate): Boolean {
|
||||||
return try {
|
return try {
|
||||||
partialUpdate(update)
|
partialUpdate(update)
|
||||||
|
|
|
@ -32,10 +32,13 @@ import eu.kanade.domain.history.interactor.UpsertHistory
|
||||||
import eu.kanade.domain.history.repository.HistoryRepository
|
import eu.kanade.domain.history.repository.HistoryRepository
|
||||||
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.GetFavorites
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
|
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
|
||||||
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
||||||
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
|
@ -71,11 +74,14 @@ class DomainModule : InjektModule {
|
||||||
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||||
addFactory { GetDuplicateLibraryManga(get()) }
|
addFactory { GetDuplicateLibraryManga(get()) }
|
||||||
addFactory { GetFavorites(get()) }
|
addFactory { GetFavorites(get()) }
|
||||||
|
addFactory { GetLibraryManga(get()) }
|
||||||
addFactory { GetMangaWithChapters(get(), get()) }
|
addFactory { GetMangaWithChapters(get(), get()) }
|
||||||
addFactory { GetMangaById(get()) }
|
addFactory { GetManga(get()) }
|
||||||
addFactory { GetNextChapter(get()) }
|
addFactory { GetNextChapter(get()) }
|
||||||
addFactory { ResetViewerFlags(get()) }
|
addFactory { ResetViewerFlags(get()) }
|
||||||
addFactory { SetMangaChapterFlags(get()) }
|
addFactory { SetMangaChapterFlags(get()) }
|
||||||
|
addFactory { SetMangaViewerFlags(get()) }
|
||||||
|
addFactory { InsertManga(get()) }
|
||||||
addFactory { UpdateManga(get()) }
|
addFactory { UpdateManga(get()) }
|
||||||
addFactory { SetMangaCategories(get()) }
|
addFactory { SetMangaCategories(get()) }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class GetLibraryManga(
|
||||||
|
private val mangaRepository: MangaRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(): List<LibraryManga> {
|
||||||
|
return mangaRepository.getLibraryManga()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(): Flow<List<LibraryManga>> {
|
||||||
|
return mangaRepository.getLibraryMangaAsFlow()
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
|
||||||
class GetMangaById(
|
class GetManga(
|
||||||
private val mangaRepository: MangaRepository,
|
private val mangaRepository: MangaRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ class GetMangaById(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun subscribe(id: Long): Flow<Manga> {
|
suspend fun subscribe(id: Long): Flow<Manga> {
|
||||||
return mangaRepository.subscribeMangaById(id)
|
return mangaRepository.getMangaByIdAsFlow(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun await(url: String, sourceId: Long): Manga? {
|
||||||
|
return mangaRepository.getMangaByUrlAndSourceId(url, sourceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ class GetMangaWithChapters(
|
||||||
|
|
||||||
suspend fun subscribe(id: Long): Flow<Pair<Manga, List<Chapter>>> {
|
suspend fun subscribe(id: Long): Flow<Pair<Manga, List<Chapter>>> {
|
||||||
return combine(
|
return combine(
|
||||||
mangaRepository.subscribeMangaById(id),
|
mangaRepository.getMangaByIdAsFlow(id),
|
||||||
chapterRepository.getChapterByMangaIdAsFlow(id),
|
chapterRepository.getChapterByMangaIdAsFlow(id),
|
||||||
) { manga, chapters ->
|
) { manga, chapters ->
|
||||||
Pair(manga, chapters)
|
Pair(manga, chapters)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.model.Manga
|
||||||
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
|
|
||||||
|
class InsertManga(
|
||||||
|
private val mangaRepository: MangaRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(manga: Manga): Long? {
|
||||||
|
return mangaRepository.insert(manga)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||||
|
|
||||||
|
class SetMangaViewerFlags(
|
||||||
|
private val mangaRepository: MangaRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun awaitSetMangaReadingMode(id: Long, flag: Long) {
|
||||||
|
mangaRepository.update(
|
||||||
|
MangaUpdate(
|
||||||
|
id = id,
|
||||||
|
viewerFlags = flag.setFlag(flag, ReadingModeType.MASK.toLong()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitSetOrientationType(id: Long, flag: Long) {
|
||||||
|
mangaRepository.update(
|
||||||
|
MangaUpdate(
|
||||||
|
id = id,
|
||||||
|
viewerFlags = flag.setFlag(flag, OrientationType.MASK.toLong()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Long.setFlag(flag: Long, mask: Long): Long {
|
||||||
|
return this and mask.inv() or (flag and mask)
|
||||||
|
}
|
||||||
|
}
|
|
@ -175,6 +175,28 @@ fun Manga.toMangaInfo(): MangaInfo = MangaInfo(
|
||||||
title = title,
|
title = title,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun Manga.toMangaUpdate(): MangaUpdate {
|
||||||
|
return MangaUpdate(
|
||||||
|
id = id,
|
||||||
|
source = source,
|
||||||
|
favorite = favorite,
|
||||||
|
lastUpdate = lastUpdate,
|
||||||
|
dateAdded = dateAdded,
|
||||||
|
viewerFlags = viewerFlags,
|
||||||
|
chapterFlags = chapterFlags,
|
||||||
|
coverLastModified = coverLastModified,
|
||||||
|
url = url,
|
||||||
|
title = title,
|
||||||
|
artist = artist,
|
||||||
|
author = author,
|
||||||
|
description = description,
|
||||||
|
genre = genre,
|
||||||
|
status = status,
|
||||||
|
thumbnailUrl = thumbnailUrl,
|
||||||
|
initialized = initialized,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun Manga.isLocal(): Boolean = source == LocalSource.ID
|
fun Manga.isLocal(): Boolean = source == LocalSource.ID
|
||||||
|
|
||||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
||||||
|
|
|
@ -2,18 +2,23 @@ package eu.kanade.domain.manga.repository
|
||||||
|
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface MangaRepository {
|
interface MangaRepository {
|
||||||
|
|
||||||
suspend fun getMangaById(id: Long): Manga
|
suspend fun getMangaById(id: Long): Manga
|
||||||
|
|
||||||
suspend fun subscribeMangaById(id: Long): Flow<Manga>
|
|
||||||
|
|
||||||
suspend fun getMangaByIdAsFlow(id: Long): Flow<Manga>
|
suspend fun getMangaByIdAsFlow(id: Long): Flow<Manga>
|
||||||
|
|
||||||
|
suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga?
|
||||||
|
|
||||||
suspend fun getFavorites(): List<Manga>
|
suspend fun getFavorites(): List<Manga>
|
||||||
|
|
||||||
|
suspend fun getLibraryManga(): List<LibraryManga>
|
||||||
|
|
||||||
|
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
|
||||||
|
|
||||||
fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
|
fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
|
||||||
|
|
||||||
suspend fun getDuplicateLibraryManga(title: String, sourceId: Long): Manga?
|
suspend fun getDuplicateLibraryManga(title: String, sourceId: Long): Manga?
|
||||||
|
@ -22,6 +27,8 @@ interface MangaRepository {
|
||||||
|
|
||||||
suspend fun setMangaCategories(mangaId: Long, categoryIds: List<Long>)
|
suspend fun setMangaCategories(mangaId: Long, categoryIds: List<Long>)
|
||||||
|
|
||||||
|
suspend fun insert(manga: Manga): Long?
|
||||||
|
|
||||||
suspend fun update(update: MangaUpdate): Boolean
|
suspend fun update(update: MangaUpdate): Boolean
|
||||||
|
|
||||||
suspend fun updateAll(values: List<MangaUpdate>): Boolean
|
suspend fun updateAll(values: List<MangaUpdate>): Boolean
|
||||||
|
|
|
@ -15,7 +15,6 @@ import eu.kanade.data.dateAdapter
|
||||||
import eu.kanade.data.listOfStringsAdapter
|
import eu.kanade.data.listOfStringsAdapter
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.DbOpenCallback
|
import eu.kanade.tachiyomi.data.database.DbOpenCallback
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
@ -76,8 +75,6 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
addSingletonFactory { PreferencesHelper(app) }
|
addSingletonFactory { PreferencesHelper(app) }
|
||||||
|
|
||||||
addSingletonFactory { DatabaseHelper(get()) }
|
|
||||||
|
|
||||||
addSingletonFactory { ChapterCache(app) }
|
addSingletonFactory { ChapterCache(app) }
|
||||||
|
|
||||||
addSingletonFactory { CoverCache(app) }
|
addSingletonFactory { CoverCache(app) }
|
||||||
|
@ -106,8 +103,6 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
get<Database>()
|
get<Database>()
|
||||||
|
|
||||||
get<DatabaseHelper>()
|
|
||||||
|
|
||||||
get<DownloadManager>()
|
get<DownloadManager>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database
|
|
||||||
|
|
||||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
|
||||||
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite
|
|
||||||
import eu.kanade.tachiyomi.data.database.mappers.ChapterTypeMapping
|
|
||||||
import eu.kanade.tachiyomi.data.database.mappers.MangaCategoryTypeMapping
|
|
||||||
import eu.kanade.tachiyomi.data.database.mappers.MangaTypeMapping
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
|
||||||
import eu.kanade.tachiyomi.data.database.queries.MangaQueries
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides operations to manage the database through its interfaces.
|
|
||||||
*/
|
|
||||||
class DatabaseHelper(
|
|
||||||
openHelper: SupportSQLiteOpenHelper,
|
|
||||||
) :
|
|
||||||
MangaQueries {
|
|
||||||
|
|
||||||
override val db = DefaultStorIOSQLite.builder()
|
|
||||||
.sqliteOpenHelper(openHelper)
|
|
||||||
.addTypeMapping(Manga::class.java, MangaTypeMapping())
|
|
||||||
.addTypeMapping(Chapter::class.java, ChapterTypeMapping())
|
|
||||||
.addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping())
|
|
||||||
.build()
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database
|
|
||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
|
||||||
|
|
||||||
inline fun StorIOSQLite.inTransaction(block: () -> Unit) {
|
|
||||||
lowLevel().beginTransaction()
|
|
||||||
try {
|
|
||||||
block()
|
|
||||||
lowLevel().setTransactionSuccessful()
|
|
||||||
} finally {
|
|
||||||
lowLevel().endTransaction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T> StorIOSQLite.inTransactionReturn(block: () -> T): T {
|
|
||||||
lowLevel().beginTransaction()
|
|
||||||
try {
|
|
||||||
val result = block()
|
|
||||||
lowLevel().setTransactionSuccessful()
|
|
||||||
return result
|
|
||||||
} finally {
|
|
||||||
lowLevel().endTransaction()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database
|
|
||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite
|
|
||||||
|
|
||||||
interface DbProvider {
|
|
||||||
val db: DefaultStorIOSQLite
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.mappers
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_BOOKMARK
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_CHAPTER_NUMBER
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_DATE_FETCH
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_DATE_UPLOAD
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_LAST_PAGE_READ
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_MANGA_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_NAME
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_READ
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_SCANLATOR
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_SOURCE_ORDER
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_URL
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.TABLE
|
|
||||||
|
|
||||||
class ChapterTypeMapping : SQLiteTypeMapping<Chapter>(
|
|
||||||
ChapterPutResolver(),
|
|
||||||
ChapterGetResolver(),
|
|
||||||
ChapterDeleteResolver(),
|
|
||||||
)
|
|
||||||
|
|
||||||
class ChapterPutResolver : DefaultPutResolver<Chapter>() {
|
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Chapter) = InsertQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Chapter) = UpdateQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Chapter) =
|
|
||||||
contentValuesOf(
|
|
||||||
COL_ID to obj.id,
|
|
||||||
COL_MANGA_ID to obj.manga_id,
|
|
||||||
COL_URL to obj.url,
|
|
||||||
COL_NAME to obj.name,
|
|
||||||
COL_READ to obj.read,
|
|
||||||
COL_SCANLATOR to obj.scanlator,
|
|
||||||
COL_BOOKMARK to obj.bookmark,
|
|
||||||
COL_DATE_FETCH to obj.date_fetch,
|
|
||||||
COL_DATE_UPLOAD to obj.date_upload,
|
|
||||||
COL_LAST_PAGE_READ to obj.last_page_read,
|
|
||||||
COL_CHAPTER_NUMBER to obj.chapter_number,
|
|
||||||
COL_SOURCE_ORDER to obj.source_order,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChapterGetResolver : DefaultGetResolver<Chapter>() {
|
|
||||||
|
|
||||||
override fun mapFromCursor(cursor: Cursor): Chapter = ChapterImpl().apply {
|
|
||||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
|
||||||
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
|
|
||||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
|
||||||
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME))
|
|
||||||
scanlator = cursor.getString(cursor.getColumnIndexOrThrow(COL_SCANLATOR))
|
|
||||||
read = cursor.getInt(cursor.getColumnIndexOrThrow(COL_READ)) == 1
|
|
||||||
bookmark = cursor.getInt(cursor.getColumnIndexOrThrow(COL_BOOKMARK)) == 1
|
|
||||||
date_fetch = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_FETCH))
|
|
||||||
date_upload = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_UPLOAD))
|
|
||||||
last_page_read = cursor.getInt(cursor.getColumnIndexOrThrow(COL_LAST_PAGE_READ))
|
|
||||||
chapter_number = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_CHAPTER_NUMBER))
|
|
||||||
source_order = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SOURCE_ORDER))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChapterDeleteResolver : DefaultDeleteResolver<Chapter>() {
|
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Chapter) = DeleteQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.mappers
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_CATEGORY_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_MANGA_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.TABLE
|
|
||||||
|
|
||||||
class MangaCategoryTypeMapping : SQLiteTypeMapping<MangaCategory>(
|
|
||||||
MangaCategoryPutResolver(),
|
|
||||||
MangaCategoryGetResolver(),
|
|
||||||
MangaCategoryDeleteResolver(),
|
|
||||||
)
|
|
||||||
|
|
||||||
class MangaCategoryPutResolver : DefaultPutResolver<MangaCategory>() {
|
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: MangaCategory) = InsertQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: MangaCategory) = UpdateQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToContentValues(obj: MangaCategory) =
|
|
||||||
contentValuesOf(
|
|
||||||
COL_ID to obj.id,
|
|
||||||
COL_MANGA_ID to obj.manga_id,
|
|
||||||
COL_CATEGORY_ID to obj.category_id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MangaCategoryGetResolver : DefaultGetResolver<MangaCategory>() {
|
|
||||||
|
|
||||||
override fun mapFromCursor(cursor: Cursor): MangaCategory = MangaCategory().apply {
|
|
||||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
|
||||||
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
|
|
||||||
category_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CATEGORY_ID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MangaCategoryDeleteResolver : DefaultDeleteResolver<MangaCategory>() {
|
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: MangaCategory) = DeleteQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.mappers
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ARTIST
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_AUTHOR
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_CHAPTER_FLAGS
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_COVER_LAST_MODIFIED
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DATE_ADDED
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DESCRIPTION
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FAVORITE
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ID
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_INITIALIZED
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_LAST_UPDATE
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_SOURCE
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_STATUS
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_THUMBNAIL_URL
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_TITLE
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_URL
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_VIEWER
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.TABLE
|
|
||||||
|
|
||||||
class MangaTypeMapping : SQLiteTypeMapping<Manga>(
|
|
||||||
MangaPutResolver(),
|
|
||||||
MangaGetResolver(),
|
|
||||||
MangaDeleteResolver(),
|
|
||||||
)
|
|
||||||
|
|
||||||
class MangaPutResolver : DefaultPutResolver<Manga>() {
|
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Manga) = InsertQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Manga) = UpdateQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Manga) =
|
|
||||||
contentValuesOf(
|
|
||||||
COL_ID to obj.id,
|
|
||||||
COL_SOURCE to obj.source,
|
|
||||||
COL_URL to obj.url,
|
|
||||||
COL_ARTIST to obj.artist,
|
|
||||||
COL_AUTHOR to obj.author,
|
|
||||||
COL_DESCRIPTION to obj.description,
|
|
||||||
COL_GENRE to obj.genre,
|
|
||||||
COL_TITLE to obj.title,
|
|
||||||
COL_STATUS to obj.status,
|
|
||||||
COL_THUMBNAIL_URL to obj.thumbnail_url,
|
|
||||||
COL_FAVORITE to obj.favorite,
|
|
||||||
COL_LAST_UPDATE to obj.last_update,
|
|
||||||
COL_INITIALIZED to obj.initialized,
|
|
||||||
COL_VIEWER to obj.viewer_flags,
|
|
||||||
COL_CHAPTER_FLAGS to obj.chapter_flags,
|
|
||||||
COL_COVER_LAST_MODIFIED to obj.cover_last_modified,
|
|
||||||
COL_DATE_ADDED to obj.date_added,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BaseMangaGetResolver {
|
|
||||||
fun mapBaseFromCursor(manga: Manga, cursor: Cursor) = manga.apply {
|
|
||||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
|
|
||||||
source = cursor.getLong(cursor.getColumnIndexOrThrow(COL_SOURCE))
|
|
||||||
url = cursor.getString(cursor.getColumnIndexOrThrow(COL_URL))
|
|
||||||
artist = cursor.getString(cursor.getColumnIndexOrThrow(COL_ARTIST))
|
|
||||||
author = cursor.getString(cursor.getColumnIndexOrThrow(COL_AUTHOR))
|
|
||||||
description = cursor.getString(cursor.getColumnIndexOrThrow(COL_DESCRIPTION))
|
|
||||||
genre = cursor.getString(cursor.getColumnIndexOrThrow(COL_GENRE))
|
|
||||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
|
|
||||||
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
|
|
||||||
thumbnail_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_THUMBNAIL_URL))
|
|
||||||
favorite = cursor.getInt(cursor.getColumnIndexOrThrow(COL_FAVORITE)) == 1
|
|
||||||
last_update = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LAST_UPDATE))
|
|
||||||
initialized = cursor.getInt(cursor.getColumnIndexOrThrow(COL_INITIALIZED)) == 1
|
|
||||||
viewer_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_VIEWER))
|
|
||||||
chapter_flags = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CHAPTER_FLAGS))
|
|
||||||
cover_last_modified = cursor.getLong(cursor.getColumnIndexOrThrow(COL_COVER_LAST_MODIFIED))
|
|
||||||
date_added = cursor.getLong(cursor.getColumnIndexOrThrow(COL_DATE_ADDED))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class MangaGetResolver : DefaultGetResolver<Manga>(), BaseMangaGetResolver {
|
|
||||||
|
|
||||||
override fun mapFromCursor(cursor: Cursor): Manga {
|
|
||||||
return mapBaseFromCursor(MangaImpl(), cursor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MangaDeleteResolver : DefaultDeleteResolver<Manga>() {
|
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Manga) = DeleteQuery.builder()
|
|
||||||
.table(TABLE)
|
|
||||||
.where("$COL_ID = ?")
|
|
||||||
.whereArgs(obj.id)
|
|
||||||
.build()
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.queries
|
|
||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
|
|
||||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
|
||||||
|
|
||||||
interface MangaQueries : DbProvider {
|
|
||||||
|
|
||||||
fun getLibraryMangas() = db.get()
|
|
||||||
.listOfObjects(LibraryManga::class.java)
|
|
||||||
.withQuery(
|
|
||||||
RawQuery.builder()
|
|
||||||
.query(libraryQuery)
|
|
||||||
.observesTables(MangaTable.TABLE, ChapterTable.TABLE, MangaCategoryTable.TABLE, CategoryTable.TABLE)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.withGetResolver(LibraryMangaGetResolver.INSTANCE)
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun getFavoriteMangas() = db.get()
|
|
||||||
.listOfObjects(Manga::class.java)
|
|
||||||
.withQuery(
|
|
||||||
Query.builder()
|
|
||||||
.table(MangaTable.TABLE)
|
|
||||||
.where("${MangaTable.COL_FAVORITE} = ?")
|
|
||||||
.whereArgs(1)
|
|
||||||
.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(
|
|
||||||
Query.builder()
|
|
||||||
.table(MangaTable.TABLE)
|
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
|
||||||
.whereArgs(id)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
|
||||||
|
|
||||||
fun updateChapterFlags(manga: Manga) = db.put()
|
|
||||||
.`object`(manga)
|
|
||||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags))
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun updateChapterFlags(manga: List<Manga>) = db.put()
|
|
||||||
.objects(manga)
|
|
||||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_CHAPTER_FLAGS, Manga::chapter_flags))
|
|
||||||
.prepare()
|
|
||||||
|
|
||||||
fun updateViewerFlags(manga: Manga) = db.put()
|
|
||||||
.`object`(manga)
|
|
||||||
.withPutResolver(MangaFlagsPutResolver(MangaTable.COL_VIEWER, Manga::viewer_flags))
|
|
||||||
.prepare()
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.queries
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query to get the manga from the library, with their categories, read and unread count.
|
|
||||||
*/
|
|
||||||
val libraryQuery =
|
|
||||||
"""
|
|
||||||
SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY}
|
|
||||||
FROM (
|
|
||||||
SELECT ${Manga.TABLE}.*, COALESCE(C.unreadCount, 0) AS ${Manga.COMPUTED_COL_UNREAD_COUNT}, COALESCE(R.readCount, 0) AS ${Manga.COMPUTED_COL_READ_COUNT}
|
|
||||||
FROM ${Manga.TABLE}
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS unreadCount
|
|
||||||
FROM ${Chapter.TABLE}
|
|
||||||
WHERE ${Chapter.COL_READ} = 0
|
|
||||||
GROUP BY ${Chapter.COL_MANGA_ID}
|
|
||||||
) AS C
|
|
||||||
ON ${Manga.COL_ID} = C.${Chapter.COL_MANGA_ID}
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS readCount
|
|
||||||
FROM ${Chapter.TABLE}
|
|
||||||
WHERE ${Chapter.COL_READ} = 1
|
|
||||||
GROUP BY ${Chapter.COL_MANGA_ID}
|
|
||||||
) AS R
|
|
||||||
ON ${Manga.COL_ID} = R.${Chapter.COL_MANGA_ID}
|
|
||||||
WHERE ${Manga.COL_FAVORITE} = 1
|
|
||||||
GROUP BY ${Manga.COL_ID}
|
|
||||||
ORDER BY ${Manga.COL_TITLE}
|
|
||||||
) AS M
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT * FROM ${MangaCategory.TABLE}) AS MC
|
|
||||||
ON MC.${MangaCategory.COL_MANGA_ID} = M.${Manga.COL_ID}
|
|
||||||
"""
|
|
|
@ -1,34 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.resolvers
|
|
||||||
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
|
||||||
|
|
||||||
class ChapterProgressPutResolver : PutResolver<Chapter>() {
|
|
||||||
|
|
||||||
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
|
|
||||||
val updateQuery = mapToUpdateQuery(chapter)
|
|
||||||
val contentValues = mapToContentValues(chapter)
|
|
||||||
|
|
||||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
|
||||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
|
||||||
.table(ChapterTable.TABLE)
|
|
||||||
.where("${ChapterTable.COL_ID} = ?")
|
|
||||||
.whereArgs(chapter.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun mapToContentValues(chapter: Chapter) =
|
|
||||||
contentValuesOf(
|
|
||||||
ChapterTable.COL_READ to chapter.read,
|
|
||||||
ChapterTable.COL_BOOKMARK to chapter.bookmark,
|
|
||||||
ChapterTable.COL_LAST_PAGE_READ to chapter.last_page_read,
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.resolvers
|
|
||||||
|
|
||||||
import android.database.Cursor
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
|
||||||
import eu.kanade.tachiyomi.data.database.mappers.BaseMangaGetResolver
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
|
||||||
|
|
||||||
class LibraryMangaGetResolver : DefaultGetResolver<LibraryManga>(), BaseMangaGetResolver {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val INSTANCE = LibraryMangaGetResolver()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mapFromCursor(cursor: Cursor): LibraryManga {
|
|
||||||
val manga = LibraryManga()
|
|
||||||
|
|
||||||
mapBaseFromCursor(manga, cursor)
|
|
||||||
manga.unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_UNREAD_COUNT))
|
|
||||||
manga.category = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_CATEGORY))
|
|
||||||
manga.readCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_READ_COUNT))
|
|
||||||
|
|
||||||
return manga
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.resolvers
|
|
||||||
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
|
||||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
|
||||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
|
||||||
import kotlin.reflect.KProperty1
|
|
||||||
|
|
||||||
class MangaFlagsPutResolver(private val colName: String, private val fieldGetter: KProperty1<Manga, Int>) : PutResolver<Manga>() {
|
|
||||||
|
|
||||||
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
|
|
||||||
val updateQuery = mapToUpdateQuery(manga)
|
|
||||||
val contentValues = mapToContentValues(manga)
|
|
||||||
|
|
||||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
|
||||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
|
||||||
.table(MangaTable.TABLE)
|
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
|
||||||
.whereArgs(manga.id)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) =
|
|
||||||
contentValuesOf(
|
|
||||||
colName to fieldGetter.get(manga),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.tables
|
|
||||||
|
|
||||||
object CategoryTable {
|
|
||||||
|
|
||||||
const val TABLE = "categories"
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.tables
|
|
||||||
|
|
||||||
object ChapterTable {
|
|
||||||
|
|
||||||
const val TABLE = "chapters"
|
|
||||||
|
|
||||||
const val COL_ID = "_id"
|
|
||||||
|
|
||||||
const val COL_MANGA_ID = "manga_id"
|
|
||||||
|
|
||||||
const val COL_URL = "url"
|
|
||||||
|
|
||||||
const val COL_NAME = "name"
|
|
||||||
|
|
||||||
const val COL_READ = "read"
|
|
||||||
|
|
||||||
const val COL_SCANLATOR = "scanlator"
|
|
||||||
|
|
||||||
const val COL_BOOKMARK = "bookmark"
|
|
||||||
|
|
||||||
const val COL_DATE_FETCH = "date_fetch"
|
|
||||||
|
|
||||||
const val COL_DATE_UPLOAD = "date_upload"
|
|
||||||
|
|
||||||
const val COL_LAST_PAGE_READ = "last_page_read"
|
|
||||||
|
|
||||||
const val COL_CHAPTER_NUMBER = "chapter_number"
|
|
||||||
|
|
||||||
const val COL_SOURCE_ORDER = "source_order"
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.tables
|
|
||||||
|
|
||||||
object MangaCategoryTable {
|
|
||||||
|
|
||||||
const val TABLE = "mangas_categories"
|
|
||||||
|
|
||||||
const val COL_ID = "_id"
|
|
||||||
|
|
||||||
const val COL_MANGA_ID = "manga_id"
|
|
||||||
|
|
||||||
const val COL_CATEGORY_ID = "category_id"
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.database.tables
|
|
||||||
|
|
||||||
object MangaTable {
|
|
||||||
|
|
||||||
const val TABLE = "mangas"
|
|
||||||
|
|
||||||
const val COL_ID = "_id"
|
|
||||||
|
|
||||||
const val COL_SOURCE = "source"
|
|
||||||
|
|
||||||
const val COL_URL = "url"
|
|
||||||
|
|
||||||
const val COL_ARTIST = "artist"
|
|
||||||
|
|
||||||
const val COL_AUTHOR = "author"
|
|
||||||
|
|
||||||
const val COL_DESCRIPTION = "description"
|
|
||||||
|
|
||||||
const val COL_GENRE = "genre"
|
|
||||||
|
|
||||||
const val COL_TITLE = "title"
|
|
||||||
|
|
||||||
const val COL_STATUS = "status"
|
|
||||||
|
|
||||||
const val COL_THUMBNAIL_URL = "thumbnail_url"
|
|
||||||
|
|
||||||
const val COL_FAVORITE = "favorite"
|
|
||||||
|
|
||||||
const val COL_LAST_UPDATE = "last_update"
|
|
||||||
|
|
||||||
// Not actually used anymore
|
|
||||||
const val COL_NEXT_UPDATE = "next_update"
|
|
||||||
|
|
||||||
const val COL_DATE_ADDED = "date_added"
|
|
||||||
|
|
||||||
const val COL_INITIALIZED = "initialized"
|
|
||||||
|
|
||||||
const val COL_VIEWER = "viewer"
|
|
||||||
|
|
||||||
const val COL_CHAPTER_FLAGS = "chapter_flags"
|
|
||||||
|
|
||||||
const val COL_CATEGORY = "category"
|
|
||||||
|
|
||||||
const val COL_COVER_LAST_MODIFIED = "cover_last_modified"
|
|
||||||
|
|
||||||
// Not an actual value but computed when created
|
|
||||||
const val COMPUTED_COL_UNREAD_COUNT = "unread_count"
|
|
||||||
|
|
||||||
const val COMPUTED_COL_READ_COUNT = "read_count"
|
|
||||||
}
|
|
|
@ -5,7 +5,6 @@ import com.hippo.unifile.UniFile
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
import eu.kanade.domain.category.interactor.GetCategories
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
@ -32,7 +31,6 @@ import uy.kohesive.injekt.injectLazy
|
||||||
*/
|
*/
|
||||||
class DownloadManager(
|
class DownloadManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapter
|
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
@ -34,7 +34,7 @@ class DownloadStore(
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
private val getMangaById: GetMangaById by injectLazy()
|
private val getManga: GetManga by injectLazy()
|
||||||
private val getChapter: GetChapter by injectLazy()
|
private val getChapter: GetChapter by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +96,7 @@ class DownloadStore(
|
||||||
val cachedManga = mutableMapOf<Long, Manga?>()
|
val cachedManga = mutableMapOf<Long, Manga?>()
|
||||||
for ((mangaId, chapterId) in objs) {
|
for ((mangaId, chapterId) in objs) {
|
||||||
val manga = cachedManga.getOrPut(mangaId) {
|
val manga = cachedManga.getOrPut(mangaId) {
|
||||||
runBlocking { getMangaById.await(mangaId)?.toDbManga() }
|
runBlocking { getManga.await(mangaId)?.toDbManga() }
|
||||||
} ?: continue
|
} ?: continue
|
||||||
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
|
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
|
||||||
val chapter = runBlocking { getChapter.await(chapterId) }?.toDbChapter() ?: continue
|
val chapter = runBlocking { getChapter.await(chapterId) }?.toDbChapter() ?: continue
|
||||||
|
|
|
@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.download.model
|
||||||
|
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapter
|
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
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
|
||||||
|
@ -69,11 +69,11 @@ data class Download(
|
||||||
suspend fun fromChapterId(
|
suspend fun fromChapterId(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
getChapter: GetChapter = Injekt.get(),
|
getChapter: GetChapter = Injekt.get(),
|
||||||
getMangaById: GetMangaById = Injekt.get(),
|
getManga: GetManga = Injekt.get(),
|
||||||
sourceManager: SourceManager = Injekt.get(),
|
sourceManager: SourceManager = Injekt.get(),
|
||||||
): Download? {
|
): Download? {
|
||||||
val chapter = getChapter.await(chapterId) ?: return null
|
val chapter = getChapter.await(chapterId) ?: return null
|
||||||
val manga = getMangaById.await(chapter.mangaId) ?: return null
|
val manga = getManga.await(chapter.mangaId) ?: return null
|
||||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return null
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return null
|
||||||
|
|
||||||
return Download(source, manga.toDbManga(), chapter.toDbChapter())
|
return Download(source, manga.toDbManga(), chapter.toDbChapter())
|
||||||
|
|
|
@ -13,16 +13,17 @@ import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toMangaInfo
|
import eu.kanade.domain.manga.model.toMangaInfo
|
||||||
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
import eu.kanade.domain.track.model.toDomainTrack
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
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.LibraryManga
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
@ -61,6 +62,7 @@ import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
|
@ -84,13 +86,13 @@ import eu.kanade.domain.manga.model.Manga as DomainManga
|
||||||
* destroyed.
|
* destroyed.
|
||||||
*/
|
*/
|
||||||
class LibraryUpdateService(
|
class LibraryUpdateService(
|
||||||
val db: DatabaseHelper = Injekt.get(),
|
|
||||||
val sourceManager: SourceManager = Injekt.get(),
|
val sourceManager: SourceManager = Injekt.get(),
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
val downloadManager: DownloadManager = Injekt.get(),
|
val downloadManager: DownloadManager = Injekt.get(),
|
||||||
val trackManager: TrackManager = Injekt.get(),
|
val trackManager: TrackManager = Injekt.get(),
|
||||||
val coverCache: CoverCache = Injekt.get(),
|
val coverCache: CoverCache = Injekt.get(),
|
||||||
private val getMangaById: GetMangaById = Injekt.get(),
|
private val getLibraryManga: GetLibraryManga = Injekt.get(),
|
||||||
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
|
@ -255,7 +257,7 @@ class LibraryUpdateService(
|
||||||
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
* @param categoryId the ID of the category to update, or -1 if no category specified.
|
||||||
*/
|
*/
|
||||||
fun addMangaToQueue(categoryId: Long) {
|
fun addMangaToQueue(categoryId: Long) {
|
||||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
val libraryManga = runBlocking { getLibraryManga.await() }
|
||||||
|
|
||||||
val listToUpdate = if (categoryId != -1L) {
|
val listToUpdate = if (categoryId != -1L) {
|
||||||
libraryManga.filter { it.category.toLong() == categoryId }
|
libraryManga.filter { it.category.toLong() == categoryId }
|
||||||
|
@ -323,7 +325,7 @@ class LibraryUpdateService(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't continue to update if manga not in library
|
// Don't continue to update if manga not in library
|
||||||
manga.id?.let { getMangaById.await(it) } ?: return@forEach
|
manga.id?.let { getManga.await(it) } ?: return@forEach
|
||||||
|
|
||||||
withUpdateNotification(
|
withUpdateNotification(
|
||||||
currentlyUpdatingManga,
|
currentlyUpdatingManga,
|
||||||
|
@ -434,7 +436,7 @@ class LibraryUpdateService(
|
||||||
.map { it.toSChapter() }
|
.map { it.toSChapter() }
|
||||||
|
|
||||||
// Get manga from database to account for if it was removed during the update
|
// Get manga from database to account for if it was removed during the update
|
||||||
val dbManga = getMangaById.await(manga.id)
|
val dbManga = getManga.await(manga.id)
|
||||||
?: return Pair(emptyList(), emptyList())
|
?: return Pair(emptyList(), emptyList())
|
||||||
|
|
||||||
// [dbmanga] was used so that manga data doesn't get overwritten
|
// [dbmanga] was used so that manga data doesn't get overwritten
|
||||||
|
@ -471,7 +473,14 @@ class LibraryUpdateService(
|
||||||
mangaWithNotif.prepUpdateCover(coverCache, sManga, true)
|
mangaWithNotif.prepUpdateCover(coverCache, sManga, true)
|
||||||
sManga.thumbnail_url?.let {
|
sManga.thumbnail_url?.let {
|
||||||
mangaWithNotif.thumbnail_url = it
|
mangaWithNotif.thumbnail_url = it
|
||||||
db.insertManga(mangaWithNotif).executeAsBlocking()
|
try {
|
||||||
|
updateManga.await(
|
||||||
|
mangaWithNotif.toDomainManga()!!
|
||||||
|
.toMangaUpdate(),
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR) { "Manga don't exist anymore" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
// Ignore errors and continue
|
// Ignore errors and continue
|
||||||
|
|
|
@ -11,7 +11,7 @@ import eu.kanade.domain.chapter.interactor.GetChapter
|
||||||
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
||||||
import eu.kanade.domain.chapter.model.toChapterUpdate
|
import eu.kanade.domain.chapter.model.toChapterUpdate
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||||
|
@ -46,7 +46,7 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
||||||
*/
|
*/
|
||||||
class NotificationReceiver : BroadcastReceiver() {
|
class NotificationReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
private val getMangaById: GetMangaById by injectLazy()
|
private val getManga: GetManga by injectLazy()
|
||||||
private val getChapter: GetChapter by injectLazy()
|
private val getChapter: GetChapter by injectLazy()
|
||||||
private val updateChapter: UpdateChapter by injectLazy()
|
private val updateChapter: UpdateChapter by injectLazy()
|
||||||
private val downloadManager: DownloadManager by injectLazy()
|
private val downloadManager: DownloadManager by injectLazy()
|
||||||
|
@ -178,7 +178,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
* @param chapterId id of chapter
|
* @param chapterId id of chapter
|
||||||
*/
|
*/
|
||||||
private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
|
private fun openChapter(context: Context, mangaId: Long, chapterId: Long) {
|
||||||
val manga = runBlocking { getMangaById.await(mangaId) }
|
val manga = runBlocking { getManga.await(mangaId) }
|
||||||
val chapter = runBlocking { getChapter.await(chapterId) }
|
val chapter = runBlocking { getChapter.await(chapterId) }
|
||||||
if (manga != null && chapter != null) {
|
if (manga != null && chapter != null) {
|
||||||
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
|
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
|
||||||
|
@ -248,7 +248,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
.map {
|
.map {
|
||||||
val chapter = it.copy(read = true)
|
val chapter = it.copy(read = true)
|
||||||
if (preferences.removeAfterMarkedAsRead()) {
|
if (preferences.removeAfterMarkedAsRead()) {
|
||||||
val manga = getMangaById.await(mangaId)
|
val manga = getManga.await(mangaId)
|
||||||
if (manga != null) {
|
if (manga != null) {
|
||||||
val source = sourceManager.get(manga.source)
|
val source = sourceManager.get(manga.source)
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
|
@ -270,7 +270,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
*/
|
*/
|
||||||
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
||||||
launchIO {
|
launchIO {
|
||||||
val manga = getMangaById.await(mangaId)?.toDbManga()
|
val manga = getManga.await(mangaId)?.toDbManga()
|
||||||
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId)?.toDbChapter() }
|
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId)?.toDbChapter() }
|
||||||
if (manga != null && chapters.isNotEmpty()) {
|
if (manga != null && chapters.isNotEmpty()) {
|
||||||
downloadManager.downloadChapters(manga, chapters)
|
downloadManager.downloadChapters(manga, chapters)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import androidx.work.NetworkType
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
@ -26,7 +26,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
|
||||||
CoroutineWorker(context, workerParams) {
|
CoroutineWorker(context, workerParams) {
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val getMangaById = Injekt.get<GetMangaById>()
|
val getManga = Injekt.get<GetManga>()
|
||||||
val getTracks = Injekt.get<GetTracks>()
|
val getTracks = Injekt.get<GetTracks>()
|
||||||
val insertTrack = Injekt.get<InsertTrack>()
|
val insertTrack = Injekt.get<InsertTrack>()
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val tracks = delayedTrackingStore.getItems().mapNotNull {
|
val tracks = delayedTrackingStore.getItems().mapNotNull {
|
||||||
val manga = getMangaById.await(it.mangaId) ?: return@withContext
|
val manga = getManga.await(it.mangaId) ?: return@withContext
|
||||||
getTracks.await(manga.id)
|
getTracks.await(manga.id)
|
||||||
.find { track -> track.id == it.trackId }
|
.find { track -> track.id == it.trackId }
|
||||||
?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||||
|
|
|
@ -6,8 +6,9 @@ import androidx.core.view.isVisible
|
||||||
import com.bluelinelabs.conductor.Controller
|
import com.bluelinelabs.conductor.Controller
|
||||||
import com.bluelinelabs.conductor.RouterTransaction
|
import com.bluelinelabs.conductor.RouterTransaction
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
@ -18,6 +19,7 @@ import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
|
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
|
||||||
|
@ -27,9 +29,11 @@ class SearchController(
|
||||||
) : GlobalSearchController(manga?.title) {
|
) : GlobalSearchController(manga?.title) {
|
||||||
|
|
||||||
constructor(mangaId: Long) : this(
|
constructor(mangaId: Long) : this(
|
||||||
Injekt.get<DatabaseHelper>()
|
runBlocking {
|
||||||
.getManga(mangaId)
|
Injekt.get<GetManga>()
|
||||||
.executeAsBlocking(),
|
.await(mangaId)
|
||||||
|
?.toDbManga()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private var newManga: Manga? = null
|
private var newManga: Manga? = null
|
||||||
|
|
|
@ -7,11 +7,14 @@ import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
||||||
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDomainTrack
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
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.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
|
@ -67,13 +70,15 @@ open class BrowseSourcePresenter(
|
||||||
private val sourceId: Long,
|
private val sourceId: Long,
|
||||||
searchQuery: String? = null,
|
searchQuery: String? = null,
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
private val prefs: PreferencesHelper = Injekt.get(),
|
private val prefs: PreferencesHelper = Injekt.get(),
|
||||||
private val coverCache: CoverCache = Injekt.get(),
|
private val coverCache: CoverCache = Injekt.get(),
|
||||||
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
private val setMangaCategories: SetMangaCategories = Injekt.get(),
|
||||||
|
private val insertManga: InsertManga = Injekt.get(),
|
||||||
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
private val insertTrack: InsertTrack = Injekt.get(),
|
||||||
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
||||||
) : BasePresenter<BrowseSourceController>() {
|
) : BasePresenter<BrowseSourceController>() {
|
||||||
|
@ -208,19 +213,22 @@ open class BrowseSourcePresenter(
|
||||||
* @return a manga from the database.
|
* @return a manga from the database.
|
||||||
*/
|
*/
|
||||||
private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
||||||
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
|
var localManga = runBlocking { getManga.await(sManga.url, sourceId) }
|
||||||
if (localManga == null) {
|
if (localManga == null) {
|
||||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||||
newManga.copyFrom(sManga)
|
newManga.copyFrom(sManga)
|
||||||
val result = db.insertManga(newManga).executeAsBlocking()
|
newManga.id = -1
|
||||||
newManga.id = result.insertedId()
|
val result = runBlocking {
|
||||||
localManga = newManga
|
val id = insertManga.await(newManga.toDomainManga()!!)
|
||||||
|
getManga.await(id!!)
|
||||||
|
}
|
||||||
|
localManga = result
|
||||||
} else if (!localManga.favorite) {
|
} else if (!localManga.favorite) {
|
||||||
// if the manga isn't a favorite, set its display title from source
|
// 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
|
// if it later becomes a favorite, updated title will go to db
|
||||||
localManga.title = sManga.title
|
localManga = localManga.copy(title = sManga.title)
|
||||||
}
|
}
|
||||||
return localManga
|
return localManga?.toDbManga()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -255,7 +263,11 @@ open class BrowseSourcePresenter(
|
||||||
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
manga.copyFrom(networkManga.toSManga())
|
manga.copyFrom(networkManga.toSManga())
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
db.insertManga(manga).executeAsBlocking()
|
updateManga.await(
|
||||||
|
manga
|
||||||
|
.toDomainManga()
|
||||||
|
?.toMangaUpdate()!!,
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
}
|
}
|
||||||
|
@ -282,7 +294,13 @@ open class BrowseSourcePresenter(
|
||||||
autoAddTrack(manga)
|
autoAddTrack(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.insertManga(manga).executeAsBlocking()
|
runBlocking {
|
||||||
|
updateManga.await(
|
||||||
|
manga
|
||||||
|
.toDomainManga()
|
||||||
|
?.toMangaUpdate()!!,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun autoAddTrack(manga: Manga) {
|
private fun autoAddTrack(manga: Manga) {
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
|
@ -16,6 +21,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
|
||||||
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
|
@ -38,8 +44,10 @@ open class GlobalSearchPresenter(
|
||||||
private val initialQuery: String? = "",
|
private val initialQuery: String? = "",
|
||||||
private val initialExtensionFilter: String? = null,
|
private val initialExtensionFilter: String? = null,
|
||||||
val sourceManager: SourceManager = Injekt.get(),
|
val sourceManager: SourceManager = Injekt.get(),
|
||||||
val db: DatabaseHelper = Injekt.get(),
|
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
|
private val getManga: GetManga = Injekt.get(),
|
||||||
|
private val insertManga: InsertManga = Injekt.get(),
|
||||||
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
) : BasePresenter<GlobalSearchController>() {
|
) : BasePresenter<GlobalSearchController>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,7 +256,7 @@ open class GlobalSearchPresenter(
|
||||||
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
||||||
manga.copyFrom(networkManga.toSManga())
|
manga.copyFrom(networkManga.toSManga())
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
db.insertManga(manga).executeAsBlocking()
|
runBlocking { updateManga.await(manga.toDomainManga()!!.toMangaUpdate()) }
|
||||||
return manga
|
return manga
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,18 +268,21 @@ open class GlobalSearchPresenter(
|
||||||
* @return a manga from the database.
|
* @return a manga from the database.
|
||||||
*/
|
*/
|
||||||
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
protected open fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
||||||
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
|
var localManga = runBlocking { getManga.await(sManga.url, sourceId) }
|
||||||
if (localManga == null) {
|
if (localManga == null) {
|
||||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||||
newManga.copyFrom(sManga)
|
newManga.copyFrom(sManga)
|
||||||
val result = db.insertManga(newManga).executeAsBlocking()
|
newManga.id = -1
|
||||||
newManga.id = result.insertedId()
|
val result = runBlocking {
|
||||||
localManga = newManga
|
val id = insertManga.await(newManga.toDomainManga()!!)
|
||||||
|
getManga.await(id!!)
|
||||||
|
}
|
||||||
|
localManga = result
|
||||||
} else if (!localManga.favorite) {
|
} else if (!localManga.favorite) {
|
||||||
// if the manga isn't a favorite, set its display title from source
|
// 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
|
// if it later becomes a favorite, updated title will go to db
|
||||||
localManga.title = sManga.title
|
localManga = localManga.copy(title = sManga.title)
|
||||||
}
|
}
|
||||||
return localManga
|
return localManga!!.toDbManga()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
||||||
import eu.kanade.domain.chapter.model.ChapterUpdate
|
import eu.kanade.domain.chapter.model.ChapterUpdate
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
|
@ -60,6 +60,7 @@ typealias LibraryMap = Map<Long, List<LibraryItem>>
|
||||||
*/
|
*/
|
||||||
class LibraryPresenter(
|
class LibraryPresenter(
|
||||||
private val handler: DatabaseHandler = Injekt.get(),
|
private val handler: DatabaseHandler = Injekt.get(),
|
||||||
|
private val getLibraryManga: GetLibraryManga = Injekt.get(),
|
||||||
private val getTracks: GetTracks = Injekt.get(),
|
private val getTracks: GetTracks = Injekt.get(),
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
|
@ -410,35 +411,7 @@ class LibraryPresenter(
|
||||||
val defaultLibraryDisplayMode = preferences.libraryDisplayMode()
|
val defaultLibraryDisplayMode = preferences.libraryDisplayMode()
|
||||||
val shouldSetFromCategory = preferences.categorizedDisplaySettings()
|
val shouldSetFromCategory = preferences.categorizedDisplaySettings()
|
||||||
|
|
||||||
// TODO: Move this to domain/data layer
|
return getLibraryManga.subscribe().asObservable()
|
||||||
return handler
|
|
||||||
.subscribeToList {
|
|
||||||
mangasQueries.getLibrary { _id: Long, source: Long, url: String, artist: String?, author: String?, description: String?, genre: List<String>?, title: String, status: Long, thumbnail_url: String?, favorite: Boolean, last_update: Long?, next_update: Long?, initialized: Boolean, viewer: Long, chapter_flags: Long, cover_last_modified: Long, date_added: Long, unread_count: Long, read_count: Long, category: Long ->
|
|
||||||
LibraryManga().apply {
|
|
||||||
this.id = _id
|
|
||||||
this.source = source
|
|
||||||
this.url = url
|
|
||||||
this.artist = artist
|
|
||||||
this.author = author
|
|
||||||
this.description = description
|
|
||||||
this.genre = genre?.joinToString()
|
|
||||||
this.title = title
|
|
||||||
this.status = status.toInt()
|
|
||||||
this.thumbnail_url = thumbnail_url
|
|
||||||
this.favorite = favorite
|
|
||||||
this.last_update = last_update ?: 0
|
|
||||||
this.initialized = initialized
|
|
||||||
this.viewer_flags = viewer.toInt()
|
|
||||||
this.chapter_flags = chapter_flags.toInt()
|
|
||||||
this.cover_last_modified = cover_last_modified
|
|
||||||
this.date_added = date_added
|
|
||||||
this.unreadCount = unread_count.toInt()
|
|
||||||
this.readCount = read_count.toInt()
|
|
||||||
this.category = category.toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.asObservable()
|
|
||||||
.map { list ->
|
.map { list ->
|
||||||
list.map { libraryManga ->
|
list.map { libraryManga ->
|
||||||
// Display mode based on user preference: take it from global library setting or category
|
// Display mode based on user preference: take it from global library setting or category
|
||||||
|
|
|
@ -149,7 +149,7 @@ class MangaPresenter(
|
||||||
|
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
if (!getMangaAndChapters.awaitManga(mangaId).favorite) {
|
if (!getMangaAndChapters.awaitManga(mangaId).favorite) {
|
||||||
ChapterSettingsHelper.applySettingDefaults(mangaId, setMangaChapterFlags)
|
ChapterSettingsHelper.applySettingDefaults(mangaId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getMangaAndChapters.subscribe(mangaId)
|
getMangaAndChapters.subscribe(mangaId)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import androidx.core.os.bundleOf
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.size.Size
|
import coil.size.Size
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.domain.manga.model.hasCustomCover
|
import eu.kanade.domain.manga.model.hasCustomCover
|
||||||
|
@ -161,7 +161,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
|
||||||
|
|
||||||
inner class MangaFullCoverPresenter(
|
inner class MangaFullCoverPresenter(
|
||||||
private val mangaId: Long,
|
private val mangaId: Long,
|
||||||
private val getMangaById: GetMangaById = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
) : Presenter<MangaFullCoverDialog>() {
|
) : Presenter<MangaFullCoverDialog>() {
|
||||||
|
|
||||||
private var presenterScope: CoroutineScope = MainScope()
|
private var presenterScope: CoroutineScope = MainScope()
|
||||||
|
@ -176,7 +176,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
getMangaById.subscribe(mangaId)
|
getManga.subscribe(mangaId)
|
||||||
.collect { _mangaFlow.value = it }
|
.collect { _mangaFlow.value = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ import eu.kanade.domain.chapter.model.ChapterUpdate
|
||||||
import eu.kanade.domain.chapter.model.toDbChapter
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
import eu.kanade.domain.history.interactor.UpsertHistory
|
import eu.kanade.domain.history.interactor.UpsertHistory
|
||||||
import eu.kanade.domain.history.model.HistoryUpdate
|
import eu.kanade.domain.history.model.HistoryUpdate
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaById
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.model.isLocal
|
import eu.kanade.domain.manga.model.isLocal
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
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.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
|
@ -68,17 +68,17 @@ import java.util.concurrent.TimeUnit
|
||||||
* Presenter used by the activity to perform background operations.
|
* Presenter used by the activity to perform background operations.
|
||||||
*/
|
*/
|
||||||
class ReaderPresenter(
|
class ReaderPresenter(
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val preferences: PreferencesHelper = Injekt.get(),
|
private val preferences: PreferencesHelper = Injekt.get(),
|
||||||
private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(),
|
private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(),
|
||||||
private val getMangaById: GetMangaById = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
private val getTracks: GetTracks = Injekt.get(),
|
private val getTracks: GetTracks = Injekt.get(),
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
private val insertTrack: InsertTrack = Injekt.get(),
|
||||||
private val upsertHistory: UpsertHistory = Injekt.get(),
|
private val upsertHistory: UpsertHistory = Injekt.get(),
|
||||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
private val updateChapter: UpdateChapter = Injekt.get(),
|
||||||
|
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
|
||||||
) : BasePresenter<ReaderActivity>() {
|
) : BasePresenter<ReaderActivity>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,7 +242,7 @@ class ReaderPresenter(
|
||||||
|
|
||||||
launchIO {
|
launchIO {
|
||||||
try {
|
try {
|
||||||
val manga = getMangaById.await(mangaId)
|
val manga = getManga.await(mangaId)
|
||||||
withUIContext {
|
withUIContext {
|
||||||
manga?.let { init(it.toDbManga(), initialChapterId) }
|
manga?.let { init(it.toDbManga(), initialChapterId) }
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,9 @@ class ReaderPresenter(
|
||||||
fun setMangaReadingMode(readingModeType: Int) {
|
fun setMangaReadingMode(readingModeType: Int) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
manga.readingModeType = readingModeType
|
manga.readingModeType = readingModeType
|
||||||
db.updateViewerFlags(manga).executeAsBlocking()
|
runBlocking {
|
||||||
|
setMangaViewerFlags.awaitSetMangaReadingMode(manga.id!!.toLong(), readingModeType.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||||
.subscribeFirst({ view, _ ->
|
.subscribeFirst({ view, _ ->
|
||||||
|
@ -605,7 +607,9 @@ class ReaderPresenter(
|
||||||
fun setMangaOrientationType(rotationType: Int) {
|
fun setMangaOrientationType(rotationType: Int) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
manga.orientationType = rotationType
|
manga.orientationType = rotationType
|
||||||
db.updateViewerFlags(manga).executeAsBlocking()
|
runBlocking {
|
||||||
|
setMangaViewerFlags.awaitSetOrientationType(manga.id!!.toLong(), rotationType.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
logcat(LogPriority.INFO) { "Manga orientation is ${manga.orientationType}" }
|
logcat(LogPriority.INFO) { "Manga orientation is ${manga.orientationType}" }
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.util.chapter
|
package eu.kanade.tachiyomi.util.chapter
|
||||||
|
|
||||||
|
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
||||||
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.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
|
@ -10,7 +10,8 @@ import uy.kohesive.injekt.injectLazy
|
||||||
object ChapterSettingsHelper {
|
object ChapterSettingsHelper {
|
||||||
|
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val prefs: PreferencesHelper by injectLazy()
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val getFavorites: GetFavorites by injectLazy()
|
||||||
|
private val setMangaChapterFlags: SetMangaChapterFlags by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the global Chapter Settings in Preferences.
|
* Updates the global Chapter Settings in Preferences.
|
||||||
|
@ -23,19 +24,20 @@ object ChapterSettingsHelper {
|
||||||
* Updates a single manga's Chapter Settings to match what's set in Preferences.
|
* Updates a single manga's Chapter Settings to match what's set in Preferences.
|
||||||
*/
|
*/
|
||||||
fun applySettingDefaults(manga: Manga) {
|
fun applySettingDefaults(manga: Manga) {
|
||||||
with(manga) {
|
launchIO {
|
||||||
readFilter = prefs.filterChapterByRead()
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
downloadedFilter = prefs.filterChapterByDownloaded()
|
mangaId = manga.id!!,
|
||||||
bookmarkedFilter = prefs.filterChapterByBookmarked()
|
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||||
sorting = prefs.sortChapterBySourceOrNumber()
|
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
||||||
displayMode = prefs.displayChapterByNameOrNumber()
|
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
||||||
setChapterOrder(prefs.sortChapterByAscendingOrDescending())
|
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
||||||
|
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
||||||
|
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.updateChapterFlags(manga).executeAsBlocking()
|
suspend fun applySettingDefaults(mangaId: Long) {
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun applySettingDefaults(mangaId: Long, setMangaChapterFlags: SetMangaChapterFlags) {
|
|
||||||
setMangaChapterFlags.awaitSetAllFlags(
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
mangaId = mangaId,
|
mangaId = mangaId,
|
||||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||||
|
@ -52,21 +54,18 @@ object ChapterSettingsHelper {
|
||||||
*/
|
*/
|
||||||
fun updateAllMangasWithGlobalDefaults() {
|
fun updateAllMangasWithGlobalDefaults() {
|
||||||
launchIO {
|
launchIO {
|
||||||
val updatedMangas = db.getFavoriteMangas()
|
getFavorites.await()
|
||||||
.executeAsBlocking()
|
|
||||||
.map { manga ->
|
.map { manga ->
|
||||||
with(manga) {
|
setMangaChapterFlags.awaitSetAllFlags(
|
||||||
readFilter = prefs.filterChapterByRead()
|
mangaId = manga.id,
|
||||||
downloadedFilter = prefs.filterChapterByDownloaded()
|
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||||
bookmarkedFilter = prefs.filterChapterByBookmarked()
|
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
||||||
sorting = prefs.sortChapterBySourceOrNumber()
|
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
||||||
displayMode = prefs.displayChapterByNameOrNumber()
|
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
||||||
setChapterOrder(prefs.sortChapterByAscendingOrDescending())
|
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
||||||
|
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
manga
|
|
||||||
}
|
|
||||||
|
|
||||||
db.updateChapterFlags(updatedMangas).executeAsBlocking()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,6 +145,44 @@ deleteMangasNotInLibraryBySourceIds:
|
||||||
DELETE FROM mangas
|
DELETE FROM mangas
|
||||||
WHERE favorite = 0 AND source IN :sourceIds;
|
WHERE favorite = 0 AND source IN :sourceIds;
|
||||||
|
|
||||||
|
INSERT INTO mangas(
|
||||||
|
source,
|
||||||
|
url,
|
||||||
|
artist,
|
||||||
|
author,
|
||||||
|
description,
|
||||||
|
genre,
|
||||||
|
title,
|
||||||
|
status,
|
||||||
|
thumbnail_url,
|
||||||
|
favorite,
|
||||||
|
last_update,
|
||||||
|
next_update,
|
||||||
|
initialized,
|
||||||
|
viewer,
|
||||||
|
chapter_flags,
|
||||||
|
cover_last_modified,
|
||||||
|
date_added
|
||||||
|
) VALUES (
|
||||||
|
:source,
|
||||||
|
:url,
|
||||||
|
:artist,
|
||||||
|
:author,
|
||||||
|
:description,
|
||||||
|
:genre,
|
||||||
|
:title,
|
||||||
|
:status,
|
||||||
|
:thumbnailUrl,
|
||||||
|
:favorite,
|
||||||
|
:lastUpdate,
|
||||||
|
0,
|
||||||
|
:initialized,
|
||||||
|
:viewerFlags,
|
||||||
|
:chapterFlags,
|
||||||
|
:coverLastModified,
|
||||||
|
:dateAdded
|
||||||
|
);
|
||||||
|
|
||||||
update:
|
update:
|
||||||
UPDATE mangas SET
|
UPDATE mangas SET
|
||||||
source = coalesce(:source, source),
|
source = coalesce(:source, source),
|
||||||
|
|
Loading…
Reference in a new issue