Fix URLs with query strings at the end, e.g. ?size=large

Fix HLS Playlists (only support mp4, m4v and mov file extension)

Add debug logging for guiding library consumers about why their video is not cached
This commit is contained in:
Laurin Quast 2018-08-05 23:06:25 +02:00
parent 9f2cb8f92d
commit c9e2ba0547
2 changed files with 87 additions and 24 deletions

View File

@ -311,13 +311,7 @@ static int const RCTVideoUnset = -1;
[self removePlayerLayer]; [self removePlayerLayer];
[self removePlayerTimeObserver]; [self removePlayerTimeObserver];
[self removePlayerItemObservers]; [self removePlayerItemObservers];
[self playerItemForSource:source withCallback:^(AVPlayerItem * playerItem) {
[self didSetPlayerItemWithSource:source playerItem:playerItem];
}];
}
- (void) didSetPlayerItemWithSource:(NSDictionary *)source playerItem:(AVPlayerItem *) playerItem
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// perform on next run loop, otherwise other passed react-props may not be set // perform on next run loop, otherwise other passed react-props may not be set
@ -433,10 +427,29 @@ static int const RCTVideoUnset = -1;
if (isNetwork) { if (isNetwork) {
#if __has_include(<react-native-video/RCTVideoCache.h>) #if __has_include(<react-native-video/RCTVideoCache.h>)
[_videoCache getItemForUri:uri withCallback:^(AVAsset * _Nullable cachedAsset) { [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) {
if (cachedAsset) { switch (videoCacheStatus) {
[self playerItemPrepareText:cachedAsset assetOptions:assetOptions withCallback:handler]; case RCTVideoCacheStatusMissingFileExtension: {
return; #ifdef DEBUG
NSLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md.", uri);
#endif
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions];
[self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler];
return;
}
case RCTVideoCacheStatusUnsupportedFileExtension: {
#ifdef DEBUG
NSLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md.", uri);
#endif
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions];
[self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler];
return;
}
default:
if (cachedAsset) {
[self playerItemPrepareText:cachedAsset assetOptions:assetOptions withCallback:handler];
return;
}
} }
#endif #endif
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
@ -1138,6 +1151,7 @@ static int const RCTVideoUnset = -1;
#endif #endif
}]; }];
} }
#endif #endif
#pragma mark - RCTVideoPlayerViewControllerDelegate #pragma mark - RCTVideoPlayerViewControllerDelegate

View File

@ -57,7 +57,11 @@
- (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler; - (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler;
{ {
NSString * key = [[self generateHashForUrl:uri] stringByAppendingPathExtension: [uri pathExtension]]; NSString *key = [self generateCacheKeyForUri:uri];
if (key == nil) {
handler(NO);
return;
}
[self saveDataToTemporaryStorage:data key:key]; [self saveDataToTemporaryStorage:data key:key];
[self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) { [self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) {
if (response.error) { if (response.error) {
@ -90,23 +94,68 @@
return YES; return YES;
} }
- (void)getItemForUri:(NSString *)uri withCallback:(void(^)(AVAsset * _Nullable)) handler { - (NSString *)generateCacheKeyForUri:(NSString *)uri {
NSString * key = [[self generateHashForUrl:uri] stringByAppendingPathExtension: [uri pathExtension]]; NSString *uriWithoutQueryParams = uri;
AVURLAsset * temporaryAsset = [self getItemFromTemporaryStorage:key]; // parse file extension
if (temporaryAsset != nil) { if ([uri rangeOfString:@"?"].location != NSNotFound) {
handler(temporaryAsset); NSArray<NSString*> * components = [uri componentsSeparatedByString:@"?"];
return; uriWithoutQueryParams = [components objectAtIndex:0];
} }
[self.videoCache loadDataForKey:key withCallback:^(SPTPersistentCacheResponse * _Nonnull response) { NSString * pathExtension = [uriWithoutQueryParams pathExtension];
if (response.record == nil || response.record.data == nil) { NSArray * supportedExtensions = @[@"m4v", @"mp4", @"mov"];
handler(nil); if ([supportedExtensions containsObject:pathExtension] == NO) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Missing file extension.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Missing file extension.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Missing file extension.", nil)
};
NSError *error = [NSError errorWithDomain:@"RCTVideoCache"
code:RCTVideoCacheStatusMissingFileExtension userInfo:userInfo];
@throw error;
} else if ([pathExtension isEqualToString:@"m3u8"]) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Missing file extension.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Missing file extension.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Missing file extension.", nil)
};
NSError *error = [NSError errorWithDomain:@"RCTVideoCache"
code:RCTVideoCacheStatusUnsupportedFileExtension userInfo:userInfo];
@throw error;
}
return [[self generateHashForUrl:uri] stringByAppendingPathExtension:pathExtension];
}
- (void)getItemForUri:(NSString *)uri withCallback:(void(^)(RCTVideoCacheStatus, AVAsset * _Nullable)) handler {
@try {
NSString *key = [self generateCacheKeyForUri:uri];
AVURLAsset * temporaryAsset = [self getItemFromTemporaryStorage:key];
if (temporaryAsset != nil) {
handler(RCTVideoCacheStatusAvailable, temporaryAsset);
return; return;
} }
[self saveDataToTemporaryStorage:response.record.data key:key];
handler([self getItemFromTemporaryStorage:key]); [self.videoCache loadDataForKey:key withCallback:^(SPTPersistentCacheResponse * _Nonnull response) {
} onQueue:dispatch_get_main_queue()]; if (response.record == nil || response.record.data == nil) {
handler(RCTVideoCacheStatusNotAvailable, nil);
return;
}
[self saveDataToTemporaryStorage:response.record.data key:key];
handler(RCTVideoCacheStatusAvailable, [self getItemFromTemporaryStorage:key]);
} onQueue:dispatch_get_main_queue()];
} @catch (NSError * err) {
switch (err.code) {
case RCTVideoCacheStatusMissingFileExtension:
handler(RCTVideoCacheStatusMissingFileExtension, nil);
return;
case RCTVideoCacheStatusUnsupportedFileExtension:
handler(RCTVideoCacheStatusUnsupportedFileExtension, nil);
return;
default:
@throw err;
}
}
} }
- (NSString *) generateHashForUrl:(NSString *)string { - (NSString *) generateHashForUrl:(NSString *)string {