new: add subtitle translations
This commit is contained in:
@@ -52,6 +52,8 @@ fun App() {
|
||||
title = record.title,
|
||||
audioResourcePath = record.audioResourcePath,
|
||||
subtitleResourcePath = record.subtitleResourcePath,
|
||||
translationSubtitleResourcePath =
|
||||
record.translationSubtitleResourcePath,
|
||||
),
|
||||
)
|
||||
},
|
||||
@@ -63,6 +65,7 @@ fun App() {
|
||||
recordTitle = key.title,
|
||||
audioResourcePath = key.audioResourcePath,
|
||||
subtitleResourcePath = key.subtitleResourcePath,
|
||||
translationSubtitleResourcePath = key.translationSubtitleResourcePath,
|
||||
onBackClicked = { backStack.removeLastOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ sealed interface Route : NavKey {
|
||||
val title: String,
|
||||
val audioResourcePath: String,
|
||||
val subtitleResourcePath: String,
|
||||
val translationSubtitleResourcePath: String? = null,
|
||||
) : Route
|
||||
}
|
||||
|
||||
@@ -34,14 +34,18 @@ fun PlayerScreen(
|
||||
recordTitle: String,
|
||||
audioResourcePath: String,
|
||||
subtitleResourcePath: String,
|
||||
translationSubtitleResourcePath: String? = null,
|
||||
onBackClicked: () -> Unit = {},
|
||||
viewModel: PlayerViewModel = koinViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
DisposableEffect(audioResourcePath, subtitleResourcePath) {
|
||||
DisposableEffect(audioResourcePath, subtitleResourcePath, translationSubtitleResourcePath) {
|
||||
viewModel.play(filePath = audioResourcePath)
|
||||
viewModel.loadSubtitle(resourcePath = subtitleResourcePath)
|
||||
viewModel.loadSubtitle(
|
||||
resourcePath = subtitleResourcePath,
|
||||
translationResourcePath = translationSubtitleResourcePath,
|
||||
)
|
||||
onDispose {
|
||||
viewModel.stop()
|
||||
}
|
||||
@@ -115,9 +119,9 @@ fun PlayerScreen(
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !"),
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !", translation = "Bonjour !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !", translation = "Au revoir !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !", translation = "À ce soir !"),
|
||||
)
|
||||
|
||||
@Preview
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.compose.foundation.gestures.animateScrollBy
|
||||
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -22,6 +23,7 @@ import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -29,7 +31,6 @@ import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
@Composable
|
||||
fun SubtitleList(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -47,7 +48,11 @@ fun SubtitleList(
|
||||
-1
|
||||
} else {
|
||||
val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2f
|
||||
info.visibleItemsInfo.minByOrNull { abs((it.offset + it.size / 2f) - viewportCenter) }!!.index
|
||||
info.visibleItemsInfo
|
||||
.minByOrNull {
|
||||
abs((it.offset + it.size / 2f) - viewportCenter)
|
||||
}?.index
|
||||
?: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,18 +120,69 @@ private fun Subtitle(
|
||||
line: SubtitleLine,
|
||||
isCurrent: Boolean,
|
||||
) {
|
||||
val contentColor = if (isCurrent) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
MainTextLine(
|
||||
text = line.text,
|
||||
isCurrent = isCurrent,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
line.translation?.let { translation ->
|
||||
TranslationLine(
|
||||
text = translation,
|
||||
isCurrent = isCurrent,
|
||||
contentColor = contentColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainTextLine(
|
||||
text: String,
|
||||
isCurrent: Boolean,
|
||||
contentColor: Color,
|
||||
) {
|
||||
val mainTextStyle = if (isCurrent) {
|
||||
MaterialTheme.typography.headlineSmall
|
||||
} else {
|
||||
MaterialTheme.typography.bodyLarge
|
||||
}
|
||||
|
||||
Text(
|
||||
text = line.text,
|
||||
style = if (isCurrent) {
|
||||
MaterialTheme.typography.headlineSmall
|
||||
} else {
|
||||
MaterialTheme.typography.bodyLarge
|
||||
},
|
||||
color = if (isCurrent) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
|
||||
},
|
||||
text = text,
|
||||
style = mainTextStyle,
|
||||
color = contentColor,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TranslationLine(
|
||||
text: String,
|
||||
isCurrent: Boolean,
|
||||
contentColor: Color,
|
||||
) {
|
||||
val translationStyle = if (isCurrent) {
|
||||
MaterialTheme.typography.bodyLarge
|
||||
} else {
|
||||
MaterialTheme.typography.bodySmall
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
style = translationStyle.copy(fontStyle = FontStyle.Italic),
|
||||
color = contentColor,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
@@ -134,9 +190,9 @@ private fun Subtitle(
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !"),
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !", translation = "Bonjour !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !", translation = "Au revoir !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !", translation = "À ce soir !"),
|
||||
)
|
||||
|
||||
@Preview
|
||||
@@ -148,4 +204,4 @@ private fun SubtitleListPreview() {
|
||||
currentIndex = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -93,11 +93,19 @@ class PlayerViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSubtitle(resourcePath: String) {
|
||||
fun loadSubtitle(
|
||||
resourcePath: String,
|
||||
translationResourcePath: String? = null,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
subtitle.value = try {
|
||||
val content = resourceReader.read(resourcePath = resourcePath)
|
||||
subtitleRepository.getSubtitle(content = content)
|
||||
val translationContent = translationResourcePath
|
||||
?.let { resourceReader.read(resourcePath = it) }
|
||||
subtitleRepository.getSubtitle(
|
||||
content = content,
|
||||
translationContent = translationContent,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
logger.error(
|
||||
message = "Failed to load subtitle: $resourcePath",
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ class InMemoryRecordRepository : RecordRepository {
|
||||
title = title,
|
||||
audioResourcePath = "files/Chom_bev_$paddedNumber.m4a",
|
||||
subtitleResourcePath = "files/Chom_bev_${paddedNumber}_st_bzh.lrc",
|
||||
translationSubtitleResourcePath = "files/Chom_bev_${paddedNumber}_st_fr.lrc",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,4 +5,5 @@ data class Record(
|
||||
val title: String,
|
||||
val audioResourcePath: String,
|
||||
val subtitleResourcePath: String,
|
||||
val translationSubtitleResourcePath: String? = null,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user