From 7fab7f7eeb2be1257da513af34f6fccb9a44f0e1 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 16 Dec 2024 20:45:26 -0500 Subject: [PATCH] musikr: add full playlist evaluation --- .../main/java/org/oxycblt/musikr/Library.kt | 3 ++ .../main/java/org/oxycblt/musikr/Musikr.kt | 2 +- .../org/oxycblt/musikr/graph/MusicGraph.kt | 15 ++++--- .../oxycblt/musikr/model/LibraryFactory.kt | 21 ++++++++-- .../org/oxycblt/musikr/model/LibraryImpl.kt | 17 ++++++-- .../org/oxycblt/musikr/model/PlaylistImpl.kt | 7 ++-- .../oxycblt/musikr/pipeline/EvaluateStep.kt | 16 +++++-- .../musikr/playlist/db/StoredPlaylists.kt | 24 ++++++++++- .../playlist/interpret/PlaylistInterpreter.kt | 42 +++++++++++++++++++ .../musikr/playlist/interpret/PrePlaylist.kt | 24 +++++++++++ .../oxycblt/musikr/tag/interpret/PreMusic.kt | 6 --- .../musikr/tag/interpret/TagInterpreter.kt | 1 + 12 files changed, 148 insertions(+), 30 deletions(-) create mode 100644 musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt create mode 100644 musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PrePlaylist.kt diff --git a/musikr/src/main/java/org/oxycblt/musikr/Library.kt b/musikr/src/main/java/org/oxycblt/musikr/Library.kt index c5d73312f..2fd7ccb7b 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Library.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Library.kt @@ -18,6 +18,7 @@ package org.oxycblt.musikr +import org.oxycblt.musikr.cover.StoredCovers import org.oxycblt.musikr.fs.Path interface Library { @@ -27,6 +28,8 @@ 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 5838ecf68..51c87e08c 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt @@ -80,6 +80,6 @@ private class MusikrImpl( .buffer(Channel.UNLIMITED) .onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) } .onCompletion { onProgress(IndexingProgress.Indeterminate) } - evaluateStep.evaluate(interpretation, extracted) + evaluateStep.evaluate(storage, interpretation, extracted) } } diff --git a/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt b/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt index 6e43bf092..ff93fb81e 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt @@ -22,6 +22,7 @@ import org.oxycblt.musikr.Music import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.playlist.PlaylistFile import org.oxycblt.musikr.playlist.SongPointer +import org.oxycblt.musikr.playlist.interpret.PrePlaylist import org.oxycblt.musikr.tag.interpret.PreAlbum import org.oxycblt.musikr.tag.interpret.PreArtist import org.oxycblt.musikr.tag.interpret.PreGenre @@ -32,12 +33,13 @@ internal data class MusicGraph( val songVertex: List, val albumVertex: List, val artistVertex: List, - val genreVertex: List + val genreVertex: List, + val playlistVertex: Set ) { interface Builder { fun add(preSong: PreSong) - fun add(file: PlaylistFile) + fun add(prePlaylist: PrePlaylist) fun build(): MusicGraph } @@ -108,8 +110,8 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder { songVertices[uid] = songVertex } - override fun add(file: PlaylistFile) { - playlistVertices.add(PlaylistVertex(file)) + override fun add(prePlaylist: PrePlaylist) { + playlistVertices.add(PlaylistVertex(prePlaylist)) } override fun build(): MusicGraph { @@ -152,7 +154,8 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder { songVertices.values.toList(), albumVertices.values.toList(), artistVertices.values.toList(), - genreVertices.values.toList()) + genreVertices.values.toList(), + playlistVertices) } private fun simplifyGenreCluster(cluster: Collection) { @@ -338,7 +341,7 @@ internal class GenreVertex(val preGenre: PreGenre) { var tag: Any? = null } -internal class PlaylistVertex(val file: PlaylistFile) { +internal class PlaylistVertex(val prePlaylist: PrePlaylist) { val songVertices = mutableListOf() val pointerMap = mutableMapOf() val tag: Any? = null 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 bdadcfc7d..501937bfe 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt @@ -21,16 +21,19 @@ 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 internal interface LibraryFactory { - fun create(graph: MusicGraph): MutableLibrary + fun create(graph: MusicGraph, storage: Storage, interpretation: Interpretation): MutableLibrary companion object { fun new(): LibraryFactory = LibraryFactoryImpl() @@ -38,7 +41,7 @@ internal interface LibraryFactory { } private class LibraryFactoryImpl() : LibraryFactory { - override fun create(graph: MusicGraph): MutableLibrary { + override fun create(graph: MusicGraph, storage: Storage, interpretation: Interpretation): MutableLibrary { val songs = graph.songVertex.mapTo(mutableSetOf()) { vertex -> SongImpl(SongVertexCore(vertex)).also { vertex.tag = it } @@ -55,7 +58,11 @@ private class LibraryFactoryImpl() : LibraryFactory { graph.genreVertex.mapTo(mutableSetOf()) { vertex -> GenreImpl(GenreVertexCore(vertex)).also { vertex.tag = it } } - return LibraryImpl(songs, albums, artists, genres) + val playlists = + graph.playlistVertex.mapTo(mutableSetOf()) { vertex -> + PlaylistImpl(PlaylistVertexCore(vertex)) + } + return LibraryImpl(songs, albums, artists, genres, playlists, storage, interpretation) } private class SongVertexCore(private val vertex: SongVertex) : SongCore { @@ -94,4 +101,12 @@ private class LibraryFactoryImpl() : LibraryFactory { override val artists = vertex.artistVertices.mapTo(mutableSetOf()) { it.tag as Artist } } + + private class PlaylistVertexCore(vertex: PlaylistVertex) : PlaylistCore { + override val prePlaylist = vertex.prePlaylist + + override val songs = vertex.songVertices.mapNotNull { vertex -> + vertex?.let { it.tag as Song } + } + } } 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 3e067b57e..abed80959 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt @@ -18,26 +18,35 @@ 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.cover.StoredCovers import org.oxycblt.musikr.fs.Path +import org.oxycblt.musikr.playlist.PlaylistHandle +import org.oxycblt.musikr.playlist.db.PlaylistInfo +import org.oxycblt.musikr.playlist.db.StoredPlaylists -internal class LibraryImpl( +internal data class LibraryImpl( override val songs: Collection, override val albums: Collection, override val artists: Collection, - override val genres: Collection + override val genres: Collection, + override val playlists: Collection, + private val storage: Storage, + private val interpretation: Interpretation ) : MutableLibrary { - override val playlists = emptySet() - private val songUidMap = songs.associateBy { it.uid } private val albumUidMap = albums.associateBy { it.uid } private val artistUidMap = artists.associateBy { it.uid } 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 } diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt index 16259b613..1136c2895 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt @@ -22,17 +22,16 @@ import org.oxycblt.musikr.Playlist import org.oxycblt.musikr.Song import org.oxycblt.musikr.cover.Cover import org.oxycblt.musikr.playlist.PlaylistHandle +import org.oxycblt.musikr.playlist.interpret.PrePlaylistInfo import org.oxycblt.musikr.tag.Name -import org.oxycblt.musikr.tag.interpret.PrePlaylist internal interface PlaylistCore { - val prePlaylist: PrePlaylist - val handle: PlaylistHandle + val prePlaylist: PrePlaylistInfo val songs: List } internal class PlaylistImpl(private val core: PlaylistCore) : Playlist { - override val uid = core.handle.uid + override val uid = core.prePlaylist.handle.uid override val name: Name.Known = core.prePlaylist.name override val durationMs = core.songs.sumOf { it.durationMs } override val cover = Cover.multi(core.songs) 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 40abdd333..08874ecec 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt @@ -30,28 +30,33 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import org.oxycblt.musikr.Interpretation 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.model.PlaylistImpl import org.oxycblt.musikr.playlist.SongPointer +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 companion object { - fun new(): EvaluateStep = EvaluateStepImpl(TagInterpreter.new(), LibraryFactory.new()) + fun new(): EvaluateStep = EvaluateStepImpl(TagInterpreter.new(), PlaylistInterpreter.new(), LibraryFactory.new()) } } private class EvaluateStepImpl( private val tagInterpreter: TagInterpreter, + private val playlistInterpreter: PlaylistInterpreter, private val libraryFactory: LibraryFactory ) : EvaluateStep { override suspend fun evaluate( + storage: Storage, interpretation: Interpretation, extractedMusic: Flow ): MutableLibrary { @@ -67,14 +72,17 @@ private class EvaluateStepImpl( .map { tagInterpreter.interpret(it, interpretation) } .flowOn(Dispatchers.Main) .buffer(Channel.UNLIMITED) - val playlistFiles = filterFlow.left + val prePlaylists = filterFlow.left + .map { playlistInterpreter.interpret(it, interpretation) } + .flowOn(Dispatchers.Main) + .buffer(Channel.UNLIMITED) val graphBuilder = MusicGraph.builder() val graphBuild = merge( preSongs.onEach { graphBuilder.add(it) }, - playlistFiles.onEach { graphBuilder.add(it) } + prePlaylists.onEach { graphBuilder.add(it) } ) graphBuild.collect() val graph = graphBuilder.build() - return libraryFactory.create(graph) + return libraryFactory.create(graph, storage, interpretation) } } diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt index dd940583c..85d2cf143 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/db/StoredPlaylists.kt @@ -15,13 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package org.oxycblt.musikr.playlist.db +import org.oxycblt.musikr.Music +import org.oxycblt.musikr.Song import org.oxycblt.musikr.playlist.PlaylistFile +import org.oxycblt.musikr.playlist.PlaylistHandle import org.oxycblt.musikr.playlist.SongPointer interface StoredPlaylists { + suspend fun new(name: String, songs: List): PlaylistHandle suspend fun read(): List companion object { @@ -31,11 +35,27 @@ interface StoredPlaylists { } private class StoredPlaylistsImpl(private val playlistDao: PlaylistDao) : StoredPlaylists { + override suspend fun new(name: String, songs: List): PlaylistHandle { + val info = + PlaylistInfo( + Music.UID.auxio(Music.UID.Item.PLAYLIST), + name + ) + playlistDao.insertPlaylist( + RawPlaylist( + info, + songs.map { PlaylistSong(it.uid) } + ) + ) + return StoredPlaylistHandle(info, playlistDao) + } + override suspend fun read() = playlistDao.readRawPlaylists().map { PlaylistFile( it.playlistInfo.name, it.songs.map { song -> SongPointer.UID(song.songUid) }, - StoredPlaylistHandle(it.playlistInfo, playlistDao)) + StoredPlaylistHandle(it.playlistInfo, playlistDao) + ) } } 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 new file mode 100644 index 000000000..be6fba530 --- /dev/null +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PlaylistInterpreter.kt @@ -0,0 +1,42 @@ +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.playlist.SongPointer + +internal interface PlaylistInterpreter { + fun interpret(file: PlaylistFile, interpretation: Interpretation): PrePlaylist + + fun interpret( + name: String, + handle: PlaylistHandle, + interpretation: Interpretation + ): PostPlaylist + + companion object { + fun new(): PlaylistInterpreter = PlaylistInterpreterImpl + } +} + +private data object PlaylistInterpreterImpl : PlaylistInterpreter { + override fun interpret(file: PlaylistFile, interpretation: Interpretation) = + PrePlaylist( + name = interpretation.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 + ) + +} \ No newline at end of file diff --git a/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PrePlaylist.kt b/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PrePlaylist.kt new file mode 100644 index 000000000..df6d014fb --- /dev/null +++ b/musikr/src/main/java/org/oxycblt/musikr/playlist/interpret/PrePlaylist.kt @@ -0,0 +1,24 @@ +package org.oxycblt.musikr.playlist.interpret + +import org.oxycblt.musikr.playlist.PlaylistHandle +import org.oxycblt.musikr.playlist.SongPointer +import org.oxycblt.musikr.tag.Name + +internal interface PrePlaylistInfo { + val name: Name.Known + val rawName: String? + val handle: PlaylistHandle +} + +internal data class PrePlaylist( + override val name: Name.Known, + override val rawName: String?, + override val handle: PlaylistHandle, + val songPointers: List +) : PrePlaylistInfo + +internal data class PostPlaylist( + override val name: Name.Known, + override val rawName: String?, + override val handle: PlaylistHandle, +) : PrePlaylistInfo \ No newline at end of file diff --git a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt index 5af1770c1..b9c317891 100644 --- a/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt +++ b/musikr/src/main/java/org/oxycblt/musikr/tag/interpret/PreMusic.kt @@ -84,9 +84,3 @@ internal data class PreGenre( val name: Name, val rawName: String?, ) - -internal data class PrePlaylist( - val name: Name.Known, - val rawName: String?, - val handle: PlaylistHandle -) 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 551a31611..dfc8fb361 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 @@ -21,6 +21,7 @@ package org.oxycblt.musikr.tag.interpret import org.oxycblt.musikr.Interpretation import org.oxycblt.musikr.fs.Format import org.oxycblt.musikr.pipeline.RawSong +import org.oxycblt.musikr.playlist.PlaylistFile import org.oxycblt.musikr.tag.Disc import org.oxycblt.musikr.tag.Name import org.oxycblt.musikr.tag.Placeholder