react-native-unified-player 0.6.1 → 0.6.5
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/src/main/java/com/unifiedplayer/UnifiedPlayerView.kt +23 -3
- package/ios/UnifiedPlayerViewManager.m +297 -22
- package/lib/module/index.js +88 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +6 -2
- package/src/index.tsx +150 -4
|
@@ -57,6 +57,7 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
57
57
|
private var thumbnailUrl: String? = null
|
|
58
58
|
private var autoplay: Boolean = true
|
|
59
59
|
private var loop: Boolean = false
|
|
60
|
+
private var speed: Float = 1.0f // Default playback speed
|
|
60
61
|
private var textureView: android.view.TextureView
|
|
61
62
|
private var thumbnailImageView: ImageView? = null
|
|
62
63
|
internal var player: ExoPlayer? = null
|
|
@@ -157,6 +158,11 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
157
158
|
sendEvent(EVENT_COMPLETE, Arguments.createMap())
|
|
158
159
|
} else {
|
|
159
160
|
Log.d(TAG, "Looping enabled, not sending completion event")
|
|
161
|
+
// Ensure playback speed is preserved after loop
|
|
162
|
+
if (speed != 1.0f && player != null) {
|
|
163
|
+
player?.setPlaybackSpeed(speed)
|
|
164
|
+
Log.d(TAG, "Restored playback speed: $speed after loop")
|
|
165
|
+
}
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
Player.STATE_BUFFERING -> {
|
|
@@ -175,6 +181,11 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
175
181
|
Log.d(TAG, "ExoPlayer is now playing")
|
|
176
182
|
// Hide thumbnail when video starts playing
|
|
177
183
|
thumbnailImageView?.visibility = View.GONE
|
|
184
|
+
// Ensure playback speed is preserved when playback resumes (e.g., after loop)
|
|
185
|
+
if (speed != 1.0f) {
|
|
186
|
+
player?.setPlaybackSpeed(speed)
|
|
187
|
+
Log.d(TAG, "Restored playback speed: $speed when playback resumed")
|
|
188
|
+
}
|
|
178
189
|
sendEvent(EVENT_RESUMED, Arguments.createMap())
|
|
179
190
|
sendEvent(EVENT_PLAYING, Arguments.createMap())
|
|
180
191
|
} else {
|
|
@@ -248,8 +259,12 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
248
259
|
player?.prepare()
|
|
249
260
|
player?.playWhenReady = autoplay && !isPaused
|
|
250
261
|
player?.repeatMode = if (loop) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
|
|
262
|
+
// Apply playback speed if set
|
|
263
|
+
if (speed != 1.0f) {
|
|
264
|
+
player?.setPlaybackSpeed(speed)
|
|
265
|
+
}
|
|
251
266
|
|
|
252
|
-
Log.d(TAG, "ExoPlayer configured with URL: $url, autoplay: $autoplay, loop: $loop, isPaused: $isPaused")
|
|
267
|
+
Log.d(TAG, "ExoPlayer configured with URL: $url, autoplay: $autoplay, loop: $loop, isPaused: $isPaused, speed: $speed")
|
|
253
268
|
sendEvent(EVENT_LOAD_START, Arguments.createMap())
|
|
254
269
|
|
|
255
270
|
} catch (e: Exception) {
|
|
@@ -436,6 +451,10 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
436
451
|
fun play() {
|
|
437
452
|
Log.d(TAG, "Play method called")
|
|
438
453
|
player?.playWhenReady = true
|
|
454
|
+
// Ensure playback speed is applied when playing
|
|
455
|
+
if (speed != 1.0f) {
|
|
456
|
+
player?.setPlaybackSpeed(speed)
|
|
457
|
+
}
|
|
439
458
|
}
|
|
440
459
|
|
|
441
460
|
fun pause() {
|
|
@@ -455,8 +474,9 @@ class UnifiedPlayerView(context: Context) : FrameLayout(context) {
|
|
|
455
474
|
} ?: Log.e(TAG, "Cannot seek: player is null")
|
|
456
475
|
}
|
|
457
476
|
|
|
458
|
-
fun setSpeed(
|
|
459
|
-
Log.d(TAG, "SetSpeed method called with speed: $
|
|
477
|
+
fun setSpeed(speedValue: Float) {
|
|
478
|
+
Log.d(TAG, "SetSpeed method called with speed: $speedValue")
|
|
479
|
+
speed = speedValue // Store the speed value
|
|
460
480
|
player?.let {
|
|
461
481
|
it.setPlaybackSpeed(speed)
|
|
462
482
|
Log.d(TAG, "Playback speed set to: $speed")
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#import <React/RCTComponent.h>
|
|
9
9
|
#import <React/RCTConvert.h>
|
|
10
10
|
#import <AVFoundation/AVFoundation.h>
|
|
11
|
+
#import <UIKit/UIKit.h>
|
|
11
12
|
#import <objc/runtime.h>
|
|
12
13
|
#import "UnifiedPlayerModule.h"
|
|
13
14
|
#import "UnifiedPlayerUIView.h"
|
|
@@ -19,6 +20,8 @@
|
|
|
19
20
|
UIView *_originalSuperview;
|
|
20
21
|
NSUInteger _originalIndex;
|
|
21
22
|
UIView *_fullscreenContainer;
|
|
23
|
+
UIViewController *_fullscreenViewController;
|
|
24
|
+
UIButton *_fullscreenCloseButton;
|
|
22
25
|
float _cachedDuration;
|
|
23
26
|
BOOL _isObservingPlayerItem;
|
|
24
27
|
}
|
|
@@ -62,13 +65,13 @@
|
|
|
62
65
|
_autoplay = YES;
|
|
63
66
|
_loop = NO;
|
|
64
67
|
_isFullscreen = NO;
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
// Add notification observers
|
|
67
70
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
68
71
|
selector:@selector(appDidEnterBackground:)
|
|
69
72
|
name:UIApplicationDidEnterBackgroundNotification
|
|
70
73
|
object:nil];
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
73
76
|
selector:@selector(appDidBecomeActive:)
|
|
74
77
|
name:UIApplicationDidBecomeActiveNotification
|
|
@@ -121,8 +124,8 @@
|
|
|
121
124
|
- (void)sendProgressEvent:(float)currentTime duration:(float)duration {
|
|
122
125
|
if (self.onProgress) {
|
|
123
126
|
self.onProgress(@{
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
@"currentTime": @(currentTime),
|
|
128
|
+
@"duration": @(duration)
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
131
|
}
|
|
@@ -394,15 +397,20 @@
|
|
|
394
397
|
|
|
395
398
|
- (void)play {
|
|
396
399
|
if (_playerItem && _playerItem.status == AVPlayerItemStatusReadyToPlay) {
|
|
397
|
-
// Apply speed if set
|
|
398
|
-
if (_speed > 0 && _speed != 1.0f) {
|
|
399
|
-
float validSpeed = MAX(0.25f, MIN(4.0f, _speed));
|
|
400
|
-
_player.rate = validSpeed;
|
|
401
|
-
} else {
|
|
402
|
-
_player.rate = 1.0f;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
400
|
[_player play];
|
|
401
|
+
|
|
402
|
+
// Apply speed AFTER play() is called to ensure it's not reset
|
|
403
|
+
// Use a small delay to ensure play() has started
|
|
404
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
405
|
+
if (self->_speed > 0 && self->_speed != 1.0f) {
|
|
406
|
+
float validSpeed = MAX(0.25f, MIN(4.0f, self->_speed));
|
|
407
|
+
self->_player.rate = validSpeed;
|
|
408
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Applied speed: %f after play", validSpeed);
|
|
409
|
+
} else {
|
|
410
|
+
self->_player.rate = 1.0f;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
406
414
|
[self sendEvent:@"onPlaying" body:@{}];
|
|
407
415
|
RCTLogInfo(@"[UnifiedPlayerViewManager] play called");
|
|
408
416
|
} else {
|
|
@@ -490,13 +498,33 @@
|
|
|
490
498
|
#pragma mark - Notifications
|
|
491
499
|
|
|
492
500
|
- (void)playerItemDidReachEnd:(NSNotification *)notification {
|
|
493
|
-
RCTLogInfo(@"[UnifiedPlayerViewManager] Video ended, loop:
|
|
494
|
-
|
|
501
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Video ended, loop: %@, speed: %f", _loop ? @"YES" : @"NO", _speed);
|
|
502
|
+
|
|
495
503
|
if (_loop) {
|
|
504
|
+
// Store current speed before seeking
|
|
505
|
+
float savedSpeed = _speed;
|
|
506
|
+
|
|
496
507
|
// Seek to beginning and play again
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
508
|
+
[_player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
|
|
509
|
+
if (finished) {
|
|
510
|
+
[self->_player play];
|
|
511
|
+
// Restore playback speed AFTER play() is called
|
|
512
|
+
// Use multiple attempts to ensure speed is set
|
|
513
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
514
|
+
if (savedSpeed > 0 && savedSpeed != 1.0f) {
|
|
515
|
+
float validSpeed = MAX(0.25f, MIN(4.0f, savedSpeed));
|
|
516
|
+
self->_player.rate = validSpeed;
|
|
517
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Restored speed: %f for loop (attempt 1)", validSpeed);
|
|
518
|
+
|
|
519
|
+
// Double-check after a bit more time
|
|
520
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
521
|
+
if (self->_player.rate != validSpeed && self->_player.rate > 0) {
|
|
522
|
+
self->_player.rate = validSpeed;
|
|
523
|
+
RCTLogInfo(@"[UnifiedPlayerViewManager] Restored speed: %f for loop (attempt 2)", validSpeed);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
});
|
|
500
528
|
RCTLogInfo(@"[UnifiedPlayerViewManager] Looped video - restarted from beginning");
|
|
501
529
|
}
|
|
502
530
|
}];
|
|
@@ -519,11 +547,258 @@
|
|
|
519
547
|
#pragma mark - Fullscreen
|
|
520
548
|
|
|
521
549
|
- (void)toggleFullscreen:(BOOL)fullscreen {
|
|
522
|
-
|
|
523
|
-
|
|
550
|
+
if (_isFullscreen == fullscreen) {
|
|
551
|
+
return; // Already in the requested state
|
|
552
|
+
}
|
|
553
|
+
|
|
524
554
|
_isFullscreen = fullscreen;
|
|
525
|
-
|
|
526
|
-
|
|
555
|
+
|
|
556
|
+
// Get root view controller
|
|
557
|
+
UIViewController *rootViewController = nil;
|
|
558
|
+
UIWindow *window = nil;
|
|
559
|
+
|
|
560
|
+
// Get key window - try modern API first (iOS 13+)
|
|
561
|
+
if (@available(iOS 13.0, *)) {
|
|
562
|
+
NSSet<UIScene *> *connectedScenes = [[UIApplication sharedApplication] connectedScenes];
|
|
563
|
+
for (UIScene *scene in connectedScenes) {
|
|
564
|
+
if ([scene isKindOfClass:[UIWindowScene class]]) {
|
|
565
|
+
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
566
|
+
for (UIWindow *w in windowScene.windows) {
|
|
567
|
+
if (w.isKeyWindow) {
|
|
568
|
+
window = w;
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (window) break;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Fallback: use deprecated keyWindow (iOS 12 and earlier, or if no scene found)
|
|
578
|
+
if (!window) {
|
|
579
|
+
#pragma clang diagnostic push
|
|
580
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
581
|
+
window = [[UIApplication sharedApplication] keyWindow];
|
|
582
|
+
#pragma clang diagnostic pop
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!window) {
|
|
586
|
+
RCTLogError(@"[UnifiedPlayerViewManager] Could not find window for fullscreen");
|
|
587
|
+
if (self.onFullscreenChanged) {
|
|
588
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(NO)});
|
|
589
|
+
}
|
|
590
|
+
_isFullscreen = NO;
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
rootViewController = window.rootViewController;
|
|
595
|
+
|
|
596
|
+
// Navigate to the topmost view controller
|
|
597
|
+
while (rootViewController.presentedViewController) {
|
|
598
|
+
rootViewController = rootViewController.presentedViewController;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (!rootViewController) {
|
|
602
|
+
RCTLogError(@"[UnifiedPlayerViewManager] Could not find root view controller for fullscreen");
|
|
603
|
+
if (self.onFullscreenChanged) {
|
|
604
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(NO)});
|
|
605
|
+
}
|
|
606
|
+
_isFullscreen = NO;
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (fullscreen) {
|
|
611
|
+
// Enter fullscreen
|
|
612
|
+
[self enterFullscreenWithViewController:rootViewController];
|
|
613
|
+
} else {
|
|
614
|
+
// Exit fullscreen
|
|
615
|
+
[self exitFullscreen];
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
- (void)enterFullscreenWithViewController:(UIViewController *)rootViewController {
|
|
620
|
+
// Save original frame and superview
|
|
621
|
+
_originalFrame = self.frame;
|
|
622
|
+
_originalSuperview = self.superview;
|
|
623
|
+
if (_originalSuperview) {
|
|
624
|
+
_originalIndex = [_originalSuperview.subviews indexOfObject:self];
|
|
625
|
+
} else {
|
|
626
|
+
_originalIndex = NSNotFound;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Create fullscreen container view controller
|
|
630
|
+
_fullscreenViewController = [[UIViewController alloc] init];
|
|
631
|
+
_fullscreenViewController.view.backgroundColor = [UIColor blackColor];
|
|
632
|
+
_fullscreenViewController.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
633
|
+
|
|
634
|
+
// Create container view for the player
|
|
635
|
+
_fullscreenContainer = [[UIView alloc] initWithFrame:rootViewController.view.bounds];
|
|
636
|
+
_fullscreenContainer.backgroundColor = [UIColor blackColor];
|
|
637
|
+
[_fullscreenViewController.view addSubview:_fullscreenContainer];
|
|
638
|
+
|
|
639
|
+
// Move player to fullscreen container
|
|
640
|
+
CGRect screenBounds = [[UIScreen mainScreen] bounds];
|
|
641
|
+
self.frame = screenBounds;
|
|
642
|
+
[_fullscreenContainer addSubview:self];
|
|
643
|
+
|
|
644
|
+
// Update player layer frame
|
|
645
|
+
_playerLayer.frame = screenBounds;
|
|
646
|
+
|
|
647
|
+
// Create close button
|
|
648
|
+
_fullscreenCloseButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
649
|
+
_fullscreenCloseButton.translatesAutoresizingMaskIntoConstraints = NO;
|
|
650
|
+
|
|
651
|
+
// Create X icon using SF Symbols (iOS 13+) or fallback to text
|
|
652
|
+
if (@available(iOS 13.0, *)) {
|
|
653
|
+
UIImage *closeImage = [UIImage systemImageNamed:@"xmark.circle.fill"];
|
|
654
|
+
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium];
|
|
655
|
+
closeImage = [closeImage imageByApplyingSymbolConfiguration:config];
|
|
656
|
+
[_fullscreenCloseButton setImage:closeImage forState:UIControlStateNormal];
|
|
657
|
+
_fullscreenCloseButton.tintColor = [UIColor whiteColor];
|
|
658
|
+
} else {
|
|
659
|
+
// Fallback for iOS 12
|
|
660
|
+
[_fullscreenCloseButton setTitle:@"✕" forState:UIControlStateNormal];
|
|
661
|
+
_fullscreenCloseButton.titleLabel.font = [UIFont boldSystemFontOfSize:24];
|
|
662
|
+
[_fullscreenCloseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Style the button with semi-transparent background
|
|
666
|
+
_fullscreenCloseButton.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
|
667
|
+
_fullscreenCloseButton.layer.cornerRadius = 22;
|
|
668
|
+
_fullscreenCloseButton.clipsToBounds = YES;
|
|
669
|
+
|
|
670
|
+
// Add action
|
|
671
|
+
[_fullscreenCloseButton addTarget:self action:@selector(closeButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
|
672
|
+
|
|
673
|
+
// Add to fullscreen container
|
|
674
|
+
[_fullscreenContainer addSubview:_fullscreenCloseButton];
|
|
675
|
+
|
|
676
|
+
// Position in top-right corner with safe area
|
|
677
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
678
|
+
[_fullscreenCloseButton.widthAnchor constraintEqualToConstant:44],
|
|
679
|
+
[_fullscreenCloseButton.heightAnchor constraintEqualToConstant:44],
|
|
680
|
+
[_fullscreenCloseButton.topAnchor constraintEqualToAnchor:_fullscreenContainer.safeAreaLayoutGuide.topAnchor constant:16],
|
|
681
|
+
[_fullscreenCloseButton.trailingAnchor constraintEqualToAnchor:_fullscreenContainer.safeAreaLayoutGuide.trailingAnchor constant:-16]
|
|
682
|
+
]];
|
|
683
|
+
|
|
684
|
+
// Configure orientation
|
|
685
|
+
NSNumber *orientationValue = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
|
|
686
|
+
[[UIDevice currentDevice] setValue:orientationValue forKey:@"orientation"];
|
|
687
|
+
|
|
688
|
+
// Present fullscreen view controller
|
|
689
|
+
[rootViewController presentViewController:_fullscreenViewController animated:YES completion:^{
|
|
690
|
+
// Force landscape orientation
|
|
691
|
+
[UIViewController attemptRotationToDeviceOrientation];
|
|
692
|
+
|
|
693
|
+
// Send event
|
|
694
|
+
if (self.onFullscreenChanged) {
|
|
695
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(YES)});
|
|
696
|
+
}
|
|
697
|
+
}];
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
- (void)closeButtonTapped {
|
|
701
|
+
[self exitFullscreen];
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
- (void)exitFullscreen {
|
|
705
|
+
// Restore portrait orientation first
|
|
706
|
+
NSNumber *orientationValue = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
|
|
707
|
+
[[UIDevice currentDevice] setValue:orientationValue forKey:@"orientation"];
|
|
708
|
+
|
|
709
|
+
// Dismiss fullscreen view controller if we have a reference to it
|
|
710
|
+
if (_fullscreenViewController && _fullscreenViewController.presentingViewController) {
|
|
711
|
+
[_fullscreenViewController dismissViewControllerAnimated:YES completion:^{
|
|
712
|
+
// Restore player to original position
|
|
713
|
+
[self restorePlayerFromFullscreen];
|
|
714
|
+
|
|
715
|
+
// Force portrait orientation
|
|
716
|
+
[UIViewController attemptRotationToDeviceOrientation];
|
|
717
|
+
|
|
718
|
+
// Send event
|
|
719
|
+
if (self.onFullscreenChanged) {
|
|
720
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(NO)});
|
|
721
|
+
}
|
|
722
|
+
}];
|
|
723
|
+
} else {
|
|
724
|
+
// Fallback: try to find and dismiss any presented view controller
|
|
725
|
+
UIWindow *window = nil;
|
|
726
|
+
|
|
727
|
+
// Get key window - try modern API first (iOS 13+)
|
|
728
|
+
if (@available(iOS 13.0, *)) {
|
|
729
|
+
NSSet<UIScene *> *connectedScenes = [[UIApplication sharedApplication] connectedScenes];
|
|
730
|
+
for (UIScene *scene in connectedScenes) {
|
|
731
|
+
if ([scene isKindOfClass:[UIWindowScene class]]) {
|
|
732
|
+
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
733
|
+
for (UIWindow *w in windowScene.windows) {
|
|
734
|
+
if (w.isKeyWindow) {
|
|
735
|
+
window = w;
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (window) break;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Fallback: use deprecated keyWindow (iOS 12 and earlier, or if no scene found)
|
|
745
|
+
if (!window) {
|
|
746
|
+
#pragma clang diagnostic push
|
|
747
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
748
|
+
window = [[UIApplication sharedApplication] keyWindow];
|
|
749
|
+
#pragma clang diagnostic pop
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (window) {
|
|
753
|
+
UIViewController *rootVC = window.rootViewController;
|
|
754
|
+
while (rootVC.presentedViewController) {
|
|
755
|
+
rootVC = rootVC.presentedViewController;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (rootVC.presentingViewController) {
|
|
759
|
+
[rootVC dismissViewControllerAnimated:YES completion:^{
|
|
760
|
+
[self restorePlayerFromFullscreen];
|
|
761
|
+
[UIViewController attemptRotationToDeviceOrientation];
|
|
762
|
+
if (self.onFullscreenChanged) {
|
|
763
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(NO)});
|
|
764
|
+
}
|
|
765
|
+
}];
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Last resort: just restore player position
|
|
771
|
+
[self restorePlayerFromFullscreen];
|
|
772
|
+
[UIViewController attemptRotationToDeviceOrientation];
|
|
773
|
+
if (self.onFullscreenChanged) {
|
|
774
|
+
self.onFullscreenChanged(@{@"isFullscreen": @(NO)});
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
- (void)restorePlayerFromFullscreen {
|
|
780
|
+
if (_originalSuperview && _originalIndex != NSNotFound) {
|
|
781
|
+
// Restore original frame
|
|
782
|
+
self.frame = _originalFrame;
|
|
783
|
+
|
|
784
|
+
// Move back to original superview
|
|
785
|
+
[self removeFromSuperview];
|
|
786
|
+
|
|
787
|
+
// Insert at original index
|
|
788
|
+
if (_originalIndex < _originalSuperview.subviews.count) {
|
|
789
|
+
[_originalSuperview insertSubview:self atIndex:_originalIndex];
|
|
790
|
+
} else {
|
|
791
|
+
[_originalSuperview addSubview:self];
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Update player layer frame
|
|
795
|
+
_playerLayer.frame = self.bounds;
|
|
796
|
+
|
|
797
|
+
// Clean up
|
|
798
|
+
_originalSuperview = nil;
|
|
799
|
+
_fullscreenContainer = nil;
|
|
800
|
+
_fullscreenViewController = nil;
|
|
801
|
+
_fullscreenCloseButton = nil;
|
|
527
802
|
}
|
|
528
803
|
}
|
|
529
804
|
|
|
@@ -562,7 +837,7 @@
|
|
|
562
837
|
// Generate the image
|
|
563
838
|
[imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:currentTime]]
|
|
564
839
|
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
|
|
565
|
-
|
|
840
|
+
if (error) {
|
|
566
841
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
567
842
|
completion(nil, error);
|
|
568
843
|
});
|
package/lib/module/index.js
CHANGED
|
@@ -27,6 +27,8 @@ if (!UIManager.getViewManagerConfig('UnifiedPlayerView') && !NativeModules.Unifi
|
|
|
27
27
|
* Props for the UnifiedPlayerView component
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
// Internal type for native component props (accepts native event objects)
|
|
31
|
+
|
|
30
32
|
// Native component registration
|
|
31
33
|
const NativeUnifiedPlayerView = requireNativeComponent('UnifiedPlayerView');
|
|
32
34
|
|
|
@@ -76,6 +78,20 @@ export const UnifiedPlayerEventTypes = {
|
|
|
76
78
|
* ```
|
|
77
79
|
*/
|
|
78
80
|
export const UnifiedPlayerView = /*#__PURE__*/forwardRef((props, ref) => {
|
|
81
|
+
// Destructure all event handlers at the top to satisfy ESLint exhaustive-deps
|
|
82
|
+
const {
|
|
83
|
+
onLoadStart,
|
|
84
|
+
onReadyToPlay,
|
|
85
|
+
onError,
|
|
86
|
+
onProgress,
|
|
87
|
+
onPlaybackComplete,
|
|
88
|
+
onPlaybackStalled,
|
|
89
|
+
onPlaybackResumed,
|
|
90
|
+
onPaused,
|
|
91
|
+
onPlaying,
|
|
92
|
+
onFullscreenChanged,
|
|
93
|
+
...restProps
|
|
94
|
+
} = props;
|
|
79
95
|
const nativeRef = useRef(null);
|
|
80
96
|
const getViewTag = useCallback(() => {
|
|
81
97
|
return findNodeHandle(nativeRef.current);
|
|
@@ -101,6 +117,61 @@ export const UnifiedPlayerView = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
101
117
|
throw error;
|
|
102
118
|
}
|
|
103
119
|
}, [getViewTag]);
|
|
120
|
+
|
|
121
|
+
// Wrap event handlers to extract data immediately and avoid event pooling warnings
|
|
122
|
+
// Must extract nativeEvent data synchronously before React recycles the event
|
|
123
|
+
const wrappedOnProgress = useCallback(event => {
|
|
124
|
+
if (!onProgress) return;
|
|
125
|
+
// Extract data synchronously - access nativeEvent immediately
|
|
126
|
+
const nativeEvent = event?.nativeEvent;
|
|
127
|
+
const currentTime = nativeEvent?.currentTime ?? event?.currentTime;
|
|
128
|
+
const duration = nativeEvent?.duration ?? event?.duration;
|
|
129
|
+
onProgress({
|
|
130
|
+
currentTime,
|
|
131
|
+
duration
|
|
132
|
+
});
|
|
133
|
+
}, [onProgress]);
|
|
134
|
+
const wrappedOnFullscreenChanged = useCallback(event => {
|
|
135
|
+
if (!onFullscreenChanged) return;
|
|
136
|
+
// Extract data synchronously - access nativeEvent immediately
|
|
137
|
+
const nativeEvent = event?.nativeEvent;
|
|
138
|
+
const fullscreen = nativeEvent?.isFullscreen ?? event?.isFullscreen ?? false;
|
|
139
|
+
onFullscreenChanged(fullscreen);
|
|
140
|
+
}, [onFullscreenChanged]);
|
|
141
|
+
const wrappedOnError = useCallback(event => {
|
|
142
|
+
if (!onError) return;
|
|
143
|
+
// Extract error data synchronously
|
|
144
|
+
const nativeEvent = event?.nativeEvent;
|
|
145
|
+
const error = {
|
|
146
|
+
code: nativeEvent?.code ?? event?.code ?? 'UNKNOWN',
|
|
147
|
+
message: nativeEvent?.message ?? event?.message ?? 'Unknown error',
|
|
148
|
+
nativeError: nativeEvent?.nativeError ?? event?.nativeError
|
|
149
|
+
};
|
|
150
|
+
onError(error);
|
|
151
|
+
}, [onError]);
|
|
152
|
+
|
|
153
|
+
// Wrap simple event handlers to prevent synthetic event access issues
|
|
154
|
+
const wrappedOnLoadStart = useCallback(_event => {
|
|
155
|
+
onLoadStart?.();
|
|
156
|
+
}, [onLoadStart]);
|
|
157
|
+
const wrappedOnReadyToPlay = useCallback(_event => {
|
|
158
|
+
onReadyToPlay?.();
|
|
159
|
+
}, [onReadyToPlay]);
|
|
160
|
+
const wrappedOnPlaybackComplete = useCallback(_event => {
|
|
161
|
+
onPlaybackComplete?.();
|
|
162
|
+
}, [onPlaybackComplete]);
|
|
163
|
+
const wrappedOnPlaybackStalled = useCallback(_event => {
|
|
164
|
+
onPlaybackStalled?.();
|
|
165
|
+
}, [onPlaybackStalled]);
|
|
166
|
+
const wrappedOnPlaybackResumed = useCallback(_event => {
|
|
167
|
+
onPlaybackResumed?.();
|
|
168
|
+
}, [onPlaybackResumed]);
|
|
169
|
+
const wrappedOnPaused = useCallback(_event => {
|
|
170
|
+
onPaused?.();
|
|
171
|
+
}, [onPaused]);
|
|
172
|
+
const wrappedOnPlaying = useCallback(_event => {
|
|
173
|
+
onPlaying?.();
|
|
174
|
+
}, [onPlaying]);
|
|
104
175
|
useImperativeHandle(ref, () => ({
|
|
105
176
|
play: () => callNativeMethod('play', NativeModule.play),
|
|
106
177
|
pause: () => callNativeMethod('pause', NativeModule.pause),
|
|
@@ -110,11 +181,26 @@ export const UnifiedPlayerView = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
110
181
|
capture: () => callNativeMethod('capture', NativeModule.capture),
|
|
111
182
|
startRecording: outputPath => callNativeMethod('startRecording', NativeModule.startRecording, outputPath),
|
|
112
183
|
stopRecording: () => callNativeMethod('stopRecording', NativeModule.stopRecording),
|
|
113
|
-
toggleFullscreen:
|
|
184
|
+
toggleFullscreen: fullscreen => callNativeMethod('toggleFullscreen', NativeModule.toggleFullscreen, fullscreen),
|
|
114
185
|
setSpeed: speed => callNativeMethod('setSpeed', NativeModule.setSpeed, speed)
|
|
115
186
|
}), [callNativeMethod]);
|
|
187
|
+
|
|
188
|
+
// Build native props with wrapped event handlers to avoid event pooling warnings
|
|
189
|
+
const nativeProps = {
|
|
190
|
+
...restProps,
|
|
191
|
+
onLoadStart: onLoadStart ? wrappedOnLoadStart : undefined,
|
|
192
|
+
onReadyToPlay: onReadyToPlay ? wrappedOnReadyToPlay : undefined,
|
|
193
|
+
onError: onError ? wrappedOnError : undefined,
|
|
194
|
+
onProgress: onProgress ? wrappedOnProgress : undefined,
|
|
195
|
+
onPlaybackComplete: onPlaybackComplete ? wrappedOnPlaybackComplete : undefined,
|
|
196
|
+
onPlaybackStalled: onPlaybackStalled ? wrappedOnPlaybackStalled : undefined,
|
|
197
|
+
onPlaybackResumed: onPlaybackResumed ? wrappedOnPlaybackResumed : undefined,
|
|
198
|
+
onPaused: onPaused ? wrappedOnPaused : undefined,
|
|
199
|
+
onPlaying: onPlaying ? wrappedOnPlaying : undefined,
|
|
200
|
+
onFullscreenChanged: onFullscreenChanged ? wrappedOnFullscreenChanged : undefined
|
|
201
|
+
};
|
|
116
202
|
return /*#__PURE__*/_jsx(NativeUnifiedPlayerView, {
|
|
117
|
-
...
|
|
203
|
+
...nativeProps,
|
|
118
204
|
ref: nativeRef
|
|
119
205
|
});
|
|
120
206
|
});
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useImperativeHandle","useRef","useCallback","requireNativeComponent","UIManager","NativeModules","Platform","findNodeHandle","jsx","_jsx","LINKING_ERROR","select","ios","default","getViewManagerConfig","UnifiedPlayer","Error","NativeUnifiedPlayerView","NativeModule","UnifiedPlayerEventTypes","LOAD_START","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED","PLAYING","PAUSED","UnifiedPlayerView","props","ref","nativeRef","getViewTag","current","callNativeMethod","methodName","method","args","viewTag","__DEV__","console","log","result","error","message","String","play","pause","seekTo","time","getCurrentTime","getDuration","capture","startRecording","outputPath","stopRecording","toggleFullscreen","
|
|
1
|
+
{"version":3,"names":["forwardRef","useImperativeHandle","useRef","useCallback","requireNativeComponent","UIManager","NativeModules","Platform","findNodeHandle","jsx","_jsx","LINKING_ERROR","select","ios","default","getViewManagerConfig","UnifiedPlayer","Error","NativeUnifiedPlayerView","NativeModule","UnifiedPlayerEventTypes","LOAD_START","READY","ERROR","PROGRESS","COMPLETE","STALLED","RESUMED","PLAYING","PAUSED","UnifiedPlayerView","props","ref","onLoadStart","onReadyToPlay","onError","onProgress","onPlaybackComplete","onPlaybackStalled","onPlaybackResumed","onPaused","onPlaying","onFullscreenChanged","restProps","nativeRef","getViewTag","current","callNativeMethod","methodName","method","args","viewTag","__DEV__","console","log","result","error","message","String","wrappedOnProgress","event","nativeEvent","currentTime","duration","wrappedOnFullscreenChanged","fullscreen","isFullscreen","wrappedOnError","code","nativeError","wrappedOnLoadStart","_event","wrappedOnReadyToPlay","wrappedOnPlaybackComplete","wrappedOnPlaybackStalled","wrappedOnPlaybackResumed","wrappedOnPaused","wrappedOnPlaying","play","pause","seekTo","time","getCurrentTime","getDuration","capture","startRecording","outputPath","stopRecording","toggleFullscreen","setSpeed","speed","nativeProps","undefined"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,mBAAmB,EAAEC,MAAM,EAAEC,WAAW,QAAQ,OAAO;AAC5E,SACEC,sBAAsB,EACtBC,SAAS,EACTC,aAAa,EACbC,QAAQ,EACRC,cAAc,QAET,cAAc;;AAErB;AAAA,SAAAC,GAAA,IAAAC,IAAA;AACA,MAAMC,aAAa,GACjB,sFAAsF,GACtFJ,QAAQ,CAACK,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA,IACE,CAACT,SAAS,CAACU,oBAAoB,CAAC,mBAAmB,CAAC,IACpD,CAACT,aAAa,CAACU,aAAa,EAC5B;EACA,MAAM,IAAIC,KAAK,CAACN,aAAa,CAAC;AAChC;;AAEA;AACA;AACA;;AAOA;AACA;AACA;;AA2BA;AACA;AACA;;AAsCA;;AA2BA;AACA,MAAMO,uBAAuB,GAC3Bd,sBAAsB,CAA2B,mBAAmB,CAAC;;AAEvE;AACA,MAAMe,YAAY,GAAGb,aAAa,CAACU,aAAa;;AAEhD;AACA,OAAO,MAAMI,uBAAuB,GAAG;EACrCC,UAAU,EAAE,aAAa;EACzBC,KAAK,EAAE,eAAe;EACtBC,KAAK,EAAE,SAAS;EAChBC,QAAQ,EAAE,YAAY;EACtBC,QAAQ,EAAE,oBAAoB;EAC9BC,OAAO,EAAE,mBAAmB;EAC5BC,OAAO,EAAE,mBAAmB;EAC5BC,OAAO,EAAE,WAAW;EACpBC,MAAM,EAAE;AACV,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,gBAAG9B,UAAU,CAGzC,CAAC+B,KAAK,EAAEC,GAAG,KAAK;EAChB;EACA,MAAM;IACJC,WAAW;IACXC,aAAa;IACbC,OAAO;IACPC,UAAU;IACVC,kBAAkB;IAClBC,iBAAiB;IACjBC,iBAAiB;IACjBC,QAAQ;IACRC,SAAS;IACTC,mBAAmB;IACnB,GAAGC;EACL,CAAC,GAAGZ,KAAK;EAET,MAAMa,SAAS,GAAG1C,MAAM,CAAM,IAAI,CAAC;EAEnC,MAAM2C,UAAU,GAAG1C,WAAW,CAAC,MAAqB;IAClD,OAAOK,cAAc,CAACoC,SAAS,CAACE,OAAO,CAAC;EAC1C,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMC,gBAAgB,GAAG5C,WAAW,CAClC,OACE6C,UAAkB,EAClBC,MAAuD,EACvD,GAAGC,IAAW,KACC;IACf,MAAMC,OAAO,GAAGN,UAAU,CAAC,CAAC;IAC5B,IAAIM,OAAO,KAAK,IAAI,EAAE;MACpB,MAAM,IAAIlC,KAAK,CAAC,uBAAuB,CAAC;IAC1C;IAEA,IAAImC,OAAO,EAAE;MACXC,OAAO,CAACC,GAAG,CAAC,iBAAiBN,UAAU,SAAS,CAAC;IACnD;IAEA,IAAI;MACF,MAAMO,MAAM,GAAG,MAAMN,MAAM,CAACE,OAAO,EAAE,GAAGD,IAAI,CAAC;MAC7C,IAAIE,OAAO,EAAE;QACXC,OAAO,CAACC,GAAG,CAAC,GAAGN,UAAU,yBAAyB,CAAC;MACrD;MACA,OAAOO,MAAM;IACf,CAAC,CAAC,OAAOC,KAAK,EAAE;MACd,IAAIJ,OAAO,EAAE;QACXC,OAAO,CAACG,KAAK,CACX,YAAYR,UAAU,GAAG,EACzBQ,KAAK,YAAYvC,KAAK,GAAGuC,KAAK,CAACC,OAAO,GAAGC,MAAM,CAACF,KAAK,CACvD,CAAC;MACH;MACA,MAAMA,KAAK;IACb;EACF,CAAC,EACD,CAACX,UAAU,CACb,CAAC;;EAED;EACA;EACA,MAAMc,iBAAiB,GAAGxD,WAAW,CAClCyD,KAAU,IAAK;IACd,IAAI,CAACxB,UAAU,EAAE;IACjB;IACA,MAAMyB,WAAW,GAAGD,KAAK,EAAEC,WAAW;IACtC,MAAMC,WAAW,GAAGD,WAAW,EAAEC,WAAW,IAAIF,KAAK,EAAEE,WAAW;IAClE,MAAMC,QAAQ,GAAGF,WAAW,EAAEE,QAAQ,IAAIH,KAAK,EAAEG,QAAQ;IACzD3B,UAAU,CAAC;MAAE0B,WAAW;MAAEC;IAAS,CAAC,CAAC;EACvC,CAAC,EACD,CAAC3B,UAAU,CACb,CAAC;EAED,MAAM4B,0BAA0B,GAAG7D,WAAW,CAC3CyD,KAAU,IAAK;IACd,IAAI,CAAClB,mBAAmB,EAAE;IAC1B;IACA,MAAMmB,WAAW,GAAGD,KAAK,EAAEC,WAAW;IACtC,MAAMI,UAAU,GACdJ,WAAW,EAAEK,YAAY,IAAIN,KAAK,EAAEM,YAAY,IAAI,KAAK;IAC3DxB,mBAAmB,CAACuB,UAAU,CAAC;EACjC,CAAC,EACD,CAACvB,mBAAmB,CACtB,CAAC;EAED,MAAMyB,cAAc,GAAGhE,WAAW,CAC/ByD,KAAU,IAAK;IACd,IAAI,CAACzB,OAAO,EAAE;IACd;IACA,MAAM0B,WAAW,GAAGD,KAAK,EAAEC,WAAW;IACtC,MAAML,KAAK,GAAG;MACZY,IAAI,EAAEP,WAAW,EAAEO,IAAI,IAAIR,KAAK,EAAEQ,IAAI,IAAI,SAAS;MACnDX,OAAO,EAAEI,WAAW,EAAEJ,OAAO,IAAIG,KAAK,EAAEH,OAAO,IAAI,eAAe;MAClEY,WAAW,EAAER,WAAW,EAAEQ,WAAW,IAAIT,KAAK,EAAES;IAClD,CAAC;IACDlC,OAAO,CAACqB,KAAK,CAAC;EAChB,CAAC,EACD,CAACrB,OAAO,CACV,CAAC;;EAED;EACA,MAAMmC,kBAAkB,GAAGnE,WAAW,CACnCoE,MAAW,IAAW;IACrBtC,WAAW,GAAG,CAAC;EACjB,CAAC,EACD,CAACA,WAAW,CACd,CAAC;EACD,MAAMuC,oBAAoB,GAAGrE,WAAW,CACrCoE,MAAW,IAAW;IACrBrC,aAAa,GAAG,CAAC;EACnB,CAAC,EACD,CAACA,aAAa,CAChB,CAAC;EACD,MAAMuC,yBAAyB,GAAGtE,WAAW,CAC1CoE,MAAW,IAAW;IACrBlC,kBAAkB,GAAG,CAAC;EACxB,CAAC,EACD,CAACA,kBAAkB,CACrB,CAAC;EACD,MAAMqC,wBAAwB,GAAGvE,WAAW,CACzCoE,MAAW,IAAW;IACrBjC,iBAAiB,GAAG,CAAC;EACvB,CAAC,EACD,CAACA,iBAAiB,CACpB,CAAC;EACD,MAAMqC,wBAAwB,GAAGxE,WAAW,CACzCoE,MAAW,IAAW;IACrBhC,iBAAiB,GAAG,CAAC;EACvB,CAAC,EACD,CAACA,iBAAiB,CACpB,CAAC;EACD,MAAMqC,eAAe,GAAGzE,WAAW,CAChCoE,MAAW,IAAW;IACrB/B,QAAQ,GAAG,CAAC;EACd,CAAC,EACD,CAACA,QAAQ,CACX,CAAC;EACD,MAAMqC,gBAAgB,GAAG1E,WAAW,CACjCoE,MAAW,IAAW;IACrB9B,SAAS,GAAG,CAAC;EACf,CAAC,EACD,CAACA,SAAS,CACZ,CAAC;EAEDxC,mBAAmB,CACjB+B,GAAG,EACH,OAAO;IACL8C,IAAI,EAAEA,CAAA,KAAM/B,gBAAgB,CAAC,MAAM,EAAE5B,YAAY,CAAC2D,IAAI,CAAC;IACvDC,KAAK,EAAEA,CAAA,KAAMhC,gBAAgB,CAAC,OAAO,EAAE5B,YAAY,CAAC4D,KAAK,CAAC;IAC1DC,MAAM,EAAGC,IAAY,IACnBlC,gBAAgB,CAAC,QAAQ,EAAE5B,YAAY,CAAC6D,MAAM,EAAEC,IAAI,CAAC;IACvDC,cAAc,EAAEA,CAAA,KACdnC,gBAAgB,CAAC,gBAAgB,EAAE5B,YAAY,CAAC+D,cAAc,CAAC;IACjEC,WAAW,EAAEA,CAAA,KACXpC,gBAAgB,CAAC,aAAa,EAAE5B,YAAY,CAACgE,WAAW,CAAC;IAC3DC,OAAO,EAAEA,CAAA,KAAMrC,gBAAgB,CAAC,SAAS,EAAE5B,YAAY,CAACiE,OAAO,CAAC;IAChEC,cAAc,EAAGC,UAAmB,IAClCvC,gBAAgB,CACd,gBAAgB,EAChB5B,YAAY,CAACkE,cAAc,EAC3BC,UACF,CAAC;IACHC,aAAa,EAAEA,CAAA,KACbxC,gBAAgB,CAAC,eAAe,EAAE5B,YAAY,CAACoE,aAAa,CAAC;IAC/DC,gBAAgB,EAAGvB,UAAmB,IACpClB,gBAAgB,CACd,kBAAkB,EAClB5B,YAAY,CAACqE,gBAAgB,EAC7BvB,UACF,CAAC;IACHwB,QAAQ,EAAGC,KAAa,IACtB3C,gBAAgB,CAAC,UAAU,EAAE5B,YAAY,CAACsE,QAAQ,EAAEC,KAAK;EAC7D,CAAC,CAAC,EACF,CAAC3C,gBAAgB,CACnB,CAAC;;EAED;EACA,MAAM4C,WAAqC,GAAG;IAC5C,GAAGhD,SAAS;IACZV,WAAW,EAAEA,WAAW,GAAGqC,kBAAkB,GAAGsB,SAAS;IACzD1D,aAAa,EAAEA,aAAa,GAAGsC,oBAAoB,GAAGoB,SAAS;IAC/DzD,OAAO,EAAEA,OAAO,GAAGgC,cAAc,GAAGyB,SAAS;IAC7CxD,UAAU,EAAEA,UAAU,GAAGuB,iBAAiB,GAAGiC,SAAS;IACtDvD,kBAAkB,EAAEA,kBAAkB,GAClCoC,yBAAyB,GACzBmB,SAAS;IACbtD,iBAAiB,EAAEA,iBAAiB,GAAGoC,wBAAwB,GAAGkB,SAAS;IAC3ErD,iBAAiB,EAAEA,iBAAiB,GAAGoC,wBAAwB,GAAGiB,SAAS;IAC3EpD,QAAQ,EAAEA,QAAQ,GAAGoC,eAAe,GAAGgB,SAAS;IAChDnD,SAAS,EAAEA,SAAS,GAAGoC,gBAAgB,GAAGe,SAAS;IACnDlD,mBAAmB,EAAEA,mBAAmB,GACpCsB,0BAA0B,GAC1B4B;EACN,CAAC;EAED,oBAAOlF,IAAA,CAACQ,uBAAuB;IAAA,GAAKyE,WAAW;IAAE3D,GAAG,EAAEY;EAAU,CAAE,CAAC;AACrE,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAiBtB;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB;IACrB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,yCAAyC;IACzC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,2CAA2C;IAC3C,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,oCAAoC;IACpC,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,gEAAgE;IAChE,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B;;;OAGG;IACH,cAAc,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,wDAAwD;IACxD,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,6BAA6B;IAC7B,gBAAgB,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,oDAAoD;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qCAAqC;IACrC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6CAA6C;IAC7C,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACvD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAiBtB;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB;IACrB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,yCAAyC;IACzC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,2CAA2C;IAC3C,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,oCAAoC;IACpC,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,gEAAgE;IAChE,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B;;;OAGG;IACH,cAAc,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,wDAAwD;IACxD,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,6BAA6B;IAC7B,gBAAgB,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,oDAAoD;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qCAAqC;IACrC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6CAA6C;IAC7C,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACvD,CAAC;AAqCF,eAAO,MAAM,uBAAuB;;;;;;;;;;CAU1B,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,iBAAiB,iHAoM5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-unified-player",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Unified Player",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -107,7 +107,11 @@
|
|
|
107
107
|
"git": {
|
|
108
108
|
"commitMessage": "chore: release ${version}",
|
|
109
109
|
"tagName": "v${version}",
|
|
110
|
-
"requireCleanWorkingDir": false
|
|
110
|
+
"requireCleanWorkingDir": false,
|
|
111
|
+
"push": true,
|
|
112
|
+
"pushArgs": [
|
|
113
|
+
"--force-with-lease"
|
|
114
|
+
]
|
|
111
115
|
},
|
|
112
116
|
"npm": {
|
|
113
117
|
"publish": true,
|
package/src/index.tsx
CHANGED
|
@@ -101,9 +101,36 @@ export type UnifiedPlayerProps = {
|
|
|
101
101
|
onFullscreenChanged?: (isFullscreen: boolean) => void;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
// Internal type for native component props (accepts native event objects)
|
|
105
|
+
type NativeEventHandler = (event: any) => void;
|
|
106
|
+
type NativeUnifiedPlayerProps = Omit<
|
|
107
|
+
UnifiedPlayerProps,
|
|
108
|
+
| 'onLoadStart'
|
|
109
|
+
| 'onReadyToPlay'
|
|
110
|
+
| 'onError'
|
|
111
|
+
| 'onProgress'
|
|
112
|
+
| 'onPlaybackComplete'
|
|
113
|
+
| 'onPlaybackStalled'
|
|
114
|
+
| 'onPlaybackResumed'
|
|
115
|
+
| 'onPaused'
|
|
116
|
+
| 'onPlaying'
|
|
117
|
+
| 'onFullscreenChanged'
|
|
118
|
+
> & {
|
|
119
|
+
onLoadStart?: NativeEventHandler;
|
|
120
|
+
onReadyToPlay?: NativeEventHandler;
|
|
121
|
+
onError?: NativeEventHandler;
|
|
122
|
+
onProgress?: NativeEventHandler;
|
|
123
|
+
onPlaybackComplete?: NativeEventHandler;
|
|
124
|
+
onPlaybackStalled?: NativeEventHandler;
|
|
125
|
+
onPlaybackResumed?: NativeEventHandler;
|
|
126
|
+
onPaused?: NativeEventHandler;
|
|
127
|
+
onPlaying?: NativeEventHandler;
|
|
128
|
+
onFullscreenChanged?: NativeEventHandler;
|
|
129
|
+
};
|
|
130
|
+
|
|
104
131
|
// Native component registration
|
|
105
132
|
const NativeUnifiedPlayerView =
|
|
106
|
-
requireNativeComponent<
|
|
133
|
+
requireNativeComponent<NativeUnifiedPlayerProps>('UnifiedPlayerView');
|
|
107
134
|
|
|
108
135
|
// Native module for player control methods
|
|
109
136
|
const NativeModule = NativeModules.UnifiedPlayer;
|
|
@@ -154,6 +181,21 @@ export const UnifiedPlayerView = forwardRef<
|
|
|
154
181
|
UnifiedPlayerRef,
|
|
155
182
|
UnifiedPlayerProps
|
|
156
183
|
>((props, ref) => {
|
|
184
|
+
// Destructure all event handlers at the top to satisfy ESLint exhaustive-deps
|
|
185
|
+
const {
|
|
186
|
+
onLoadStart,
|
|
187
|
+
onReadyToPlay,
|
|
188
|
+
onError,
|
|
189
|
+
onProgress,
|
|
190
|
+
onPlaybackComplete,
|
|
191
|
+
onPlaybackStalled,
|
|
192
|
+
onPlaybackResumed,
|
|
193
|
+
onPaused,
|
|
194
|
+
onPlaying,
|
|
195
|
+
onFullscreenChanged,
|
|
196
|
+
...restProps
|
|
197
|
+
} = props;
|
|
198
|
+
|
|
157
199
|
const nativeRef = useRef<any>(null);
|
|
158
200
|
|
|
159
201
|
const getViewTag = useCallback((): number | null => {
|
|
@@ -194,6 +236,91 @@ export const UnifiedPlayerView = forwardRef<
|
|
|
194
236
|
[getViewTag]
|
|
195
237
|
);
|
|
196
238
|
|
|
239
|
+
// Wrap event handlers to extract data immediately and avoid event pooling warnings
|
|
240
|
+
// Must extract nativeEvent data synchronously before React recycles the event
|
|
241
|
+
const wrappedOnProgress = useCallback(
|
|
242
|
+
(event: any) => {
|
|
243
|
+
if (!onProgress) return;
|
|
244
|
+
// Extract data synchronously - access nativeEvent immediately
|
|
245
|
+
const nativeEvent = event?.nativeEvent;
|
|
246
|
+
const currentTime = nativeEvent?.currentTime ?? event?.currentTime;
|
|
247
|
+
const duration = nativeEvent?.duration ?? event?.duration;
|
|
248
|
+
onProgress({ currentTime, duration });
|
|
249
|
+
},
|
|
250
|
+
[onProgress]
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const wrappedOnFullscreenChanged = useCallback(
|
|
254
|
+
(event: any) => {
|
|
255
|
+
if (!onFullscreenChanged) return;
|
|
256
|
+
// Extract data synchronously - access nativeEvent immediately
|
|
257
|
+
const nativeEvent = event?.nativeEvent;
|
|
258
|
+
const fullscreen =
|
|
259
|
+
nativeEvent?.isFullscreen ?? event?.isFullscreen ?? false;
|
|
260
|
+
onFullscreenChanged(fullscreen);
|
|
261
|
+
},
|
|
262
|
+
[onFullscreenChanged]
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const wrappedOnError = useCallback(
|
|
266
|
+
(event: any) => {
|
|
267
|
+
if (!onError) return;
|
|
268
|
+
// Extract error data synchronously
|
|
269
|
+
const nativeEvent = event?.nativeEvent;
|
|
270
|
+
const error = {
|
|
271
|
+
code: nativeEvent?.code ?? event?.code ?? 'UNKNOWN',
|
|
272
|
+
message: nativeEvent?.message ?? event?.message ?? 'Unknown error',
|
|
273
|
+
nativeError: nativeEvent?.nativeError ?? event?.nativeError,
|
|
274
|
+
};
|
|
275
|
+
onError(error);
|
|
276
|
+
},
|
|
277
|
+
[onError]
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Wrap simple event handlers to prevent synthetic event access issues
|
|
281
|
+
const wrappedOnLoadStart = useCallback(
|
|
282
|
+
(_event: any): void => {
|
|
283
|
+
onLoadStart?.();
|
|
284
|
+
},
|
|
285
|
+
[onLoadStart]
|
|
286
|
+
);
|
|
287
|
+
const wrappedOnReadyToPlay = useCallback(
|
|
288
|
+
(_event: any): void => {
|
|
289
|
+
onReadyToPlay?.();
|
|
290
|
+
},
|
|
291
|
+
[onReadyToPlay]
|
|
292
|
+
);
|
|
293
|
+
const wrappedOnPlaybackComplete = useCallback(
|
|
294
|
+
(_event: any): void => {
|
|
295
|
+
onPlaybackComplete?.();
|
|
296
|
+
},
|
|
297
|
+
[onPlaybackComplete]
|
|
298
|
+
);
|
|
299
|
+
const wrappedOnPlaybackStalled = useCallback(
|
|
300
|
+
(_event: any): void => {
|
|
301
|
+
onPlaybackStalled?.();
|
|
302
|
+
},
|
|
303
|
+
[onPlaybackStalled]
|
|
304
|
+
);
|
|
305
|
+
const wrappedOnPlaybackResumed = useCallback(
|
|
306
|
+
(_event: any): void => {
|
|
307
|
+
onPlaybackResumed?.();
|
|
308
|
+
},
|
|
309
|
+
[onPlaybackResumed]
|
|
310
|
+
);
|
|
311
|
+
const wrappedOnPaused = useCallback(
|
|
312
|
+
(_event: any): void => {
|
|
313
|
+
onPaused?.();
|
|
314
|
+
},
|
|
315
|
+
[onPaused]
|
|
316
|
+
);
|
|
317
|
+
const wrappedOnPlaying = useCallback(
|
|
318
|
+
(_event: any): void => {
|
|
319
|
+
onPlaying?.();
|
|
320
|
+
},
|
|
321
|
+
[onPlaying]
|
|
322
|
+
);
|
|
323
|
+
|
|
197
324
|
useImperativeHandle(
|
|
198
325
|
ref,
|
|
199
326
|
() => ({
|
|
@@ -214,11 +341,11 @@ export const UnifiedPlayerView = forwardRef<
|
|
|
214
341
|
),
|
|
215
342
|
stopRecording: () =>
|
|
216
343
|
callNativeMethod('stopRecording', NativeModule.stopRecording),
|
|
217
|
-
toggleFullscreen: (
|
|
344
|
+
toggleFullscreen: (fullscreen: boolean) =>
|
|
218
345
|
callNativeMethod(
|
|
219
346
|
'toggleFullscreen',
|
|
220
347
|
NativeModule.toggleFullscreen,
|
|
221
|
-
|
|
348
|
+
fullscreen
|
|
222
349
|
),
|
|
223
350
|
setSpeed: (speed: number) =>
|
|
224
351
|
callNativeMethod('setSpeed', NativeModule.setSpeed, speed),
|
|
@@ -226,5 +353,24 @@ export const UnifiedPlayerView = forwardRef<
|
|
|
226
353
|
[callNativeMethod]
|
|
227
354
|
);
|
|
228
355
|
|
|
229
|
-
|
|
356
|
+
// Build native props with wrapped event handlers to avoid event pooling warnings
|
|
357
|
+
const nativeProps: NativeUnifiedPlayerProps = {
|
|
358
|
+
...restProps,
|
|
359
|
+
onLoadStart: onLoadStart ? wrappedOnLoadStart : undefined,
|
|
360
|
+
onReadyToPlay: onReadyToPlay ? wrappedOnReadyToPlay : undefined,
|
|
361
|
+
onError: onError ? wrappedOnError : undefined,
|
|
362
|
+
onProgress: onProgress ? wrappedOnProgress : undefined,
|
|
363
|
+
onPlaybackComplete: onPlaybackComplete
|
|
364
|
+
? wrappedOnPlaybackComplete
|
|
365
|
+
: undefined,
|
|
366
|
+
onPlaybackStalled: onPlaybackStalled ? wrappedOnPlaybackStalled : undefined,
|
|
367
|
+
onPlaybackResumed: onPlaybackResumed ? wrappedOnPlaybackResumed : undefined,
|
|
368
|
+
onPaused: onPaused ? wrappedOnPaused : undefined,
|
|
369
|
+
onPlaying: onPlaying ? wrappedOnPlaying : undefined,
|
|
370
|
+
onFullscreenChanged: onFullscreenChanged
|
|
371
|
+
? wrappedOnFullscreenChanged
|
|
372
|
+
: undefined,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
return <NativeUnifiedPlayerView {...nativeProps} ref={nativeRef} />;
|
|
230
376
|
});
|