music: implement artist linking

This commit is contained in:
Alexander Capehart 2024-11-25 20:23:51 -07:00
parent db391da4b8
commit c7f4f842f3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47

View file

@ -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
}
}
} }