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:
Alexander Capehart 2022-12-29 21:09:35 -07:00
parent 6db50a0e45
commit 05cf0f7261
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
4 changed files with 23 additions and 21 deletions

View file

@ -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

View file

@ -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 {

View file

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

View file

@ -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