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:
parent
530e427b79
commit
5b44c31689
3 changed files with 37 additions and 45 deletions
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue