musikr: add full playlist evaluation
This commit is contained in:
parent
3d94ab67cf
commit
7fab7f7eeb
12 changed files with 148 additions and 30 deletions
|
@ -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<Genre>
|
||||
val playlists: Collection<Playlist>
|
||||
|
||||
val storedCovers: StoredCovers
|
||||
|
||||
fun findSong(uid: Music.UID): Song?
|
||||
|
||||
fun findSongByPath(path: Path): Song?
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SongVertex>,
|
||||
val albumVertex: List<AlbumVertex>,
|
||||
val artistVertex: List<ArtistVertex>,
|
||||
val genreVertex: List<GenreVertex>
|
||||
val genreVertex: List<GenreVertex>,
|
||||
val playlistVertex: Set<PlaylistVertex>
|
||||
) {
|
||||
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<GenreVertex>) {
|
||||
|
@ -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<SongVertex?>()
|
||||
val pointerMap = mutableMapOf<SongPointer, Int>()
|
||||
val tag: Any? = null
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SongImpl>,
|
||||
override val albums: Collection<AlbumImpl>,
|
||||
override val artists: Collection<ArtistImpl>,
|
||||
override val genres: Collection<GenreImpl>
|
||||
override val genres: Collection<GenreImpl>,
|
||||
override val playlists: Collection<Playlist>,
|
||||
private val storage: Storage,
|
||||
private val interpretation: Interpretation
|
||||
) : MutableLibrary {
|
||||
override val playlists = emptySet<Playlist>()
|
||||
|
||||
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 }
|
||||
|
|
|
@ -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<Song>
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<ExtractedMusic>
|
||||
): 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<ExtractedMusic>
|
||||
): 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,17 @@
|
|||
* 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.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<Song>): PlaylistHandle
|
||||
suspend fun read(): List<PlaylistFile>
|
||||
|
||||
companion object {
|
||||
|
@ -31,11 +35,27 @@ interface StoredPlaylists {
|
|||
}
|
||||
|
||||
private class StoredPlaylistsImpl(private val playlistDao: PlaylistDao) : StoredPlaylists {
|
||||
override suspend fun new(name: String, songs: List<Song>): 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
}
|
|
@ -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<SongPointer>
|
||||
) : PrePlaylistInfo
|
||||
|
||||
internal data class PostPlaylist(
|
||||
override val name: Name.Known,
|
||||
override val rawName: String?,
|
||||
override val handle: PlaylistHandle,
|
||||
) : PrePlaylistInfo
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue