music: group albums by raw artist keys

Properly group albums by raw artist keys, instead of by raw artist
instances.

This was an accidental regression in 3.1.1 that resulted in duplicate
albums in some circumstances.

Resolves #475.
This commit is contained in:
Alexander Capehart 2023-06-08 20:40:09 -06:00
parent e848bea0bf
commit 672c256b1e
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 19 additions and 16 deletions

View file

@ -38,7 +38,6 @@ import org.oxycblt.auxio.music.info.ReleaseType
import org.oxycblt.auxio.music.metadata.parseId3GenreNames import org.oxycblt.auxio.music.metadata.parseId3GenreNames
import org.oxycblt.auxio.music.metadata.parseMultiValue import org.oxycblt.auxio.music.metadata.parseMultiValue
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.nonZeroOrNull import org.oxycblt.auxio.util.nonZeroOrNull
import org.oxycblt.auxio.util.toUuidOrNull import org.oxycblt.auxio.util.toUuidOrNull
import org.oxycblt.auxio.util.unlikelyToBeNull import org.oxycblt.auxio.util.unlikelyToBeNull
@ -92,16 +91,13 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son
override val durationMs = requireNotNull(rawSong.durationMs) { "Invalid raw: No duration" } override val durationMs = requireNotNull(rawSong.durationMs) { "Invalid raw: No duration" }
override val replayGainAdjustment = override val replayGainAdjustment =
if (rawSong.replayGainTrackAdjustment != null && if (rawSong.replayGainTrackAdjustment != null &&
rawSong.replayGainAlbumAdjustment != null) { rawSong.replayGainAlbumAdjustment != null) {
ReplayGainAdjustment( ReplayGainAdjustment(
track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment), track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment),
album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment)) album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment))
} else { } else {
null null
} }
.also {
logD("${rawSong.replayGainTrackAdjustment} ${rawSong.replayGainAlbumAdjustment}}")
}
override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" } override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" }
private var _album: AlbumImpl? = null private var _album: AlbumImpl? = null
override val album: Album override val album: Album

View file

@ -25,6 +25,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.fs.Directory import org.oxycblt.auxio.music.fs.Directory
import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.info.Date
import org.oxycblt.auxio.music.info.ReleaseType import org.oxycblt.auxio.music.info.ReleaseType
import org.oxycblt.auxio.util.logD
/** /**
* Raw information about a [SongImpl] obtained from the filesystem/Extractor instances. * Raw information about a [SongImpl] obtained from the filesystem/Extractor instances.
@ -128,10 +129,12 @@ data class RawAlbum(
// artist name. This allows for case-insensitive artist/album grouping, which can be common // artist name. This allows for case-insensitive artist/album grouping, which can be common
// for albums/artists that have different naming (ex. "RAMMSTEIN" vs. "Rammstein"). // for albums/artists that have different naming (ex. "RAMMSTEIN" vs. "Rammstein").
private val artistKeys = inner.rawArtists.map { it.key }
// Cache the hash-code for HashMap efficiency. // Cache the hash-code for HashMap efficiency.
private val hashCode = private val hashCode =
inner.musicBrainzId?.hashCode() inner.musicBrainzId?.hashCode()
?: (31 * inner.name.lowercase().hashCode() + inner.rawArtists.hashCode()) ?: (31 * inner.name.lowercase().hashCode() + artistKeys.hashCode())
override fun hashCode() = hashCode override fun hashCode() = hashCode
@ -141,8 +144,7 @@ data class RawAlbum(
inner.musicBrainzId != null && other.inner.musicBrainzId != null -> inner.musicBrainzId != null && other.inner.musicBrainzId != null ->
inner.musicBrainzId == other.inner.musicBrainzId inner.musicBrainzId == other.inner.musicBrainzId
inner.musicBrainzId == null && other.inner.musicBrainzId == null -> inner.musicBrainzId == null && other.inner.musicBrainzId == null ->
inner.name.equals(other.inner.name, true) && inner.name.equals(other.inner.name, true) && artistKeys == other.artistKeys
inner.rawArtists == other.inner.rawArtists
else -> false else -> false
} }
} }
@ -176,7 +178,11 @@ data class RawArtist(
// grouping to be case-insensitive. // grouping to be case-insensitive.
// Cache the hashCode for HashMap efficiency. // Cache the hashCode for HashMap efficiency.
private val hashCode = inner.musicBrainzId?.hashCode() ?: inner.name?.lowercase().hashCode() val hashCode = inner.musicBrainzId?.hashCode() ?: inner.name?.lowercase().hashCode()
init {
logD("${inner.name} ${inner.name?.lowercase().hashCode()} $hashCode")
}
// Compare names and MusicBrainz IDs in order to differentiate artists with the // Compare names and MusicBrainz IDs in order to differentiate artists with the
// same name in large libraries. // same name in large libraries.

View file

@ -203,7 +203,8 @@ private data class IntelligentKnownName(override val raw: String, override val s
// Separate each token into their numeric and lexicographic counterparts. // Separate each token into their numeric and lexicographic counterparts.
if (token.first().isDigit()) { if (token.first().isDigit()) {
// The digit string comparison breaks with preceding zero digits, remove those // The digit string comparison breaks with preceding zero digits, remove those
val digits = token.trimStart { Character.getNumericValue(it) == 0 }.ifEmpty { token } val digits =
token.trimStart { Character.getNumericValue(it) == 0 }.ifEmpty { token }
// Other languages have other types of digit strings, still use collation keys // Other languages have other types of digit strings, still use collation keys
collationKey = COLLATOR.getCollationKey(digits) collationKey = COLLATOR.getCollationKey(digits)
type = SortToken.Type.NUMERIC type = SortToken.Type.NUMERIC