From 36bb729e672051c0e7f2a24d1c28a258fffdaf7f Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Tue, 19 Jul 2022 10:32:03 -0600 Subject: [PATCH] music: add additional album types Add compilation and soundtrack album type support. I find use in these, so implement them. --- .../oxycblt/auxio/detail/DetailViewModel.kt | 16 +++--- .../detail/recycler/AlbumDetailAdapter.kt | 2 +- .../java/org/oxycblt/auxio/music/Music.kt | 50 +++++++++++++++++-- .../java/org/oxycblt/auxio/music/MusicUtil.kt | 13 +---- .../auxio/music/system/ExoPlayerBackend.kt | 8 +-- .../org/oxycblt/auxio/music/system/Indexer.kt | 2 +- .../auxio/music/system/MediaStoreBackend.kt | 2 +- app/src/main/res/values/strings.xml | 4 ++ 8 files changed, 65 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index a5ba4d1c1..fcb159c9c 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -51,6 +51,8 @@ import org.oxycblt.auxio.util.unlikelyToBeNull * - The RecyclerView data for each fragment * - The sorts for each type of data * @author OxygenCobalt + * + * TODO: Unify how detail items are indicated */ class DetailViewModel(application: Application) : AndroidViewModel(application), MusicStore.Callback { @@ -242,16 +244,11 @@ class DetailViewModel(application: Application) : logD("Refreshing artist data") val data = mutableListOf(artist) val albums = Sort(Sort.Mode.ByYear, false).albums(artist.albums) - val byType = albums.groupBy { it.type ?: Album.Type.Album } - byType.keys.sorted().forEachIndexed { index, type -> - val typeString = - when (type) { - Album.Type.Album -> R.string.lbl_albums - Album.Type.EP -> R.string.lbl_eps - Album.Type.Single -> R.string.lbl_singles - } - data.add(Header(-2L - index, typeString)) + // Organize albums by their release type. We do not dor + val byType = albums.groupBy { it.type ?: Album.Type.ALBUM } + byType.keys.sorted().forEachIndexed { index, type -> + data.add(Header(-2L - index, type.pluralStringRes)) data.addAll(unlikelyToBeNull(byType[type])) } @@ -303,6 +300,7 @@ class DetailViewModel(application: Application) : val genre = currentGenre.value if (genre != null) { + logD("Genre changed, refreshing data") val newGenre = library.sanitize(genre).also { _currentGenre.value = it } if (newGenre != null) { refreshGenreData(newGenre) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt index 2e396971a..b04a15a8b 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt @@ -135,7 +135,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite if (item.type != null) { context.getString( R.string.fmt_four, - context.getString(item.type.string), + context.getString(item.type.stringRes), date, songCount, duration) diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index 3c0469738..51b3ef510 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -256,17 +256,57 @@ data class Album( } enum class Type { - Album, + ALBUM, EP, - Single; + SINGLE, + COMPILATION, + SOUNDTRACK; - val string: Int + // I only implemented the release types that I use. If there is sufficient demand, + // I'll extend them to these release types. + // REMIX, LIVE, MIXTAPE + + val stringRes: Int get() = when (this) { - Album -> R.string.lbl_album + ALBUM -> R.string.lbl_album EP -> R.string.lbl_ep - Single -> R.string.lbl_single + SINGLE -> R.string.lbl_single + COMPILATION -> R.string.lbl_compilation + SOUNDTRACK -> R.string.lbl_soundtrack } + + val pluralStringRes: Int + get() = + when (this) { + ALBUM -> R.string.lbl_albums + EP -> R.string.lbl_eps + SINGLE -> R.string.lbl_singles + COMPILATION -> R.string.lbl_compilations + SOUNDTRACK -> R.string.lbl_soundtracks + } + + companion object { + fun parse(type: String): Type { + // Release types (at least to MusicBrainz) are formatted as + + // where primary is something like "album", "ep", or "single", and secondary is + // "compilation", "soundtrack", etc. Use the secondary type as the album type before + // falling back to the primary type. + val primarySecondary = type.split('+').map { it.trim() } + return primarySecondary.getOrNull(1)?.parseReleaseType() + ?: primarySecondary[0].parseReleaseType() ?: ALBUM + } + + private fun String.parseReleaseType() = + when { + equals("album", ignoreCase = true) -> ALBUM + equals("ep", ignoreCase = true) -> EP + equals("single", ignoreCase = true) -> SINGLE + equals("compilation", ignoreCase = true) -> COMPILATION + equals("soundtrack", ignoreCase = true) -> SOUNDTRACK + else -> null + } + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt index 94a6b6777..834070942 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt @@ -100,17 +100,8 @@ fun String.parseSortName() = else -> this } -/** Parse a release type from this string into an [Album.Type]. Handles MusicBrainz separators. */ -fun String.parseReleaseType() = - parseReleaseTypeImpl() ?: split("+", limit = 2)[0].trim().parseReleaseTypeImpl() - -private fun String.parseReleaseTypeImpl() = - when (this) { - "album" -> Album.Type.Album - "ep" -> Album.Type.EP - "single" -> Album.Type.Single - else -> null - } +/** Shortcut to parse an [Album.Type] from a string */ +fun String.parseAlbumType() = Album.Type.parse(this) /** * Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt b/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt index fe8e954d3..3993896ac 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt @@ -28,9 +28,9 @@ import com.google.android.exoplayer2.metadata.vorbis.VorbisComment import org.oxycblt.auxio.music.Date import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.audioUri +import org.oxycblt.auxio.music.parseAlbumType import org.oxycblt.auxio.music.parseId3GenreName import org.oxycblt.auxio.music.parsePositionNum -import org.oxycblt.auxio.music.parseReleaseType import org.oxycblt.auxio.music.parseTimestamp import org.oxycblt.auxio.music.parseYear import org.oxycblt.auxio.util.logD @@ -247,8 +247,8 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) { // Genre, with the weird ID3 rules. tags["TCON"]?.let { audio.genre = it.parseId3GenreName() } - // Release type - (tags["TXXX:MusicBrainz Album Type"] ?: tags["GRP1"])?.parseReleaseType()?.let { + // Release type (GRP1 is sometimes used for this, so fall back to it) + (tags["TXXX:MusicBrainz Album Type"] ?: tags["GRP1"])?.parseAlbumType()?.let { audio.albumType = it } } @@ -311,7 +311,7 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) { tags["GENRE"]?.let { audio.genre = it } // Release type - tags["RELEASETYPE"]?.parseReleaseType()?.let { audio.albumType = it } + tags["RELEASETYPE"]?.parseAlbumType()?.let { audio.albumType = it } } /** diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt index 07c130033..ff4b0faff 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt @@ -302,7 +302,7 @@ class Indexer { // If album types aren't used by the music library (Represented by all songs having // an album type), there is no point in displaying them. - val enableAlbumTypes = songs.any { it._albumType != Album.Type.Album } + val enableAlbumTypes = songs.any { it._albumType != Album.Type.ALBUM } if (!enableAlbumTypes) { logD("No distinct album types detected, ignoring them") } diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt b/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt index c4fa1d0f9..a57fc0003 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt @@ -371,7 +371,7 @@ abstract class MediaStoreBackend : Indexer.Backend { _date = date, _albumName = requireNotNull(album) { "Malformed audio: No album name" }, _albumSortName = sortAlbum, - _albumType = albumType ?: Album.Type.Album, + _albumType = albumType ?: Album.Type.ALBUM, _albumCoverUri = requireNotNull(albumId) { "Malformed audio: No album id" }.albumCoverUri, _artistName = artist, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 255c56fe0..90c85f072 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,10 @@ EPs Single Singles + Compilation + Compilations + Soundtrack + Soundtracks Artist Artists