refactor: prefer using Duration than milliseconds
This commit is contained in:
+2
-2
@@ -56,8 +56,8 @@ internal class PlaybackRepositoryImpl(
|
|||||||
|
|
||||||
private val playbackTiming = currentPosition.map { currentPosition ->
|
private val playbackTiming = currentPosition.map { currentPosition ->
|
||||||
PlaybackTiming(
|
PlaybackTiming(
|
||||||
positionMs = currentPosition.inWholeMilliseconds,
|
position = currentPosition,
|
||||||
durationMs = duration.inWholeMilliseconds,
|
duration = duration,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,9 +1,9 @@
|
|||||||
package fr.ajaury.gwenedeg.player.model
|
package fr.ajaury.gwenedeg.player.model
|
||||||
|
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
data class PlaybackTiming(
|
data class PlaybackTiming(
|
||||||
val positionMs: Long = 0L,
|
val position: Duration = 0.milliseconds,
|
||||||
val durationMs: Long = 0L,
|
val duration: Duration = 0.milliseconds,
|
||||||
) {
|
)
|
||||||
val progress: Float
|
|
||||||
get() = if (durationMs > 0) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package fr.ajaury.gwenedeg.subtitle.data
|
package fr.ajaury.gwenedeg.subtitle.data
|
||||||
|
|
||||||
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses [LRC](https://en.wikipedia.org/wiki/LRC_(file_format)) subtitle content.
|
* Parses [LRC](https://en.wikipedia.org/wiki/LRC_(file_format)) subtitle content.
|
||||||
@@ -26,15 +29,15 @@ internal class LrcParser {
|
|||||||
.findAll(tags.value)
|
.findAll(tags.value)
|
||||||
.mapNotNull { match ->
|
.mapNotNull { match ->
|
||||||
TimeCodedText(
|
TimeCodedText(
|
||||||
startTime = match.toMs().milliseconds,
|
startTime = match.asDuration(),
|
||||||
text = text,
|
text = text,
|
||||||
).takeIf { text.isNotBlank() }
|
).takeIf { text.isNotBlank() }
|
||||||
}.toList()
|
}.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MatchResult.toMs(): Long {
|
private fun MatchResult.asDuration(): Duration {
|
||||||
val (minutes, seconds, fraction) = destructured
|
val (minutes, seconds, fraction) = destructured
|
||||||
val fractionMs = if (fraction.length == 2) fraction.toLong() * 10 else fraction.toLong()
|
val fractionMs = if (fraction.length == 2) fraction.toLong() * 10 else fraction.toLong()
|
||||||
return minutes.toLong() * 60_000 + seconds.toLong() * 1_000 + fractionMs
|
return minutes.toInt().minutes + seconds.toInt().seconds + fractionMs.toInt().milliseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import fr.ajaury.gwenedeg.player.ui.components.PreviousButton
|
|||||||
import fr.ajaury.gwenedeg.player.ui.components.SpeedPreferenceButton
|
import fr.ajaury.gwenedeg.player.ui.components.SpeedPreferenceButton
|
||||||
import fr.ajaury.gwenedeg.preferences.model.PlaybackPreferences
|
import fr.ajaury.gwenedeg.preferences.model.PlaybackPreferences
|
||||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerControl(
|
fun PlayerControl(
|
||||||
@@ -30,7 +32,7 @@ fun PlayerControl(
|
|||||||
canGoToNextSentence: Boolean,
|
canGoToNextSentence: Boolean,
|
||||||
isSpeedActive: Boolean,
|
isSpeedActive: Boolean,
|
||||||
playbackSpeed: Float,
|
playbackSpeed: Float,
|
||||||
onSeek: (Float) -> Unit = {},
|
onSeek: (Duration) -> Unit = {},
|
||||||
onMainPlayActionButtonClicked: () -> Unit = {},
|
onMainPlayActionButtonClicked: () -> Unit = {},
|
||||||
onPreviousSentenceClicked: () -> Unit = {},
|
onPreviousSentenceClicked: () -> Unit = {},
|
||||||
onNextSentenceClicked: () -> Unit = {},
|
onNextSentenceClicked: () -> Unit = {},
|
||||||
@@ -95,7 +97,7 @@ private fun PlayerControlPreview() {
|
|||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlayerControl(
|
PlayerControl(
|
||||||
playbackState = PlaybackState.PLAYING,
|
playbackState = PlaybackState.PLAYING,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
canGoToPreviousSentence = true,
|
canGoToPreviousSentence = true,
|
||||||
canGoToNextSentence = true,
|
canGoToNextSentence = true,
|
||||||
isSpeedActive = false,
|
isSpeedActive = false,
|
||||||
@@ -110,7 +112,7 @@ private fun PlayerControlFirstSentencePreview() {
|
|||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlayerControl(
|
PlayerControl(
|
||||||
playbackState = PlaybackState.PLAYING,
|
playbackState = PlaybackState.PLAYING,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
canGoToPreviousSentence = false,
|
canGoToPreviousSentence = false,
|
||||||
canGoToNextSentence = true,
|
canGoToNextSentence = true,
|
||||||
playbackSpeed = PlaybackPreferences.DEFAULT_SPEED,
|
playbackSpeed = PlaybackPreferences.DEFAULT_SPEED,
|
||||||
@@ -125,7 +127,7 @@ private fun PlayerControlActiveSpeedPreview() {
|
|||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlayerControl(
|
PlayerControl(
|
||||||
playbackState = PlaybackState.PLAYING,
|
playbackState = PlaybackState.PLAYING,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
canGoToPreviousSentence = true,
|
canGoToPreviousSentence = true,
|
||||||
canGoToNextSentence = true,
|
canGoToNextSentence = true,
|
||||||
playbackSpeed = 0.7f,
|
playbackSpeed = 0.7f,
|
||||||
@@ -140,7 +142,7 @@ private fun PlayerControlLastSentencePreview() {
|
|||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlayerControl(
|
PlayerControl(
|
||||||
playbackState = PlaybackState.PLAYING,
|
playbackState = PlaybackState.PLAYING,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
canGoToPreviousSentence = true,
|
canGoToPreviousSentence = true,
|
||||||
canGoToNextSentence = false,
|
canGoToNextSentence = false,
|
||||||
playbackSpeed = PlaybackPreferences.DEFAULT_SPEED,
|
playbackSpeed = PlaybackPreferences.DEFAULT_SPEED,
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine
|
|||||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerScreen(
|
fun PlayerScreen(
|
||||||
@@ -75,7 +77,7 @@ fun PlayerScreen(
|
|||||||
fun PlayerScreen(
|
fun PlayerScreen(
|
||||||
uiState: PlayerUiState,
|
uiState: PlayerUiState,
|
||||||
onMainPlayActionButtonClicked: () -> Unit = {},
|
onMainPlayActionButtonClicked: () -> Unit = {},
|
||||||
onSeekToTimePart: (progress: Float) -> Unit = {},
|
onSeekToTimePart: (progress: Duration) -> Unit = {},
|
||||||
onSeekToSentence: (position: Int) -> Unit = {},
|
onSeekToSentence: (position: Int) -> Unit = {},
|
||||||
onPreviousSentenceClicked: () -> Unit = {},
|
onPreviousSentenceClicked: () -> Unit = {},
|
||||||
onNextSentenceClicked: () -> Unit = {},
|
onNextSentenceClicked: () -> Unit = {},
|
||||||
@@ -218,7 +220,7 @@ private fun PlayerScreenPlayingPreview() {
|
|||||||
),
|
),
|
||||||
playerState = PlayerState(
|
playerState = PlayerState(
|
||||||
playbackState = PlaybackState.PLAYING,
|
playbackState = PlaybackState.PLAYING,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
subtitleLines = previewSubtitleLines,
|
subtitleLines = previewSubtitleLines,
|
||||||
currentSubtitleIndex = 1,
|
currentSubtitleIndex = 1,
|
||||||
),
|
),
|
||||||
@@ -242,7 +244,7 @@ private fun PlayerScreenPausedPreview() {
|
|||||||
),
|
),
|
||||||
playerState = PlayerState(
|
playerState = PlayerState(
|
||||||
playbackState = PlaybackState.PAUSED,
|
playbackState = PlaybackState.PAUSED,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
subtitleLines = previewSubtitleLines,
|
subtitleLines = previewSubtitleLines,
|
||||||
currentSubtitleIndex = 1,
|
currentSubtitleIndex = 1,
|
||||||
),
|
),
|
||||||
@@ -266,7 +268,7 @@ private fun PlayerScreenEndedPreview() {
|
|||||||
),
|
),
|
||||||
playerState = PlayerState(
|
playerState = PlayerState(
|
||||||
playbackState = PlaybackState.ENDED,
|
playbackState = PlaybackState.ENDED,
|
||||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||||
subtitleLines = previewSubtitleLines,
|
subtitleLines = previewSubtitleLines,
|
||||||
currentSubtitleIndex = 2,
|
currentSubtitleIndex = 2,
|
||||||
),
|
),
|
||||||
|
|||||||
+11
-7
@@ -11,21 +11,25 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlaybackProgress(
|
fun PlaybackProgress(
|
||||||
playbackTiming: PlaybackTiming,
|
playbackTiming: PlaybackTiming,
|
||||||
onSeek: (Float) -> Unit = {},
|
onSeek: (Duration) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
var seekProgress by remember { mutableStateOf<Float?>(null) }
|
var seekProgress by remember { mutableStateOf<Float?>(null) }
|
||||||
Slider(
|
Slider(
|
||||||
value = seekProgress ?: playbackTiming.progress,
|
value = seekProgress ?: playbackTiming.position.inWholeMilliseconds.toFloat(),
|
||||||
|
valueRange = 0f..playbackTiming.duration.inWholeMilliseconds.toFloat(),
|
||||||
onValueChange = { seekProgress = it },
|
onValueChange = { seekProgress = it },
|
||||||
onValueChangeFinished = {
|
onValueChangeFinished = {
|
||||||
seekProgress?.let { fraction ->
|
seekProgress?.let { fraction ->
|
||||||
if (playbackTiming.durationMs > 0) {
|
if (playbackTiming.duration.isPositive()) {
|
||||||
onSeek(fraction)
|
onSeek(fraction.toInt().milliseconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seekProgress = null
|
seekProgress = null
|
||||||
@@ -39,7 +43,7 @@ fun PlaybackProgress(
|
|||||||
private fun PlaybackProgressPreview() {
|
private fun PlaybackProgressPreview() {
|
||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlaybackProgress(
|
PlaybackProgress(
|
||||||
playbackTiming = PlaybackTiming(0, 0),
|
playbackTiming = PlaybackTiming(0.seconds, 0.seconds),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +53,7 @@ private fun PlaybackProgressPreview() {
|
|||||||
private fun PlaybackProgressInProgressPreview() {
|
private fun PlaybackProgressInProgressPreview() {
|
||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlaybackProgress(
|
PlaybackProgress(
|
||||||
playbackTiming = PlaybackTiming(7000, 10000),
|
playbackTiming = PlaybackTiming(7.seconds, 10.seconds),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +63,7 @@ private fun PlaybackProgressInProgressPreview() {
|
|||||||
private fun PlaybackProgressEndedPreview() {
|
private fun PlaybackProgressEndedPreview() {
|
||||||
GwenedegTheme {
|
GwenedegTheme {
|
||||||
PlaybackProgress(
|
PlaybackProgress(
|
||||||
playbackTiming = PlaybackTiming(10000, 10000),
|
playbackTiming = PlaybackTiming(10.seconds, 10.seconds),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ import fr.ajaury.gwenedeg.core.model.Phrase
|
|||||||
import fr.ajaury.gwenedeg.player.model.PlayerState
|
import fr.ajaury.gwenedeg.player.model.PlayerState
|
||||||
import fr.ajaury.gwenedeg.preferences.model.PlaybackPreferences
|
import fr.ajaury.gwenedeg.preferences.model.PlaybackPreferences
|
||||||
import fr.ajaury.gwenedeg.preferences.model.SubtitlePreferences
|
import fr.ajaury.gwenedeg.preferences.model.SubtitlePreferences
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
data class PlayerUiState(
|
data class PlayerUiState(
|
||||||
val recordTitle: Phrase = Phrase(transcription = ""),
|
val recordTitle: Phrase = Phrase(transcription = ""),
|
||||||
@@ -12,7 +13,7 @@ data class PlayerUiState(
|
|||||||
val playbackSpeed: Float = PlaybackPreferences.DEFAULT_SPEED,
|
val playbackSpeed: Float = PlaybackPreferences.DEFAULT_SPEED,
|
||||||
) {
|
) {
|
||||||
val canGoToPreviousSentence: Boolean
|
val canGoToPreviousSentence: Boolean
|
||||||
get() = playerState.playbackTiming.positionMs > 2000
|
get() = playerState.playbackTiming.position > 2.seconds
|
||||||
|
|
||||||
val canGoToNextSentence: Boolean
|
val canGoToNextSentence: Boolean
|
||||||
get() = (playerState.currentSubtitleIndex ?: Int.MAX_VALUE) < playerState.subtitleLines.lastIndex
|
get() = (playerState.currentSubtitleIndex ?: Int.MAX_VALUE) < playerState.subtitleLines.lastIndex
|
||||||
|
|||||||
+3
-5
@@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.combine
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class PlayerViewModel(
|
class PlayerViewModel(
|
||||||
@@ -89,10 +89,8 @@ class PlayerViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekToTimePart(progress: Float) {
|
fun seekToTimePart(progress: Duration) {
|
||||||
val playbackTiming = uiState.value.playerState.playbackTiming
|
playbackRepository.seekTo(progress)
|
||||||
val position = (progress.toDouble() * playbackTiming.durationMs).milliseconds
|
|
||||||
playbackRepository.seekTo(position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekToSentence(sentenceIndex: Int) {
|
fun seekToSentence(sentenceIndex: Int) {
|
||||||
|
|||||||
Reference in New Issue
Block a user