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.MutableLibrary
import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Playlist
import org.oxycblt.musikr.Song 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.Name
import org.oxycblt.musikr.tag.cache.FullTagCache
import org.oxycblt.musikr.tag.cache.WriteOnlyTagCache
import org.oxycblt.musikr.tag.interpret.Separators import org.oxycblt.musikr.tag.interpret.Separators
import timber.log.Timber as L import timber.log.Timber as L
@ -203,7 +208,7 @@ interface MusicRepository {
class MusicRepositoryImpl class MusicRepositoryImpl
@Inject @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 { MusicRepository {
private val updateListeners = mutableListOf<MusicRepository.UpdateListener>() private val updateListeners = mutableListOf<MusicRepository.UpdateListener>()
private val indexingListeners = mutableListOf<MusicRepository.IndexingListener>() private val indexingListeners = mutableListOf<MusicRepository.IndexingListener>()
@ -349,8 +354,23 @@ constructor(private val musikr: Musikr, private val musicSettings: MusicSettings
} }
val locations = musicSettings.musicLocations 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 = val newLibrary =
musikr.run(locations, Interpretation(nameFactory, separators), ::emitIndexingProgress) musikr.run(locations, storage, Interpretation(nameFactory, separators), ::emitIndexingProgress)
emitIndexingCompletion(null) 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.EvaluateStep
import org.oxycblt.musikr.pipeline.ExploreStep import org.oxycblt.musikr.pipeline.ExploreStep
import org.oxycblt.musikr.pipeline.ExtractStep import org.oxycblt.musikr.pipeline.ExtractStep
import org.oxycblt.musikr.tag.Interpretation
interface Musikr { interface Musikr {
suspend fun run( suspend fun run(
locations: List<MusicLocation>, locations: List<MusicLocation>,
storage: Storage,
interpretation: Interpretation, interpretation: Interpretation,
onProgress: suspend (IndexingProgress) -> Unit = {} onProgress: suspend (IndexingProgress) -> Unit = {}
): MutableLibrary ): MutableLibrary
@ -59,6 +59,7 @@ constructor(
) : Musikr { ) : Musikr {
override suspend fun run( override suspend fun run(
locations: List<MusicLocation>, locations: List<MusicLocation>,
storage: Storage,
interpretation: Interpretation, interpretation: Interpretation,
onProgress: suspend (IndexingProgress) -> Unit onProgress: suspend (IndexingProgress) -> Unit
) = coroutineScope { ) = coroutineScope {
@ -72,7 +73,7 @@ constructor(
.onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) } .onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) }
val extracted = val extracted =
extractStep extractStep
.extract(explored) .extract(storage, explored)
.buffer(Channel.UNLIMITED) .buffer(Channel.UNLIMITED)
.onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) } .onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) }
.onCompletion { onProgress(IndexingProgress.Indeterminate) } .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.MutableLibrary
import org.oxycblt.musikr.graph.MusicGraph import org.oxycblt.musikr.graph.MusicGraph
import org.oxycblt.musikr.model.LibraryFactory import org.oxycblt.musikr.model.LibraryFactory
import org.oxycblt.musikr.tag.Interpretation import org.oxycblt.musikr.Interpretation
import org.oxycblt.musikr.tag.interpret.TagInterpreter import org.oxycblt.musikr.tag.interpret.TagInterpreter
interface EvaluateStep { interface EvaluateStep {

View file

@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.fs.query.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.MetadataExtractor import org.oxycblt.musikr.metadata.MetadataExtractor
import org.oxycblt.musikr.tag.cache.TagCache 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 import org.oxycblt.musikr.tag.parse.TagParser
interface ExtractStep { interface ExtractStep {
fun extract(nodes: Flow<ExploreNode>): Flow<ExtractedMusic> fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic>
} }
class ExtractStepImpl class ExtractStepImpl
@Inject @Inject
constructor( constructor(
private val tagCache: TagCache,
private val metadataExtractor: MetadataExtractor, private val metadataExtractor: MetadataExtractor,
private val tagParser: TagParser private val tagParser: TagParser
) : ExtractStep { ) : ExtractStep {
override fun extract(nodes: Flow<ExploreNode>): Flow<ExtractedMusic> { override fun extract(storage: Storage, nodes: Flow<ExploreNode>): Flow<ExtractedMusic> {
val cacheResults = val cacheResults =
nodes nodes
.filterIsInstance<ExploreNode.Audio>() .filterIsInstance<ExploreNode.Audio>()
.map { .map {
val tags = tagCache.read(it.file) val tags = storage.tagCache.read(it.file)
MaybeCachedSong(it.file, tags) MaybeCachedSong(it.file, tags)
} }
.flowOn(Dispatchers.IO) .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) 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) = override suspend fun read(file: DeviceFile) =
tagDao.selectTags(file.uri.toString(), file.lastModified)?.intoParsedTags() tagDao.selectTags(file.uri.toString(), file.lastModified)?.intoParsedTags()
override suspend fun write(file: DeviceFile, tags: ParsedTags) = override suspend fun write(file: DeviceFile, tags: ParsedTags) =
tagDao.updateTags(CachedTags.fromParsedTags(file, tags)) 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.MimeType
import org.oxycblt.musikr.fs.query.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.tag.Disc 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.Name
import org.oxycblt.musikr.tag.ReleaseType import org.oxycblt.musikr.tag.ReleaseType
import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.tag.parse.ParsedTags