musikr: add new storage config

Allowed TagCache to be configured alongside a new StoredCovers
(to be implemented later)
This commit is contained in:
Alexander Capehart 2024-12-09 16:06:25 -07:00
parent df1faa11e4
commit 8adda19d1a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 63 additions and 35 deletions

View file

@ -32,8 +32,13 @@ import org.oxycblt.musikr.Musikr
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.Song
import org.oxycblt.musikr.tag.Interpretation
import org.oxycblt.musikr.Interpretation
import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.cover.StoredCovers
import org.oxycblt.musikr.tag.Name
import org.oxycblt.musikr.tag.cache.FullTagCache
import org.oxycblt.musikr.tag.cache.WriteOnlyTagCache
import org.oxycblt.musikr.tag.interpret.Separators
import timber.log.Timber as L
@ -203,7 +208,7 @@ interface MusicRepository {
class MusicRepositoryImpl
@Inject
constructor(private val musikr: Musikr, private val musicSettings: MusicSettings) :
constructor(private val musikr: Musikr, private val fullTagCache: FullTagCache, private val writeOnlyTagCache: WriteOnlyTagCache, private val musicSettings: MusicSettings) :
MusicRepository {
private val updateListeners = mutableListOf<MusicRepository.UpdateListener>()
private val indexingListeners = mutableListOf<MusicRepository.IndexingListener>()
@ -349,8 +354,23 @@ constructor(private val musikr: Musikr, private val musicSettings: MusicSettings
}
val locations = musicSettings.musicLocations
val fakeCoverEditorTemporary = object : StoredCovers.Editor {
override suspend fun write(data: ByteArray): Cover.Single? {
TODO("Not yet implemented")
}
override suspend fun apply() {
TODO("Not yet implemented")
}
}
val storage = if (withCache) {
Storage(fullTagCache, fakeCoverEditorTemporary)
} else {
Storage(writeOnlyTagCache, fakeCoverEditorTemporary)
}
val newLibrary =
musikr.run(locations, Interpretation(nameFactory, separators), ::emitIndexingProgress)
musikr.run(locations, storage, Interpretation(nameFactory, separators), ::emitIndexingProgress)
emitIndexingCompletion(null)

View file

@ -0,0 +1,10 @@
package org.oxycblt.musikr
import org.oxycblt.musikr.cover.StoredCovers
import org.oxycblt.musikr.tag.Name
import org.oxycblt.musikr.tag.cache.TagCache
import org.oxycblt.musikr.tag.interpret.Separators
data class Storage(val tagCache: TagCache, val coverEditor: StoredCovers.Editor)
data class Interpretation(val nameFactory: Name.Known.Factory, val separators: Separators)

View file

@ -29,11 +29,11 @@ import org.oxycblt.musikr.fs.MusicLocation
import org.oxycblt.musikr.pipeline.EvaluateStep
import org.oxycblt.musikr.pipeline.ExploreStep
import org.oxycblt.musikr.pipeline.ExtractStep
import org.oxycblt.musikr.tag.Interpretation
interface Musikr {
suspend fun run(
locations: List<MusicLocation>,
storage: Storage,
interpretation: Interpretation,
onProgress: suspend (IndexingProgress) -> Unit = {}
): MutableLibrary
@ -59,6 +59,7 @@ constructor(
) : Musikr {
override suspend fun run(
locations: List<MusicLocation>,
storage: Storage,
interpretation: Interpretation,
onProgress: suspend (IndexingProgress) -> Unit
) = coroutineScope {
@ -72,7 +73,7 @@ constructor(
.onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) }
val extracted =
extractStep
.extract(explored)
.extract(storage, explored)
.buffer(Channel.UNLIMITED)
.onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) }
.onCompletion { onProgress(IndexingProgress.Indeterminate) }

View file

@ -0,0 +1,13 @@
package org.oxycblt.musikr.cover
import java.io.InputStream
interface StoredCovers {
suspend fun read(cover: Cover.Single): InputStream?
interface Editor {
suspend fun write(data: ByteArray): Cover.Single?
suspend fun apply()
}
}

View file

@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.map
import org.oxycblt.musikr.MutableLibrary
import org.oxycblt.musikr.graph.MusicGraph
import org.oxycblt.musikr.model.LibraryFactory
import org.oxycblt.musikr.tag.Interpretation
import org.oxycblt.musikr.Interpretation
import org.oxycblt.musikr.tag.interpret.TagInterpreter
interface EvaluateStep {

View file

@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.MetadataExtractor
import org.oxycblt.musikr.tag.cache.TagCache
@ -34,22 +35,21 @@ import org.oxycblt.musikr.tag.parse.ParsedTags
import org.oxycblt.musikr.tag.parse.TagParser
interface ExtractStep {
fun extract(nodes: Flow<ExploreNode>): Flow<ExtractedMusic>
fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic>
}
class ExtractStepImpl
@Inject
constructor(
private val tagCache: TagCache,
private val metadataExtractor: MetadataExtractor,
private val tagParser: TagParser
) : ExtractStep {
override fun extract(nodes: Flow<ExploreNode>): Flow<ExtractedMusic> {
override fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic> {
val cacheResults =
nodes
.filterIsInstance<ExploreNode.Audio>()
.map {
val tags = tagCache.read(it.file)
val tags = storage.tagCache.read(it.file)
MaybeCachedSong(it.file, tags)
}
.flowOn(Dispatchers.IO)

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2024 Auxio Project
* Interpretation.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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.tag
import org.oxycblt.musikr.tag.interpret.Separators
data class Interpretation(val nameFactory: Name.Known.Factory, val separators: Separators)

View file

@ -28,10 +28,17 @@ interface TagCache {
suspend fun write(file: DeviceFile, tags: ParsedTags)
}
class TagCacheImpl @Inject constructor(private val tagDao: TagDao) : TagCache {
class FullTagCache @Inject constructor(private val tagDao: TagDao) : TagCache {
override suspend fun read(file: DeviceFile) =
tagDao.selectTags(file.uri.toString(), file.lastModified)?.intoParsedTags()
override suspend fun write(file: DeviceFile, tags: ParsedTags) =
tagDao.updateTags(CachedTags.fromParsedTags(file, tags))
}
class WriteOnlyTagCache @Inject constructor(private val tagDao: TagDao) : TagCache {
override suspend fun read(file: DeviceFile) = null
override suspend fun write(file: DeviceFile, tags: ParsedTags) =
tagDao.updateTags(CachedTags.fromParsedTags(file, tags))
}

View file

@ -25,7 +25,7 @@ import org.oxycblt.auxio.util.toUuidOrNull
import org.oxycblt.musikr.fs.MimeType
import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.tag.Disc
import org.oxycblt.musikr.tag.Interpretation
import org.oxycblt.musikr.Interpretation
import org.oxycblt.musikr.tag.Name
import org.oxycblt.musikr.tag.ReleaseType
import org.oxycblt.musikr.tag.parse.ParsedTags