react-native-unified-player 0.3.0 → 0.3.2
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/android/build.gradle +2 -1
- package/android/gradle.properties +1 -0
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerModule.kt +119 -42
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerPackage.kt +6 -3
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerView.kt +137 -54
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerViewManager.kt +23 -5
- package/lib/module/index.js +71 -59
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +32 -19
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +99 -75
package/android/build.gradle
CHANGED
|
@@ -9,7 +9,7 @@ buildscript {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
dependencies {
|
|
12
|
-
classpath "com.android.tools.build:gradle:
|
|
12
|
+
classpath "com.android.tools.build:gradle:8.1.0"
|
|
13
13
|
// noinspection DifferentKotlinGradleVersion
|
|
14
14
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
|
|
15
15
|
}
|
|
@@ -68,6 +68,7 @@ android {
|
|
|
68
68
|
repositories {
|
|
69
69
|
mavenCentral()
|
|
70
70
|
google()
|
|
71
|
+
maven { url "$rootDir/../node_modules/react-native/android" }
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
@@ -6,89 +6,166 @@ import com.facebook.react.bridge.ReactMethod
|
|
|
6
6
|
import com.facebook.react.bridge.Promise
|
|
7
7
|
import com.facebook.react.uimanager.UIManagerModule
|
|
8
8
|
import android.util.Log
|
|
9
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
10
|
+
import android.view.View
|
|
9
11
|
|
|
10
12
|
class UnifiedPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
11
13
|
companion object {
|
|
12
14
|
private const val TAG = "UnifiedPlayerModule"
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
init {
|
|
18
|
+
Log.d(TAG, "UnifiedPlayerModule initialized")
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
override fun getName(): String {
|
|
22
|
+
Log.d(TAG, "getName() called, returning 'UnifiedPlayer'")
|
|
16
23
|
return "UnifiedPlayer"
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
private fun
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
private fun getPlayerViewByTag(viewTag: Int): UnifiedPlayerView? {
|
|
27
|
+
try {
|
|
28
|
+
val view = reactApplicationContext.currentActivity?.findViewById<View>(viewTag)
|
|
29
|
+
Log.d(TAG, "Looking for view with tag: $viewTag, found: ${view != null}")
|
|
30
|
+
|
|
31
|
+
if (view is UnifiedPlayerView) {
|
|
32
|
+
return view
|
|
33
|
+
} else if (view != null) {
|
|
34
|
+
Log.e(TAG, "View with tag $viewTag is not a UnifiedPlayerView, it's a ${view.javaClass.simpleName}")
|
|
35
|
+
}
|
|
36
|
+
} catch (e: Exception) {
|
|
37
|
+
Log.e(TAG, "Error finding view with tag $viewTag: ${e.message}", e)
|
|
25
38
|
}
|
|
26
|
-
|
|
27
|
-
return view
|
|
39
|
+
return null
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
@ReactMethod
|
|
31
|
-
fun play(
|
|
43
|
+
fun play(viewTag: Int, promise: Promise) {
|
|
44
|
+
Log.d(TAG, "Native play method called with viewTag: $viewTag")
|
|
32
45
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
47
|
+
if (playerView != null) {
|
|
48
|
+
UiThreadUtil.runOnUiThread {
|
|
49
|
+
try {
|
|
50
|
+
playerView.play()
|
|
51
|
+
Log.d(TAG, "Play command executed successfully")
|
|
52
|
+
promise.resolve(true)
|
|
53
|
+
} catch (e: Exception) {
|
|
54
|
+
Log.e(TAG, "Error during play: ${e.message}", e)
|
|
55
|
+
promise.reject("PLAY_ERROR", "Error during play: ${e.message}", e)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
60
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
36
61
|
}
|
|
37
62
|
} catch (e: Exception) {
|
|
38
|
-
Log.e(TAG, "Error
|
|
63
|
+
Log.e(TAG, "Error in play method: ${e.message}", e)
|
|
64
|
+
promise.reject("PLAY_ERROR", "Error in play method: ${e.message}", e)
|
|
39
65
|
}
|
|
40
66
|
}
|
|
41
|
-
|
|
67
|
+
|
|
42
68
|
@ReactMethod
|
|
43
|
-
fun pause(
|
|
69
|
+
fun pause(viewTag: Int, promise: Promise) {
|
|
70
|
+
Log.d(TAG, "Native pause method called with viewTag: $viewTag")
|
|
44
71
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
72
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
73
|
+
if (playerView != null) {
|
|
74
|
+
UiThreadUtil.runOnUiThread {
|
|
75
|
+
try {
|
|
76
|
+
playerView.pause()
|
|
77
|
+
Log.d(TAG, "Pause command executed successfully")
|
|
78
|
+
promise.resolve(true)
|
|
79
|
+
} catch (e: Exception) {
|
|
80
|
+
Log.e(TAG, "Error during pause: ${e.message}", e)
|
|
81
|
+
promise.reject("PAUSE_ERROR", "Error during pause: ${e.message}", e)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
86
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
49
87
|
}
|
|
50
88
|
} catch (e: Exception) {
|
|
51
|
-
Log.e(TAG, "Error
|
|
89
|
+
Log.e(TAG, "Error in pause method: ${e.message}", e)
|
|
90
|
+
promise.reject("PAUSE_ERROR", "Error in pause method: ${e.message}", e)
|
|
52
91
|
}
|
|
53
92
|
}
|
|
54
|
-
|
|
93
|
+
|
|
55
94
|
@ReactMethod
|
|
56
|
-
fun seekTo(
|
|
95
|
+
fun seekTo(viewTag: Int, seconds: Float, promise: Promise) {
|
|
96
|
+
Log.d(TAG, "Native seekTo method called with viewTag: $viewTag, seconds: $seconds")
|
|
57
97
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
99
|
+
if (playerView != null) {
|
|
100
|
+
UiThreadUtil.runOnUiThread {
|
|
101
|
+
try {
|
|
102
|
+
playerView.seekTo(seconds)
|
|
103
|
+
Log.d(TAG, "SeekTo command executed successfully to $seconds seconds")
|
|
104
|
+
promise.resolve(true)
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
Log.e(TAG, "Error during seekTo: ${e.message}", e)
|
|
107
|
+
promise.reject("SEEK_ERROR", "Error during seekTo: ${e.message}", e)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
112
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
61
113
|
}
|
|
62
114
|
} catch (e: Exception) {
|
|
63
|
-
Log.e(TAG, "Error
|
|
115
|
+
Log.e(TAG, "Error in seekTo method: ${e.message}", e)
|
|
116
|
+
promise.reject("SEEK_ERROR", "Error in seekTo method: ${e.message}", e)
|
|
64
117
|
}
|
|
65
118
|
}
|
|
66
|
-
|
|
119
|
+
|
|
67
120
|
@ReactMethod
|
|
68
|
-
fun getCurrentTime(
|
|
121
|
+
fun getCurrentTime(viewTag: Int, promise: Promise) {
|
|
122
|
+
Log.d(TAG, "Native getCurrentTime method called with viewTag: $viewTag")
|
|
69
123
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
124
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
125
|
+
if (playerView != null) {
|
|
126
|
+
UiThreadUtil.runOnUiThread {
|
|
127
|
+
try {
|
|
128
|
+
val currentTime = playerView.getCurrentTime()
|
|
129
|
+
Log.d(TAG, "getCurrentTime executed successfully, current time: $currentTime")
|
|
130
|
+
promise.resolve(currentTime)
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
Log.e(TAG, "Error getting current time: ${e.message}", e)
|
|
133
|
+
promise.reject("GET_TIME_ERROR", "Error getting current time: ${e.message}", e)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
138
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
74
139
|
}
|
|
75
140
|
} catch (e: Exception) {
|
|
76
|
-
Log.e(TAG, "Error
|
|
77
|
-
promise.reject("
|
|
141
|
+
Log.e(TAG, "Error in getCurrentTime method: ${e.message}", e)
|
|
142
|
+
promise.reject("GET_TIME_ERROR", "Error in getCurrentTime method: ${e.message}", e)
|
|
78
143
|
}
|
|
79
144
|
}
|
|
80
|
-
|
|
145
|
+
|
|
81
146
|
@ReactMethod
|
|
82
|
-
fun getDuration(
|
|
147
|
+
fun getDuration(viewTag: Int, promise: Promise) {
|
|
148
|
+
Log.d(TAG, "Native getDuration method called with viewTag: $viewTag")
|
|
83
149
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
150
|
+
val playerView = getPlayerViewByTag(viewTag)
|
|
151
|
+
if (playerView != null) {
|
|
152
|
+
UiThreadUtil.runOnUiThread {
|
|
153
|
+
try {
|
|
154
|
+
val duration = playerView.getDuration()
|
|
155
|
+
Log.d(TAG, "getDuration executed successfully, duration: $duration")
|
|
156
|
+
promise.resolve(duration)
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
Log.e(TAG, "Error getting duration: ${e.message}", e)
|
|
159
|
+
promise.reject("GET_DURATION_ERROR", "Error getting duration: ${e.message}", e)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
Log.e(TAG, "Player view not found for tag: $viewTag")
|
|
164
|
+
promise.reject("VIEW_NOT_FOUND", "Player view not found for tag: $viewTag")
|
|
88
165
|
}
|
|
89
166
|
} catch (e: Exception) {
|
|
90
|
-
Log.e(TAG, "Error
|
|
91
|
-
promise.reject("
|
|
167
|
+
Log.e(TAG, "Error in getDuration method: ${e.message}", e)
|
|
168
|
+
promise.reject("GET_DURATION_ERROR", "Error in getDuration method: ${e.message}", e)
|
|
92
169
|
}
|
|
93
170
|
}
|
|
94
171
|
|
|
@@ -4,17 +4,20 @@ import com.facebook.react.ReactPackage
|
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
|
-
|
|
7
|
+
import android.util.Log
|
|
8
8
|
|
|
9
9
|
class UnifiedPlayerPackage : ReactPackage {
|
|
10
|
+
private val TAG = "UnifiedPlayerPackage"
|
|
11
|
+
|
|
10
12
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
13
|
+
Log.d(TAG, "Creating native modules")
|
|
11
14
|
return listOf(
|
|
12
|
-
UnifiedPlayerModule(reactContext)
|
|
13
|
-
UnifiedPlayerEventEmitter(reactContext)
|
|
15
|
+
UnifiedPlayerModule(reactContext)
|
|
14
16
|
)
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
20
|
+
Log.d(TAG, "Creating view managers")
|
|
18
21
|
return listOf(UnifiedPlayerViewManager())
|
|
19
22
|
}
|
|
20
23
|
}
|
|
@@ -13,11 +13,12 @@ import com.google.android.exoplayer2.ExoPlayer
|
|
|
13
13
|
import com.google.android.exoplayer2.MediaItem
|
|
14
14
|
import com.google.android.exoplayer2.Player
|
|
15
15
|
import com.google.android.exoplayer2.PlaybackException
|
|
16
|
-
import com.google.android.exoplayer2.Timeline
|
|
17
16
|
import com.google.android.exoplayer2.Tracks
|
|
18
17
|
import com.google.android.exoplayer2.ui.PlayerView
|
|
19
18
|
import com.google.android.exoplayer2.video.VideoSize
|
|
20
19
|
import com.facebook.react.bridge.WritableMap
|
|
20
|
+
import com.facebook.react.bridge.ReactContext
|
|
21
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
21
22
|
|
|
22
23
|
class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
23
24
|
companion object {
|
|
@@ -25,12 +26,12 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
private var videoUrl: String? = null
|
|
28
|
-
private var authToken: String? = null
|
|
29
29
|
private var autoplay: Boolean = true
|
|
30
30
|
private var loop: Boolean = false
|
|
31
31
|
private var playerView: PlayerView
|
|
32
32
|
private var player: ExoPlayer? = null
|
|
33
33
|
private var currentProgress = 0
|
|
34
|
+
private var isPaused = false
|
|
34
35
|
|
|
35
36
|
private val progressHandler = Handler(Looper.getMainLooper())
|
|
36
37
|
private val progressRunnable: Runnable = object : Runnable {
|
|
@@ -39,14 +40,22 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
39
40
|
val currentTime = it.currentPosition.toFloat() / 1000f
|
|
40
41
|
val duration = it.duration.toFloat() / 1000f
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
// Log the actual values for debugging
|
|
44
|
+
Log.d(TAG, "Progress values - currentTime: $currentTime, duration: $duration, raw duration: ${it.duration}")
|
|
45
|
+
|
|
46
|
+
// Only send valid duration values
|
|
47
|
+
if (it.duration > 0) {
|
|
48
|
+
val event = Arguments.createMap()
|
|
49
|
+
event.putDouble("currentTime", currentTime.toDouble())
|
|
50
|
+
event.putDouble("duration", duration.toDouble())
|
|
51
|
+
|
|
52
|
+
Log.d(TAG, "Sending progress event: currentTime=$currentTime, duration=$duration")
|
|
53
|
+
sendEvent("topProgress", event)
|
|
54
|
+
} else {
|
|
55
|
+
Log.d(TAG, "Not sending progress event because duration is $duration (raw: ${it.duration})")
|
|
48
56
|
}
|
|
49
|
-
}
|
|
57
|
+
} ?: Log.e(TAG, "Cannot send progress event: player is null")
|
|
58
|
+
|
|
50
59
|
// Schedule the next update
|
|
51
60
|
progressHandler.postDelayed(this, 250) // Update every 250ms
|
|
52
61
|
}
|
|
@@ -80,11 +89,11 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
80
89
|
when (playbackState) {
|
|
81
90
|
Player.STATE_READY -> {
|
|
82
91
|
Log.d(TAG, "ExoPlayer STATE_READY")
|
|
83
|
-
sendEvent("
|
|
92
|
+
sendEvent("topReadyToPlay", Arguments.createMap())
|
|
84
93
|
}
|
|
85
94
|
Player.STATE_ENDED -> {
|
|
86
95
|
Log.d(TAG, "ExoPlayer STATE_ENDED")
|
|
87
|
-
sendEvent("
|
|
96
|
+
sendEvent("topPlaybackComplete", Arguments.createMap())
|
|
88
97
|
}
|
|
89
98
|
Player.STATE_BUFFERING -> {
|
|
90
99
|
Log.d(TAG, "ExoPlayer STATE_BUFFERING")
|
|
@@ -92,34 +101,33 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
92
101
|
Player.STATE_IDLE -> {
|
|
93
102
|
Log.d(TAG, "ExoPlayer STATE_IDLE")
|
|
94
103
|
}
|
|
95
|
-
Player.STATE_BUFFERING -> {
|
|
96
|
-
Log.d(TAG, "ExoPlayer STATE_BUFFERING")
|
|
97
|
-
}
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
|
108
|
+
Log.d(TAG, "onIsPlayingChanged: $isPlaying")
|
|
102
109
|
if (isPlaying) {
|
|
103
110
|
Log.d(TAG, "ExoPlayer isPlaying")
|
|
104
|
-
|
|
111
|
+
// Use the defined event constant for playback resumed
|
|
112
|
+
sendEvent("topPlaybackResumed", Arguments.createMap())
|
|
105
113
|
} else {
|
|
106
114
|
Log.d(TAG, "ExoPlayer isPaused")
|
|
107
|
-
|
|
115
|
+
// Add event emission for pause state
|
|
116
|
+
sendEvent("topPlaybackPaused", Arguments.createMap())
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
|
|
111
120
|
override fun onPlayerError(error: PlaybackException) {
|
|
112
|
-
Log.
|
|
121
|
+
Log.d(TAG, "ExoPlayer onPlayerError: ${error.message}")
|
|
113
122
|
val event = Arguments.createMap().apply {
|
|
114
|
-
|
|
115
|
-
putString("message", error.message)
|
|
123
|
+
putString("error", error.message)
|
|
116
124
|
}
|
|
117
|
-
sendEvent("
|
|
125
|
+
sendEvent("topError", event)
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
121
129
|
Log.d(TAG, "ExoPlayer onMediaItemTransition")
|
|
122
|
-
sendEvent("
|
|
130
|
+
sendEvent("topLoadStart", Arguments.createMap())
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) {
|
|
@@ -177,39 +185,67 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
177
185
|
// Called when skip silence is enabled or disabled.
|
|
178
186
|
Log.d(TAG, "ExoPlayer onSkipSilenceEnabledChanged: skipSilenceEnabled=$skipSilenceEnabled")
|
|
179
187
|
}
|
|
180
|
-
|
|
181
|
-
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
|
182
|
-
// Called when the timeline changes, like when a new media source is loaded or an ad break starts or ends.
|
|
183
|
-
Log.d(TAG, "ExoPlayer onTimelineChanged: timeline=$timeline, reason=$reason")
|
|
184
|
-
}
|
|
185
188
|
})
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
fun setVideoUrl(url: String?) {
|
|
189
192
|
Log.d(TAG, "Setting video URL: $url")
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
|
|
194
|
+
if (url == null || url.isEmpty()) {
|
|
195
|
+
Log.e(TAG, "Empty or null URL provided")
|
|
192
196
|
return
|
|
193
197
|
}
|
|
194
|
-
|
|
198
|
+
|
|
195
199
|
videoUrl = url
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
// Create a MediaItem
|
|
203
|
+
val mediaItem = MediaItem.fromUri(url)
|
|
204
|
+
|
|
205
|
+
// Reset the player to ensure clean state
|
|
206
|
+
player?.stop()
|
|
207
|
+
player?.clearMediaItems()
|
|
208
|
+
|
|
209
|
+
// Set the media item
|
|
210
|
+
player?.setMediaItem(mediaItem)
|
|
211
|
+
|
|
212
|
+
// Prepare the player (this will start loading the media)
|
|
213
|
+
player?.prepare()
|
|
214
|
+
|
|
215
|
+
// Set playWhenReady based on autoplay setting
|
|
216
|
+
player?.playWhenReady = autoplay && !isPaused
|
|
217
|
+
|
|
218
|
+
// Set repeat mode based on loop setting
|
|
219
|
+
player?.repeatMode = if (loop) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
|
|
220
|
+
|
|
221
|
+
// Log that we've set up the player
|
|
222
|
+
Log.d(TAG, "ExoPlayer configured with URL: $url, autoplay: $autoplay, loop: $loop")
|
|
223
|
+
|
|
224
|
+
// Add a listener to check when the player is ready and has duration
|
|
225
|
+
player?.addListener(object : Player.Listener {
|
|
226
|
+
override fun onPlaybackStateChanged(state: Int) {
|
|
227
|
+
if (state == Player.STATE_READY) {
|
|
228
|
+
val duration = player?.duration ?: 0
|
|
229
|
+
Log.d(TAG, "Player ready with duration: ${duration / 1000f} seconds")
|
|
230
|
+
|
|
231
|
+
// Force a progress update immediately
|
|
232
|
+
progressRunnable.run()
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
} catch (e: Exception) {
|
|
237
|
+
Log.e(TAG, "Error setting video URL: ${e.message}", e)
|
|
238
|
+
|
|
239
|
+
// Send error event
|
|
240
|
+
val event = Arguments.createMap()
|
|
241
|
+
event.putString("error", "Failed to load video: ${e.message}")
|
|
242
|
+
sendEvent("topError", event)
|
|
243
|
+
}
|
|
205
244
|
}
|
|
206
245
|
|
|
207
246
|
fun setAuthToken(token: String?) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (!videoUrl.isNullOrEmpty()) {
|
|
211
|
-
setVideoUrl(videoUrl) // reload video
|
|
212
|
-
}
|
|
247
|
+
// Removed as per request
|
|
248
|
+
Log.d(TAG, "Auth token handling removed")
|
|
213
249
|
}
|
|
214
250
|
|
|
215
251
|
fun setAutoplay(value: Boolean) {
|
|
@@ -224,6 +260,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
224
260
|
|
|
225
261
|
fun setIsPaused(isPaused: Boolean) {
|
|
226
262
|
Log.d(TAG, "setIsPaused called with value: $isPaused")
|
|
263
|
+
this.isPaused = isPaused
|
|
227
264
|
if (isPaused) {
|
|
228
265
|
player?.pause()
|
|
229
266
|
} else {
|
|
@@ -232,30 +269,76 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
232
269
|
}
|
|
233
270
|
|
|
234
271
|
fun play() {
|
|
235
|
-
Log.d(TAG, "Play called")
|
|
236
|
-
player?.
|
|
272
|
+
Log.d(TAG, "Play method called")
|
|
273
|
+
player?.playWhenReady = true
|
|
237
274
|
}
|
|
238
275
|
|
|
239
276
|
fun pause() {
|
|
240
|
-
Log.d(TAG, "Pause called
|
|
241
|
-
player?.
|
|
277
|
+
Log.d(TAG, "Pause method called")
|
|
278
|
+
player?.playWhenReady = false
|
|
242
279
|
}
|
|
243
280
|
|
|
244
|
-
fun seekTo(
|
|
245
|
-
Log.d(TAG, "
|
|
246
|
-
player?.
|
|
281
|
+
fun seekTo(seconds: Float) {
|
|
282
|
+
Log.d(TAG, "SeekTo method called with seconds: $seconds")
|
|
283
|
+
player?.let {
|
|
284
|
+
val milliseconds = (seconds * 1000).toLong()
|
|
285
|
+
Log.d(TAG, "Seeking to $milliseconds ms")
|
|
286
|
+
it.seekTo(milliseconds)
|
|
287
|
+
|
|
288
|
+
// Force a progress update after seeking
|
|
289
|
+
progressRunnable.run()
|
|
290
|
+
} ?: Log.e(TAG, "Cannot seek: player is null")
|
|
247
291
|
}
|
|
248
292
|
|
|
249
293
|
fun getCurrentTime(): Float {
|
|
250
|
-
|
|
294
|
+
Log.d(TAG, "GetCurrentTime method called")
|
|
295
|
+
return player?.let {
|
|
296
|
+
val currentTime = it.currentPosition.toFloat() / 1000f
|
|
297
|
+
Log.d(TAG, "Current time: $currentTime seconds")
|
|
298
|
+
currentTime
|
|
299
|
+
} ?: run {
|
|
300
|
+
Log.e(TAG, "Cannot get current time: player is null")
|
|
301
|
+
0f
|
|
302
|
+
}
|
|
251
303
|
}
|
|
252
304
|
|
|
253
305
|
fun getDuration(): Float {
|
|
254
|
-
|
|
306
|
+
Log.d(TAG, "GetDuration method called")
|
|
307
|
+
return player?.let {
|
|
308
|
+
val duration = it.duration.toFloat() / 1000f
|
|
309
|
+
Log.d(TAG, "Duration: $duration seconds (raw: ${it.duration})")
|
|
310
|
+
if (it.duration > 0) duration else 0f
|
|
311
|
+
} ?: run {
|
|
312
|
+
Log.e(TAG, "Cannot get duration: player is null")
|
|
313
|
+
0f
|
|
314
|
+
}
|
|
255
315
|
}
|
|
256
316
|
|
|
317
|
+
// Add a getter for the ExoPlayer instance
|
|
318
|
+
val exoPlayer: ExoPlayer?
|
|
319
|
+
get() = this.player
|
|
320
|
+
|
|
257
321
|
private fun sendEvent(eventName: String, params: WritableMap) {
|
|
258
|
-
|
|
322
|
+
try {
|
|
323
|
+
// Log the event for debugging
|
|
324
|
+
Log.d(TAG, "Sending direct event: $eventName with params: $params")
|
|
325
|
+
|
|
326
|
+
// Use the ReactContext to dispatch the event directly to the view
|
|
327
|
+
val reactContext = context as ReactContext
|
|
328
|
+
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
329
|
+
.receiveEvent(id, eventName, params)
|
|
330
|
+
} catch (e: Exception) {
|
|
331
|
+
Log.e(TAG, "Error sending event $eventName: ${e.message}", e)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Add a method to explicitly start progress updates
|
|
336
|
+
private fun startProgressUpdates() {
|
|
337
|
+
Log.d(TAG, "Starting progress updates")
|
|
338
|
+
// Remove any existing callbacks to avoid duplicates
|
|
339
|
+
progressHandler.removeCallbacks(progressRunnable)
|
|
340
|
+
// Post the runnable to start updates
|
|
341
|
+
progressHandler.post(progressRunnable)
|
|
259
342
|
}
|
|
260
343
|
|
|
261
344
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
@@ -271,7 +354,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
271
354
|
super.onAttachedToWindow()
|
|
272
355
|
Log.d(TAG, "UnifiedPlayerView onAttachedToWindow")
|
|
273
356
|
playerView.setPlayer(player)
|
|
274
|
-
|
|
357
|
+
startProgressUpdates() // Use the new method to start progress updates
|
|
275
358
|
}
|
|
276
359
|
|
|
277
360
|
override fun onDetachedFromWindow() {
|
|
@@ -3,11 +3,18 @@ package com.unifiedplayer
|
|
|
3
3
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
4
4
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
5
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
6
|
+
import com.facebook.react.common.MapBuilder
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import com.facebook.react.bridge.ReadableArray
|
|
9
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
6
10
|
|
|
7
11
|
class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
|
|
12
|
+
private val TAG = "UnifiedPlayerViewManager"
|
|
13
|
+
|
|
8
14
|
override fun getName() = "UnifiedPlayerView"
|
|
9
15
|
|
|
10
16
|
override fun createViewInstance(reactContext: ThemedReactContext): UnifiedPlayerView {
|
|
17
|
+
Log.d(TAG, "Creating UnifiedPlayerView instance")
|
|
11
18
|
return UnifiedPlayerView(reactContext)
|
|
12
19
|
}
|
|
13
20
|
|
|
@@ -16,11 +23,6 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
|
|
|
16
23
|
view.setVideoUrl(url)
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
@ReactProp(name = "authToken")
|
|
20
|
-
fun setAuthToken(view: UnifiedPlayerView, token: String?) {
|
|
21
|
-
view.setAuthToken(token)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
26
|
@ReactProp(name = "autoplay")
|
|
25
27
|
fun setAutoplay(view: UnifiedPlayerView, autoplay: Boolean) {
|
|
26
28
|
view.setAutoplay(autoplay)
|
|
@@ -35,4 +37,20 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
|
|
|
35
37
|
fun setIsPaused(view: UnifiedPlayerView, isPaused: Boolean) {
|
|
36
38
|
view.setIsPaused(isPaused)
|
|
37
39
|
}
|
|
40
|
+
|
|
41
|
+
// Register direct events
|
|
42
|
+
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
43
|
+
Log.d(TAG, "Registering direct events")
|
|
44
|
+
|
|
45
|
+
// Create a map of event names to their registration names
|
|
46
|
+
return MapBuilder.builder<String, Any>()
|
|
47
|
+
.put("topReadyToPlay", MapBuilder.of("registrationName", "onReadyToPlay"))
|
|
48
|
+
.put("topError", MapBuilder.of("registrationName", "onError"))
|
|
49
|
+
.put("topProgress", MapBuilder.of("registrationName", "onProgress"))
|
|
50
|
+
.put("topPlaybackComplete", MapBuilder.of("registrationName", "onPlaybackComplete"))
|
|
51
|
+
.put("topPlaybackResumed", MapBuilder.of("registrationName", "onPlaybackResumed"))
|
|
52
|
+
.put("topPlaybackPaused", MapBuilder.of("registrationName", "onPlaybackPaused"))
|
|
53
|
+
.put("topLoadStart", MapBuilder.of("registrationName", "onLoadStart"))
|
|
54
|
+
.build()
|
|
55
|
+
}
|
|
38
56
|
}
|
package/lib/module/index.js
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from 'react'; // Import from 'react'
|
|
4
|
-
import { requireNativeComponent, UIManager,
|
|
4
|
+
import { requireNativeComponent, UIManager, NativeModules, Platform } from 'react-native';
|
|
5
|
+
|
|
6
|
+
// Check if the native module is available
|
|
5
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
8
|
const LINKING_ERROR = `The package 'react-native-unified-player' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
7
9
|
ios: "- You have run 'pod install'\n",
|
|
8
10
|
default: ''
|
|
9
11
|
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// Verify the native module exists
|
|
14
|
+
if (!UIManager.getViewManagerConfig('UnifiedPlayerView') && !NativeModules.UnifiedPlayer) {
|
|
15
|
+
throw new Error(LINKING_ERROR);
|
|
16
|
+
}
|
|
14
17
|
|
|
15
|
-
//
|
|
16
|
-
const ComponentName = 'UnifiedPlayerView';
|
|
18
|
+
// Define the props for the UnifiedPlayerView component
|
|
17
19
|
|
|
18
|
-
//
|
|
20
|
+
// Native component registration
|
|
21
|
+
const NativeUnifiedPlayerView = requireNativeComponent('UnifiedPlayerView');
|
|
19
22
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
// Newline added here
|
|
24
|
+
|
|
25
|
+
// Native module for player control methods
|
|
26
|
+
const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
|
|
24
27
|
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
// Export event types for reference
|
|
29
|
+
export const UnifiedPlayerEventTypes = {
|
|
30
|
+
READY: 'onReadyToPlay',
|
|
31
|
+
ERROR: 'onError',
|
|
32
|
+
PROGRESS: 'onProgress',
|
|
33
|
+
COMPLETE: 'onPlaybackComplete',
|
|
34
|
+
STALLED: 'onPlaybackStalled',
|
|
35
|
+
RESUMED: 'onPlaybackResumed'
|
|
36
|
+
};
|
|
29
37
|
|
|
30
38
|
/**
|
|
31
39
|
* UnifiedPlayerView component for video playback
|
|
@@ -41,69 +49,73 @@ export const UnifiedPlayerView = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
41
49
|
* API methods for controlling playback
|
|
42
50
|
*/
|
|
43
51
|
export const UnifiedPlayer = {
|
|
44
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Start playback
|
|
54
|
+
* @param viewTag - The tag of the player view
|
|
55
|
+
*/
|
|
45
56
|
play: viewTag => {
|
|
46
|
-
|
|
57
|
+
try {
|
|
58
|
+
console.log('UnifiedPlayer.play called with viewTag:', viewTag);
|
|
47
59
|
UnifiedPlayerModule.play(viewTag);
|
|
60
|
+
console.log('Native play method called successfully');
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.log('Error calling play:', error instanceof Error ? error.message : String(error));
|
|
48
63
|
}
|
|
49
64
|
},
|
|
50
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Pause playback
|
|
67
|
+
* @param viewTag - The tag of the player view
|
|
68
|
+
*/
|
|
51
69
|
pause: viewTag => {
|
|
52
|
-
|
|
70
|
+
try {
|
|
71
|
+
console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
|
|
53
72
|
UnifiedPlayerModule.pause(viewTag);
|
|
73
|
+
console.log('Native pause method called successfully');
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.log('Error calling pause:', error instanceof Error ? error.message : String(error));
|
|
54
76
|
}
|
|
55
77
|
},
|
|
56
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Seek to a specific time
|
|
80
|
+
* @param viewTag - The tag of the player view
|
|
81
|
+
* @param time - Time in seconds to seek to
|
|
82
|
+
*/
|
|
57
83
|
seekTo: (viewTag, time) => {
|
|
58
|
-
|
|
84
|
+
try {
|
|
85
|
+
console.log('UnifiedPlayer.seekTo called with viewTag:', viewTag, 'time:', time);
|
|
59
86
|
UnifiedPlayerModule.seekTo(viewTag, time);
|
|
87
|
+
console.log('Native seekTo method called successfully');
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log('Error calling seekTo:', error instanceof Error ? error.message : String(error));
|
|
60
90
|
}
|
|
61
91
|
},
|
|
62
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Get current playback time
|
|
94
|
+
* @param viewTag - The tag of the player view
|
|
95
|
+
* @returns Promise resolving to current time in seconds
|
|
96
|
+
*/
|
|
63
97
|
getCurrentTime: viewTag => {
|
|
64
|
-
|
|
98
|
+
try {
|
|
99
|
+
console.log('UnifiedPlayer.getCurrentTime called with viewTag:', viewTag);
|
|
65
100
|
return UnifiedPlayerModule.getCurrentTime(viewTag);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log('Error calling getCurrentTime:', error instanceof Error ? error.message : String(error));
|
|
103
|
+
return Promise.reject(error);
|
|
66
104
|
}
|
|
67
|
-
return Promise.resolve(0);
|
|
68
105
|
},
|
|
69
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Get video duration
|
|
108
|
+
* @param viewTag - The tag of the player view
|
|
109
|
+
* @returns Promise resolving to duration in seconds
|
|
110
|
+
*/
|
|
70
111
|
getDuration: viewTag => {
|
|
71
|
-
|
|
112
|
+
try {
|
|
113
|
+
console.log('UnifiedPlayer.getDuration called with viewTag:', viewTag);
|
|
72
114
|
return UnifiedPlayerModule.getDuration(viewTag);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log('Error calling getDuration:', error instanceof Error ? error.message : String(error));
|
|
117
|
+
return Promise.reject(error);
|
|
73
118
|
}
|
|
74
|
-
return Promise.resolve(0);
|
|
75
|
-
},
|
|
76
|
-
// Test method
|
|
77
|
-
testMethod: message => {
|
|
78
|
-
if (UnifiedPlayerModule?.testMethod) {
|
|
79
|
-
UnifiedPlayerModule.testMethod(message);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Events emitter for native events
|
|
85
|
-
let eventEmitter = null;
|
|
86
|
-
if (UnifiedPlayerEventEmitterModule) {
|
|
87
|
-
eventEmitter = new NativeEventEmitter(UnifiedPlayerEventEmitterModule);
|
|
88
|
-
}
|
|
89
|
-
export const UnifiedPlayerEvents = {
|
|
90
|
-
addListener: (eventType, listener) => {
|
|
91
|
-
if (eventEmitter) {
|
|
92
|
-
return eventEmitter.addListener(eventType, listener);
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
remove: () => {}
|
|
96
|
-
};
|
|
97
119
|
}
|
|
98
120
|
};
|
|
99
|
-
|
|
100
|
-
// Event names
|
|
101
|
-
export const UnifiedPlayerEventTypes = {
|
|
102
|
-
READY: 'onReadyToPlay',
|
|
103
|
-
ERROR: 'onError',
|
|
104
|
-
PROGRESS: 'onProgress',
|
|
105
|
-
COMPLETE: 'onPlaybackComplete',
|
|
106
|
-
STALLED: 'onPlaybackStalled',
|
|
107
|
-
RESUMED: 'onPlaybackResumed'
|
|
108
|
-
};
|
|
109
121
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","requireNativeComponent","UIManager","
|
|
1
|
+
{"version":3,"names":["forwardRef","requireNativeComponent","UIManager","NativeModules","Platform","jsx","_jsx","LINKING_ERROR","select","ios","default","getViewManagerConfig","UnifiedPlayer","Error","NativeUnifiedPlayerView","UnifiedPlayerModule","UnifiedPlayerEventTypes","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED","UnifiedPlayerView","props","ref","play","viewTag","console","log","error","message","String","pause","seekTo","time","getCurrentTime","Promise","reject","getDuration"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { type ViewStyle } from 'react-native';
|
|
2
|
-
/**
|
|
3
|
-
* Props for the UnifiedPlayerView component
|
|
4
|
-
*/
|
|
5
2
|
export type UnifiedPlayerProps = {
|
|
6
3
|
videoUrl: string;
|
|
7
4
|
style: ViewStyle;
|
|
8
5
|
autoplay?: boolean;
|
|
9
6
|
loop?: boolean;
|
|
10
7
|
isPaused?: boolean;
|
|
11
|
-
authToken?: string;
|
|
12
8
|
onReadyToPlay?: () => void;
|
|
13
9
|
onError?: (error: any) => void;
|
|
14
10
|
onPlaybackComplete?: () => void;
|
|
@@ -17,32 +13,49 @@ export type UnifiedPlayerProps = {
|
|
|
17
13
|
duration: number;
|
|
18
14
|
}) => void;
|
|
19
15
|
};
|
|
16
|
+
export declare const UnifiedPlayerEventTypes: {
|
|
17
|
+
READY: string;
|
|
18
|
+
ERROR: string;
|
|
19
|
+
PROGRESS: string;
|
|
20
|
+
COMPLETE: string;
|
|
21
|
+
STALLED: string;
|
|
22
|
+
RESUMED: string;
|
|
23
|
+
};
|
|
20
24
|
/**
|
|
21
25
|
* UnifiedPlayerView component for video playback
|
|
22
26
|
*/
|
|
23
|
-
export declare const UnifiedPlayerView: import("react").ForwardRefExoticComponent<UnifiedPlayerProps & import("react").RefAttributes<
|
|
27
|
+
export declare const UnifiedPlayerView: import("react").ForwardRefExoticComponent<UnifiedPlayerProps & import("react").RefAttributes<import("react").Component<UnifiedPlayerProps, {}, any> & import("react-native").NativeMethods>>;
|
|
24
28
|
/**
|
|
25
29
|
* API methods for controlling playback
|
|
26
30
|
*/
|
|
27
31
|
export declare const UnifiedPlayer: {
|
|
32
|
+
/**
|
|
33
|
+
* Start playback
|
|
34
|
+
* @param viewTag - The tag of the player view
|
|
35
|
+
*/
|
|
28
36
|
play: (viewTag: number) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Pause playback
|
|
39
|
+
* @param viewTag - The tag of the player view
|
|
40
|
+
*/
|
|
29
41
|
pause: (viewTag: number) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Seek to a specific time
|
|
44
|
+
* @param viewTag - The tag of the player view
|
|
45
|
+
* @param time - Time in seconds to seek to
|
|
46
|
+
*/
|
|
30
47
|
seekTo: (viewTag: number, time: number) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Get current playback time
|
|
50
|
+
* @param viewTag - The tag of the player view
|
|
51
|
+
* @returns Promise resolving to current time in seconds
|
|
52
|
+
*/
|
|
31
53
|
getCurrentTime: (viewTag: number) => Promise<number>;
|
|
54
|
+
/**
|
|
55
|
+
* Get video duration
|
|
56
|
+
* @param viewTag - The tag of the player view
|
|
57
|
+
* @returns Promise resolving to duration in seconds
|
|
58
|
+
*/
|
|
32
59
|
getDuration: (viewTag: number) => Promise<number>;
|
|
33
|
-
testMethod: (message: string) => void;
|
|
34
|
-
};
|
|
35
|
-
export declare const UnifiedPlayerEvents: {
|
|
36
|
-
addListener: (eventType: string, listener: (...args: any[]) => any) => import("react-native").EmitterSubscription | {
|
|
37
|
-
remove: () => void;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
export declare const UnifiedPlayerEventTypes: {
|
|
41
|
-
READY: string;
|
|
42
|
-
ERROR: string;
|
|
43
|
-
PROGRESS: string;
|
|
44
|
-
COMPLETE: string;
|
|
45
|
-
STALLED: string;
|
|
46
|
-
RESUMED: string;
|
|
47
60
|
};
|
|
48
61
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAkBtB,MAAM,MAAM,kBAAkB,GAAG;IAE/B,QAAQ,EAAE,MAAM,CAAC;IAGjB,KAAK,EAAE,SAAS,CAAC;IAGjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,IAAI,CAAC,EAAE,OAAO,CAAC;IAGf,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAG3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAG/B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAGhC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE,CAAC;AAYF,eAAO,MAAM,uBAAuB;;;;;;;CAOnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,8LAK5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;IACxB;;;OAGG;oBACa,MAAM,KAAG,IAAI;IAa7B;;;OAGG;qBACc,MAAM,KAAG,IAAI;IAa9B;;;;OAIG;sBACe,MAAM,QAAQ,MAAM,KAAG,IAAI;IAkB7C;;;;OAIG;8BACuB,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;IAalD;;;;OAIG;2BACoB,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;CAYhD,CAAC"}
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ElementRef, forwardRef } from 'react'; // Import from 'react'
|
|
2
2
|
import {
|
|
3
3
|
requireNativeComponent,
|
|
4
4
|
UIManager,
|
|
5
|
+
NativeModules,
|
|
5
6
|
Platform,
|
|
6
7
|
type ViewStyle,
|
|
7
|
-
|
|
8
|
-
NativeEventEmitter,
|
|
9
|
-
} from 'react-native'; // Import from 'react-native'
|
|
8
|
+
} from 'react-native';
|
|
10
9
|
|
|
10
|
+
// Check if the native module is available
|
|
11
11
|
const LINKING_ERROR =
|
|
12
12
|
`The package 'react-native-unified-player' doesn't seem to be linked. Make sure: \n\n` +
|
|
13
13
|
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
14
14
|
'- You rebuilt the app after installing the package\n' +
|
|
15
15
|
'- You are not using Expo Go\n';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
// Verify the native module exists
|
|
18
|
+
if (
|
|
19
|
+
!UIManager.getViewManagerConfig('UnifiedPlayerView') &&
|
|
20
|
+
!NativeModules.UnifiedPlayer
|
|
21
|
+
) {
|
|
22
|
+
throw new Error(LINKING_ERROR);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Define the props for the UnifiedPlayerView component
|
|
20
26
|
export type UnifiedPlayerProps = {
|
|
21
27
|
// Video source URL
|
|
22
28
|
videoUrl: string;
|
|
@@ -33,9 +39,6 @@ export type UnifiedPlayerProps = {
|
|
|
33
39
|
// Is the player currently paused
|
|
34
40
|
isPaused?: boolean;
|
|
35
41
|
|
|
36
|
-
// Optional auth token for protected streams
|
|
37
|
-
authToken?: string;
|
|
38
|
-
|
|
39
42
|
// Callback when video is ready to play
|
|
40
43
|
onReadyToPlay?: () => void;
|
|
41
44
|
|
|
@@ -49,26 +52,24 @@ export type UnifiedPlayerProps = {
|
|
|
49
52
|
onProgress?: (data: { currentTime: number; duration: number }) => void;
|
|
50
53
|
};
|
|
51
54
|
|
|
52
|
-
//
|
|
53
|
-
const
|
|
55
|
+
// Native component registration
|
|
56
|
+
const NativeUnifiedPlayerView =
|
|
57
|
+
requireNativeComponent<UnifiedPlayerProps>('UnifiedPlayerView');
|
|
54
58
|
|
|
55
|
-
//
|
|
56
|
-
type NativeUnifiedPlayerViewProps = UnifiedPlayerProps & {
|
|
57
|
-
ref?: Ref<ElementRef<typeof NativeUnifiedPlayerView>>;
|
|
58
|
-
};
|
|
59
|
+
// Newline added here
|
|
59
60
|
|
|
60
|
-
// Native
|
|
61
|
-
const
|
|
62
|
-
UIManager.getViewManagerConfig(ComponentName) != null
|
|
63
|
-
? requireNativeComponent<NativeUnifiedPlayerViewProps>(ComponentName)
|
|
64
|
-
: () => {
|
|
65
|
-
throw new Error(LINKING_ERROR);
|
|
66
|
-
};
|
|
61
|
+
// Native module for player control methods
|
|
62
|
+
const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
|
|
67
63
|
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
// Export event types for reference
|
|
65
|
+
export const UnifiedPlayerEventTypes = {
|
|
66
|
+
READY: 'onReadyToPlay',
|
|
67
|
+
ERROR: 'onError',
|
|
68
|
+
PROGRESS: 'onProgress',
|
|
69
|
+
COMPLETE: 'onPlaybackComplete',
|
|
70
|
+
STALLED: 'onPlaybackStalled',
|
|
71
|
+
RESUMED: 'onPlaybackResumed',
|
|
72
|
+
};
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
75
|
* UnifiedPlayerView component for video playback
|
|
@@ -84,73 +85,96 @@ export const UnifiedPlayerView = forwardRef<
|
|
|
84
85
|
* API methods for controlling playback
|
|
85
86
|
*/
|
|
86
87
|
export const UnifiedPlayer = {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Start playback
|
|
90
|
+
* @param viewTag - The tag of the player view
|
|
91
|
+
*/
|
|
92
|
+
play: (viewTag: number): void => {
|
|
93
|
+
try {
|
|
94
|
+
console.log('UnifiedPlayer.play called with viewTag:', viewTag);
|
|
90
95
|
UnifiedPlayerModule.play(viewTag);
|
|
96
|
+
console.log('Native play method called successfully');
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.log(
|
|
99
|
+
'Error calling play:',
|
|
100
|
+
error instanceof Error ? error.message : String(error)
|
|
101
|
+
);
|
|
91
102
|
}
|
|
92
103
|
},
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Pause playback
|
|
107
|
+
* @param viewTag - The tag of the player view
|
|
108
|
+
*/
|
|
109
|
+
pause: (viewTag: number): void => {
|
|
110
|
+
try {
|
|
111
|
+
console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
|
|
97
112
|
UnifiedPlayerModule.pause(viewTag);
|
|
113
|
+
console.log('Native pause method called successfully');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log(
|
|
116
|
+
'Error calling pause:',
|
|
117
|
+
error instanceof Error ? error.message : String(error)
|
|
118
|
+
);
|
|
98
119
|
}
|
|
99
120
|
},
|
|
100
121
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Seek to a specific time
|
|
124
|
+
* @param viewTag - The tag of the player view
|
|
125
|
+
* @param time - Time in seconds to seek to
|
|
126
|
+
*/
|
|
127
|
+
seekTo: (viewTag: number, time: number): void => {
|
|
128
|
+
try {
|
|
129
|
+
console.log(
|
|
130
|
+
'UnifiedPlayer.seekTo called with viewTag:',
|
|
131
|
+
viewTag,
|
|
132
|
+
'time:',
|
|
133
|
+
time
|
|
134
|
+
);
|
|
104
135
|
UnifiedPlayerModule.seekTo(viewTag, time);
|
|
136
|
+
console.log('Native seekTo method called successfully');
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.log(
|
|
139
|
+
'Error calling seekTo:',
|
|
140
|
+
error instanceof Error ? error.message : String(error)
|
|
141
|
+
);
|
|
105
142
|
}
|
|
106
143
|
},
|
|
107
144
|
|
|
108
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Get current playback time
|
|
147
|
+
* @param viewTag - The tag of the player view
|
|
148
|
+
* @returns Promise resolving to current time in seconds
|
|
149
|
+
*/
|
|
109
150
|
getCurrentTime: (viewTag: number): Promise<number> => {
|
|
110
|
-
|
|
151
|
+
try {
|
|
152
|
+
console.log('UnifiedPlayer.getCurrentTime called with viewTag:', viewTag);
|
|
111
153
|
return UnifiedPlayerModule.getCurrentTime(viewTag);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.log(
|
|
156
|
+
'Error calling getCurrentTime:',
|
|
157
|
+
error instanceof Error ? error.message : String(error)
|
|
158
|
+
);
|
|
159
|
+
return Promise.reject(error);
|
|
112
160
|
}
|
|
113
|
-
return Promise.resolve(0);
|
|
114
161
|
},
|
|
115
162
|
|
|
116
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Get video duration
|
|
165
|
+
* @param viewTag - The tag of the player view
|
|
166
|
+
* @returns Promise resolving to duration in seconds
|
|
167
|
+
*/
|
|
117
168
|
getDuration: (viewTag: number): Promise<number> => {
|
|
118
|
-
|
|
169
|
+
try {
|
|
170
|
+
console.log('UnifiedPlayer.getDuration called with viewTag:', viewTag);
|
|
119
171
|
return UnifiedPlayerModule.getDuration(viewTag);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.log(
|
|
174
|
+
'Error calling getDuration:',
|
|
175
|
+
error instanceof Error ? error.message : String(error)
|
|
176
|
+
);
|
|
177
|
+
return Promise.reject(error);
|
|
120
178
|
}
|
|
121
|
-
return Promise.resolve(0);
|
|
122
179
|
},
|
|
123
|
-
|
|
124
|
-
// Test method
|
|
125
|
-
testMethod: (message: string) => {
|
|
126
|
-
if (UnifiedPlayerModule?.testMethod) {
|
|
127
|
-
UnifiedPlayerModule.testMethod(message);
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Events emitter for native events
|
|
133
|
-
let eventEmitter: NativeEventEmitter | null = null;
|
|
134
|
-
|
|
135
|
-
if (UnifiedPlayerEventEmitterModule) {
|
|
136
|
-
eventEmitter = new NativeEventEmitter(UnifiedPlayerEventEmitterModule);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export const UnifiedPlayerEvents = {
|
|
140
|
-
addListener: (eventType: string, listener: (...args: any[]) => any) => {
|
|
141
|
-
if (eventEmitter) {
|
|
142
|
-
return eventEmitter.addListener(eventType, listener);
|
|
143
|
-
}
|
|
144
|
-
return { remove: () => {} };
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Event names
|
|
149
|
-
export const UnifiedPlayerEventTypes = {
|
|
150
|
-
READY: 'onReadyToPlay',
|
|
151
|
-
ERROR: 'onError',
|
|
152
|
-
PROGRESS: 'onProgress',
|
|
153
|
-
COMPLETE: 'onPlaybackComplete',
|
|
154
|
-
STALLED: 'onPlaybackStalled',
|
|
155
|
-
RESUMED: 'onPlaybackResumed',
|
|
156
180
|
};
|