musikr: split albums w/full album artist coverage

This is like the old Auxio behavior, but should now trigger with only
full album artist coverage, rather than before where it would always
trigger and break apart sparsely tagged albums.

Still not a perfect heuristic, but it's the best one I can do.
This commit is contained in:
Alexander Capehart 2025-03-17 09:15:35 -06:00
parent eaba11fa44
commit b21b2e49d3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 41 additions and 8 deletions

View file

@ -23,6 +23,7 @@ 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.PreArtistsFrom
import org.oxycblt.musikr.tag.interpret.PreGenre
import org.oxycblt.musikr.tag.interpret.PreSong
import org.oxycblt.musikr.util.unlikelyToBeNull
@ -75,7 +76,7 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
// Albums themselves have their own parent artists that also need to be
// linked up.
val albumArtistVertices =
preSong.preAlbum.preArtists.map { preArtist ->
preSong.preAlbum.preArtists.preArtists.map { preArtist ->
artistVertices.getOrPut(preArtist) { ArtistVertex(preArtist) }
}
val albumVertex = AlbumVertex(preSong.preAlbum, albumArtistVertices.toMutableList())
@ -288,7 +289,7 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
return
}
// No full MBID coverage, discard the MBIDs from the graph.
val strippedCluster =
val strippedMbidCluster =
cluster.map {
val noMbidPreAlbum = it.preAlbum.copy(musicBrainzId = null)
val simpleMbidVertex =
@ -298,7 +299,31 @@ private class MusicGraphBuilderImpl : MusicGraph.Builder {
meldAlbumVertices(it, simpleMbidVertex)
simpleMbidVertex
}
simplifyAlbumClusterImpl(strippedCluster)
val fullAlbumArtistCoverage =
strippedMbidCluster.all { it.preAlbum.preArtists is PreArtistsFrom.Album }
if (fullAlbumArtistCoverage) {
// All albums have album artists, we can reasonably cluster around artists
// rather than just name.
val albumArtistClusters =
strippedMbidCluster.groupBy { it.preAlbum.preArtists.preArtists }
for (albumArtistCluster in albumArtistClusters.values) {
simplifyAlbumClusterImpl(albumArtistCluster)
}
return
}
val strippedAlbumArtistCluster =
strippedMbidCluster.map {
val noAlbumArtistPreAlbum =
it.preAlbum.copy(
preArtists = PreArtistsFrom.Individual(it.preAlbum.preArtists.preArtists))
val simpleAlbumArtistVertex =
albumVertices.getOrPut(noAlbumArtistPreAlbum) {
AlbumVertex(noAlbumArtistPreAlbum, it.artistVertices.toMutableList())
}
meldAlbumVertices(it, simpleAlbumArtistVertex)
simpleAlbumArtistVertex
}
simplifyAlbumClusterImpl(strippedAlbumArtistCluster)
}
private fun simplifyAlbumClusterImpl(cluster: Collection<AlbumVertex>) {

View file

@ -54,16 +54,24 @@ internal data class PreSong(
val preAlbum: PreAlbum,
val preArtists: List<PreArtist>,
val preGenres: List<PreGenre>
) {}
)
internal data class PreAlbum(
val musicBrainzId: UUID?,
val name: Name,
val rawName: String?,
val releaseType: ReleaseType,
val preArtists: List<PreArtist>
val preArtists: PreArtistsFrom,
)
internal sealed interface PreArtistsFrom {
val preArtists: List<PreArtist>
data class Individual(override val preArtists: List<PreArtist>) : PreArtistsFrom
data class Album(override val preArtists: List<PreArtist>) : PreArtistsFrom
}
internal data class PreArtist(val musicBrainzId: UUID?, val name: Name, val rawName: String?)
internal data class PreGenre(

View file

@ -165,9 +165,9 @@ private class TagInterpreterImpl(private val interpretation: Interpretation) : T
ReleaseType.parse(interpretation.separators.split(parsedTags.releaseTypes))
?: ReleaseType.Album(null),
preArtists =
albumPreArtists
.ifEmpty { individualPreArtists }
.ifEmpty { listOf(unknownPreArtist()) })
PreArtistsFrom.Album(albumPreArtists).takeIf { it.preArtists.isNotEmpty() }
?: PreArtistsFrom.Individual(
individualPreArtists.ifEmpty { listOf(unknownPreArtist()) }))
}
private fun makePreArtists(