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> {
|
||||
val start = System.currentTimeMillis()
|
||||
val songs =
|
||||
Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
|
||||
.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
|
||||
}
|
||||
|
||||
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
|
||||
// grouped songs to Album values. Album.Raw will handle the actual grouping rules.
|
||||
val songsByAlbum = songs.groupBy { it.rawAlbum.key }
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -191,6 +193,7 @@ private class DeviceLibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings
|
|||
albums: List<AlbumImpl>,
|
||||
settings: MusicSettings
|
||||
): List<ArtistImpl> {
|
||||
val start = System.currentTimeMillis()
|
||||
// Add every raw artist credited to each Song/Album to the grouping. This way,
|
||||
// different multi-artist combinations are not treated as different artists.
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
// different multi-genre combinations are not treated as different genres.
|
||||
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.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,9 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son
|
|||
override val album: 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?) =
|
||||
other is SongImpl && uid == other.uid && rawSong == other.rawSong
|
||||
override fun toString() = "Song(uid=$uid, name=$name)"
|
||||
|
@ -253,22 +255,12 @@ class AlbumImpl(
|
|||
override val durationMs: 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>()
|
||||
override val artists: List<Artist>
|
||||
get() = _artists
|
||||
|
||||
private var hashCode = uid.hashCode()
|
||||
|
||||
init {
|
||||
var totalDuration: Long = 0
|
||||
var earliestDateAdded: Long = Long.MAX_VALUE
|
||||
|
@ -284,8 +276,16 @@ class AlbumImpl(
|
|||
|
||||
durationMs = totalDuration
|
||||
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
|
||||
* 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 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>
|
||||
|
||||
private var hashCode = uid.hashCode()
|
||||
|
||||
init {
|
||||
val distinctSongs = mutableSetOf<Song>()
|
||||
val albumMap = mutableMapOf<Album, Boolean>()
|
||||
|
@ -396,8 +381,23 @@ class ArtistImpl(
|
|||
explicitAlbums = albums.filter { unlikelyToBeNull(albumMap[it]) }
|
||||
implicitAlbums = albums.filterNot { unlikelyToBeNull(albumMap[it]) }
|
||||
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]
|
||||
* 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 durationMs: Long
|
||||
|
||||
override fun hashCode(): Int {
|
||||
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)"
|
||||
private var hashCode = uid.hashCode()
|
||||
|
||||
init {
|
||||
val distinctAlbums = mutableSetOf<Album>()
|
||||
|
@ -471,8 +461,17 @@ class GenreImpl(
|
|||
|
||||
artists = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING).artists(distinctArtists)
|
||||
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.
|
||||
* 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")
|
||||
return wrapQuery(cursor, genreNamesMap)
|
||||
}
|
||||
|
|
|
@ -73,10 +73,9 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
return format.format(date)
|
||||
}
|
||||
|
||||
override fun hashCode() = tokens.hashCode()
|
||||
|
||||
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 {
|
||||
for (i in 0 until max(tokens.size, other.tokens.size)) {
|
||||
val ai = tokens.getOrNull(i)
|
||||
|
@ -97,8 +96,6 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
return 0
|
||||
}
|
||||
|
||||
override fun toString() = StringBuilder().appendDate().toString()
|
||||
|
||||
private fun StringBuilder.appendDate(): StringBuilder {
|
||||
// Construct an ISO-8601 date, dropping precision that doesn't exist.
|
||||
append(year.toStringFixed(4))
|
||||
|
|
|
@ -33,6 +33,17 @@ private constructor(
|
|||
override val songs: List<Song>
|
||||
) : Playlist {
|
||||
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].
|
||||
|
@ -57,18 +68,6 @@ private constructor(
|
|||
*/
|
||||
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 {
|
||||
/**
|
||||
* Create a new instance with a novel UID.
|
||||
|
|
Loading…
Reference in a new issue