music: make package for auxillary music info
Like the library package, move out tag information (Date/Album.Type) into a separate package. Date never really made sense as base-package information.
This commit is contained in:
parent
a2b51825e8
commit
5adc87550e
14 changed files with 338 additions and 317 deletions
|
@ -37,6 +37,7 @@ import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.library.Library
|
import org.oxycblt.auxio.music.library.Library
|
||||||
import org.oxycblt.auxio.music.library.Sort
|
import org.oxycblt.auxio.music.library.Sort
|
||||||
import org.oxycblt.auxio.music.storage.MimeType
|
import org.oxycblt.auxio.music.storage.MimeType
|
||||||
|
import org.oxycblt.auxio.music.tags.ReleaseType
|
||||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
|
@ -344,21 +345,21 @@ class DetailViewModel(application: Application) :
|
||||||
|
|
||||||
val byReleaseGroup =
|
val byReleaseGroup =
|
||||||
albums.groupBy {
|
albums.groupBy {
|
||||||
// Remap the complicated Album.Type data structure into an easier
|
// Remap the complicated ReleaseType data structure into an easier
|
||||||
// "AlbumGrouping" enum that will automatically group and sort
|
// "AlbumGrouping" enum that will automatically group and sort
|
||||||
// the artist's albums.
|
// the artist's albums.
|
||||||
when (it.type.refinement) {
|
when (it.releaseType.refinement) {
|
||||||
Album.Type.Refinement.LIVE -> AlbumGrouping.LIVE
|
ReleaseType.Refinement.LIVE -> AlbumGrouping.LIVE
|
||||||
Album.Type.Refinement.REMIX -> AlbumGrouping.REMIXES
|
ReleaseType.Refinement.REMIX -> AlbumGrouping.REMIXES
|
||||||
null ->
|
null ->
|
||||||
when (it.type) {
|
when (it.releaseType) {
|
||||||
is Album.Type.Album -> AlbumGrouping.ALBUMS
|
is ReleaseType.Album -> AlbumGrouping.ALBUMS
|
||||||
is Album.Type.EP -> AlbumGrouping.EPS
|
is ReleaseType.EP -> AlbumGrouping.EPS
|
||||||
is Album.Type.Single -> AlbumGrouping.SINGLES
|
is ReleaseType.Single -> AlbumGrouping.SINGLES
|
||||||
is Album.Type.Compilation -> AlbumGrouping.COMPILATIONS
|
is ReleaseType.Compilation -> AlbumGrouping.COMPILATIONS
|
||||||
is Album.Type.Soundtrack -> AlbumGrouping.SOUNDTRACKS
|
is ReleaseType.Soundtrack -> AlbumGrouping.SOUNDTRACKS
|
||||||
is Album.Type.Mix -> AlbumGrouping.MIXES
|
is ReleaseType.Mix -> AlbumGrouping.MIXES
|
||||||
is Album.Type.Mixtape -> AlbumGrouping.MIXTAPES
|
is ReleaseType.Mixtape -> AlbumGrouping.MIXTAPES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +393,7 @@ class DetailViewModel(application: Application) :
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simpler mapping of [Album.Type] used for grouping and sorting songs.
|
* A simpler mapping of [ReleaseType] used for grouping and sorting songs.
|
||||||
* @param headerTitleRes The title string resource to use for a header created out of an
|
* @param headerTitleRes The title string resource to use for a header created out of an
|
||||||
* instance of this enum.
|
* instance of this enum.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -126,7 +126,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
||||||
binding.detailCover.bind(album)
|
binding.detailCover.bind(album)
|
||||||
|
|
||||||
// The type text depends on the release type (Album, EP, Single, etc.)
|
// The type text depends on the release type (Album, EP, Single, etc.)
|
||||||
binding.detailType.text = binding.context.getString(album.type.stringRes)
|
binding.detailType.text = binding.context.getString(album.releaseType.stringRes)
|
||||||
|
|
||||||
binding.detailName.text = album.resolveName(binding.context)
|
binding.detailName.text = album.resolveName(binding.context)
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
||||||
oldItem.dates == newItem.dates &&
|
oldItem.dates == newItem.dates &&
|
||||||
oldItem.songs.size == newItem.songs.size &&
|
oldItem.songs.size == newItem.songs.size &&
|
||||||
oldItem.durationMs == newItem.durationMs &&
|
oldItem.durationMs == newItem.durationMs &&
|
||||||
oldItem.type == newItem.type
|
oldItem.releaseType == newItem.releaseType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
|
||||||
override fun areContentsTheSame(oldItem: Album, newItem: Album) =
|
override fun areContentsTheSame(oldItem: Album, newItem: Album) =
|
||||||
oldItem.rawName == newItem.rawName &&
|
oldItem.rawName == newItem.rawName &&
|
||||||
oldItem.areArtistContentsTheSame(newItem) &&
|
oldItem.areArtistContentsTheSame(newItem) &&
|
||||||
oldItem.type == newItem.type
|
oldItem.releaseType == newItem.releaseType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ import org.oxycblt.auxio.music.library.Sort
|
||||||
import org.oxycblt.auxio.music.parsing.parseId3GenreNames
|
import org.oxycblt.auxio.music.parsing.parseId3GenreNames
|
||||||
import org.oxycblt.auxio.music.parsing.parseMultiValue
|
import org.oxycblt.auxio.music.parsing.parseMultiValue
|
||||||
import org.oxycblt.auxio.music.storage.*
|
import org.oxycblt.auxio.music.storage.*
|
||||||
|
import org.oxycblt.auxio.music.tags.Date
|
||||||
|
import org.oxycblt.auxio.music.tags.ReleaseType
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
|
|
||||||
|
@ -463,7 +465,7 @@ class Song constructor(raw: Raw, musicSettings: MusicSettings) : Music() {
|
||||||
musicBrainzId = raw.albumMusicBrainzId?.toUuidOrNull(),
|
musicBrainzId = raw.albumMusicBrainzId?.toUuidOrNull(),
|
||||||
name = requireNotNull(raw.albumName) { "Invalid raw: No album name" },
|
name = requireNotNull(raw.albumName) { "Invalid raw: No album name" },
|
||||||
sortName = raw.albumSortName,
|
sortName = raw.albumSortName,
|
||||||
type = Album.Type.parse(raw.albumTypes.parseMultiValue(musicSettings)),
|
releaseType = ReleaseType.parse(raw.releaseTypes.parseMultiValue(musicSettings)),
|
||||||
rawArtists =
|
rawArtists =
|
||||||
rawAlbumArtists.ifEmpty { rawArtists }.ifEmpty { listOf(Artist.Raw(null, null)) })
|
rawAlbumArtists.ifEmpty { rawArtists }.ifEmpty { listOf(Artist.Raw(null, null)) })
|
||||||
|
|
||||||
|
@ -582,8 +584,8 @@ class Song constructor(raw: Raw, musicSettings: MusicSettings) : Music() {
|
||||||
var albumName: String? = null,
|
var albumName: String? = null,
|
||||||
/** @see Album.Raw.sortName */
|
/** @see Album.Raw.sortName */
|
||||||
var albumSortName: String? = null,
|
var albumSortName: String? = null,
|
||||||
/** @see Album.Raw.type */
|
/** @see Album.Raw.releaseType */
|
||||||
var albumTypes: List<String> = listOf(),
|
var releaseTypes: List<String> = listOf(),
|
||||||
/** @see Artist.Raw.musicBrainzId */
|
/** @see Artist.Raw.musicBrainzId */
|
||||||
var artistMusicBrainzIds: List<String> = listOf(),
|
var artistMusicBrainzIds: List<String> = listOf(),
|
||||||
/** @see Artist.Raw.name */
|
/** @see Artist.Raw.name */
|
||||||
|
@ -629,10 +631,10 @@ class Album constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
||||||
val dates = Date.Range.from(songs.mapNotNull { it.date })
|
val dates = Date.Range.from(songs.mapNotNull { it.date })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [Type] of this album, signifying the type of release it actually is. Defaults to
|
* The [ReleaseType] of this album, signifying the type of release it actually is. Defaults to
|
||||||
* [Type.Album].
|
* [ReleaseType.Album].
|
||||||
*/
|
*/
|
||||||
val type = raw.type ?: Type.Album(null)
|
val releaseType = raw.releaseType ?: ReleaseType.Album(null)
|
||||||
/**
|
/**
|
||||||
* The URI to a MediaStore-provided album cover. These images will be fast to load, but at the
|
* The URI to a MediaStore-provided album cover. These images will be fast to load, but at the
|
||||||
* cost of image quality.
|
* cost of image quality.
|
||||||
|
@ -727,201 +729,6 @@ class Album constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of release an [Album] is considered. This includes EPs, Singles, Compilations, etc.
|
|
||||||
*
|
|
||||||
* This class is derived from the MusicBrainz Release Group Type specification. It can be found
|
|
||||||
* at: https://musicbrainz.org/doc/Release_Group/Type
|
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
|
||||||
*/
|
|
||||||
sealed class Type {
|
|
||||||
/**
|
|
||||||
* A specification of what kind of performance this release is. If null, the release is
|
|
||||||
* considered "Plain".
|
|
||||||
*/
|
|
||||||
abstract val refinement: Refinement?
|
|
||||||
|
|
||||||
/** The string resource corresponding to the name of this release type to show in the UI. */
|
|
||||||
abstract val stringRes: Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A plain album.
|
|
||||||
* @param refinement A specification of what kind of performance this release is. If null,
|
|
||||||
* the release is considered "Plain".
|
|
||||||
*/
|
|
||||||
data class Album(override val refinement: Refinement?) : Type() {
|
|
||||||
override val stringRes: Int
|
|
||||||
get() =
|
|
||||||
when (refinement) {
|
|
||||||
null -> R.string.lbl_album
|
|
||||||
// If present, include the refinement in the name of this release type.
|
|
||||||
Refinement.LIVE -> R.string.lbl_album_live
|
|
||||||
Refinement.REMIX -> R.string.lbl_album_remix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "Extended Play", or EP. Usually a smaller release consisting of 4-5 songs.
|
|
||||||
* @param refinement A specification of what kind of performance this release is. If null,
|
|
||||||
* the release is considered "Plain".
|
|
||||||
*/
|
|
||||||
data class EP(override val refinement: Refinement?) : Type() {
|
|
||||||
override val stringRes: Int
|
|
||||||
get() =
|
|
||||||
when (refinement) {
|
|
||||||
null -> R.string.lbl_ep
|
|
||||||
// If present, include the refinement in the name of this release type.
|
|
||||||
Refinement.LIVE -> R.string.lbl_ep_live
|
|
||||||
Refinement.REMIX -> R.string.lbl_ep_remix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A single. Usually a release consisting of 1-2 songs.
|
|
||||||
* @param refinement A specification of what kind of performance this release is. If null,
|
|
||||||
* the release is considered "Plain".
|
|
||||||
*/
|
|
||||||
data class Single(override val refinement: Refinement?) : Type() {
|
|
||||||
override val stringRes: Int
|
|
||||||
get() =
|
|
||||||
when (refinement) {
|
|
||||||
null -> R.string.lbl_single
|
|
||||||
// If present, include the refinement in the name of this release type.
|
|
||||||
Refinement.LIVE -> R.string.lbl_single_live
|
|
||||||
Refinement.REMIX -> R.string.lbl_single_remix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A compilation. Usually consists of many songs from a variety of artists.
|
|
||||||
* @param refinement A specification of what kind of performance this release is. If null,
|
|
||||||
* the release is considered "Plain".
|
|
||||||
*/
|
|
||||||
data class Compilation(override val refinement: Refinement?) : Type() {
|
|
||||||
override val stringRes: Int
|
|
||||||
get() =
|
|
||||||
when (refinement) {
|
|
||||||
null -> R.string.lbl_compilation
|
|
||||||
// If present, include the refinement in the name of this release type.
|
|
||||||
Refinement.LIVE -> R.string.lbl_compilation_live
|
|
||||||
Refinement.REMIX -> R.string.lbl_compilation_remix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A soundtrack. Similar to a [Compilation], but created for a specific piece of (usually
|
|
||||||
* visual) media.
|
|
||||||
*/
|
|
||||||
object Soundtrack : Type() {
|
|
||||||
override val refinement: Refinement?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
override val stringRes: Int
|
|
||||||
get() = R.string.lbl_soundtrack
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A (DJ) Mix. These are usually one large track consisting of the artist playing several
|
|
||||||
* sub-tracks with smooth transitions between them.
|
|
||||||
*/
|
|
||||||
object Mix : Type() {
|
|
||||||
override val refinement: Refinement?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
override val stringRes: Int
|
|
||||||
get() = R.string.lbl_mix
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Mix-tape. These are usually [EP]-sized releases of music made to promote an [Artist] or
|
|
||||||
* a future release.
|
|
||||||
*/
|
|
||||||
object Mixtape : Type() {
|
|
||||||
override val refinement: Refinement?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
override val stringRes: Int
|
|
||||||
get() = R.string.lbl_mixtape
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A specification of what kind of performance a particular release is. */
|
|
||||||
enum class Refinement {
|
|
||||||
/** A release consisting of a live performance */
|
|
||||||
LIVE,
|
|
||||||
|
|
||||||
/** A release consisting of another [Artist]s remix of a prior performance. */
|
|
||||||
REMIX
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Parse a [Type] from a string formatted with the MusicBrainz Release Group Type
|
|
||||||
* specification.
|
|
||||||
* @param types A list of values consisting of valid release type values.
|
|
||||||
* @return A [Type] consisting of the given types, or null if the types were not valid.
|
|
||||||
*/
|
|
||||||
fun parse(types: List<String>): Type? {
|
|
||||||
val primary = types.getOrNull(0) ?: return null
|
|
||||||
return when {
|
|
||||||
// Primary types should be the first types in the sequence.
|
|
||||||
primary.equals("album", true) -> types.parseSecondaryTypes(1) { Album(it) }
|
|
||||||
primary.equals("ep", true) -> types.parseSecondaryTypes(1) { EP(it) }
|
|
||||||
primary.equals("single", true) -> types.parseSecondaryTypes(1) { Single(it) }
|
|
||||||
// The spec makes no mention of whether primary types are a pre-requisite for
|
|
||||||
// secondary types, so we assume that it's not and map oprhan secondary types
|
|
||||||
// to Album release types.
|
|
||||||
else -> types.parseSecondaryTypes(0) { Album(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse "secondary" types (i.e not [Album], [EP], or [Single]) from a string formatted
|
|
||||||
* with the MusicBrainz Release Group Type specification.
|
|
||||||
* @param index The index of the release type to parse.
|
|
||||||
* @param convertRefinement Code to convert a [Refinement] into a [Type] corresponding
|
|
||||||
* to the callee's context. This is used in order to handle secondary times that are
|
|
||||||
* actually [Refinement]s.
|
|
||||||
* @return A [Type] corresponding to the secondary type found at that index.
|
|
||||||
*/
|
|
||||||
private inline fun List<String>.parseSecondaryTypes(
|
|
||||||
index: Int,
|
|
||||||
convertRefinement: (Refinement?) -> Type
|
|
||||||
): Type {
|
|
||||||
val secondary = getOrNull(index)
|
|
||||||
return if (secondary.equals("compilation", true)) {
|
|
||||||
// Secondary type is a compilation, actually parse the third type
|
|
||||||
// and put that into a compilation if needed.
|
|
||||||
parseSecondaryTypeImpl(getOrNull(index + 1)) { Compilation(it) }
|
|
||||||
} else {
|
|
||||||
// Secondary type is a plain value, use the original values given.
|
|
||||||
parseSecondaryTypeImpl(secondary, convertRefinement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse "secondary" types (i.e not [Album], [EP], [Single]) that do not correspond to
|
|
||||||
* any child values.
|
|
||||||
* @param type The release type value to parse.
|
|
||||||
* @param convertRefinement Code to convert a [Refinement] into a [Type] corresponding
|
|
||||||
* to the callee's context. This is used in order to handle secondary times that are
|
|
||||||
* actually [Refinement]s.
|
|
||||||
*/
|
|
||||||
private inline fun parseSecondaryTypeImpl(
|
|
||||||
type: String?,
|
|
||||||
convertRefinement: (Refinement?) -> Type
|
|
||||||
) =
|
|
||||||
when {
|
|
||||||
// Parse all the types that have no children
|
|
||||||
type.equals("soundtrack", true) -> Soundtrack
|
|
||||||
type.equals("mixtape/street", true) -> Mixtape
|
|
||||||
type.equals("dj-mix", true) -> Mix
|
|
||||||
type.equals("live", true) -> convertRefinement(Refinement.LIVE)
|
|
||||||
type.equals("remix", true) -> convertRefinement(Refinement.REMIX)
|
|
||||||
else -> convertRefinement(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw information about an [Album] obtained from the component [Song] instances. **This is only
|
* Raw information about an [Album] obtained from the component [Song] instances. **This is only
|
||||||
* meant for use within the music package.**
|
* meant for use within the music package.**
|
||||||
|
@ -938,8 +745,8 @@ class Album constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
||||||
val name: String,
|
val name: String,
|
||||||
/** @see Music.rawSortName */
|
/** @see Music.rawSortName */
|
||||||
val sortName: String?,
|
val sortName: String?,
|
||||||
/** @see Album.type */
|
/** @see Album.releaseType */
|
||||||
val type: Type?,
|
val releaseType: ReleaseType?,
|
||||||
/** @see Artist.Raw.name */
|
/** @see Artist.Raw.name */
|
||||||
val rawArtists: List<Artist.Raw>
|
val rawArtists: List<Artist.Raw>
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import androidx.core.database.getIntOrNull
|
import androidx.core.database.getIntOrNull
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import org.oxycblt.auxio.music.Date
|
import org.oxycblt.auxio.music.tags.Date
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.parsing.correctWhitespace
|
import org.oxycblt.auxio.music.parsing.correctWhitespace
|
||||||
import org.oxycblt.auxio.music.parsing.splitEscaped
|
import org.oxycblt.auxio.music.parsing.splitEscaped
|
||||||
|
@ -142,7 +142,7 @@ class ReadWriteCacheExtractor(private val context: Context) : WriteOnlyCacheExtr
|
||||||
rawSong.albumMusicBrainzId = cachedRawSong.albumMusicBrainzId
|
rawSong.albumMusicBrainzId = cachedRawSong.albumMusicBrainzId
|
||||||
rawSong.albumName = cachedRawSong.albumName
|
rawSong.albumName = cachedRawSong.albumName
|
||||||
rawSong.albumSortName = cachedRawSong.albumSortName
|
rawSong.albumSortName = cachedRawSong.albumSortName
|
||||||
rawSong.albumTypes = cachedRawSong.albumTypes
|
rawSong.releaseTypes = cachedRawSong.releaseTypes
|
||||||
|
|
||||||
rawSong.artistMusicBrainzIds = cachedRawSong.artistMusicBrainzIds
|
rawSong.artistMusicBrainzIds = cachedRawSong.artistMusicBrainzIds
|
||||||
rawSong.artistNames = cachedRawSong.artistNames
|
rawSong.artistNames = cachedRawSong.artistNames
|
||||||
|
@ -190,7 +190,7 @@ private class CacheDatabase(context: Context) :
|
||||||
append("${Columns.ALBUM_MUSIC_BRAINZ_ID} STRING,")
|
append("${Columns.ALBUM_MUSIC_BRAINZ_ID} STRING,")
|
||||||
append("${Columns.ALBUM_NAME} STRING NOT NULL,")
|
append("${Columns.ALBUM_NAME} STRING NOT NULL,")
|
||||||
append("${Columns.ALBUM_SORT_NAME} STRING,")
|
append("${Columns.ALBUM_SORT_NAME} STRING,")
|
||||||
append("${Columns.ALBUM_TYPES} STRING,")
|
append("${Columns.RELEASE_TYPES} STRING,")
|
||||||
append("${Columns.ARTIST_MUSIC_BRAINZ_IDS} STRING,")
|
append("${Columns.ARTIST_MUSIC_BRAINZ_IDS} STRING,")
|
||||||
append("${Columns.ARTIST_NAMES} STRING,")
|
append("${Columns.ARTIST_NAMES} STRING,")
|
||||||
append("${Columns.ARTIST_SORT_NAMES} STRING,")
|
append("${Columns.ARTIST_SORT_NAMES} STRING,")
|
||||||
|
@ -249,7 +249,7 @@ private class CacheDatabase(context: Context) :
|
||||||
cursor.getColumnIndexOrThrow(Columns.ALBUM_MUSIC_BRAINZ_ID)
|
cursor.getColumnIndexOrThrow(Columns.ALBUM_MUSIC_BRAINZ_ID)
|
||||||
val albumNameIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_NAME)
|
val albumNameIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_NAME)
|
||||||
val albumSortNameIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_SORT_NAME)
|
val albumSortNameIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_SORT_NAME)
|
||||||
val albumTypesIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_TYPES)
|
val releaseTypesIndex = cursor.getColumnIndexOrThrow(Columns.RELEASE_TYPES)
|
||||||
|
|
||||||
val artistMusicBrainzIdsIndex =
|
val artistMusicBrainzIdsIndex =
|
||||||
cursor.getColumnIndexOrThrow(Columns.ARTIST_MUSIC_BRAINZ_IDS)
|
cursor.getColumnIndexOrThrow(Columns.ARTIST_MUSIC_BRAINZ_IDS)
|
||||||
|
@ -286,8 +286,8 @@ private class CacheDatabase(context: Context) :
|
||||||
raw.albumMusicBrainzId = cursor.getStringOrNull(albumMusicBrainzIdIndex)
|
raw.albumMusicBrainzId = cursor.getStringOrNull(albumMusicBrainzIdIndex)
|
||||||
raw.albumName = cursor.getString(albumNameIndex)
|
raw.albumName = cursor.getString(albumNameIndex)
|
||||||
raw.albumSortName = cursor.getStringOrNull(albumSortNameIndex)
|
raw.albumSortName = cursor.getStringOrNull(albumSortNameIndex)
|
||||||
cursor.getStringOrNull(albumTypesIndex)?.let {
|
cursor.getStringOrNull(releaseTypesIndex)?.let {
|
||||||
raw.albumTypes = it.parseSQLMultiValue()
|
raw.releaseTypes = it.parseSQLMultiValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.getStringOrNull(artistMusicBrainzIdsIndex)?.let {
|
cursor.getStringOrNull(artistMusicBrainzIdsIndex)?.let {
|
||||||
|
@ -351,7 +351,7 @@ private class CacheDatabase(context: Context) :
|
||||||
put(Columns.ALBUM_MUSIC_BRAINZ_ID, rawSong.albumMusicBrainzId)
|
put(Columns.ALBUM_MUSIC_BRAINZ_ID, rawSong.albumMusicBrainzId)
|
||||||
put(Columns.ALBUM_NAME, rawSong.albumName)
|
put(Columns.ALBUM_NAME, rawSong.albumName)
|
||||||
put(Columns.ALBUM_SORT_NAME, rawSong.albumSortName)
|
put(Columns.ALBUM_SORT_NAME, rawSong.albumSortName)
|
||||||
put(Columns.ALBUM_TYPES, rawSong.albumTypes.toSQLMultiValue())
|
put(Columns.RELEASE_TYPES, rawSong.releaseTypes.toSQLMultiValue())
|
||||||
|
|
||||||
put(Columns.ARTIST_MUSIC_BRAINZ_IDS, rawSong.artistMusicBrainzIds.toSQLMultiValue())
|
put(Columns.ARTIST_MUSIC_BRAINZ_IDS, rawSong.artistMusicBrainzIds.toSQLMultiValue())
|
||||||
put(Columns.ARTIST_NAMES, rawSong.artistNames.toSQLMultiValue())
|
put(Columns.ARTIST_NAMES, rawSong.artistNames.toSQLMultiValue())
|
||||||
|
@ -422,8 +422,8 @@ private class CacheDatabase(context: Context) :
|
||||||
const val ALBUM_NAME = "album"
|
const val ALBUM_NAME = "album"
|
||||||
/** @see Song.Raw.albumSortName */
|
/** @see Song.Raw.albumSortName */
|
||||||
const val ALBUM_SORT_NAME = "album_sort"
|
const val ALBUM_SORT_NAME = "album_sort"
|
||||||
/** @see Song.Raw.albumTypes */
|
/** @see Song.Raw.releaseTypes */
|
||||||
const val ALBUM_TYPES = "album_types"
|
const val RELEASE_TYPES = "album_types"
|
||||||
/** @see Song.Raw.artistMusicBrainzIds */
|
/** @see Song.Raw.artistMusicBrainzIds */
|
||||||
const val ARTIST_MUSIC_BRAINZ_IDS = "artists_mbid"
|
const val ARTIST_MUSIC_BRAINZ_IDS = "artists_mbid"
|
||||||
/** @see Song.Raw.artistNames */
|
/** @see Song.Raw.artistNames */
|
||||||
|
|
|
@ -27,7 +27,7 @@ import androidx.annotation.RequiresApi
|
||||||
import androidx.core.database.getIntOrNull
|
import androidx.core.database.getIntOrNull
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.oxycblt.auxio.music.Date
|
import org.oxycblt.auxio.music.tags.Date
|
||||||
import org.oxycblt.auxio.music.MusicSettings
|
import org.oxycblt.auxio.music.MusicSettings
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.parsing.parseId3v2Position
|
import org.oxycblt.auxio.music.parsing.parseId3v2Position
|
||||||
|
|
|
@ -22,7 +22,7 @@ import androidx.core.text.isDigitsOnly
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.MetadataRetriever
|
import com.google.android.exoplayer2.MetadataRetriever
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import org.oxycblt.auxio.music.Date
|
import org.oxycblt.auxio.music.tags.Date
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.parsing.parseId3v2Position
|
import org.oxycblt.auxio.music.parsing.parseId3v2Position
|
||||||
import org.oxycblt.auxio.music.storage.toAudioUri
|
import org.oxycblt.auxio.music.storage.toAudioUri
|
||||||
|
@ -208,7 +208,7 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
textFrames["TALB"]?.let { raw.albumName = it[0] }
|
textFrames["TALB"]?.let { raw.albumName = it[0] }
|
||||||
textFrames["TSOA"]?.let { raw.albumSortName = it[0] }
|
textFrames["TSOA"]?.let { raw.albumSortName = it[0] }
|
||||||
(textFrames["TXXX:musicbrainz album type"] ?: textFrames["GRP1"])?.let {
|
(textFrames["TXXX:musicbrainz album type"] ?: textFrames["GRP1"])?.let {
|
||||||
raw.albumTypes = it
|
raw.releaseTypes = it
|
||||||
}
|
}
|
||||||
|
|
||||||
// Artist
|
// Artist
|
||||||
|
@ -300,7 +300,7 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
comments["musicbrainz_albumid"]?.let { raw.albumMusicBrainzId = it[0] }
|
comments["musicbrainz_albumid"]?.let { raw.albumMusicBrainzId = it[0] }
|
||||||
comments["album"]?.let { raw.albumName = it[0] }
|
comments["album"]?.let { raw.albumName = it[0] }
|
||||||
comments["albumsort"]?.let { raw.albumSortName = it[0] }
|
comments["albumsort"]?.let { raw.albumSortName = it[0] }
|
||||||
comments["releasetype"]?.let { raw.albumTypes = it }
|
comments["releasetype"]?.let { raw.releaseTypes = it }
|
||||||
|
|
||||||
// Artist
|
// Artist
|
||||||
comments["musicbrainz_artistid"]?.let { raw.artistMusicBrainzIds = it }
|
comments["musicbrainz_artistid"]?.let { raw.artistMusicBrainzIds = it }
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
import org.oxycblt.auxio.music.library.Sort.Mode
|
import org.oxycblt.auxio.music.library.Sort.Mode
|
||||||
|
import org.oxycblt.auxio.music.tags.Date
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sorting method.
|
* A sorting method.
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music.tags
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
|
@ -235,7 +235,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
||||||
val tokens =
|
val tokens =
|
||||||
// Match the input with the timestamp regex. If there is no match, see if we can
|
// Match the input with the timestamp regex. If there is no match, see if we can
|
||||||
// fall back to some kind of year value.
|
// fall back to some kind of year value.
|
||||||
(ISO8601_REGEX.matchEntire(timestamp) ?: return timestamp.toIntOrNull()?.let(::from))
|
(ISO8601_REGEX.matchEntire(timestamp) ?: return timestamp.toIntOrNull()?.let(Companion::from))
|
||||||
.groupValues
|
.groupValues
|
||||||
// Filter to the specific tokens we want and convert them to integer tokens.
|
// Filter to the specific tokens we want and convert them to integer tokens.
|
||||||
.mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null }
|
.mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null }
|
198
app/src/main/java/org/oxycblt/auxio/music/tags/ReleaseType.kt
Normal file
198
app/src/main/java/org/oxycblt/auxio/music/tags/ReleaseType.kt
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
package org.oxycblt.auxio.music.tags
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of release an [Album] is considered. This includes EPs, Singles, Compilations, etc.
|
||||||
|
*
|
||||||
|
* This class is derived from the MusicBrainz Release Group Type specification. It can be found
|
||||||
|
* at: https://musicbrainz.org/doc/Release_Group/Type
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
sealed class ReleaseType {
|
||||||
|
/**
|
||||||
|
* A specification of what kind of performance this release is. If null, the release is
|
||||||
|
* considered "Plain".
|
||||||
|
*/
|
||||||
|
abstract val refinement: Refinement?
|
||||||
|
|
||||||
|
/** The string resource corresponding to the name of this release type to show in the UI. */
|
||||||
|
abstract val stringRes: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plain album.
|
||||||
|
* @param refinement A specification of what kind of performance this release is. If null,
|
||||||
|
* the release is considered "Plain".
|
||||||
|
*/
|
||||||
|
data class Album(override val refinement: Refinement?) : ReleaseType() {
|
||||||
|
override val stringRes: Int
|
||||||
|
get() =
|
||||||
|
when (refinement) {
|
||||||
|
null -> R.string.lbl_album
|
||||||
|
// If present, include the refinement in the name of this release type.
|
||||||
|
Refinement.LIVE -> R.string.lbl_album_live
|
||||||
|
Refinement.REMIX -> R.string.lbl_album_remix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "Extended Play", or EP. Usually a smaller release consisting of 4-5 songs.
|
||||||
|
* @param refinement A specification of what kind of performance this release is. If null,
|
||||||
|
* the release is considered "Plain".
|
||||||
|
*/
|
||||||
|
data class EP(override val refinement: Refinement?) : ReleaseType() {
|
||||||
|
override val stringRes: Int
|
||||||
|
get() =
|
||||||
|
when (refinement) {
|
||||||
|
null -> R.string.lbl_ep
|
||||||
|
// If present, include the refinement in the name of this release type.
|
||||||
|
Refinement.LIVE -> R.string.lbl_ep_live
|
||||||
|
Refinement.REMIX -> R.string.lbl_ep_remix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single. Usually a release consisting of 1-2 songs.
|
||||||
|
* @param refinement A specification of what kind of performance this release is. If null,
|
||||||
|
* the release is considered "Plain".
|
||||||
|
*/
|
||||||
|
data class Single(override val refinement: Refinement?) : ReleaseType() {
|
||||||
|
override val stringRes: Int
|
||||||
|
get() =
|
||||||
|
when (refinement) {
|
||||||
|
null -> R.string.lbl_single
|
||||||
|
// If present, include the refinement in the name of this release type.
|
||||||
|
Refinement.LIVE -> R.string.lbl_single_live
|
||||||
|
Refinement.REMIX -> R.string.lbl_single_remix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compilation. Usually consists of many songs from a variety of artists.
|
||||||
|
* @param refinement A specification of what kind of performance this release is. If null,
|
||||||
|
* the release is considered "Plain".
|
||||||
|
*/
|
||||||
|
data class Compilation(override val refinement: Refinement?) : ReleaseType() {
|
||||||
|
override val stringRes: Int
|
||||||
|
get() =
|
||||||
|
when (refinement) {
|
||||||
|
null -> R.string.lbl_compilation
|
||||||
|
// If present, include the refinement in the name of this release type.
|
||||||
|
Refinement.LIVE -> R.string.lbl_compilation_live
|
||||||
|
Refinement.REMIX -> R.string.lbl_compilation_remix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A soundtrack. Similar to a [Compilation], but created for a specific piece of (usually
|
||||||
|
* visual) media.
|
||||||
|
*/
|
||||||
|
object Soundtrack : ReleaseType() {
|
||||||
|
override val refinement: Refinement?
|
||||||
|
get() = null
|
||||||
|
|
||||||
|
override val stringRes: Int
|
||||||
|
get() = R.string.lbl_soundtrack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A (DJ) Mix. These are usually one large track consisting of the artist playing several
|
||||||
|
* sub-tracks with smooth transitions between them.
|
||||||
|
*/
|
||||||
|
object Mix : ReleaseType() {
|
||||||
|
override val refinement: Refinement?
|
||||||
|
get() = null
|
||||||
|
|
||||||
|
override val stringRes: Int
|
||||||
|
get() = R.string.lbl_mix
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Mix-tape. These are usually [EP]-sized releases of music made to promote an [Artist] or
|
||||||
|
* a future release.
|
||||||
|
*/
|
||||||
|
object Mixtape : ReleaseType() {
|
||||||
|
override val refinement: Refinement?
|
||||||
|
get() = null
|
||||||
|
|
||||||
|
override val stringRes: Int
|
||||||
|
get() = R.string.lbl_mixtape
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A specification of what kind of performance a particular release is. */
|
||||||
|
enum class Refinement {
|
||||||
|
/** A release consisting of a live performance */
|
||||||
|
LIVE,
|
||||||
|
|
||||||
|
/** A release consisting of another [Artist]s remix of a prior performance. */
|
||||||
|
REMIX
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Parse a [ReleaseType] from a string formatted with the MusicBrainz Release Group Type
|
||||||
|
* specification.
|
||||||
|
* @param types A list of values consisting of valid release type values.
|
||||||
|
* @return A [ReleaseType] consisting of the given types, or null if the types were not valid.
|
||||||
|
*/
|
||||||
|
fun parse(types: List<String>): ReleaseType? {
|
||||||
|
val primary = types.getOrNull(0) ?: return null
|
||||||
|
return when {
|
||||||
|
// Primary types should be the first types in the sequence.
|
||||||
|
primary.equals("album", true) -> types.parseSecondaryTypes(1) { Album(it) }
|
||||||
|
primary.equals("ep", true) -> types.parseSecondaryTypes(1) { EP(it) }
|
||||||
|
primary.equals("single", true) -> types.parseSecondaryTypes(1) { Single(it) }
|
||||||
|
// The spec makes no mention of whether primary types are a pre-requisite for
|
||||||
|
// secondary types, so we assume that it's not and map oprhan secondary types
|
||||||
|
// to Album release types.
|
||||||
|
else -> types.parseSecondaryTypes(0) { Album(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse "secondary" types (i.e not [Album], [EP], or [Single]) from a string formatted
|
||||||
|
* with the MusicBrainz Release Group Type specification.
|
||||||
|
* @param index The index of the release type to parse.
|
||||||
|
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType] corresponding
|
||||||
|
* to the callee's context. This is used in order to handle secondary times that are
|
||||||
|
* actually [Refinement]s.
|
||||||
|
* @return A [ReleaseType] corresponding to the secondary type found at that index.
|
||||||
|
*/
|
||||||
|
private inline fun List<String>.parseSecondaryTypes(
|
||||||
|
index: Int,
|
||||||
|
convertRefinement: (Refinement?) -> ReleaseType
|
||||||
|
): ReleaseType {
|
||||||
|
val secondary = getOrNull(index)
|
||||||
|
return if (secondary.equals("compilation", true)) {
|
||||||
|
// Secondary type is a compilation, actually parse the third type
|
||||||
|
// and put that into a compilation if needed.
|
||||||
|
parseSecondaryTypeImpl(getOrNull(index + 1)) { Compilation(it) }
|
||||||
|
} else {
|
||||||
|
// Secondary type is a plain value, use the original values given.
|
||||||
|
parseSecondaryTypeImpl(secondary, convertRefinement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse "secondary" types (i.e not [Album], [EP], [Single]) that do not correspond to
|
||||||
|
* any child values.
|
||||||
|
* @param type The release type value to parse.
|
||||||
|
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType] corresponding
|
||||||
|
* to the callee's context. This is used in order to handle secondary times that are
|
||||||
|
* actually [Refinement]s.
|
||||||
|
*/
|
||||||
|
private inline fun parseSecondaryTypeImpl(
|
||||||
|
type: String?,
|
||||||
|
convertRefinement: (Refinement?) -> ReleaseType
|
||||||
|
) =
|
||||||
|
when {
|
||||||
|
// Parse all the types that have no children
|
||||||
|
type.equals("soundtrack", true) -> Soundtrack
|
||||||
|
type.equals("mixtape/street", true) -> Mixtape
|
||||||
|
type.equals("dj-mix", true) -> Mix
|
||||||
|
type.equals("live", true) -> convertRefinement(Refinement.LIVE)
|
||||||
|
type.equals("remix", true) -> convertRefinement(Refinement.REMIX)
|
||||||
|
else -> convertRefinement(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023 Auxio Project
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.oxycblt.auxio.music
|
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class AlbumTypeTest {
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_primary() {
|
|
||||||
assertEquals(Album.Type.Album(null), Album.Type.parse(listOf("album")))
|
|
||||||
assertEquals(Album.Type.EP(null), Album.Type.parse(listOf("ep")))
|
|
||||||
assertEquals(Album.Type.Single(null), Album.Type.parse(listOf("single")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_secondary() {
|
|
||||||
assertEquals(Album.Type.Compilation(null), Album.Type.parse(listOf("album", "compilation")))
|
|
||||||
assertEquals(Album.Type.Soundtrack, Album.Type.parse(listOf("album", "soundtrack")))
|
|
||||||
assertEquals(Album.Type.Mix, Album.Type.parse(listOf("album", "dj-mix")))
|
|
||||||
assertEquals(Album.Type.Mixtape, Album.Type.parse(listOf("album", "mixtape/street")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_modifiers() {
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Album(Album.Type.Refinement.LIVE), Album.Type.parse(listOf("album", "live")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Album(Album.Type.Refinement.REMIX),
|
|
||||||
Album.Type.parse(listOf("album", "remix")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.EP(Album.Type.Refinement.LIVE), Album.Type.parse(listOf("ep", "live")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.EP(Album.Type.Refinement.REMIX), Album.Type.parse(listOf("ep", "remix")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Single(Album.Type.Refinement.LIVE),
|
|
||||||
Album.Type.parse(listOf("single", "live")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Single(Album.Type.Refinement.REMIX),
|
|
||||||
Album.Type.parse(listOf("single", "remix")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_secondaryModifiers() {
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Compilation(Album.Type.Refinement.LIVE),
|
|
||||||
Album.Type.parse(listOf("album", "compilation", "live")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Compilation(Album.Type.Refinement.REMIX),
|
|
||||||
Album.Type.parse(listOf("album", "compilation", "remix")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_orphanedSecondary() {
|
|
||||||
assertEquals(Album.Type.Compilation(null), Album.Type.parse(listOf("compilation")))
|
|
||||||
assertEquals(Album.Type.Soundtrack, Album.Type.parse(listOf("soundtrack")))
|
|
||||||
assertEquals(Album.Type.Mix, Album.Type.parse(listOf("dj-mix")))
|
|
||||||
assertEquals(Album.Type.Mixtape, Album.Type.parse(listOf("mixtape/street")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun albumType_parse_orphanedModifier() {
|
|
||||||
assertEquals(Album.Type.Album(Album.Type.Refinement.LIVE), Album.Type.parse(listOf("live")))
|
|
||||||
assertEquals(
|
|
||||||
Album.Type.Album(Album.Type.Refinement.REMIX), Album.Type.parse(listOf("remix")))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.oxycblt.auxio.music.library
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
|
class LibraryTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val LIBRARY = listOf(
|
||||||
|
Song.Raw(
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music.tags
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.music.tags
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ReleaseTypeTest {
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_primary() {
|
||||||
|
assertEquals(ReleaseType.Album(null), ReleaseType.parse(listOf("album")))
|
||||||
|
assertEquals(ReleaseType.EP(null), ReleaseType.parse(listOf("ep")))
|
||||||
|
assertEquals(ReleaseType.Single(null), ReleaseType.parse(listOf("single")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_secondary() {
|
||||||
|
assertEquals(ReleaseType.Compilation(null), ReleaseType.parse(listOf("album", "compilation")))
|
||||||
|
assertEquals(ReleaseType.Soundtrack, ReleaseType.parse(listOf("album", "soundtrack")))
|
||||||
|
assertEquals(ReleaseType.Mix, ReleaseType.parse(listOf("album", "dj-mix")))
|
||||||
|
assertEquals(ReleaseType.Mixtape, ReleaseType.parse(listOf("album", "mixtape/street")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_modifiers() {
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Album(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("album", "live")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Album(ReleaseType.Refinement.REMIX),
|
||||||
|
ReleaseType.parse(listOf("album", "remix")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.EP(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("ep", "live")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.EP(ReleaseType.Refinement.REMIX), ReleaseType.parse(listOf("ep", "remix")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Single(ReleaseType.Refinement.LIVE),
|
||||||
|
ReleaseType.parse(listOf("single", "live")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Single(ReleaseType.Refinement.REMIX),
|
||||||
|
ReleaseType.parse(listOf("single", "remix")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_secondaryModifiers() {
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Compilation(ReleaseType.Refinement.LIVE),
|
||||||
|
ReleaseType.parse(listOf("album", "compilation", "live")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Compilation(ReleaseType.Refinement.REMIX),
|
||||||
|
ReleaseType.parse(listOf("album", "compilation", "remix")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_orphanedSecondary() {
|
||||||
|
assertEquals(ReleaseType.Compilation(null), ReleaseType.parse(listOf("compilation")))
|
||||||
|
assertEquals(ReleaseType.Soundtrack, ReleaseType.parse(listOf("soundtrack")))
|
||||||
|
assertEquals(ReleaseType.Mix, ReleaseType.parse(listOf("dj-mix")))
|
||||||
|
assertEquals(ReleaseType.Mixtape, ReleaseType.parse(listOf("mixtape/street")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun releaseType_parse_orphanedModifier() {
|
||||||
|
assertEquals(ReleaseType.Album(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("live")))
|
||||||
|
assertEquals(
|
||||||
|
ReleaseType.Album(ReleaseType.Refinement.REMIX), ReleaseType.parse(listOf("remix")))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue