Improve genre compatibility

Add a new range of Winamp 5.6+ genres to further improve compatibility with old genres.
This commit is contained in:
OxygenCobalt 2021-03-05 11:20:22 -07:00
parent 58ec1ce293
commit fab377eba4
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 52 additions and 32 deletions

View file

@ -28,6 +28,7 @@ android {
applicationIdSuffix = '.debug'
versionNameSuffix = "-DEBUG"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@ -62,7 +63,7 @@ dependencies {
// General
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.activity:activity-ktx:1.3.0-alpha02'
implementation 'androidx.activity:activity-ktx:1.3.0-alpha03'
implementation 'androidx.fragment:fragment-ktx:1.3.0'
// Layout

View file

@ -26,11 +26,13 @@ sealed class Parent : BaseModel() {
/**
* The data object for a song. Inherits [BaseModel].
* @property fileName The raw filename for this track
* @property albumId The Song's Album ID. Never use this outside of when attaching a song to its album.
* @property albumId The Song's Album ID.
* Never use this outside of when attaching a song to its album.
* @property track The Song's Track number
* @property duration The duration of the song, in millis.
* @property album The Song's parent album. Use this instead of [albumId].
* @property genre The Song's [Genre]
* @property genre The Song's [Genre].
* These are not ensured to be linked due to possible quirks in the genre loading system.
* @property seconds The Song's duration in seconds
* @property formattedDuration The Song's duration as a duration string.
*/
@ -86,7 +88,8 @@ data class Album(
private val mSongs = mutableListOf<Song>()
val songs: List<Song> get() = mSongs
val totalDuration: String get() = songs.sumOf { it.seconds }.toDuration()
val totalDuration: String get() =
songs.sumOf { it.seconds }.toDuration()
fun linkArtist(artist: Artist) {
mArtist = artist
@ -118,6 +121,8 @@ data class Artist(
}
val genre: Genre? by lazy {
// Get the genre that corresponds to the most songs in this artist, which would be
// the most "Prominent" genre.
songs.groupBy { it.genre }.entries.maxByOrNull { it.value.size }?.key
}
@ -135,18 +140,15 @@ data class Genre(
override val id: Long = -1,
override val name: String,
) : Parent() {
val resolvedName: String by lazy {
if (name.contains(Regex("([1-9])"))) {
name.toNamedGenre() ?: name
} else {
name
}
}
private val mSongs = mutableListOf<Song>()
val songs: List<Song> get() = mSongs
val totalDuration: String get() = songs.sumOf { it.seconds }.toDuration()
val resolvedName: String by lazy {
name.getGenreNameCompat() ?: name
}
val totalDuration: String get() =
songs.sumOf { it.seconds }.toDuration()
fun linkSong(song: Song) {
mSongs.add(song)

View file

@ -191,8 +191,9 @@ class MusicLoader(private val context: Context) {
artists.add(
// IDs are incremented from the minimum int value so that they remain unique.
Artist(
id = (artists.size + Int.MIN_VALUE).toLong(),
name = entry.key, albums = entry.value
id = (Int.MIN_VALUE + artists.size).toLong(),
name = entry.key,
albums = entry.value
)
)
}

View file

@ -84,9 +84,7 @@ class MusicStore private constructor() {
* @return The corresponding [Song] for this [uri], null if there isnt one.
*/
fun getSongForUri(uri: Uri, resolver: ContentResolver): Song? {
resolver.query(
uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null
)?.use { cursor ->
resolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
cursor.moveToFirst()
val fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))

View file

@ -7,6 +7,7 @@ import android.provider.MediaStore
import android.text.format.DateUtils
import android.widget.ImageButton
import android.widget.TextView
import androidx.core.text.isDigitsOnly
import androidx.databinding.BindingAdapter
import org.oxycblt.auxio.R
import org.oxycblt.auxio.recycler.SortMode
@ -17,6 +18,7 @@ import org.oxycblt.auxio.ui.getPlural
* There are a lot more int-genre extensions as far as Im aware, but this works for most cases.
*/
private val ID3_GENRES = arrayOf(
// ID3 Standard
"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
"Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno",
"Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno",
@ -28,6 +30,7 @@ private val ID3_GENRES = arrayOf(
"Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
"Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
// Winamp Extensions
"Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
"Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
"Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
@ -37,26 +40,41 @@ private val ID3_GENRES = arrayOf(
"Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie",
"Britpop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal",
"Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal",
"Anime", "JPop", "Synthpop"
)
"Anime", "JPop", "Synthpop",
private const val PAREN_FILTER = "()"
// Winamp 5.6+ extensions, used by EasyTAG and friends
"Abstract", "Art Rock", "Baroque", "Bhangra", "Big Beat", "Breakbeat", "Chillout", "Downtempo",
"Dub", "EBM", "Eclectic", "Electro", "Electroclash", "Emo", "Experimental", "Garage", "Global",
"IDM", "Illbient", "Industro-Goth", "Jam Band", "Krautrock", "Leftfield", "Lounge", "Math Rock", // S I X T Y F I V E
"New Romantic", "Nu-Breakz", "Post-Punk", "Post-Rock", "Psytrance", "Shoegaze", "Space Rock",
"Trop Rock", "World Music", "Neoclassical", "Audiobook", "Audio Theatre", "Neue Deutsche Welle",
"Podcast", "Indie Rock", "G-Funk", "Dubstep", "Garage Rock", "Psybient"
)
// --- EXTENSION FUNCTIONS ---
/**
* Convert legacy ID3 genres to their named genre
* @return The named genre for this legacy genre.
* Convert legacy int-based ID3 genres to their human-readable genre
* @return The named genre for this legacy genre, null if there is no need to parse it or if the genre is invalid.
*/
fun String.toNamedGenre(): String? {
// Strip the genres of any parentheses, and convert it to an int
val intGenre = this.filterNot {
PAREN_FILTER.indexOf(it) > -1
}.toInt()
fun String.getGenreNameCompat(): String? {
if (isDigitsOnly()) {
// ID3 v1, just parse as an integer
return ID3_GENRES.getOrNull(toInt())
}
// If the conversion fails [Due to the genre using an extension that Auxio doesn't have],
// then return null.
return ID3_GENRES.getOrNull(intGenre)
if (startsWith('(') && endsWith(')')) {
// ID3 v2+, parse out the parentheses and get the integer
// Any genres formatted as "(CHARS)" will be ignored.
val genreInt = substring(1 until lastIndex).toIntOrNull()
if (genreInt != null) {
return ID3_GENRES.getOrNull(genreInt)
}
}
// ID3 v3+, current name is fine.
return null
}
/**
@ -92,7 +110,7 @@ fun Long.toDuration(): String {
*/
fun Int.toYear(context: Context): String {
return if (this > 0) {
this.toString()
toString()
} else {
context.getString(R.string.placeholder_no_date)
}