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 RecyclerView data for each fragment
|
||||||
* - The sorts for each type of data
|
* - The sorts for each type of data
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
|
*
|
||||||
|
* TODO: Unify how detail items are indicated
|
||||||
*/
|
*/
|
||||||
class DetailViewModel(application: Application) :
|
class DetailViewModel(application: Application) :
|
||||||
AndroidViewModel(application), MusicStore.Callback {
|
AndroidViewModel(application), MusicStore.Callback {
|
||||||
|
@ -242,16 +244,11 @@ class DetailViewModel(application: Application) :
|
||||||
logD("Refreshing artist data")
|
logD("Refreshing artist data")
|
||||||
val data = mutableListOf<Item>(artist)
|
val data = mutableListOf<Item>(artist)
|
||||||
val albums = Sort(Sort.Mode.ByYear, false).albums(artist.albums)
|
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]))
|
data.addAll(unlikelyToBeNull(byType[type]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +300,7 @@ class DetailViewModel(application: Application) :
|
||||||
|
|
||||||
val genre = currentGenre.value
|
val genre = currentGenre.value
|
||||||
if (genre != null) {
|
if (genre != null) {
|
||||||
|
logD("Genre changed, refreshing data")
|
||||||
val newGenre = library.sanitize(genre).also { _currentGenre.value = it }
|
val newGenre = library.sanitize(genre).also { _currentGenre.value = it }
|
||||||
if (newGenre != null) {
|
if (newGenre != null) {
|
||||||
refreshGenreData(newGenre)
|
refreshGenreData(newGenre)
|
||||||
|
|
|
@ -135,7 +135,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
||||||
if (item.type != null) {
|
if (item.type != null) {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.fmt_four,
|
R.string.fmt_four,
|
||||||
context.getString(item.type.string),
|
context.getString(item.type.stringRes),
|
||||||
date,
|
date,
|
||||||
songCount,
|
songCount,
|
||||||
duration)
|
duration)
|
||||||
|
|
|
@ -256,16 +256,56 @@ data class Album(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Album,
|
ALBUM,
|
||||||
EP,
|
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() =
|
get() =
|
||||||
when (this) {
|
when (this) {
|
||||||
Album -> R.string.lbl_album
|
ALBUM -> R.string.lbl_album
|
||||||
EP -> R.string.lbl_ep
|
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
|
else -> this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse a release type from this string into an [Album.Type]. Handles MusicBrainz separators. */
|
/** Shortcut to parse an [Album.Type] from a string */
|
||||||
fun String.parseReleaseType() =
|
fun String.parseAlbumType() = Album.Type.parse(this)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
* 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.Date
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.audioUri
|
import org.oxycblt.auxio.music.audioUri
|
||||||
|
import org.oxycblt.auxio.music.parseAlbumType
|
||||||
import org.oxycblt.auxio.music.parseId3GenreName
|
import org.oxycblt.auxio.music.parseId3GenreName
|
||||||
import org.oxycblt.auxio.music.parsePositionNum
|
import org.oxycblt.auxio.music.parsePositionNum
|
||||||
import org.oxycblt.auxio.music.parseReleaseType
|
|
||||||
import org.oxycblt.auxio.music.parseTimestamp
|
import org.oxycblt.auxio.music.parseTimestamp
|
||||||
import org.oxycblt.auxio.music.parseYear
|
import org.oxycblt.auxio.music.parseYear
|
||||||
import org.oxycblt.auxio.util.logD
|
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.
|
// Genre, with the weird ID3 rules.
|
||||||
tags["TCON"]?.let { audio.genre = it.parseId3GenreName() }
|
tags["TCON"]?.let { audio.genre = it.parseId3GenreName() }
|
||||||
|
|
||||||
// Release type
|
// Release type (GRP1 is sometimes used for this, so fall back to it)
|
||||||
(tags["TXXX:MusicBrainz Album Type"] ?: tags["GRP1"])?.parseReleaseType()?.let {
|
(tags["TXXX:MusicBrainz Album Type"] ?: tags["GRP1"])?.parseAlbumType()?.let {
|
||||||
audio.albumType = it
|
audio.albumType = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +311,7 @@ class Task(context: Context, private val audio: MediaStoreBackend.Audio) {
|
||||||
tags["GENRE"]?.let { audio.genre = it }
|
tags["GENRE"]?.let { audio.genre = it }
|
||||||
|
|
||||||
// Release type
|
// 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
|
// 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.
|
// 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) {
|
if (!enableAlbumTypes) {
|
||||||
logD("No distinct album types detected, ignoring them")
|
logD("No distinct album types detected, ignoring them")
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,7 +371,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
|
||||||
_date = date,
|
_date = date,
|
||||||
_albumName = requireNotNull(album) { "Malformed audio: No album name" },
|
_albumName = requireNotNull(album) { "Malformed audio: No album name" },
|
||||||
_albumSortName = sortAlbum,
|
_albumSortName = sortAlbum,
|
||||||
_albumType = albumType ?: Album.Type.Album,
|
_albumType = albumType ?: Album.Type.ALBUM,
|
||||||
_albumCoverUri =
|
_albumCoverUri =
|
||||||
requireNotNull(albumId) { "Malformed audio: No album id" }.albumCoverUri,
|
requireNotNull(albumId) { "Malformed audio: No album id" }.albumCoverUri,
|
||||||
_artistName = artist,
|
_artistName = artist,
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
<string name="lbl_eps">EPs</string>
|
<string name="lbl_eps">EPs</string>
|
||||||
<string name="lbl_single">Single</string>
|
<string name="lbl_single">Single</string>
|
||||||
<string name="lbl_singles">Singles</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_artist">Artist</string>
|
||||||
<string name="lbl_artists">Artists</string>
|
<string name="lbl_artists">Artists</string>
|
||||||
|
|
Loading…
Reference in a new issue