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:
Alexander Capehart 2023-01-07 09:31:48 -07:00
parent a2b51825e8
commit 5adc87550e
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
14 changed files with 338 additions and 317 deletions

View file

@ -37,6 +37,7 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.library.Library
import org.oxycblt.auxio.music.library.Sort
import org.oxycblt.auxio.music.storage.MimeType
import org.oxycblt.auxio.music.tags.ReleaseType
import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.*
@ -344,21 +345,21 @@ class DetailViewModel(application: Application) :
val byReleaseGroup =
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
// the artist's albums.
when (it.type.refinement) {
Album.Type.Refinement.LIVE -> AlbumGrouping.LIVE
Album.Type.Refinement.REMIX -> AlbumGrouping.REMIXES
when (it.releaseType.refinement) {
ReleaseType.Refinement.LIVE -> AlbumGrouping.LIVE
ReleaseType.Refinement.REMIX -> AlbumGrouping.REMIXES
null ->
when (it.type) {
is Album.Type.Album -> AlbumGrouping.ALBUMS
is Album.Type.EP -> AlbumGrouping.EPS
is Album.Type.Single -> AlbumGrouping.SINGLES
is Album.Type.Compilation -> AlbumGrouping.COMPILATIONS
is Album.Type.Soundtrack -> AlbumGrouping.SOUNDTRACKS
is Album.Type.Mix -> AlbumGrouping.MIXES
is Album.Type.Mixtape -> AlbumGrouping.MIXTAPES
when (it.releaseType) {
is ReleaseType.Album -> AlbumGrouping.ALBUMS
is ReleaseType.EP -> AlbumGrouping.EPS
is ReleaseType.Single -> AlbumGrouping.SINGLES
is ReleaseType.Compilation -> AlbumGrouping.COMPILATIONS
is ReleaseType.Soundtrack -> AlbumGrouping.SOUNDTRACKS
is ReleaseType.Mix -> AlbumGrouping.MIXES
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
* instance of this enum.
*/

View file

@ -126,7 +126,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
binding.detailCover.bind(album)
// 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)
@ -173,7 +173,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
oldItem.dates == newItem.dates &&
oldItem.songs.size == newItem.songs.size &&
oldItem.durationMs == newItem.durationMs &&
oldItem.type == newItem.type
oldItem.releaseType == newItem.releaseType
}
}
}

View file

@ -122,7 +122,7 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
override fun areContentsTheSame(oldItem: Album, newItem: Album) =
oldItem.rawName == newItem.rawName &&
oldItem.areArtistContentsTheSame(newItem) &&
oldItem.type == newItem.type
oldItem.releaseType == newItem.releaseType
}
}
}

View file

@ -34,6 +34,8 @@ import org.oxycblt.auxio.music.library.Sort
import org.oxycblt.auxio.music.parsing.parseId3GenreNames
import org.oxycblt.auxio.music.parsing.parseMultiValue
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.unlikelyToBeNull
@ -463,7 +465,7 @@ class Song constructor(raw: Raw, musicSettings: MusicSettings) : Music() {
musicBrainzId = raw.albumMusicBrainzId?.toUuidOrNull(),
name = requireNotNull(raw.albumName) { "Invalid raw: No album name" },
sortName = raw.albumSortName,
type = Album.Type.parse(raw.albumTypes.parseMultiValue(musicSettings)),
releaseType = ReleaseType.parse(raw.releaseTypes.parseMultiValue(musicSettings)),
rawArtists =
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,
/** @see Album.Raw.sortName */
var albumSortName: String? = null,
/** @see Album.Raw.type */
var albumTypes: List<String> = listOf(),
/** @see Album.Raw.releaseType */
var releaseTypes: List<String> = listOf(),
/** @see Artist.Raw.musicBrainzId */
var artistMusicBrainzIds: List<String> = listOf(),
/** @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 })
/**
* The [Type] of this album, signifying the type of release it actually is. Defaults to
* [Type.Album].
* The [ReleaseType] of this album, signifying the type of release it actually is. Defaults to
* [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
* 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
* 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,
/** @see Music.rawSortName */
val sortName: String?,
/** @see Album.type */
val type: Type?,
/** @see Album.releaseType */
val releaseType: ReleaseType?,
/** @see Artist.Raw.name */
val rawArtists: List<Artist.Raw>
) {

View file

@ -23,7 +23,7 @@ import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.database.getIntOrNull
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.parsing.correctWhitespace
import org.oxycblt.auxio.music.parsing.splitEscaped
@ -142,7 +142,7 @@ class ReadWriteCacheExtractor(private val context: Context) : WriteOnlyCacheExtr
rawSong.albumMusicBrainzId = cachedRawSong.albumMusicBrainzId
rawSong.albumName = cachedRawSong.albumName
rawSong.albumSortName = cachedRawSong.albumSortName
rawSong.albumTypes = cachedRawSong.albumTypes
rawSong.releaseTypes = cachedRawSong.releaseTypes
rawSong.artistMusicBrainzIds = cachedRawSong.artistMusicBrainzIds
rawSong.artistNames = cachedRawSong.artistNames
@ -190,7 +190,7 @@ private class CacheDatabase(context: Context) :
append("${Columns.ALBUM_MUSIC_BRAINZ_ID} STRING,")
append("${Columns.ALBUM_NAME} STRING NOT NULL,")
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_NAMES} STRING,")
append("${Columns.ARTIST_SORT_NAMES} STRING,")
@ -249,7 +249,7 @@ private class CacheDatabase(context: Context) :
cursor.getColumnIndexOrThrow(Columns.ALBUM_MUSIC_BRAINZ_ID)
val albumNameIndex = cursor.getColumnIndexOrThrow(Columns.ALBUM_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 =
cursor.getColumnIndexOrThrow(Columns.ARTIST_MUSIC_BRAINZ_IDS)
@ -286,8 +286,8 @@ private class CacheDatabase(context: Context) :
raw.albumMusicBrainzId = cursor.getStringOrNull(albumMusicBrainzIdIndex)
raw.albumName = cursor.getString(albumNameIndex)
raw.albumSortName = cursor.getStringOrNull(albumSortNameIndex)
cursor.getStringOrNull(albumTypesIndex)?.let {
raw.albumTypes = it.parseSQLMultiValue()
cursor.getStringOrNull(releaseTypesIndex)?.let {
raw.releaseTypes = it.parseSQLMultiValue()
}
cursor.getStringOrNull(artistMusicBrainzIdsIndex)?.let {
@ -351,7 +351,7 @@ private class CacheDatabase(context: Context) :
put(Columns.ALBUM_MUSIC_BRAINZ_ID, rawSong.albumMusicBrainzId)
put(Columns.ALBUM_NAME, rawSong.albumName)
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_NAMES, rawSong.artistNames.toSQLMultiValue())
@ -422,8 +422,8 @@ private class CacheDatabase(context: Context) :
const val ALBUM_NAME = "album"
/** @see Song.Raw.albumSortName */
const val ALBUM_SORT_NAME = "album_sort"
/** @see Song.Raw.albumTypes */
const val ALBUM_TYPES = "album_types"
/** @see Song.Raw.releaseTypes */
const val RELEASE_TYPES = "album_types"
/** @see Song.Raw.artistMusicBrainzIds */
const val ARTIST_MUSIC_BRAINZ_IDS = "artists_mbid"
/** @see Song.Raw.artistNames */

View file

@ -27,7 +27,7 @@ import androidx.annotation.RequiresApi
import androidx.core.database.getIntOrNull
import androidx.core.database.getStringOrNull
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.Song
import org.oxycblt.auxio.music.parsing.parseId3v2Position

View file

@ -22,7 +22,7 @@ import androidx.core.text.isDigitsOnly
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.MetadataRetriever
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.parsing.parseId3v2Position
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["TSOA"]?.let { raw.albumSortName = it[0] }
(textFrames["TXXX:musicbrainz album type"] ?: textFrames["GRP1"])?.let {
raw.albumTypes = it
raw.releaseTypes = it
}
// Artist
@ -300,7 +300,7 @@ class Task(context: Context, private val raw: Song.Raw) {
comments["musicbrainz_albumid"]?.let { raw.albumMusicBrainzId = it[0] }
comments["album"]?.let { raw.albumName = it[0] }
comments["albumsort"]?.let { raw.albumSortName = it[0] }
comments["releasetype"]?.let { raw.albumTypes = it }
comments["releasetype"]?.let { raw.releaseTypes = it }
// Artist
comments["musicbrainz_artistid"]?.let { raw.artistMusicBrainzIds = it }

View file

@ -23,6 +23,7 @@ import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.*
import org.oxycblt.auxio.music.library.Sort.Mode
import org.oxycblt.auxio.music.tags.Date
/**
* A sorting method.

View file

@ -15,7 +15,7 @@
* 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 java.text.ParseException
@ -235,7 +235,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
val tokens =
// Match the input with the timestamp regex. If there is no match, see if we can
// 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
// Filter to the specific tokens we want and convert them to integer tokens.
.mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null }

View 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)
}
}
}

View file

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

View file

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

View file

@ -15,7 +15,7 @@
* 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.assertTrue

View file

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