react-native-unified-player 0.4.1 → 0.5.4

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/README.md CHANGED
@@ -151,6 +151,10 @@ Control playback using the `UnifiedPlayer` object and the native tag of the `Uni
151
151
  yarn android # for Android
152
152
  ```
153
153
 
154
+ ## Publishing
155
+
156
+ This package is automatically published to npm when changes are pushed to the `main` or `master` branch. See [PUBLISHING.md](.github/PUBLISHING.md) for setup instructions and details about npm's new authentication system (granular access tokens).
157
+
154
158
  ## Contributing
155
159
 
156
160
  Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
@@ -4,7 +4,6 @@ import com.facebook.react.bridge.ReactApplicationContext
4
4
  import com.facebook.react.bridge.ReactContextBaseJavaModule
5
5
  import com.facebook.react.bridge.ReactMethod
6
6
  import com.facebook.react.bridge.Promise
7
- import com.facebook.react.uimanager.UIManagerModule
8
7
  import android.util.Log
9
8
  import com.facebook.react.bridge.UiThreadUtil
10
9
  import android.view.View
@@ -15,279 +14,118 @@ class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : R
15
14
  companion object {
16
15
  private const val TAG = "UnifiedPlayerModule"
17
16
  }
18
-
19
- init {
20
- Log.d(TAG, "UnifiedPlayerModule initialized")
21
- }
22
-
23
- override fun getName(): String {
24
- Log.d(TAG, "getName() called, returning 'UnifiedPlayer'")
25
- return "UnifiedPlayer"
26
- }
27
-
17
+
18
+ override fun getName(): String = "UnifiedPlayer"
19
+
28
20
  private fun getPlayerViewByTag(viewTag: Int): UnifiedPlayerView? {
29
- try {
21
+ return try {
30
22
  val view = reactApplicationContext.currentActivity?.findViewById<View>(viewTag)
31
- Log.d(TAG, "Looking for view with tag: $viewTag, found: ${view != null}")
32
-
33
- if (view is UnifiedPlayerView) {
34
- return view
35
- } else if (view != null) {
36
- Log.e(TAG, "View with tag $viewTag is not a UnifiedPlayerView, it's a ${view.javaClass.simpleName}")
23
+ (view as? UnifiedPlayerView) ?: run {
24
+ if (view != null) {
25
+ Log.e(TAG, "View with tag $viewTag is not a UnifiedPlayerView")
26
+ }
27
+ null
37
28
  }
38
29
  } catch (e: Exception) {
39
- Log.e(TAG, "Error finding view with tag $viewTag: ${e.message}", e)
30
+ Log.e(TAG, "Error finding view: ${e.message}")
31
+ null
40
32
  }
41
- return null
42
33
  }
43
-
44
- @ReactMethod
45
- fun play(viewTag: Int, promise: Promise) {
46
- Log.d(TAG, "Native play method called with viewTag: $viewTag")
34
+
35
+ /**
36
+ * Helper function to execute operations on the player view.
37
+ * Eliminates repetitive boilerplate across all methods.
38
+ */
39
+ private inline fun <T> executeOnPlayerView(
40
+ viewTag: Int,
41
+ promise: Promise,
42
+ errorCode: String,
43
+ crossinline action: (UnifiedPlayerView) -> T
44
+ ) {
47
45
  try {
48
46
  val playerView = getPlayerViewByTag(viewTag)
49
47
  if (playerView != null) {
50
48
  UiThreadUtil.runOnUiThread {
51
49
  try {
52
- playerView.play()
53
- Log.d(TAG, "Play command executed successfully")
54
- promise.resolve(true)
50
+ val result = action(playerView)
51
+ promise.resolve(result)
55
52
  } catch (e: Exception) {
56
- Log.e(TAG, "Error during play: ${e.message}", e)
57
- promise.reject("PLAY_ERROR", "Error during play: ${e.message}", e)
53
+ Log.e(TAG, "Error during $errorCode: ${e.message}")
54
+ promise.reject(errorCode, e.message, e)
58
55
  }
59
56
  }
60
57
  } else {
61
- Log.e(TAG, "Player view not found for tag: $viewTag")
62
58
  promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
63
59
  }
64
60
  } catch (e: Exception) {
65
- Log.e(TAG, "Error in play method: ${e.message}", e)
66
- promise.reject("PLAY_ERROR", "Error in play method: ${e.message}", e)
61
+ Log.e(TAG, "Error in $errorCode: ${e.message}")
62
+ promise.reject(errorCode, e.message, e)
67
63
  }
68
64
  }
69
65
 
70
66
  @ReactMethod
71
- fun pause(viewTag: Int, promise: Promise) {
72
- Log.d(TAG, "Native pause method called with viewTag: $viewTag")
73
- try {
74
- val playerView = getPlayerViewByTag(viewTag)
75
- if (playerView != null) {
76
- UiThreadUtil.runOnUiThread {
77
- try {
78
- playerView.pause()
79
- Log.d(TAG, "Pause command executed successfully")
80
- promise.resolve(true)
81
- } catch (e: Exception) {
82
- Log.e(TAG, "Error during pause: ${e.message}", e)
83
- promise.reject("PAUSE_ERROR", "Error during pause: ${e.message}", e)
84
- }
85
- }
86
- } else {
87
- Log.e(TAG, "Player view not found for tag: $viewTag")
88
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
89
- }
90
- } catch (e: Exception) {
91
- Log.e(TAG, "Error in pause method: ${e.message}", e)
92
- promise.reject("PAUSE_ERROR", "Error in pause method: ${e.message}", e)
93
- }
67
+ fun play(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "PLAY_ERROR") {
68
+ it.play()
69
+ true
94
70
  }
95
71
 
96
72
  @ReactMethod
97
- fun seekTo(viewTag: Int, seconds: Float, promise: Promise) {
98
- Log.d(TAG, "Native seekTo method called with viewTag: $viewTag, seconds: $seconds")
99
- try {
100
- val playerView = getPlayerViewByTag(viewTag)
101
- if (playerView != null) {
102
- UiThreadUtil.runOnUiThread {
103
- try {
104
- playerView.seekTo(seconds)
105
- Log.d(TAG, "SeekTo command executed successfully to $seconds seconds")
106
- promise.resolve(true)
107
- } catch (e: Exception) {
108
- Log.e(TAG, "Error during seekTo: ${e.message}", e)
109
- promise.reject("SEEK_ERROR", "Error during seekTo: ${e.message}", e)
110
- }
111
- }
112
- } else {
113
- Log.e(TAG, "Player view not found for tag: $viewTag")
114
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
115
- }
116
- } catch (e: Exception) {
117
- Log.e(TAG, "Error in seekTo method: ${e.message}", e)
118
- promise.reject("SEEK_ERROR", "Error in seekTo method: ${e.message}", e)
119
- }
73
+ fun pause(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "PAUSE_ERROR") {
74
+ it.pause()
75
+ true
120
76
  }
121
77
 
122
78
  @ReactMethod
123
- fun getCurrentTime(viewTag: Int, promise: Promise) {
124
- Log.d(TAG, "Native getCurrentTime method called with viewTag: $viewTag")
125
- try {
126
- val playerView = getPlayerViewByTag(viewTag)
127
- if (playerView != null) {
128
- UiThreadUtil.runOnUiThread {
129
- try {
130
- val currentTime = playerView.getCurrentTime()
131
- Log.d(TAG, "getCurrentTime executed successfully, current time: $currentTime")
132
- promise.resolve(currentTime)
133
- } catch (e: Exception) {
134
- Log.e(TAG, "Error getting current time: ${e.message}", e)
135
- promise.reject("GET_TIME_ERROR", "Error getting current time: ${e.message}", e)
136
- }
137
- }
138
- } else {
139
- Log.e(TAG, "Player view not found for tag: $viewTag")
140
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
141
- }
142
- } catch (e: Exception) {
143
- Log.e(TAG, "Error in getCurrentTime method: ${e.message}", e)
144
- promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method: ${e.message}", e)
145
- }
79
+ fun seekTo(viewTag: Int, seconds: Float, promise: Promise) = executeOnPlayerView(viewTag, promise, "SEEK_ERROR") {
80
+ it.seekTo(seconds)
81
+ true
146
82
  }
147
83
 
148
84
  @ReactMethod
149
- fun getDuration(viewTag: Int, promise: Promise) {
150
- Log.d(TAG, "Native getDuration method called with viewTag: $viewTag")
151
- try {
152
- val playerView = getPlayerViewByTag(viewTag)
153
- if (playerView != null) {
154
- UiThreadUtil.runOnUiThread {
155
- try {
156
- val duration = playerView.getDuration()
157
- Log.d(TAG, "getDuration executed successfully, duration: $duration")
158
- promise.resolve(duration)
159
- } catch (e: Exception) {
160
- Log.e(TAG, "Error getting duration: ${e.message}", e)
161
- promise.reject("GET_DURATION_ERROR", "Error getting duration: ${e.message}", e)
162
- }
163
- }
164
- } else {
165
- Log.e(TAG, "Player view not found for tag: $viewTag")
166
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
167
- }
168
- } catch (e: Exception) {
169
- Log.e(TAG, "Error in getDuration method: ${e.message}", e)
170
- promise.reject("GET_DURATION_ERROR", "Error in getDuration method: ${e.message}", e)
171
- }
85
+ fun getCurrentTime(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "GET_TIME_ERROR") {
86
+ it.getCurrentTime()
172
87
  }
173
88
 
174
89
  @ReactMethod
175
- fun capture(viewTag: Int, promise: Promise) {
176
- Log.d(TAG, "Native capture method called with viewTag: $viewTag")
177
- try {
178
- val playerView = getPlayerViewByTag(viewTag)
179
- if (playerView != null) {
180
- UiThreadUtil.runOnUiThread {
181
- try {
182
- // Assuming playerView has a method called capture() that returns a String
183
- val captureResult = playerView.capture()
184
- Log.d(TAG, "Capture command executed successfully, result: $captureResult")
185
- promise.resolve(captureResult)
186
- } catch (e: Exception) {
187
- Log.e(TAG, "Error during capture: ${e.message}", e)
188
- promise.reject("CAPTURE_ERROR", "Error during capture: ${e.message}", e)
189
- }
190
- }
191
- } else {
192
- Log.e(TAG, "Player view not found for tag: $viewTag")
193
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
194
- }
195
- } catch (e: Exception) {
196
- Log.e(TAG, "Error in capture method: ${e.message}", e)
197
- promise.reject("CAPTURE_ERROR", "Error in capture method: ${e.message}", e)
198
- }
90
+ fun getDuration(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "GET_DURATION_ERROR") {
91
+ it.getDuration()
199
92
  }
200
-
93
+
201
94
  @ReactMethod
202
- fun startRecording(viewTag: Int, outputPath: String?, promise: Promise) {
203
- Log.d(TAG, "Native startRecording method called with viewTag: $viewTag, outputPath: $outputPath")
204
- try {
205
- val playerView = getPlayerViewByTag(viewTag)
206
- if (playerView != null) {
207
- UiThreadUtil.runOnUiThread {
208
- try {
209
- // Determine the output path
210
- val finalOutputPath = if (outputPath.isNullOrEmpty()) {
211
- // Use app-specific storage for Android 10+ (API level 29+)
212
- val moviesDir = File(reactApplicationContext.getExternalFilesDir(Environment.DIRECTORY_MOVIES), "recordings")
213
- if (!moviesDir.exists()) {
214
- moviesDir.mkdirs()
215
- }
216
- val timestamp = System.currentTimeMillis()
217
- File(moviesDir, "recording_$timestamp.mp4").absolutePath
218
- } else {
219
- outputPath
220
- }
221
-
222
- // Start recording
223
- val result = playerView.startRecording(finalOutputPath)
224
- Log.d(TAG, "Start recording command executed successfully, result: $result")
225
- promise.resolve(result)
226
- } catch (e: Exception) {
227
- Log.e(TAG, "Error during startRecording: ${e.message}", e)
228
- promise.reject("RECORDING_ERROR", "Error during startRecording: ${e.message}", e)
229
- }
230
- }
231
- } else {
232
- Log.e(TAG, "Player view not found for tag: $viewTag")
233
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
234
- }
235
- } catch (e: Exception) {
236
- Log.e(TAG, "Error in startRecording method: ${e.message}", e)
237
- promise.reject("RECORDING_ERROR", "Error in startRecording method: ${e.message}", e)
238
- }
95
+ fun capture(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "CAPTURE_ERROR") {
96
+ it.capture()
239
97
  }
240
-
98
+
99
+ /**
100
+ * Start recording/downloading the video.
101
+ * Note: This downloads the source video file rather than recording the rendered video.
102
+ */
241
103
  @ReactMethod
242
- fun toggleFullscreen(viewTag: Int, isFullscreen: Boolean, promise: Promise) {
243
- Log.d(TAG, "Native toggleFullscreen method called with viewTag: $viewTag, isFullscreen: $isFullscreen")
244
- try {
245
- val playerView = getPlayerViewByTag(viewTag)
246
- if (playerView != null) {
247
- UiThreadUtil.runOnUiThread {
248
- try {
249
- playerView.setIsFullscreen(isFullscreen)
250
- Log.d(TAG, "toggleFullscreen executed successfully")
251
- promise.resolve(true)
252
- } catch (e: Exception) {
253
- Log.e(TAG, "Error toggling fullscreen: ${e.message}", e)
254
- promise.reject("FULLSCREEN_ERROR", "Error toggling fullscreen: ${e.message}", e)
255
- }
256
- }
257
- } else {
258
- Log.e(TAG, "Player view not found for tag: $viewTag")
259
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
260
- }
261
- } catch (e: Exception) {
262
- Log.e(TAG, "Error in toggleFullscreen method: ${e.message}", e)
263
- promise.reject("FULLSCREEN_ERROR", "Error in toggleFullscreen method: ${e.message}", e)
104
+ fun startRecording(viewTag: Int, outputPath: String?, promise: Promise) = executeOnPlayerView(viewTag, promise, "RECORDING_ERROR") {
105
+ val finalOutputPath = if (outputPath.isNullOrEmpty()) {
106
+ val moviesDir = File(reactApplicationContext.getExternalFilesDir(Environment.DIRECTORY_MOVIES), "recordings")
107
+ if (!moviesDir.exists()) moviesDir.mkdirs()
108
+ File(moviesDir, "recording_${System.currentTimeMillis()}.mp4").absolutePath
109
+ } else {
110
+ outputPath
264
111
  }
112
+ it.startRecording(finalOutputPath)
265
113
  }
266
114
 
267
115
  @ReactMethod
268
- fun stopRecording(viewTag: Int, promise: Promise) {
269
- Log.d(TAG, "Native stopRecording method called with viewTag: $viewTag")
270
- try {
271
- val playerView = getPlayerViewByTag(viewTag)
272
- if (playerView != null) {
273
- UiThreadUtil.runOnUiThread {
274
- try {
275
- // Stop recording
276
- val filePath = playerView.stopRecording()
277
- Log.d(TAG, "Stop recording command executed successfully, filePath: $filePath")
278
- promise.resolve(filePath)
279
- } catch (e: Exception) {
280
- Log.e(TAG, "Error during stopRecording: ${e.message}", e)
281
- promise.reject("RECORDING_ERROR", "Error during stopRecording: ${e.message}", e)
282
- }
283
- }
284
- } else {
285
- Log.e(TAG, "Player view not found for tag: $viewTag")
286
- promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
287
- }
288
- } catch (e: Exception) {
289
- Log.e(TAG, "Error in stopRecording method: ${e.message}", e)
290
- promise.reject("RECORDING_ERROR", "Error in stopRecording method: ${e.message}", e)
291
- }
116
+ fun toggleFullscreen(viewTag: Int, isFullscreen: Boolean, promise: Promise) = executeOnPlayerView(viewTag, promise, "FULLSCREEN_ERROR") {
117
+ it.setIsFullscreen(isFullscreen)
118
+ true
119
+ }
120
+
121
+ @ReactMethod
122
+ fun stopRecording(viewTag: Int, promise: Promise) = executeOnPlayerView(viewTag, promise, "RECORDING_ERROR") {
123
+ it.stopRecording()
124
+ }
125
+
126
+ @ReactMethod
127
+ fun setSpeed(viewTag: Int, speed: Float, promise: Promise) = executeOnPlayerView(viewTag, promise, "SPEED_ERROR") {
128
+ it.setSpeed(speed)
129
+ true
292
130
  }
293
131
  }