From bdfd9d6e233db018b66ccef72dea49ec4d16f25e Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 17 Dec 2024 11:45:04 -0500 Subject: [PATCH] musikr: move storage/interpretation dependence to construction This makes some testing and certain code more ergonomic. --- .../org/oxycblt/auxio/music/MusicModule.kt | 3 -- .../oxycblt/auxio/music/MusicRepository.kt | 8 ++++-- .../main/java/org/oxycblt/musikr/Library.kt | 3 -- .../main/java/org/oxycblt/musikr/Musikr.kt | 17 ++++++----- .../oxycblt/musikr/model/LibraryFactory.kt | 17 +++++++---- .../org/oxycblt/musikr/model/LibraryImpl.kt | 16 +++++++---- .../oxycblt/musikr/pipeline/EvaluateStep.kt | 28 +++++++++---------- .../oxycblt/musikr/pipeline/ExploreStep.kt | 15 ++++++---- .../oxycblt/musikr/pipeline/ExtractStep.kt | 27 ++++++++++-------- .../playlist/interpret/PlaylistInterpreter.kt | 26 +++++++---------- .../musikr/tag/interpret/TagInterpreter.kt | 8 +++--- 11 files changed, 87 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicModule.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicModule.kt index d0ee847c4..f4bd3eb3d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicModule.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicModule.kt @@ -26,7 +26,6 @@ import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton -import org.oxycblt.musikr.Musikr import org.oxycblt.musikr.cache.CacheDatabase import org.oxycblt.musikr.playlist.db.PlaylistDatabase @@ -48,6 +47,4 @@ class MusikrShimModule { @Singleton @Provides fun playlistDatabase(@ApplicationContext context: Context) = PlaylistDatabase.from(context) - - @Provides fun musikr(@ApplicationContext context: Context) = Musikr.new(context) } diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index 7b2a8a3f8..e5b6934e3 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -58,6 +58,7 @@ import timber.log.Timber as L */ interface MusicRepository { val library: Library? + /** The current state of music loading. Null if no load has occurred yet. */ val indexingState: IndexingState? @@ -212,7 +213,6 @@ interface MusicRepository { class MusicRepositoryImpl @Inject constructor( - private val musikr: Musikr, @ApplicationContext private val context: Context, private val cacheDatabase: CacheDatabase, private val playlistDatabase: PlaylistDatabase, @@ -375,9 +375,11 @@ constructor( StoredCovers.from(context, "covers"), StoredPlaylists.from(playlistDatabase)) } + + val interpretation = Interpretation(nameFactory, separators) + val newLibrary = - musikr.run( - locations, storage, Interpretation(nameFactory, separators), ::emitIndexingProgress) + Musikr.new(context, storage, interpretation).run(locations, ::emitIndexingProgress) emitIndexingCompletion(null) diff --git a/musikr/src/main/java/org/oxycblt/musikr/Library.kt b/musikr/src/main/java/org/oxycblt/musikr/Library.kt index 2fd7ccb7b..c5d73312f 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Library.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Library.kt @@ -18,7 +18,6 @@ package org.oxycblt.musikr -import org.oxycblt.musikr.cover.StoredCovers import org.oxycblt.musikr.fs.Path interface Library { @@ -28,8 +27,6 @@ interface Library { val genres: Collection val playlists: Collection - val storedCovers: StoredCovers - fun findSong(uid: Music.UID): Song? fun findSongByPath(path: Path): Song? diff --git a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt index 51c87e08c..55081a442 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt @@ -33,14 +33,15 @@ import org.oxycblt.musikr.pipeline.ExtractStep interface Musikr { suspend fun run( locations: List, - storage: Storage, - interpretation: Interpretation, onProgress: suspend (IndexingProgress) -> Unit = {} ): MutableLibrary companion object { - fun new(context: Context): Musikr = - MusikrImpl(ExploreStep.from(context), ExtractStep.from(context), EvaluateStep.new()) + fun new(context: Context, storage: Storage, interpretation: Interpretation): Musikr = + MusikrImpl( + ExploreStep.from(context, storage), + ExtractStep.from(context, storage), + EvaluateStep.new(storage, interpretation)) } } @@ -62,24 +63,22 @@ private class MusikrImpl( ) : Musikr { override suspend fun run( locations: List, - storage: Storage, - interpretation: Interpretation, onProgress: suspend (IndexingProgress) -> Unit ) = coroutineScope { var exploredCount = 0 var extractedCount = 0 val explored = exploreStep - .explore(locations, storage) + .explore(locations) .buffer(Channel.UNLIMITED) .onStart { onProgress(IndexingProgress.Songs(0, 0)) } .onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) } val extracted = extractStep - .extract(storage, explored) + .extract(explored) .buffer(Channel.UNLIMITED) .onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) } .onCompletion { onProgress(IndexingProgress.Indeterminate) } - evaluateStep.evaluate(storage, interpretation, extracted) + evaluateStep.evaluate(extracted) } } diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt index 41af9a0c6..bffe06b0c 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt @@ -21,19 +21,23 @@ package org.oxycblt.musikr.model import org.oxycblt.musikr.Album import org.oxycblt.musikr.Artist import org.oxycblt.musikr.Genre -import org.oxycblt.musikr.Interpretation import org.oxycblt.musikr.MutableLibrary import org.oxycblt.musikr.Song -import org.oxycblt.musikr.Storage import org.oxycblt.musikr.graph.AlbumVertex import org.oxycblt.musikr.graph.ArtistVertex import org.oxycblt.musikr.graph.GenreVertex import org.oxycblt.musikr.graph.MusicGraph import org.oxycblt.musikr.graph.PlaylistVertex import org.oxycblt.musikr.graph.SongVertex +import org.oxycblt.musikr.playlist.db.StoredPlaylists +import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter internal interface LibraryFactory { - fun create(graph: MusicGraph, storage: Storage, interpretation: Interpretation): MutableLibrary + fun create( + graph: MusicGraph, + storedPlaylists: StoredPlaylists, + playlistInterpreter: PlaylistInterpreter + ): MutableLibrary companion object { fun new(): LibraryFactory = LibraryFactoryImpl() @@ -43,8 +47,8 @@ internal interface LibraryFactory { private class LibraryFactoryImpl() : LibraryFactory { override fun create( graph: MusicGraph, - storage: Storage, - interpretation: Interpretation + storedPlaylists: StoredPlaylists, + playlistInterpreter: PlaylistInterpreter ): MutableLibrary { val songs = graph.songVertex.mapTo(mutableSetOf()) { vertex -> @@ -66,7 +70,8 @@ private class LibraryFactoryImpl() : LibraryFactory { graph.playlistVertex.mapTo(mutableSetOf()) { vertex -> PlaylistImpl(PlaylistVertexCore(vertex)) } - return LibraryImpl(songs, albums, artists, genres, playlists, storage, interpretation) + return LibraryImpl( + songs, albums, artists, genres, playlists, storedPlaylists, playlistInterpreter) } private class SongVertexCore(private val vertex: SongVertex) : SongCore { diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt index 1f4c14bb2..0675ea6c1 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt @@ -18,13 +18,14 @@ package org.oxycblt.musikr.model -import org.oxycblt.musikr.Interpretation import org.oxycblt.musikr.Music import org.oxycblt.musikr.MutableLibrary import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Song -import org.oxycblt.musikr.Storage import org.oxycblt.musikr.fs.Path +import org.oxycblt.musikr.playlist.db.StoredPlaylists +import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter +import org.oxycblt.musikr.playlist.interpret.PostPlaylist internal data class LibraryImpl( override val songs: Collection, @@ -32,8 +33,8 @@ internal data class LibraryImpl( override val artists: Collection, override val genres: Collection, override val playlists: Collection, - private val storage: Storage, - private val interpretation: Interpretation + private val storedPlaylists: StoredPlaylists, + private val playlistInterpreter: PlaylistInterpreter ) : MutableLibrary { private val songUidMap = songs.associateBy { it.uid } private val albumUidMap = albums.associateBy { it.uid } @@ -41,8 +42,6 @@ internal data class LibraryImpl( private val genreUidMap = genres.associateBy { it.uid } private val playlistUidMap = playlists.associateBy { it.uid } - override val storedCovers = storage.storedCovers - override fun findSong(uid: Music.UID) = songUidMap[uid] override fun findSongByPath(path: Path) = songs.find { it.path == path } @@ -76,4 +75,9 @@ internal data class LibraryImpl( override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary { return this } + + private class NewPlaylistCore( + override val prePlaylist: PostPlaylist, + override val songs: List + ) : PlaylistCore } diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt index ec03aa5eb..6484a41a8 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt @@ -32,32 +32,30 @@ import org.oxycblt.musikr.MutableLibrary import org.oxycblt.musikr.Storage import org.oxycblt.musikr.graph.MusicGraph import org.oxycblt.musikr.model.LibraryFactory +import org.oxycblt.musikr.playlist.db.StoredPlaylists import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter import org.oxycblt.musikr.tag.interpret.TagInterpreter internal interface EvaluateStep { - suspend fun evaluate( - storage: Storage, - interpretation: Interpretation, - extractedMusic: Flow - ): MutableLibrary + suspend fun evaluate(extractedMusic: Flow): MutableLibrary companion object { - fun new(): EvaluateStep = - EvaluateStepImpl(TagInterpreter.new(), PlaylistInterpreter.new(), LibraryFactory.new()) + fun new(storage: Storage, interpretation: Interpretation): EvaluateStep = + EvaluateStepImpl( + TagInterpreter.new(interpretation), + PlaylistInterpreter.new(interpretation), + storage.storedPlaylists, + LibraryFactory.new()) } } private class EvaluateStepImpl( private val tagInterpreter: TagInterpreter, private val playlistInterpreter: PlaylistInterpreter, + private val storedPlaylists: StoredPlaylists, private val libraryFactory: LibraryFactory ) : EvaluateStep { - override suspend fun evaluate( - storage: Storage, - interpretation: Interpretation, - extractedMusic: Flow - ): MutableLibrary { + override suspend fun evaluate(extractedMusic: Flow): MutableLibrary { val filterFlow = extractedMusic.divert { when (it) { @@ -68,12 +66,12 @@ private class EvaluateStepImpl( val rawSongs = filterFlow.right val preSongs = rawSongs - .map { tagInterpreter.interpret(it, interpretation) } + .map { tagInterpreter.interpret(it) } .flowOn(Dispatchers.Default) .buffer(Channel.UNLIMITED) val prePlaylists = filterFlow.left - .map { playlistInterpreter.interpret(it, interpretation) } + .map { playlistInterpreter.interpret(it) } .flowOn(Dispatchers.Default) .buffer(Channel.UNLIMITED) val graphBuilder = MusicGraph.builder() @@ -84,6 +82,6 @@ private class EvaluateStepImpl( prePlaylists.onEach { graphBuilder.add(it) }) graphBuild.collect() val graph = graphBuilder.build() - return libraryFactory.create(graph, storage, interpretation) + return libraryFactory.create(graph, storedPlaylists, playlistInterpreter) } } diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt index 47e675d69..2439288b0 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt @@ -34,18 +34,23 @@ import org.oxycblt.musikr.fs.DeviceFile import org.oxycblt.musikr.fs.MusicLocation import org.oxycblt.musikr.fs.query.DeviceFiles import org.oxycblt.musikr.playlist.PlaylistFile +import org.oxycblt.musikr.playlist.db.StoredPlaylists import org.oxycblt.musikr.playlist.m3u.M3U internal interface ExploreStep { - fun explore(locations: List, storage: Storage): Flow + fun explore(locations: List): Flow companion object { - fun from(context: Context): ExploreStep = ExploreStepImpl(DeviceFiles.from(context)) + fun from(context: Context, storage: Storage): ExploreStep = + ExploreStepImpl(DeviceFiles.from(context), storage.storedPlaylists) } } -private class ExploreStepImpl(private val deviceFiles: DeviceFiles) : ExploreStep { - override fun explore(locations: List, storage: Storage): Flow { +private class ExploreStepImpl( + private val deviceFiles: DeviceFiles, + private val storedPlaylists: StoredPlaylists +) : ExploreStep { + override fun explore(locations: List): Flow { val audios = deviceFiles .explore(locations.asFlow()) @@ -59,7 +64,7 @@ private class ExploreStepImpl(private val deviceFiles: DeviceFiles) : ExploreSte .flowOn(Dispatchers.IO) .buffer() val playlists = - flow { emitAll(storage.storedPlaylists.read().asFlow()) } + flow { emitAll(storedPlaylists.read().asFlow()) } .map { ExploreNode.Playlist(it) } .flowOn(Dispatchers.IO) .buffer() diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt index 85f2acf78..442449e30 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt @@ -28,8 +28,10 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.merge 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.StoredCovers import org.oxycblt.musikr.fs.DeviceFile import org.oxycblt.musikr.metadata.MetadataExtractor import org.oxycblt.musikr.metadata.Properties @@ -38,19 +40,25 @@ import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.tag.parse.TagParser internal interface ExtractStep { - fun extract(storage: Storage, nodes: Flow): Flow + fun extract(nodes: Flow): Flow companion object { - fun from(context: Context): ExtractStep = - ExtractStepImpl(MetadataExtractor.from(context), TagParser.new()) + fun from(context: Context, storage: Storage): ExtractStep = + ExtractStepImpl( + MetadataExtractor.from(context), + TagParser.new(), + storage.cache, + storage.storedCovers) } } private class ExtractStepImpl( private val metadataExtractor: MetadataExtractor, - private val tagParser: TagParser + private val tagParser: TagParser, + private val cache: Cache, + private val storedCovers: StoredCovers ) : ExtractStep { - override fun extract(storage: Storage, nodes: Flow): Flow { + override fun extract(nodes: Flow): Flow { val filterFlow = nodes.divert { when (it) { @@ -62,10 +70,7 @@ private class ExtractStepImpl( val playlistNodes = filterFlow.left.map { ExtractedMusic.Playlist(it) } val cacheResults = - audioNodes - .map { storage.cache.read(it) } - .flowOn(Dispatchers.IO) - .buffer(Channel.UNLIMITED) + audioNodes.map { cache.read(it) }.flowOn(Dispatchers.IO).buffer(Channel.UNLIMITED) val cacheFlow = cacheResults.divert { when (it) { @@ -82,7 +87,7 @@ private class ExtractStepImpl( .mapNotNull { file -> val metadata = metadataExtractor.extract(file) ?: return@mapNotNull null val tags = tagParser.parse(file, metadata) - val cover = metadata.cover?.let { storage.storedCovers.write(it) } + val cover = metadata.cover?.let { storedCovers.write(it) } RawSong(file, metadata.properties, tags, cover) } .flowOn(Dispatchers.IO) @@ -91,7 +96,7 @@ private class ExtractStepImpl( val writtenSongs = merge(*extractedSongs) .map { - storage.cache.write(it) + cache.write(it) ExtractedMusic.Song(it) } .flowOn(Dispatchers.IO) diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt index 917954c0e..2525a4141 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt @@ -21,33 +21,27 @@ package org.oxycblt.musikr.playlist.interpret import org.oxycblt.musikr.Interpretation import org.oxycblt.musikr.playlist.PlaylistFile import org.oxycblt.musikr.playlist.PlaylistHandle +import org.oxycblt.musikr.tag.interpret.Naming internal interface PlaylistInterpreter { - fun interpret(file: PlaylistFile, interpretation: Interpretation): PrePlaylist + fun interpret(file: PlaylistFile): PrePlaylist - fun interpret( - name: String, - handle: PlaylistHandle, - interpretation: Interpretation - ): PostPlaylist + fun interpret(name: String, handle: PlaylistHandle): PostPlaylist companion object { - fun new(): PlaylistInterpreter = PlaylistInterpreterImpl + fun new(interpretation: Interpretation): PlaylistInterpreter = + PlaylistInterpreterImpl(interpretation.naming) } } -private data object PlaylistInterpreterImpl : PlaylistInterpreter { - override fun interpret(file: PlaylistFile, interpretation: Interpretation) = +private class PlaylistInterpreterImpl(private val naming: Naming) : PlaylistInterpreter { + override fun interpret(file: PlaylistFile) = PrePlaylist( - name = interpretation.naming.name(file.name, null), + name = naming.name(file.name, null), rawName = file.name, handle = file.handle, songPointers = file.songPointers) - override fun interpret( - name: String, - handle: PlaylistHandle, - interpretation: Interpretation - ): PostPlaylist = - PostPlaylist(name = interpretation.naming.name(name, null), rawName = name, handle = handle) + override fun interpret(name: String, handle: PlaylistHandle): PostPlaylist = + PostPlaylist(name = naming.name(name, null), rawName = name, handle = handle) } diff --git a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/TagInterpreter.kt b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/TagInterpreter.kt index 6a1d340cf..b7a596369 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/TagInterpreter.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/TagInterpreter.kt @@ -31,15 +31,15 @@ import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.util.toUuidOrNull internal interface TagInterpreter { - fun interpret(song: RawSong, interpretation: Interpretation): PreSong + fun interpret(song: RawSong): PreSong companion object { - fun new(): TagInterpreter = TagInterpreterImpl + fun new(interpretation: Interpretation): TagInterpreter = TagInterpreterImpl(interpretation) } } -private data object TagInterpreterImpl : TagInterpreter { - override fun interpret(song: RawSong, interpretation: Interpretation): PreSong { +private class TagInterpreterImpl(private val interpretation: Interpretation) : TagInterpreter { + override fun interpret(song: RawSong): PreSong { val individualPreArtists = makePreArtists( song.tags.artistMusicBrainzIds,