Cache chapters list (close #812)
This commit is contained in:
@@ -29,8 +29,9 @@ class AutoFixUseCase @Inject constructor(
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(mangaId: Long): Pair<Manga, Manga?> {
|
||||
val seed = checkNotNull(mangaDataRepository.findMangaById(mangaId)) { "Manga $mangaId not found" }
|
||||
.getDetailsSafe()
|
||||
val seed = checkNotNull(
|
||||
mangaDataRepository.findMangaById(mangaId, withChapters = true),
|
||||
) { "Manga $mangaId not found" }.getDetailsSafe()
|
||||
if (seed.isHealthy()) {
|
||||
return seed to null // no fix required
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.bookmarks.data.BookmarkEntity
|
||||
import org.koitharu.kotatsu.bookmarks.data.BookmarksDao
|
||||
import org.koitharu.kotatsu.core.db.dao.ChaptersDao
|
||||
import org.koitharu.kotatsu.core.db.dao.MangaDao
|
||||
import org.koitharu.kotatsu.core.db.dao.MangaSourcesDao
|
||||
import org.koitharu.kotatsu.core.db.dao.PreferencesDao
|
||||
import org.koitharu.kotatsu.core.db.dao.TagsDao
|
||||
import org.koitharu.kotatsu.core.db.dao.TrackLogsDao
|
||||
import org.koitharu.kotatsu.core.db.entity.ChapterEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
|
||||
@@ -36,6 +38,7 @@ import org.koitharu.kotatsu.core.db.migrations.Migration1To2
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration20To21
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration21To22
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration22To23
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration23To24
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration2To3
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration3To4
|
||||
import org.koitharu.kotatsu.core.db.migrations.Migration4To5
|
||||
@@ -63,14 +66,14 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TracksDao
|
||||
|
||||
const val DATABASE_VERSION = 23
|
||||
const val DATABASE_VERSION = 24
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class,
|
||||
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class,
|
||||
TrackEntity::class, TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class,
|
||||
ScrobblingEntity::class, MangaSourceEntity::class, StatsEntity::class, LocalMangaIndexEntity::class,
|
||||
MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class, ChapterEntity::class,
|
||||
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class, TrackEntity::class,
|
||||
TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class, ScrobblingEntity::class,
|
||||
MangaSourceEntity::class, StatsEntity::class, LocalMangaIndexEntity::class,
|
||||
],
|
||||
version = DATABASE_VERSION,
|
||||
)
|
||||
@@ -103,6 +106,8 @@ abstract class MangaDatabase : RoomDatabase() {
|
||||
abstract fun getStatsDao(): StatsDao
|
||||
|
||||
abstract fun getLocalMangaIndexDao(): LocalMangaIndexDao
|
||||
|
||||
abstract fun getChaptersDao(): ChaptersDao
|
||||
}
|
||||
|
||||
fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
|
||||
@@ -128,6 +133,7 @@ fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
|
||||
Migration20To21(),
|
||||
Migration21To22(),
|
||||
Migration22To23(),
|
||||
Migration23To24(),
|
||||
)
|
||||
|
||||
fun MangaDatabase(context: Context): MangaDatabase = Room
|
||||
|
||||
@@ -7,3 +7,4 @@ const val TABLE_FAVOURITE_CATEGORIES = "favourite_categories"
|
||||
const val TABLE_HISTORY = "history"
|
||||
const val TABLE_MANGA_TAGS = "manga_tags"
|
||||
const val TABLE_SOURCES = "sources"
|
||||
const val TABLE_CHAPTERS = "chapters"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.koitharu.kotatsu.core.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import org.koitharu.kotatsu.core.db.entity.ChapterEntity
|
||||
|
||||
@Dao
|
||||
abstract class ChaptersDao {
|
||||
|
||||
@Query("SELECT * FROM chapters WHERE manga_id = :mangaId ORDER BY `index` ASC")
|
||||
abstract suspend fun findAll(mangaId: Long): List<ChapterEntity>
|
||||
|
||||
@Query("DELETE FROM chapters WHERE manga_id = :mangaId")
|
||||
abstract suspend fun deleteAll(mangaId: Long)
|
||||
|
||||
@Query("DELETE FROM chapters WHERE manga_id NOT IN (SELECT manga_id FROM history WHERE deleted_at = 0) AND manga_id NOT IN (SELECT manga_id FROM favourites WHERE deleted_at = 0)")
|
||||
abstract suspend fun gc()
|
||||
|
||||
@Transaction
|
||||
open suspend fun replaceAll(mangaId: Long, entities: Collection<ChapterEntity>) {
|
||||
deleteAll(mangaId)
|
||||
insert(entities)
|
||||
}
|
||||
|
||||
@Insert
|
||||
protected abstract suspend fun insert(entities: Collection<ChapterEntity>)
|
||||
}
|
||||
@@ -20,6 +20,9 @@ abstract class MangaDao {
|
||||
@Query("SELECT * FROM manga WHERE manga_id = :id")
|
||||
abstract suspend fun find(id: Long): MangaWithTags?
|
||||
|
||||
@Query("SELECT EXISTS(SELECT * FROM manga WHERE manga_id = :id)")
|
||||
abstract suspend operator fun contains(id: Long): Boolean
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM manga WHERE public_url = :publicUrl")
|
||||
abstract suspend fun findByPublicUrl(publicUrl: String): MangaWithTags?
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.koitharu.kotatsu.core.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import org.koitharu.kotatsu.core.db.TABLE_CHAPTERS
|
||||
|
||||
@Entity(
|
||||
tableName = TABLE_CHAPTERS,
|
||||
primaryKeys = ["manga_id", "chapter_id"],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = MangaEntity::class,
|
||||
parentColumns = ["manga_id"],
|
||||
childColumns = ["manga_id"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class ChapterEntity(
|
||||
@ColumnInfo(name = "chapter_id") val chapterId: Long,
|
||||
@ColumnInfo(name = "manga_id") val mangaId: Long,
|
||||
@ColumnInfo(name = "name") val name: String,
|
||||
@ColumnInfo(name = "number") val number: Float,
|
||||
@ColumnInfo(name = "volume") val volume: Int,
|
||||
@ColumnInfo(name = "url") val url: String,
|
||||
@ColumnInfo(name = "scanlator") val scanlator: String?,
|
||||
@ColumnInfo(name = "upload_date") val uploadDate: Long,
|
||||
@ColumnInfo(name = "branch") val branch: String?,
|
||||
@ColumnInfo(name = "source") val source: String,
|
||||
@ColumnInfo(name = "index") val index: Int,
|
||||
)
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.db.entity
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.util.ext.longHashCode
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
@@ -21,7 +22,7 @@ fun Collection<TagEntity>.toMangaTags() = mapToSet(TagEntity::toMangaTag)
|
||||
|
||||
fun Collection<TagEntity>.toMangaTagsList() = map(TagEntity::toMangaTag)
|
||||
|
||||
fun MangaEntity.toManga(tags: Set<MangaTag>) = Manga(
|
||||
fun MangaEntity.toManga(tags: Set<MangaTag>, chapters: List<ChapterEntity>?) = Manga(
|
||||
id = this.id,
|
||||
title = this.title,
|
||||
altTitle = this.altTitle,
|
||||
@@ -35,12 +36,27 @@ fun MangaEntity.toManga(tags: Set<MangaTag>) = Manga(
|
||||
author = this.author,
|
||||
source = MangaSource(this.source),
|
||||
tags = tags,
|
||||
chapters = chapters?.toMangaChapters(),
|
||||
)
|
||||
|
||||
fun MangaWithTags.toManga() = manga.toManga(tags.toMangaTags())
|
||||
fun MangaWithTags.toManga(chapters: List<ChapterEntity>? = null) = manga.toManga(tags.toMangaTags(), chapters)
|
||||
|
||||
fun Collection<MangaWithTags>.toMangaList() = map { it.toManga() }
|
||||
|
||||
fun ChapterEntity.toMangaChapter() = MangaChapter(
|
||||
id = chapterId,
|
||||
name = name,
|
||||
number = number,
|
||||
volume = volume,
|
||||
url = url,
|
||||
scanlator = scanlator,
|
||||
uploadDate = uploadDate,
|
||||
branch = branch,
|
||||
source = MangaSource(source),
|
||||
)
|
||||
|
||||
fun Collection<ChapterEntity>.toMangaChapters() = map { it.toMangaChapter() }
|
||||
|
||||
// Model to entity
|
||||
|
||||
fun Manga.toEntity() = MangaEntity(
|
||||
@@ -67,6 +83,22 @@ fun MangaTag.toEntity() = TagEntity(
|
||||
|
||||
fun Collection<MangaTag>.toEntities() = map(MangaTag::toEntity)
|
||||
|
||||
fun Iterable<IndexedValue<MangaChapter>>.toEntities(mangaId: Long) = map { (index, chapter) ->
|
||||
ChapterEntity(
|
||||
chapterId = chapter.id,
|
||||
mangaId = mangaId,
|
||||
name = chapter.name,
|
||||
number = chapter.number,
|
||||
volume = chapter.volume,
|
||||
url = chapter.url,
|
||||
scanlator = chapter.scanlator,
|
||||
uploadDate = chapter.uploadDate,
|
||||
branch = chapter.branch,
|
||||
source = chapter.source.name,
|
||||
index = index,
|
||||
)
|
||||
}
|
||||
|
||||
// Other
|
||||
|
||||
fun SortOrder(name: String, fallback: SortOrder): SortOrder = runCatching {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.koitharu.kotatsu.core.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration23To24 : Migration(23, 24) {
|
||||
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `chapters` (`chapter_id` INTEGER NOT NULL, `manga_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `number` REAL NOT NULL, `volume` INTEGER NOT NULL, `url` TEXT NOT NULL, `scanlator` TEXT, `upload_date` INTEGER NOT NULL, `branch` TEXT, `source` TEXT NOT NULL, `index` INTEGER NOT NULL, PRIMARY KEY(`manga_id`, `chapter_id`), FOREIGN KEY(`manga_id`) REFERENCES `manga`(`manga_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
|
||||
}
|
||||
}
|
||||
@@ -71,8 +71,13 @@ class MangaDataRepository @Inject constructor(
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
suspend fun findMangaById(mangaId: Long): Manga? {
|
||||
return db.getMangaDao().find(mangaId)?.toManga()
|
||||
suspend fun findMangaById(mangaId: Long, withChapters: Boolean): Manga? {
|
||||
val chapters = if (withChapters) {
|
||||
db.getChaptersDao().findAll(mangaId).takeUnless { it.isEmpty() }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return db.getMangaDao().find(mangaId)?.toManga(chapters)
|
||||
}
|
||||
|
||||
suspend fun findMangaByPublicUrl(publicUrl: String): Manga? {
|
||||
@@ -81,7 +86,7 @@ class MangaDataRepository @Inject constructor(
|
||||
|
||||
suspend fun resolveIntent(intent: MangaIntent): Manga? = when {
|
||||
intent.manga != null -> intent.manga
|
||||
intent.mangaId != 0L -> findMangaById(intent.mangaId)
|
||||
intent.mangaId != 0L -> findMangaById(intent.mangaId, true)
|
||||
intent.uri != null -> resolverProvider.get().resolve(intent.uri)
|
||||
else -> null
|
||||
}
|
||||
@@ -98,10 +103,26 @@ class MangaDataRepository @Inject constructor(
|
||||
val tags = manga.tags.toEntities()
|
||||
db.getTagsDao().upsert(tags)
|
||||
db.getMangaDao().upsert(manga.toEntity(), tags)
|
||||
if (!manga.isLocal) {
|
||||
manga.chapters?.let { chapters ->
|
||||
db.getChaptersDao().replaceAll(manga.id, chapters.withIndex().toEntities(manga.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateChapters(manga: Manga) {
|
||||
val chapters = manga.chapters
|
||||
if (!chapters.isNullOrEmpty() && manga.id in db.getMangaDao()) {
|
||||
db.getChaptersDao().replaceAll(manga.id, chapters.withIndex().toEntities(manga.id))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun gcChapters() {
|
||||
db.getChaptersDao().gc()
|
||||
}
|
||||
|
||||
suspend fun findTags(source: MangaSource): Set<MangaTag> {
|
||||
return db.getTagsDao().findTags(source.name).toMangaTags()
|
||||
}
|
||||
|
||||
@@ -43,7 +43,14 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
operator fun invoke(intent: MangaIntent): Flow<MangaDetails> = channelFlow {
|
||||
val manga = requireNotNull(mangaDataRepository.resolveIntent(intent)) {
|
||||
"Cannot resolve intent $intent"
|
||||
}.let { m ->
|
||||
if (m.chapters.isNullOrEmpty()) {
|
||||
getCachedDetails(m.id) ?: m
|
||||
} else {
|
||||
m
|
||||
}
|
||||
}
|
||||
send(MangaDetails(manga, null, null, false))
|
||||
val local = if (!manga.isLocal) {
|
||||
async {
|
||||
localMangaRepository.findSavedManga(manga)
|
||||
@@ -51,9 +58,9 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
send(MangaDetails(manga, null, null, false))
|
||||
try {
|
||||
val details = getDetails(manga)
|
||||
launch { mangaDataRepository.updateChapters(details) }
|
||||
launch { updateTracker(details) }
|
||||
send(
|
||||
MangaDetails(
|
||||
@@ -122,4 +129,8 @@ class DetailsLoadUseCase @Inject constructor(
|
||||
}.onFailure { e ->
|
||||
e.printStackTraceDebug()
|
||||
}
|
||||
|
||||
private suspend fun getCachedDetails(mangaId: Long): Manga? = runCatchingCancellable {
|
||||
mangaDataRepository.findMangaById(mangaId, withChapters = true)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
@@ -226,14 +226,15 @@ class DetailsViewModel @Inject constructor(
|
||||
private fun doLoad() = launchLoadingJob(Dispatchers.Default) {
|
||||
detailsLoadUseCase.invoke(intent)
|
||||
.onEachWhile {
|
||||
if (it.allChapters.isEmpty()) {
|
||||
return@onEachWhile false
|
||||
if (it.allChapters.isNotEmpty()) {
|
||||
val manga = it.toManga()
|
||||
// find default branch
|
||||
val hist = historyRepository.getOne(manga)
|
||||
selectedBranch.value = manga.getPreferredBranch(hist)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val manga = it.toManga()
|
||||
// find default branch
|
||||
val hist = historyRepository.getOne(manga)
|
||||
selectedBranch.value = manga.getPreferredBranch(hist)
|
||||
true
|
||||
}.collect {
|
||||
mangaDetails.value = it
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ class DownloadsViewModel @Inject constructor(
|
||||
}
|
||||
return cacheMutex.withLock {
|
||||
mangaCache.getOrElse(mangaId) {
|
||||
mangaDataRepository.findMangaById(mangaId)?.also {
|
||||
mangaDataRepository.findMangaById(mangaId, withChapters = true)?.also {
|
||||
mangaCache[mangaId] = it
|
||||
} ?: return null
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
setForeground(getForegroundInfo())
|
||||
val manga = mangaDataRepository.findMangaById(task.mangaId) ?: return Result.failure()
|
||||
val manga = mangaDataRepository.findMangaById(task.mangaId, withChapters = true) ?: return Result.failure()
|
||||
publishState(DownloadState(manga = manga, isIndeterminate = true).also { lastPublishedState = it })
|
||||
val downloadedIds = getDoneChapters(manga)
|
||||
return try {
|
||||
|
||||
@@ -16,6 +16,6 @@ fun FavouriteCategoryEntity.toFavouriteCategory(id: Long = categoryId.toLong())
|
||||
isVisibleInLibrary = isVisibleInLibrary,
|
||||
)
|
||||
|
||||
fun FavouriteManga.toManga() = manga.toManga(tags.toMangaTags())
|
||||
fun FavouriteManga.toManga() = manga.toManga(tags.toMangaTags(), null)
|
||||
|
||||
fun Collection<FavouriteManga>.toMangaList() = map { it.toManga() }
|
||||
|
||||
@@ -199,6 +199,7 @@ class FavouritesRepository @Inject constructor(
|
||||
db.getFavouritesDao().deleteAll(id)
|
||||
db.getFavouriteCategoriesDao().delete(id)
|
||||
}
|
||||
db.getChaptersDao().gc()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +239,7 @@ class FavouritesRepository @Inject constructor(
|
||||
for (id in ids) {
|
||||
db.getFavouritesDao().delete(mangaId = id)
|
||||
}
|
||||
db.getChaptersDao().gc()
|
||||
}
|
||||
return ReversibleHandle { recoverToFavourites(ids) }
|
||||
}
|
||||
@@ -247,6 +249,7 @@ class FavouritesRepository @Inject constructor(
|
||||
for (id in ids) {
|
||||
db.getFavouritesDao().delete(categoryId = categoryId, mangaId = id)
|
||||
}
|
||||
db.getChaptersDao().gc()
|
||||
}
|
||||
return ReversibleHandle { recoverToCategory(categoryId, ids) }
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class LocalFavoritesObserver @Inject constructor(
|
||||
limit: Int
|
||||
): Flow<List<Manga>> = db.getFavouritesDao().observeAll(categoryId, order, filterOptions, limit).mapToLocal()
|
||||
|
||||
override fun toManga(e: FavouriteManga) = e.manga.toManga(e.tags.toMangaTags())
|
||||
override fun toManga(e: FavouriteManga) = e.manga.toManga(e.tags.toMangaTags(), null)
|
||||
|
||||
override fun toResult(e: FavouriteManga, manga: Manga) = manga
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class HistoryLocalObserver @Inject constructor(
|
||||
limit: Int
|
||||
) = db.getHistoryDao().observeAll(order, filterOptions, limit).mapToLocal()
|
||||
|
||||
override fun toManga(e: HistoryWithManga) = e.manga.toManga(e.tags.toMangaTags())
|
||||
override fun toManga(e: HistoryWithManga) = e.manga.toManga(e.tags.toMangaTags(), null)
|
||||
|
||||
override fun toResult(e: HistoryWithManga, manga: Manga) = MangaWithHistory(
|
||||
manga = manga,
|
||||
|
||||
@@ -49,7 +49,7 @@ class HistoryRepository @Inject constructor(
|
||||
|
||||
suspend fun getList(offset: Int, limit: Int): List<Manga> {
|
||||
val entities = db.getHistoryDao().findAll(offset, limit)
|
||||
return entities.map { it.manga.toManga(it.tags.toMangaTags()) }
|
||||
return entities.map { it.toManga() }
|
||||
}
|
||||
|
||||
suspend fun search(query: String, limit: Int): List<Manga> {
|
||||
@@ -63,25 +63,25 @@ class HistoryRepository @Inject constructor(
|
||||
|
||||
suspend fun getLastOrNull(): Manga? {
|
||||
val entity = db.getHistoryDao().findAll(0, 1).firstOrNull() ?: return null
|
||||
return entity.manga.toManga(entity.tags.toMangaTags())
|
||||
return entity.toManga()
|
||||
}
|
||||
|
||||
fun observeLast(): Flow<Manga?> {
|
||||
return db.getHistoryDao().observeAll(1).map {
|
||||
val first = it.firstOrNull()
|
||||
first?.manga?.toManga(first.tags.toMangaTags())
|
||||
first?.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
fun observeAll(): Flow<List<Manga>> {
|
||||
return db.getHistoryDao().observeAll().mapItems {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
it.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
fun observeAll(limit: Int): Flow<List<Manga>> {
|
||||
return db.getHistoryDao().observeAll(limit).mapItems {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
it.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class HistoryRepository @Inject constructor(
|
||||
}
|
||||
return db.getHistoryDao().observeAll(order, filterOptions, limit).mapItems {
|
||||
MangaWithHistory(
|
||||
it.manga.toManga(it.tags.toMangaTags()),
|
||||
it.toManga(),
|
||||
it.history.toMangaHistory(),
|
||||
)
|
||||
}
|
||||
@@ -156,16 +156,19 @@ class HistoryRepository @Inject constructor(
|
||||
db.getHistoryDao().clear()
|
||||
}
|
||||
|
||||
suspend fun delete(manga: Manga) {
|
||||
suspend fun delete(manga: Manga) = db.withTransaction {
|
||||
db.getHistoryDao().delete(manga.id)
|
||||
mangaRepository.gcChapters()
|
||||
}
|
||||
|
||||
suspend fun deleteAfter(minDate: Long) {
|
||||
suspend fun deleteAfter(minDate: Long) = db.withTransaction {
|
||||
db.getHistoryDao().deleteAfter(minDate)
|
||||
mangaRepository.gcChapters()
|
||||
}
|
||||
|
||||
suspend fun deleteNotFavorite() {
|
||||
suspend fun deleteNotFavorite() = db.withTransaction {
|
||||
db.getHistoryDao().deleteNotFavorite()
|
||||
mangaRepository.gcChapters()
|
||||
}
|
||||
|
||||
suspend fun delete(ids: Collection<Long>): ReversibleHandle {
|
||||
@@ -173,6 +176,7 @@ class HistoryRepository @Inject constructor(
|
||||
for (id in ids) {
|
||||
db.getHistoryDao().delete(id)
|
||||
}
|
||||
mangaRepository.gcChapters()
|
||||
}
|
||||
return ReversibleHandle {
|
||||
recover(ids)
|
||||
@@ -185,7 +189,7 @@ class HistoryRepository @Inject constructor(
|
||||
*/
|
||||
suspend fun deleteOrSwap(manga: Manga, alternative: Manga?) {
|
||||
if (alternative == null || db.getMangaDao().update(alternative.toEntity()) <= 0) {
|
||||
db.getHistoryDao().delete(manga.id)
|
||||
delete(manga)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,4 +233,6 @@ class HistoryRepository @Inject constructor(
|
||||
db.getHistoryDao().update(newEntity)
|
||||
return newEntity
|
||||
}
|
||||
|
||||
private fun HistoryWithManga.toManga() = manga.toManga(tags.toMangaTags(), null)
|
||||
}
|
||||
|
||||
@@ -32,17 +32,17 @@ class CoverRestoreInterceptor @Inject constructor(
|
||||
val result = chain.proceed()
|
||||
if (result is ErrorResult && result.throwable.shouldRestore()) {
|
||||
request.extras[mangaKey]?.let {
|
||||
if (restoreManga(it)) {
|
||||
return chain.withRequest(request.newBuilder().build()).proceed()
|
||||
return if (restoreManga(it)) {
|
||||
chain.withRequest(request.newBuilder().build()).proceed()
|
||||
} else {
|
||||
return result
|
||||
result
|
||||
}
|
||||
}
|
||||
request.extras[bookmarkKey]?.let {
|
||||
if (restoreBookmark(it)) {
|
||||
return chain.withRequest(request.newBuilder().build()).proceed()
|
||||
return if (restoreBookmark(it)) {
|
||||
chain.withRequest(request.newBuilder().build()).proceed()
|
||||
} else {
|
||||
return result
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ class CoverRestoreInterceptor @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun restoreMangaImpl(manga: Manga): Boolean {
|
||||
if (dataRepository.findMangaById(manga.id) == null || manga.isLocal) {
|
||||
if (dataRepository.findMangaById(manga.id, withChapters = false) == null || manga.isLocal) {
|
||||
return false
|
||||
}
|
||||
val repo = repositoryFactory.create(manga.source)
|
||||
|
||||
@@ -33,7 +33,7 @@ class StatsRepository @Inject constructor(
|
||||
var other = StatsRecord(null, 0)
|
||||
val total = stats.values.sum()
|
||||
for ((mangaEntity, duration) in stats) {
|
||||
val manga = mangaEntity.toManga(emptySet())
|
||||
val manga = mangaEntity.toManga(emptySet(), null)
|
||||
val percent = duration.toDouble() / total
|
||||
if (percent < 0.05) {
|
||||
other = other.copy(duration = other.duration + duration)
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.suggestions.data.SuggestionEntity
|
||||
import org.koitharu.kotatsu.suggestions.data.SuggestionWithManga
|
||||
import javax.inject.Inject
|
||||
|
||||
class SuggestionRepository @Inject constructor(
|
||||
@@ -23,25 +24,23 @@ class SuggestionRepository @Inject constructor(
|
||||
|
||||
fun observeAll(): Flow<List<Manga>> {
|
||||
return db.getSuggestionDao().observeAll().mapItems {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
it.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
fun observeAll(limit: Int, filterOptions: Set<ListFilterOption>): Flow<List<Manga>> {
|
||||
return db.getSuggestionDao().observeAll(limit, filterOptions).mapItems {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
it.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getRandom(): Manga? {
|
||||
return db.getSuggestionDao().getRandom()?.let {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
}
|
||||
return db.getSuggestionDao().getRandom()?.toManga()
|
||||
}
|
||||
|
||||
suspend fun getRandomList(limit: Int): List<Manga> {
|
||||
return db.getSuggestionDao().getRandom(limit).map {
|
||||
it.manga.toManga(it.tags.toMangaTags())
|
||||
it.toManga()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +79,6 @@ class SuggestionRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun SuggestionWithManga.toManga() = manga.toManga(tags.toMangaTags(), null)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ fun TrackLogWithManga.toTrackingLogItem(): TrackingLogItem {
|
||||
return TrackingLogItem(
|
||||
id = trackLog.id,
|
||||
chapters = chaptersList,
|
||||
manga = manga.toManga(tags.toMangaTags()),
|
||||
manga = manga.toManga(tags.toMangaTags(), null),
|
||||
createdAt = Instant.ofEpochMilli(trackLog.createdAt),
|
||||
isNew = trackLog.isUnread,
|
||||
)
|
||||
|
||||
@@ -60,7 +60,7 @@ class TrackingRepository @Inject constructor(
|
||||
return db.getTracksDao().observeUpdatedManga(limit, filterOptions)
|
||||
.mapItems {
|
||||
MangaTracking(
|
||||
manga = it.manga.toManga(it.tags.toMangaTags()),
|
||||
manga = it.manga.toManga(it.tags.toMangaTags(), null),
|
||||
lastChapterId = it.track.lastChapterId,
|
||||
lastCheck = it.track.lastCheckTime.toInstantOrNull(),
|
||||
lastChapterDate = it.track.lastChapterDate.toInstantOrNull(),
|
||||
@@ -73,7 +73,7 @@ class TrackingRepository @Inject constructor(
|
||||
suspend fun getTracks(offset: Int, limit: Int): List<MangaTracking> {
|
||||
return db.getTracksDao().findAll(offset = offset, limit = limit).map {
|
||||
MangaTracking(
|
||||
manga = it.manga.toManga(emptySet()),
|
||||
manga = it.manga.toManga(emptySet(), null),
|
||||
lastChapterId = it.track.lastChapterId,
|
||||
lastCheck = it.track.lastCheckTime.toInstantOrNull(),
|
||||
lastChapterDate = it.track.lastChapterDate.toInstantOrNull(),
|
||||
|
||||
@@ -25,7 +25,7 @@ class TrackerDebugViewModel @Inject constructor(
|
||||
|
||||
private fun List<TrackWithManga>.toUiList(): List<TrackDebugItem> = map {
|
||||
TrackDebugItem(
|
||||
manga = it.manga.toManga(emptySet()),
|
||||
manga = it.manga.toManga(emptySet(), null),
|
||||
lastChapterId = it.track.lastChapterId,
|
||||
newChapters = it.track.newChapters,
|
||||
lastCheckTime = it.track.lastCheckTime.toInstantOrNull(),
|
||||
|
||||
@@ -43,7 +43,7 @@ transition = "1.5.1"
|
||||
viewpager2 = "1.1.0"
|
||||
webkit = "1.12.1"
|
||||
workRuntime = "2.10.0"
|
||||
workinspector = "1.0"
|
||||
workinspector = "1.2"
|
||||
|
||||
[libraries]
|
||||
acra-dialog = { module = "ch.acra:acra-dialog", version.ref = "acra" }
|
||||
|
||||
Reference in New Issue
Block a user