Skip to content

Commit

Permalink
Merge pull request #805 from emrah88/master
Browse files Browse the repository at this point in the history
added optional request headers for remote assests (android & ios)
  • Loading branch information
cobarx committed Jun 25, 2018
2 parents 15174b7 + 448e57b commit 5f1ce1a
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 22 deletions.
24 changes: 24 additions & 0 deletions Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,29 @@ export default class Video extends Component {
setNativeProps(nativeProps) {
this._root.setNativeProps(nativeProps);
}

toTypeString(x) {
switch (typeof x) {
case "object":
return x instanceof Date
? x.toISOString()
: JSON.stringify(x); // object, null
case "undefined":
return "";
default: // boolean, number, string
return x.toString();
}
}

stringsOnlyObject(obj) {
const strObj = {};

Object.keys(obj).forEach(x => {
strObj[x] = this.toTypeString(obj[x]);
});

return strObj;
}

seek = (time, tolerance = 100) => {
if (Platform.OS === 'ios') {
Expand Down Expand Up @@ -202,6 +225,7 @@ export default class Video extends Component {
type: source.type || '',
mainVer: source.mainVer || 0,
patchVer: source.patchVer || 0,
requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}
},
onVideoLoadStart: this._onLoadStart,
onVideoLoad: this._onLoad,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import okhttp3.Cookie;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import java.util.Map;


public class DataSourceUtil {

Expand Down Expand Up @@ -49,9 +51,10 @@ public static void setRawDataSourceFactory(DataSource.Factory factory) {
DataSourceUtil.rawDataSourceFactory = factory;
}

public static DataSource.Factory getDefaultDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) {
if (defaultDataSourceFactory == null) {
defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter);

public static DataSource.Factory getDefaultDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
if (defaultDataSourceFactory == null || (requestHeaders != null && !requestHeaders.isEmpty())) {
defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter, requestHeaders);
}
return defaultDataSourceFactory;
}
Expand All @@ -64,17 +67,21 @@ private static DataSource.Factory buildRawDataSourceFactory(ReactContext context
return new RawResourceDataSourceFactory(context.getApplicationContext());
}

private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) {
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
return new DefaultDataSourceFactory(context, bandwidthMeter,
buildHttpDataSourceFactory(context, bandwidthMeter));
buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
}

private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter) {
private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
OkHttpClient client = OkHttpClientProvider.getOkHttpClient();
CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
container.setCookieJar(new JavaNetCookieJar(handler));
return new OkHttpDataSourceFactory(client, getUserAgent(context), bandwidthMeter);
}
OkHttpDataSourceFactory okHttpDataSourceFactory = new OkHttpDataSourceFactory(client, getUserAgent(context), bandwidthMeter);

if (requestHeaders != null)
okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders);

return okHttpDataSourceFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.lang.Math;
import java.util.Map;
import java.lang.Object;
import java.util.ArrayList;

Expand Down Expand Up @@ -117,6 +118,7 @@ class ReactExoplayerView extends FrameLayout implements
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
private boolean useTextureView = false;
private Map<String, String> requestHeaders;
// \ End props

// React
Expand Down Expand Up @@ -416,7 +418,7 @@ private void clearResumePosition() {
* @return A new DataSource factory.
*/
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, useBandwidthMeter ? BANDWIDTH_METER : null);
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, useBandwidthMeter ? BANDWIDTH_METER : null, requestHeaders);
}

// AudioManager.OnAudioFocusChangeListener implementation
Expand Down Expand Up @@ -660,14 +662,15 @@ public void onMetadata(Metadata metadata) {

// ReactExoplayerViewManager public api

public void setSrc(final Uri uri, final String extension) {
public void setSrc(final Uri uri, final String extension, Map<String, String> headers) {
if (uri != null) {
boolean isOriginalSourceNull = srcUri == null;
boolean isSourceEqual = uri.equals(srcUri);

this.srcUri = uri;
this.extension = extension;
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER);
this.requestHeaders = headers;
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER, this.requestHeaders);

if (!isOriginalSourceNull && !isSourceEqual) {
reloadSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.facebook.react.uimanager.annotations.ReactProp;
import com.google.android.exoplayer2.upstream.RawResourceDataSource;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;
Expand All @@ -24,6 +25,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_SRC = "src";
private static final String PROP_SRC_URI = "uri";
private static final String PROP_SRC_TYPE = "type";
private static final String PROP_SRC_HEADERS = "requestHeaders";
private static final String PROP_RESIZE_MODE = "resizeMode";
private static final String PROP_REPEAT = "repeat";
private static final String PROP_SELECTED_TEXT_TRACK = "selectedTextTrack";
Expand Down Expand Up @@ -80,6 +82,8 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src
Context context = videoView.getContext().getApplicationContext();
String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;


if (TextUtils.isEmpty(uriString)) {
return;
Expand All @@ -89,7 +93,7 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src
Uri srcUri = Uri.parse(uriString);

if (srcUri != null) {
videoView.setSrc(srcUri, extension);
videoView.setSrc(srcUri, extension, headers);
}
} else {
int identifier = context.getResources().getIdentifier(
Expand Down Expand Up @@ -208,4 +212,28 @@ private boolean startsWithValidScheme(String uriString) {
}
return ResizeMode.RESIZE_MODE_FIT;
}

/**
* toStringMap converts a {@link ReadableMap} into a HashMap.
*
* @param readableMap The ReadableMap to be conveted.
* @return A HashMap containing the data that was in the ReadableMap.
* @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
*/
public static Map<String, String> toStringMap(@Nullable ReadableMap readableMap) {
if (readableMap == null)
return null;

com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
if (!iterator.hasNextKey())
return null;

Map<String, String> result = new HashMap<>();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
result.put(key, readableMap.getString(key));
}

return result;
}
}
51 changes: 45 additions & 6 deletions android/src/main/java/com/brentvatne/react/ReactVideoView.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.android.vending.expansion.zipfile.ZipResourceFile;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
Expand All @@ -30,6 +31,8 @@
import java.lang.Math;
import java.math.BigDecimal;

import javax.annotation.Nullable;

@SuppressLint("ViewConstructor")
public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer
.OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, LifecycleEventListener, MediaController.MediaPlayerControl {
Expand Down Expand Up @@ -89,6 +92,7 @@ public String toString() {

private String mSrcUriString = null;
private String mSrcType = "mp4";
private ReadableMap mRequestHeaders = null;
private boolean mSrcIsNetwork = false;
private boolean mSrcIsAsset = false;
private ScalableType mResizeMode = ScalableType.LEFT_TOP;
Expand Down Expand Up @@ -206,16 +210,17 @@ public void cleanupMediaPlayerResources() {
}
}

public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset) {
setSrc(uriString,type,isNetwork,isAsset,0,0);
public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders) {
setSrc(uriString, type, isNetwork, isAsset, requestHeaders, 0, 0);
}

public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final int expansionMainVersion, final int expansionPatchVersion) {
public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders, final int expansionMainVersion, final int expansionPatchVersion) {

mSrcUriString = uriString;
mSrcType = type;
mSrcIsNetwork = isNetwork;
mSrcIsAsset = isAsset;
mRequestHeaders = requestHeaders;
mMainVer = expansionMainVersion;
mPatchVer = expansionPatchVersion;

Expand Down Expand Up @@ -244,7 +249,15 @@ public void setSrc(final String uriString, final String type, final boolean isNe
headers.put("Cookie", cookie);
}

setDataSource(uriString);
if (mRequestHeaders != null) {
headers.putAll(toStringMap(mRequestHeaders));
}

/* According to https://github.com/react-native-community/react-native-video/pull/537
* there is an issue with this where it can cause a IOException.
* TODO: diagnose this exception and fix it
*/
setDataSource(mThemedReactContext, parsedUrl, headers);
} else if (isAsset) {
if (uriString.startsWith("content://")) {
Uri parsedUrl = Uri.parse(uriString);
Expand Down Expand Up @@ -290,8 +303,13 @@ public void setSrc(final String uriString, final String type, final boolean isNe
}

WritableMap src = Arguments.createMap();

WritableMap wRequestHeaders = Arguments.createMap();
wRequestHeaders.merge(mRequestHeaders);

src.putString(ReactVideoViewManager.PROP_SRC_URI, uriString);
src.putString(ReactVideoViewManager.PROP_SRC_TYPE, type);
src.putMap(ReactVideoViewManager.PROP_SRC_HEADERS, wRequestHeaders);
src.putBoolean(ReactVideoViewManager.PROP_SRC_IS_NETWORK, isNetwork);
if(mMainVer>0) {
src.putInt(ReactVideoViewManager.PROP_SRC_MAINVER, mMainVer);
Expand Down Expand Up @@ -581,10 +599,10 @@ protected void onAttachedToWindow() {
super.onAttachedToWindow();

if(mMainVer>0) {
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork,mSrcIsAsset,mMainVer,mPatchVer);
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders, mMainVer, mPatchVer);
}
else {
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork,mSrcIsAsset);
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders);
}

}
Expand Down Expand Up @@ -618,4 +636,25 @@ public void run() {
@Override
public void onHostDestroy() {
}

/**
* toStringMap converts a {@link ReadableMap} into a HashMap.
*
* @param readableMap The ReadableMap to be conveted.
* @return A HashMap containing the data that was in the ReadableMap.
* @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
*/
public static Map<String, String> toStringMap(@Nullable ReadableMap readableMap) {
Map<String, String> result = new HashMap<>();
if (readableMap == null)
return result;

com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
result.put(key, readableMap.getString(key));
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class ReactVideoViewManager extends SimpleViewManager<ReactVideoView> {
public static final String PROP_SRC = "src";
public static final String PROP_SRC_URI = "uri";
public static final String PROP_SRC_TYPE = "type";
public static final String PROP_SRC_HEADERS = "requestHeaders";
public static final String PROP_SRC_IS_NETWORK = "isNetwork";
public static final String PROP_SRC_MAINVER = "mainVer";
public static final String PROP_SRC_PATCHVER = "patchVer";
Expand Down Expand Up @@ -86,6 +87,7 @@ public void setSrc(final ReactVideoView videoView, @Nullable ReadableMap src) {
src.getString(PROP_SRC_TYPE),
src.getBoolean(PROP_SRC_IS_NETWORK),
src.getBoolean(PROP_SRC_IS_ASSET),
src.getMap(PROP_SRC_HEADERS),
mainVer,
patchVer
);
Expand All @@ -95,8 +97,9 @@ public void setSrc(final ReactVideoView videoView, @Nullable ReadableMap src) {
src.getString(PROP_SRC_URI),
src.getString(PROP_SRC_TYPE),
src.getBoolean(PROP_SRC_IS_NETWORK),
src.getBoolean(PROP_SRC_IS_ASSET)
);
src.getBoolean(PROP_SRC_IS_ASSET),
src.getMap(PROP_SRC_HEADERS)
);
}
}

Expand Down
11 changes: 9 additions & 2 deletions ios/RCTVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,21 @@ - (AVPlayerItem*)playerItemForSource:(NSDictionary *)source
bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]];
NSString *uri = [source objectForKey:@"uri"];
NSString *type = [source objectForKey:@"type"];

NSDictionary *headers = [source objectForKey:@"requestHeaders"];

NSURL *url = (isNetwork || isAsset) ?
[NSURL URLWithString:uri] :
[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]];

if (isNetwork) {
NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc]init];
if ([headers count] > 0) {
[assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
}
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetHTTPCookiesKey : cookies}];
[assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey];

AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions];
return [AVPlayerItem playerItemWithAsset:asset];
}
else if (isAsset) {
Expand Down

0 comments on commit 5f1ce1a

Please sign in to comment.