musikr: make cover creation more flexible

Enables some compat cover changes I need to make.
This commit is contained in:
Alexander Capehart 2025-02-26 14:51:23 -07:00
parent 403f93b6df
commit 25901a0f76
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
10 changed files with 51 additions and 37 deletions

View file

@ -29,7 +29,7 @@ import kotlinx.coroutines.runBlocking
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.image.covers.SiloedCoverId
import org.oxycblt.auxio.image.covers.SiloedCovers
import org.oxycblt.musikr.cover.ObtainResult
import org.oxycblt.musikr.cover.CoverResult
class CoverProvider : ContentProvider() {
override fun onCreate(): Boolean = true
@ -43,8 +43,8 @@ class CoverProvider : ContentProvider() {
return runBlocking {
val siloedCovers = SiloedCovers.from(requireNotNull(context), coverId.silo)
when (val res = siloedCovers.obtain(id)) {
is ObtainResult.Hit -> res.cover.fd()
is ObtainResult.Miss -> null
is CoverResult.Hit -> res.cover.fd()
is CoverResult.Miss -> null
}
}
}

View file

@ -409,7 +409,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
@Px val iconSize: Int?
) : Drawable() {
init {
// Re-tint the drawable to use the analogous "on surfaceg" color for
// Re-tint the drawable to use the analogous "on surface" color for
// StyledImageView.
DrawableCompat.setTintList(inner, context.getColorCompat(R.color.sel_on_cover_bg))
}

View file

@ -20,13 +20,15 @@ package org.oxycblt.auxio.image.covers
import android.content.Context
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.cover.CoverResult
import org.oxycblt.musikr.cover.MutableCovers
import org.oxycblt.musikr.cover.ObtainResult
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.metadata.Metadata
class NullCovers(private val context: Context) : MutableCovers {
override suspend fun obtain(id: String) = ObtainResult.Hit(NullCover)
override suspend fun obtain(id: String) = CoverResult.Hit(NullCover)
override suspend fun write(data: ByteArray): Cover = NullCover
override suspend fun create(file: DeviceFile, metadata: Metadata) = CoverResult.Hit(NullCover)
override suspend fun cleanup(excluding: Collection<Cover>) {
context.coversDir().listFiles()?.forEach { it.deleteRecursively() }

View file

@ -25,21 +25,23 @@ 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.CoverResult
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.DeviceFile
import org.oxycblt.musikr.fs.app.AppFiles
import org.oxycblt.musikr.metadata.Metadata
open class SiloedCovers(private val silo: CoverSilo, private val fileCovers: FileCovers) : Covers {
override suspend fun obtain(id: String): ObtainResult<SiloedCover> {
val coverId = SiloedCoverId.parse(id) ?: return ObtainResult.Miss()
if (coverId.silo != silo) return ObtainResult.Miss()
override suspend fun obtain(id: String): CoverResult<SiloedCover> {
val coverId = SiloedCoverId.parse(id) ?: return CoverResult.Miss()
if (coverId.silo != silo) return CoverResult.Miss()
return when (val result = fileCovers.obtain(coverId.id)) {
is ObtainResult.Hit -> ObtainResult.Hit(SiloedCover(silo, result.cover))
is ObtainResult.Miss -> ObtainResult.Miss()
is CoverResult.Hit -> CoverResult.Hit(SiloedCover(silo, result.cover))
is CoverResult.Miss -> CoverResult.Miss()
}
}
@ -57,7 +59,11 @@ private constructor(
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 create(file: DeviceFile, metadata: Metadata): CoverResult<out Cover> =
when (val result = fileCovers.create(file, metadata)) {
is CoverResult.Hit -> CoverResult.Hit(SiloedCover(silo, result.cover))
is CoverResult.Miss -> CoverResult.Miss()
}
override suspend fun cleanup(excluding: Collection<Cover>) {
fileCovers.cleanup(excluding.filterIsInstance<SiloedCover>().map { it.innerCover })

View file

@ -31,8 +31,8 @@ import androidx.room.RoomDatabase
import androidx.room.Transaction
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import org.oxycblt.musikr.cover.CoverResult
import org.oxycblt.musikr.cover.Covers
import org.oxycblt.musikr.cover.ObtainResult
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.metadata.Properties
import org.oxycblt.musikr.pipeline.RawSong
@ -122,9 +122,9 @@ internal data class CachedSong(
val cover =
when (val result = coverId?.let { covers.obtain(it) }) {
// We found the cover.
is ObtainResult.Hit -> result.cover
is CoverResult.Hit -> result.cover
// We actually didn't find the cover, can't safely convert.
is ObtainResult.Miss -> return null
is CoverResult.Miss -> return null
// No cover in the first place, can ignore.
null -> null
}

View file

@ -19,21 +19,23 @@
package org.oxycblt.musikr.cover
import java.io.InputStream
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.metadata.Metadata
interface Covers {
suspend fun obtain(id: String): ObtainResult<out Cover>
suspend fun obtain(id: String): CoverResult<out Cover>
}
interface MutableCovers : Covers {
suspend fun write(data: ByteArray): Cover
suspend fun create(file: DeviceFile, metadata: Metadata): CoverResult<out Cover>
suspend fun cleanup(excluding: Collection<Cover>)
}
sealed interface ObtainResult<T : Cover> {
data class Hit<T : Cover>(val cover: T) : ObtainResult<T>
sealed interface CoverResult<T : Cover> {
data class Hit<T : Cover>(val cover: T) : CoverResult<T>
class Miss<T : Cover> : ObtainResult<T>
class Miss<T : Cover> : CoverResult<T>
}
interface Cover {

View file

@ -19,17 +19,19 @@
package org.oxycblt.musikr.cover
import android.os.ParcelFileDescriptor
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.fs.app.AppFile
import org.oxycblt.musikr.fs.app.AppFiles
import org.oxycblt.musikr.metadata.Metadata
open class FileCovers(private val appFiles: AppFiles, private val coverFormat: CoverFormat) :
Covers {
override suspend fun obtain(id: String): ObtainResult<FileCover> {
override suspend fun obtain(id: String): CoverResult<FileCover> {
val file = appFiles.find(getFileName(id))
return if (file != null) {
ObtainResult.Hit(FileCoverImpl(id, file))
CoverResult.Hit(FileCoverImpl(id, file))
} else {
ObtainResult.Miss()
CoverResult.Miss()
}
}
@ -41,10 +43,11 @@ class MutableFileCovers(
private val coverFormat: CoverFormat,
private val coverIdentifier: CoverIdentifier
) : FileCovers(appFiles, coverFormat), MutableCovers {
override suspend fun write(data: ByteArray): FileCover {
override suspend fun create(file: DeviceFile, metadata: Metadata): CoverResult<FileCover> {
val data = metadata.cover ?: return CoverResult.Miss()
val id = coverIdentifier.identify(data)
val file = appFiles.write(getFileName(id)) { coverFormat.transcodeInto(data, it) }
return FileCoverImpl(id, file)
val coverFile = appFiles.write(getFileName(id)) { coverFormat.transcodeInto(data, it) }
return CoverResult.Hit(FileCoverImpl(id, coverFile))
}
override suspend fun cleanup(excluding: Collection<Cover>) {

View file

@ -20,7 +20,7 @@ package org.oxycblt.musikr.fs
import android.net.Uri
internal data class DeviceFile(
data class DeviceFile(
val uri: Uri,
val mimeType: String,
val path: Path,

View file

@ -18,7 +18,7 @@
package org.oxycblt.musikr.metadata
internal data class Metadata(
data class Metadata(
val id3v2: Map<String, List<String>>,
val xiph: Map<String, List<String>>,
val mp4: Map<String, List<String>>,
@ -53,7 +53,7 @@ internal data class Metadata(
}
}
internal data class Properties(
data class Properties(
val mimeType: String,
val durationMs: Long,
val bitrateKbps: Int,

View file

@ -35,6 +35,7 @@ import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.cache.Cache
import org.oxycblt.musikr.cache.CacheResult
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.cover.CoverResult
import org.oxycblt.musikr.cover.MutableCovers
import org.oxycblt.musikr.fs.DeviceFile
import org.oxycblt.musikr.metadata.MetadataExtractor
@ -62,7 +63,7 @@ private class ExtractStepImpl(
private val metadataExtractor: MetadataExtractor,
private val tagParser: TagParser,
private val cacheFactory: Cache.Factory,
private val storedCovers: MutableCovers
private val covers: MutableCovers
) : ExtractStep {
@OptIn(ExperimentalCoroutinesApi::class)
override fun extract(nodes: Flow<ExploreNode>): Flow<ExtractedMusic> {
@ -84,7 +85,7 @@ private class ExtractStepImpl(
readDistributedFlow.flows
.map { flow ->
flow
.map { wrap(it) { file -> cache.read(file, storedCovers) } }
.map { wrap(it) { file -> cache.read(file, covers) } }
.flowOn(Dispatchers.IO)
.buffer(Channel.UNLIMITED)
}
@ -123,8 +124,10 @@ private class ExtractStepImpl(
if (extractedMetadata != null) {
val tags = tagParser.parse(extractedMetadata)
val cover =
extractedMetadata.cover?.let {
storedCovers.write(it)
when (val result =
covers.create(f, extractedMetadata)) {
is CoverResult.Hit -> result.cover
else -> null
}
val rawSong =
RawSong(
@ -175,8 +178,6 @@ private class ExtractStepImpl(
return merged.onCompletion { cache.finalize() }
}
private data class FileWith<T>(val file: DeviceFile, val with: T)
}
internal data class RawSong(