Skip to content

Commit 37bda82

Browse files
committed
feat: Some improvements
1 parent a6fc1e2 commit 37bda82

15 files changed

+491
-201
lines changed

README.md

+99-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# React Native Brightcove Player
22

3-
A React Native implementation of Brightcove Player SDK.
3+
- A React Native implementation of Brightcove Player SDK.
4+
- Supported Features
5+
- Brightcove player component
6+
- Poster image component
7+
- Retrieving playlist
8+
- Managing offline playback with Dynamic Delivery
49

5-
<img src="https://user-images.githubusercontent.com/443965/40413410-b9963158-5eb0-11e8-924f-9f61df58fa04.jpg" width="500">
10+
<img src="https://user-images.githubusercontent.com/443965/58468251-2e0a5b00-8178-11e9-87b7-0acd15b750ba.jpg" width="500">
611

712
## Supported Version
813

914
- iOS 10 >=
1015
- Android 5.0 >=
16+
- Brightcove SDK 6.x
1117

1218
## Installation
1319

@@ -31,7 +37,7 @@ module.exports = {
3137

3238
### iOS
3339

34-
- Make `Podfile` like below and `pod install`
40+
- Make `Podfile` and `pod install`
3541

3642
```rb
3743
source 'https://github.com/brightcove/BrightcoveSpecs.git'
@@ -46,7 +52,7 @@ end
4652

4753
### Android
4854

49-
- Add following lines in `android/build.gradle`
55+
- Add maven source to repositories in `android/build.gradle`
5056
- [Enables multiDex](https://developer.android.com/studio/build/multidex).
5157

5258
```gradle
@@ -82,6 +88,7 @@ export default class App extends Component {
8288
}
8389
```
8490

91+
- Video player component of Brightcove.
8592
- It may not work on Android simulator.
8693
- For a more detailed example, please see [example](https://github.com/manse/react-native-brightcove-player/blob/master/example/).
8794

@@ -113,6 +120,40 @@ export default class App extends Component {
113120
| ------------------------------------- | --------------------------------- |
114121
| seekTo(timeInSeconds: number) => void | Change playhead to arbitrary time |
115122

123+
### BrightcovePlayerPoster
124+
125+
```jsx
126+
import { BrightcovePlayerPoster } from 'react-native-brightcove-player';
127+
128+
export default class App extends Component {
129+
render() {
130+
return (
131+
<View style={styles.container}>
132+
<BrightcovePlayerPoster
133+
style={styles.player}
134+
accountId="3636334163001"
135+
videoId="3666678807001"
136+
policyKey="BCpkADawqM1W-vUOMe6RSA3pA6Vw-VWUNn5rL0lzQabvrI63-VjS93gVUugDlmBpHIxP16X8TSe5LSKM415UHeMBmxl7pqcwVY_AZ4yKFwIpZPvXE34TpXEYYcmulxJQAOvHbv2dpfq-S_cm"
137+
resizeMode="contain"
138+
/>
139+
</View>
140+
);
141+
}
142+
}
143+
```
144+
145+
- Displays a poster specified by `videoId`, `referenceId` or `videoToken`.
146+
- Prop is about the same as `BrightcovePlayer`.
147+
148+
| Prop | Type | Description |
149+
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
150+
| accountId | string | Required |
151+
| policyKey | string | Required |
152+
| videoId | string | |
153+
| referenceId | string | |
154+
| videoToken | string | |
155+
| resizeMode | string | Set the image resize method. Specifying `cover` or `contain` works the same as CSS keywords of `background-size`. Specifying `fit`, scales to fit the component size without considering aspect ratio of the image. Default value is `cover`. |
156+
116157
### BrightcovePlayerUtil
117158

118159
- Promise for all methods may be invoke `reject`. Be sure to catch the exception.
@@ -135,11 +176,12 @@ BrightcovePlayerUtil.requestDownloadVideoWithReferenceId(accountId: string, poli
135176
- If no rendition can be found with a bitrate that is smaller than or equal to this bitrate, the lowest rendition will be downloaded.
136177
- If this value is `0` or not specified, the lowest rendition with video will be downloaded.
137178

138-
139179
#### getOfflineVideoStatuses
140180

141181
```ts
142182
BrightcovePlayerUtil.getOfflineVideoStatuses(accountId: string, policyKey: string): Promise<{
183+
accountId: string;
184+
videoId: string;
143185
videoToken: string;
144186
downloadProgress: number;
145187
}[]>
@@ -158,6 +200,58 @@ BrightcovePlayerUtil.deleteOfflineVideo(accountId: string, policyKey: string, vi
158200

159201
- Deletes offline videos or abort the ongoing download session.
160202

203+
#### addOfflineNotificationListener
204+
205+
```ts
206+
BrightcovePlayerUtil.addOfflineNotificationListener(callback: (statuses: {
207+
accountId: string;
208+
videoId: string;
209+
videoToken: string;
210+
downloadProgress: number;
211+
}[]) => void): Function
212+
```
213+
214+
- Register a callback to notify storage changes such as video download progress or deletion of offline video.
215+
- Make sure call disposer function when callback is no longer needed.
216+
217+
```ts
218+
class Example extends Component {
219+
componentDidMount() {
220+
this.disposer = BrightcovePlayerUtil.addOfflineNotificationListener(console.log);
221+
}
222+
223+
componentWillUnmount() {
224+
this.disposer();
225+
}
226+
227+
render() {
228+
...
229+
}
230+
}
231+
```
232+
233+
#### getPlaylistWithPlaylistId, getPlaylistWithReferenceId
234+
235+
```ts
236+
BrightcovePlayerUtil.getPlaylistWithPlaylistId(accountId: string, policyKey: string, playlistId: string): Promise<{
237+
accountId: String;
238+
videoId: String;
239+
referenceId: String;
240+
name: String;
241+
description: String;
242+
duration: number;
243+
}[]>;
244+
BrightcovePlayerUtil.getPlaylistWithReferenceId(accountId: string, policyKey: string, referenceId: string): Promise<{
245+
accountId: String;
246+
videoId: String;
247+
referenceId: String;
248+
name: String;
249+
description: String;
250+
duration: number;
251+
}[]>;
252+
```
253+
254+
- Retrieves a playlist specified by `playlistId` or `referenceId`.
161255

162256
## License
163257

android/src/main/java/jp/manse/BrightcovePlayerAccount.java

+38-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import com.facebook.react.bridge.WritableNativeMap;
1919

2020
import java.util.ArrayList;
21+
import java.util.HashMap;
2122
import java.util.List;
23+
import java.util.Map;
2224

2325
import jp.manse.util.DefaultEventEmitter;
2426

@@ -46,14 +48,20 @@ public class BrightcovePlayerAccount implements OfflineVideoDownloadSession.OnOf
4648
private List<OfflineVideoDownloadSession> offlineVideoDownloadSessions = new ArrayList<>();
4749
private boolean getOfflineVideoStatusesRunning = false;
4850
private List<Promise> getOfflineVideoStatusesPendingPromises = new ArrayList<>();
49-
private List<Video> allDownloadedVideos;
51+
private List<Video> allDownloadedVideos = new ArrayList<>();
5052
private Catalog catalog;
5153
private OfflineCatalog offlineCatalog;
54+
private OnBrightcovePlayerAccountListener listener;
5255

53-
public BrightcovePlayerAccount(final ReactApplicationContext context, final String accountId, final String policyKey) {
56+
public interface OnBrightcovePlayerAccountListener {
57+
void onOfflineStorageStateChanged(NativeArray array);
58+
}
59+
60+
public BrightcovePlayerAccount(final ReactApplicationContext context, final String accountId, final String policyKey, OnBrightcovePlayerAccountListener listener) {
5461
this.context = context;
5562
this.accountId = accountId;
5663
this.policyKey = policyKey;
64+
this.listener = listener;
5765
handler = new Handler(Looper.myLooper());
5866
this.catalog = new Catalog(DefaultEventEmitter.sharedEventEmitter, accountId, policyKey);
5967
this.offlineCatalog = new OfflineCatalog(context, DefaultEventEmitter.sharedEventEmitter, accountId, policyKey);
@@ -84,6 +92,7 @@ public void requestDownloadWithReferenceId(String referenceId, int bitRate, Prom
8492
OfflineVideoDownloadSession session = new OfflineVideoDownloadSession(this.context, this.accountId, this.policyKey, this);
8593
session.requestDownloadWithReferenceId(referenceId, bitRate, promise);
8694
this.offlineVideoDownloadSessions.add(session);
95+
this.listener.onOfflineStorageStateChanged(collectNativeOfflineVideoStatuses());
8796
}
8897

8998
public void requestDownloadWithVideoId(String videoId, int bitRate, Promise promise) {
@@ -94,6 +103,7 @@ public void requestDownloadWithVideoId(String videoId, int bitRate, Promise prom
94103
OfflineVideoDownloadSession session = new OfflineVideoDownloadSession(this.context, this.accountId, this.policyKey, this);
95104
session.requestDownloadWithVideoId(videoId, bitRate, promise);
96105
this.offlineVideoDownloadSessions.add(session);
106+
this.listener.onOfflineStorageStateChanged(collectNativeOfflineVideoStatuses());
97107
}
98108

99109
public void getOfflineVideoStatuses(Promise promise) {
@@ -134,19 +144,27 @@ private void sendOfflineVideoStatuses() {
134144
}
135145
}
136146

137-
public void deleteOfflineVideo(String videoId, final Promise promise) {
147+
public void deleteOfflineVideo(final String videoId, final Promise promise) {
138148
try {
149+
if (videoId == null) throw new Exception();
139150
this.offlineCatalog.cancelVideoDownload(videoId);
140151
for (int i = this.offlineVideoDownloadSessions.size() - 1; i >= 0; i--) {
141152
OfflineVideoDownloadSession session = this.offlineVideoDownloadSessions.get(i);
142153
if (videoId.equals(session.videoId)) {
143-
this.offlineVideoDownloadSessions.remove(session);
154+
this.offlineVideoDownloadSessions.remove(i);
144155
}
145156
}
146157
this.offlineCatalog.deleteVideo(videoId, new OfflineCallback<Boolean>() {
147158
@Override
148159
public void onSuccess(Boolean aBoolean) {
149160
promise.resolve(null);
161+
for (int i = allDownloadedVideos.size() - 1; i >= 0; i--) {
162+
Video video = allDownloadedVideos.get(i);
163+
if (videoId.equals(video.getId())) {
164+
allDownloadedVideos.remove(i);
165+
}
166+
}
167+
listener.onOfflineStorageStateChanged(collectNativeOfflineVideoStatuses());
150168
}
151169

152170
@Override
@@ -192,6 +210,8 @@ private NativeArray collectNativeOfflineVideoStatuses() {
192210
WritableNativeArray statuses = new WritableNativeArray();
193211
for (Video video : this.allDownloadedVideos) {
194212
WritableNativeMap map = new WritableNativeMap();
213+
map.putString(CALLBACK_KEY_ACCOUNT_ID, this.accountId);
214+
map.putString(CALLBACK_KEY_VIDEO_ID, video.getId());
195215
map.putString(CALLBACK_KEY_VIDEO_TOKEN, video.getId());
196216
map.putDouble(CALLBACK_KEY_DOWNLOAD_PROGRESS, 1);
197217
statuses.pushMap(map);
@@ -200,13 +220,15 @@ private NativeArray collectNativeOfflineVideoStatuses() {
200220
if (session.videoId == null) continue;
201221
boolean found = false;
202222
for (Video video : this.allDownloadedVideos) {
203-
if (video.getId().equals(session.videoId)) {
223+
if (session.videoId.equals(video.getId())) {
204224
found = true;
205225
break;
206226
}
207227
}
208228
if (found) continue;
209229
WritableNativeMap map = new WritableNativeMap();
230+
map.putString(CALLBACK_KEY_ACCOUNT_ID, this.accountId);
231+
map.putString(CALLBACK_KEY_VIDEO_ID, session.videoId);
210232
map.putString(CALLBACK_KEY_VIDEO_TOKEN, session.videoId);
211233
map.putDouble(CALLBACK_KEY_DOWNLOAD_PROGRESS, session.downloadProgress);
212234
statuses.pushMap(map);
@@ -246,5 +268,16 @@ private boolean hasOfflineVideoDownloadSessionWithVideoId(String videoId) {
246268
@Override
247269
public void onCompleted(OfflineVideoDownloadSession session) {
248270
this.offlineVideoDownloadSessions.remove(session);
271+
if (session.downloadProgress == 1) {
272+
Map<String, Object> param = new HashMap<>();
273+
param.put(Video.Fields.ID, session.videoId);
274+
this.allDownloadedVideos.add(new Video(param));
275+
}
276+
this.listener.onOfflineStorageStateChanged(collectNativeOfflineVideoStatuses());
277+
}
278+
279+
@Override
280+
public void onProgress() {
281+
this.listener.onOfflineStorageStateChanged(collectNativeOfflineVideoStatuses());
249282
}
250283
}

android/src/main/java/jp/manse/BrightcovePlayerPosterView.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ public BrightcovePlayerPosterView(ThemedReactContext context, ReactApplicationCo
3636
this.applicationContext.addLifecycleEventListener(this);
3737
this.imageView = new ImageView(context);
3838
this.imageView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
39-
this.imageView.setBackgroundColor(Color.RED);
40-
this.imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
39+
this.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
4140
this.addView(imageView);
4241
this.requestLayout();
4342
}

android/src/main/java/jp/manse/BrightcovePlayerUtil.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package jp.manse;
22

3+
import com.facebook.react.bridge.NativeArray;
34
import com.facebook.react.bridge.Promise;
45
import com.facebook.react.bridge.ReactApplicationContext;
56
import com.facebook.react.bridge.ReactContextBaseJavaModule;
67
import com.facebook.react.bridge.ReactMethod;
8+
import com.facebook.react.modules.core.DeviceEventManagerModule;
79

810
import java.util.ArrayList;
911
import java.util.List;
1012

11-
public class BrightcovePlayerUtil extends ReactContextBaseJavaModule {
13+
public class BrightcovePlayerUtil extends ReactContextBaseJavaModule implements BrightcovePlayerAccount.OnBrightcovePlayerAccountListener {
14+
final private static String CALLBACK_OFFLINE_NOTIFICATION = "OfflineNotification";
1215
final private static String ERROR_CODE = "error";
1316
final private static String ERROR_MESSAGE_MISSING_ARGUMENTS = "Both accountId and policyKey must not be null";
1417

@@ -83,14 +86,23 @@ public void getPlaylistWithReferenceId(String referenceId, String accountId, Str
8386
account.getPlaylistWithReferenceId(referenceId, promise);
8487
}
8588

89+
@Override
90+
public void onOfflineStorageStateChanged(NativeArray array) {
91+
this.sendOfflineNotification(array);
92+
}
93+
94+
private void sendOfflineNotification(NativeArray array) {
95+
this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(CALLBACK_OFFLINE_NOTIFICATION, array);
96+
}
97+
8698
private BrightcovePlayerAccount getBrightcovePlayerAccount(String accountId, String policyKey) {
8799
if (accountId == null || policyKey == null) return null;
88100
for (BrightcovePlayerAccount owner: this.brightcovePlayerAccounts) {
89101
if (owner.accountId.equals(accountId) && policyKey.equals(policyKey)) {
90102
return owner;
91103
}
92104
}
93-
BrightcovePlayerAccount owner = new BrightcovePlayerAccount(this.getReactApplicationContext(), accountId, policyKey);
105+
BrightcovePlayerAccount owner = new BrightcovePlayerAccount(this.getReactApplicationContext(), accountId, policyKey, this);
94106
this.brightcovePlayerAccounts.add(owner);
95107
return owner;
96108
}

android/src/main/java/jp/manse/OfflineVideoDownloadSession.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jp.manse;
22

33
import android.support.annotation.NonNull;
4+
import android.util.Log;
45

56
import com.brightcove.player.edge.Catalog;
67
import com.brightcove.player.edge.OfflineCallback;
@@ -35,6 +36,8 @@ public class OfflineVideoDownloadSession extends VideoListener implements MediaD
3536

3637
public interface OnOfflineVideoDownloadSessionListener {
3738
void onCompleted(OfflineVideoDownloadSession session);
39+
40+
void onProgress();
3841
}
3942

4043
public OfflineVideoDownloadSession(ReactApplicationContext context, String accountId, String policyKey, OnOfflineVideoDownloadSessionListener listener) {
@@ -70,7 +73,6 @@ public void onVideo(final Video video) {
7073
this.videoId = video.getId();
7174
this.referenceId = video.getReferenceId();
7275
DownloadStatus status = this.offlineCatalog.getVideoDownloadStatus(video);
73-
this.downloadProgress = status.getProgress() * 0.01;
7476
switch (status.getCode()) {
7577
case DownloadStatus.STATUS_NOT_QUEUED:
7678
this.offlineCatalog.downloadVideo(video);
@@ -116,7 +118,8 @@ public void onDownloadStarted(@NonNull Video video, long l, @NonNull Map<String,
116118

117119
@Override
118120
public void onDownloadProgress(@NonNull Video video, @NonNull DownloadStatus downloadStatus) {
119-
this.downloadProgress = downloadStatus.getProgress() * 0.01;
121+
this.listener.onProgress();
122+
this.downloadProgress = downloadStatus.getProgress() * 0.01d;
120123
}
121124

122125
@Override

0 commit comments

Comments
 (0)