music: implement artist linking
This commit is contained in:
parent
db391da4b8
commit
c7f4f842f3
1 changed files with 85 additions and 2 deletions
|
@ -2,15 +2,63 @@ 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.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.PreAlbum
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreArtist
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreGenre
|
||||||
|
import org.oxycblt.auxio.music.stack.interpret.prepare.PreSong
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
class ArtistLinker {
|
class ArtistLinker {
|
||||||
fun register(preSong: Flow<GenreLinker.LinkedSong>): Flow<LinkedSong> = emptyFlow()
|
private val tree = mutableMapOf<String?, MutableMap<UUID?, ArtistLink>>()
|
||||||
fun resolve(): Collection<ArtistImpl> = setOf()
|
|
||||||
|
fun register(linkedSongs: Flow<GenreLinker.LinkedSong>) = linkedSongs.map {
|
||||||
|
val linkedSongArtists = it.preSong.preArtists.map { artist ->
|
||||||
|
val nameKey = artist.rawName?.lowercase()
|
||||||
|
val musicBrainzIdKey = artist.musicBrainzId
|
||||||
|
val artistLink = tree.getOrPut(nameKey) { mutableMapOf() }
|
||||||
|
.getOrPut(musicBrainzIdKey) { ArtistLink(ArtistNode(Contribution())) }
|
||||||
|
artistLink.node.contributors.contribute(artist)
|
||||||
|
artistLink
|
||||||
|
}
|
||||||
|
val linkedAlbumArtists = it.preSong.preAlbum.preArtists.map { artist ->
|
||||||
|
val nameKey = artist.rawName?.lowercase()
|
||||||
|
val musicBrainzIdKey = artist.musicBrainzId
|
||||||
|
val artistLink = tree.getOrPut(nameKey) { mutableMapOf() }
|
||||||
|
.getOrPut(musicBrainzIdKey) { ArtistLink(ArtistNode(Contribution())) }
|
||||||
|
artistLink.node.contributors.contribute(artist)
|
||||||
|
artistLink
|
||||||
|
}
|
||||||
|
val linkedAlbum =
|
||||||
|
LinkedAlbum(it.preSong.preAlbum, MultiArtistLink(linkedAlbumArtists))
|
||||||
|
LinkedSong(it, linkedAlbum, MultiArtistLink(linkedSongArtists))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolve(): Collection<ArtistImpl> =
|
||||||
|
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 linkedGenreSong: GenreLinker.LinkedSong,
|
val linkedGenreSong: GenreLinker.LinkedSong,
|
||||||
|
@ -22,4 +70,39 @@ class ArtistLinker {
|
||||||
val preAlbum: PreAlbum,
|
val preAlbum: PreAlbum,
|
||||||
val artists: Linked<List<ArtistImpl>, AlbumImpl>
|
val artists: Linked<List<ArtistImpl>, AlbumImpl>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private class MultiArtistLink<T : Music>(
|
||||||
|
val links: List<Linked<ArtistImpl, Music>>
|
||||||
|
) : Linked<List<ArtistImpl>, T> {
|
||||||
|
override fun resolve(child: T): List<ArtistImpl> {
|
||||||
|
return links.map { it.resolve(child) }.distinct()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ArtistLink(
|
||||||
|
var node: ArtistNode
|
||||||
|
) : Linked<ArtistImpl, Music> {
|
||||||
|
override fun resolve(child: Music): ArtistImpl {
|
||||||
|
return requireNotNull(node.artistImpl) { "Artist not resolved yet" }.also {
|
||||||
|
when (child) {
|
||||||
|
is SongImpl -> it.link(child)
|
||||||
|
is AlbumImpl -> it.link(child)
|
||||||
|
else -> error("Cannot link to child $child")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ArtistNode(
|
||||||
|
val contributors: Contribution<PreArtist>
|
||||||
|
) {
|
||||||
|
var artistImpl: ArtistImpl? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun resolve(): ArtistImpl {
|
||||||
|
val impl = ArtistImpl(contributors.resolve())
|
||||||
|
artistImpl = impl
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue