diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index 78fefe345..80f27daf6 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -65,17 +65,17 @@ data class Song( /** The track number of this song. */ val track: Int, /** Internal field. Do not use. */ - val _mediaStoreId: Long, + val internalMediaStoreId: Long, /** Internal field. Do not use. */ - val _mediaStoreArtistName: String?, + val internalMediaStoreArtistName: String?, /** Internal field. Do not use. */ - val _mediaStoreAlbumArtistName: String?, + val internalMediaStoreAlbumArtistName: String?, /** Internal field. Do not use. */ - val _mediaStoreAlbumId: Long, + val internalMediaStoreAlbumId: Long, /** Internal field. Do not use. */ - val _mediaStoreAlbumName: String, + val internalMediaStoreAlbumName: String, /** Internal field. Do not use. */ - val _mediaStoreYear: Int + val internalMediaStoreYear: Int ) : Music() { override val id: Long get() { var result = name.hashCode().toLong() @@ -88,7 +88,7 @@ data class Song( /** The URI for this song. */ val uri: Uri get() = ContentUris.withAppendedId( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, _mediaStoreId + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, internalMediaStoreId ) /** The duration of this song, in seconds (rounded down) */ val seconds: Long get() = duration / 1000 @@ -100,8 +100,8 @@ data class Song( val album: Album get() = requireNotNull(mAlbum) var mGenre: Genre? = null - /** The genre of this song. May be null due to MediaStore insanity. */ - val genre: Genre? get() = mGenre + /** The genre of this song. Will be an "unknown genre" if the song does not have any. */ + val genre: Genre get() = requireNotNull(mGenre) /** An album name resolved to this song in particular. */ val resolvedAlbumName: String get() = @@ -109,15 +109,29 @@ data class Song( /** An artist name resolved to this song in particular. */ val resolvedArtistName: String get() = - _mediaStoreArtistName ?: album.artist.resolvedName + internalMediaStoreArtistName ?: album.artist.resolvedName + + /** Internal field. Do not use. */ + val internalGroupingId: Int get() { + var result = internalGroupingArtistName.lowercase().hashCode() + result = 31 * result + internalMediaStoreAlbumName.lowercase().hashCode() + return result + } + + /** Internal field. Do not use. */ + val internalGroupingArtistName: String get() = internalMediaStoreAlbumArtistName + ?: internalMediaStoreArtistName ?: MediaStore.UNKNOWN_STRING + + /** Internal field. Do not use. **/ + val internalMissingGenre: Boolean get() = mGenre == null /** Internal method. Do not use. */ - fun mediaStoreLinkAlbum(album: Album) { + fun internalLinkAlbum(album: Album) { mAlbum = album } /** Internal method. Do not use. */ - fun mediaStoreLinkGenre(genre: Genre) { + fun internalLinkGenre(genre: Genre) { mGenre = genre } } @@ -134,11 +148,11 @@ data class Album( /** The songs of this album. */ val songs: List, /** Internal field. Do not use. */ - val _mediaStoreArtistName: String, + val internalGroupingArtistName: String, ) : MusicParent() { init { for (song in songs) { - song.mediaStoreLinkAlbum(this) + song.internalLinkAlbum(this) } } @@ -165,7 +179,7 @@ data class Album( artist.resolvedName /** Internal method. Do not use. */ - fun mediaStoreLinkArtist(artist: Artist) { + fun internalLinkArtist(artist: Artist) { mArtist = artist } } @@ -182,7 +196,7 @@ data class Artist( ) : MusicParent() { init { for (album in albums) { - album.mediaStoreLinkArtist(this) + album.internalLinkArtist(this) } } @@ -202,7 +216,7 @@ data class Genre( ) : MusicParent() { init { for (song in songs) { - song.mediaStoreLinkGenre(this) + song.internalLinkGenre(this) } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt index f6a5cbdc6..e6088f2d2 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt @@ -92,6 +92,7 @@ class MusicLoader { for (song in songs) { try { song.album.artist + song.genre } catch (e: Exception) { logE("Found malformed song: ${song.name}") throw e @@ -187,18 +188,13 @@ class MusicLoader { } songs = songs.distinctBy { - it.name to it._mediaStoreAlbumName to it._mediaStoreArtistName to it._mediaStoreAlbumArtistName to it.track to it.duration + it.name to it.internalMediaStoreAlbumName to it.internalMediaStoreArtistName to it.internalMediaStoreAlbumArtistName to it.track to it.duration }.toMutableList() return songs } private fun buildAlbums(songs: List): List { - // When assigning an artist to an album, use the album artist first, then the - // normal artist, and then the internal representation of an unknown artist name. - fun Song.resolveAlbumArtistName() = _mediaStoreAlbumArtistName ?: _mediaStoreArtistName - ?: MediaStore.UNKNOWN_STRING - // Group up songs by their lowercase artist and album name. This serves two purposes: // 1. Sometimes artist names can be styled differently, e.g "Rammstein" vs. "RAMMSTEIN". // This makes sure both of those are resolved into a single artist called "Rammstein" @@ -209,9 +205,7 @@ class MusicLoader { // the template, but it seems to work pretty well. val albums = mutableListOf() val songsByAlbum = songs.groupBy { song -> - val albumName = song._mediaStoreAlbumName - val artistName = song.resolveAlbumArtistName() - Pair(albumName.lowercase(), artistName.lowercase()) + song.internalGroupingId } for (entry in songsByAlbum) { @@ -220,14 +214,14 @@ class MusicLoader { // Use the song with the latest year as our metadata song. // This allows us to replicate the LAST_YEAR field, which is useful as it means that // weird years like "0" wont show up if there are alternatives. - val templateSong = requireNotNull(albumSongs.maxByOrNull { it._mediaStoreYear }) - val albumName = templateSong._mediaStoreAlbumName - val albumYear = templateSong._mediaStoreYear + val templateSong = requireNotNull(albumSongs.maxByOrNull { it.internalMediaStoreYear }) + val albumName = templateSong.internalMediaStoreAlbumName + val albumYear = templateSong.internalMediaStoreYear val albumCoverUri = ContentUris.withAppendedId( Uri.parse("content://media/external/audio/albumart"), - templateSong._mediaStoreAlbumId + templateSong.internalMediaStoreAlbumId ) - val artistName = templateSong.resolveAlbumArtistName() + val artistName = templateSong.internalGroupingArtistName albums.add( Album( @@ -245,7 +239,7 @@ class MusicLoader { private fun buildArtists(context: Context, albums: List): List { val artists = mutableListOf() - val albumsByArtist = albums.groupBy { it._mediaStoreArtistName } + val albumsByArtist = albums.groupBy { it.internalGroupingArtistName } for (entry in albumsByArtist) { val artistName = entry.key @@ -318,7 +312,7 @@ class MusicLoader { } } - val songsWithoutGenres = songs.filter { it.genre == null } + val songsWithoutGenres = songs.filter { it.internalMissingGenre } if (songsWithoutGenres.isNotEmpty()) { // Songs that don't have a genre will be thrown into an unknown genre. @@ -350,7 +344,7 @@ class MusicLoader { while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) - songs.find { it._mediaStoreId == id }?.let { song -> + songs.find { it.internalMediaStoreId == id }?.let { song -> genreSongs.add(song) } }