refactor: Add cover_last_modified

This commit is contained in:
Ahmad Ansori Palembani 2024-08-17 14:13:49 +07:00
parent 653b2d7839
commit c9b302ab21
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
23 changed files with 158 additions and 104 deletions

View file

@ -4,8 +4,8 @@ import android.content.Context
import android.text.format.Formatter
import co.touchlab.kermit.Logger
import coil3.imageLoader
import coil3.memory.MemoryCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.e
@ -71,7 +71,10 @@ class CoverCache(val context: Context) {
val db = Injekt.get<DatabaseHelper>()
var deletedSize = 0L
val urls = db.getFavoriteMangas().executeOnIO().mapNotNull {
it.thumbnail_url?.let { url -> return@mapNotNull DiskUtil.hashKeyForDisk(url) }
it.thumbnail_url?.let { url ->
it.updateCoverLastModified()
return@mapNotNull DiskUtil.hashKeyForDisk(url)
}
null
}
val files = cacheDir.listFiles()?.iterator() ?: return
@ -170,7 +173,6 @@ class CoverCache(val context: Context) {
fun setCustomCoverToCache(manga: Manga, inputStream: InputStream) {
getCustomCoverFile(manga).outputStream().use {
inputStream.copyTo(it)
removeFromMemory(manga, true)
}
}
@ -184,14 +186,13 @@ class CoverCache(val context: Context) {
val result = getCustomCoverFile(manga).let {
it.exists() && it.delete()
}
removeFromMemory(manga, true)
return result
}
/**
* Returns the cover from cache.
*
* @param thumbnailUrl the thumbnail url.
* @param mangaThumbnailUrl the thumbnail url.
* @return cover image.
*/
fun getCoverFile(mangaThumbnailUrl: String?, isOnline: Boolean = false): File? {
@ -200,26 +201,6 @@ class CoverCache(val context: Context) {
}
}
fun removeFromMemory(manga: Manga, custom: Boolean = false) {
if (custom) {
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
return
}
manga.thumbnail_url?.let {
if (it.isEmpty()) return
context.imageLoader.memoryCache
?.remove(MemoryCache.Key(if (!manga.favorite) it else DiskUtil.hashKeyForDisk(it)))
}
}
fun deleteFromCache(name: String?) {
if (name.isNullOrEmpty()) return
val file = getCoverFile(name, true) ?: return
context.imageLoader.memoryCache?.remove(MemoryCache.Key(file.name))
if (file.exists()) file.delete()
}
/**
* Delete the cover file from the disk cache and optional from memory cache
*
@ -235,8 +216,7 @@ class CoverCache(val context: Context) {
// Remove file
getCoverFile(manga.thumbnail_url, !manga.favorite)?.let {
removeFromMemory(manga)
it.delete()
if (it.exists()) it.delete()
}
if (deleteCustom) deleteCustomCover(manga)
}

View file

@ -10,6 +10,7 @@ import coil3.request.Disposable
import coil3.request.ImageRequest
import coil3.target.ImageViewTarget
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.system.launchIO
import uy.kohesive.injekt.injectLazy
@ -32,7 +33,7 @@ class LibraryMangaImageTarget(
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.path, options)
if (options.outWidth == -1 || options.outHeight == -1) {
coverCache.removeFromMemory(manga)
manga.updateCoverLastModified()
file.delete()
}
}

View file

@ -9,13 +9,15 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
class MangaCoverKeyer : Keyer<Manga> {
override fun key(data: Manga, options: Options): String? {
val hasCustomCover by lazy { data.hasCustomCover() }
val suffix by lazy { ";${data.cover_last_modified}" }
if (data.thumbnail_url.isNullOrBlank() && !hasCustomCover) return null
if (hasCustomCover) return data.key()
if (hasCustomCover) return "${data.id}$suffix"
return if (!data.favorite) {
data.thumbnail_url!!
} else {
DiskUtil.hashKeyForDisk(data.thumbnail_url!!)
}
} + suffix
}
}

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.database
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.StorIOSQLite
inline fun StorIOSQLite.inTransaction(block: () -> Unit) {
@ -22,3 +23,5 @@ inline fun <T> StorIOSQLite.inTransactionReturn(block: () -> T): T {
lowLevel().endTransaction()
}
}
fun Cursor.getBoolean(index: Int) = getLong(index) > 0

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.annotation.SuppressLint
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
@ -9,10 +10,12 @@ 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.MangaImpl
import eu.kanade.tachiyomi.data.database.getBoolean
import eu.kanade.tachiyomi.data.database.models.mapper
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
@ -59,8 +62,8 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
put(COL_AUTHOR, obj.originalAuthor)
put(COL_DESCRIPTION, obj.originalDescription)
put(COL_GENRE, obj.originalGenre)
put(COL_TITLE, obj.originalTitle)
put(COL_STATUS, obj.originalStatus)
put(COL_TITLE, obj.ogTitle)
put(COL_STATUS, obj.ogStatus)
put(COL_THUMBNAIL_URL, obj.thumbnail_url)
put(COL_FAVORITE, obj.favorite)
put(COL_LAST_UPDATE, obj.last_update)
@ -70,40 +73,41 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
put(COL_CHAPTER_FLAGS, obj.chapter_flags)
put(COL_DATE_ADDED, obj.date_added)
put(COL_FILTERED_SCANLATORS, obj.filtered_scanlators)
put(COL_UPDATE_STRATEGY, obj.update_strategy.let(updateStrategyAdapter::encode).toInt())
put(COL_UPDATE_STRATEGY, obj.update_strategy.let(updateStrategyAdapter::encode))
put(COL_COVER_LAST_MODIFIED, obj.cover_last_modified)
}
}
interface BaseMangaGetResolver {
fun mapBaseFromCursor(manga: Manga, cursor: Cursor) = manga.apply {
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
source = cursor.getLong(cursor.getColumnIndex(COL_SOURCE))
url = cursor.getString(cursor.getColumnIndex(COL_URL))
artist = cursor.getString(cursor.getColumnIndex(COL_ARTIST))
author = cursor.getString(cursor.getColumnIndex(COL_AUTHOR))
description = cursor.getString(cursor.getColumnIndex(COL_DESCRIPTION))
genre = cursor.getString(cursor.getColumnIndex(COL_GENRE))
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
thumbnail_url = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL))
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
hide_title = cursor.getInt(cursor.getColumnIndex(COL_HIDE_TITLE)) == 1
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
filtered_scanlators = cursor.getString(cursor.getColumnIndex(COL_FILTERED_SCANLATORS))
update_strategy = cursor.getInt(cursor.getColumnIndex(COL_UPDATE_STRATEGY)).let {
updateStrategyAdapter.decode(it.toLong())
}
}
@SuppressLint("Range")
fun mapBaseFromCursor(cursor: Cursor) = Manga.mapper(
id = cursor.getLong(cursor.getColumnIndex(COL_ID)),
source = cursor.getLong(cursor.getColumnIndex(COL_SOURCE)),
url = cursor.getString(cursor.getColumnIndex(COL_URL)),
artist = cursor.getString(cursor.getColumnIndex(COL_ARTIST)),
author = cursor.getString(cursor.getColumnIndex(COL_AUTHOR)),
description = cursor.getString(cursor.getColumnIndex(COL_DESCRIPTION)),
genre = cursor.getString(cursor.getColumnIndex(COL_GENRE)),
title = cursor.getString(cursor.getColumnIndex(COL_TITLE)),
status = cursor.getLong(cursor.getColumnIndex(COL_STATUS)),
thumbnailUrl = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL)),
favorite = cursor.getBoolean(cursor.getColumnIndex(COL_FAVORITE)),
lastUpdate = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE)),
initialized = cursor.getBoolean(cursor.getColumnIndex(COL_INITIALIZED)),
viewerFlags = cursor.getLong(cursor.getColumnIndex(COL_VIEWER)),
chapterFlags = cursor.getLong(cursor.getColumnIndex(COL_CHAPTER_FLAGS)),
hideTitle = cursor.getBoolean(cursor.getColumnIndex(COL_HIDE_TITLE)),
dateAdded = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED)),
filteredScanlators = cursor.getString(cursor.getColumnIndex(COL_FILTERED_SCANLATORS)),
updateStrategy = cursor.getLong(cursor.getColumnIndex(COL_UPDATE_STRATEGY)),
coverLastModified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED)),
)
}
open class MangaGetResolver : DefaultGetResolver<Manga>(), BaseMangaGetResolver {
override fun mapFromCursor(cursor: Cursor): Manga {
return mapBaseFromCursor(MangaImpl(), cursor)
return mapBaseFromCursor(cursor)
}
}

View file

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.ui.library.LibraryItem
import yokai.data.updateStrategyAdapter
import kotlin.math.roundToInt
import yokai.data.updateStrategyAdapter
data class LibraryManga(
var unread: Int = 0,
@ -49,6 +49,7 @@ data class LibraryManga(
}
fun mapper(
// manga
id: Long,
source: Long,
url: String,
@ -59,15 +60,17 @@ data class LibraryManga(
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Long,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
hideTitle: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// libraryManga
total: Long,
readCount: Double,
bookmarkCount: Double,
@ -86,15 +89,16 @@ data class LibraryManga(
this.title = title
this.status = status.toInt()
this.thumbnail_url = thumbnailUrl
this.favorite = favorite > 0
this.favorite = favorite
this.last_update = lastUpdate ?: 0L
this.initialized = initialized
this.viewer_flags = viewerFlags.toInt()
this.hide_title = hideTitle > 0
this.hide_title = hideTitle
this.chapter_flags = chapterFlags.toInt()
this.date_added = dateAdded ?: 0L
this.filtered_scanlators = filteredScanlators
this.update_strategy = updateStrategy.let(updateStrategyAdapter::decode)
this.cover_last_modified = coverLastModified
this.read = readCount.roundToInt()
this.unread = maxOf((total - readCount).roundToInt(), 0)
this.totalChapters = total.toInt()

View file

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
import eu.kanade.tachiyomi.util.system.withIOContext
import java.util.Locale
@ -21,6 +22,8 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.data.updateStrategyAdapter
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.MangaUpdate
import yokai.i18n.MR
import yokai.util.lang.getString
@ -193,15 +196,16 @@ fun Manga.Companion.mapper(
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Long,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
hideTitle: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long
updateStrategy: Long,
coverLastModified: Long,
) = create(source).apply {
this.id = id
this.url = url
@ -212,17 +216,41 @@ fun Manga.Companion.mapper(
this.title = title
this.status = status.toInt()
this.thumbnail_url = thumbnailUrl
this.favorite = favorite > 0
this.favorite = favorite
this.last_update = lastUpdate ?: 0L
this.initialized = initialized
this.viewer_flags = viewerFlags.toInt()
this.chapter_flags = chapterFlags.toInt()
this.hide_title = hideTitle > 0
this.hide_title = hideTitle
this.date_added = dateAdded ?: 0L
this.filtered_scanlators = filteredScanlators
this.update_strategy = updateStrategy.let(updateStrategyAdapter::decode)
this.cover_last_modified = coverLastModified
}
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(this).exists()
}
/**
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
*/
fun Manga.prepareCoverUpdate(coverCache: CoverCache = Injekt.get()) {
cover_last_modified = System.currentTimeMillis()
if (!isLocal()) {
coverCache.deleteFromCache(this, true)
}
}
fun Manga.removeCover(coverCache: CoverCache = Injekt.get(), deleteCustom: Boolean = true) {
if (isLocal()) return
cover_last_modified = System.currentTimeMillis()
coverCache.deleteFromCache(this, deleteCustom)
}
suspend fun Manga.updateCoverLastModified(updateManga: UpdateManga = Injekt.get()) {
cover_last_modified = System.currentTimeMillis()
updateManga.await(MangaUpdate(id = id!!, coverLastModified = cover_last_modified))
}

View file

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaChapter(val manga: Manga, val chapter: Chapter) {
companion object {
fun mapper(
// manga
mangaId: Long,
source: Long,
mangaUrl: String,
@ -15,15 +16,17 @@ class MangaChapter(val manga: Manga, val chapter: Chapter) {
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Long,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewer: Long,
hideTitle: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// chapter
chapterId: Long,
_mangaId: Long,
chapterUrl: String,
@ -58,6 +61,7 @@ class MangaChapter(val manga: Manga, val chapter: Chapter) {
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
coverLastModified = coverLastModified,
),
Chapter.mapper(
id = chapterId,

View file

@ -81,6 +81,8 @@ open class MangaImpl : Manga {
override var ogGenre: String? = null
override var ogStatus: Int = 0
override var cover_last_modified: Long = 0L
override fun copyFrom(other: SManga) {
if (other is MangaImpl && other::ogTitle.isInitialized &&
other.title.isNotBlank() && other.ogTitle != ogTitle

View file

@ -42,4 +42,5 @@ object MangaTable {
const val COL_UPDATE_STRATEGY = "update_strategy"
const val COL_COVER_LAST_MODIFIED = "cover_last_modified"
}

View file

@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.prepareCoverUpdate
import eu.kanade.tachiyomi.data.download.DownloadJob
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.notification.Notifications
@ -55,6 +56,12 @@ import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.tryToSetForeground
import eu.kanade.tachiyomi.util.system.withIOContext
import java.io.File
import java.lang.ref.WeakReference
import java.util.Date
import java.util.concurrent.CancellationException
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
@ -79,11 +86,6 @@ import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.UpdateManga
import yokai.i18n.MR
import yokai.util.lang.getString
import java.io.File
import java.lang.ref.WeakReference
import java.util.*
import java.util.concurrent.*
import java.util.concurrent.atomic.*
class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
@ -267,11 +269,13 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
}
if (networkManga != null) {
val thumbnailUrl = manga.thumbnail_url
if (thumbnailUrl != networkManga.thumbnail_url) {
manga.prepareCoverUpdate()
}
manga.copyFrom(networkManga)
manga.initialized = true
val request: ImageRequest =
if (thumbnailUrl != manga.thumbnail_url) {
coverCache.deleteFromCache(thumbnailUrl)
// load new covers in background
ImageRequest.Builder(context).data(manga)
.memoryCachePolicy(CachePolicy.DISABLED).build()

View file

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter.Companion.copy
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.database.models.seriesType
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
@ -1260,7 +1261,7 @@ class LibraryPresenter(
val mangaToDelete = mangas.distinctBy { it.id }
mangaToDelete.forEach { manga ->
if (coverCacheToo) {
coverCache.deleteFromCache(manga)
manga.removeCover(coverCache)
}
val source = sourceManager.get(manga.source) as? HttpSource
if (source != null) {

View file

@ -20,9 +20,11 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.bookmarkedFilter
import eu.kanade.tachiyomi.data.database.models.chapterOrder
import eu.kanade.tachiyomi.data.database.models.downloadedFilter
import eu.kanade.tachiyomi.data.database.models.hasCustomCover
import eu.kanade.tachiyomi.data.database.models.prepareCoverUpdate
import eu.kanade.tachiyomi.data.database.models.readFilter
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.database.models.sortDescending
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
@ -59,6 +61,7 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchNonCancellableIO
import eu.kanade.tachiyomi.util.system.launchNow
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.withIOContext
@ -375,7 +378,6 @@ class MangaDetailsPresenter(
emptyList()
}
}
val thumbnailUrl = manga.thumbnail_url
val nManga = async(Dispatchers.IO) {
try {
source.getMangaDetails(manga.copy())
@ -387,12 +389,13 @@ class MangaDetailsPresenter(
val networkManga = nManga.await()
if (networkManga != null) {
if (manga.thumbnail_url != networkManga.thumbnail_url) {
manga.prepareCoverUpdate()
}
manga.copyFrom(networkManga)
manga.initialized = true
if (thumbnailUrl != networkManga.thumbnail_url) {
coverCache.deleteFromCache(thumbnailUrl)
}
db.insertManga(manga).executeAsBlocking()
launchIO {
@ -403,7 +406,6 @@ class MangaDetailsPresenter(
.build()
if (preferences.context.imageLoader.execute(request) is SuccessResult) {
coverCache.removeFromMemory(manga, manga.hasCustomCover(coverCache))
withContext(Dispatchers.Main) {
view?.setPaletteColor()
}
@ -735,7 +737,7 @@ class MangaDetailsPresenter(
fun confirmDeletion() {
launchIO {
coverCache.deleteFromCache(manga)
manga.removeCover(coverCache)
customMangaManager.saveMangaInfo(CustomMangaInfo(
mangaId = manga.id!!,
title = null,
@ -858,6 +860,7 @@ class MangaDetailsPresenter(
editCoverWithStream(uri)
} else if (resetCover) {
coverCache.deleteCustomCover(manga)
presenterScope.launchIO { manga.updateCoverLastModified() }
view?.setPaletteColor()
}
view?.updateHeader()
@ -881,12 +884,14 @@ class MangaDetailsPresenter(
downloadManager.context.contentResolver.openInputStream(uri) ?: return false
if (manga.isLocal()) {
LocalSource.updateCover(manga, inputStream)
presenterScope.launchNonCancellableIO { manga.updateCoverLastModified() }
view?.setPaletteColor()
return true
}
if (manga.favorite) {
coverCache.setCustomCoverToCache(manga, inputStream)
presenterScope.launchNonCancellableIO { manga.updateCoverLastModified() }
view?.setPaletteColor()
return true
}

View file

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
@ -229,6 +230,7 @@ class MigrationProcessAdapter(
if (MigrationFlags.hasCustomMangaInfo(flags)) {
if (coverCache.getCustomCoverFile(prevManga).exists()) {
coverCache.setCustomCoverToCache(manga, coverCache.getCustomCoverFile(prevManga).inputStream())
launchNow { manga.updateCoverLastModified() }
}
customMangaManager.getManga(prevManga)?.let { customManga ->
launchNow {

View file

@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.defaultReaderType
import eu.kanade.tachiyomi.data.database.models.orientationType
import eu.kanade.tachiyomi.data.database.models.readingModeType
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.download.model.Download
@ -938,14 +939,15 @@ class ReaderViewModel(
viewModelScope.launchNonCancellableIO {
val result = try {
if (manga.isLocal()) {
val context = Injekt.get<Application>()
coverCache.deleteFromCache(manga)
LocalSource.updateCover(manga, stream())
manga.updateCoverLastModified()
MR.strings.cover_updated
SetAsCoverResult.Success
} else {
if (manga.favorite) {
coverCache.setCustomCoverToCache(manga, stream())
manga.updateCoverLastModified()
SetAsCoverResult.Success
} else {
SetAsCoverResult.AddToLibraryFirst

View file

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.create
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.domain.manga.models.Manga
@ -283,7 +284,7 @@ open class BrowseSourcePresenter(
fun confirmDeletion(manga: Manga) {
launchIO {
coverCache.deleteFromCache(manga)
manga.removeCover(coverCache)
val downloadManager: DownloadManager = Injekt.get()
downloadManager.deleteManga(manga, source)
}

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.source.globalsearch
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.create
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.domain.manga.models.Manga
@ -16,6 +17,8 @@ import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.withUIContext
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
@ -26,7 +29,6 @@ import kotlinx.coroutines.sync.withPermit
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.*
/**
* Presenter of [GlobalSearchController]
@ -138,7 +140,7 @@ open class GlobalSearchPresenter(
}
fun confirmDeletion(manga: Manga) {
coverCache.deleteFromCache(manga)
manga.removeCover(coverCache)
val downloadManager: DownloadManager = Injekt.get()
sourceManager.get(manga.source)?.let { source ->
downloadManager.deleteManga(manga, source)

View file

@ -4,7 +4,6 @@ import co.touchlab.kermit.Logger
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.mapper
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.system.toInt
import kotlinx.coroutines.flow.Flow
import yokai.data.DatabaseHandler
import yokai.data.updateStrategyAdapter
@ -66,15 +65,16 @@ class MangaRepositoryImpl(private val handler: DatabaseHandler) : MangaRepositor
title = update.title,
status = update.status?.toLong(),
thumbnailUrl = update.thumbnailUrl,
favorite = update.favorite?.toInt()?.toLong(),
favorite = update.favorite,
lastUpdate = update.lastUpdate,
initialized = update.initialized,
viewer = update.viewerFlags?.toLong(),
hideTitle = update.hideTitle?.toInt()?.toLong(),
hideTitle = update.hideTitle,
chapterFlags = update.chapterFlags?.toLong(),
dateAdded = update.dateAdded,
filteredScanlators = update.filteredScanlators,
updateStrategy = update.updateStrategy?.let(updateStrategyAdapter::encode),
coverLastModified = update.coverLastModified,
mangaId = update.id,
)
}
@ -93,15 +93,16 @@ class MangaRepositoryImpl(private val handler: DatabaseHandler) : MangaRepositor
title = manga.title,
status = manga.status.toLong(),
thumbnailUrl = manga.thumbnail_url,
favorite = manga.favorite.toInt().toLong(),
favorite = manga.favorite,
lastUpdate = manga.last_update,
initialized = manga.initialized,
viewer = manga.viewer_flags.toLong(),
hideTitle = manga.hide_title.toInt().toLong(),
hideTitle = manga.hide_title,
chapterFlags = manga.chapter_flags.toLong(),
dateAdded = manga.date_added,
filteredScanlators = manga.filtered_scanlators,
updateStrategy = manga.update_strategy.let(updateStrategyAdapter::encode),
coverLastModified = manga.cover_last_modified,
)
mangasQueries.selectLastInsertedRowId()
}

View file

@ -5,6 +5,7 @@ import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch

View file

@ -1,5 +1,4 @@
import kotlin.Boolean;
import kotlin.Long;
CREATE TABLE mangas(
_id INTEGER NOT NULL PRIMARY KEY,
@ -12,15 +11,16 @@ CREATE TABLE mangas(
title TEXT NOT NULL,
status INTEGER NOT NULL,
thumbnail_url TEXT,
favorite INTEGER NOT NULL,
last_update INTEGER AS Long,
favorite INTEGER AS Boolean NOT NULL,
last_update INTEGER,
initialized INTEGER AS Boolean NOT NULL,
viewer INTEGER NOT NULL,
hide_title INTEGER NOT NULL,
hide_title INTEGER AS Boolean NOT NULL,
chapter_flags INTEGER NOT NULL,
date_added INTEGER AS Long,
date_added INTEGER,
filtered_scanlators TEXT,
update_strategy INTEGER NOT NULL DEFAULT 0
update_strategy INTEGER NOT NULL DEFAULT 0,
cover_last_modified INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX mangas_url_index ON mangas(url);
@ -51,8 +51,8 @@ FROM mangas
WHERE favorite = 1;
insert:
INSERT INTO mangas (source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, initialized, viewer, hide_title, chapter_flags, date_added, filtered_scanlators, update_strategy)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :initialized, :viewer, :hideTitle, :chapterFlags, :dateAdded, :filteredScanlators, :updateStrategy);
INSERT INTO mangas (source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, initialized, viewer, hide_title, chapter_flags, date_added, filtered_scanlators, update_strategy, cover_last_modified)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :initialized, :viewer, :hideTitle, :chapterFlags, :dateAdded, :filteredScanlators, :updateStrategy, :coverLastModified);
update:
UPDATE mangas SET
@ -73,7 +73,8 @@ UPDATE mangas SET
chapter_flags = coalesce(:chapterFlags, chapter_flags),
date_added = coalesce(:dateAdded, date_added),
filtered_scanlators = coalesce(:filteredScanlators, filtered_scanlators),
update_strategy = coalesce(:updateStrategy, update_strategy)
update_strategy = coalesce(:updateStrategy, update_strategy),
cover_last_modified = coalesce(:coverLastModified, cover_last_modified)
WHERE _id = :mangaId;
selectLastInsertedRowId:

View file

@ -0,0 +1,2 @@
ALTER TABLE mangas
ADD COLUMN cover_last_modified INTEGER NOT NULL DEFAULT 0;

View file

@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.domain.manga.models
import eu.kanade.tachiyomi.source.model.SManga
import yokai.domain.manga.models.MangaUpdate
import java.util.*
import java.util.Locale
import kotlin.collections.set
import yokai.domain.manga.models.MangaUpdate
// TODO: Transform into data class
interface Manga : SManga {
@ -33,6 +33,8 @@ interface Manga : SManga {
var ogGenre: String?
var ogStatus: Int
var cover_last_modified: Long
@Deprecated("Use ogTitle directly instead", ReplaceWith("ogTitle"))
val originalTitle: String
get() = ogTitle

View file

@ -22,4 +22,5 @@ data class MangaUpdate(
var chapterFlags: Int? = null,
var hideTitle: Boolean? = null,
var filteredScanlators: String? = null,
var coverLastModified: Long? = null,
)