mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 02:34:39 +00:00
refactor(cover): Data class for manga cover
This commit is contained in:
parent
8ad123956c
commit
839f762fa7
20 changed files with 191 additions and 80 deletions
|
@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
|||
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
|
||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
||||
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
|
||||
import eu.kanade.tachiyomi.data.coil.MangaKeyer
|
||||
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
|
@ -243,8 +244,10 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
|
|||
add(TachiyomiImageDecoder.Factory())
|
||||
// Fetcher.Factory
|
||||
add(BufferedSourceFetcher.Factory())
|
||||
add(MangaCoverFetcher.Factory(callFactoryLazy))
|
||||
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
|
||||
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
|
||||
// Keyer
|
||||
add(MangaKeyer())
|
||||
add(MangaCoverKeyer())
|
||||
}
|
||||
crossfade(true)
|
||||
|
|
|
@ -37,10 +37,12 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
|
|||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import kotlin.math.min
|
||||
import kotlinx.coroutines.MainScope
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
import yokai.domain.manga.models.cover
|
||||
|
||||
class UpdatesGridGlanceWidget : GlanceAppWidget() {
|
||||
private val app: Application by injectLazy()
|
||||
|
@ -97,7 +99,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() {
|
|||
.map { it.first }
|
||||
.map { updatesView ->
|
||||
val request = ImageRequest.Builder(app)
|
||||
.data(updatesView)
|
||||
.data(updatesView.cover())
|
||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||
.precision(Precision.EXACT)
|
||||
.size(widthPx, heightPx)
|
||||
|
|
|
@ -11,8 +11,6 @@ import androidx.glance.layout.ContentScale
|
|||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.size
|
||||
import eu.kanade.tachiyomi.R
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import eu.kanade.tachiyomi.appwidget.util.appWidgetInnerRadius
|
||||
|
||||
val CoverWidth = 58.dp
|
||||
|
|
|
@ -158,8 +158,10 @@ class CoverCache(val context: Context) {
|
|||
* @param manga the manga.
|
||||
* @return cover image.
|
||||
*/
|
||||
fun getCustomCoverFile(manga: Manga): File {
|
||||
return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(manga.id.toString()))
|
||||
fun getCustomCoverFile(manga: Manga): File = getCustomCoverFile(manga.id)
|
||||
|
||||
fun getCustomCoverFile(mangaId: Long?): File {
|
||||
return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(mangaId.toString()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,29 +11,29 @@ 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
|
||||
import yokai.domain.manga.models.MangaCover
|
||||
|
||||
class LibraryMangaImageTarget(
|
||||
override val view: ImageView,
|
||||
val manga: Manga,
|
||||
val cover: MangaCover,
|
||||
) : ImageViewTarget(view) {
|
||||
|
||||
private val coverCache: CoverCache by injectLazy()
|
||||
|
||||
override fun onError(error: Image?) {
|
||||
super.onError(error)
|
||||
if (manga.favorite) {
|
||||
if (cover.inLibrary) {
|
||||
launchIO {
|
||||
val file = coverCache.getCoverFile(manga.thumbnail_url, false)
|
||||
val file = coverCache.getCoverFile(cover.url, false)
|
||||
// if the file exists and the there was still an error then the file is corrupted
|
||||
if (file != null && file.exists()) {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeFile(file.path, options)
|
||||
if (options.outWidth == -1 || options.outHeight == -1) {
|
||||
manga.updateCoverLastModified()
|
||||
cover.updateCoverLastModified()
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
@ -44,13 +44,13 @@ class LibraryMangaImageTarget(
|
|||
|
||||
@JvmSynthetic
|
||||
inline fun ImageView.loadManga(
|
||||
manga: Manga,
|
||||
cover: MangaCover,
|
||||
imageLoader: ImageLoader = context.imageLoader,
|
||||
builder: ImageRequest.Builder.() -> Unit = {},
|
||||
): Disposable {
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(manga)
|
||||
.target(LibraryMangaImageTarget(this, manga))
|
||||
.data(cover)
|
||||
.target(LibraryMangaImageTarget(this, cover))
|
||||
.apply(builder)
|
||||
.build()
|
||||
return imageLoader.enqueue(request)
|
||||
|
|
|
@ -39,9 +39,12 @@ import okio.buffer
|
|||
import okio.sink
|
||||
import okio.source
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.domain.manga.models.MangaCover
|
||||
|
||||
class MangaCoverFetcher(
|
||||
private val manga: Manga,
|
||||
private val mangaId: Long?,
|
||||
private val url: String?,
|
||||
private val isInLibrary: Boolean,
|
||||
private val sourceLazy: Lazy<HttpSource?>,
|
||||
private val options: Options,
|
||||
private val callFactoryLazy: Lazy<Call.Factory>,
|
||||
|
@ -53,7 +56,6 @@ class MangaCoverFetcher(
|
|||
|
||||
private val diskCacheKey: String
|
||||
get() = diskCacheKeyLazy.value
|
||||
private lateinit var url: String
|
||||
|
||||
private val fileScope = CoroutineScope(Job() + Dispatchers.IO)
|
||||
|
||||
|
@ -61,21 +63,21 @@ class MangaCoverFetcher(
|
|||
if (options.extras.getOrDefault(USE_CUSTOM_COVER_KEY)) {
|
||||
val customCoverFile = customCoverFileLazy.value
|
||||
if (customCoverFile.exists()) {
|
||||
setRatioAndColorsInScope(manga, UniFile.fromFile(customCoverFile))
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(customCoverFile))
|
||||
return fileLoader(customCoverFile)
|
||||
}
|
||||
}
|
||||
|
||||
url = manga.thumbnail_url ?: error("No cover specified")
|
||||
if (url == null) error("No cover specified")
|
||||
return when (getResourceType(url)) {
|
||||
Type.URL -> httpLoader()
|
||||
Type.File -> {
|
||||
val file = File(url.substringAfter("file://"))
|
||||
setRatioAndColorsInScope(manga, UniFile.fromFile(file))
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(file))
|
||||
fileLoader(file)
|
||||
}
|
||||
Type.URI -> {
|
||||
setRatioAndColorsInScope(manga, UniFile.fromUri(options.context, url.toUri()))
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromUri(options.context, url.toUri()))
|
||||
fileUriLoader(url)
|
||||
}
|
||||
null -> error("Invalid image")
|
||||
|
@ -109,10 +111,10 @@ class MangaCoverFetcher(
|
|||
private suspend fun httpLoader(): FetchResult {
|
||||
val coverFile = coverFileLazy.value
|
||||
if (coverFile?.exists() == true && options.diskCachePolicy.readEnabled) {
|
||||
if (!manga.favorite) {
|
||||
if (!isInLibrary) {
|
||||
coverFile.setLastModified(Date().time)
|
||||
}
|
||||
setRatioAndColorsInScope(manga, UniFile.fromFile(coverFile))
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(coverFile))
|
||||
return fileLoader(coverFile)
|
||||
}
|
||||
|
||||
|
@ -123,12 +125,12 @@ class MangaCoverFetcher(
|
|||
val snapshotCoverCache = moveSnapshotToCoverCache(snapshot, coverFile)
|
||||
if (snapshotCoverCache != null) {
|
||||
// Read from cover cache after added to library
|
||||
setRatioAndColorsInScope(manga, UniFile.fromFile(snapshotCoverCache))
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(snapshotCoverCache))
|
||||
return fileLoader(snapshotCoverCache)
|
||||
}
|
||||
|
||||
// Read from snapshot
|
||||
setRatioAndColorsInScope(manga)
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary)
|
||||
return SourceFetchResult(
|
||||
source = snapshot.toImageSource(),
|
||||
mimeType = "image/*",
|
||||
|
@ -142,7 +144,7 @@ class MangaCoverFetcher(
|
|||
try {
|
||||
// Read from cover cache after library manga cover updated
|
||||
val responseCoverCache = writeResponseToCoverCache(response, coverFile)
|
||||
setRatioAndColorsInScope(manga)
|
||||
setRatioAndColorsInScope(mangaId, url, isInLibrary)
|
||||
if (responseCoverCache != null) {
|
||||
return fileLoader(responseCoverCache)
|
||||
}
|
||||
|
@ -185,7 +187,7 @@ class MangaCoverFetcher(
|
|||
|
||||
private fun newRequest(): Request {
|
||||
val request = Request.Builder().apply {
|
||||
url(url)
|
||||
url(url!!)
|
||||
|
||||
val sourceHeaders = sourceLazy.value?.headers
|
||||
if (sourceHeaders != null)
|
||||
|
@ -293,9 +295,9 @@ class MangaCoverFetcher(
|
|||
)
|
||||
}
|
||||
|
||||
private fun setRatioAndColorsInScope(manga: Manga, ogFile: UniFile? = null, force: Boolean = false) {
|
||||
private fun setRatioAndColorsInScope(mangaId: Long?, mangaThumbnailUrl: String?, isInLibrary: Boolean, ogFile: UniFile? = null, force: Boolean = false) {
|
||||
fileScope.launch {
|
||||
MangaCoverMetadata.setRatioAndColors(manga, ogFile, force)
|
||||
MangaCoverMetadata.setRatioAndColors(mangaId, mangaThumbnailUrl, isInLibrary, ogFile, force)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +326,7 @@ class MangaCoverFetcher(
|
|||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
class MangaFactory(
|
||||
private val callFactoryLazy: Lazy<Call.Factory>,
|
||||
) : Fetcher.Factory<Manga> {
|
||||
|
||||
|
@ -333,7 +335,9 @@ class MangaCoverFetcher(
|
|||
|
||||
override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher {
|
||||
return MangaCoverFetcher(
|
||||
manga = data,
|
||||
mangaId = data.id,
|
||||
url = data.thumbnail_url,
|
||||
isInLibrary = data.favorite,
|
||||
sourceLazy = lazy { sourceManager.get(data.source) as? HttpSource },
|
||||
options = options,
|
||||
callFactoryLazy = callFactoryLazy,
|
||||
|
@ -345,6 +349,29 @@ class MangaCoverFetcher(
|
|||
}
|
||||
}
|
||||
|
||||
class MangaCoverFactory(
|
||||
private val callFactoryLazy: Lazy<Call.Factory>,
|
||||
) : Fetcher.Factory<MangaCover> {
|
||||
|
||||
private val coverCache: CoverCache by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher {
|
||||
return MangaCoverFetcher(
|
||||
mangaId = data.mangaId,
|
||||
url = data.url,
|
||||
isInLibrary = data.inLibrary,
|
||||
sourceLazy = lazy { sourceManager.get(data.sourceId) as? HttpSource },
|
||||
options = options,
|
||||
callFactoryLazy = callFactoryLazy,
|
||||
diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! },
|
||||
coverFileLazy = lazy { coverCache.getCoverFile(data.url, !data.inLibrary) },
|
||||
customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.mangaId) },
|
||||
imageLoader = imageLoader,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Type {
|
||||
File, URL, URI;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,15 @@ package eu.kanade.tachiyomi.data.coil
|
|||
|
||||
import coil3.key.Keyer
|
||||
import coil3.request.Options
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.database.models.hasCustomCover
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.domain.manga.models.MangaCover
|
||||
|
||||
class MangaCoverKeyer : Keyer<Manga> {
|
||||
class MangaKeyer : Keyer<Manga> {
|
||||
override fun key(data: Manga, options: Options): String {
|
||||
val key = when {
|
||||
data.hasCustomCover() -> data.id
|
||||
|
@ -17,3 +21,15 @@ class MangaCoverKeyer : Keyer<Manga> {
|
|||
return "${key};${data.cover_last_modified}"
|
||||
}
|
||||
}
|
||||
|
||||
class MangaCoverKeyer(private val coverCache: CoverCache = Injekt.get()) : Keyer<MangaCover> {
|
||||
override fun key(data: MangaCover, options: Options): String {
|
||||
val key = when {
|
||||
coverCache.getCustomCoverFile(data.mangaId).exists() -> data.mangaId
|
||||
data.inLibrary -> DiskUtil.hashKeyForDisk(data.url)
|
||||
else -> data.url
|
||||
}
|
||||
|
||||
return "${key};${data.lastModified}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ 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.MangaCover
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
|
@ -175,6 +176,12 @@ var Manga.dominantCoverColors: Pair<Int, Int>?
|
|||
MangaCoverMetadata.addCoverColor(this, value.first, value.second)
|
||||
}
|
||||
|
||||
var Manga.vibrantCoverColor: Int?
|
||||
get() = MangaCoverMetadata.getVibrantColor(id)
|
||||
set(value) {
|
||||
id?.let { MangaCoverMetadata.setVibrantColor(it, value) }
|
||||
}
|
||||
|
||||
fun Manga.Companion.create(source: Long) = MangaImpl().apply {
|
||||
this.source = source
|
||||
}
|
||||
|
@ -269,3 +276,7 @@ suspend fun Manga.updateCoverLastModified(updateManga: UpdateManga = Injekt.get(
|
|||
cover_last_modified = System.currentTimeMillis()
|
||||
updateManga.await(MangaUpdate(id = id!!, coverLastModified = cover_last_modified))
|
||||
}
|
||||
|
||||
suspend fun MangaCover.updateCoverLastModified(updateManga: UpdateManga = Injekt.get()) {
|
||||
updateManga.await(MangaUpdate(id = mangaId!!, coverLastModified = System.currentTimeMillis()))
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
|||
import eu.kanade.tachiyomi.util.view.backgroundColor
|
||||
import eu.kanade.tachiyomi.util.view.setCards
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import yokai.domain.manga.models.cover
|
||||
|
||||
/**
|
||||
* Class used to hold the displayed data of a manga in the library, like the cover or the title.
|
||||
|
@ -125,7 +126,7 @@ class LibraryGridHolder(
|
|||
|
||||
private fun setCover(manga: Manga) {
|
||||
if ((adapter.recyclerView.context as? Activity)?.isDestroyed == true) return
|
||||
binding.coverThumbnail.loadManga(manga) {
|
||||
binding.coverThumbnail.loadManga(manga.cover()) {
|
||||
val hasRatio = binding.coverThumbnail.layoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
if (!fixedSize && !hasRatio) {
|
||||
precision(Precision.INEXACT)
|
||||
|
|
|
@ -5,15 +5,14 @@ import android.view.ViewGroup
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import coil3.dispose
|
||||
import eu.kanade.tachiyomi.R
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import eu.kanade.tachiyomi.data.coil.loadManga
|
||||
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
||||
import eu.kanade.tachiyomi.util.lang.highlightText
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.view.setCards
|
||||
import yokai.domain.manga.models.cover
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
|
||||
/**
|
||||
* Class used to hold the displayed data of a manga in the library, like the cover or the binding.title.
|
||||
|
@ -97,7 +96,7 @@ class LibraryListHolder(
|
|||
|
||||
// Update the cover.
|
||||
binding.coverThumbnail.dispose()
|
||||
binding.coverThumbnail.loadManga(item.manga)
|
||||
binding.coverThumbnail.loadManga(item.manga.cover())
|
||||
}
|
||||
|
||||
override fun onActionStateChanged(position: Int, actionState: Int) {
|
||||
|
|
|
@ -1633,7 +1633,7 @@ class LibraryPresenter(
|
|||
) {
|
||||
val libraryManga = getManga.awaitFavorites()
|
||||
libraryManga.forEach { manga ->
|
||||
try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga) } } catch (_: Exception) { }
|
||||
try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga.id, manga.thumbnail_url, manga.favorite) } } catch (_: Exception) { }
|
||||
}
|
||||
MangaCoverMetadata.savePrefs()
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
|||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.domain.manga.models.cover
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import android.R as AR
|
||||
|
@ -98,7 +99,7 @@ class EditMangaDialog : DialogController {
|
|||
fun onViewCreated() {
|
||||
val context = binding.root.context
|
||||
|
||||
binding.mangaCover.loadManga(manga)
|
||||
binding.mangaCover.loadManga(manga.cover())
|
||||
val isLocal = manga.isLocal()
|
||||
|
||||
binding.mangaLang.isVisible = isLocal
|
||||
|
|
|
@ -61,6 +61,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.seriesType
|
||||
import eu.kanade.tachiyomi.data.database.models.vibrantCoverColor
|
||||
import eu.kanade.tachiyomi.data.download.DownloadJob
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
|
|
|
@ -47,6 +47,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
|||
import eu.kanade.tachiyomi.util.system.isInNightMode
|
||||
import eu.kanade.tachiyomi.util.system.isLTR
|
||||
import eu.kanade.tachiyomi.util.view.resetStrokeColor
|
||||
import yokai.domain.manga.models.cover
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import android.R as AR
|
||||
|
@ -671,7 +672,7 @@ class MangaHeaderHolder(
|
|||
if (!manga.initialized) return
|
||||
val drawable = adapter.controller.binding.mangaCoverFull.drawable
|
||||
binding.mangaCover.loadManga(
|
||||
manga,
|
||||
manga.cover(),
|
||||
builder = {
|
||||
placeholder(drawable)
|
||||
error(drawable)
|
||||
|
@ -680,7 +681,7 @@ class MangaHeaderHolder(
|
|||
},
|
||||
)
|
||||
binding.backdrop.loadManga(
|
||||
manga,
|
||||
manga.cover(),
|
||||
builder = {
|
||||
placeholder(drawable)
|
||||
error(drawable)
|
||||
|
|
|
@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.coil.loadManga
|
|||
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.util.view.setCards
|
||||
import yokai.domain.manga.models.cover
|
||||
|
||||
class MangaHolder(
|
||||
view: View,
|
||||
|
@ -29,6 +30,6 @@ class MangaHolder(
|
|||
|
||||
// Update the cover.
|
||||
binding.coverThumbnail.dispose()
|
||||
binding.coverThumbnail.loadManga(item.manga)
|
||||
binding.coverThumbnail.loadManga(item.manga.cover())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@ import androidx.core.view.updatePaddingRelative
|
|||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
import eu.kanade.tachiyomi.R
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import eu.kanade.tachiyomi.data.coil.loadManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.ChapterHistory
|
||||
|
@ -37,6 +34,9 @@ import eu.kanade.tachiyomi.util.view.setAnimVectorCompat
|
|||
import eu.kanade.tachiyomi.util.view.setCards
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
import yokai.domain.manga.models.cover
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import android.R as AR
|
||||
|
||||
class RecentMangaHolder(
|
||||
|
@ -213,7 +213,7 @@ class RecentMangaHolder(
|
|||
else -> context.timeSpanFromNow(MR.strings.read_, item.mch.history.last_read)
|
||||
}
|
||||
if ((context as? Activity)?.isDestroyed != true) {
|
||||
binding.coverThumbnail.loadManga(item.mch.manga)
|
||||
binding.coverThumbnail.loadManga(item.mch.manga.cover())
|
||||
}
|
||||
if (!item.mch.manga.isLocal()) {
|
||||
notifyStatus(
|
||||
|
|
|
@ -6,7 +6,6 @@ import androidx.palette.graphics.Palette
|
|||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.coil.getBestColor
|
||||
import eu.kanade.tachiyomi.data.database.models.dominantCoverColors
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
@ -16,6 +15,7 @@ import uy.kohesive.injekt.injectLazy
|
|||
object MangaCoverMetadata {
|
||||
private var coverRatioMap = ConcurrentHashMap<Long, Float>()
|
||||
private var coverColorMap = ConcurrentHashMap<Long, Pair<Int, Int>>()
|
||||
private var vibrantCoverColorMap = ConcurrentHashMap<Long, Int>()
|
||||
private val preferences by injectLazy<PreferencesHelper>()
|
||||
private val coverCache by injectLazy<CoverCache>()
|
||||
|
||||
|
@ -49,19 +49,19 @@ object MangaCoverMetadata {
|
|||
)
|
||||
}
|
||||
|
||||
fun setRatioAndColors(manga: Manga, ogFile: UniFile? = null, force: Boolean = false) {
|
||||
if (!manga.favorite) {
|
||||
remove(manga)
|
||||
fun setRatioAndColors(mangaId: Long?, mangaThumbnailUrl: String?, isInLibrary: Boolean, ogFile: UniFile? = null, force: Boolean = false) {
|
||||
if (!isInLibrary) {
|
||||
remove(mangaId)
|
||||
}
|
||||
if (manga.vibrantCoverColor != null && !manga.favorite) return
|
||||
if (getVibrantColor(mangaId) != null && !isInLibrary) return
|
||||
val file = ogFile
|
||||
?: UniFile.fromFile(coverCache.getCustomCoverFile(manga))?.takeIf { it.exists() }
|
||||
?: UniFile.fromFile(coverCache.getCoverFile(manga.thumbnail_url, !manga.favorite))
|
||||
?: UniFile.fromFile(coverCache.getCustomCoverFile(mangaId))?.takeIf { it.exists() }
|
||||
?: UniFile.fromFile(coverCache.getCoverFile(mangaThumbnailUrl, !isInLibrary))
|
||||
// if the file exists and the there was still an error then the file is corrupted
|
||||
if (file?.exists() == true) {
|
||||
val options = BitmapFactory.Options()
|
||||
val hasVibrantColor = if (manga.favorite) manga.vibrantCoverColor != null else true
|
||||
if (manga.dominantCoverColors != null && hasVibrantColor && !force) {
|
||||
val hasVibrantColor = if (isInLibrary) vibrantCoverColorMap[mangaId] != null else true
|
||||
if (getColors(mangaId) != null && hasVibrantColor && !force) {
|
||||
options.inJustDecodeBounds = true
|
||||
} else {
|
||||
options.inSampleSize = 4
|
||||
|
@ -70,45 +70,74 @@ object MangaCoverMetadata {
|
|||
if (bitmap != null) {
|
||||
Palette.from(bitmap).generate {
|
||||
if (it == null) return@generate
|
||||
if (manga.favorite) {
|
||||
if (isInLibrary) {
|
||||
it.dominantSwatch?.let { swatch ->
|
||||
manga.dominantCoverColors = swatch.rgb to swatch.titleTextColor
|
||||
addCoverColor(mangaId, swatch.rgb, swatch.titleTextColor)
|
||||
}
|
||||
}
|
||||
val color = it.getBestColor() ?: return@generate
|
||||
manga.vibrantCoverColor = color
|
||||
setVibrantColor(mangaId, color)
|
||||
}
|
||||
}
|
||||
if (manga.favorite && !(options.outWidth == -1 || options.outHeight == -1)) {
|
||||
addCoverRatio(manga, options.outWidth / options.outHeight.toFloat())
|
||||
if (isInLibrary && !(options.outWidth == -1 || options.outHeight == -1)) {
|
||||
addCoverRatio(mangaId, options.outWidth / options.outHeight.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(manga: Manga) {
|
||||
val id = manga.id ?: return
|
||||
coverRatioMap.remove(id)
|
||||
coverColorMap.remove(id)
|
||||
remove(manga.id)
|
||||
}
|
||||
|
||||
fun remove(mangaId: Long?) {
|
||||
mangaId ?: return
|
||||
coverRatioMap.remove(mangaId)
|
||||
coverColorMap.remove(mangaId)
|
||||
}
|
||||
|
||||
fun addCoverRatio(manga: Manga, ratio: Float) {
|
||||
val id = manga.id ?: return
|
||||
coverRatioMap[id] = ratio
|
||||
addCoverRatio(manga.id, ratio)
|
||||
}
|
||||
|
||||
fun addCoverRatio(mangaId: Long?, ratio: Float) {
|
||||
mangaId ?: return
|
||||
coverRatioMap[mangaId] = ratio
|
||||
}
|
||||
|
||||
fun addCoverColor(manga: Manga, @ColorInt color: Int, @ColorInt textColor: Int) {
|
||||
val id = manga.id ?: return
|
||||
coverColorMap[id] = color to textColor
|
||||
addCoverColor(manga.id, color, textColor)
|
||||
}
|
||||
|
||||
fun getColors(manga: Manga): Pair<Int, Int>? {
|
||||
return coverColorMap[manga.id]
|
||||
fun addCoverColor(mangaId: Long?, @ColorInt color: Int, @ColorInt textColor: Int) {
|
||||
mangaId ?: return
|
||||
coverColorMap[mangaId] = color to textColor
|
||||
}
|
||||
|
||||
fun getColors(manga: Manga): Pair<Int, Int>? = getColors(manga.id)
|
||||
|
||||
fun getColors(mangaId: Long?): Pair<Int, Int>? {
|
||||
return coverColorMap[mangaId]
|
||||
}
|
||||
|
||||
fun getRatio(manga: Manga): Float? {
|
||||
return coverRatioMap[manga.id]
|
||||
}
|
||||
|
||||
fun setVibrantColor(mangaId: Long?, @ColorInt color: Int?) {
|
||||
mangaId ?: return
|
||||
|
||||
if (color == null) {
|
||||
vibrantCoverColorMap.remove(mangaId)
|
||||
return
|
||||
}
|
||||
|
||||
vibrantCoverColorMap[mangaId] = color
|
||||
}
|
||||
|
||||
fun getVibrantColor(mangaId: Long?): Int? {
|
||||
return vibrantCoverColorMap[mangaId]
|
||||
}
|
||||
|
||||
fun savePrefs() {
|
||||
val mapCopy = coverRatioMap.toMap()
|
||||
preferences.coverRatios().set(mapCopy.map { "${it.key}|${it.value}" }.toSet())
|
||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.domain.manga.models
|
|||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import java.util.Locale
|
||||
import kotlin.collections.set
|
||||
import yokai.domain.manga.models.MangaUpdate
|
||||
|
||||
// TODO: Transform into data class
|
||||
|
@ -197,12 +196,6 @@ interface Manga : SManga {
|
|||
get() = chapter_flags and CHAPTER_SORTING_MASK
|
||||
set(sort) = setChapterFlags(sort, CHAPTER_SORTING_MASK)
|
||||
|
||||
var vibrantCoverColor: Int?
|
||||
get() = vibrantCoverColorMap[id]
|
||||
set(value) {
|
||||
id?.let { vibrantCoverColorMap[it] = value }
|
||||
}
|
||||
|
||||
fun toMangaUpdate(): MangaUpdate {
|
||||
return MangaUpdate(
|
||||
id = id!!,
|
||||
|
@ -268,7 +261,5 @@ interface Manga : SManga {
|
|||
const val TYPE_MANHUA = 3
|
||||
const val TYPE_COMIC = 4
|
||||
const val TYPE_WEBTOON = 5
|
||||
|
||||
private val vibrantCoverColorMap: HashMap<Long, Int?> = hashMapOf()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,5 @@ data class Manga(
|
|||
var chapterFlags: Int,
|
||||
var hideTitle: Boolean,
|
||||
var filteredScanlators: String?,
|
||||
var coverLastModified: Long,
|
||||
): Serializable
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package yokai.domain.manga.models
|
||||
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga as TachiManga
|
||||
|
||||
data class MangaCover(
|
||||
val mangaId: Long?,
|
||||
val sourceId: Long,
|
||||
val url: String,
|
||||
val lastModified: Long,
|
||||
val inLibrary: Boolean,
|
||||
)
|
||||
|
||||
fun TachiManga.cover() = MangaCover(
|
||||
mangaId = id,
|
||||
sourceId = source,
|
||||
url = thumbnail_url ?: "",
|
||||
lastModified = cover_last_modified,
|
||||
inLibrary = favorite,
|
||||
)
|
||||
|
||||
fun Manga.cover() = MangaCover(
|
||||
mangaId = id,
|
||||
sourceId = source,
|
||||
url = thumbnailUrl ?: "",
|
||||
lastModified = coverLastModified,
|
||||
inLibrary = favorite,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue