react-native-unified-player 0.3.2 → 0.3.3
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 +0 -2
- package/UnifiedPlayer.podspec +1 -1
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerEventEmitter.kt +4 -1
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerModule.kt +0 -4
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerView.kt +44 -20
- package/android/src/main/java/com/unifiedplayer/UnifiedPlayerViewManager.kt +5 -1
- package/ios/UnifiedPlayerModule.m +210 -0
- package/ios/UnifiedPlayerViewManager.m +84 -160
- package/lib/module/index.js +34 -7
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +15 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +66 -9
package/README.md
CHANGED
|
@@ -65,7 +65,6 @@ const MyPlayerComponent = () => {
|
|
|
65
65
|
videoUrl="YOUR_VIDEO_URL_HERE" // Replace with your video URL
|
|
66
66
|
autoplay={false} // Optional: set to true to autoplay
|
|
67
67
|
loop={false} // Optional: set to true to loop
|
|
68
|
-
// authToken="YOUR_AUTH_TOKEN" // Optional: for protected streams
|
|
69
68
|
// You can also use direct view props instead of or in addition to event listeners:
|
|
70
69
|
// onReadyToPlay={() => console.log('View prop: Ready to play')}
|
|
71
70
|
// onError={(e) => console.log('View prop: Error', e)}
|
|
@@ -87,7 +86,6 @@ export default MyPlayerComponent;
|
|
|
87
86
|
| `style` | `ViewStyle` | Yes | Apply custom styling |
|
|
88
87
|
| `autoplay` | `boolean` | No | Autoplay video when loaded |
|
|
89
88
|
| `loop` | `boolean` | No | Should video loop when finished |
|
|
90
|
-
| `authToken` | `string` | No | Optional auth token for protected streams |
|
|
91
89
|
| `onReadyToPlay` | `() => void` | No | Callback when video is ready to play |
|
|
92
90
|
| `onError` | `(error: any) => void` | No | Callback when an error occurs |
|
|
93
91
|
| `onPlaybackComplete` | `() => void` | No | Callback when video playback finishes |
|
package/UnifiedPlayer.podspec
CHANGED
|
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
|
-
s.platforms = { :ios =>
|
|
13
|
+
s.platforms = { :ios => "12.0" }
|
|
14
14
|
s.source = { :git => "https://github.com/blueromans/react-native-unified-player.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm}"
|
|
@@ -12,12 +12,15 @@ class UnifiedPlayerEventEmitter(private val reactContext: ReactApplicationContex
|
|
|
12
12
|
private const val TAG = "UnifiedPlayerEventEmitter"
|
|
13
13
|
|
|
14
14
|
// Define all possible event types
|
|
15
|
+
const val EVENT_LOAD_START = "onLoadStart"
|
|
15
16
|
const val EVENT_READY = "onReadyToPlay"
|
|
16
17
|
const val EVENT_ERROR = "onError"
|
|
17
18
|
const val EVENT_PROGRESS = "onProgress"
|
|
18
19
|
const val EVENT_COMPLETE = "onPlaybackComplete"
|
|
19
20
|
const val EVENT_STALLED = "onPlaybackStalled"
|
|
20
21
|
const val EVENT_RESUMED = "onPlaybackResumed"
|
|
22
|
+
const val EVENT_PLAYING = "onPlaying"
|
|
23
|
+
const val EVENT_PAUSED = "onPaused"
|
|
21
24
|
|
|
22
25
|
// Singleton instance for access from other classes
|
|
23
26
|
private var instance: UnifiedPlayerEventEmitter? = null
|
|
@@ -59,4 +62,4 @@ class UnifiedPlayerEventEmitter(private val reactContext: ReactApplicationContex
|
|
|
59
62
|
super.onCatalystInstanceDestroy()
|
|
60
63
|
instance = null
|
|
61
64
|
}
|
|
62
|
-
}
|
|
65
|
+
}
|
|
@@ -19,6 +19,15 @@ import com.google.android.exoplayer2.video.VideoSize
|
|
|
19
19
|
import com.facebook.react.bridge.WritableMap
|
|
20
20
|
import com.facebook.react.bridge.ReactContext
|
|
21
21
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
22
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_COMPLETE
|
|
23
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_ERROR
|
|
24
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_LOAD_START
|
|
25
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PAUSED
|
|
26
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PLAYING
|
|
27
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_PROGRESS
|
|
28
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_READY
|
|
29
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_RESUMED
|
|
30
|
+
import com.unifiedplayer.UnifiedPlayerEventEmitter.Companion.EVENT_STALLED
|
|
22
31
|
|
|
23
32
|
class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
24
33
|
companion object {
|
|
@@ -50,7 +59,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
50
59
|
event.putDouble("duration", duration.toDouble())
|
|
51
60
|
|
|
52
61
|
Log.d(TAG, "Sending progress event: currentTime=$currentTime, duration=$duration")
|
|
53
|
-
sendEvent(
|
|
62
|
+
sendEvent(EVENT_PROGRESS, event)
|
|
54
63
|
} else {
|
|
55
64
|
Log.d(TAG, "Not sending progress event because duration is $duration (raw: ${it.duration})")
|
|
56
65
|
}
|
|
@@ -89,14 +98,15 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
89
98
|
when (playbackState) {
|
|
90
99
|
Player.STATE_READY -> {
|
|
91
100
|
Log.d(TAG, "ExoPlayer STATE_READY")
|
|
92
|
-
sendEvent(
|
|
101
|
+
sendEvent(EVENT_READY, Arguments.createMap())
|
|
93
102
|
}
|
|
94
103
|
Player.STATE_ENDED -> {
|
|
95
104
|
Log.d(TAG, "ExoPlayer STATE_ENDED")
|
|
96
|
-
sendEvent(
|
|
105
|
+
sendEvent(EVENT_COMPLETE, Arguments.createMap())
|
|
97
106
|
}
|
|
98
107
|
Player.STATE_BUFFERING -> {
|
|
99
108
|
Log.d(TAG, "ExoPlayer STATE_BUFFERING")
|
|
109
|
+
sendEvent(EVENT_STALLED, Arguments.createMap())
|
|
100
110
|
}
|
|
101
111
|
Player.STATE_IDLE -> {
|
|
102
112
|
Log.d(TAG, "ExoPlayer STATE_IDLE")
|
|
@@ -105,29 +115,28 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
|
108
|
-
Log.d(TAG, "onIsPlayingChanged: $isPlaying")
|
|
118
|
+
Log.d(TAG, "onIsPlayingChanged: $isPlaying") // Added log
|
|
109
119
|
if (isPlaying) {
|
|
110
|
-
Log.d(TAG, "ExoPlayer
|
|
111
|
-
|
|
112
|
-
sendEvent(
|
|
120
|
+
Log.d(TAG, "ExoPlayer is now playing")
|
|
121
|
+
sendEvent(EVENT_RESUMED, Arguments.createMap())
|
|
122
|
+
sendEvent(EVENT_PLAYING, Arguments.createMap())
|
|
113
123
|
} else {
|
|
114
|
-
Log.d(TAG, "ExoPlayer
|
|
115
|
-
|
|
116
|
-
sendEvent("topPlaybackPaused", Arguments.createMap())
|
|
124
|
+
Log.d(TAG, "ExoPlayer is now paused")
|
|
125
|
+
sendEvent(EVENT_PAUSED, Arguments.createMap())
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
|
|
120
129
|
override fun onPlayerError(error: PlaybackException) {
|
|
121
|
-
Log.
|
|
122
|
-
val event = Arguments.createMap()
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
sendEvent(
|
|
130
|
+
Log.e(TAG, "ExoPlayer error: $error")
|
|
131
|
+
val event = Arguments.createMap()
|
|
132
|
+
event.putString("code", "PLAYBACK_ERROR")
|
|
133
|
+
event.putString("message", error.message ?: "Unknown playback error")
|
|
134
|
+
sendEvent(EVENT_ERROR, event)
|
|
126
135
|
}
|
|
127
136
|
|
|
128
137
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
|
129
|
-
Log.d(TAG, "
|
|
130
|
-
sendEvent(
|
|
138
|
+
Log.d(TAG, "onMediaItemTransition with reason: $reason")
|
|
139
|
+
sendEvent(EVENT_LOAD_START, Arguments.createMap())
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
override fun onPlaybackSuppressionReasonChanged(playbackSuppressionReason: Int) {
|
|
@@ -238,8 +247,9 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
238
247
|
|
|
239
248
|
// Send error event
|
|
240
249
|
val event = Arguments.createMap()
|
|
241
|
-
event.putString("
|
|
242
|
-
|
|
250
|
+
event.putString("code", "SOURCE_ERROR")
|
|
251
|
+
event.putString("message", "Failed to load video source: $url")
|
|
252
|
+
sendEvent(EVENT_ERROR, event)
|
|
243
253
|
}
|
|
244
254
|
}
|
|
245
255
|
|
|
@@ -323,10 +333,24 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
323
333
|
// Log the event for debugging
|
|
324
334
|
Log.d(TAG, "Sending direct event: $eventName with params: $params")
|
|
325
335
|
|
|
336
|
+
// Map event names to their corresponding top event names
|
|
337
|
+
val topEventName = when (eventName) {
|
|
338
|
+
EVENT_READY -> "topReadyToPlay"
|
|
339
|
+
EVENT_ERROR -> "topError"
|
|
340
|
+
EVENT_PROGRESS -> "topProgress"
|
|
341
|
+
EVENT_COMPLETE -> "topPlaybackComplete"
|
|
342
|
+
EVENT_STALLED -> "topPlaybackStalled"
|
|
343
|
+
EVENT_RESUMED -> "topPlaybackResumed"
|
|
344
|
+
EVENT_PLAYING -> "topPlaying"
|
|
345
|
+
EVENT_PAUSED -> "topPlaybackPaused"
|
|
346
|
+
EVENT_LOAD_START -> "topLoadStart"
|
|
347
|
+
else -> "top${eventName.substring(2)}" // Fallback for any other events
|
|
348
|
+
}
|
|
349
|
+
|
|
326
350
|
// Use the ReactContext to dispatch the event directly to the view
|
|
327
351
|
val reactContext = context as ReactContext
|
|
328
352
|
reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
329
|
-
.receiveEvent(id,
|
|
353
|
+
.receiveEvent(id, topEventName, params)
|
|
330
354
|
} catch (e: Exception) {
|
|
331
355
|
Log.e(TAG, "Error sending event $eventName: ${e.message}", e)
|
|
332
356
|
}
|
|
@@ -37,6 +37,8 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
|
|
|
37
37
|
fun setIsPaused(view: UnifiedPlayerView, isPaused: Boolean) {
|
|
38
38
|
view.setIsPaused(isPaused)
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
|
|
40
42
|
|
|
41
43
|
// Register direct events
|
|
42
44
|
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
|
@@ -49,7 +51,9 @@ class UnifiedPlayerViewManager : SimpleViewManager<UnifiedPlayerView>() {
|
|
|
49
51
|
.put("topProgress", MapBuilder.of("registrationName", "onProgress"))
|
|
50
52
|
.put("topPlaybackComplete", MapBuilder.of("registrationName", "onPlaybackComplete"))
|
|
51
53
|
.put("topPlaybackResumed", MapBuilder.of("registrationName", "onPlaybackResumed"))
|
|
52
|
-
.put("
|
|
54
|
+
.put("topPlaybackStalled", MapBuilder.of("registrationName", "onPlaybackStalled"))
|
|
55
|
+
.put("topPlaybackPaused", MapBuilder.of("registrationName", "onPaused"))
|
|
56
|
+
.put("topPlaying", MapBuilder.of("registrationName", "onPlaying"))
|
|
53
57
|
.put("topLoadStart", MapBuilder.of("registrationName", "onLoadStart"))
|
|
54
58
|
.build()
|
|
55
59
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#import "UnifiedPlayerModule.h"
|
|
2
|
+
#import <React/RCTLog.h>
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
#import <React/RCTEventEmitter.h>
|
|
5
|
+
#import <React/RCTUIManager.h>
|
|
6
|
+
#import <MobileVLCKit/MobileVLCKit.h>
|
|
7
|
+
|
|
8
|
+
@implementation UnifiedPlayerModule
|
|
9
|
+
|
|
10
|
+
// Explicitly name the module to match what's expected in JavaScript
|
|
11
|
+
RCT_EXPORT_MODULE(UnifiedPlayer);
|
|
12
|
+
|
|
13
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
14
|
+
return YES;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
18
|
+
return @[
|
|
19
|
+
@"onLoadStart",
|
|
20
|
+
@"onReadyToPlay",
|
|
21
|
+
@"onError",
|
|
22
|
+
@"onProgress",
|
|
23
|
+
@"onPlaybackComplete",
|
|
24
|
+
@"onPlaybackStalled",
|
|
25
|
+
@"onPlaybackResumed",
|
|
26
|
+
@"onPlaying",
|
|
27
|
+
@"onPaused"
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (dispatch_queue_t)methodQueue {
|
|
32
|
+
return dispatch_get_main_queue();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Play video
|
|
36
|
+
RCT_EXPORT_METHOD(play:(nonnull NSNumber *)reactTag
|
|
37
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
38
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
39
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
40
|
+
UIView *view = viewRegistry[reactTag];
|
|
41
|
+
if (!view) {
|
|
42
|
+
reject(@"error", @"Invalid view for tag", nil);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Cast to UnifiedPlayerUIView class
|
|
47
|
+
Class UnifiedPlayerUIViewClass = NSClassFromString(@"UnifiedPlayerUIView");
|
|
48
|
+
if (![view isKindOfClass:UnifiedPlayerUIViewClass]) {
|
|
49
|
+
reject(@"error", @"View is not a UnifiedPlayerUIView", nil);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@try {
|
|
54
|
+
// Use direct ivar access for safety
|
|
55
|
+
VLCMediaPlayer *player = [view valueForKey:@"player"];
|
|
56
|
+
if (player && player.media) {
|
|
57
|
+
[player play];
|
|
58
|
+
resolve(@(YES));
|
|
59
|
+
} else {
|
|
60
|
+
reject(@"error", @"Player or media not initialized", nil);
|
|
61
|
+
}
|
|
62
|
+
} @catch (NSException *exception) {
|
|
63
|
+
reject(@"error", [NSString stringWithFormat:@"Error playing: %@", exception.reason], nil);
|
|
64
|
+
}
|
|
65
|
+
}];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Pause video
|
|
69
|
+
RCT_EXPORT_METHOD(pause:(nonnull NSNumber *)reactTag
|
|
70
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
71
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
72
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
73
|
+
UIView *view = viewRegistry[reactTag];
|
|
74
|
+
if (!view) {
|
|
75
|
+
reject(@"error", @"Invalid view for tag", nil);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Cast to UnifiedPlayerUIView class
|
|
80
|
+
Class UnifiedPlayerUIViewClass = NSClassFromString(@"UnifiedPlayerUIView");
|
|
81
|
+
if (![view isKindOfClass:UnifiedPlayerUIViewClass]) {
|
|
82
|
+
reject(@"error", @"View is not a UnifiedPlayerUIView", nil);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@try {
|
|
87
|
+
// Use direct ivar access for safety
|
|
88
|
+
VLCMediaPlayer *player = [view valueForKey:@"player"];
|
|
89
|
+
if (player) {
|
|
90
|
+
[player pause];
|
|
91
|
+
resolve(@(YES));
|
|
92
|
+
} else {
|
|
93
|
+
reject(@"error", @"Player not initialized", nil);
|
|
94
|
+
}
|
|
95
|
+
} @catch (NSException *exception) {
|
|
96
|
+
reject(@"error", [NSString stringWithFormat:@"Error pausing: %@", exception.reason], nil);
|
|
97
|
+
}
|
|
98
|
+
}];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Seek to specific time
|
|
102
|
+
RCT_EXPORT_METHOD(seekTo:(nonnull NSNumber *)reactTag
|
|
103
|
+
time:(nonnull NSNumber *)time
|
|
104
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
105
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
106
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
107
|
+
UIView *view = viewRegistry[reactTag];
|
|
108
|
+
if (!view) {
|
|
109
|
+
reject(@"error", @"Invalid view for tag", nil);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Cast to UnifiedPlayerUIView class
|
|
114
|
+
Class UnifiedPlayerUIViewClass = NSClassFromString(@"UnifiedPlayerUIView");
|
|
115
|
+
if (![view isKindOfClass:UnifiedPlayerUIViewClass]) {
|
|
116
|
+
reject(@"error", @"View is not a UnifiedPlayerUIView", nil);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@try {
|
|
121
|
+
// Use direct ivar access for safety
|
|
122
|
+
VLCMediaPlayer *player = [view valueForKey:@"player"];
|
|
123
|
+
if (player && player.media) {
|
|
124
|
+
float timeValue = [time floatValue];
|
|
125
|
+
float duration = player.media.length.intValue / 1000.0f;
|
|
126
|
+
float position = duration > 0 ? timeValue / duration : 0;
|
|
127
|
+
position = MAX(0, MIN(1, position)); // Ensure position is between 0 and 1
|
|
128
|
+
|
|
129
|
+
[player setPosition:position];
|
|
130
|
+
resolve(@(YES));
|
|
131
|
+
} else {
|
|
132
|
+
reject(@"error", @"Player or media not initialized", nil);
|
|
133
|
+
}
|
|
134
|
+
} @catch (NSException *exception) {
|
|
135
|
+
reject(@"error", [NSString stringWithFormat:@"Error seeking: %@", exception.reason], nil);
|
|
136
|
+
}
|
|
137
|
+
}];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Get current playback time
|
|
141
|
+
RCT_EXPORT_METHOD(getCurrentTime:(nonnull NSNumber *)reactTag
|
|
142
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
143
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
144
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
145
|
+
UIView *view = viewRegistry[reactTag];
|
|
146
|
+
if (!view) {
|
|
147
|
+
reject(@"error", @"Invalid view for tag", nil);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Cast to UnifiedPlayerUIView class
|
|
152
|
+
Class UnifiedPlayerUIViewClass = NSClassFromString(@"UnifiedPlayerUIView");
|
|
153
|
+
if (![view isKindOfClass:UnifiedPlayerUIViewClass]) {
|
|
154
|
+
reject(@"error", @"View is not a UnifiedPlayerUIView", nil);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Use direct method call with proper type safety
|
|
159
|
+
float currentTime = 0;
|
|
160
|
+
@try {
|
|
161
|
+
// Use direct ivar access for safety
|
|
162
|
+
VLCMediaPlayer *player = [view valueForKey:@"player"];
|
|
163
|
+
if (player) {
|
|
164
|
+
currentTime = player.time.intValue / 1000.0f;
|
|
165
|
+
resolve(@(currentTime));
|
|
166
|
+
} else {
|
|
167
|
+
reject(@"error", @"Player not initialized", nil);
|
|
168
|
+
}
|
|
169
|
+
} @catch (NSException *exception) {
|
|
170
|
+
reject(@"error", [NSString stringWithFormat:@"Error getting current time: %@", exception.reason], nil);
|
|
171
|
+
}
|
|
172
|
+
}];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Get video duration
|
|
176
|
+
RCT_EXPORT_METHOD(getDuration:(nonnull NSNumber *)reactTag
|
|
177
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
178
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
179
|
+
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
180
|
+
UIView *view = viewRegistry[reactTag];
|
|
181
|
+
if (!view) {
|
|
182
|
+
reject(@"error", @"Invalid view for tag", nil);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Cast to UnifiedPlayerUIView class
|
|
187
|
+
Class UnifiedPlayerUIViewClass = NSClassFromString(@"UnifiedPlayerUIView");
|
|
188
|
+
if (![view isKindOfClass:UnifiedPlayerUIViewClass]) {
|
|
189
|
+
reject(@"error", @"View is not a UnifiedPlayerUIView", nil);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Use direct method call with proper type safety
|
|
194
|
+
float duration = 0;
|
|
195
|
+
@try {
|
|
196
|
+
// Use direct ivar access for safety
|
|
197
|
+
VLCMediaPlayer *player = [view valueForKey:@"player"];
|
|
198
|
+
if (player && player.media) {
|
|
199
|
+
duration = player.media.length.intValue / 1000.0f;
|
|
200
|
+
resolve(@(duration));
|
|
201
|
+
} else {
|
|
202
|
+
reject(@"error", @"Player or media not initialized", nil);
|
|
203
|
+
}
|
|
204
|
+
} @catch (NSException *exception) {
|
|
205
|
+
reject(@"error", [NSString stringWithFormat:@"Error getting duration: %@", exception.reason], nil);
|
|
206
|
+
}
|
|
207
|
+
}];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@end
|
|
@@ -5,125 +5,41 @@
|
|
|
5
5
|
#import <React/RCTUIManager.h>
|
|
6
6
|
#import <React/RCTBridge.h>
|
|
7
7
|
#import <React/RCTUIManagerUtils.h>
|
|
8
|
+
#import <React/RCTComponent.h>
|
|
8
9
|
#import <MobileVLCKit/MobileVLCKit.h>
|
|
10
|
+
#import "UnifiedPlayerModule.h"
|
|
9
11
|
|
|
10
12
|
// Forward declarations
|
|
11
13
|
@interface UnifiedPlayerUIView : UIView <VLCMediaPlayerDelegate>
|
|
12
14
|
@property (nonatomic, strong) VLCMediaPlayer *player;
|
|
13
15
|
@property (nonatomic, copy) NSString *videoUrlString;
|
|
14
|
-
@property (nonatomic, copy) NSString *authToken;
|
|
15
16
|
@property (nonatomic, assign) BOOL autoplay;
|
|
16
17
|
@property (nonatomic, assign) BOOL loop;
|
|
17
|
-
@property (nonatomic, assign) BOOL isPaused;
|
|
18
|
+
@property (nonatomic, assign) BOOL isPaused;
|
|
18
19
|
@property (nonatomic, strong) NSArray *mediaOptions;
|
|
19
20
|
@property (nonatomic, weak) RCTBridge *bridge;
|
|
20
21
|
@property (nonatomic, assign) VLCMediaPlayerState previousState;
|
|
21
22
|
@property (nonatomic, assign) BOOL hasRenderedVideo;
|
|
22
23
|
|
|
24
|
+
// Event callbacks
|
|
25
|
+
@property (nonatomic, copy) RCTDirectEventBlock onLoadStart;
|
|
26
|
+
@property (nonatomic, copy) RCTDirectEventBlock onReadyToPlay;
|
|
27
|
+
@property (nonatomic, copy) RCTDirectEventBlock onError;
|
|
28
|
+
@property (nonatomic, copy) RCTDirectEventBlock onProgress;
|
|
29
|
+
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackComplete;
|
|
30
|
+
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackStalled;
|
|
31
|
+
@property (nonatomic, copy) RCTDirectEventBlock onPlaybackResumed;
|
|
32
|
+
@property (nonatomic, copy) RCTDirectEventBlock onPlaying;
|
|
33
|
+
@property (nonatomic, copy) RCTDirectEventBlock onPaused;
|
|
34
|
+
|
|
23
35
|
- (void)setupWithVideoUrlString:(NSString *)videoUrlString;
|
|
24
36
|
- (void)play;
|
|
25
37
|
- (void)pause;
|
|
26
|
-
- (void)seekToTime:(
|
|
38
|
+
- (void)seekToTime:(NSNumber *)timeNumber;
|
|
27
39
|
- (float)getCurrentTime;
|
|
28
40
|
- (float)getDuration;
|
|
29
41
|
@end
|
|
30
42
|
|
|
31
|
-
// UnifiedPlayerModule - Module for handling control methods
|
|
32
|
-
@interface UnifiedPlayerModule : RCTEventEmitter <RCTBridgeModule>
|
|
33
|
-
@end
|
|
34
|
-
|
|
35
|
-
@implementation UnifiedPlayerModule
|
|
36
|
-
|
|
37
|
-
RCT_EXPORT_MODULE();
|
|
38
|
-
|
|
39
|
-
- (NSArray<NSString *> *)supportedEvents {
|
|
40
|
-
return @[
|
|
41
|
-
@"onReadyToPlay",
|
|
42
|
-
@"onError",
|
|
43
|
-
@"onProgress",
|
|
44
|
-
@"onPlaybackComplete",
|
|
45
|
-
@"onPlaybackStalled",
|
|
46
|
-
@"onPlaybackResumed",
|
|
47
|
-
@"onBuffering",
|
|
48
|
-
@"onPlaying",
|
|
49
|
-
@"onPaused",
|
|
50
|
-
@"onStopped"
|
|
51
|
-
];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
- (dispatch_queue_t)methodQueue {
|
|
55
|
-
return dispatch_get_main_queue();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Play video
|
|
59
|
-
RCT_EXPORT_METHOD(play:(nonnull NSNumber *)reactTag) {
|
|
60
|
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
61
|
-
UnifiedPlayerUIView *view = (UnifiedPlayerUIView *)viewRegistry[reactTag];
|
|
62
|
-
if (![view isKindOfClass:[UnifiedPlayerUIView class]]) {
|
|
63
|
-
RCTLogError(@"Invalid view for tag %@", reactTag);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
[view play];
|
|
67
|
-
}];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Pause video
|
|
71
|
-
RCT_EXPORT_METHOD(pause:(nonnull NSNumber *)reactTag) {
|
|
72
|
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
73
|
-
UnifiedPlayerUIView *view = (UnifiedPlayerUIView *)viewRegistry[reactTag];
|
|
74
|
-
if (![view isKindOfClass:[UnifiedPlayerUIView class]]) {
|
|
75
|
-
RCTLogError(@"Invalid view for tag %@", reactTag);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
[view pause];
|
|
79
|
-
}];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Seek to specific time
|
|
83
|
-
RCT_EXPORT_METHOD(seekTo:(nonnull NSNumber *)reactTag time:(nonnull NSNumber *)time) {
|
|
84
|
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
85
|
-
UnifiedPlayerUIView *view = (UnifiedPlayerUIView *)viewRegistry[reactTag];
|
|
86
|
-
if (![view isKindOfClass:[UnifiedPlayerUIView class]]) {
|
|
87
|
-
RCTLogError(@"Invalid view for tag %@", reactTag);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
[view seekToTime:[time floatValue]];
|
|
91
|
-
}];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Get current playback time
|
|
95
|
-
RCT_EXPORT_METHOD(getCurrentTime:(nonnull NSNumber *)reactTag
|
|
96
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
97
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
98
|
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
99
|
-
UnifiedPlayerUIView *view = (UnifiedPlayerUIView *)viewRegistry[reactTag];
|
|
100
|
-
if (![view isKindOfClass:[UnifiedPlayerUIView class]]) {
|
|
101
|
-
reject(@"error", @"Invalid view for tag", nil);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
resolve(@([view getCurrentTime]));
|
|
105
|
-
}];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Get video duration
|
|
109
|
-
RCT_EXPORT_METHOD(getDuration:(nonnull NSNumber *)reactTag
|
|
110
|
-
resolver:(RCTPromiseResolveBlock)resolve
|
|
111
|
-
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
112
|
-
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
|
113
|
-
UnifiedPlayerUIView *view = (UnifiedPlayerUIView *)viewRegistry[reactTag];
|
|
114
|
-
if (![view isKindOfClass:[UnifiedPlayerUIView class]]) {
|
|
115
|
-
reject(@"error", @"Invalid view for tag", nil);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
resolve(@([view getDuration]));
|
|
119
|
-
}];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@end
|
|
123
|
-
|
|
124
|
-
// Global event emitter instance
|
|
125
|
-
static UnifiedPlayerModule *eventEmitter = nil;
|
|
126
|
-
|
|
127
43
|
// Main player view implementation
|
|
128
44
|
@implementation UnifiedPlayerUIView
|
|
129
45
|
|
|
@@ -235,17 +151,38 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
235
151
|
}
|
|
236
152
|
|
|
237
153
|
- (void)sendProgressEvent:(float)currentTime duration:(float)duration {
|
|
238
|
-
|
|
239
|
-
@
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (eventEmitter != nil) {
|
|
244
|
-
[eventEmitter sendEventWithName:@"onProgress" body:event];
|
|
154
|
+
if (self.onProgress) {
|
|
155
|
+
self.onProgress(@{
|
|
156
|
+
@"currentTime": @(currentTime),
|
|
157
|
+
@"duration": @(duration)
|
|
158
|
+
});
|
|
245
159
|
}
|
|
246
160
|
}
|
|
247
161
|
|
|
248
162
|
- (void)sendEvent:(NSString *)eventName body:(NSDictionary *)body {
|
|
163
|
+
// Map event names to their corresponding callback properties
|
|
164
|
+
if ([eventName isEqualToString:@"onLoadStart"] && self.onLoadStart) {
|
|
165
|
+
self.onLoadStart(body);
|
|
166
|
+
} else if ([eventName isEqualToString:@"onReadyToPlay"] && self.onReadyToPlay) {
|
|
167
|
+
self.onReadyToPlay(body);
|
|
168
|
+
} else if ([eventName isEqualToString:@"onError"] && self.onError) {
|
|
169
|
+
self.onError(body);
|
|
170
|
+
} else if ([eventName isEqualToString:@"onProgress"] && self.onProgress) {
|
|
171
|
+
self.onProgress(body);
|
|
172
|
+
} else if ([eventName isEqualToString:@"onPlaybackComplete"] && self.onPlaybackComplete) {
|
|
173
|
+
self.onPlaybackComplete(body);
|
|
174
|
+
} else if ([eventName isEqualToString:@"onPlaybackStalled"] && self.onPlaybackStalled) {
|
|
175
|
+
self.onPlaybackStalled(body);
|
|
176
|
+
} else if ([eventName isEqualToString:@"onPlaybackResumed"] && self.onPlaybackResumed) {
|
|
177
|
+
self.onPlaybackResumed(body);
|
|
178
|
+
} else if ([eventName isEqualToString:@"onPlaying"] && self.onPlaying) {
|
|
179
|
+
self.onPlaying(body);
|
|
180
|
+
} else if ([eventName isEqualToString:@"onPaused"] && self.onPaused) {
|
|
181
|
+
self.onPaused(body);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Also send events through the module for backward compatibility
|
|
185
|
+
UnifiedPlayerModule *eventEmitter = [self.bridge moduleForClass:[UnifiedPlayerModule class]];
|
|
249
186
|
if (eventEmitter != nil) {
|
|
250
187
|
[eventEmitter sendEventWithName:eventName body:body];
|
|
251
188
|
}
|
|
@@ -256,6 +193,8 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
256
193
|
_videoUrlString = [videoUrlString copy];
|
|
257
194
|
|
|
258
195
|
if (videoUrlString) {
|
|
196
|
+
// Send onLoadStart event when we set a video URL, before loading starts
|
|
197
|
+
[self sendEvent:@"onLoadStart" body:@{}];
|
|
259
198
|
[self loadVideo];
|
|
260
199
|
} else {
|
|
261
200
|
[_player stop];
|
|
@@ -301,10 +240,6 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
301
240
|
// Create VLC media options array
|
|
302
241
|
NSMutableArray *mediaOptions = [NSMutableArray array];
|
|
303
242
|
|
|
304
|
-
// Add auth token if available
|
|
305
|
-
if (self->_authToken.length > 0) {
|
|
306
|
-
[mediaOptions addObject:[NSString stringWithFormat:@"http-header-fields=Authorization: Bearer %@", self->_authToken]];
|
|
307
|
-
}
|
|
308
243
|
|
|
309
244
|
// Add default network caching options for streaming
|
|
310
245
|
BOOL isStreaming = [videoURL.scheme hasPrefix:@"http"] ||
|
|
@@ -422,9 +357,11 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
422
357
|
RCTLogInfo(@"[UnifiedPlayerViewManager] pause called");
|
|
423
358
|
}
|
|
424
359
|
|
|
425
|
-
- (void)seekToTime:(
|
|
360
|
+
- (void)seekToTime:(NSNumber *)timeNumber {
|
|
361
|
+
float time = [timeNumber floatValue];
|
|
426
362
|
// VLC uses a 0-1 position value for seeking
|
|
427
|
-
float
|
|
363
|
+
float duration = [self getDuration];
|
|
364
|
+
float position = duration > 0 ? time / duration : 0;
|
|
428
365
|
position = MAX(0, MIN(1, position)); // Ensure position is between 0 and 1
|
|
429
366
|
|
|
430
367
|
[_player setPosition:position];
|
|
@@ -462,15 +399,6 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
462
399
|
}
|
|
463
400
|
}
|
|
464
401
|
|
|
465
|
-
- (void)setAuthToken:(NSString *)authToken {
|
|
466
|
-
if (_authToken != authToken) {
|
|
467
|
-
_authToken = [authToken copy];
|
|
468
|
-
// If we already have a URL, reload with the new auth token
|
|
469
|
-
if (_videoUrlString) {
|
|
470
|
-
[self loadVideo];
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
402
|
|
|
475
403
|
#pragma mark - VLCMediaPlayerDelegate
|
|
476
404
|
|
|
@@ -495,12 +423,17 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
495
423
|
_player.hasVideoOut ? @"YES" : @"NO");
|
|
496
424
|
|
|
497
425
|
// Check video tracks
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
426
|
+
NSArray *videoTracks = [_player.media tracksInformation];
|
|
427
|
+
if (videoTracks.count > 0) {
|
|
428
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Video tracks found: %lu", (unsigned long)videoTracks.count);
|
|
429
|
+
|
|
430
|
+
// Send ready event the first time we start playing
|
|
431
|
+
if (!_hasRenderedVideo) {
|
|
432
|
+
[self sendEvent:@"onReadyToPlay" body:@{}];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Send playing event when we actually start playing
|
|
436
|
+
[self sendEvent:@"onPlaying" body:@{}];
|
|
504
437
|
|
|
505
438
|
// Trigger a delayed drawable update to help with rendering
|
|
506
439
|
if (!_hasRenderedVideo && _player.videoSize.width > 0) {
|
|
@@ -513,14 +446,15 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
513
446
|
}
|
|
514
447
|
|
|
515
448
|
// Check for buffer state transitions
|
|
516
|
-
if (
|
|
517
|
-
|
|
518
|
-
[self
|
|
449
|
+
if (_previousState == VLCMediaPlayerStateBuffering && state == VLCMediaPlayerStatePlaying) {
|
|
450
|
+
// We've recovered from buffering
|
|
451
|
+
[self sendEvent:@"onPlaybackResumed" body:@{}];
|
|
519
452
|
}
|
|
520
453
|
|
|
521
|
-
//
|
|
454
|
+
// Store the current state for future comparisons
|
|
522
455
|
_previousState = state;
|
|
523
456
|
|
|
457
|
+
// React to state changes
|
|
524
458
|
switch (state) {
|
|
525
459
|
case VLCMediaPlayerStateOpening:
|
|
526
460
|
RCTLogInfo(@"[UnifiedPlayerViewManager] VLCMediaPlayerStateOpening");
|
|
@@ -528,20 +462,11 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
528
462
|
|
|
529
463
|
case VLCMediaPlayerStateBuffering:
|
|
530
464
|
RCTLogInfo(@"[UnifiedPlayerViewManager] VLCMediaPlayerStateBuffering");
|
|
531
|
-
[self sendEvent:@"
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}];
|
|
535
|
-
break;
|
|
536
|
-
|
|
465
|
+
[self sendEvent:@"onPlaybackStalled" body:@{}];
|
|
466
|
+
break;
|
|
467
|
+
|
|
537
468
|
case VLCMediaPlayerStatePlaying:
|
|
538
469
|
RCTLogInfo(@"[UnifiedPlayerViewManager] VLCMediaPlayerStatePlaying");
|
|
539
|
-
[self sendEvent:@"onPlaying" body:@{
|
|
540
|
-
@"target": @(1),
|
|
541
|
-
@"duration": @([self getDuration] * 1000), // Convert to ms for consistency
|
|
542
|
-
@"seekable": @(YES)
|
|
543
|
-
}];
|
|
544
|
-
[self sendEvent:@"onReadyToPlay" body:@{}];
|
|
545
470
|
break;
|
|
546
471
|
|
|
547
472
|
case VLCMediaPlayerStatePaused:
|
|
@@ -551,7 +476,7 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
551
476
|
|
|
552
477
|
case VLCMediaPlayerStateStopped:
|
|
553
478
|
RCTLogInfo(@"[UnifiedPlayerViewManager] VLCMediaPlayerStateStopped");
|
|
554
|
-
|
|
479
|
+
// We don't emit onStopped event as it's not in our unified event list
|
|
555
480
|
break;
|
|
556
481
|
|
|
557
482
|
case VLCMediaPlayerStateEnded:
|
|
@@ -561,9 +486,9 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
561
486
|
// Handle looping
|
|
562
487
|
if (_loop) {
|
|
563
488
|
[_player stop];
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
489
|
+
[_player play];
|
|
490
|
+
}
|
|
491
|
+
break;
|
|
567
492
|
|
|
568
493
|
case VLCMediaPlayerStateError:
|
|
569
494
|
RCTLogInfo(@"[UnifiedPlayerViewManager] VLCMediaPlayerStateError");
|
|
@@ -575,7 +500,7 @@ static UnifiedPlayerModule *eventEmitter = nil;
|
|
|
575
500
|
break;
|
|
576
501
|
|
|
577
502
|
default:
|
|
578
|
-
|
|
503
|
+
break;
|
|
579
504
|
}
|
|
580
505
|
}
|
|
581
506
|
|
|
@@ -644,12 +569,6 @@ RCT_EXPORT_MODULE(UnifiedPlayerView)
|
|
|
644
569
|
{
|
|
645
570
|
UnifiedPlayerUIView *playerView = [[UnifiedPlayerUIView alloc] init];
|
|
646
571
|
playerView.bridge = self.bridge;
|
|
647
|
-
|
|
648
|
-
// Store the event emitter for sending events
|
|
649
|
-
if (eventEmitter == nil) {
|
|
650
|
-
eventEmitter = [self.bridge moduleForClass:[UnifiedPlayerModule class]];
|
|
651
|
-
}
|
|
652
|
-
|
|
653
572
|
return playerView;
|
|
654
573
|
}
|
|
655
574
|
|
|
@@ -671,12 +590,6 @@ RCT_CUSTOM_VIEW_PROPERTY(loop, BOOL, UnifiedPlayerUIView)
|
|
|
671
590
|
view.loop = [RCTConvert BOOL:json];
|
|
672
591
|
}
|
|
673
592
|
|
|
674
|
-
// Auth token property
|
|
675
|
-
RCT_CUSTOM_VIEW_PROPERTY(authToken, NSString, UnifiedPlayerUIView)
|
|
676
|
-
{
|
|
677
|
-
view.authToken = [RCTConvert NSString:json];
|
|
678
|
-
}
|
|
679
|
-
|
|
680
593
|
// Media options property
|
|
681
594
|
RCT_CUSTOM_VIEW_PROPERTY(mediaOptions, NSArray, UnifiedPlayerUIView)
|
|
682
595
|
{
|
|
@@ -689,4 +602,15 @@ RCT_CUSTOM_VIEW_PROPERTY(isPaused, BOOL, UnifiedPlayerUIView)
|
|
|
689
602
|
view.isPaused = [RCTConvert BOOL:json];
|
|
690
603
|
}
|
|
691
604
|
|
|
605
|
+
// Event handlers
|
|
606
|
+
RCT_EXPORT_VIEW_PROPERTY(onLoadStart, RCTDirectEventBlock);
|
|
607
|
+
RCT_EXPORT_VIEW_PROPERTY(onReadyToPlay, RCTDirectEventBlock);
|
|
608
|
+
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock);
|
|
609
|
+
RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock);
|
|
610
|
+
RCT_EXPORT_VIEW_PROPERTY(onPlaybackComplete, RCTDirectEventBlock);
|
|
611
|
+
RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTDirectEventBlock);
|
|
612
|
+
RCT_EXPORT_VIEW_PROPERTY(onPlaybackResumed, RCTDirectEventBlock);
|
|
613
|
+
RCT_EXPORT_VIEW_PROPERTY(onPlaying, RCTDirectEventBlock);
|
|
614
|
+
RCT_EXPORT_VIEW_PROPERTY(onPaused, RCTDirectEventBlock);
|
|
615
|
+
|
|
692
616
|
@end
|
package/lib/module/index.js
CHANGED
|
@@ -27,14 +27,20 @@ const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
|
|
|
27
27
|
|
|
28
28
|
// Export event types for reference
|
|
29
29
|
export const UnifiedPlayerEventTypes = {
|
|
30
|
+
LOAD_START: 'onLoadStart',
|
|
30
31
|
READY: 'onReadyToPlay',
|
|
31
32
|
ERROR: 'onError',
|
|
32
33
|
PROGRESS: 'onProgress',
|
|
33
34
|
COMPLETE: 'onPlaybackComplete',
|
|
34
35
|
STALLED: 'onPlaybackStalled',
|
|
35
|
-
RESUMED: 'onPlaybackResumed'
|
|
36
|
+
RESUMED: 'onPlaybackResumed',
|
|
37
|
+
PLAYING: 'onPlaying',
|
|
38
|
+
PAUSED: 'onPaused'
|
|
36
39
|
};
|
|
37
40
|
|
|
41
|
+
// Export events emitter for event listeners
|
|
42
|
+
export const UnifiedPlayerEvents = NativeModules.UnifiedPlayer;
|
|
43
|
+
|
|
38
44
|
/**
|
|
39
45
|
* UnifiedPlayerView component for video playback
|
|
40
46
|
*/
|
|
@@ -52,41 +58,62 @@ export const UnifiedPlayer = {
|
|
|
52
58
|
/**
|
|
53
59
|
* Start playback
|
|
54
60
|
* @param viewTag - The tag of the player view
|
|
61
|
+
* @returns Promise resolving to true if successful
|
|
55
62
|
*/
|
|
56
63
|
play: viewTag => {
|
|
57
64
|
try {
|
|
58
65
|
console.log('UnifiedPlayer.play called with viewTag:', viewTag);
|
|
59
|
-
UnifiedPlayerModule.play(viewTag)
|
|
60
|
-
|
|
66
|
+
return UnifiedPlayerModule.play(viewTag).then(result => {
|
|
67
|
+
console.log('Native play method called successfully');
|
|
68
|
+
return result;
|
|
69
|
+
}).catch(error => {
|
|
70
|
+
console.log('Error calling play:', error instanceof Error ? error.message : String(error));
|
|
71
|
+
throw error;
|
|
72
|
+
});
|
|
61
73
|
} catch (error) {
|
|
62
74
|
console.log('Error calling play:', error instanceof Error ? error.message : String(error));
|
|
75
|
+
return Promise.reject(error);
|
|
63
76
|
}
|
|
64
77
|
},
|
|
65
78
|
/**
|
|
66
79
|
* Pause playback
|
|
67
80
|
* @param viewTag - The tag of the player view
|
|
81
|
+
* @returns Promise resolving to true if successful
|
|
68
82
|
*/
|
|
69
83
|
pause: viewTag => {
|
|
70
84
|
try {
|
|
71
85
|
console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
|
|
72
|
-
UnifiedPlayerModule.pause(viewTag)
|
|
73
|
-
|
|
86
|
+
return UnifiedPlayerModule.pause(viewTag).then(result => {
|
|
87
|
+
console.log('Native pause method called successfully');
|
|
88
|
+
return result;
|
|
89
|
+
}).catch(error => {
|
|
90
|
+
console.log('Error calling pause:', error instanceof Error ? error.message : String(error));
|
|
91
|
+
throw error;
|
|
92
|
+
});
|
|
74
93
|
} catch (error) {
|
|
75
94
|
console.log('Error calling pause:', error instanceof Error ? error.message : String(error));
|
|
95
|
+
return Promise.reject(error);
|
|
76
96
|
}
|
|
77
97
|
},
|
|
78
98
|
/**
|
|
79
99
|
* Seek to a specific time
|
|
80
100
|
* @param viewTag - The tag of the player view
|
|
81
101
|
* @param time - Time in seconds to seek to
|
|
102
|
+
* @returns Promise resolving to true if successful
|
|
82
103
|
*/
|
|
83
104
|
seekTo: (viewTag, time) => {
|
|
84
105
|
try {
|
|
85
106
|
console.log('UnifiedPlayer.seekTo called with viewTag:', viewTag, 'time:', time);
|
|
86
|
-
UnifiedPlayerModule.seekTo(viewTag, time)
|
|
87
|
-
|
|
107
|
+
return UnifiedPlayerModule.seekTo(viewTag, time).then(result => {
|
|
108
|
+
console.log('Native seekTo method called successfully');
|
|
109
|
+
return result;
|
|
110
|
+
}).catch(error => {
|
|
111
|
+
console.log('Error calling seekTo:', error instanceof Error ? error.message : String(error));
|
|
112
|
+
throw error;
|
|
113
|
+
});
|
|
88
114
|
} catch (error) {
|
|
89
115
|
console.log('Error calling seekTo:', error instanceof Error ? error.message : String(error));
|
|
116
|
+
return Promise.reject(error);
|
|
90
117
|
}
|
|
91
118
|
},
|
|
92
119
|
/**
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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","
|
|
1
|
+
{"version":3,"names":["forwardRef","requireNativeComponent","UIManager","NativeModules","Platform","jsx","_jsx","LINKING_ERROR","select","ios","default","getViewManagerConfig","UnifiedPlayer","Error","NativeUnifiedPlayerView","UnifiedPlayerModule","UnifiedPlayerEventTypes","LOAD_START","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED","PLAYING","PAUSED","UnifiedPlayerEvents","UnifiedPlayerView","props","ref","play","viewTag","console","log","then","result","catch","error","message","String","Promise","reject","pause","seekTo","time","getCurrentTime","getDuration"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,6 +5,7 @@ export type UnifiedPlayerProps = {
|
|
|
5
5
|
autoplay?: boolean;
|
|
6
6
|
loop?: boolean;
|
|
7
7
|
isPaused?: boolean;
|
|
8
|
+
onLoadStart?: () => void;
|
|
8
9
|
onReadyToPlay?: () => void;
|
|
9
10
|
onError?: (error: any) => void;
|
|
10
11
|
onPlaybackComplete?: () => void;
|
|
@@ -12,15 +13,23 @@ export type UnifiedPlayerProps = {
|
|
|
12
13
|
currentTime: number;
|
|
13
14
|
duration: number;
|
|
14
15
|
}) => void;
|
|
16
|
+
onPlaybackStalled?: () => void;
|
|
17
|
+
onPlaybackResumed?: () => void;
|
|
18
|
+
onPaused?: () => void;
|
|
19
|
+
onPlaying?: () => void;
|
|
15
20
|
};
|
|
16
21
|
export declare const UnifiedPlayerEventTypes: {
|
|
22
|
+
LOAD_START: string;
|
|
17
23
|
READY: string;
|
|
18
24
|
ERROR: string;
|
|
19
25
|
PROGRESS: string;
|
|
20
26
|
COMPLETE: string;
|
|
21
27
|
STALLED: string;
|
|
22
28
|
RESUMED: string;
|
|
29
|
+
PLAYING: string;
|
|
30
|
+
PAUSED: string;
|
|
23
31
|
};
|
|
32
|
+
export declare const UnifiedPlayerEvents: any;
|
|
24
33
|
/**
|
|
25
34
|
* UnifiedPlayerView component for video playback
|
|
26
35
|
*/
|
|
@@ -32,19 +41,22 @@ export declare const UnifiedPlayer: {
|
|
|
32
41
|
/**
|
|
33
42
|
* Start playback
|
|
34
43
|
* @param viewTag - The tag of the player view
|
|
44
|
+
* @returns Promise resolving to true if successful
|
|
35
45
|
*/
|
|
36
|
-
play: (viewTag: number) =>
|
|
46
|
+
play: (viewTag: number) => Promise<boolean>;
|
|
37
47
|
/**
|
|
38
48
|
* Pause playback
|
|
39
49
|
* @param viewTag - The tag of the player view
|
|
50
|
+
* @returns Promise resolving to true if successful
|
|
40
51
|
*/
|
|
41
|
-
pause: (viewTag: number) =>
|
|
52
|
+
pause: (viewTag: number) => Promise<boolean>;
|
|
42
53
|
/**
|
|
43
54
|
* Seek to a specific time
|
|
44
55
|
* @param viewTag - The tag of the player view
|
|
45
56
|
* @param time - Time in seconds to seek to
|
|
57
|
+
* @returns Promise resolving to true if successful
|
|
46
58
|
*/
|
|
47
|
-
seekTo: (viewTag: number, time: number) =>
|
|
59
|
+
seekTo: (viewTag: number, time: number) => Promise<boolean>;
|
|
48
60
|
/**
|
|
49
61
|
* Get current playback time
|
|
50
62
|
* @param viewTag - The tag of the player view
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAGzB,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;IAGvE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAG/B,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAG/B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAGtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAYF,eAAO,MAAM,uBAAuB;;;;;;;;;;CAUnC,CAAC;AAGF,eAAO,MAAM,mBAAmB,KAA8B,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,iBAAiB,8LAK5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;IACxB;;;;OAIG;oBACa,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;IAwBzC;;;;OAIG;qBACc,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;IAwB1C;;;;;OAKG;sBACe,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;IA6BzD;;;;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
|
@@ -39,6 +39,9 @@ export type UnifiedPlayerProps = {
|
|
|
39
39
|
// Is the player currently paused
|
|
40
40
|
isPaused?: boolean;
|
|
41
41
|
|
|
42
|
+
// Callback when video begins loading
|
|
43
|
+
onLoadStart?: () => void;
|
|
44
|
+
|
|
42
45
|
// Callback when video is ready to play
|
|
43
46
|
onReadyToPlay?: () => void;
|
|
44
47
|
|
|
@@ -50,6 +53,18 @@ export type UnifiedPlayerProps = {
|
|
|
50
53
|
|
|
51
54
|
// Callback for playback progress
|
|
52
55
|
onProgress?: (data: { currentTime: number; duration: number }) => void;
|
|
56
|
+
|
|
57
|
+
// Callback when playback is stalled (buffering)
|
|
58
|
+
onPlaybackStalled?: () => void;
|
|
59
|
+
|
|
60
|
+
// Callback when playback resumes after stalling
|
|
61
|
+
onPlaybackResumed?: () => void;
|
|
62
|
+
|
|
63
|
+
// Callback when playback is paused
|
|
64
|
+
onPaused?: () => void;
|
|
65
|
+
|
|
66
|
+
// Callback when playback is playing
|
|
67
|
+
onPlaying?: () => void;
|
|
53
68
|
};
|
|
54
69
|
|
|
55
70
|
// Native component registration
|
|
@@ -63,14 +78,20 @@ const UnifiedPlayerModule = NativeModules.UnifiedPlayer;
|
|
|
63
78
|
|
|
64
79
|
// Export event types for reference
|
|
65
80
|
export const UnifiedPlayerEventTypes = {
|
|
81
|
+
LOAD_START: 'onLoadStart',
|
|
66
82
|
READY: 'onReadyToPlay',
|
|
67
83
|
ERROR: 'onError',
|
|
68
84
|
PROGRESS: 'onProgress',
|
|
69
85
|
COMPLETE: 'onPlaybackComplete',
|
|
70
86
|
STALLED: 'onPlaybackStalled',
|
|
71
87
|
RESUMED: 'onPlaybackResumed',
|
|
88
|
+
PLAYING: 'onPlaying',
|
|
89
|
+
PAUSED: 'onPaused',
|
|
72
90
|
};
|
|
73
91
|
|
|
92
|
+
// Export events emitter for event listeners
|
|
93
|
+
export const UnifiedPlayerEvents = NativeModules.UnifiedPlayer;
|
|
94
|
+
|
|
74
95
|
/**
|
|
75
96
|
* UnifiedPlayerView component for video playback
|
|
76
97
|
*/
|
|
@@ -88,34 +109,58 @@ export const UnifiedPlayer = {
|
|
|
88
109
|
/**
|
|
89
110
|
* Start playback
|
|
90
111
|
* @param viewTag - The tag of the player view
|
|
112
|
+
* @returns Promise resolving to true if successful
|
|
91
113
|
*/
|
|
92
|
-
play: (viewTag: number):
|
|
114
|
+
play: (viewTag: number): Promise<boolean> => {
|
|
93
115
|
try {
|
|
94
116
|
console.log('UnifiedPlayer.play called with viewTag:', viewTag);
|
|
95
|
-
UnifiedPlayerModule.play(viewTag)
|
|
96
|
-
|
|
117
|
+
return UnifiedPlayerModule.play(viewTag)
|
|
118
|
+
.then((result: boolean) => {
|
|
119
|
+
console.log('Native play method called successfully');
|
|
120
|
+
return result;
|
|
121
|
+
})
|
|
122
|
+
.catch((error: any) => {
|
|
123
|
+
console.log(
|
|
124
|
+
'Error calling play:',
|
|
125
|
+
error instanceof Error ? error.message : String(error)
|
|
126
|
+
);
|
|
127
|
+
throw error;
|
|
128
|
+
});
|
|
97
129
|
} catch (error) {
|
|
98
130
|
console.log(
|
|
99
131
|
'Error calling play:',
|
|
100
132
|
error instanceof Error ? error.message : String(error)
|
|
101
133
|
);
|
|
134
|
+
return Promise.reject(error);
|
|
102
135
|
}
|
|
103
136
|
},
|
|
104
137
|
|
|
105
138
|
/**
|
|
106
139
|
* Pause playback
|
|
107
140
|
* @param viewTag - The tag of the player view
|
|
141
|
+
* @returns Promise resolving to true if successful
|
|
108
142
|
*/
|
|
109
|
-
pause: (viewTag: number):
|
|
143
|
+
pause: (viewTag: number): Promise<boolean> => {
|
|
110
144
|
try {
|
|
111
145
|
console.log('UnifiedPlayer.pause called with viewTag:', viewTag);
|
|
112
|
-
UnifiedPlayerModule.pause(viewTag)
|
|
113
|
-
|
|
146
|
+
return UnifiedPlayerModule.pause(viewTag)
|
|
147
|
+
.then((result: boolean) => {
|
|
148
|
+
console.log('Native pause method called successfully');
|
|
149
|
+
return result;
|
|
150
|
+
})
|
|
151
|
+
.catch((error: any) => {
|
|
152
|
+
console.log(
|
|
153
|
+
'Error calling pause:',
|
|
154
|
+
error instanceof Error ? error.message : String(error)
|
|
155
|
+
);
|
|
156
|
+
throw error;
|
|
157
|
+
});
|
|
114
158
|
} catch (error) {
|
|
115
159
|
console.log(
|
|
116
160
|
'Error calling pause:',
|
|
117
161
|
error instanceof Error ? error.message : String(error)
|
|
118
162
|
);
|
|
163
|
+
return Promise.reject(error);
|
|
119
164
|
}
|
|
120
165
|
},
|
|
121
166
|
|
|
@@ -123,8 +168,9 @@ export const UnifiedPlayer = {
|
|
|
123
168
|
* Seek to a specific time
|
|
124
169
|
* @param viewTag - The tag of the player view
|
|
125
170
|
* @param time - Time in seconds to seek to
|
|
171
|
+
* @returns Promise resolving to true if successful
|
|
126
172
|
*/
|
|
127
|
-
seekTo: (viewTag: number, time: number):
|
|
173
|
+
seekTo: (viewTag: number, time: number): Promise<boolean> => {
|
|
128
174
|
try {
|
|
129
175
|
console.log(
|
|
130
176
|
'UnifiedPlayer.seekTo called with viewTag:',
|
|
@@ -132,13 +178,24 @@ export const UnifiedPlayer = {
|
|
|
132
178
|
'time:',
|
|
133
179
|
time
|
|
134
180
|
);
|
|
135
|
-
UnifiedPlayerModule.seekTo(viewTag, time)
|
|
136
|
-
|
|
181
|
+
return UnifiedPlayerModule.seekTo(viewTag, time)
|
|
182
|
+
.then((result: boolean) => {
|
|
183
|
+
console.log('Native seekTo method called successfully');
|
|
184
|
+
return result;
|
|
185
|
+
})
|
|
186
|
+
.catch((error: any) => {
|
|
187
|
+
console.log(
|
|
188
|
+
'Error calling seekTo:',
|
|
189
|
+
error instanceof Error ? error.message : String(error)
|
|
190
|
+
);
|
|
191
|
+
throw error;
|
|
192
|
+
});
|
|
137
193
|
} catch (error) {
|
|
138
194
|
console.log(
|
|
139
195
|
'Error calling seekTo:',
|
|
140
196
|
error instanceof Error ? error.message : String(error)
|
|
141
197
|
);
|
|
198
|
+
return Promise.reject(error);
|
|
142
199
|
}
|
|
143
200
|
},
|
|
144
201
|
|