sync: drop non-finite replay gain values at the parse boundary

`toFloatOrNull` accepts `"Infinity"`, `"NaN"` and overflowing literals
like `"1e40"`, so a server (or a corrupt tag it read) could put a
non-finite gain or peak into the library. An infinite peak later caps the
applied gain at negative infinity, i.e. silence; an infinite gain clips
every sample to full scale.

Nothing consumes these columns yet, so this is only about not storing
values that cannot mean anything.
This commit is contained in:
austinried
2026-10-16 13:06:20 +00:00
parent 86d0d06e76
commit 069a0f7f19
2 changed files with 28 additions and 1 deletions
@@ -245,7 +245,7 @@ internal fun Attributes.attr(name: String): String = getValue(name) ?: ""
internal fun Attributes.longAttr(name: String): Long? = attr(name).takeIf { it.isNotEmpty() }?.toLongOrNull()
internal fun Attributes.floatAttr(name: String): Float? = attr(name).takeIf { it.isNotEmpty() }?.toFloatOrNull()
internal fun Attributes.floatAttr(name: String): Float? = attr(name).takeIf { it.isNotEmpty() }?.toFloatOrNull()?.takeIf { it.isFinite() }
internal fun Attributes.dateAttr(name: String): Long? = attr(name).takeIf { it.isNotEmpty() }?.let(IsoDate::parse)
@@ -231,6 +231,33 @@ class SubsonicXmlTest {
assertEquals(null, songs[1].albumPeak)
}
@Test
fun ignoresNonFiniteReplayGainValues() {
val songs =
songs(
"""
<subsonic-response status="ok">
<searchResult3>
<song id="s1" title="Junk">
<replayGain trackGain="Infinity" albumGain="NaN" trackPeak="1e40" albumPeak="-Infinity"/>
</song>
<song id="s2" title="Mostly fine">
<replayGain trackGain="-3.2" albumGain="NaN" trackPeak="0.98"/>
</song>
</searchResult3>
</subsonic-response>
""".trimIndent(),
)
assertEquals(null, songs[0].trackGain)
assertEquals(null, songs[0].albumGain)
assertEquals(null, songs[0].trackPeak)
assertEquals(null, songs[0].albumPeak)
assertEquals(-3.2f, songs[1].trackGain)
assertEquals(null, songs[1].albumGain)
assertEquals(0.98f, songs[1].trackPeak)
}
@Test
fun ignoresSongGenresThatAreNotDirectChildren() {
val songs =