Use SQLDelight for all Manga related queries (#7447)
This commit is contained in:
parent
6d6237e370
commit
17951cfd68
@ -165,6 +165,7 @@ dependencies {
|
||||
implementation(androidx.paging.runtime)
|
||||
implementation(androidx.paging.compose)
|
||||
|
||||
implementation(libs.bundles.sqlite)
|
||||
implementation(androidx.sqlite)
|
||||
implementation(libs.sqldelight.android.driver)
|
||||
implementation(libs.sqldelight.coroutines)
|
||||
@ -218,11 +219,6 @@ dependencies {
|
||||
implementation(libs.unifile)
|
||||
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
|
||||
implementation(libs.preferencektx)
|
||||
implementation(libs.flowpreferences)
|
||||
|
@ -2,6 +2,7 @@ package eu.kanade.data.manga
|
||||
|
||||
import eu.kanade.domain.chapter.model.Chapter
|
||||
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 =
|
||||
{ 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,
|
||||
)
|
||||
}
|
||||
|
||||
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.MangaUpdate
|
||||
import eu.kanade.domain.manga.repository.MangaRepository
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import logcat.LogPriority
|
||||
@ -18,18 +19,26 @@ class MangaRepositoryImpl(
|
||||
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> {
|
||||
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> {
|
||||
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>> {
|
||||
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 {
|
||||
return try {
|
||||
partialUpdate(update)
|
||||
|
@ -32,10 +32,13 @@ import eu.kanade.domain.history.interactor.UpsertHistory
|
||||
import eu.kanade.domain.history.repository.HistoryRepository
|
||||
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
||||
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.InsertManga
|
||||
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
||||
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.repository.MangaRepository
|
||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||
@ -71,11 +74,14 @@ class DomainModule : InjektModule {
|
||||
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||
addFactory { GetDuplicateLibraryManga(get()) }
|
||||
addFactory { GetFavorites(get()) }
|
||||
addFactory { GetLibraryManga(get()) }
|
||||
addFactory { GetMangaWithChapters(get(), get()) }
|
||||
addFactory { GetMangaById(get()) }
|
||||
addFactory { GetManga(get()) }
|
||||
addFactory { GetNextChapter(get()) }
|
||||
addFactory { ResetViewerFlags(get()) }
|
||||
addFactory { SetMangaChapterFlags(get()) }
|
||||
addFactory { SetMangaViewerFlags(get()) }
|
||||
addFactory { InsertManga(get()) }
|
||||
addFactory { UpdateManga(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 logcat.LogPriority
|
||||
|
||||
class GetMangaById(
|
||||
class GetManga(
|
||||
private val mangaRepository: MangaRepository,
|
||||
) {
|
||||
|
||||
@ -20,6 +20,10 @@ class GetMangaById(
|
||||
}
|
||||
|
||||
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>>> {
|
||||
return combine(
|
||||
mangaRepository.subscribeMangaById(id),
|
||||
mangaRepository.getMangaByIdAsFlow(id),
|
||||
chapterRepository.getChapterByMangaIdAsFlow(id),
|
||||
) { 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,
|
||||
)
|
||||
|
||||
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.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.MangaUpdate
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MangaRepository {
|
||||
|
||||
suspend fun getMangaById(id: Long): Manga
|
||||
|
||||
suspend fun subscribeMangaById(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 getLibraryManga(): List<LibraryManga>
|
||||
|
||||
fun getLibraryMangaAsFlow(): Flow<List<LibraryManga>>
|
||||
|
||||
fun getFavoritesBySourceId(sourceId: Long): Flow<List<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 insert(manga: Manga): Long?
|
||||
|
||||
suspend fun update(update: 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.tachiyomi.data.cache.ChapterCache
|
||||
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.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
@ -76,8 +75,6 @@ class AppModule(val app: Application) : InjektModule {
|
||||
|
||||
addSingletonFactory { PreferencesHelper(app) }
|
||||
|
||||
addSingletonFactory { DatabaseHelper(get()) }
|
||||
|
||||
addSingletonFactory { ChapterCache(app) }
|
||||
|
||||
addSingletonFactory { CoverCache(app) }
|
||||
@ -106,8 +103,6 @@ class AppModule(val app: Application) : InjektModule {
|
||||
|
||||
get<Database>()
|
||||
|
||||
get<DatabaseHelper>()
|
||||
|
||||
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 eu.kanade.domain.category.interactor.GetCategories
|
||||
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.Manga
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
@ -32,7 +31,6 @@ import uy.kohesive.injekt.injectLazy
|
||||
*/
|
||||
class DownloadManager(
|
||||
private val context: Context,
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
) {
|
||||
|
||||
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||
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.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
@ -34,7 +34,7 @@ class DownloadStore(
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val getMangaById: GetMangaById by injectLazy()
|
||||
private val getManga: GetManga by injectLazy()
|
||||
private val getChapter: GetChapter by injectLazy()
|
||||
|
||||
/**
|
||||
@ -96,7 +96,7 @@ class DownloadStore(
|
||||
val cachedManga = mutableMapOf<Long, Manga?>()
|
||||
for ((mangaId, chapterId) in objs) {
|
||||
val manga = cachedManga.getOrPut(mangaId) {
|
||||
runBlocking { getMangaById.await(mangaId)?.toDbManga() }
|
||||
runBlocking { getManga.await(mangaId)?.toDbManga() }
|
||||
} ?: continue
|
||||
val source = sourceManager.get(manga.source) as? HttpSource ?: 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.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.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
@ -69,11 +69,11 @@ data class Download(
|
||||
suspend fun fromChapterId(
|
||||
chapterId: Long,
|
||||
getChapter: GetChapter = Injekt.get(),
|
||||
getMangaById: GetMangaById = Injekt.get(),
|
||||
getManga: GetManga = Injekt.get(),
|
||||
sourceManager: SourceManager = Injekt.get(),
|
||||
): Download? {
|
||||
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
|
||||
|
||||
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.SyncChaptersWithTrackServiceTwoWay
|
||||
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.model.toMangaInfo
|
||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||
import eu.kanade.domain.track.interactor.GetTracks
|
||||
import eu.kanade.domain.track.interactor.InsertTrack
|
||||
import eu.kanade.domain.track.model.toDbTrack
|
||||
import eu.kanade.domain.track.model.toDomainTrack
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
@ -61,6 +62,7 @@ import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
@ -84,13 +86,13 @@ import eu.kanade.domain.manga.model.Manga as DomainManga
|
||||
* destroyed.
|
||||
*/
|
||||
class LibraryUpdateService(
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
val sourceManager: SourceManager = Injekt.get(),
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
val downloadManager: DownloadManager = Injekt.get(),
|
||||
val trackManager: TrackManager = 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 getChapterByMangaId: GetChapterByMangaId = 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.
|
||||
*/
|
||||
fun addMangaToQueue(categoryId: Long) {
|
||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
||||
val libraryManga = runBlocking { getLibraryManga.await() }
|
||||
|
||||
val listToUpdate = if (categoryId != -1L) {
|
||||
libraryManga.filter { it.category.toLong() == categoryId }
|
||||
@ -323,7 +325,7 @@ class LibraryUpdateService(
|
||||
}
|
||||
|
||||
// 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(
|
||||
currentlyUpdatingManga,
|
||||
@ -434,7 +436,7 @@ class LibraryUpdateService(
|
||||
.map { it.toSChapter() }
|
||||
|
||||
// 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())
|
||||
|
||||
// [dbmanga] was used so that manga data doesn't get overwritten
|
||||
@ -471,7 +473,14 @@ class LibraryUpdateService(
|
||||
mangaWithNotif.prepUpdateCover(coverCache, sManga, true)
|
||||
sManga.thumbnail_url?.let {
|
||||
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) {
|
||||
// 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.model.toChapterUpdate
|
||||
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.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||
@ -46,7 +46,7 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
||||
*/
|
||||
class NotificationReceiver : BroadcastReceiver() {
|
||||
|
||||
private val getMangaById: GetMangaById by injectLazy()
|
||||
private val getManga: GetManga by injectLazy()
|
||||
private val getChapter: GetChapter by injectLazy()
|
||||
private val updateChapter: UpdateChapter by injectLazy()
|
||||
private val downloadManager: DownloadManager by injectLazy()
|
||||
@ -178,7 +178,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
* @param chapterId id of chapter
|
||||
*/
|
||||
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) }
|
||||
if (manga != null && chapter != null) {
|
||||
val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply {
|
||||
@ -248,7 +248,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
.map {
|
||||
val chapter = it.copy(read = true)
|
||||
if (preferences.removeAfterMarkedAsRead()) {
|
||||
val manga = getMangaById.await(mangaId)
|
||||
val manga = getManga.await(mangaId)
|
||||
if (manga != null) {
|
||||
val source = sourceManager.get(manga.source)
|
||||
if (source != null) {
|
||||
@ -270,7 +270,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
*/
|
||||
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
||||
launchIO {
|
||||
val manga = getMangaById.await(mangaId)?.toDbManga()
|
||||
val manga = getManga.await(mangaId)?.toDbManga()
|
||||
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId)?.toDbChapter() }
|
||||
if (manga != null && chapters.isNotEmpty()) {
|
||||
downloadManager.downloadChapters(manga, chapters)
|
||||
|
@ -9,7 +9,7 @@ import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
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.InsertTrack
|
||||
import eu.kanade.domain.track.model.toDbTrack
|
||||
@ -26,7 +26,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
|
||||
CoroutineWorker(context, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val getMangaById = Injekt.get<GetMangaById>()
|
||||
val getManga = Injekt.get<GetManga>()
|
||||
val getTracks = Injekt.get<GetTracks>()
|
||||
val insertTrack = Injekt.get<InsertTrack>()
|
||||
|
||||
@ -35,7 +35,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
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)
|
||||
.find { track -> track.id == it.trackId }
|
||||
?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||
|
@ -6,8 +6,9 @@ import androidx.core.view.isVisible
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
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.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||
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.GlobalSearchPresenter
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -27,9 +29,11 @@ class SearchController(
|
||||
) : GlobalSearchController(manga?.title) {
|
||||
|
||||
constructor(mangaId: Long) : this(
|
||||
Injekt.get<DatabaseHelper>()
|
||||
.getManga(mangaId)
|
||||
.executeAsBlocking(),
|
||||
runBlocking {
|
||||
Injekt.get<GetManga>()
|
||||
.await(mangaId)
|
||||
?.toDbManga()
|
||||
},
|
||||
)
|
||||
|
||||
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.SyncChaptersWithTrackServiceTwoWay
|
||||
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.toMangaUpdate
|
||||
import eu.kanade.domain.track.interactor.InsertTrack
|
||||
import eu.kanade.domain.track.model.toDomainTrack
|
||||
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.toDomainManga
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
@ -67,13 +70,15 @@ open class BrowseSourcePresenter(
|
||||
private val sourceId: Long,
|
||||
searchQuery: String? = null,
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val prefs: PreferencesHelper = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get(),
|
||||
private val getManga: GetManga = Injekt.get(),
|
||||
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
private val getChapterByMangaId: GetChapterByMangaId = 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 syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
||||
) : BasePresenter<BrowseSourceController>() {
|
||||
@ -208,19 +213,22 @@ open class BrowseSourcePresenter(
|
||||
* @return a manga from the database.
|
||||
*/
|
||||
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) {
|
||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||
newManga.copyFrom(sManga)
|
||||
val result = db.insertManga(newManga).executeAsBlocking()
|
||||
newManga.id = result.insertedId()
|
||||
localManga = newManga
|
||||
newManga.id = -1
|
||||
val result = runBlocking {
|
||||
val id = insertManga.await(newManga.toDomainManga()!!)
|
||||
getManga.await(id!!)
|
||||
}
|
||||
localManga = result
|
||||
} else if (!localManga.favorite) {
|
||||
// if the manga isn't a favorite, set its display title from source
|
||||
// if it later becomes a favorite, updated title will go to db
|
||||
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())
|
||||
manga.copyFrom(networkManga.toSManga())
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
updateManga.await(
|
||||
manga
|
||||
.toDomainManga()
|
||||
?.toMangaUpdate()!!,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
}
|
||||
@ -282,7 +294,13 @@ open class BrowseSourcePresenter(
|
||||
autoAddTrack(manga)
|
||||
}
|
||||
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
runBlocking {
|
||||
updateManga.await(
|
||||
manga
|
||||
.toDomainManga()
|
||||
?.toMangaUpdate()!!,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoAddTrack(manga: Manga) {
|
||||
|
@ -1,8 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
||||
|
||||
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.toDomainManga
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
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.util.lang.runAsObservable
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
@ -38,8 +44,10 @@ open class GlobalSearchPresenter(
|
||||
private val initialQuery: String? = "",
|
||||
private val initialExtensionFilter: String? = null,
|
||||
val sourceManager: SourceManager = Injekt.get(),
|
||||
val db: DatabaseHelper = 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>() {
|
||||
|
||||
/**
|
||||
@ -248,7 +256,7 @@ open class GlobalSearchPresenter(
|
||||
val networkManga = source.getMangaDetails(manga.toMangaInfo())
|
||||
manga.copyFrom(networkManga.toSManga())
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
runBlocking { updateManga.await(manga.toDomainManga()!!.toMangaUpdate()) }
|
||||
return manga
|
||||
}
|
||||
|
||||
@ -260,18 +268,21 @@ open class GlobalSearchPresenter(
|
||||
* @return a manga from the database.
|
||||
*/
|
||||
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) {
|
||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||
newManga.copyFrom(sManga)
|
||||
val result = db.insertManga(newManga).executeAsBlocking()
|
||||
newManga.id = result.insertedId()
|
||||
localManga = newManga
|
||||
newManga.id = -1
|
||||
val result = runBlocking {
|
||||
val id = insertManga.await(newManga.toDomainManga()!!)
|
||||
getManga.await(id!!)
|
||||
}
|
||||
localManga = result
|
||||
} else if (!localManga.favorite) {
|
||||
// if the manga isn't a favorite, set its display title from source
|
||||
// if it later becomes a favorite, updated title will go to db
|
||||
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.model.ChapterUpdate
|
||||
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.model.Manga
|
||||
import eu.kanade.domain.manga.model.MangaUpdate
|
||||
import eu.kanade.domain.track.interactor.GetTracks
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
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.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
@ -60,6 +60,7 @@ typealias LibraryMap = Map<Long, List<LibraryItem>>
|
||||
*/
|
||||
class LibraryPresenter(
|
||||
private val handler: DatabaseHandler = Injekt.get(),
|
||||
private val getLibraryManga: GetLibraryManga = Injekt.get(),
|
||||
private val getTracks: GetTracks = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||
@ -410,35 +411,7 @@ class LibraryPresenter(
|
||||
val defaultLibraryDisplayMode = preferences.libraryDisplayMode()
|
||||
val shouldSetFromCategory = preferences.categorizedDisplaySettings()
|
||||
|
||||
// TODO: Move this to domain/data layer
|
||||
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()
|
||||
return getLibraryManga.subscribe().asObservable()
|
||||
.map { list ->
|
||||
list.map { libraryManga ->
|
||||
// Display mode based on user preference: take it from global library setting or category
|
||||
|
@ -149,7 +149,7 @@ class MangaPresenter(
|
||||
|
||||
presenterScope.launchIO {
|
||||
if (!getMangaAndChapters.awaitManga(mangaId).favorite) {
|
||||
ChapterSettingsHelper.applySettingDefaults(mangaId, setMangaChapterFlags)
|
||||
ChapterSettingsHelper.applySettingDefaults(mangaId)
|
||||
}
|
||||
|
||||
getMangaAndChapters.subscribe(mangaId)
|
||||
|
@ -20,7 +20,7 @@ import androidx.core.os.bundleOf
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
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.model.Manga
|
||||
import eu.kanade.domain.manga.model.hasCustomCover
|
||||
@ -161,7 +161,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
|
||||
|
||||
inner class MangaFullCoverPresenter(
|
||||
private val mangaId: Long,
|
||||
private val getMangaById: GetMangaById = Injekt.get(),
|
||||
private val getManga: GetManga = Injekt.get(),
|
||||
) : Presenter<MangaFullCoverDialog>() {
|
||||
|
||||
private var presenterScope: CoroutineScope = MainScope()
|
||||
@ -176,7 +176,7 @@ class MangaFullCoverDialog : FullComposeController<MangaFullCoverDialog.MangaFul
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
presenterScope.launchIO {
|
||||
getMangaById.subscribe(mangaId)
|
||||
getManga.subscribe(mangaId)
|
||||
.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.history.interactor.UpsertHistory
|
||||
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.toDbManga
|
||||
import eu.kanade.domain.track.interactor.GetTracks
|
||||
import eu.kanade.domain.track.interactor.InsertTrack
|
||||
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.toDomainManga
|
||||
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.
|
||||
*/
|
||||
class ReaderPresenter(
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val downloadManager: DownloadManager = Injekt.get(),
|
||||
private val preferences: PreferencesHelper = 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 getTracks: GetTracks = Injekt.get(),
|
||||
private val insertTrack: InsertTrack = Injekt.get(),
|
||||
private val upsertHistory: UpsertHistory = Injekt.get(),
|
||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
||||
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
|
||||
) : BasePresenter<ReaderActivity>() {
|
||||
|
||||
/**
|
||||
@ -242,7 +242,7 @@ class ReaderPresenter(
|
||||
|
||||
launchIO {
|
||||
try {
|
||||
val manga = getMangaById.await(mangaId)
|
||||
val manga = getManga.await(mangaId)
|
||||
withUIContext {
|
||||
manga?.let { init(it.toDbManga(), initialChapterId) }
|
||||
}
|
||||
@ -570,7 +570,9 @@ class ReaderPresenter(
|
||||
fun setMangaReadingMode(readingModeType: Int) {
|
||||
val manga = manga ?: return
|
||||
manga.readingModeType = readingModeType
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
runBlocking {
|
||||
setMangaViewerFlags.awaitSetMangaReadingMode(manga.id!!.toLong(), readingModeType.toLong())
|
||||
}
|
||||
|
||||
Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribeFirst({ view, _ ->
|
||||
@ -605,7 +607,9 @@ class ReaderPresenter(
|
||||
fun setMangaOrientationType(rotationType: Int) {
|
||||
val manga = manga ?: return
|
||||
manga.orientationType = rotationType
|
||||
db.updateViewerFlags(manga).executeAsBlocking()
|
||||
runBlocking {
|
||||
setMangaViewerFlags.awaitSetOrientationType(manga.id!!.toLong(), rotationType.toLong())
|
||||
}
|
||||
|
||||
logcat(LogPriority.INFO) { "Manga orientation is ${manga.orientationType}" }
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package eu.kanade.tachiyomi.util.chapter
|
||||
|
||||
import eu.kanade.domain.manga.interactor.GetFavorites
|
||||
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.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
@ -10,7 +10,8 @@ import uy.kohesive.injekt.injectLazy
|
||||
object ChapterSettingsHelper {
|
||||
|
||||
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.
|
||||
@ -23,19 +24,20 @@ object ChapterSettingsHelper {
|
||||
* Updates a single manga's Chapter Settings to match what's set in Preferences.
|
||||
*/
|
||||
fun applySettingDefaults(manga: Manga) {
|
||||
with(manga) {
|
||||
readFilter = prefs.filterChapterByRead()
|
||||
downloadedFilter = prefs.filterChapterByDownloaded()
|
||||
bookmarkedFilter = prefs.filterChapterByBookmarked()
|
||||
sorting = prefs.sortChapterBySourceOrNumber()
|
||||
displayMode = prefs.displayChapterByNameOrNumber()
|
||||
setChapterOrder(prefs.sortChapterByAscendingOrDescending())
|
||||
launchIO {
|
||||
setMangaChapterFlags.awaitSetAllFlags(
|
||||
mangaId = manga.id!!,
|
||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
||||
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
||||
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
||||
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
||||
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
||||
)
|
||||
}
|
||||
|
||||
db.updateChapterFlags(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
suspend fun applySettingDefaults(mangaId: Long, setMangaChapterFlags: SetMangaChapterFlags) {
|
||||
suspend fun applySettingDefaults(mangaId: Long) {
|
||||
setMangaChapterFlags.awaitSetAllFlags(
|
||||
mangaId = mangaId,
|
||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||
@ -52,21 +54,18 @@ object ChapterSettingsHelper {
|
||||
*/
|
||||
fun updateAllMangasWithGlobalDefaults() {
|
||||
launchIO {
|
||||
val updatedMangas = db.getFavoriteMangas()
|
||||
.executeAsBlocking()
|
||||
getFavorites.await()
|
||||
.map { manga ->
|
||||
with(manga) {
|
||||
readFilter = prefs.filterChapterByRead()
|
||||
downloadedFilter = prefs.filterChapterByDownloaded()
|
||||
bookmarkedFilter = prefs.filterChapterByBookmarked()
|
||||
sorting = prefs.sortChapterBySourceOrNumber()
|
||||
displayMode = prefs.displayChapterByNameOrNumber()
|
||||
setChapterOrder(prefs.sortChapterByAscendingOrDescending())
|
||||
}
|
||||
manga
|
||||
setMangaChapterFlags.awaitSetAllFlags(
|
||||
mangaId = manga.id,
|
||||
unreadFilter = prefs.filterChapterByRead().toLong(),
|
||||
downloadedFilter = prefs.filterChapterByDownloaded().toLong(),
|
||||
bookmarkedFilter = prefs.filterChapterByBookmarked().toLong(),
|
||||
sortingMode = prefs.sortChapterBySourceOrNumber().toLong(),
|
||||
sortingDirection = prefs.sortChapterByAscendingOrDescending().toLong(),
|
||||
displayMode = prefs.displayChapterByNameOrNumber().toLong(),
|
||||
)
|
||||
}
|
||||
|
||||
db.updateChapterFlags(updatedMangas).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +145,44 @@ deleteMangasNotInLibraryBySourceIds:
|
||||
DELETE FROM mangas
|
||||
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 mangas SET
|
||||
source = coalesce(:source, source),
|
||||
|
Loading…
Reference in New Issue
Block a user