music: consider settings in equality

Make it so that music items are meaningfully different when they were
created under different settings. This resolves an issue where music
information would not correctly update when separators or intelligent
sorting would change.

Resolves #546.
This commit is contained in:
Alexander Capehart 2023-08-18 15:57:53 -06:00
parent 9a67a0d539
commit 881fb58648
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 61 additions and 23 deletions

View file

@ -1,5 +1,11 @@
# Changelog
## dev
#### What's Fixed
- Fixed app restart being required when changing intelligent sorting
or music separator settings
## 3.2.0
#### What's New

View file

@ -53,8 +53,8 @@ import org.oxycblt.auxio.util.update
*/
class SongImpl(
private val rawSong: RawSong,
nameFactory: Name.Known.Factory,
separators: Separators
private val nameFactory: Name.Known.Factory,
private val separators: Separators
) : Song {
override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
@ -110,17 +110,6 @@ class SongImpl(
override val genres: List<Genre>
get() = _genres
private val hashCode = 31 * uid.hashCode() + rawSong.hashCode()
override fun hashCode() = hashCode
// TODO: I cant compare by raw information actually, as it also means that any settings
// configuration will be lost as well.
override fun equals(other: Any?) =
other is SongImpl && uid == other.uid && rawSong == other.rawSong
override fun toString() = "Song(uid=$uid, name=$name)"
/**
* The [RawAlbum] instances collated by the [Song]. This can be used to group [Song]s into an
* [Album].
@ -140,6 +129,8 @@ class SongImpl(
*/
val rawGenres: List<RawGenre>
private var hashCode: Int = uid.hashCode()
init {
val artistMusicBrainzIds = separators.split(rawSong.artistMusicBrainzIds)
val artistNames = separators.split(rawSong.artistNames)
@ -190,8 +181,24 @@ class SongImpl(
.mapTo(mutableSetOf()) { RawGenre(it) }
.toList()
.ifEmpty { listOf(RawGenre()) }
hashCode = 31 * rawSong.hashCode()
hashCode = 31 * nameFactory.hashCode()
}
override fun hashCode() = hashCode
// Since equality on public-facing music models is not identical to the tag equality,
// we just compare raw instances and how they are interpreted.
override fun equals(other: Any?) =
other is SongImpl &&
uid == other.uid &&
nameFactory == other.nameFactory &&
separators == other.separators &&
rawSong == other.rawSong
override fun toString() = "Song(uid=$uid, name=$name)"
/**
* Links this [Song] with a parent [Album].
*
@ -259,7 +266,10 @@ class SongImpl(
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
* @author Alexander Capehart (OxygenCobalt)
*/
class AlbumImpl(grouping: Grouping<RawAlbum, SongImpl>, nameFactory: Name.Known.Factory) : Album {
class AlbumImpl(
grouping: Grouping<RawAlbum, SongImpl>,
private val nameFactory: Name.Known.Factory
) : Album {
private val rawAlbum = grouping.raw.inner
override val uid =
@ -322,13 +332,20 @@ class AlbumImpl(grouping: Grouping<RawAlbum, SongImpl>, nameFactory: Name.Known.
dateAdded = earliestDateAdded
hashCode = 31 * hashCode + rawAlbum.hashCode()
hashCode = 31 * nameFactory.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
}
override fun hashCode() = hashCode
// Since equality on public-facing music models is not identical to the tag equality,
// we just compare raw instances and how they are interpreted.
override fun equals(other: Any?) =
other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs
other is AlbumImpl &&
uid == other.uid &&
rawAlbum == other.rawAlbum &&
nameFactory == other.nameFactory &&
songs == other.songs
override fun toString() = "Album(uid=$uid, name=$name)"
@ -376,7 +393,10 @@ class AlbumImpl(grouping: Grouping<RawAlbum, SongImpl>, nameFactory: Name.Known.
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
* @author Alexander Capehart (OxygenCobalt)
*/
class ArtistImpl(grouping: Grouping<RawArtist, Music>, nameFactory: Name.Known.Factory) : Artist {
class ArtistImpl(
grouping: Grouping<RawArtist, Music>,
private val nameFactory: Name.Known.Factory
) : Artist {
private val rawArtist = grouping.raw.inner
override val uid =
@ -425,6 +445,7 @@ class ArtistImpl(grouping: Grouping<RawArtist, Music>, nameFactory: Name.Known.F
durationMs = songs.sumOf { it.durationMs }.positiveOrNull()
hashCode = 31 * hashCode + rawArtist.hashCode()
hashCode = 31 * hashCode + nameFactory.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
}
@ -432,10 +453,13 @@ class ArtistImpl(grouping: Grouping<RawArtist, Music>, nameFactory: Name.Known.F
// the same UID but different songs are not equal.
override fun hashCode() = hashCode
// Since equality on public-facing music models is not identical to the tag equality,
// we just compare raw instances and how they are interpreted.
override fun equals(other: Any?) =
other is ArtistImpl &&
uid == other.uid &&
rawArtist == other.rawArtist &&
nameFactory == other.nameFactory &&
songs == other.songs
override fun toString() = "Artist(uid=$uid, name=$name)"
@ -473,7 +497,10 @@ class ArtistImpl(grouping: Grouping<RawArtist, Music>, nameFactory: Name.Known.F
* @param nameFactory The [Name.Known.Factory] to interpret name information with.
* @author Alexander Capehart (OxygenCobalt)
*/
class GenreImpl(grouping: Grouping<RawGenre, SongImpl>, nameFactory: Name.Known.Factory) : Genre {
class GenreImpl(
grouping: Grouping<RawGenre, SongImpl>,
private val nameFactory: Name.Known.Factory
) : Genre {
private val rawGenre = grouping.raw.inner
override val uid = Music.UID.auxio(MusicType.GENRES) { update(rawGenre.name) }
@ -502,13 +529,18 @@ class GenreImpl(grouping: Grouping<RawGenre, SongImpl>, nameFactory: Name.Known.
durationMs = totalDuration
hashCode = 31 * hashCode + rawGenre.hashCode()
hashCode = 31 * nameFactory.hashCode()
hashCode = 31 * hashCode + songs.hashCode()
}
override fun hashCode() = hashCode
override fun equals(other: Any?) =
other is GenreImpl && uid == other.uid && rawGenre == other.rawGenre && songs == other.songs
other is GenreImpl &&
uid == other.uid &&
rawGenre == other.rawGenre &&
nameFactory == other.nameFactory &&
songs == other.songs
override fun toString() = "Genre(uid=$uid, name=$name)"

View file

@ -95,13 +95,13 @@ sealed interface Name : Comparable<Name> {
* user-defined name configuration.
*
* @param settings The [MusicSettings] to use.
* @return A new [Factory] instance reflecting the configuration state.
* @return A [Factory] instance reflecting the configuration state.
*/
fun from(settings: MusicSettings) =
if (settings.intelligentSorting) {
IntelligentKnownName.Factory()
IntelligentKnownName.Factory
} else {
SimpleKnownName.Factory()
SimpleKnownName.Factory
}
}
}
@ -149,7 +149,7 @@ data class SimpleKnownName(override val raw: String, override val sort: String?)
return SortToken(collationKey, SortToken.Type.LEXICOGRAPHIC)
}
class Factory : Name.Known.Factory {
data object Factory : Name.Known.Factory {
override fun parse(raw: String, sort: String?) = SimpleKnownName(raw, sort)
}
}
@ -208,7 +208,7 @@ data class IntelligentKnownName(override val raw: String, override val sort: Str
}
}
class Factory : Name.Known.Factory {
data object Factory : Name.Known.Factory {
override fun parse(raw: String, sort: String?) = IntelligentKnownName(raw, sort)
}