Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,36 @@ public void getCurrentPosition(Promise promise) {
}
}

public void getCurrentPlaybackStatus(Promise promise) {
if (player != null) {
int playbackState = player.getPlaybackState();
switch (playbackState) {
case Player.STATE_IDLE:
promise.resolve("idle");
break;
case Player.STATE_BUFFERING:
promise.resolve("buffering");
break;
case Player.STATE_READY:
if (isPaused) {
promise.resolve("paused");
} else {
promise.resolve("playing");
}
break;
case Player.STATE_ENDED:
promise.resolve("ended");
break;
default:
promise.resolve("unknown");
break;
}
} else {
promise.resolve("released");
}
}


private void initializePlayerCore(ReactExoplayerView self) {
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
self.trackSelector = new DefaultTrackSelector(getContext(), videoTrackSelectionFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ class VideoManagerModule(reactContext: ReactApplicationContext?) : ReactContextB
}
}

@ReactMethod
fun getCurrentPlaybackStatus(reactTag: Int, promise: Promise) {
performOnPlayerView(reactTag) {
it?.getCurrentPlaybackStatus(promise)
}
}

companion object {
private const val REACT_CLASS = "VideoManager"
}
Expand Down
12 changes: 12 additions & 0 deletions docs/pages/component/methods.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ On iOS, this displays the video in a fullscreen view controller with controls.

On Android, this puts the navigation controls in fullscreen mode. It is not a complete fullscreen implementation, so you will still need to apply a style that makes the width and height match your screen dimensions to get a fullscreen video.

### `getCurrentPlaybackStatus`

This function retrieves the current playback status of the media and can return one of the following states:

- **idle** (Android only): The media is not yet prepared, and the player must be prepared before it can play the media.
- **paused**: The media is loaded but not actively playing.
- **playing**: The media is currently playing.
- **buffering**: The media is loading or waiting for data to continue playback.
- **ended**: The media has finished playing.
- **unknown**: The media has unknown status.
- **released**: The player is not initialized.

### Example Usage

```tsx
Expand Down
10 changes: 5 additions & 5 deletions examples/basic/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ PODS:
- React-Mapbuffer (0.74.5):
- glog
- React-debug
- react-native-video (6.6.2):
- react-native-video (6.6.4):
- DoubleConversion
- glog
- hermes-engine
Expand All @@ -1008,7 +1008,7 @@ PODS:
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-video/Video (= 6.6.2)
- react-native-video/Video (= 6.6.4)
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
Expand Down Expand Up @@ -1038,7 +1038,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-video/Video (6.6.2):
- react-native-video/Video (6.6.4):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1559,7 +1559,7 @@ SPEC CHECKSUMS:
React-jsitracing: c83efb63c8e9e1dff72a3c56e88ae1c530a87795
React-logger: 257858bd55f3a4e1bc0cf07ddc8fb9faba6f8c7c
React-Mapbuffer: dce508662b995ffefd29e278a16b78217039d43d
react-native-video: b52a7473fd467d8c18032535ec5f332b81f9bdf0
react-native-video: c9889e8b3449e88191c396aaff3b9a48868ce695
react-native-video-plugin-sample: d3a93b7ad777cad7fa2c30473de75a2635ce5feb
React-nativeconfig: f326487bc61eba3f0e328da6efb2711533dcac46
React-NativeModulesApple: d89733f5baed8b9249ca5a8e497d63c550097312
Expand Down Expand Up @@ -1590,7 +1590,7 @@ SPEC CHECKSUMS:
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: 1ab23c1835475da69cf14e211a560e73aab24cb0
Yoga: 33622183a85805e12703cd618b2c16bfd18bfffb

PODFILE CHECKSUM: a73d485df51877001f2b04a5a4379cfa5a3ba8fa

Expand Down
38 changes: 38 additions & 0 deletions ios/Video/RCTVideo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,44 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
}
}

@objc
func getCurrentPlaybackStatus(_ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) {
if let player = _player {
switch player.status {
case .unknown:
resolve("unknown")
case .failed:
reject("PLAYBACK_ERROR", "Playback failed", player.error)
case .readyToPlay:
if let currentItem = player.currentItem {
let currentTime = CMTimeGetSeconds(player.currentTime())
let duration = CMTimeGetSeconds(currentItem.duration)

if currentTime >= duration {
resolve("ended")
} else {
switch player.timeControlStatus {
case .paused:
resolve("paused")
case .waitingToPlayAtSpecifiedRate:
resolve("buffering")
case .playing:
resolve("playing")
@unknown default:
resolve("unknown")
}
}
} else {
resolve("unknown")
}
@unknown default:
resolve("unknown")
}
} else {
resolve("released")
}
}

// Workaround for #3418 - https://github.com/TheWidlarzGroup/react-native-video/issues/3418#issuecomment-2043508862
@objc
func setOnClick(_: Any) {}
Expand Down
4 changes: 4 additions & 0 deletions ios/Video/RCTVideoManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,9 @@ @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager)
: (nonnull NSNumber*)reactTag resolve
: (RCTPromiseResolveBlock)resolve reject
: (RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getCurrentPlaybackStatus
: (nonnull NSNumber*)reactTag resolve
: (RCTPromiseResolveBlock)resolve reject
: (RCTPromiseRejectBlock)reject)

@end
7 changes: 7 additions & 0 deletions ios/Video/RCTVideoManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ class RCTVideoManager: RCTViewManager {
})
}

@objc(getCurrentPlaybackStatus:resolve:reject:)
func getCurrentPlaybackStatus(_ reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
performOnVideoView(withReactTag: reactTag, callback: { videoView in
videoView?.getCurrentPlaybackStatus(resolve, reject)
})
}

override class func requiresMainQueueSetup() -> Bool {
return true
}
Expand Down
11 changes: 11 additions & 0 deletions src/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import type {
ReactVideoProps,
CmcdData,
ReactVideoSource,
TPlaybackStatus,
} from './types';

export interface VideoRef {
Expand All @@ -70,6 +71,7 @@ export interface VideoRef {
setSource: (source?: ReactVideoSource) => void;
save: (options: object) => Promise<VideoSaveData> | void;
getCurrentPosition: () => Promise<number>;
getCurrentPlaybackStatus: () => Promise<TPlaybackStatus>;
}

const Video = forwardRef<VideoRef, ReactVideoProps>(
Expand Down Expand Up @@ -413,6 +415,13 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
return NativeVideoManager.getCurrentPosition(getReactTag(nativeRef));
}, []);

const getCurrentPlaybackStatus = useCallback(() => {
// @todo Must implement it in a different way.
return NativeVideoManager.getCurrentPlaybackStatus(
getReactTag(nativeRef),
);
}, []);

const restoreUserInterfaceForPictureInPictureStopCompleted = useCallback(
(restored: boolean) => {
setRestoreUserInterfaceForPIPStopCompletionHandler(restored);
Expand Down Expand Up @@ -648,6 +657,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
getCurrentPosition,
setFullScreen,
setSource,
getCurrentPlaybackStatus,
}),
[
seek,
Expand All @@ -661,6 +671,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
getCurrentPosition,
setFullScreen,
setSource,
getCurrentPlaybackStatus,
],
);

Expand Down
2 changes: 2 additions & 0 deletions src/specs/NativeVideoManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
Float,
UnsafeObject,
} from 'react-native/Libraries/Types/CodegenTypes';
import {TPlaybackStatus} from '../types';

export type VideoSaveData = {
uri: string;
Expand All @@ -28,6 +29,7 @@ export interface VideoManagerType {
setVolumeCmd: (reactTag: Int32, volume: number) => Promise<void>;
save: (reactTag: Int32, option: UnsafeObject) => Promise<VideoSaveData>;
getCurrentPosition: (reactTag: Int32) => Promise<Int32>;
getCurrentPlaybackStatus: (reactTag: Int32) => Promise<TPlaybackStatus>;
}

export default NativeModules.VideoManager as VideoManagerType;
9 changes: 9 additions & 0 deletions src/types/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ export type ReactVideoSourceProperties = {
textTracks?: TextTracks;
};

export type TPlaybackStatus = {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlaybackStatus would be enough ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to add the prefix T before type names, meaning playbackStatus would be a type. Similarly, we can add an I before interface names
FX: type TSample = {} and interface ISample = {}
WDYT?

Copy link
Copy Markdown
Contributor

@IslamRustamov IslamRustamov Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Robert Martin wrote in his "Clean Code" - T, I, Enum, List (and so on) prefixes/suffixes before types, interfaces, enums (and so on) are meaningless.

We are not necessarily need to follow what he writes in his books, but it's better to follow guidelines that are accepted in the project you are working on (for example, as I can see in this project suffixes/prefixes are not used)

paused: 'paused';
playing: 'playing';
buffering: 'buffering';
ended: 'ended';
idle: 'idle';
unknown: 'unknown';
};

export type ReactVideoSource = Readonly<
Omit<ReactVideoSourceProperties, 'uri'> & {
uri?: string | NodeRequire;
Expand Down