Skip to content

Commit

Permalink
Do a best-effort attempt to handle broken chapter marks. Fixes #1866 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulWoitaschek authored May 13, 2023
1 parent 5a8ca7e commit d0e41b2
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 8 deletions.
30 changes: 23 additions & 7 deletions data/src/main/kotlin/voice/data/Chapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,30 @@ data class Chapter(
val chapterMarks: List<ChapterMark> = if (markData.isEmpty()) {
listOf(ChapterMark(name, 0L, duration))
} else {
val sorted = markData.sorted()
sorted.mapIndexed { index, (startMs, name) ->
val isFirst = index == 0
val isLast = index == sorted.size - 1
val start = if (isFirst) 0L else startMs
val end = if (isLast) duration else sorted[index + 1].startMs - 1
ChapterMark(name = name, startMs = start, endMs = end)
val sorted = markData.distinctBy { it.startMs }
.filter { it.startMs in 0..<duration }
.sorted()
val result = mutableListOf<ChapterMark>()
for ((index, markData) in sorted.withIndex()) {
val name = markData.name
val previous = result.lastOrNull()
val next = sorted.getOrNull(index + 1)
val startMs = if (previous == null) 0L else previous.endMs + 1
val endMs = if (next != null && next.startMs + 2 < duration && startMs < next.startMs - 1) {
next.startMs - 1
} else {
duration - 1
}
result += ChapterMark(
name = name,
startMs = startMs,
endMs = endMs,
)
if (endMs == duration - 1) {
break
}
}
result.ifEmpty { listOf(ChapterMark(name, 0L, duration)) }
}

override fun compareTo(other: Chapter): Int {
Expand Down
6 changes: 6 additions & 0 deletions data/src/main/kotlin/voice/data/ChapterMark.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ data class ChapterMark(
val endMs: Long,
) {

init {
require(startMs < endMs) {
"Start must be less than end in $this"
}
}

operator fun contains(position: Duration): Boolean = position.inWholeMilliseconds in startMs..endMs
operator fun contains(positionMs: Long): Boolean = positionMs in startMs..endMs
}
Expand Down
31 changes: 31 additions & 0 deletions data/src/test/kotlin/voice/data/ChapterTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package voice.data

import io.kotest.matchers.collections.shouldContainInOrder
import org.junit.Test
import java.time.Instant

class ChapterTest {

@Test
fun `chapter parsing does best effort on broken marks`() {
val positions = Chapter(
duration = 20L,
fileLastModified = Instant.now(),
id = ChapterId(""),
markData = listOf(11, 5, 5, 10).mapIndexed { index, i ->
MarkData(
startMs = i.toLong(),
name = "Mark $index",
)
},
name = "Chapter",
).chapterMarks.map { MarkPosition(it.startMs, it.endMs) }

positions.shouldContainInOrder(
MarkPosition(0L, 9L),
MarkPosition(10L, 19L),
)
}

data class MarkPosition(val start: Long, val end: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ data class BookPlayViewState(
val playing: Boolean,
val cover: ImmutableFile?,
val skipSilence: Boolean,
)
) {

init {
require(duration > Duration.ZERO) {
"Duration must be positive in $this"
}
}
}

internal sealed interface BookPlayDialogViewState {
data class SpeedDialog(
Expand Down

0 comments on commit d0e41b2

Please sign in to comment.