diff --git a/app/src/main/assets/mpvkt.lua b/app/src/main/assets/mpvkt.lua new file mode 100644 index 0000000..0667e69 --- /dev/null +++ b/app/src/main/assets/mpvkt.lua @@ -0,0 +1,8 @@ +mpvkt = {} +function mpvkt.show_text(text) + mp.set_property("user-data/mpvkt/show_text", text) +end +function mpvkt.hide_ui() + mp.set_property("user-data/mpvkt/hide_ui", "unused") +end +return mpvkt diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt index 26586aa..620863c 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import live.mehiz.mpvkt.database.MpvKtDatabase +import live.mehiz.mpvkt.database.entities.CustomButtonEntity import live.mehiz.mpvkt.database.entities.PlaybackStateEntity import live.mehiz.mpvkt.databinding.PlayerLayoutBinding import live.mehiz.mpvkt.preferences.AdvancedPreferences @@ -230,6 +231,7 @@ class PlayerActivity : AppCompatActivity() { private fun copyMPVAssets() { Utils.copyAssets(this@PlayerActivity) + copyMPVScripts() copyMPVConfigFiles() // fonts can be lazily loaded lifecycleScope.launch(Dispatchers.IO) { @@ -277,6 +279,46 @@ class PlayerActivity : AppCompatActivity() { } } + private fun copyMPVScripts() { + val mpvktLua = assets.open("mpvkt.lua") + val applicationPath = filesDir.path + + val scriptsDir = fileManager.createDir(fileManager.fromPath(applicationPath), "scripts")!! + + fileManager.deleteContent(scriptsDir) + + File("$scriptsDir/mpvkt.lua") + .also { if (!it.exists()) it.createNewFile() } + .writeText(mpvktLua.bufferedReader().readText()) + } + + fun setupCustomButtons(buttons: List) { + val applicationPath = filesDir.path + + val scriptsDir = fileManager.createDir(fileManager.fromPath(applicationPath), "scripts")!! + + val customButtonsContent = buildString { + appendLine("local lua_modules = mp.find_config_file('scripts')") + appendLine("if lua_modules then") + appendLine("package.path = package.path .. ';' .. lua_modules .. '/?.lua;' .. lua_modules .. '/?/init.lua'") + appendLine("end") + appendLine("local mpvkt = require \"mpvkt\"") + buttons.forEach { button -> + appendLine("function button${button.id}()") + appendLine(button.content) + appendLine("end") + appendLine("mp.register_script_message('call_button${button.id}', button${button.id})") + } + } + + val file = File("$scriptsDir/custombuttons.lua") + .also { if (!it.exists()) it.createNewFile() } + + file.writeText(customButtonsContent) + + MPVLib.command(arrayOf("load-script", file.absolutePath)) + } + private fun copyMPVFonts() { try { val cachePath = cacheDir.path @@ -468,7 +510,7 @@ class PlayerActivity : AppCompatActivity() { "sid" -> trackId(value)?.let { viewModel.updateSubtitle(it, viewModel.selectedSubtitles.value.second) } "secondary-sid" -> trackId(value)?.let { viewModel.updateSubtitle(viewModel.selectedSubtitles.value.first, it) } "hwdec", "hwdec-current" -> viewModel.getDecoder() - "user-data/mpvkt" -> {} + "user-data/mpvkt" -> viewModel.handleLuaInvocation(value) } } diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt index 2a82a52..64d4368 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt @@ -74,10 +74,11 @@ enum class Panels { VideoFilters, } -enum class PlayerUpdates { - None, - DoubleSpeed, - AspectRatio, +sealed class PlayerUpdates { + data object None : PlayerUpdates() + data object DoubleSpeed : PlayerUpdates() + data object AspectRatio : PlayerUpdates() + data class ShowText(val value: String) : PlayerUpdates() } enum class VideoFilters( diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt index b75de7e..a96fbbb 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt @@ -18,8 +18,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import live.mehiz.mpvkt.R @@ -28,8 +27,8 @@ import live.mehiz.mpvkt.database.entities.CustomButtonEntity import live.mehiz.mpvkt.preferences.GesturePreferences import live.mehiz.mpvkt.preferences.PlayerPreferences import live.mehiz.mpvkt.ui.custombuttons.CustomButtonsUiState +import org.json.JSONObject import org.koin.java.KoinJavaComponent.inject -import java.io.File @Suppress("TooManyFunctions") class PlayerViewModel( @@ -45,14 +44,9 @@ class PlayerViewModel( init { viewModelScope.launch(Dispatchers.IO) { try { - mpvKtDatabase.customButtonDao().getCustomButtons() - .catch { e -> - Log.e(TAG, e.message ?: "Unable to fetch buttons") - _customButtons.update { _ -> CustomButtonsUiState.Error(e.message ?: "Unable to fetch buttons") } - } - .collectLatest { buttons -> - _customButtons.update { _ -> CustomButtonsUiState.Success(buttons) } - } + val buttons = mpvKtDatabase.customButtonDao().getCustomButtons().first() + activity.setupCustomButtons(buttons) + _customButtons.update { _ -> CustomButtonsUiState.Success(buttons) } } catch (e: Exception) { Log.e(TAG, e.message ?: "Unable to fetch buttons") _customButtons.update { _ -> CustomButtonsUiState.Error(e.message ?: "Unable to fetch buttons") } @@ -100,7 +94,7 @@ class PlayerViewModel( private val _areControlsLocked = MutableStateFlow(false) val areControlsLocked = _areControlsLocked.asStateFlow() - val playerUpdate = MutableStateFlow(PlayerUpdates.None) + val playerUpdate = MutableStateFlow(PlayerUpdates.None) val isBrightnessSliderShown = MutableStateFlow(false) val isVolumeSliderShown = MutableStateFlow(false) val currentBrightness = MutableStateFlow( @@ -467,6 +461,21 @@ class PlayerViewModel( } } + fun handleLuaInvocation(value: String) { + val jsonObject = JSONObject(value) + val type = jsonObject.keys().asSequence().firstOrNull { key -> + jsonObject.getString(key).isNotEmpty() + } ?: return + val data = jsonObject.getString(type) + + when (type) { + "show_text" -> playerUpdate.update { PlayerUpdates.ShowText(data) } + "hide_ui" -> hideControls() + } + + MPVLib.setPropertyString("user-data/mpvkt/$type", "") + } + private val doubleTapToSeekDuration = gesturePreferences.doubleTapToSeekDuration.get() fun updateSeekAmount(amount: Int) { @@ -535,12 +544,7 @@ class PlayerViewModel( } fun executeCustomButton(button: CustomButtonEntity) { - val tempFile = File.createTempFile("script", ".lua").apply { - writeText(button.content) - deleteOnExit() - } - - MPVLib.command(arrayOf("load-script", tempFile.absolutePath)) + MPVLib.command(arrayOf("script-message", "call_button${button.id}")) } } diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt index 6af7aaa..190b48d 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt @@ -252,14 +252,14 @@ fun PlayerControls( val currentPlayerUpdate by viewModel.playerUpdate.collectAsState() val aspectRatio by playerPreferences.videoAspect.collectAsState() LaunchedEffect(currentPlayerUpdate, aspectRatio) { - if (currentPlayerUpdate == PlayerUpdates.DoubleSpeed || currentPlayerUpdate == PlayerUpdates.None) { + if (currentPlayerUpdate is PlayerUpdates.DoubleSpeed || currentPlayerUpdate is PlayerUpdates.None) { return@LaunchedEffect } delay(2000) viewModel.playerUpdate.update { PlayerUpdates.None } } AnimatedVisibility( - currentPlayerUpdate != PlayerUpdates.None, + currentPlayerUpdate !is PlayerUpdates.None, enter = fadeIn(playerControlsEnterAnimationSpec()), exit = fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(playerUpdates) { @@ -268,8 +268,9 @@ fun PlayerControls( }, ) { when (currentPlayerUpdate) { - PlayerUpdates.DoubleSpeed -> DoubleSpeedPlayerUpdate() - PlayerUpdates.AspectRatio -> TextPlayerUpdate(stringResource(aspectRatio.titleRes)) + is PlayerUpdates.DoubleSpeed -> DoubleSpeedPlayerUpdate() + is PlayerUpdates.AspectRatio -> TextPlayerUpdate(stringResource(aspectRatio.titleRes)) + is PlayerUpdates.ShowText -> TextPlayerUpdate((currentPlayerUpdate as PlayerUpdates.ShowText).value) else -> {} } }