new: implement PlaybackRepository to centralize playback and subtitle management across app
This commit is contained in:
@@ -2,6 +2,7 @@ package fr.ajaury.gwenedeg.subtitle.data
|
||||
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
* Parses [LRC](https://en.wikipedia.org/wiki/LRC_(file_format)) subtitle content.
|
||||
@@ -17,7 +18,7 @@ internal class LrcParser {
|
||||
val lines = content
|
||||
.lineSequence()
|
||||
.flatMap { line -> parseLine(line) }
|
||||
.sortedBy { it.startMs }
|
||||
.sortedBy { it.startTime }
|
||||
.toList()
|
||||
return Subtitle(lines)
|
||||
}
|
||||
@@ -28,7 +29,7 @@ internal class LrcParser {
|
||||
return timeTag
|
||||
.findAll(tags.value)
|
||||
.mapNotNull { match ->
|
||||
SubtitleLine(startMs = match.toMs(), text = text)
|
||||
SubtitleLine(startTime = match.toMs().milliseconds, text = text)
|
||||
.takeIf { it.text.isNotBlank() }
|
||||
}.toList()
|
||||
}
|
||||
|
||||
+2
-2
@@ -31,10 +31,10 @@ internal class LrcSubtitleRepository(
|
||||
/** Attaches each [translation] line's text to the matching original line, paired by timestamp. */
|
||||
private fun Subtitle.mergeTranslation(translation: Subtitle?): Subtitle {
|
||||
if (translation == null) return this
|
||||
val translationByStart = translation.lines.associate { it.startMs to it.text }
|
||||
val translationByStart = translation.lines.associate { it.startTime to it.text }
|
||||
return Subtitle(
|
||||
lines = lines.map { line ->
|
||||
line.copy(translation = translationByStart[line.startMs])
|
||||
line.copy(translation = translationByStart[line.startTime])
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,8 +1,10 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
class GetCurrentSubtitleIndexUseCase {
|
||||
operator fun invoke(
|
||||
subtitle: Subtitle,
|
||||
progressMs: Long,
|
||||
): Int? = subtitle.lines.indexOfLast { it.startMs <= progressMs }.takeIf { it >= 0 }
|
||||
position: Duration,
|
||||
): Int? = subtitle.lines.indexOfLast { it.startTime <= position }.takeIf { it >= 0 }
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,11 +1,13 @@
|
||||
package fr.ajaury.gwenedeg.subtitle.domain
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* A single timed subtitle cue: [text] becomes active at [startMs]. [translation] is the optional
|
||||
* A single timed subtitle cue: [text] becomes active at [startTime]. [translation] is the optional
|
||||
* translation of [text] in the user's language (e.g. French), shown alongside the original.
|
||||
*/
|
||||
data class SubtitleLine(
|
||||
val startMs: Long,
|
||||
val startTime: Duration,
|
||||
val text: String,
|
||||
val translation: String? = null,
|
||||
)
|
||||
|
||||
+14
-10
@@ -5,6 +5,7 @@ import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class GetCurrentSubtitleIndexUseCaseTest {
|
||||
private val getCurrentSubtitleIndex = GetCurrentSubtitleIndexUseCase()
|
||||
@@ -13,32 +14,35 @@ class GetCurrentSubtitleIndexUseCaseTest {
|
||||
Subtitle(
|
||||
lines =
|
||||
listOf(
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !"),
|
||||
SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !"),
|
||||
SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !"),
|
||||
),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun returns_minus_one_before_the_first_cue() {
|
||||
assertEquals(null, getCurrentSubtitleIndex(subtitle, progressMs = -1))
|
||||
assertEquals(null, getCurrentSubtitleIndex(subtitle, position = (-1).milliseconds))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returns_the_index_at_and_after_each_cue_start() {
|
||||
assertEquals(0, getCurrentSubtitleIndex(subtitle, progressMs = 0))
|
||||
assertEquals(0, getCurrentSubtitleIndex(subtitle, progressMs = 4309))
|
||||
assertEquals(1, getCurrentSubtitleIndex(subtitle, progressMs = 4310))
|
||||
assertEquals(1, getCurrentSubtitleIndex(subtitle, progressMs = 5000))
|
||||
assertEquals(0, getCurrentSubtitleIndex(subtitle, position = 0.milliseconds))
|
||||
assertEquals(0, getCurrentSubtitleIndex(subtitle, position = 4309.milliseconds))
|
||||
assertEquals(1, getCurrentSubtitleIndex(subtitle, position = 4310.milliseconds))
|
||||
assertEquals(1, getCurrentSubtitleIndex(subtitle, position = 5000.milliseconds))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returns_the_last_index_past_the_end() {
|
||||
assertEquals(2, getCurrentSubtitleIndex(subtitle, progressMs = 50_000))
|
||||
assertEquals(2, getCurrentSubtitleIndex(subtitle, position = 50_000.milliseconds))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returns_minus_one_for_an_empty_subtitle() {
|
||||
assertEquals(null, getCurrentSubtitleIndex(Subtitle(emptyList()), progressMs = 1_000))
|
||||
assertEquals(
|
||||
null,
|
||||
getCurrentSubtitleIndex(Subtitle(emptyList()), position = 1_000.milliseconds),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package fr.ajaury.gwenedeg.subtitle
|
||||
import fr.ajaury.gwenedeg.subtitle.data.LrcParser
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class LrcParserTest {
|
||||
private val parser = LrcParser()
|
||||
@@ -31,16 +32,16 @@ class LrcParserTest {
|
||||
fun converts_timestamp_to_milliseconds() {
|
||||
val subtitle = parser.parse(sample)
|
||||
|
||||
assertEquals(0, subtitle.lines[0].startMs)
|
||||
assertEquals(4310, subtitle.lines[1].startMs)
|
||||
assertEquals(7350, subtitle.lines[2].startMs)
|
||||
assertEquals(0.milliseconds, subtitle.lines[0].startTime)
|
||||
assertEquals(4310.milliseconds, subtitle.lines[1].startTime)
|
||||
assertEquals(7350.milliseconds, subtitle.lines[2].startTime)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parses_three_digit_fraction_as_milliseconds() {
|
||||
val subtitle = parser.parse("[00:01.250]Hello")
|
||||
|
||||
assertEquals(1250, subtitle.lines.single().startMs)
|
||||
assertEquals(1250.milliseconds, subtitle.lines.single().startTime)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user