mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 02:34:39 +00:00
refactor: Add cover_last_modified
This commit is contained in:
parent
653b2d7839
commit
c9b302ab21
23 changed files with 158 additions and 104 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,4 +42,5 @@ object MangaTable {
|
|||
|
||||
const val COL_UPDATE_STRATEGY = "update_strategy"
|
||||
|
||||
const val COL_COVER_LAST_MODIFIED = "cover_last_modified"
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE mangas
|
||||
ADD COLUMN cover_last_modified INTEGER NOT NULL DEFAULT 0;
|
|
@ -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
|
||||
|
|
|
@ -22,4 +22,5 @@ data class MangaUpdate(
|
|||
var chapterFlags: Int? = null,
|
||||
var hideTitle: Boolean? = null,
|
||||
var filteredScanlators: String? = null,
|
||||
var coverLastModified: Long? = null,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue