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.
@@ -0,0 +1,276 @@
1
+ package com.libmpv
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import android.view.SurfaceView
6
+ import dev.jdtech.mpv.MPVLib
7
+ import java.io.File
8
+ import java.io.FileOutputStream
9
+ import java.io.InputStream
10
+ import java.io.OutputStream
11
+
12
+ class LibmpvWrapper(private val applicationContext: Context) {
13
+ companion object {
14
+ private const val TAG = "react-native-libmpv"
15
+ private var swallow = true
16
+ }
17
+
18
+ private var created = false
19
+ private var isPlaying = false
20
+ private var hasPlayedOnce = false
21
+ private var eventObserver: MPVLib.EventObserver? = null
22
+ private var logObserver: MPVLib.LogObserver? = null
23
+ private var mpvDirectory: String? = null
24
+ private var surfaceWidth: Int = -1
25
+ private var surfaceHeight: Int = -1
26
+ private var surfaceView: SurfaceView? = null
27
+ private val mpv: MPVLib = MPVLib()
28
+
29
+ fun create(): Boolean {
30
+ mpv.create(applicationContext)
31
+ createMpvDirectory()
32
+ created = true
33
+ return true
34
+ }
35
+
36
+ fun isCreated(): Boolean = created
37
+ fun isPlaying(): Boolean = isPlaying
38
+ fun hasPlayedOnce(): Boolean = hasPlayedOnce
39
+ fun getMpvDirectoryPath(): String? = mpvDirectory
40
+
41
+ private fun createMpvDirectory() {
42
+ val mpvDir = File(applicationContext.getExternalFilesDir("mpv"), "mpv")
43
+ try {
44
+ mpvDirectory = mpvDir.absolutePath
45
+ if (!mpvDir.exists() && !mpvDir.mkdirs()) {
46
+ Log.e(TAG, "exception", IllegalArgumentException("Unable to create $mpvDir"))
47
+ return
48
+ }
49
+
50
+ // Copy font
51
+ val mpvFontPath = "${mpvDir}/subfont.ttf"
52
+ applicationContext.assets.open("subfont.ttf").use { subfontIn ->
53
+ FileOutputStream(mpvFontPath).use { fontOut ->
54
+ subfontIn.copyTo(fontOut)
55
+ }
56
+ }
57
+
58
+ // Copy conf
59
+ val mpvConfPath = "${mpvDir}/mpv.conf"
60
+ applicationContext.assets.open("mpv.conf").use { mpvConfIn ->
61
+ FileOutputStream(mpvConfPath).use { confOut ->
62
+ mpvConfIn.copyTo(confOut)
63
+ }
64
+ }
65
+ } catch (e: Exception) {
66
+ Log.e(TAG, "Unable to create the directory $mpvDir", e)
67
+ }
68
+ }
69
+
70
+ private fun logException(exception: Exception) {
71
+ try {
72
+ val message: String = (exception.message as? String) ?: "Unable to read error message"
73
+ logObserver?.logMessage("RNLE", 20, message)
74
+ } catch (e: Exception) {
75
+ if (!swallow) throw e
76
+ }
77
+ }
78
+
79
+ fun addEventObserver(observer: MPVLib.EventObserver) {
80
+ try {
81
+ if (!created) return
82
+ mpv.removeObservers()
83
+ eventObserver = observer
84
+ mpv.addObserver(eventObserver)
85
+ mpv.observeProperty("demuxer-cache-time", MPVLib.MPV_FORMAT_INT64)
86
+ mpv.observeProperty("duration", MPVLib.MPV_FORMAT_INT64)
87
+ mpv.observeProperty("eof-reached", MPVLib.MPV_FORMAT_FLAG)
88
+ mpv.observeProperty("paused-for-cache", MPVLib.MPV_FORMAT_FLAG)
89
+ mpv.observeProperty("seekable", MPVLib.MPV_FORMAT_FLAG)
90
+ mpv.observeProperty("speed", MPVLib.MPV_FORMAT_DOUBLE)
91
+ mpv.observeProperty("time-pos", MPVLib.MPV_FORMAT_INT64)
92
+ mpv.observeProperty("track-list", MPVLib.MPV_FORMAT_STRING)
93
+ } catch (e: Exception) {
94
+ logException(e)
95
+ if (!swallow) throw e
96
+ }
97
+ }
98
+
99
+ fun addLogObserver(observer: MPVLib.LogObserver) {
100
+ try {
101
+ if (!created) return
102
+ mpv.removeLogObservers()
103
+ logObserver = observer
104
+ mpv.addLogObserver(logObserver)
105
+ } catch (e: Exception) {
106
+ logException(e)
107
+ if (!swallow) throw e
108
+ }
109
+ }
110
+
111
+ fun setOptionString(option: String, setting: String) {
112
+ try {
113
+ if (created) mpv.setOptionString(option, setting)
114
+ } catch (e: Exception) {
115
+ logException(e)
116
+ if (!swallow) throw e
117
+ }
118
+ }
119
+
120
+ fun setPropertyString(property: String, setting: String) {
121
+ try {
122
+ if (created) mpv.setPropertyString(property, setting)
123
+ } catch (e: Exception) {
124
+ logException(e)
125
+ if (!swallow) throw e
126
+ }
127
+ }
128
+
129
+ fun init() {
130
+ try {
131
+ if (created) mpv.init()
132
+ } catch (e: Exception) {
133
+ logException(e)
134
+ if (!swallow) throw e
135
+ }
136
+ }
137
+
138
+ fun command(orders: Array<String>) {
139
+ try {
140
+ if (created) mpv.command(orders)
141
+ } catch (e: Exception) {
142
+ logException(e)
143
+ if (!swallow) throw e
144
+ }
145
+ }
146
+
147
+ fun attachSurface(surfaceView: SurfaceView) {
148
+ try {
149
+ if (created) {
150
+ this.surfaceView = surfaceView
151
+ applySurfaceDimensions()
152
+ mpv.attachSurface(surfaceView.holder.surface)
153
+ }
154
+ } catch (e: Exception) {
155
+ logException(e)
156
+ if (!swallow) throw e
157
+ }
158
+ }
159
+
160
+ fun play(url: String, options: String? = null) {
161
+ if (!isPlaying) {
162
+ if (options == null) {
163
+ command(arrayOf("loadfile", url))
164
+ } else {
165
+ command(arrayOf("loadfile", url, "replace", "0", options))
166
+ }
167
+ command(arrayOf("set", "pause", "no"))
168
+ hasPlayedOnce = true
169
+ isPlaying = true
170
+ }
171
+ }
172
+
173
+ fun pauseOrUnpause() {
174
+ if (!hasPlayedOnce) return
175
+ if (isPlaying) pause() else unpause()
176
+ }
177
+
178
+ fun pause() {
179
+ if (!hasPlayedOnce) return
180
+ if (isPlaying) {
181
+ command(arrayOf("set", "pause", "yes"))
182
+ isPlaying = false
183
+ }
184
+ }
185
+
186
+ fun unpause() {
187
+ if (!hasPlayedOnce) return
188
+ if (!isPlaying) {
189
+ command(arrayOf("set", "pause", "no"))
190
+ isPlaying = true
191
+ }
192
+ }
193
+
194
+ fun seekToSeconds(seconds: Int) {
195
+ if (created) {
196
+ command(arrayOf("seek", seconds.toString(), "absolute"))
197
+ }
198
+ }
199
+
200
+ private fun applySurfaceDimensions() {
201
+ if (surfaceHeight != -1 && surfaceWidth != -1 && surfaceView != null) {
202
+ surfaceView?.holder?.setFixedSize(surfaceWidth, surfaceHeight)
203
+ }
204
+ }
205
+
206
+ fun setSurfaceWidth(width: Int) {
207
+ surfaceWidth = width
208
+ applySurfaceDimensions()
209
+ }
210
+
211
+ fun setSurfaceHeight(height: Int) {
212
+ surfaceHeight = height
213
+ applySurfaceDimensions()
214
+ }
215
+
216
+ fun detachSurface() {
217
+ try {
218
+ mpv.detachSurface()
219
+ } catch (e: Exception) {
220
+ logException(e)
221
+ if (!swallow) throw e
222
+ }
223
+ }
224
+
225
+ fun destroy() {
226
+ try {
227
+ mpv.removeObservers()
228
+ } catch (e: Exception) {
229
+ logException(e)
230
+ if (!swallow) throw e
231
+ }
232
+ try {
233
+ mpv.removeLogObservers()
234
+ } catch (e: Exception) {
235
+ if (!swallow) throw e
236
+ }
237
+ try {
238
+ mpv.destroy()
239
+ created = false
240
+ } catch (e: Exception) {
241
+ logException(e)
242
+ if (!swallow) throw e
243
+ }
244
+ }
245
+
246
+ fun cleanup() {
247
+ if (created) {
248
+ try {
249
+ pause()
250
+ setPropertyString("vo", "null")
251
+ setPropertyString("ao", "null")
252
+ } catch (e: Exception) {
253
+ logException(e)
254
+ if (!swallow) throw e
255
+ }
256
+ try {
257
+ setOptionString("force-window", "no")
258
+ } catch (e: Exception) {
259
+ logException(e)
260
+ if (!swallow) throw e
261
+ }
262
+ try {
263
+ detachSurface()
264
+ } catch (e: Exception) {
265
+ logException(e)
266
+ if (!swallow) throw e
267
+ }
268
+ try {
269
+ destroy()
270
+ } catch (e: Exception) {
271
+ logException(e)
272
+ if (!swallow) throw e
273
+ }
274
+ }
275
+ }
276
+ }
package/app.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "expo": {
3
+ "plugins": [
4
+ [
5
+ "expo-build-properties",
6
+ {
7
+ "android": {
8
+ "minSdkVersion": 26
9
+ }
10
+ }
11
+ ]
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,15 @@
1
+ export type LibmpvVideoViewProps = {
2
+ ref: any;
3
+ style: any;
4
+ playUrl: string;
5
+ isPlaying: boolean;
6
+ useHardwareDecoder: boolean;
7
+ selectedAudioTrack: number;
8
+ selectedSubtitleTrack: number;
9
+ seekToSeconds: number;
10
+ surfaceWidth: number;
11
+ surfaceHeight: number;
12
+ onLibmpvEvent: (libmpvEvent: any) => void;
13
+ onLibmpvLog: (libmpvLog: any) => void;
14
+ };
15
+ //# sourceMappingURL=LibmpvVideo.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LibmpvVideo.types.d.ts","sourceRoot":"","sources":["../src/LibmpvVideo.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,WAAW,EAAE,GAAG,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,IAAI,CAAC;CACvC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=LibmpvVideo.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LibmpvVideo.types.js","sourceRoot":"","sources":["../src/LibmpvVideo.types.ts"],"names":[],"mappings":"","sourcesContent":["export type LibmpvVideoViewProps = {\n ref: any,\n style: any,\n playUrl: string,\n isPlaying: boolean,\n useHardwareDecoder: boolean,\n selectedAudioTrack: number,\n selectedSubtitleTrack: number,\n seekToSeconds: number,\n surfaceWidth: number,\n surfaceHeight: number,\n onLibmpvEvent: (libmpvEvent: any) => void,\n onLibmpvLog: (libmpvLog: any) => void,\n};"]}
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ export declare const LibmpvVideo: React.ForwardRefExoticComponent<Omit<any, "ref"> & React.RefAttributes<unknown>>;
3
+ export default LibmpvVideo;
4
+ //# sourceMappingURL=LibmpvVideoView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LibmpvVideoView.d.ts","sourceRoot":"","sources":["../src/LibmpvVideoView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqC/B,eAAO,MAAM,WAAW,kFAmEtB,CAAA;AAEF,eAAe,WAAW,CAAA"}
@@ -0,0 +1,84 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+ const styles = {
4
+ videoPlayer: {
5
+ position: "absolute",
6
+ left: 0,
7
+ bottom: 0,
8
+ right: 0,
9
+ top: 0
10
+ }
11
+ };
12
+ const EVENT_LOOKUP = {
13
+ 0: 'NONE',
14
+ 1: 'SHUTDOWN',
15
+ 2: 'LOG_MESSAGE',
16
+ 3: 'GET_PROPERTY_REPLY',
17
+ 4: 'SET_PROPERTY_REPLY',
18
+ 5: 'COMMAND_REPLY',
19
+ 6: 'START_FILE',
20
+ 7: 'END_FILE',
21
+ 8: 'FILE_LOADED',
22
+ 16: 'CLIENT_MESSAGE',
23
+ 17: 'VIDEO_RECONFIG',
24
+ 18: 'AUDIO_RECONFIG',
25
+ 20: 'SEEK',
26
+ 21: 'PLAYBACK_RESTART',
27
+ 22: 'PROPERTY_CHANGE',
28
+ 24: 'QUEUE_OVERFLOW',
29
+ 25: 'HOOK'
30
+ };
31
+ const LibmpvVideoView = requireNativeView('LibmpvVideo');
32
+ export const LibmpvVideo = React.forwardRef((props, parentRef) => {
33
+ const nativeRef = React.useRef(null);
34
+ // Pass mpv events and logs back up to the parent
35
+ const onLogEvent = (libmpvEvent) => {
36
+ if (props.onLibmpvEvent) {
37
+ if (libmpvEvent && libmpvEvent.nativeEvent) {
38
+ libmpvEvent = libmpvEvent.nativeEvent;
39
+ }
40
+ if (libmpvEvent.eventId) {
41
+ libmpvEvent.value = parseInt(libmpvEvent.eventId, 10);
42
+ libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId];
43
+ }
44
+ else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {
45
+ libmpvEvent.value = Number(libmpvEvent.value);
46
+ }
47
+ else if (libmpvEvent.kind === 'boolean') {
48
+ libmpvEvent.value = libmpvEvent.value === 'true';
49
+ }
50
+ return props.onLibmpvEvent(libmpvEvent);
51
+ }
52
+ };
53
+ const onLibmpvLog = (libmpvLog) => {
54
+ if (props.onLibmpvLog) {
55
+ if (libmpvLog && libmpvLog.nativeEvent) {
56
+ libmpvLog = libmpvLog.nativeEvent;
57
+ }
58
+ return props.onLibmpvLog(libmpvLog);
59
+ }
60
+ };
61
+ // Allow a parent to call native methods, such as tweaking subtitle properties
62
+ const callNativeMethod = (target) => {
63
+ return (pipeDelimitedArguments) => {
64
+ if (nativeRef.current) {
65
+ if (target === 'runCommand') {
66
+ nativeRef.current.runCommand(pipeDelimitedArguments);
67
+ }
68
+ else if (target === 'setOptionString') {
69
+ nativeRef.current.setOptionString(pipeDelimitedArguments);
70
+ }
71
+ }
72
+ };
73
+ };
74
+ React.useImperativeHandle(parentRef, () => ({
75
+ runCommand: callNativeMethod("runCommand"),
76
+ setOptionString: callNativeMethod("setOptionString")
77
+ }));
78
+ // The order props are handled in the native code is non-deterministic
79
+ // Each native prop setter checks to see if all required props are set
80
+ // Only then will it try to create an instance of mpv
81
+ return <LibmpvVideoView ref={nativeRef} style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer} playUrl={props.playUrl} isPlaying={props.isPlaying} useHardwareDecoder={props.useHardwareDecoder} surfaceWidth={props.surfaceWidth} surfaceHeight={props.surfaceHeight} selectedAudioTrack={props.selectedAudioTrack} selectedSubtitleTrack={props.selectedSubtitleTrack} seekToSeconds={props.seekToSeconds} onLibmpvEvent={onLogEvent} onLibmpvLog={onLibmpvLog}/>;
82
+ });
83
+ export default LibmpvVideo;
84
+ //# sourceMappingURL=LibmpvVideoView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LibmpvVideoView.js","sourceRoot":"","sources":["../src/LibmpvVideoView.tsx"],"names":[],"mappings":"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","sourcesContent":["import { requireNativeView } from 'expo';\nimport * as React from 'react';\n\nimport { LibmpvVideoViewProps } from './LibmpvVideo.types';\n\nconst styles: any = {\n videoPlayer: {\n position: \"absolute\",\n left: 0,\n bottom: 0,\n right: 0,\n top: 0\n }\n};\n\nconst EVENT_LOOKUP: any = {\n 0: 'NONE',\n 1: 'SHUTDOWN',\n 2: 'LOG_MESSAGE',\n 3: 'GET_PROPERTY_REPLY',\n 4: 'SET_PROPERTY_REPLY',\n 5: 'COMMAND_REPLY',\n 6: 'START_FILE',\n 7: 'END_FILE',\n 8: 'FILE_LOADED',\n 16: 'CLIENT_MESSAGE',\n 17: 'VIDEO_RECONFIG',\n 18: 'AUDIO_RECONFIG',\n 20: 'SEEK',\n 21: 'PLAYBACK_RESTART',\n 22: 'PROPERTY_CHANGE',\n 24: 'QUEUE_OVERFLOW',\n 25: 'HOOK'\n}\n\nconst LibmpvVideoView: React.ComponentType<LibmpvVideoViewProps> =\n requireNativeView('LibmpvVideo');\n\nexport const LibmpvVideo = React.forwardRef((props: any, parentRef: any) => {\n const nativeRef = React.useRef<any>(null);\n\n // Pass mpv events and logs back up to the parent\n const onLogEvent = (libmpvEvent: any) => {\n if (props.onLibmpvEvent) {\n if (libmpvEvent && libmpvEvent.nativeEvent) {\n libmpvEvent = libmpvEvent.nativeEvent\n }\n if (libmpvEvent.eventId) {\n libmpvEvent.value = parseInt(libmpvEvent.eventId, 10)\n libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId]\n }\n else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {\n libmpvEvent.value = Number(libmpvEvent.value)\n }\n else if (libmpvEvent.kind === 'boolean') {\n libmpvEvent.value = libmpvEvent.value === 'true'\n }\n return props.onLibmpvEvent(libmpvEvent)\n }\n }\n const onLibmpvLog = (libmpvLog: any) => {\n if (props.onLibmpvLog) {\n if (libmpvLog && libmpvLog.nativeEvent) {\n libmpvLog = libmpvLog.nativeEvent\n }\n return props.onLibmpvLog(libmpvLog);\n }\n }\n\n // Allow a parent to call native methods, such as tweaking subtitle properties\n const callNativeMethod = (target: string) => {\n return (pipeDelimitedArguments: string) => {\n if (nativeRef.current) {\n if (target === 'runCommand') {\n nativeRef.current.runCommand(pipeDelimitedArguments)\n }\n else if (target === 'setOptionString') {\n nativeRef.current.setOptionString(pipeDelimitedArguments)\n }\n }\n }\n }\n React.useImperativeHandle(parentRef, () => ({\n runCommand: callNativeMethod(\"runCommand\"),\n setOptionString: callNativeMethod(\"setOptionString\")\n }));\n\n\n // The order props are handled in the native code is non-deterministic\n // Each native prop setter checks to see if all required props are set\n // Only then will it try to create an instance of mpv\n return <LibmpvVideoView\n ref={nativeRef}\n style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer}\n playUrl={props.playUrl}\n isPlaying={props.isPlaying}\n useHardwareDecoder={props.useHardwareDecoder}\n surfaceWidth={props.surfaceWidth}\n surfaceHeight={props.surfaceHeight}\n selectedAudioTrack={props.selectedAudioTrack}\n selectedSubtitleTrack={props.selectedSubtitleTrack}\n seekToSeconds={props.seekToSeconds}\n onLibmpvEvent={onLogEvent}\n onLibmpvLog={onLibmpvLog}\n />\n})\n\nexport default LibmpvVideo\n"]}
@@ -0,0 +1,3 @@
1
+ export { default as LibmpvVideo } from './LibmpvVideoView';
2
+ export * from './LibmpvVideo.types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,cAAc,qBAAqB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { default as LibmpvVideo } from './LibmpvVideoView';
2
+ export * from './LibmpvVideo.types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,cAAc,qBAAqB,CAAC","sourcesContent":["export { default as LibmpvVideo } from './LibmpvVideoView';\nexport * from './LibmpvVideo.types';\n"]}
@@ -0,0 +1,10 @@
1
+ {
2
+ "platforms": [
3
+ "android"
4
+ ],
5
+ "android": {
6
+ "modules": [
7
+ "com.libmpv.LibmpvVideoModule"
8
+ ]
9
+ }
10
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "expo-libmpv",
3
+ "version": "0.1.0",
4
+ "description": "A libmpv native component for Android",
5
+ "main": "build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "scripts": {
8
+ "build": "expo-module build",
9
+ "clean": "expo-module clean",
10
+ "lint": "expo-module lint",
11
+ "test": "expo-module test",
12
+ "prepare": "expo-module prepare",
13
+ "prepublishOnly": "expo-module prepublishOnly",
14
+ "expo-module": "expo-module",
15
+ "open:android": "open -a \"Android Studio\" example/android"
16
+ },
17
+ "keywords": [
18
+ "react-native",
19
+ "expo",
20
+ "expo-libmpv",
21
+ "LibmpvVideo"
22
+ ],
23
+ "repository": "https://github.com/XBigTK13X/expo-libmpv",
24
+ "bugs": {
25
+ "url": "https://github.com/XBigTK13X/expo-libmpv/issues"
26
+ },
27
+ "author": "XBigTK13X <xbigtk13x@gmail.com> (https://github.com/XBigTK13X)",
28
+ "license": "MIT",
29
+ "homepage": "https://github.com/XBigTK13X/expo-libmpv#readme",
30
+ "dependencies": {
31
+ "expo-build-properties": "^0.14.8",
32
+ "expo-screen-orientation": "^8.1.7"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "~19.0.0",
36
+ "expo": "~53.0.0",
37
+ "expo-module-scripts": "^4.1.10",
38
+ "react-native": "0.79.1"
39
+ },
40
+ "peerDependencies": {
41
+ "expo": "*",
42
+ "react": "*",
43
+ "react-native": "*"
44
+ }
45
+ }
@@ -0,0 +1,13 @@
1
+ #! /bin/bash
2
+
3
+ rm -rf build
4
+ rm -rf android/build
5
+ rm -rf node_modules
6
+ cd example
7
+ rm -rf android/build
8
+ rm -rf node_modules
9
+ cd ..
10
+ npx yarn install
11
+ cd example
12
+ npx yarn install
13
+
@@ -0,0 +1,5 @@
1
+ #! /bin/bash
2
+
3
+ cd example
4
+
5
+ npx expo run:android
@@ -0,0 +1,14 @@
1
+ export type LibmpvVideoViewProps = {
2
+ ref: any,
3
+ style: any,
4
+ playUrl: string,
5
+ isPlaying: boolean,
6
+ useHardwareDecoder: boolean,
7
+ selectedAudioTrack: number,
8
+ selectedSubtitleTrack: number,
9
+ seekToSeconds: number,
10
+ surfaceWidth: number,
11
+ surfaceHeight: number,
12
+ onLibmpvEvent: (libmpvEvent: any) => void,
13
+ onLibmpvLog: (libmpvLog: any) => void,
14
+ };
@@ -0,0 +1,108 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+
4
+ import { LibmpvVideoViewProps } from './LibmpvVideo.types';
5
+
6
+ const styles: any = {
7
+ videoPlayer: {
8
+ position: "absolute",
9
+ left: 0,
10
+ bottom: 0,
11
+ right: 0,
12
+ top: 0
13
+ }
14
+ };
15
+
16
+ const EVENT_LOOKUP: any = {
17
+ 0: 'NONE',
18
+ 1: 'SHUTDOWN',
19
+ 2: 'LOG_MESSAGE',
20
+ 3: 'GET_PROPERTY_REPLY',
21
+ 4: 'SET_PROPERTY_REPLY',
22
+ 5: 'COMMAND_REPLY',
23
+ 6: 'START_FILE',
24
+ 7: 'END_FILE',
25
+ 8: 'FILE_LOADED',
26
+ 16: 'CLIENT_MESSAGE',
27
+ 17: 'VIDEO_RECONFIG',
28
+ 18: 'AUDIO_RECONFIG',
29
+ 20: 'SEEK',
30
+ 21: 'PLAYBACK_RESTART',
31
+ 22: 'PROPERTY_CHANGE',
32
+ 24: 'QUEUE_OVERFLOW',
33
+ 25: 'HOOK'
34
+ }
35
+
36
+ const LibmpvVideoView: React.ComponentType<LibmpvVideoViewProps> =
37
+ requireNativeView('LibmpvVideo');
38
+
39
+ export const LibmpvVideo = React.forwardRef((props: any, parentRef: any) => {
40
+ const nativeRef = React.useRef<any>(null);
41
+
42
+ // Pass mpv events and logs back up to the parent
43
+ const onLogEvent = (libmpvEvent: any) => {
44
+ if (props.onLibmpvEvent) {
45
+ if (libmpvEvent && libmpvEvent.nativeEvent) {
46
+ libmpvEvent = libmpvEvent.nativeEvent
47
+ }
48
+ if (libmpvEvent.eventId) {
49
+ libmpvEvent.value = parseInt(libmpvEvent.eventId, 10)
50
+ libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId]
51
+ }
52
+ else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {
53
+ libmpvEvent.value = Number(libmpvEvent.value)
54
+ }
55
+ else if (libmpvEvent.kind === 'boolean') {
56
+ libmpvEvent.value = libmpvEvent.value === 'true'
57
+ }
58
+ return props.onLibmpvEvent(libmpvEvent)
59
+ }
60
+ }
61
+ const onLibmpvLog = (libmpvLog: any) => {
62
+ if (props.onLibmpvLog) {
63
+ if (libmpvLog && libmpvLog.nativeEvent) {
64
+ libmpvLog = libmpvLog.nativeEvent
65
+ }
66
+ return props.onLibmpvLog(libmpvLog);
67
+ }
68
+ }
69
+
70
+ // Allow a parent to call native methods, such as tweaking subtitle properties
71
+ const callNativeMethod = (target: string) => {
72
+ return (pipeDelimitedArguments: string) => {
73
+ if (nativeRef.current) {
74
+ if (target === 'runCommand') {
75
+ nativeRef.current.runCommand(pipeDelimitedArguments)
76
+ }
77
+ else if (target === 'setOptionString') {
78
+ nativeRef.current.setOptionString(pipeDelimitedArguments)
79
+ }
80
+ }
81
+ }
82
+ }
83
+ React.useImperativeHandle(parentRef, () => ({
84
+ runCommand: callNativeMethod("runCommand"),
85
+ setOptionString: callNativeMethod("setOptionString")
86
+ }));
87
+
88
+
89
+ // The order props are handled in the native code is non-deterministic
90
+ // Each native prop setter checks to see if all required props are set
91
+ // Only then will it try to create an instance of mpv
92
+ return <LibmpvVideoView
93
+ ref={nativeRef}
94
+ style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer}
95
+ playUrl={props.playUrl}
96
+ isPlaying={props.isPlaying}
97
+ useHardwareDecoder={props.useHardwareDecoder}
98
+ surfaceWidth={props.surfaceWidth}
99
+ surfaceHeight={props.surfaceHeight}
100
+ selectedAudioTrack={props.selectedAudioTrack}
101
+ selectedSubtitleTrack={props.selectedSubtitleTrack}
102
+ seekToSeconds={props.seekToSeconds}
103
+ onLibmpvEvent={onLogEvent}
104
+ onLibmpvLog={onLibmpvLog}
105
+ />
106
+ })
107
+
108
+ export default LibmpvVideo
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default as LibmpvVideo } from './LibmpvVideoView';
2
+ export * from './LibmpvVideo.types';
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ // @generated by expo-module-scripts
2
+ {
3
+ "extends": "expo-module-scripts/tsconfig.base",
4
+ "compilerOptions": {
5
+ "outDir": "./build"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }