Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capabilities for lua functions to execute kotlin code #149

Merged
merged 3 commits into from
Nov 9, 2024
Merged
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
8 changes: 8 additions & 0 deletions app/src/main/assets/mpvkt.lua
Original file line number Diff line number Diff line change
@@ -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
44 changes: 43 additions & 1 deletion app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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<CustomButtonEntity>) {
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
Expand Down Expand Up @@ -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)
}
}

Expand Down
9 changes: 5 additions & 4 deletions app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerEnums.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
40 changes: 22 additions & 18 deletions app/src/main/java/live/mehiz/mpvkt/ui/player/PlayerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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") }
Expand Down Expand Up @@ -100,7 +94,7 @@ class PlayerViewModel(
private val _areControlsLocked = MutableStateFlow(false)
val areControlsLocked = _areControlsLocked.asStateFlow()

val playerUpdate = MutableStateFlow(PlayerUpdates.None)
val playerUpdate = MutableStateFlow<PlayerUpdates>(PlayerUpdates.None)
val isBrightnessSliderShown = MutableStateFlow(false)
val isVolumeSliderShown = MutableStateFlow(false)
val currentBrightness = MutableStateFlow(
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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}"))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 -> {}
}
}
Expand Down