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:
parent
24062aa623
commit
36bb729e67
8 changed files with 65 additions and 32 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue