music: implement album linking
This commit is contained in:
parent
608e249a87
commit
e3d6644634
2 changed files with 76 additions and 6 deletions
|
@ -46,14 +46,17 @@ class InterpreterImpl(
|
||||||
val artistLinker = ArtistLinker()
|
val artistLinker = ArtistLinker()
|
||||||
val artistLinkedSongs =
|
val artistLinkedSongs =
|
||||||
artistLinker.register(genreLinkedSongs).flowOn(Dispatchers.Main).buffer()
|
artistLinker.register(genreLinkedSongs).flowOn(Dispatchers.Main).buffer()
|
||||||
|
// This is intentional. Song and album instances are dependent on artist
|
||||||
|
// data, so we need to ensure that all of the linked artist data is resolved
|
||||||
|
// before we go any further.
|
||||||
|
val genres = genreLinker.resolve()
|
||||||
|
val artists = artistLinker.resolve()
|
||||||
val albumLinker = AlbumLinker()
|
val albumLinker = AlbumLinker()
|
||||||
val albumLinkedSongs =
|
val albumLinkedSongs =
|
||||||
albumLinker.register(artistLinkedSongs)
|
albumLinker.register(artistLinkedSongs)
|
||||||
.flowOn(Dispatchers.Main)
|
.flowOn(Dispatchers.Main)
|
||||||
.map { LinkedSongImpl(it) }
|
.map { LinkedSongImpl(it) }
|
||||||
.toList()
|
.toList()
|
||||||
val genres = genreLinker.resolve()
|
|
||||||
val artists = artistLinker.resolve()
|
|
||||||
val albums = albumLinker.resolve()
|
val albums = albumLinker.resolve()
|
||||||
val songs = albumLinkedSongs.map { SongImpl(it) }
|
val songs = albumLinkedSongs.map { SongImpl(it) }
|
||||||
return LibraryImpl(songs, albums, artists, genres)
|
return LibraryImpl(songs, albums, artists, genres)
|
||||||
|
|
|
@ -2,16 +2,83 @@ package org.oxycblt.auxio.music.stack.interpret.linker
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.oxycblt.auxio.music.Album
|
||||||
|
import org.oxycblt.auxio.music.Music
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.stack.interpret.model.AlbumImpl
|
import org.oxycblt.auxio.music.stack.interpret.model.AlbumImpl
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.model.ArtistImpl
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.model.GenreImpl
|
||||||
import org.oxycblt.auxio.music.stack.interpret.model.SongImpl
|
import org.oxycblt.auxio.music.stack.interpret.model.SongImpl
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreAlbum
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreGenre
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreSong
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
class AlbumLinker {
|
class AlbumLinker {
|
||||||
fun register(linkedSongs: Flow<ArtistLinker.LinkedSong>): Flow<LinkedSong> = emptyFlow()
|
private val tree = mutableMapOf<String?, MutableMap<UUID?, AlbumLink>>()
|
||||||
fun resolve(): Collection<AlbumImpl> = setOf()
|
|
||||||
|
fun register(linkedSongs: Flow<ArtistLinker.LinkedSong>) = linkedSongs.map {
|
||||||
|
val nameKey = it.linkedAlbum.preAlbum.rawName.lowercase()
|
||||||
|
val musicBrainzIdKey = it.linkedAlbum.preAlbum.musicBrainzId
|
||||||
|
val albumLink = tree.getOrPut(nameKey) { mutableMapOf() }
|
||||||
|
.getOrPut(musicBrainzIdKey) { AlbumLink(AlbumNode(Contribution())) }
|
||||||
|
albumLink.node.contributors.contribute(it.linkedAlbum)
|
||||||
|
LinkedSong(it, albumLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolve(): Collection<AlbumImpl> =
|
||||||
|
tree.values.flatMap { musicBrainzIdBundle ->
|
||||||
|
val only =
|
||||||
|
musicBrainzIdBundle.values.singleOrNull()
|
||||||
|
if (only != null) {
|
||||||
|
return@flatMap listOf(only.node.resolve())
|
||||||
|
}
|
||||||
|
val nullBundle = musicBrainzIdBundle[null]
|
||||||
|
?: return@flatMap musicBrainzIdBundle.values.map { it.node.resolve() }
|
||||||
|
// Only partially tagged with MBIDs, must go through and
|
||||||
|
musicBrainzIdBundle.filter { it.key != null }.forEach {
|
||||||
|
val candidates = it.value.node.contributors.candidates
|
||||||
|
nullBundle.node.contributors.contribute(candidates)
|
||||||
|
it.value.node = nullBundle.node
|
||||||
|
}
|
||||||
|
listOf(nullBundle.node.resolve())
|
||||||
|
}
|
||||||
|
|
||||||
data class LinkedSong(
|
data class LinkedSong(
|
||||||
val linkedArtistSong: ArtistLinker.LinkedSong,
|
val linkedSong: ArtistLinker.LinkedSong,
|
||||||
val album: Linked<AlbumImpl, SongImpl>
|
val album: Linked<AlbumImpl, SongImpl>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
private data class AlbumLink(
|
||||||
|
var node: AlbumNode
|
||||||
|
) : Linked<AlbumImpl, SongImpl> {
|
||||||
|
override fun resolve(child: SongImpl): AlbumImpl {
|
||||||
|
return requireNotNull(node.albumImpl) { "Album not resolved yet" }.also {
|
||||||
|
it.link(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AlbumNode(
|
||||||
|
val contributors: Contribution<ArtistLinker.LinkedAlbum>
|
||||||
|
) {
|
||||||
|
var albumImpl: AlbumImpl? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun resolve(): AlbumImpl {
|
||||||
|
val impl = AlbumImpl(LinkedAlbumImpl(contributors.resolve()))
|
||||||
|
albumImpl = impl
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LinkedAlbumImpl(
|
||||||
|
private val artistLinkedAlbum: ArtistLinker.LinkedAlbum
|
||||||
|
) : LinkedAlbum {
|
||||||
|
override val preAlbum = artistLinkedAlbum.preAlbum
|
||||||
|
|
||||||
|
override val artists = artistLinkedAlbum.artists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue