music: add additional album types

Add compilation and soundtrack album type support.

I find use in these, so implement them.
This commit is contained in:
OxygenCobalt 2022-07-19 10:32:03 -06:00
parent 24062aa623
commit 36bb729e67
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 65 additions and 32 deletions

View file

@ -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<Item>(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)

View file

@ -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)

View file

@ -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 <primary> + <secondary>
// 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
}
}
}
}

View file

@ -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

View file

@ -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 }
}
/**

View file

@ -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")
}

View file

@ -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,

View file

@ -23,6 +23,10 @@
<string name="lbl_eps">EPs</string>
<string name="lbl_single">Single</string>
<string name="lbl_singles">Singles</string>
<string name="lbl_compilation">Compilation</string>
<string name="lbl_compilations">Compilations</string>
<string name="lbl_soundtrack">Soundtrack</string>
<string name="lbl_soundtracks">Soundtracks</string>
<string name="lbl_artist">Artist</string>
<string name="lbl_artists">Artists</string>