mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Use SQLDelight for all Manga related queries (#7447)
This commit is contained in:
		| @@ -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), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user