diff --git a/app/src/main/java/org/oxycblt/auxio/image/CoverProvider.kt b/app/src/main/java/org/oxycblt/auxio/image/CoverProvider.kt index 17ec8148a..d12fb8cc2 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/CoverProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/CoverProvider.kt @@ -25,14 +25,10 @@ import android.database.Cursor import android.net.Uri import android.os.ParcelFileDescriptor import kotlinx.coroutines.runBlocking -import org.oxycblt.auxio.image.covers.MutableSiloedCovers import org.oxycblt.auxio.image.covers.SiloedCoverId -import org.oxycblt.musikr.cover.CoverIdentifier +import org.oxycblt.auxio.image.covers.SiloedCovers import org.oxycblt.musikr.cover.ObtainResult -// AndroidManifest.xml addition - -// ImageProvider.java class CoverProvider : ContentProvider() { override fun onCreate(): Boolean = true @@ -42,8 +38,7 @@ class CoverProvider : ContentProvider() { val id = requireNotNull(uri.lastPathSegment) { "No ID in URI: $uri" } val coverId = requireNotNull(SiloedCoverId.parse(id)) { "Invalid ID: $id" } return runBlocking { - val siloedCovers = - MutableSiloedCovers.from(requireContext(), coverId.silo, CoverIdentifier.md5()) + val siloedCovers = SiloedCovers.from(requireContext(), coverId.silo) when (val res = siloedCovers.obtain(coverId.id)) { is ObtainResult.Hit -> res.cover.fd() is ObtainResult.Miss -> null diff --git a/app/src/main/java/org/oxycblt/auxio/image/covers/MutableSiloedCovers.kt b/app/src/main/java/org/oxycblt/auxio/image/covers/SiloedCovers.kt similarity index 71% rename from app/src/main/java/org/oxycblt/auxio/image/covers/MutableSiloedCovers.kt rename to app/src/main/java/org/oxycblt/auxio/image/covers/SiloedCovers.kt index 1330c765f..3ff8a90d3 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/covers/MutableSiloedCovers.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/covers/SiloedCovers.kt @@ -1,6 +1,6 @@ /* * Copyright (c) 2024 Auxio Project - * MutableSiloedCovers.kt is part of Auxio. + * SiloedCovers.kt is part of Auxio. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,31 +25,42 @@ import kotlinx.coroutines.withContext import org.oxycblt.musikr.cover.Cover import org.oxycblt.musikr.cover.CoverFormat import org.oxycblt.musikr.cover.CoverIdentifier +import org.oxycblt.musikr.cover.Covers import org.oxycblt.musikr.cover.FileCover import org.oxycblt.musikr.cover.FileCovers import org.oxycblt.musikr.cover.MutableCovers +import org.oxycblt.musikr.cover.MutableFileCovers import org.oxycblt.musikr.cover.ObtainResult import org.oxycblt.musikr.fs.app.AppFiles -class MutableSiloedCovers -private constructor( - private val rootDir: File, - private val silo: CoverSilo, - private val inner: FileCovers -) : MutableCovers { +open class SiloedCovers(private val silo: CoverSilo, private val fileCovers: FileCovers) : Covers { override suspend fun obtain(id: String): ObtainResult { val coverId = SiloedCoverId.parse(id) ?: return ObtainResult.Miss() if (coverId.silo != silo) return ObtainResult.Miss() - return when (val result = inner.obtain(coverId.id)) { + return when (val result = fileCovers.obtain(coverId.id)) { is ObtainResult.Hit -> ObtainResult.Hit(SiloedCover(silo, result.cover)) is ObtainResult.Miss -> ObtainResult.Miss() } } - override suspend fun write(data: ByteArray) = SiloedCover(silo, inner.write(data)) + companion object { + suspend fun from(context: Context, silo: CoverSilo): SiloedCovers { + val core = SiloCore.from(context, silo) + return SiloedCovers(silo, FileCovers(core.files, core.format)) + } + } +} + +class MutableSiloedCovers +private constructor( + private val rootDir: File, + private val silo: CoverSilo, + private val fileCovers: MutableFileCovers +) : SiloedCovers(silo, fileCovers), MutableCovers { + override suspend fun write(data: ByteArray) = SiloedCover(silo, fileCovers.write(data)) override suspend fun cleanup(excluding: Collection) { - inner.cleanup(excluding.filterIsInstance().map { it.innerCover }) + fileCovers.cleanup(excluding.filterIsInstance().map { it.innerCover }) // Destroy old revisions no longer being used. withContext(Dispatchers.IO) { @@ -62,17 +73,11 @@ private constructor( suspend fun from( context: Context, silo: CoverSilo, - identifier: CoverIdentifier + coverIdentifier: CoverIdentifier ): MutableSiloedCovers { - val rootDir: File - val revisionDir: File - withContext(Dispatchers.IO) { - rootDir = context.coversDir() - revisionDir = rootDir.resolve(silo.toString()).apply { mkdirs() } - } - val files = AppFiles.at(revisionDir) - val format = CoverFormat.jpeg(silo.params) - return MutableSiloedCovers(rootDir, silo, FileCovers(files, format, identifier)) + val core = SiloCore.from(context, silo) + return MutableSiloedCovers( + core.rootDir, silo, MutableFileCovers(core.files, core.format, coverIdentifier)) } } } @@ -94,3 +99,19 @@ data class SiloedCoverId(val silo: CoverSilo, val id: String) { } } } + +private data class SiloCore(val rootDir: File, val files: AppFiles, val format: CoverFormat) { + companion object { + suspend fun from(context: Context, silo: CoverSilo): SiloCore { + val rootDir: File + val revisionDir: File + withContext(Dispatchers.IO) { + rootDir = context.coversDir() + revisionDir = rootDir.resolve(silo.toString()).apply { mkdirs() } + } + val files = AppFiles.at(revisionDir) + val format = CoverFormat.jpeg(silo.params) + return SiloCore(rootDir, files, format) + } + } +} diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt index 1864fc905..5a8a54195 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt @@ -22,11 +22,8 @@ import android.os.ParcelFileDescriptor import org.oxycblt.musikr.fs.app.AppFile import org.oxycblt.musikr.fs.app.AppFiles -class FileCovers( - private val appFiles: AppFiles, - private val coverFormat: CoverFormat, - private val coverIdentifier: CoverIdentifier, -) : Covers, MutableCovers { +open class FileCovers(private val appFiles: AppFiles, private val coverFormat: CoverFormat) : + Covers { override suspend fun obtain(id: String): ObtainResult { val file = appFiles.find(getFileName(id)) return if (file != null) { @@ -36,6 +33,14 @@ class FileCovers( } } + protected fun getFileName(id: String) = "$id.${coverFormat.extension}" +} + +class MutableFileCovers( + private val appFiles: AppFiles, + private val coverFormat: CoverFormat, + private val coverIdentifier: CoverIdentifier +) : FileCovers(appFiles, coverFormat), MutableCovers { override suspend fun write(data: ByteArray): FileCover { val id = coverIdentifier.identify(data) val file = appFiles.write(getFileName(id)) { coverFormat.transcodeInto(data, it) } @@ -46,8 +51,6 @@ class FileCovers( val used = excluding.mapTo(mutableSetOf()) { getFileName(it.id) } appFiles.deleteWhere { it !in used } } - - private fun getFileName(id: String) = "$id.${coverFormat.extension}" } interface FileCover : Cover {