music: cache hashcode in data
Cache the hashcode of song/album/artist/genre information so that it can be calculated easily later.
This commit is contained in:
parent
0d28bdf99e
commit
df174e22f6
5 changed files with 63 additions and 63 deletions
|
@ -170,19 +170,21 @@ private class DeviceLibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSongs(rawSongs: List<RawSong>, settings: MusicSettings): List<SongImpl> {
|
private fun buildSongs(rawSongs: List<RawSong>, settings: MusicSettings): List<SongImpl> {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
val songs =
|
val songs =
|
||||||
Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
|
Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
|
||||||
.songs(rawSongs.map { SongImpl(it, settings) }.distinctBy { it.uid })
|
.songs(rawSongs.map { SongImpl(it, settings) }.distinctBy { it.uid })
|
||||||
logD("Successfully built ${songs.size} songs")
|
logD("Successfully built ${songs.size} songs in ${System.currentTimeMillis() - start}ms")
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildAlbums(songs: List<SongImpl>, settings: MusicSettings): List<AlbumImpl> {
|
private fun buildAlbums(songs: List<SongImpl>, settings: MusicSettings): List<AlbumImpl> {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
// Group songs by their singular raw album, then map the raw instances and their
|
// Group songs by their singular raw album, then map the raw instances and their
|
||||||
// grouped songs to Album values. Album.Raw will handle the actual grouping rules.
|
// grouped songs to Album values. Album.Raw will handle the actual grouping rules.
|
||||||
val songsByAlbum = songs.groupBy { it.rawAlbum.key }
|
val songsByAlbum = songs.groupBy { it.rawAlbum.key }
|
||||||
val albums = songsByAlbum.map { AlbumImpl(it.key.value, settings, it.value) }
|
val albums = songsByAlbum.map { AlbumImpl(it.key.value, settings, it.value) }
|
||||||
logD("Successfully built ${albums.size} albums")
|
logD("Successfully built ${albums.size} albums in ${System.currentTimeMillis() - start}ms")
|
||||||
return albums
|
return albums
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +193,7 @@ private class DeviceLibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings
|
||||||
albums: List<AlbumImpl>,
|
albums: List<AlbumImpl>,
|
||||||
settings: MusicSettings
|
settings: MusicSettings
|
||||||
): List<ArtistImpl> {
|
): List<ArtistImpl> {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
// Add every raw artist credited to each Song/Album to the grouping. This way,
|
// Add every raw artist credited to each Song/Album to the grouping. This way,
|
||||||
// different multi-artist combinations are not treated as different artists.
|
// different multi-artist combinations are not treated as different artists.
|
||||||
// Songs and albums are grouped by artist and album artist respectively.
|
// Songs and albums are grouped by artist and album artist respectively.
|
||||||
|
@ -210,11 +213,13 @@ private class DeviceLibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings
|
||||||
|
|
||||||
// Convert the combined mapping into artist instances.
|
// Convert the combined mapping into artist instances.
|
||||||
val artists = musicByArtist.map { ArtistImpl(it.key.value, settings, it.value) }
|
val artists = musicByArtist.map { ArtistImpl(it.key.value, settings, it.value) }
|
||||||
logD("Successfully built ${artists.size} artists")
|
logD(
|
||||||
|
"Successfully built ${artists.size} artists in ${System.currentTimeMillis() - start}ms")
|
||||||
return artists
|
return artists
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildGenres(songs: List<SongImpl>, settings: MusicSettings): List<GenreImpl> {
|
private fun buildGenres(songs: List<SongImpl>, settings: MusicSettings): List<GenreImpl> {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
// Add every raw genre credited to each Song to the grouping. This way,
|
// Add every raw genre credited to each Song to the grouping. This way,
|
||||||
// different multi-genre combinations are not treated as different genres.
|
// different multi-genre combinations are not treated as different genres.
|
||||||
val songsByGenre = mutableMapOf<RawGenre.Key, MutableList<SongImpl>>()
|
val songsByGenre = mutableMapOf<RawGenre.Key, MutableList<SongImpl>>()
|
||||||
|
@ -226,7 +231,7 @@ private class DeviceLibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings
|
||||||
|
|
||||||
// Convert the mapping into genre instances.
|
// Convert the mapping into genre instances.
|
||||||
val genres = songsByGenre.map { GenreImpl(it.key.value, settings, it.value) }
|
val genres = songsByGenre.map { GenreImpl(it.key.value, settings, it.value) }
|
||||||
logD("Successfully built ${genres.size} genres")
|
logD("Successfully built ${genres.size} genres in ${System.currentTimeMillis() - start}ms")
|
||||||
return genres
|
return genres
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,9 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son
|
||||||
override val album: Album
|
override val album: Album
|
||||||
get() = unlikelyToBeNull(_album)
|
get() = unlikelyToBeNull(_album)
|
||||||
|
|
||||||
override fun hashCode() = 31 * uid.hashCode() + rawSong.hashCode()
|
private val hashCode = 31 * uid.hashCode() + rawSong.hashCode()
|
||||||
|
|
||||||
|
override fun hashCode() = hashCode
|
||||||
override fun equals(other: Any?) =
|
override fun equals(other: Any?) =
|
||||||
other is SongImpl && uid == other.uid && rawSong == other.rawSong
|
other is SongImpl && uid == other.uid && rawSong == other.rawSong
|
||||||
override fun toString() = "Song(uid=$uid, name=$name)"
|
override fun toString() = "Song(uid=$uid, name=$name)"
|
||||||
|
@ -253,22 +255,12 @@ class AlbumImpl(
|
||||||
override val durationMs: Long
|
override val durationMs: Long
|
||||||
override val dateAdded: Long
|
override val dateAdded: Long
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var hashCode = uid.hashCode()
|
|
||||||
hashCode = 31 * hashCode + rawAlbum.hashCode()
|
|
||||||
hashCode = 31 * hashCode + songs.hashCode()
|
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
|
||||||
other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs
|
|
||||||
|
|
||||||
override fun toString() = "Album(uid=$uid, name=$name)"
|
|
||||||
|
|
||||||
private val _artists = mutableListOf<ArtistImpl>()
|
private val _artists = mutableListOf<ArtistImpl>()
|
||||||
override val artists: List<Artist>
|
override val artists: List<Artist>
|
||||||
get() = _artists
|
get() = _artists
|
||||||
|
|
||||||
|
private var hashCode = uid.hashCode()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
var totalDuration: Long = 0
|
var totalDuration: Long = 0
|
||||||
var earliestDateAdded: Long = Long.MAX_VALUE
|
var earliestDateAdded: Long = Long.MAX_VALUE
|
||||||
|
@ -284,8 +276,16 @@ class AlbumImpl(
|
||||||
|
|
||||||
durationMs = totalDuration
|
durationMs = totalDuration
|
||||||
dateAdded = earliestDateAdded
|
dateAdded = earliestDateAdded
|
||||||
|
|
||||||
|
hashCode = 31 * hashCode + rawAlbum.hashCode()
|
||||||
|
hashCode = 31 * hashCode + songs.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode() = hashCode
|
||||||
|
override fun equals(other: Any?) =
|
||||||
|
other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs
|
||||||
|
override fun toString() = "Album(uid=$uid, name=$name)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [RawArtist] instances collated by the [Album]. The album artists of the song take
|
* The [RawArtist] instances collated by the [Album]. The album artists of the song take
|
||||||
* priority, followed by the artists. If there are no artists, this field will be a single
|
* priority, followed by the artists. If there are no artists, this field will be a single
|
||||||
|
@ -351,25 +351,10 @@ class ArtistImpl(
|
||||||
override val implicitAlbums: List<Album>
|
override val implicitAlbums: List<Album>
|
||||||
override val durationMs: Long?
|
override val durationMs: Long?
|
||||||
|
|
||||||
// Note: Append song contents to MusicParent equality so that artists with
|
|
||||||
// the same UID but different songs are not equal.
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var hashCode = uid.hashCode()
|
|
||||||
hashCode = 31 * hashCode + rawArtist.hashCode()
|
|
||||||
hashCode = 31 * hashCode + songs.hashCode()
|
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
|
||||||
other is ArtistImpl &&
|
|
||||||
uid == other.uid &&
|
|
||||||
rawArtist == other.rawArtist &&
|
|
||||||
songs == other.songs
|
|
||||||
|
|
||||||
override fun toString() = "Artist(uid=$uid, name=$name)"
|
|
||||||
|
|
||||||
override lateinit var genres: List<Genre>
|
override lateinit var genres: List<Genre>
|
||||||
|
|
||||||
|
private var hashCode = uid.hashCode()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val distinctSongs = mutableSetOf<Song>()
|
val distinctSongs = mutableSetOf<Song>()
|
||||||
val albumMap = mutableMapOf<Album, Boolean>()
|
val albumMap = mutableMapOf<Album, Boolean>()
|
||||||
|
@ -396,8 +381,23 @@ class ArtistImpl(
|
||||||
explicitAlbums = albums.filter { unlikelyToBeNull(albumMap[it]) }
|
explicitAlbums = albums.filter { unlikelyToBeNull(albumMap[it]) }
|
||||||
implicitAlbums = albums.filterNot { unlikelyToBeNull(albumMap[it]) }
|
implicitAlbums = albums.filterNot { unlikelyToBeNull(albumMap[it]) }
|
||||||
durationMs = songs.sumOf { it.durationMs }.nonZeroOrNull()
|
durationMs = songs.sumOf { it.durationMs }.nonZeroOrNull()
|
||||||
|
|
||||||
|
hashCode = 31 * hashCode + rawArtist.hashCode()
|
||||||
|
hashCode = 31 * hashCode + songs.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: Append song contents to MusicParent equality so that artists with
|
||||||
|
// the same UID but different songs are not equal.
|
||||||
|
override fun hashCode() = hashCode
|
||||||
|
|
||||||
|
override fun equals(other: Any?) =
|
||||||
|
other is ArtistImpl &&
|
||||||
|
uid == other.uid &&
|
||||||
|
rawArtist == other.rawArtist &&
|
||||||
|
songs == other.songs
|
||||||
|
|
||||||
|
override fun toString() = "Artist(uid=$uid, name=$name)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original position of this [Artist]'s [RawArtist] within the given [RawArtist]
|
* Returns the original position of this [Artist]'s [RawArtist] within the given [RawArtist]
|
||||||
* list. This can be used to create a consistent ordering within child [Artist] lists based on
|
* list. This can be used to create a consistent ordering within child [Artist] lists based on
|
||||||
|
@ -445,17 +445,7 @@ class GenreImpl(
|
||||||
override val artists: List<Artist>
|
override val artists: List<Artist>
|
||||||
override val durationMs: Long
|
override val durationMs: Long
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
private var hashCode = uid.hashCode()
|
||||||
var hashCode = uid.hashCode()
|
|
||||||
hashCode = 31 * hashCode + rawGenre.hashCode()
|
|
||||||
hashCode = 31 * hashCode + songs.hashCode()
|
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
|
||||||
other is GenreImpl && uid == other.uid && rawGenre == other.rawGenre && songs == other.songs
|
|
||||||
|
|
||||||
override fun toString() = "Genre(uid=$uid, name=$name)"
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val distinctAlbums = mutableSetOf<Album>()
|
val distinctAlbums = mutableSetOf<Album>()
|
||||||
|
@ -471,8 +461,17 @@ class GenreImpl(
|
||||||
|
|
||||||
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 + songs.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode() = hashCode
|
||||||
|
|
||||||
|
override fun equals(other: Any?) =
|
||||||
|
other is GenreImpl && uid == other.uid && rawGenre == other.rawGenre && songs == other.songs
|
||||||
|
|
||||||
|
override fun toString() = "Genre(uid=$uid, name=$name)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original position of this [Genre]'s [RawGenre] within the given [RawGenre] list.
|
* Returns the original position of this [Genre]'s [RawGenre] within the given [RawGenre] list.
|
||||||
* This can be used to create a consistent ordering within child [Genre] lists based on the
|
* This can be used to create a consistent ordering within child [Genre] lists based on the
|
||||||
|
|
|
@ -186,8 +186,8 @@ private abstract class BaseMediaStoreExtractor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logD("Read ${genreNamesMap.size} genres from MediaStore")
|
|
||||||
|
|
||||||
|
logD("Read ${genreNamesMap.values.distinct().size} genres from MediaStore")
|
||||||
logD("Finished initialization in ${System.currentTimeMillis() - start}ms")
|
logD("Finished initialization in ${System.currentTimeMillis() - start}ms")
|
||||||
return wrapQuery(cursor, genreNamesMap)
|
return wrapQuery(cursor, genreNamesMap)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,10 +73,9 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
||||||
return format.format(date)
|
return format.format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode() = tokens.hashCode()
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = other is Date && compareTo(other) == 0
|
override fun equals(other: Any?) = other is Date && compareTo(other) == 0
|
||||||
|
override fun hashCode() = tokens.hashCode()
|
||||||
|
override fun toString() = StringBuilder().appendDate().toString()
|
||||||
override fun compareTo(other: Date): Int {
|
override fun compareTo(other: Date): Int {
|
||||||
for (i in 0 until max(tokens.size, other.tokens.size)) {
|
for (i in 0 until max(tokens.size, other.tokens.size)) {
|
||||||
val ai = tokens.getOrNull(i)
|
val ai = tokens.getOrNull(i)
|
||||||
|
@ -97,8 +96,6 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = StringBuilder().appendDate().toString()
|
|
||||||
|
|
||||||
private fun StringBuilder.appendDate(): StringBuilder {
|
private fun StringBuilder.appendDate(): StringBuilder {
|
||||||
// Construct an ISO-8601 date, dropping precision that doesn't exist.
|
// Construct an ISO-8601 date, dropping precision that doesn't exist.
|
||||||
append(year.toStringFixed(4))
|
append(year.toStringFixed(4))
|
||||||
|
|
|
@ -33,6 +33,17 @@ private constructor(
|
||||||
override val songs: List<Song>
|
override val songs: List<Song>
|
||||||
) : Playlist {
|
) : Playlist {
|
||||||
override val durationMs = songs.sumOf { it.durationMs }
|
override val durationMs = songs.sumOf { it.durationMs }
|
||||||
|
private var hashCode = uid.hashCode()
|
||||||
|
|
||||||
|
init {
|
||||||
|
hashCode = 31 * hashCode + name.hashCode()
|
||||||
|
hashCode = 31 * hashCode + songs.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?) =
|
||||||
|
other is PlaylistImpl && uid == other.uid && name == other.name && songs == other.songs
|
||||||
|
override fun hashCode() = hashCode
|
||||||
|
override fun toString() = "Playlist(uid=$uid, name=$name)"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone the data in this instance to a new [PlaylistImpl] with the given [name].
|
* Clone the data in this instance to a new [PlaylistImpl] with the given [name].
|
||||||
|
@ -57,18 +68,6 @@ private constructor(
|
||||||
*/
|
*/
|
||||||
inline fun edit(edits: MutableList<Song>.() -> Unit) = edit(songs.toMutableList().apply(edits))
|
inline fun edit(edits: MutableList<Song>.() -> Unit) = edit(songs.toMutableList().apply(edits))
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
|
||||||
other is PlaylistImpl && uid == other.uid && name == other.name && songs == other.songs
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var hashCode = uid.hashCode()
|
|
||||||
hashCode = 31 * hashCode + name.hashCode()
|
|
||||||
hashCode = 31 * hashCode + songs.hashCode()
|
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = "Playlist(uid=$uid, name=$name)"
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Create a new instance with a novel UID.
|
* Create a new instance with a novel UID.
|
||||||
|
|
Loading…
Reference in a new issue