diff --git a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/linker/ArtistLinker.kt b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/linker/ArtistLinker.kt index 4cc9c4e99..54a4e363b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/linker/ArtistLinker.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/stack/interpret/linker/ArtistLinker.kt @@ -2,15 +2,63 @@ package org.oxycblt.auxio.music.stack.interpret.linker import kotlinx.coroutines.flow.Flow 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.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.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 { - fun register(preSong: Flow): Flow = emptyFlow() - fun resolve(): Collection = setOf() + private val tree = mutableMapOf>() + + fun register(linkedSongs: Flow) = 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 = + 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( val linkedGenreSong: GenreLinker.LinkedSong, @@ -22,4 +70,39 @@ class ArtistLinker { val preAlbum: PreAlbum, val artists: Linked, AlbumImpl> ) + + private class MultiArtistLink( + val links: List> + ) : Linked, T> { + override fun resolve(child: T): List { + return links.map { it.resolve(child) }.distinct() + } + } + + private data class ArtistLink( + var node: ArtistNode + ) : Linked { + 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 + ) { + var artistImpl: ArtistImpl? = null + private set + + fun resolve(): ArtistImpl { + val impl = ArtistImpl(contributors.resolve()) + artistImpl = impl + return impl + } + } }