From 657d39a293aa1639ab6f074b0dba842364921447 Mon Sep 17 00:00:00 2001 From: Antoine Jaury Date: Wed, 1 Jul 2026 17:06:02 +0200 Subject: [PATCH] refactor: centralize domain models into `core.model` module --- core/model/build.gradle.kts | 45 +++++++++++++++++++ .../fr/ajaury/gwenedeg/core/model/Phrase.kt | 10 +++++ .../fr/ajaury/gwenedeg/core/model}/Record.kt | 2 +- data/subtitle/build.gradle.kts | 1 + .../gwenedeg/subtitle/data/LrcParser.kt | 16 +++---- .../subtitle/data/LrcSubtitleRepository.kt | 27 ++++++----- .../gwenedeg/subtitle/data/TimeCodedText.kt | 11 +++++ .../domain/GetCurrentSubtitleIndexUseCase.kt | 1 + .../gwenedeg/subtitle/domain/SubtitleLine.kt | 13 ------ .../subtitle/domain/SubtitleRepository.kt | 6 ++- .../subtitle/{domain => model}/Subtitle.kt | 2 +- .../gwenedeg/subtitle/model/SubtitleLine.kt | 12 +++++ .../GetCurrentSubtitleIndexUseCaseTest.kt | 11 ++--- .../ajaury/gwenedeg/subtitle/LrcParserTest.kt | 20 ++++----- settings.gradle.kts | 1 + shared/build.gradle.kts | 1 + .../player/data/PlaybackRepositoryImpl.kt | 6 +-- .../player/domain/PlaybackRepository.kt | 4 +- .../gwenedeg/player/model/PlaybackTiming.kt | 0 .../ajaury/gwenedeg/player/ui/PlayerScreen.kt | 27 +++++++++-- .../ajaury/gwenedeg/player/ui/SubtitleList.kt | 35 +++++++++++---- .../player/ui/viewmodel/PlayerUiState.kt | 2 +- .../player/ui/viewmodel/PlayerViewModel.kt | 2 +- .../records/data/InMemoryRecordRepository.kt | 2 +- .../records/domain/RecordRepository.kt | 1 + .../ajaury/gwenedeg/records/ui/RecordView.kt | 2 +- .../gwenedeg/records/ui/RecordsScreen.kt | 4 +- .../records/ui/viewmodel/RecordsViewModel.kt | 2 +- 28 files changed, 190 insertions(+), 76 deletions(-) create mode 100644 core/model/build.gradle.kts create mode 100644 core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Phrase.kt rename {shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain => core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model}/Record.kt (85%) create mode 100644 data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/TimeCodedText.kt delete mode 100644 data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleLine.kt rename data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/{domain => model}/Subtitle.kt (57%) create mode 100644 data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/SubtitleLine.kt rename {core/audioplayer => shared}/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/model/PlaybackTiming.kt (100%) diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts new file mode 100644 index 0000000..5a8f2dc --- /dev/null +++ b/core/model/build.gradle.kts @@ -0,0 +1,45 @@ +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidMultiplatformLibrary) +} + +kotlin { + iosArm64() + iosSimulatorArm64() + + jvm() + + js { + browser() + } + + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser() + } + + androidLibrary { + namespace = "fr.ajaury.gwenedeg.core.model" + compileSdk = + libs.versions.androidCompileSdk + .get() + .toInt() + minSdk = + libs.versions.androidMinSdk + .get() + .toInt() + + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } + } + + sourceSets { + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} diff --git a/core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Phrase.kt b/core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Phrase.kt new file mode 100644 index 0000000..30db70f --- /dev/null +++ b/core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Phrase.kt @@ -0,0 +1,10 @@ +package fr.ajaury.gwenedeg.core.model + +/** + * [translation] is the optional translation of [transcription] in the user's language + * (e.g. French), shown alongside the original. + */ +data class Phrase( + val transcription: String, + val translation: String? = null, +) diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/Record.kt b/core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Record.kt similarity index 85% rename from shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/Record.kt rename to core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Record.kt index 00a85e8..84313ae 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/Record.kt +++ b/core/model/src/commonMain/kotlin/fr/ajaury/gwenedeg/core/model/Record.kt @@ -1,4 +1,4 @@ -package fr.ajaury.gwenedeg.records.domain +package fr.ajaury.gwenedeg.core.model data class Record( val id: Int, diff --git a/data/subtitle/build.gradle.kts b/data/subtitle/build.gradle.kts index a5da309..f80c1d5 100644 --- a/data/subtitle/build.gradle.kts +++ b/data/subtitle/build.gradle.kts @@ -42,6 +42,7 @@ kotlin { // Core modules implementation(projects.core.coroutines) implementation(projects.core.logging) + implementation(projects.core.model) // Coroutines implementation(libs.kotlinx.coroutinesCore) diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcParser.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcParser.kt index a1012cb..b593fc2 100644 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcParser.kt +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcParser.kt @@ -1,7 +1,5 @@ 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 /** @@ -14,23 +12,23 @@ internal class LrcParser { private val timeTag = Regex("""\[(\d{1,2}):(\d{2})[.:](\d{2,3})]""") private val leadingTimeTags = Regex("""^(?:\[\d{1,2}:\d{2}[.:]\d{2,3}])+""") - fun parse(content: String): Subtitle { - val lines = content + fun parse(content: String): List = + content .lineSequence() .flatMap { line -> parseLine(line) } .sortedBy { it.startTime } .toList() - return Subtitle(lines) - } - private fun parseLine(line: String): List { + private fun parseLine(line: String): List { val tags = leadingTimeTags.find(line) ?: return emptyList() val text = line.substring(tags.range.last + 1).trim() return timeTag .findAll(tags.value) .mapNotNull { match -> - SubtitleLine(startTime = match.toMs().milliseconds, text = text) - .takeIf { it.text.isNotBlank() } + TimeCodedText( + startTime = match.toMs().milliseconds, + text = text, + ).takeIf { text.isNotBlank() } }.toList() } diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcSubtitleRepository.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcSubtitleRepository.kt index 5bd3752..1967052 100644 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcSubtitleRepository.kt +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/LrcSubtitleRepository.kt @@ -2,9 +2,11 @@ package fr.ajaury.gwenedeg.subtitle.data import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider import fr.ajaury.gwenedeg.core.logging.domain.Logger -import fr.ajaury.gwenedeg.subtitle.domain.Subtitle +import fr.ajaury.gwenedeg.core.model.Phrase import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository import fr.ajaury.gwenedeg.subtitle.model.CantGetSubtitleException +import fr.ajaury.gwenedeg.subtitle.model.Subtitle +import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine import kotlinx.coroutines.withContext internal class LrcSubtitleRepository( @@ -13,28 +15,33 @@ internal class LrcSubtitleRepository( private val logger: Logger, ) : SubtitleRepository { override suspend fun getSubtitle( - content: String, + transcriptionContent: String, translationContent: String?, ): Subtitle = withContext(dispatcherProvider.default) { try { - val subtitle = parser.parse(content = content) + val transcription = parser.parse(content = transcriptionContent) val translation = translationContent?.let { parser.parse(content = it) } - subtitle.mergeTranslation(translation) + transcription.mergeTranslation(translation) } catch (exception: Exception) { - val message = "Failed to load subtitle: $content" + val message = "Failed to load subtitle: $transcriptionContent" logger.error(message = message, throwable = exception) throw CantGetSubtitleException(message) } } /** 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.startTime to it.text } + private fun List.mergeTranslation(translation: List?): Subtitle { + val translationByStart = translation?.associate { it.startTime to it.text } ?: emptyMap() return Subtitle( - lines = lines.map { line -> - line.copy(translation = translationByStart[line.startTime]) + lines = this.map { + SubtitleLine( + startTime = it.startTime, + phrase = Phrase( + transcription = it.text, + translation = translationByStart[it.startTime], + ), + ) }, ) } diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/TimeCodedText.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/TimeCodedText.kt new file mode 100644 index 0000000..1de3bc4 --- /dev/null +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/data/TimeCodedText.kt @@ -0,0 +1,11 @@ +package fr.ajaury.gwenedeg.subtitle.data + +import kotlin.time.Duration + +/** + * A single timed text cue: [text] becomes active at [startTime]. + */ +data class TimeCodedText( + val startTime: Duration, + val text: String, +) diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/GetCurrentSubtitleIndexUseCase.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/GetCurrentSubtitleIndexUseCase.kt index 6dcbec9..31f1382 100644 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/GetCurrentSubtitleIndexUseCase.kt +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/GetCurrentSubtitleIndexUseCase.kt @@ -1,5 +1,6 @@ package fr.ajaury.gwenedeg.subtitle.domain +import fr.ajaury.gwenedeg.subtitle.model.Subtitle import kotlin.time.Duration class GetCurrentSubtitleIndexUseCase { diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleLine.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleLine.kt deleted file mode 100644 index f56834a..0000000 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleLine.kt +++ /dev/null @@ -1,13 +0,0 @@ -package fr.ajaury.gwenedeg.subtitle.domain - -import kotlin.time.Duration - -/** - * 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 startTime: Duration, - val text: String, - val translation: String? = null, -) diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleRepository.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleRepository.kt index 864dce2..41754d7 100644 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleRepository.kt +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/SubtitleRepository.kt @@ -1,12 +1,14 @@ package fr.ajaury.gwenedeg.subtitle.domain +import fr.ajaury.gwenedeg.subtitle.model.Subtitle + interface SubtitleRepository { /** - * Parses the subtitle [content]. When [translationContent] is provided, each line is matched to + * Parses the subtitle [transcriptionContent]. When [translationContent] is provided, each line is matched to * its translation by timestamp. */ suspend fun getSubtitle( - content: String, + transcriptionContent: String, translationContent: String? = null, ): Subtitle } diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/Subtitle.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/Subtitle.kt similarity index 57% rename from data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/Subtitle.kt rename to data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/Subtitle.kt index d2f331c..72a7fce 100644 --- a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/domain/Subtitle.kt +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/Subtitle.kt @@ -1,4 +1,4 @@ -package fr.ajaury.gwenedeg.subtitle.domain +package fr.ajaury.gwenedeg.subtitle.model data class Subtitle( val lines: List, diff --git a/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/SubtitleLine.kt b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/SubtitleLine.kt new file mode 100644 index 0000000..b72d03b --- /dev/null +++ b/data/subtitle/src/commonMain/kotlin/fr/ajaury/gwenedeg/subtitle/model/SubtitleLine.kt @@ -0,0 +1,12 @@ +package fr.ajaury.gwenedeg.subtitle.model + +import fr.ajaury.gwenedeg.core.model.Phrase +import kotlin.time.Duration + +/** + * A single timed subtitle cue: [phrase] becomes active at [startTime]. + */ +data class SubtitleLine( + val startTime: Duration, + val phrase: Phrase, +) diff --git a/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/GetCurrentSubtitleIndexUseCaseTest.kt b/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/GetCurrentSubtitleIndexUseCaseTest.kt index 30ead7f..0e1c407 100644 --- a/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/GetCurrentSubtitleIndexUseCaseTest.kt +++ b/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/GetCurrentSubtitleIndexUseCaseTest.kt @@ -1,8 +1,9 @@ package fr.ajaury.gwenedeg.subtitle +import fr.ajaury.gwenedeg.core.model.Phrase import fr.ajaury.gwenedeg.subtitle.domain.GetCurrentSubtitleIndexUseCase -import fr.ajaury.gwenedeg.subtitle.domain.Subtitle -import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine +import fr.ajaury.gwenedeg.subtitle.model.Subtitle +import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine import kotlin.test.Test import kotlin.test.assertEquals import kotlin.time.Duration.Companion.milliseconds @@ -14,9 +15,9 @@ class GetCurrentSubtitleIndexUseCaseTest { Subtitle( lines = listOf( - SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !"), - SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !"), - SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !"), + SubtitleLine(startTime = 0.milliseconds, phrase = Phrase(transcription = "Demat deoc'h !")), + SubtitleLine(startTime = 4310.milliseconds, phrase = Phrase(transcription = "Kenavo !")), + SubtitleLine(startTime = 7350.milliseconds, phrase = Phrase(transcription = "Kenavo emberr !")), ), ) diff --git a/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/LrcParserTest.kt b/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/LrcParserTest.kt index aa95887..dc18921 100644 --- a/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/LrcParserTest.kt +++ b/data/subtitle/src/commonTest/kotlin/fr/ajaury/gwenedeg/subtitle/LrcParserTest.kt @@ -23,31 +23,31 @@ class LrcParserTest { @Test fun ignores_metadata_tags_and_keeps_only_timed_cues() { - val subtitle = parser.parse(sample) + val lines = parser.parse(sample) - assertEquals(3, subtitle.lines.size) + assertEquals(3, lines.size) } @Test fun converts_timestamp_to_milliseconds() { - val subtitle = parser.parse(sample) + val lines = parser.parse(sample) - assertEquals(0.milliseconds, subtitle.lines[0].startTime) - assertEquals(4310.milliseconds, subtitle.lines[1].startTime) - assertEquals(7350.milliseconds, subtitle.lines[2].startTime) + assertEquals(0.milliseconds, lines[0].startTime) + assertEquals(4310.milliseconds, lines[1].startTime) + assertEquals(7350.milliseconds, lines[2].startTime) } @Test fun parses_three_digit_fraction_as_milliseconds() { - val subtitle = parser.parse("[00:01.250]Hello") + val lines = parser.parse("[00:01.250]Hello") - assertEquals(1250.milliseconds, subtitle.lines.single().startTime) + assertEquals(1250.milliseconds, lines.single().startTime) } @Test fun sorts_cues_ascending_by_start() { - val subtitle = parser.parse("[00:05.00]Later\n[00:01.00]Earlier") + val lines = parser.parse("[00:05.00]Later\n[00:01.00]Earlier") - assertEquals(listOf("Earlier", "Later"), subtitle.lines.map { it.text }) + assertEquals(listOf("Earlier", "Later"), lines.map { it.text }) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index a03c15a..cb2c3a8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -41,6 +41,7 @@ include(":shared") include(":core:audioplayer") include(":core:coroutines") include(":core:logging") +include(":core:model") // Data modules include(":data:subtitle") diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 8d9db5f..41845de 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -62,6 +62,7 @@ kotlin { implementation(projects.core.audioplayer) implementation(projects.core.coroutines) implementation(projects.core.logging) + implementation(projects.core.model) // Data modules implementation(projects.data.subtitle) diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/data/PlaybackRepositoryImpl.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/data/PlaybackRepositoryImpl.kt index cf11b77..219491f 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/data/PlaybackRepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/data/PlaybackRepositoryImpl.kt @@ -1,15 +1,15 @@ package fr.ajaury.gwenedeg.player.data import fr.ajaury.gwenedeg.core.logging.domain.Logger +import fr.ajaury.gwenedeg.core.model.Record import fr.ajaury.gwenedeg.player.domain.AudioPlayer import fr.ajaury.gwenedeg.player.domain.PlaybackRepository import fr.ajaury.gwenedeg.player.model.PlaybackState import fr.ajaury.gwenedeg.player.model.PlaybackTiming -import fr.ajaury.gwenedeg.records.domain.Record import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader import fr.ajaury.gwenedeg.subtitle.domain.GetCurrentSubtitleIndexUseCase -import fr.ajaury.gwenedeg.subtitle.domain.Subtitle import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository +import fr.ajaury.gwenedeg.subtitle.model.Subtitle import gwenedeg.shared.generated.resources.Res import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay @@ -91,7 +91,7 @@ internal class PlaybackRepositoryImpl( val translationContent = record.translationSubtitleResourcePath ?.let { resourceReader.read(resourcePath = it) } subtitleRepository.getSubtitle( - content = content, + transcriptionContent = content, translationContent = translationContent, ) } catch (exception: Exception) { diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/domain/PlaybackRepository.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/domain/PlaybackRepository.kt index 7062e77..e5fa579 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/domain/PlaybackRepository.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/domain/PlaybackRepository.kt @@ -1,9 +1,9 @@ package fr.ajaury.gwenedeg.player.domain +import fr.ajaury.gwenedeg.core.model.Record import fr.ajaury.gwenedeg.player.model.PlaybackState import fr.ajaury.gwenedeg.player.model.PlaybackTiming -import fr.ajaury.gwenedeg.records.domain.Record -import fr.ajaury.gwenedeg.subtitle.domain.Subtitle +import fr.ajaury.gwenedeg.subtitle.model.Subtitle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlin.time.Duration diff --git a/core/audioplayer/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/model/PlaybackTiming.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/model/PlaybackTiming.kt similarity index 100% rename from core/audioplayer/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/model/PlaybackTiming.kt rename to shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/model/PlaybackTiming.kt diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/PlayerScreen.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/PlayerScreen.kt index a2bb652..fbbd83a 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/PlayerScreen.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/PlayerScreen.kt @@ -22,11 +22,12 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import fr.ajaury.gwenedeg.core.model.Phrase import fr.ajaury.gwenedeg.player.model.PlaybackState import fr.ajaury.gwenedeg.player.model.PlaybackTiming import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerUiState import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerViewModel -import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine +import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine import fr.ajaury.gwenedeg.theme.GwenedegTheme import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -133,9 +134,27 @@ fun PlayerScreen( private val previewSubtitleLines = listOf( - SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !", translation = "Bonjour !"), - SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !", translation = "Au revoir !"), - SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !", translation = "À ce soir !"), + SubtitleLine( + startTime = 0.milliseconds, + phrase = Phrase( + transcription = "Demat deoc'h !", + translation = "Bonjour !", + ), + ), + SubtitleLine( + startTime = 4310.milliseconds, + phrase = Phrase( + transcription = "Kenavo !", + translation = "Au revoir !", + ), + ), + SubtitleLine( + startTime = 7350.milliseconds, + phrase = Phrase( + transcription = "Kenavo emberr !", + translation = "À ce soir !", + ), + ), ) @Preview diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt index 2a99b2c..cc91e4c 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/SubtitleList.kt @@ -27,7 +27,8 @@ 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 -import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine +import fr.ajaury.gwenedeg.core.model.Phrase +import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine import fr.ajaury.gwenedeg.theme.GwenedegTheme import kotlin.math.abs import kotlin.time.Duration.Companion.milliseconds @@ -132,12 +133,12 @@ private fun Subtitle( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp), ) { - MainTextLine( - text = line.text, + TranscriptionLine( + text = line.phrase.transcription, isCurrent = isCurrent, contentColor = contentColor, ) - line.translation?.let { translation -> + line.phrase.translation?.let { translation -> TranslationLine( text = translation, isCurrent = isCurrent, @@ -148,7 +149,7 @@ private fun Subtitle( } @Composable -private fun MainTextLine( +private fun TranscriptionLine( text: String, isCurrent: Boolean, contentColor: Color, @@ -191,9 +192,27 @@ private fun TranslationLine( private val previewSubtitleLines = listOf( - SubtitleLine(startTime = 0.milliseconds, text = "Demat deoc'h !", translation = "Bonjour !"), - SubtitleLine(startTime = 4310.milliseconds, text = "Kenavo !", translation = "Au revoir !"), - SubtitleLine(startTime = 7350.milliseconds, text = "Kenavo emberr !", translation = "À ce soir !"), + SubtitleLine( + startTime = 0.milliseconds, + phrase = Phrase( + transcription = "Demat deoc'h !", + translation = "Bonjour !", + ), + ), + SubtitleLine( + startTime = 4310.milliseconds, + phrase = Phrase( + transcription = "Kenavo !", + translation = "Au revoir !", + ), + ), + SubtitleLine( + startTime = 7350.milliseconds, + phrase = Phrase( + transcription = "Kenavo emberr !", + translation = "À ce soir !", + ), + ), ) @Preview diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerUiState.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerUiState.kt index 796e933..e86a7ed 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerUiState.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerUiState.kt @@ -2,7 +2,7 @@ package fr.ajaury.gwenedeg.player.ui.viewmodel import fr.ajaury.gwenedeg.player.model.PlaybackState import fr.ajaury.gwenedeg.player.model.PlaybackTiming -import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine +import fr.ajaury.gwenedeg.subtitle.model.SubtitleLine data class PlayerUiState( val recordTitle: String = "", diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerViewModel.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerViewModel.kt index 2f97060..2f54d9d 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerViewModel.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/player/ui/viewmodel/PlayerViewModel.kt @@ -3,10 +3,10 @@ package fr.ajaury.gwenedeg.player.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import fr.ajaury.gwenedeg.core.logging.domain.Logger +import fr.ajaury.gwenedeg.core.model.Record import fr.ajaury.gwenedeg.player.domain.AudioSessionManager import fr.ajaury.gwenedeg.player.domain.PlaybackRepository import fr.ajaury.gwenedeg.player.model.PlaybackState -import fr.ajaury.gwenedeg.records.domain.Record import fr.ajaury.gwenedeg.records.domain.RecordRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/data/InMemoryRecordRepository.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/data/InMemoryRecordRepository.kt index 2aead5e..f872ca0 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/data/InMemoryRecordRepository.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/data/InMemoryRecordRepository.kt @@ -1,7 +1,7 @@ package fr.ajaury.gwenedeg.records.data -import fr.ajaury.gwenedeg.records.domain.Record import fr.ajaury.gwenedeg.records.domain.RecordRepository +import fr.ajaury.gwenedeg.core.model.Record import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/RecordRepository.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/RecordRepository.kt index 9ad9c33..4e73cba 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/RecordRepository.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/domain/RecordRepository.kt @@ -1,5 +1,6 @@ package fr.ajaury.gwenedeg.records.domain +import fr.ajaury.gwenedeg.core.model.Record import kotlinx.coroutines.flow.Flow interface RecordRepository { diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordView.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordView.kt index d9d598d..d889ddf 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordView.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordView.kt @@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import fr.ajaury.gwenedeg.records.domain.Record +import fr.ajaury.gwenedeg.core.model.Record @Composable fun RecordView( diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordsScreen.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordsScreen.kt index 9b00e18..b1baa02 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordsScreen.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/RecordsScreen.kt @@ -1,9 +1,7 @@ package fr.ajaury.gwenedeg.records.ui -import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeContentPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.HorizontalDivider @@ -14,7 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import fr.ajaury.gwenedeg.records.domain.Record +import fr.ajaury.gwenedeg.core.model.Record import fr.ajaury.gwenedeg.records.ui.viewmodel.RecordsViewModel import org.koin.compose.viewmodel.koinViewModel diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/viewmodel/RecordsViewModel.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/viewmodel/RecordsViewModel.kt index 7600979..a335f4a 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/viewmodel/RecordsViewModel.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/records/ui/viewmodel/RecordsViewModel.kt @@ -2,8 +2,8 @@ package fr.ajaury.gwenedeg.records.ui.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import fr.ajaury.gwenedeg.records.domain.Record import fr.ajaury.gwenedeg.records.domain.RecordRepository +import fr.ajaury.gwenedeg.core.model.Record import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn