music: fix music loading crash with weird genres
Fix an issue where genres consisting only of whitespace crash the genre parser, and thus the music loader. Band-aid this by moving the trimming code out of splitEscaped and into maybeParseSeparators. In a future version I'll need to figure out how I want to handle these weird edge cases.
This commit is contained in:
parent
6db50a0e45
commit
05cf0f7261
4 changed files with 23 additions and 21 deletions
|
@ -51,7 +51,7 @@ audio focus was lost
|
||||||
- Switched to issue forms
|
- Switched to issue forms
|
||||||
- Completed migration to reactive playback system
|
- Completed migration to reactive playback system
|
||||||
- Refactor music backends into a unified chain of extractors
|
- Refactor music backends into a unified chain of extractors
|
||||||
- Add bluetooth connection reciever (No functionality in app yet)
|
- Add bluetooth connection receiver (No functionality in app yet)
|
||||||
|
|
||||||
## 2.6.4
|
## 2.6.4
|
||||||
|
|
||||||
|
|
|
@ -283,8 +283,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)?.parseSQLMultiValue()?.let {
|
cursor.getStringOrNull(albumTypesIndex)?.let {
|
||||||
raw.albumTypes = it
|
raw.albumTypes = it.parseSQLMultiValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.getStringOrNull(artistMusicBrainzIdsIndex)?.let {
|
cursor.getStringOrNull(artistMusicBrainzIdsIndex)?.let {
|
||||||
|
@ -387,7 +387,8 @@ private class CacheDatabase(context: Context) :
|
||||||
* @return A list of strings corresponding to the delimited values present within the original
|
* @return A list of strings corresponding to the delimited values present within the original
|
||||||
* string. Escaped delimiters are converted back into their normal forms.
|
* string. Escaped delimiters are converted back into their normal forms.
|
||||||
*/
|
*/
|
||||||
private fun String.parseSQLMultiValue() = splitEscaped { it == ';' }
|
private fun String.parseSQLMultiValue() =
|
||||||
|
splitEscaped { it == ';' }
|
||||||
|
|
||||||
/** Defines the columns used in this database. */
|
/** Defines the columns used in this database. */
|
||||||
private object Columns {
|
private object Columns {
|
||||||
|
|
|
@ -187,8 +187,8 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
// Map TXXX frames differently so we can specifically index by their
|
// Map TXXX frames differently so we can specifically index by their
|
||||||
// descriptions.
|
// descriptions.
|
||||||
val id = tag.description?.let { "TXXX:${it.sanitize()}" } ?: tag.id.sanitize()
|
val id = tag.description?.let { "TXXX:${it.sanitize()}" } ?: tag.id.sanitize()
|
||||||
val values = tag.values.map { it.sanitize() }
|
val values = tag.values.map { it.sanitize() }.filter { it.isNotEmpty() }
|
||||||
if (values.isNotEmpty() && values.all { it.isNotEmpty() }) {
|
if (values.isNotEmpty()) {
|
||||||
id3v2Tags[id] = values
|
id3v2Tags[id] = values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.core.text.isDigitsOnly
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import org.oxycblt.auxio.music.Date
|
import org.oxycblt.auxio.music.Date
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +89,7 @@ inline fun String.splitEscaped(selector: (Char) -> Boolean): List<String> {
|
||||||
if (selector(a)) {
|
if (selector(a)) {
|
||||||
// Non-escaped separator, split the string here, making sure any stray whitespace
|
// Non-escaped separator, split the string here, making sure any stray whitespace
|
||||||
// is removed.
|
// is removed.
|
||||||
split.add(currentString.trim())
|
split.add(currentString)
|
||||||
currentString = ""
|
currentString = ""
|
||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
|
@ -107,8 +108,8 @@ inline fun String.splitEscaped(selector: (Char) -> Boolean): List<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentString.isNotEmpty()) {
|
if (currentString.isNotEmpty()) {
|
||||||
// Had an in-progress split string that is now terminated, add it..
|
// Had an in-progress split string that is now terminated, add it.
|
||||||
split.add(currentString.trim())
|
split.add(currentString)
|
||||||
}
|
}
|
||||||
|
|
||||||
return split
|
return split
|
||||||
|
@ -126,7 +127,7 @@ fun List<String>.parseMultiValue(settings: Settings) =
|
||||||
get(0).maybeParseSeparators(settings)
|
get(0).maybeParseSeparators(settings)
|
||||||
} else {
|
} else {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
this
|
this.map { it.trim() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,7 +138,7 @@ fun List<String>.parseMultiValue(settings: Settings) =
|
||||||
fun String.maybeParseSeparators(settings: Settings): List<String> {
|
fun String.maybeParseSeparators(settings: Settings): List<String> {
|
||||||
// Get the separators the user desires. If null, there's nothing to do.
|
// Get the separators the user desires. If null, there's nothing to do.
|
||||||
val separators = settings.musicSeparators ?: return listOf(this)
|
val separators = settings.musicSeparators ?: return listOf(this)
|
||||||
return splitEscaped { separators.contains(it) }
|
return splitEscaped { separators.contains(it) }.map { it.trim() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,20 +180,20 @@ fun String.parseId3GenreNames(settings: Settings) =
|
||||||
* @return A named genre if the field is a valid integer, "Cover" or "Remix" if the field is
|
* @return A named genre if the field is a valid integer, "Cover" or "Remix" if the field is
|
||||||
* "CR"/"RX" respectively, and nothing if the field is not a valid ID3v1 integer genre.
|
* "CR"/"RX" respectively, and nothing if the field is not a valid ID3v1 integer genre.
|
||||||
*/
|
*/
|
||||||
private fun String.parseId3v1Genre(): String? =
|
private fun String.parseId3v1Genre(): String? {
|
||||||
when {
|
// ID3v1 genres are a plain integer value without formatting, so in that case
|
||||||
// ID3v1 genres are a plain integer value without formatting, so in that case
|
// try to index the genre table with such. If this fails, then try to compare it
|
||||||
// try to index the genre table with such.
|
// to some other hard-coded values.
|
||||||
isDigitsOnly() -> GENRE_TABLE.getOrNull(toInt())
|
val numeric = toIntOrNull() ?: return when (this) {
|
||||||
|
|
||||||
// CR and RX are not technically ID3v1, but are formatted similarly to a plain number.
|
// CR and RX are not technically ID3v1, but are formatted similarly to a plain number.
|
||||||
this == "CR" -> "Cover"
|
"CR" -> "Cover"
|
||||||
this == "RX" -> "Remix"
|
"RX" -> "Remix"
|
||||||
|
|
||||||
// Current name is fine.
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GENRE_TABLE.getOrNull(numeric)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Regex] that implements parsing for ID3v2's genre format. Derived from mutagen:
|
* A [Regex] that implements parsing for ID3v2's genre format. Derived from mutagen:
|
||||||
* https://github.com/quodlibet/mutagen
|
* https://github.com/quodlibet/mutagen
|
||||||
|
|
Loading…
Reference in a new issue