From c7f4d7b83bd09178f945d21e1f252a57ee1c8ab1 Mon Sep 17 00:00:00 2001 From: Seyed Mostafa Hasani Date: Tue, 28 May 2024 02:00:38 -0700 Subject: [PATCH] feat: add getCurrentPosition to component's ref (#3824) * feat: add getCurrentPosition to component's ref --------- Co-authored-by: mostafahasani --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 10 ++++++++++ .../java/com/brentvatne/react/VideoManagerModule.kt | 8 ++++++++ docs/pages/component/methods.mdx | 9 +++++++++ ios/Video/RCTVideo.swift | 10 ++++++++++ ios/Video/RCTVideoManager.m | 4 ++++ ios/Video/RCTVideoManager.swift | 7 +++++++ src/Video.tsx | 7 +++++++ src/specs/VideoNativeComponent.ts | 1 + 8 files changed, 56 insertions(+) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 1c7b41ddda..957f7c28ae 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -124,6 +124,7 @@ import com.brentvatne.receiver.AudioBecomingNoisyReceiver; import com.brentvatne.receiver.BecomingNoisyListener; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.ThemedReactContext; import com.google.ads.interactivemedia.v3.api.AdError; @@ -697,6 +698,15 @@ private void initializePlayer() { mainHandler.postDelayed(mainRunnable, 1); } + public void getCurrentPosition(Promise promise) { + if (player != null) { + double currentPosition = player.getCurrentPosition() / 1000; + promise.resolve(currentPosition); + } else { + promise.reject("PLAYER_NOT_AVAILABLE", "Player is not initialized."); + } + } + private void initializePlayerCore(ReactExoplayerView self) { ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); self.trackSelector = new DefaultTrackSelector(getContext(), videoTrackSelectionFactory); diff --git a/android/src/main/java/com/brentvatne/react/VideoManagerModule.kt b/android/src/main/java/com/brentvatne/react/VideoManagerModule.kt index 11781aa1a2..c736cf01a5 100644 --- a/android/src/main/java/com/brentvatne/react/VideoManagerModule.kt +++ b/android/src/main/java/com/brentvatne/react/VideoManagerModule.kt @@ -2,6 +2,7 @@ package com.brentvatne.react import com.brentvatne.common.toolbox.ReactBridgeUtils import com.brentvatne.exoplayer.ReactExoplayerView +import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod @@ -61,6 +62,13 @@ class VideoManagerModule(reactContext: ReactApplicationContext?) : ReactContextB } } + @ReactMethod + fun getCurrentPosition(reactTag: Int, promise: Promise) { + performOnPlayerView(reactTag) { + it?.getCurrentPosition(promise) + } + } + companion object { private const val REACT_CLASS = "VideoManager" } diff --git a/docs/pages/component/methods.mdx b/docs/pages/component/methods.mdx index 755901ebb3..112fccaaf7 100644 --- a/docs/pages/component/methods.mdx +++ b/docs/pages/component/methods.mdx @@ -100,6 +100,15 @@ tolerance is the max distance in milliseconds from the seconds position that's a This function will change the volume exactly like [volume](./props#volume) property. default value and range are the same then. +### `getCurrentPosition` + + + +`getCurrentPosition(): Promise` + +This function retrieves and returns the precise current position of the video playback, measured in seconds. +This function will throw an error if player is not initialized. + ### Example Usage ```tsx diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 486524d947..72439bb6c6 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -1622,6 +1622,16 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } } + @objc + func getCurrentPlaybackTime(_ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) { + if let player = _playerItem { + let currentTime = RCTVideoUtils.getCurrentTime(playerItem: player) + resolve(currentTime) + } else { + reject("PLAYER_NOT_AVAILABLE", "Player is not initialized.", nil) + } + } + // Workaround for #3418 - https://github.com/TheWidlarzGroup/react-native-video/issues/3418#issuecomment-2043508862 @objc func setOnClick(_: Any) {} diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index d0ead34a9c..d65901bb4f 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -88,4 +88,8 @@ @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager) RCT_EXTERN_METHOD(setVolume : (nonnull float*)volume reactTag : (nonnull NSNumber*)reactTag) +RCT_EXTERN_METHOD(getCurrentPosition + : (nonnull NSNumber*)reactTag resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) @end diff --git a/ios/Video/RCTVideoManager.swift b/ios/Video/RCTVideoManager.swift index 7c9181e3d5..b4e3144af7 100644 --- a/ios/Video/RCTVideoManager.swift +++ b/ios/Video/RCTVideoManager.swift @@ -86,6 +86,13 @@ class RCTVideoManager: RCTViewManager { }) } + @objc(getCurrentPosition:resolver:rejecter:) + func getCurrentPosition(reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + performOnVideoView(withReactTag: reactTag, callback: { videoView in + videoView?.getCurrentPlaybackTime(resolve, reject) + }) + } + override class func requiresMainQueueSetup() -> Bool { return true } diff --git a/src/Video.tsx b/src/Video.tsx index 55b9bd0194..c2dba261c4 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -65,6 +65,7 @@ export interface VideoRef { ) => void; save: (options: object) => Promise; setVolume: (volume: number) => void; + getCurrentPosition: () => Promise; } const Video = forwardRef( @@ -295,6 +296,10 @@ const Video = forwardRef( return VideoManager.setVolume(volume, getReactTag(nativeRef)); }, []); + const getCurrentPosition = useCallback(() => { + return VideoManager.getCurrentPosition(getReactTag(nativeRef)); + }, []); + const onVideoLoadStart = useCallback( (e: NativeSyntheticEvent) => { hasPoster && setShowPoster(true); @@ -512,6 +517,7 @@ const Video = forwardRef( resume, restoreUserInterfaceForPictureInPictureStopCompleted, setVolume, + getCurrentPosition, }), [ seek, @@ -522,6 +528,7 @@ const Video = forwardRef( resume, restoreUserInterfaceForPictureInPictureStopCompleted, setVolume, + getCurrentPosition, ], ); diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts index b9353414d1..d776956c03 100644 --- a/src/specs/VideoNativeComponent.ts +++ b/src/specs/VideoNativeComponent.ts @@ -389,6 +389,7 @@ export interface VideoManagerType { reactTag: number, ) => Promise; setVolume: (volume: number, reactTag: number) => Promise; + getCurrentPosition: (reactTag: number) => Promise; } export interface VideoDecoderPropertiesType {