Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@ package com.arflix.tv.ui.screens.player

enum class SubtitleAiModel {
GROQ_LLAMA_70B,
GEMINI_FLASH_25
GEMINI_FLASH_25,
GEMINI_FLASH_LITE;

val geminiModelId: String?
get() = when (this) {
GROQ_LLAMA_70B -> null
GEMINI_FLASH_25 -> "gemini-2.5-flash"
GEMINI_FLASH_LITE -> "gemini-2.5-flash-lite"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ data class TranslationResult(

private const val GROQ_MODEL_ID = "llama-3.3-70b-versatile"
private const val GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
private const val GEMINI_MODEL_ID = "gemini-2.5-flash"
private const val GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models/$GEMINI_MODEL_ID:generateContent"
private const val GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models"

class SubtitleTranslationService(
private val apiKeyProvider: () -> String,
Expand Down Expand Up @@ -70,9 +69,14 @@ class SubtitleTranslationService(
return TranslationResult(lines, false, "API key missing")
}

return when (modelProvider()) {
return when (val model = modelProvider()) {
SubtitleAiModel.GROQ_LLAMA_70B -> translateGroq(lines, targetLanguage, apiKey)
SubtitleAiModel.GEMINI_FLASH_25 -> translateGemini(lines, targetLanguage, apiKey)
SubtitleAiModel.GEMINI_FLASH_25,
SubtitleAiModel.GEMINI_FLASH_LITE -> {
val geminiModelId = model.geminiModelId
?: error("${model.name} is missing a Gemini model id")
translateGemini(lines, targetLanguage, apiKey, geminiModelId)
}
}
}

Expand Down Expand Up @@ -132,7 +136,12 @@ class SubtitleTranslationService(
}
}

private suspend fun translateGemini(lines: List<String>, targetLanguage: String, apiKey: String): TranslationResult {
private suspend fun translateGemini(
lines: List<String>,
targetLanguage: String,
apiKey: String,
modelId: String
): TranslationResult {
val NL = "⏎"
val encoded = lines.map { it.replace("\n", NL) }
val inputArray = JSONArray(encoded)
Expand Down Expand Up @@ -162,7 +171,7 @@ class SubtitleTranslationService(
})
}

val url = "$GEMINI_BASE_URL?key=$apiKey"
val url = "$GEMINI_BASE_URL/$modelId:generateContent?key=$apiKey"
val request = Request.Builder()
.url(url)
.header("Content-Type", "application/json")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3655,6 +3655,7 @@ private fun MobileSettingsSubPage(
value = when (uiState.subtitleAiModel) {
com.arflix.tv.ui.screens.player.SubtitleAiModel.GROQ_LLAMA_70B -> "Groq - Llama 3.3 70B"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 -> "Google - Gemini 2.5 Flash"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE -> "Google - Gemini Flash-Lite"
},
isFocused = false,
onClick = onSubtitleAiModelClick
Expand Down Expand Up @@ -3699,6 +3700,8 @@ private fun MobileSettingsSubPage(
stringResource(R.string.ai_groq_disclaimer)
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 ->
stringResource(R.string.ai_gemini_disclaimer)
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE ->
stringResource(R.string.ai_gemini_disclaimer)
},
style = ArflixTypography.caption.copy(fontSize = 11.sp),
color = TextSecondary.copy(alpha = 0.5f),
Expand Down Expand Up @@ -4777,6 +4780,7 @@ private fun TvGeneralSettingsRows(
value = when (subtitleAiModel) {
com.arflix.tv.ui.screens.player.SubtitleAiModel.GROQ_LLAMA_70B -> "Groq - Llama 3.3 70B"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 -> "Google - Gemini 2.5 Flash"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE -> "Google - Gemini Flash-Lite"
},
isFocused = focusedIndex == localIndex,
onClick = onSubtitleAiModelClick,
Expand Down Expand Up @@ -5226,6 +5230,7 @@ private fun GeneralSettings(
value = when (subtitleAiModel) {
com.arflix.tv.ui.screens.player.SubtitleAiModel.GROQ_LLAMA_70B -> "Groq – Llama 3.3 70B"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 -> "Google – Gemini 2.5 Flash"
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE -> "Google – Gemini Flash-Lite"
},
isFocused = focusedIndex == 29,
onClick = onSubtitleAiModelClick,
Expand Down Expand Up @@ -5277,6 +5282,8 @@ private fun GeneralSettings(
stringResource(R.string.ai_groq_disclaimer)
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 ->
stringResource(R.string.ai_gemini_disclaimer)
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE ->
stringResource(R.string.ai_gemini_disclaimer)
},
style = ArflixTypography.caption.copy(fontSize = 11.sp),
color = TextSecondary.copy(alpha = 0.4f),
Expand Down Expand Up @@ -5307,7 +5314,8 @@ private fun AiModelDialog(
val isMobile = LocalDeviceType.current.isTouchDevice()
val options = listOf(
Triple(com.arflix.tv.ui.screens.player.SubtitleAiModel.GROQ_LLAMA_70B, "Groq – Llama 3.3 70B", stringResource(R.string.ai_groq_model_note)),
Triple(com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25, "Google – Gemini 2.5 Flash", stringResource(R.string.ai_gemini_model_note))
Triple(com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25, "Google – Gemini 2.5 Flash", stringResource(R.string.ai_gemini_model_note)),
Triple(com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE, "Google – Gemini Flash-Lite", "Always uses the latest Flash-Lite model")
)
BackHandler { onDismiss() }
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss) {
Expand Down Expand Up @@ -5384,6 +5392,7 @@ private fun AiApiKeyDialog(
val placeholder = when (model) {
com.arflix.tv.ui.screens.player.SubtitleAiModel.GROQ_LLAMA_70B -> "gsk_..."
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_25 -> "AIzaSy..."
com.arflix.tv.ui.screens.player.SubtitleAiModel.GEMINI_FLASH_LITE -> "AIzaSy..."
}
BackHandler { onDismiss() }
LaunchedEffect(Unit) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.arflix.tv.ui.screens.player

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

class SubtitleAiModelTest {

@Test
fun `Flash-Lite enum value round trips by name`() {
val model = SubtitleAiModel.GEMINI_FLASH_LITE

assertEquals(model, SubtitleAiModel.valueOf(model.name))
}

@Test
fun `Gemini models map to Gemini API model ids`() {
assertEquals("gemini-2.5-flash", SubtitleAiModel.GEMINI_FLASH_25.geminiModelId)
assertEquals("gemini-2.5-flash-lite", SubtitleAiModel.GEMINI_FLASH_LITE.geminiModelId)
}

@Test
fun `Groq model is not routed through Gemini model ids`() {
assertNull(SubtitleAiModel.GROQ_LLAMA_70B.geminiModelId)
}

@Test
fun `unknown persisted model string falls back to Groq`() {
val model = runCatching {
SubtitleAiModel.valueOf("UNKNOWN_MODEL")
}.getOrElse {
SubtitleAiModel.GROQ_LLAMA_70B
}

assertEquals(SubtitleAiModel.GROQ_LLAMA_70B, model)
}
}
Loading