From 672c256b1e738914793c0969f56ee3af5957f4d9 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 8 Jun 2023 20:40:09 -0600 Subject: [PATCH] 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. --- .../auxio/music/device/DeviceMusicImpl.kt | 18 +++++++----------- .../org/oxycblt/auxio/music/device/RawMusic.kt | 14 ++++++++++---- .../java/org/oxycblt/auxio/music/info/Name.kt | 3 ++- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt index 022d68c86..aaad318b3 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/DeviceMusicImpl.kt @@ -38,7 +38,6 @@ import org.oxycblt.auxio.music.info.ReleaseType import org.oxycblt.auxio.music.metadata.parseId3GenreNames import org.oxycblt.auxio.music.metadata.parseMultiValue import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment -import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.nonZeroOrNull import org.oxycblt.auxio.util.toUuidOrNull 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 replayGainAdjustment = if (rawSong.replayGainTrackAdjustment != null && - rawSong.replayGainAlbumAdjustment != null) { - ReplayGainAdjustment( - track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment), - album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment)) - } else { - null - } - .also { - logD("${rawSong.replayGainTrackAdjustment} ${rawSong.replayGainAlbumAdjustment}}") - } + rawSong.replayGainAlbumAdjustment != null) { + ReplayGainAdjustment( + track = unlikelyToBeNull(rawSong.replayGainTrackAdjustment), + album = unlikelyToBeNull(rawSong.replayGainAlbumAdjustment)) + } else { + null + } override val dateAdded = requireNotNull(rawSong.dateAdded) { "Invalid raw: No date added" } private var _album: AlbumImpl? = null override val album: Album diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt b/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt index 57539ec09..5358f0244 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/device/RawMusic.kt @@ -25,6 +25,7 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.fs.Directory import org.oxycblt.auxio.music.info.Date import org.oxycblt.auxio.music.info.ReleaseType +import org.oxycblt.auxio.util.logD /** * 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 // 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. private val hashCode = inner.musicBrainzId?.hashCode() - ?: (31 * inner.name.lowercase().hashCode() + inner.rawArtists.hashCode()) + ?: (31 * inner.name.lowercase().hashCode() + artistKeys.hashCode()) override fun hashCode() = hashCode @@ -141,8 +144,7 @@ data class RawAlbum( inner.musicBrainzId != null && other.inner.musicBrainzId != null -> inner.musicBrainzId == other.inner.musicBrainzId inner.musicBrainzId == null && other.inner.musicBrainzId == null -> - inner.name.equals(other.inner.name, true) && - inner.rawArtists == other.inner.rawArtists + inner.name.equals(other.inner.name, true) && artistKeys == other.artistKeys else -> false } } @@ -176,7 +178,11 @@ data class RawArtist( // grouping to be case-insensitive. // 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 // same name in large libraries. diff --git a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt index 4a8c0d3d9..03f3a33f6 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/info/Name.kt @@ -203,7 +203,8 @@ private data class IntelligentKnownName(override val raw: String, override val s // Separate each token into their numeric and lexicographic counterparts. if (token.first().isDigit()) { // 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 collationKey = COLLATOR.getCollationKey(digits) type = SortToken.Type.NUMERIC