music: sort song information

Sort song informtion in all MusicParent instances.

This is a temporary hack to band-aid music consistency between reloads,
as with the aggressive parallelization song order is no longer
consistent.
This commit is contained in:
Alexander Capehart 2023-06-13 09:37:18 -06:00
parent 530e427b79
commit 5b44c31689
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 37 additions and 45 deletions

View file

@ -203,7 +203,7 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
// Now that all songs are processed, also process albums and group them into their // Now that all songs are processed, also process albums and group them into their
// respective artists. // respective artists.
val albums = albumGrouping.values.map { AlbumImpl(it.raw.inner, musicSettings, it.music) } val albums = albumGrouping.values.map { AlbumImpl(it, musicSettings) }
for (album in albums) { for (album in albums) {
for (rawArtist in album.rawArtists) { for (rawArtist in album.rawArtists) {
val key = RawArtist.Key(rawArtist) val key = RawArtist.Key(rawArtist)
@ -236,19 +236,11 @@ class DeviceLibraryFactoryImpl @Inject constructor(private val musicSettings: Mu
} }
// Artists and genres do not need to be grouped and can be processed immediately. // Artists and genres do not need to be grouped and can be processed immediately.
val artists = val artists = artistGrouping.values.map { ArtistImpl(it, musicSettings) }
artistGrouping.values.map { ArtistImpl(it.raw.inner, musicSettings, it.music) } val genres = genreGrouping.values.map { GenreImpl(it, musicSettings) }
val genres = genreGrouping.values.map { GenreImpl(it.raw.inner, musicSettings, it.music) }
return DeviceLibraryImpl(songGrouping.values, albums, artists, genres) return DeviceLibraryImpl(songGrouping.values, albums, artists, genres)
} }
private data class Grouping<R, M : Music>(
var raw: PrioritizedRaw<R, M>,
val music: MutableList<M>
)
private data class PrioritizedRaw<R, M : Music>(val inner: R, val src: M)
} }
class DeviceLibraryImpl( class DeviceLibraryImpl(

View file

@ -231,17 +231,16 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son
/** /**
* Library-backed implementation of [Album]. * Library-backed implementation of [Album].
* *
* @param rawAlbum The [RawAlbum] to derive the member data from. * @param grouping [Grouping] to derive the member data from.
* @param musicSettings [MusicSettings] to for user parsing configuration. * @param musicSettings [MusicSettings] to for user parsing configuration.
* @param songs The [Song]s that are a part of this [Album]. These items will be linked to this
* [Album].
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class AlbumImpl( class AlbumImpl(
private val rawAlbum: RawAlbum, grouping: Grouping<RawAlbum, SongImpl>,
musicSettings: MusicSettings, musicSettings: MusicSettings,
override val songs: List<SongImpl>
) : Album { ) : Album {
private val rawAlbum = grouping.raw.inner
override val uid = override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID. // Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
rawAlbum.musicBrainzId?.let { Music.UID.musicBrainz(MusicMode.ALBUMS, it) } rawAlbum.musicBrainzId?.let { Music.UID.musicBrainz(MusicMode.ALBUMS, it) }
@ -259,6 +258,7 @@ class AlbumImpl(
override val durationMs: Long override val durationMs: Long
override val dateAdded: Long override val dateAdded: Long
override val songs: List<Song>
private val _artists = mutableListOf<ArtistImpl>() private val _artists = mutableListOf<ArtistImpl>()
override val artists: List<Artist> override val artists: List<Artist>
get() = _artists get() = _artists
@ -272,7 +272,7 @@ class AlbumImpl(
var earliestDateAdded: Long = Long.MAX_VALUE var earliestDateAdded: Long = Long.MAX_VALUE
// Do linking and value generation in the same loop for efficiency. // Do linking and value generation in the same loop for efficiency.
for (song in songs) { for (song in grouping.music) {
song.link(this) song.link(this)
if (song.date != null) { if (song.date != null) {
@ -298,6 +298,7 @@ class AlbumImpl(
dates = if (min != null && max != null) Date.Range(min, max) else null dates = if (min != null && max != null) Date.Range(min, max) else null
durationMs = totalDuration durationMs = totalDuration
dateAdded = earliestDateAdded dateAdded = earliestDateAdded
songs = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).songs(grouping.music)
hashCode = 31 * hashCode + rawAlbum.hashCode() hashCode = 31 * hashCode + rawAlbum.hashCode()
hashCode = 31 * hashCode + songs.hashCode() hashCode = 31 * hashCode + songs.hashCode()
@ -347,18 +348,13 @@ class AlbumImpl(
/** /**
* Library-backed implementation of [Artist]. * Library-backed implementation of [Artist].
* *
* @param rawArtist The [RawArtist] to derive the member data from. * @param grouping [Grouping] to derive the member data from.
* @param musicSettings [MusicSettings] to for user parsing configuration. * @param musicSettings [MusicSettings] to for user parsing configuration.
* @param songAlbums A list of the [Song]s and [Album]s that are a part of this [Artist] , either
* through artist or album artist tags. Providing [Song]s to the artist is optional. These
* instances will be linked to this [Artist].
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class ArtistImpl( class ArtistImpl(grouping: Grouping<RawArtist, Music>, musicSettings: MusicSettings) : Artist {
private val rawArtist: RawArtist, private val rawArtist = grouping.raw.inner
musicSettings: MusicSettings,
songAlbums: List<Music>
) : Artist {
override val uid = override val uid =
// Attempt to use a MusicBrainz ID first before falling back to a hashed UID. // Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
rawArtist.musicBrainzId?.let { Music.UID.musicBrainz(MusicMode.ARTISTS, it) } rawArtist.musicBrainzId?.let { Music.UID.musicBrainz(MusicMode.ARTISTS, it) }
@ -381,7 +377,7 @@ class ArtistImpl(
val distinctSongs = mutableSetOf<Song>() val distinctSongs = mutableSetOf<Song>()
val albumMap = mutableMapOf<Album, Boolean>() val albumMap = mutableMapOf<Album, Boolean>()
for (music in songAlbums) { for (music in grouping.music) {
when (music) { when (music) {
is SongImpl -> { is SongImpl -> {
music.link(this) music.link(this)
@ -398,7 +394,7 @@ class ArtistImpl(
} }
} }
songs = distinctSongs.toList() songs = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).songs(distinctSongs)
albums = Sort(Sort.Mode.ByDate, Sort.Direction.DESCENDING).albums(albumMap.keys) albums = Sort(Sort.Mode.ByDate, Sort.Direction.DESCENDING).albums(albumMap.keys)
explicitAlbums = albums.filter { unlikelyToBeNull(albumMap[it]) } explicitAlbums = albums.filter { unlikelyToBeNull(albumMap[it]) }
implicitAlbums = albums.filterNot { unlikelyToBeNull(albumMap[it]) } implicitAlbums = albums.filterNot { unlikelyToBeNull(albumMap[it]) }
@ -449,40 +445,38 @@ class ArtistImpl(
/** /**
* Library-backed implementation of [Genre]. * Library-backed implementation of [Genre].
* *
* @param rawGenre [RawGenre] to derive the member data from. * @param grouping [Grouping] to derive the member data from.
* @param musicSettings [MusicSettings] to for user parsing configuration. * @param musicSettings [MusicSettings] to for user parsing configuration.
* @param songs Child [SongImpl]s of this instance.
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
class GenreImpl( class GenreImpl(grouping: Grouping<RawGenre, SongImpl>, musicSettings: MusicSettings) : Genre {
private val rawGenre: RawGenre, private val rawGenre = grouping.raw.inner
musicSettings: MusicSettings,
override val songs: List<SongImpl>
) : Genre {
override val uid = Music.UID.auxio(MusicMode.GENRES) { update(rawGenre.name) } override val uid = Music.UID.auxio(MusicMode.GENRES) { update(rawGenre.name) }
override val name = override val name =
rawGenre.name?.let { Name.Known.from(it, rawGenre.name, musicSettings) } rawGenre.name?.let { Name.Known.from(it, rawGenre.name, musicSettings) }
?: Name.Unknown(R.string.def_genre) ?: Name.Unknown(R.string.def_genre)
override val songs: List<Song>
override val artists: List<Artist> override val artists: List<Artist>
override val durationMs: Long override val durationMs: Long
private var hashCode = uid.hashCode() private var hashCode = uid.hashCode()
init { init {
val distinctAlbums = mutableSetOf<Album>()
val distinctArtists = mutableSetOf<Artist>() val distinctArtists = mutableSetOf<Artist>()
var totalDuration = 0L var totalDuration = 0L
for (song in songs) { for (song in grouping.music) {
song.link(this) song.link(this)
distinctAlbums.add(song.album)
distinctArtists.addAll(song.artists) distinctArtists.addAll(song.artists)
totalDuration += song.durationMs totalDuration += song.durationMs
} }
songs = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).songs(grouping.music)
artists = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).artists(distinctArtists) artists = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).artists(distinctArtists)
durationMs = totalDuration durationMs = totalDuration
hashCode = 31 * hashCode + rawGenre.hashCode() hashCode = 31 * hashCode + rawGenre.hashCode()
hashCode = 31 * hashCode + songs.hashCode() hashCode = 31 * hashCode + songs.hashCode()
} }
@ -516,3 +510,7 @@ class GenreImpl(
return this return this
} }
} }
data class Grouping<R, M : Music>(var raw: PrioritizedRaw<R, M>, val music: MutableList<M>)
data class PrioritizedRaw<R, M : Music>(val inner: R, val src: M)

View file

@ -25,7 +25,6 @@ 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.
@ -120,7 +119,10 @@ data class RawAlbum(
) { ) {
val key = Key(this) val key = Key(this)
/** Exposed information that denotes [RawAlbum] uniqueness. */ /**
* Allows [RawAlbum]s to be compared by "fundamental" information that is unlikely to change on
* an item-by-item basis.
*/
data class Key(private val inner: RawAlbum) { data class Key(private val inner: RawAlbum) {
// Albums are grouped as follows: // Albums are grouped as follows:
// - If we have a MusicBrainz ID, only group by it. This allows different Albums with the // - If we have a MusicBrainz ID, only group by it. This allows different Albums with the
@ -167,7 +169,7 @@ data class RawArtist(
/** /**
* Allows [RawArtist]s to be compared by "fundamental" information that is unlikely to change on * Allows [RawArtist]s to be compared by "fundamental" information that is unlikely to change on
* an item-by-item * an item-by-item basis.
*/ */
data class Key(private val inner: RawArtist) { data class Key(private val inner: RawArtist) {
// Artists are grouped as follows: // Artists are grouped as follows:
@ -179,10 +181,6 @@ data class RawArtist(
// Cache the hashCode for HashMap efficiency. // Cache the hashCode for HashMap efficiency.
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.
@ -216,6 +214,10 @@ data class RawGenre(
) { ) {
val key = Key(this) val key = Key(this)
/**
* Allows [RawGenre]s to be compared by "fundamental" information that is unlikely to change on
* an item-by-item basis.
*/
data class Key(private val inner: RawGenre) { data class Key(private val inner: RawGenre) {
// Cache the hashCode for HashMap efficiency. // Cache the hashCode for HashMap efficiency.
private val hashCode = inner.name?.lowercase().hashCode() private val hashCode = inner.name?.lowercase().hashCode()