expo-libmpv 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +5 -0
- package/LICENSE.txt +674 -0
- package/README.md +46 -0
- package/android/build.gradle +52 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/libmpv/LibmpvVideoModule.kt +105 -0
- package/android/src/main/java/com/libmpv/LibmpvVideoView.kt +240 -0
- package/android/src/main/java/com/libmpv/LibmpvWrapper.kt +276 -0
- package/app.json +14 -0
- package/build/LibmpvVideo.types.d.ts +15 -0
- package/build/LibmpvVideo.types.d.ts.map +1 -0
- package/build/LibmpvVideo.types.js +2 -0
- package/build/LibmpvVideo.types.js.map +1 -0
- package/build/LibmpvVideoView.d.ts +4 -0
- package/build/LibmpvVideoView.d.ts.map +1 -0
- package/build/LibmpvVideoView.js +84 -0
- package/build/LibmpvVideoView.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +10 -0
- package/package.json +45 -0
- package/script/clean.sh +13 -0
- package/script/develop.sh +5 -0
- package/src/LibmpvVideo.types.ts +14 -0
- package/src/LibmpvVideoView.tsx +108 -0
- package/src/index.ts +2 -0
- package/tsconfig.json +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# expo-libmpv
|
|
2
|
+
|
|
3
|
+
A libmpv native component for Android
|
|
4
|
+
|
|
5
|
+
# Usage
|
|
6
|
+
|
|
7
|
+
I only plan on supporting Android.
|
|
8
|
+
|
|
9
|
+
iOS support contributions are welcome, but I have no way to test it.
|
|
10
|
+
|
|
11
|
+
It uses the new Fabric architecture. This replaces react-native-libmpv's Native Module.
|
|
12
|
+
|
|
13
|
+
The component will display video. Controls are handled by the app, not this library.
|
|
14
|
+
|
|
15
|
+
Take a look at https://github.com/XBigTK13X/snowstream for a real app using the library.
|
|
16
|
+
|
|
17
|
+
## Updating the AAR
|
|
18
|
+
|
|
19
|
+
Pull down the fork of libmpv-android.
|
|
20
|
+
|
|
21
|
+
Make the needed changes.
|
|
22
|
+
|
|
23
|
+
Update the version in the kotlin file.
|
|
24
|
+
|
|
25
|
+
Run `buildscripts/docker-build.sh`
|
|
26
|
+
|
|
27
|
+
Run `buildscripts/prep-reposlite.sh VERSION`
|
|
28
|
+
|
|
29
|
+
Copy the versioned aar and pom to ~/maven-repo
|
|
30
|
+
|
|
31
|
+
Update the version in gradle.build
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Dev docs
|
|
35
|
+
|
|
36
|
+
https://docs.expo.dev/modules/native-view-tutorial/#add-an-event-to-notify-when-the-page-has-loaded
|
|
37
|
+
|
|
38
|
+
https://docs.expo.dev/modules/module-api/#events
|
|
39
|
+
|
|
40
|
+
https://docs.expo.dev/modules/module-api/#view
|
|
41
|
+
|
|
42
|
+
# Credits
|
|
43
|
+
|
|
44
|
+
I built this wrapper. But the library that drives the interactions with mpv comes from https://github.com/jarnedemeulemeester/libmpv-android.
|
|
45
|
+
|
|
46
|
+
That repo is the baseline, I merged in a PR that handle multi instance support and tweaked some things to my liking.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
|
|
3
|
+
group = 'com.libmpv'
|
|
4
|
+
version = '0.1.0'
|
|
5
|
+
|
|
6
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
|
+
apply from: expoModulesCorePlugin
|
|
8
|
+
applyKotlinExpoModulesCorePlugin()
|
|
9
|
+
useCoreDependencies()
|
|
10
|
+
useExpoPublishing()
|
|
11
|
+
|
|
12
|
+
// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
|
|
13
|
+
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
|
|
14
|
+
// Most of the time, you may like to manage the Android SDK versions yourself.
|
|
15
|
+
def useManagedAndroidSdkVersions = false
|
|
16
|
+
if (useManagedAndroidSdkVersions) {
|
|
17
|
+
useDefaultAndroidSdkVersions()
|
|
18
|
+
} else {
|
|
19
|
+
buildscript {
|
|
20
|
+
repositories {
|
|
21
|
+
google()
|
|
22
|
+
mavenCentral()
|
|
23
|
+
maven { url "/home/kretst/maven-repo" }
|
|
24
|
+
}
|
|
25
|
+
// Simple helper that allows the root project to override versions declared by this library.
|
|
26
|
+
ext.safeExtGet = { prop, fallback ->
|
|
27
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
project.android {
|
|
31
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
32
|
+
defaultConfig {
|
|
33
|
+
minSdkVersion 26
|
|
34
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dependencies {
|
|
40
|
+
implementation "com.libmpv:android-libmpv:0.5.10"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
android {
|
|
44
|
+
namespace "com.libmpv"
|
|
45
|
+
defaultConfig {
|
|
46
|
+
versionCode 1
|
|
47
|
+
versionName "0.1.0"
|
|
48
|
+
}
|
|
49
|
+
lintOptions {
|
|
50
|
+
abortOnError false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package com.libmpv
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.modules.Module
|
|
4
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
|
+
|
|
6
|
+
class LibmpvVideoModule : Module() {
|
|
7
|
+
override fun definition() = ModuleDefinition {
|
|
8
|
+
Name("LibmpvVideo")
|
|
9
|
+
View(LibmpvVideoView::class) {
|
|
10
|
+
Events("onLibmpvLog", "onLibmpvEvent")
|
|
11
|
+
|
|
12
|
+
AsyncFunction("runCommand") { view: LibmpvVideoView, orders: String ->
|
|
13
|
+
view.runCommand(orders)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
AsyncFunction("setOptionString") { view: LibmpvVideoView, options: String ->
|
|
17
|
+
view.setOptionString(options)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Prop("playUrl") { view: LibmpvVideoView, playUrl: String ->
|
|
21
|
+
view.playUrl = playUrl
|
|
22
|
+
if (view.isSurfaceReady()) {
|
|
23
|
+
view.mpv.play(playUrl)
|
|
24
|
+
} else {
|
|
25
|
+
view.attemptCreation()
|
|
26
|
+
}
|
|
27
|
+
view.log("setPlayUrl", playUrl)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Prop("useHardwareDecoder") { view: LibmpvVideoView, useHardwareDecoder: Boolean ->
|
|
31
|
+
view.useHardwareDecoder = useHardwareDecoder
|
|
32
|
+
if (view.isSurfaceReady()) {
|
|
33
|
+
view.setHardwareDecoder(useHardwareDecoder)
|
|
34
|
+
} else {
|
|
35
|
+
view.attemptCreation()
|
|
36
|
+
}
|
|
37
|
+
view.log("setUseHardwareDecoder", "$useHardwareDecoder")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Prop("surfaceWidth") { view: LibmpvVideoView, surfaceWidth: Int ->
|
|
41
|
+
view.surfaceWidth = surfaceWidth
|
|
42
|
+
if (view.isSurfaceReady()) {
|
|
43
|
+
view.mpv.setSurfaceWidth(surfaceWidth)
|
|
44
|
+
} else {
|
|
45
|
+
view.attemptCreation()
|
|
46
|
+
}
|
|
47
|
+
view.log("setSurfaceWidth", "$surfaceWidth")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Prop("surfaceHeight") { view: LibmpvVideoView, surfaceHeight: Int ->
|
|
51
|
+
view.surfaceHeight = surfaceHeight
|
|
52
|
+
if (view.isSurfaceReady()) {
|
|
53
|
+
view.mpv.setSurfaceHeight(surfaceHeight)
|
|
54
|
+
} else {
|
|
55
|
+
view.attemptCreation()
|
|
56
|
+
}
|
|
57
|
+
view.log("setSurfaceHeight", "$surfaceHeight")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Prop("selectedAudioTrack") { view: LibmpvVideoView, audioTrackIndex: Int ->
|
|
61
|
+
view.audioIndex = audioTrackIndex
|
|
62
|
+
if (view.isSurfaceReady()) {
|
|
63
|
+
val mpvIndex = if (audioTrackIndex != -1) (audioTrackIndex + 1).toString() else "no"
|
|
64
|
+
view.mpv.setOptionString("aid", mpvIndex)
|
|
65
|
+
} else {
|
|
66
|
+
view.attemptCreation()
|
|
67
|
+
}
|
|
68
|
+
view.log("selectAudioTrack", "$audioTrackIndex")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Prop("selectedSubtitleTrack") { view: LibmpvVideoView, subtitleTrackIndex: Int ->
|
|
72
|
+
view.subtitleIndex = subtitleTrackIndex
|
|
73
|
+
if (view.isSurfaceReady()) {
|
|
74
|
+
val mpvIndex = if (subtitleTrackIndex != -1) (subtitleTrackIndex + 1).toString() else "no"
|
|
75
|
+
view.mpv.setOptionString("sid", mpvIndex)
|
|
76
|
+
} else {
|
|
77
|
+
view.attemptCreation()
|
|
78
|
+
}
|
|
79
|
+
view.log("selectSubtitleTrack", "$subtitleTrackIndex")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Prop("seekToSeconds") { view: LibmpvVideoView, seconds: Int ->
|
|
83
|
+
if (view.isSurfaceReady()) {
|
|
84
|
+
view.mpv.seekToSeconds(seconds)
|
|
85
|
+
}
|
|
86
|
+
view.log("seekToSeconds", "$seconds")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Prop("isPlaying") { view: LibmpvVideoView, isPlaying: Boolean ->
|
|
90
|
+
if (view.isSurfaceReady() && view.mpv.hasPlayedOnce()) {
|
|
91
|
+
when {
|
|
92
|
+
isPlaying && !view.mpv.isPlaying() -> {
|
|
93
|
+
view.mpv.unpause()
|
|
94
|
+
}
|
|
95
|
+
!isPlaying && view.mpv.isPlaying() -> {
|
|
96
|
+
view.mpv.pause()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
view.attemptCreation()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
package com.libmpv
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.SurfaceHolder
|
|
5
|
+
import android.view.SurfaceView
|
|
6
|
+
import android.widget.FrameLayout
|
|
7
|
+
import dev.jdtech.mpv.MPVLib
|
|
8
|
+
import expo.modules.kotlin.AppContext
|
|
9
|
+
import expo.modules.kotlin.views.ExpoView
|
|
10
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
11
|
+
|
|
12
|
+
class LibmpvVideoView(context: Context, appContext: AppContext) :
|
|
13
|
+
ExpoView(context,appContext),
|
|
14
|
+
SurfaceHolder.Callback,
|
|
15
|
+
MPVLib.LogObserver,
|
|
16
|
+
MPVLib.EventObserver {
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
const val HARDWARE_OPTIONS = "mediacodec-copy"
|
|
20
|
+
const val ACCELERATED_CODECS = "h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private val onLibmpvLog by EventDispatcher()
|
|
24
|
+
private val onLibmpvEvent by EventDispatcher()
|
|
25
|
+
|
|
26
|
+
private var isSurfaceCreated: Boolean = false
|
|
27
|
+
val mpv: LibmpvWrapper = LibmpvWrapper(context)
|
|
28
|
+
private val surfaceView: SurfaceView = SurfaceView(context)
|
|
29
|
+
|
|
30
|
+
// JavaScript props
|
|
31
|
+
var playUrl: String? = null
|
|
32
|
+
var surfaceWidth: Int? = null
|
|
33
|
+
var surfaceHeight: Int? = null
|
|
34
|
+
var audioIndex: Int? = null
|
|
35
|
+
var subtitleIndex: Int? = null
|
|
36
|
+
var useHardwareDecoder: Boolean? = null
|
|
37
|
+
|
|
38
|
+
init {
|
|
39
|
+
surfaceView.holder.addCallback(this)
|
|
40
|
+
val layoutParams = FrameLayout.LayoutParams(
|
|
41
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
42
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
43
|
+
)
|
|
44
|
+
addView(surfaceView, layoutParams)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fun cleanup() {
|
|
48
|
+
surfaceView.holder.removeCallback(this)
|
|
49
|
+
mpv.cleanup()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun isSurfaceReady(): Boolean = isSurfaceCreated
|
|
53
|
+
|
|
54
|
+
fun attemptCreation(){
|
|
55
|
+
val allPropsReady = playUrl != null &&
|
|
56
|
+
surfaceWidth != null &&
|
|
57
|
+
surfaceHeight != null &&
|
|
58
|
+
audioIndex != null &&
|
|
59
|
+
subtitleIndex != null &&
|
|
60
|
+
useHardwareDecoder != null
|
|
61
|
+
|
|
62
|
+
if (allPropsReady) {
|
|
63
|
+
log("LibmpvVideoView.attemptCreation", "Initializing MPV instance")
|
|
64
|
+
createNativePlayer()
|
|
65
|
+
} else {
|
|
66
|
+
log("LibmpvVideoView.attemptCreation", "attemptCreation wasn't ready")
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun createNativePlayer() {
|
|
71
|
+
mpv.create()
|
|
72
|
+
prepareMpvSettings()
|
|
73
|
+
log("LibmpvVideoView.createNativePlayer", "mpv settings prepared. Waiting on surface creation.")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun runCommand(orders: String){
|
|
77
|
+
mpv.command(orders.split("|").toTypedArray())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fun setOptionString(options: String){
|
|
81
|
+
val parts = options.split("|").toTypedArray()
|
|
82
|
+
mpv.setOptionString(parts[0],parts[1])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private fun prepareMpvSettings() {
|
|
86
|
+
mpv.addLogObserver(this)
|
|
87
|
+
mpv.addEventObserver(this)
|
|
88
|
+
mpv.setOptionString("force-window", "no")
|
|
89
|
+
|
|
90
|
+
mpv.setOptionString("config", "yes")
|
|
91
|
+
val mpvDir = mpv.getMpvDirectoryPath()
|
|
92
|
+
mpvDir?.let{
|
|
93
|
+
mpv.setOptionString("config-dir", mpvDir)
|
|
94
|
+
mpv.setOptionString("sub-font-dir", mpvDir)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
mpv.setOptionString("keep-open", "always")
|
|
98
|
+
mpv.setOptionString("save-position-on-quit", "no")
|
|
99
|
+
mpv.setOptionString("ytdl", "no")
|
|
100
|
+
mpv.setOptionString("msg-level", "all=no")
|
|
101
|
+
|
|
102
|
+
mpv.setOptionString("profile", "fast")
|
|
103
|
+
mpv.setOptionString("vo", "gpu-next")
|
|
104
|
+
|
|
105
|
+
if (useHardwareDecoder == true) {
|
|
106
|
+
mpv.setOptionString("hwdec", HARDWARE_OPTIONS)
|
|
107
|
+
mpv.setOptionString("hwdec-codecs", ACCELERATED_CODECS)
|
|
108
|
+
} else {
|
|
109
|
+
mpv.setOptionString("hwdec", "no")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
mpv.setOptionString("gpu-context", "android")
|
|
113
|
+
mpv.setOptionString("opengl-es", "yes")
|
|
114
|
+
mpv.setOptionString("video-sync", "audio")
|
|
115
|
+
|
|
116
|
+
mpv.setOptionString("ao", "audiotrack")
|
|
117
|
+
mpv.setOptionString("alang", "")
|
|
118
|
+
|
|
119
|
+
mpv.setOptionString("sub-font-provider", "none")
|
|
120
|
+
mpv.setOptionString("slang", "")
|
|
121
|
+
mpv.setOptionString("sub-scale-with-window", "yes")
|
|
122
|
+
mpv.setOptionString("sub-use-margins", "no")
|
|
123
|
+
|
|
124
|
+
mpv.setOptionString("cache", "yes")
|
|
125
|
+
mpv.setOptionString("cache-pause-initial", "yes")
|
|
126
|
+
mpv.setOptionString("cache-secs", "5")
|
|
127
|
+
mpv.setOptionString("demuxer-readahead-secs", "5")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun log(method: String, argument: String) {
|
|
131
|
+
onLibmpvLog(mapOf(
|
|
132
|
+
"method" to method,
|
|
133
|
+
"argument" to argument
|
|
134
|
+
))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override fun surfaceCreated(holder: SurfaceHolder) {
|
|
138
|
+
val width = surfaceWidth ?: 0
|
|
139
|
+
val height = surfaceHeight ?: 0
|
|
140
|
+
// In the new Fabric version, this is stretching the content
|
|
141
|
+
//holder.setFixedSize(width, height)
|
|
142
|
+
//mpv.setPropertyString("android-surface-size", "${width}x${height}")
|
|
143
|
+
mpv.attachSurface(surfaceView)
|
|
144
|
+
prepareMpvPlayback()
|
|
145
|
+
isSurfaceCreated = true
|
|
146
|
+
log("LibmpvVideoView.surfaceCreated", "Surface created and MPV should be playing")
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun prepareMpvPlayback() {
|
|
150
|
+
mpv.init()
|
|
151
|
+
mpv.setOptionString("force-window", "yes")
|
|
152
|
+
var options = "vid=1"
|
|
153
|
+
options += if (audioIndex == -1) {
|
|
154
|
+
",aid=no"
|
|
155
|
+
} else {
|
|
156
|
+
",aid=${(audioIndex ?: 0) + 1}"
|
|
157
|
+
}
|
|
158
|
+
options += if (subtitleIndex == -1) {
|
|
159
|
+
",sid=no"
|
|
160
|
+
} else {
|
|
161
|
+
",sid=${(subtitleIndex ?: 0) + 1}"
|
|
162
|
+
}
|
|
163
|
+
val url: String = (playUrl as? String) ?: ""
|
|
164
|
+
mpv.play(url, options)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fun setHardwareDecoder(useHardware: Boolean) {
|
|
168
|
+
useHardwareDecoder = useHardware
|
|
169
|
+
if (useHardwareDecoder == true) {
|
|
170
|
+
mpv.setOptionString("hwdec", HARDWARE_OPTIONS)
|
|
171
|
+
mpv.setOptionString("hwdec-codecs", ACCELERATED_CODECS)
|
|
172
|
+
} else {
|
|
173
|
+
mpv.setOptionString("hwdec", "no")
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
|
|
178
|
+
|
|
179
|
+
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
|
180
|
+
mpv.setPropertyString("vo", "null")
|
|
181
|
+
mpv.setPropertyString("force-window", "no")
|
|
182
|
+
mpv.detachSurface()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// MPVLib.LogObserver
|
|
186
|
+
override fun logMessage(prefix: String, level: Int, text: String) {
|
|
187
|
+
onLibmpvLog(mapOf(
|
|
188
|
+
"prefix" to prefix,
|
|
189
|
+
"level" to "$level",
|
|
190
|
+
"text" to text
|
|
191
|
+
))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// MPVLib.EventObserver
|
|
195
|
+
override fun eventProperty(property: String) {
|
|
196
|
+
onLibmpvEvent(mapOf(
|
|
197
|
+
"property" to property,
|
|
198
|
+
"kind" to "none"
|
|
199
|
+
))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
override fun eventProperty(property: String, value: Long) {
|
|
203
|
+
onLibmpvEvent(mapOf(
|
|
204
|
+
"property" to property,
|
|
205
|
+
"kind" to "long",
|
|
206
|
+
"value" to "$value"
|
|
207
|
+
))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
override fun eventProperty(property: String, value: Double) {
|
|
211
|
+
onLibmpvEvent(mapOf(
|
|
212
|
+
"property" to property,
|
|
213
|
+
"kind" to "double",
|
|
214
|
+
"value" to "$value"
|
|
215
|
+
))
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
override fun eventProperty(property: String, value: Boolean) {
|
|
219
|
+
onLibmpvEvent(mapOf(
|
|
220
|
+
"property" to property,
|
|
221
|
+
"kind" to "boolean",
|
|
222
|
+
"value" to if (value) "true" else "false"
|
|
223
|
+
))
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
override fun eventProperty(property: String, value: String) {
|
|
227
|
+
onLibmpvEvent(mapOf(
|
|
228
|
+
"property" to property,
|
|
229
|
+
"kind" to "string",
|
|
230
|
+
"value" to value
|
|
231
|
+
))
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
override fun event(eventId: Int) {
|
|
235
|
+
onLibmpvEvent(mapOf(
|
|
236
|
+
"eventId" to "$eventId",
|
|
237
|
+
"kind" to "eventId"
|
|
238
|
+
))
|
|
239
|
+
}
|
|
240
|
+
}
|