| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915 |
- // Copyright 2014 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import "YTPlayerView.h"
- // These are instances of NSString because we get them from parsing a URL. It would be silly to
- // convert these into an integer just to have to convert the URL query string value into an integer
- // as well for the sake of doing a value comparison. A full list of response error codes can be
- // found here:
- // https://developers.google.com/youtube/iframe_api_reference
- NSString static *const kYTPlayerStateUnstartedCode = @"-1";
- NSString static *const kYTPlayerStateEndedCode = @"0";
- NSString static *const kYTPlayerStatePlayingCode = @"1";
- NSString static *const kYTPlayerStatePausedCode = @"2";
- NSString static *const kYTPlayerStateBufferingCode = @"3";
- NSString static *const kYTPlayerStateCuedCode = @"5";
- NSString static *const kYTPlayerStateUnknownCode = @"unknown";
- // Constants representing playback quality.
- NSString static *const kYTPlaybackQualitySmallQuality = @"small";
- NSString static *const kYTPlaybackQualityMediumQuality = @"medium";
- NSString static *const kYTPlaybackQualityLargeQuality = @"large";
- NSString static *const kYTPlaybackQualityHD720Quality = @"hd720";
- NSString static *const kYTPlaybackQualityHD1080Quality = @"hd1080";
- NSString static *const kYTPlaybackQualityHighResQuality = @"highres";
- NSString static *const kYTPlaybackQualityAutoQuality = @"auto";
- NSString static *const kYTPlaybackQualityDefaultQuality = @"default";
- NSString static *const kYTPlaybackQualityUnknownQuality = @"unknown";
- // Constants representing YouTube player errors.
- NSString static *const kYTPlayerErrorInvalidParamErrorCode = @"2";
- NSString static *const kYTPlayerErrorHTML5ErrorCode = @"5";
- NSString static *const kYTPlayerErrorVideoNotFoundErrorCode = @"100";
- NSString static *const kYTPlayerErrorNotEmbeddableErrorCode = @"101";
- NSString static *const kYTPlayerErrorCannotFindVideoErrorCode = @"105";
- NSString static *const kYTPlayerErrorSameAsNotEmbeddableErrorCode = @"150";
- // Constants representing player callbacks.
- NSString static *const kYTPlayerCallbackOnReady = @"onReady";
- NSString static *const kYTPlayerCallbackOnStateChange = @"onStateChange";
- NSString static *const kYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange";
- NSString static *const kYTPlayerCallbackOnError = @"onError";
- NSString static *const kYTPlayerCallbackOnPlayTime = @"onPlayTime";
- NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIReady = @"onYouTubeIframeAPIReady";
- NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad = @"onYouTubeIframeAPIFailedToLoad";
- NSString static *const kYTPlayerEmbedUrlRegexPattern = @"^http(s)://(www.)youtube.com/embed/(.*)$";
- NSString static *const kYTPlayerAdUrlRegexPattern = @"^http(s)://pubads.g.doubleclick.net/pagead/conversion/";
- NSString static *const kYTPlayerOAuthRegexPattern = @"^http(s)://accounts.google.com/o/oauth2/(.*)$";
- NSString static *const kYTPlayerStaticProxyRegexPattern = @"^https://content.googleapis.com/static/proxy.html(.*)$";
- NSString static *const kYTPlayerSyndicationRegexPattern = @"^https://tpc.googlesyndication.com/sodar/(.*).html$";
- @interface YTPlayerView()
- @property (nonatomic, strong) NSURL *originURL;
- @property (nonatomic, weak) UIView *initialLoadingView;
- @end
- @implementation YTPlayerView
- - (BOOL)loadWithVideoId:(NSString *)videoId {
- return [self loadWithVideoId:videoId playerVars:nil];
- }
- - (BOOL)loadWithPlaylistId:(NSString *)playlistId {
- return [self loadWithPlaylistId:playlistId playerVars:nil];
- }
- - (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars {
- if (!playerVars) {
- playerVars = @{};
- }
- NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars };
- return [self loadWithPlayerParams:playerParams];
- }
- - (BOOL)loadWithPlaylistId:(NSString *)playlistId playerVars:(NSDictionary *)playerVars {
- // Mutable copy because we may have been passed an immutable config dictionary.
- NSMutableDictionary *tempPlayerVars = [[NSMutableDictionary alloc] init];
- [tempPlayerVars setValue:@"playlist" forKey:@"listType"];
- [tempPlayerVars setValue:playlistId forKey:@"list"];
- if (playerVars) {
- [tempPlayerVars addEntriesFromDictionary:playerVars];
- }
- NSDictionary *playerParams = @{ @"playerVars" : tempPlayerVars };
- return [self loadWithPlayerParams:playerParams];
- }
- #pragma mark - Player methods
- - (void)playVideo {
- [self stringFromEvaluatingJavaScript:@"player.playVideo();"];
- }
- - (void)pauseVideo {
- [self notifyDelegateOfYouTubeCallbackUrl:[NSURL URLWithString:[NSString stringWithFormat:@"ytplayer://onStateChange?data=%@", kYTPlayerStatePausedCode]]];
- [self stringFromEvaluatingJavaScript:@"player.pauseVideo();"];
- }
- - (void)stopVideo {
- [self stringFromEvaluatingJavaScript:@"player.stopVideo();"];
- }
- - (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead {
- NSNumber *secondsValue = [NSNumber numberWithFloat:seekToSeconds];
- NSString *allowSeekAheadValue = [self stringForJSBoolean:allowSeekAhead];
- NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, %@);", secondsValue, allowSeekAheadValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- #pragma mark - Cueing methods
- - (void)cueVideoById:(NSString *)videoId
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.cueVideoById('%@', %@, '%@');",
- videoId, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)cueVideoById:(NSString *)videoId
- startSeconds:(float)startSeconds
- endSeconds:(float)endSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.cueVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});", videoId, startSecondsValue, endSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)loadVideoById:(NSString *)videoId
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.loadVideoById('%@', %@, '%@');",
- videoId, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)loadVideoById:(NSString *)videoId
- startSeconds:(float)startSeconds
- endSeconds:(float)endSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.loadVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});",videoId, startSecondsValue, endSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)cueVideoByURL:(NSString *)videoURL
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, '%@');",
- videoURL, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)cueVideoByURL:(NSString *)videoURL
- startSeconds:(float)startSeconds
- endSeconds:(float)endSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, %@, '%@');",
- videoURL, startSecondsValue, endSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)loadVideoByURL:(NSString *)videoURL
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, '%@');",
- videoURL, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)loadVideoByURL:(NSString *)videoURL
- startSeconds:(float)startSeconds
- endSeconds:(float)endSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, %@, '%@');",
- videoURL, startSecondsValue, endSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- #pragma mark - Cueing methods for lists
- - (void)cuePlaylistByPlaylistId:(NSString *)playlistId
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId];
- [self cuePlaylist:playlistIdString
- index:index
- startSeconds:startSeconds
- suggestedQuality:suggestedQuality];
- }
- - (void)cuePlaylistByVideos:(NSArray *)videoIds
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- [self cuePlaylist:[self stringFromVideoIdArray:videoIds]
- index:index
- startSeconds:startSeconds
- suggestedQuality:suggestedQuality];
- }
- - (void)loadPlaylistByPlaylistId:(NSString *)playlistId
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId];
- [self loadPlaylist:playlistIdString
- index:index
- startSeconds:startSeconds
- suggestedQuality:suggestedQuality];
- }
- - (void)loadPlaylistByVideos:(NSArray *)videoIds
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- [self loadPlaylist:[self stringFromVideoIdArray:videoIds]
- index:index
- startSeconds:startSeconds
- suggestedQuality:suggestedQuality];
- }
- #pragma mark - Setting the playback rate
- - (float)playbackRate {
- NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackRate();"];
- return [returnValue floatValue];
- }
- - (void)setPlaybackRate:(float)suggestedRate {
- NSString *command = [NSString stringWithFormat:@"player.setPlaybackRate(%f);", suggestedRate];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (NSArray *)availablePlaybackRates {
- NSString *returnValue =
- [self stringFromEvaluatingJavaScript:@"player.getAvailablePlaybackRates();"];
- NSData *playbackRateData = [returnValue dataUsingEncoding:NSUTF8StringEncoding];
- NSError *jsonDeserializationError;
- NSArray *playbackRates = [NSJSONSerialization JSONObjectWithData:playbackRateData
- options:kNilOptions
- error:&jsonDeserializationError];
- if (jsonDeserializationError) {
- return nil;
- }
- return playbackRates;
- }
- #pragma mark - Setting playback behavior for playlists
- - (void)setLoop:(BOOL)loop {
- NSString *loopPlayListValue = [self stringForJSBoolean:loop];
- NSString *command = [NSString stringWithFormat:@"player.setLoop(%@);", loopPlayListValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- - (void)setShuffle:(BOOL)shuffle {
- NSString *shufflePlayListValue = [self stringForJSBoolean:shuffle];
- NSString *command = [NSString stringWithFormat:@"player.setShuffle(%@);", shufflePlayListValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- #pragma mark - Playback status
- - (float)videoLoadedFraction {
- return [[self stringFromEvaluatingJavaScript:@"player.getVideoLoadedFraction();"] floatValue];
- }
- - (YTPlayerState)playerState {
- NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlayerState();"];
- return [YTPlayerView playerStateForString:returnValue];
- }
- - (float)currentTime {
- return [[self stringFromEvaluatingJavaScript:@"player.getCurrentTime();"] floatValue];
- }
- // Playback quality
- - (YTPlaybackQuality)playbackQuality {
- NSString *qualityValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackQuality();"];
- return [YTPlayerView playbackQualityForString:qualityValue];
- }
- - (void)setPlaybackQuality:(YTPlaybackQuality)suggestedQuality {
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.setPlaybackQuality('%@');", qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- #pragma mark - Video information methods
- - (NSTimeInterval)duration {
- return [[self stringFromEvaluatingJavaScript:@"player.getDuration();"] doubleValue];
- }
- - (NSURL *)videoUrl {
- return [NSURL URLWithString:[self stringFromEvaluatingJavaScript:@"player.getVideoUrl();"]];
- }
- - (NSString *)videoEmbedCode {
- return [self stringFromEvaluatingJavaScript:@"player.getVideoEmbedCode();"];
- }
- #pragma mark - Playlist methods
- - (NSArray *)playlist {
- NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylist();"];
- NSData *playlistData = [returnValue dataUsingEncoding:NSUTF8StringEncoding];
- NSError *jsonDeserializationError;
- NSArray *videoIds = [NSJSONSerialization JSONObjectWithData:playlistData
- options:kNilOptions
- error:&jsonDeserializationError];
- if (jsonDeserializationError) {
- return nil;
- }
- return videoIds;
- }
- - (int)playlistIndex {
- NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylistIndex();"];
- return [returnValue intValue];
- }
- #pragma mark - Playing a video in a playlist
- - (void)nextVideo {
- [self stringFromEvaluatingJavaScript:@"player.nextVideo();"];
- }
- - (void)previousVideo {
- [self stringFromEvaluatingJavaScript:@"player.previousVideo();"];
- }
- - (void)playVideoAt:(int)index {
- NSString *command =
- [NSString stringWithFormat:@"player.playVideoAt(%@);", [NSNumber numberWithInt:index]];
- [self stringFromEvaluatingJavaScript:command];
- }
- #pragma mark - Helper methods
- - (NSArray *)availableQualityLevels {
- NSString *returnValue =
- [self stringFromEvaluatingJavaScript:@"player.getAvailableQualityLevels().toString();"];
- if(!returnValue) return nil;
- NSArray *rawQualityValues = [returnValue componentsSeparatedByString:@","];
- NSMutableArray *levels = [[NSMutableArray alloc] init];
- for (NSString *rawQualityValue in rawQualityValues) {
- YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:rawQualityValue];
- [levels addObject:[NSNumber numberWithInt:quality]];
- }
- return levels;
- }
- - (BOOL)webView:(UIWebView *)webView
- shouldStartLoadWithRequest:(NSURLRequest *)request
- navigationType:(UIWebViewNavigationType)navigationType {
- if ([request.URL.host isEqual: self.originURL.host]) {
- return YES;
- } else if ([request.URL.scheme isEqual:@"ytplayer"]) {
- [self notifyDelegateOfYouTubeCallbackUrl:request.URL];
- return NO;
- } else if ([request.URL.scheme isEqual: @"http"] || [request.URL.scheme isEqual:@"https"]) {
- return [self handleHttpNavigationToUrl:request.URL];
- }
- return YES;
- }
- - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
- if (self.initialLoadingView) {
- [self.initialLoadingView removeFromSuperview];
- }
- }
- /**
- * Convert a quality value from NSString to the typed enum value.
- *
- * @param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080".
- * @return An enum value representing the playback quality.
- */
- + (YTPlaybackQuality)playbackQualityForString:(NSString *)qualityString {
- YTPlaybackQuality quality = kYTPlaybackQualityUnknown;
- if ([qualityString isEqualToString:kYTPlaybackQualitySmallQuality]) {
- quality = kYTPlaybackQualitySmall;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityMediumQuality]) {
- quality = kYTPlaybackQualityMedium;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityLargeQuality]) {
- quality = kYTPlaybackQualityLarge;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityHD720Quality]) {
- quality = kYTPlaybackQualityHD720;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityHD1080Quality]) {
- quality = kYTPlaybackQualityHD1080;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityHighResQuality]) {
- quality = kYTPlaybackQualityHighRes;
- } else if ([qualityString isEqualToString:kYTPlaybackQualityAutoQuality]) {
- quality = kYTPlaybackQualityAuto;
- }
- return quality;
- }
- /**
- * Convert a |YTPlaybackQuality| value from the typed value to NSString.
- *
- * @param quality A |YTPlaybackQuality| parameter.
- * @return An |NSString| value to be used in the JavaScript bridge.
- */
- + (NSString *)stringForPlaybackQuality:(YTPlaybackQuality)quality {
- switch (quality) {
- case kYTPlaybackQualitySmall:
- return kYTPlaybackQualitySmallQuality;
- case kYTPlaybackQualityMedium:
- return kYTPlaybackQualityMediumQuality;
- case kYTPlaybackQualityLarge:
- return kYTPlaybackQualityLargeQuality;
- case kYTPlaybackQualityHD720:
- return kYTPlaybackQualityHD720Quality;
- case kYTPlaybackQualityHD1080:
- return kYTPlaybackQualityHD1080Quality;
- case kYTPlaybackQualityHighRes:
- return kYTPlaybackQualityHighResQuality;
- case kYTPlaybackQualityAuto:
- return kYTPlaybackQualityAutoQuality;
- default:
- return kYTPlaybackQualityUnknownQuality;
- }
- }
- /**
- * Convert a state value from NSString to the typed enum value.
- *
- * @param stateString A string representing player state. Ex: "-1", "0", "1".
- * @return An enum value representing the player state.
- */
- + (YTPlayerState)playerStateForString:(NSString *)stateString {
- YTPlayerState state = kYTPlayerStateUnknown;
- if ([stateString isEqualToString:kYTPlayerStateUnstartedCode]) {
- state = kYTPlayerStateUnstarted;
- } else if ([stateString isEqualToString:kYTPlayerStateEndedCode]) {
- state = kYTPlayerStateEnded;
- } else if ([stateString isEqualToString:kYTPlayerStatePlayingCode]) {
- state = kYTPlayerStatePlaying;
- } else if ([stateString isEqualToString:kYTPlayerStatePausedCode]) {
- state = kYTPlayerStatePaused;
- } else if ([stateString isEqualToString:kYTPlayerStateBufferingCode]) {
- state = kYTPlayerStateBuffering;
- } else if ([stateString isEqualToString:kYTPlayerStateCuedCode]) {
- state = kYTPlayerStateQueued;
- }
- return state;
- }
- /**
- * Convert a state value from the typed value to NSString.
- *
- * @param quality A |YTPlayerState| parameter.
- * @return A string value to be used in the JavaScript bridge.
- */
- + (NSString *)stringForPlayerState:(YTPlayerState)state {
- switch (state) {
- case kYTPlayerStateUnstarted:
- return kYTPlayerStateUnstartedCode;
- case kYTPlayerStateEnded:
- return kYTPlayerStateEndedCode;
- case kYTPlayerStatePlaying:
- return kYTPlayerStatePlayingCode;
- case kYTPlayerStatePaused:
- return kYTPlayerStatePausedCode;
- case kYTPlayerStateBuffering:
- return kYTPlayerStateBufferingCode;
- case kYTPlayerStateQueued:
- return kYTPlayerStateCuedCode;
- default:
- return kYTPlayerStateUnknownCode;
- }
- }
- #pragma mark - Private methods
- /**
- * Private method to handle "navigation" to a callback URL of the format
- * ytplayer://action?data=someData
- * This is how the UIWebView communicates with the containing Objective-C code.
- * Side effects of this method are that it calls methods on this class's delegate.
- *
- * @param url A URL of the format ytplayer://action?data=value.
- */
- - (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url {
- NSString *action = url.host;
-
- // NSLog(@"action:%@",action);
-
- // We know the query can only be of the format ytplayer://action?data=SOMEVALUE,
- // so we parse out the value.
- NSString *query = url.query;
- NSString *data;
- if (query) {
- data = [query componentsSeparatedByString:@"="][1];
- }
-
- if ([action isEqual:kYTPlayerCallbackOnReady]) {
- if (self.initialLoadingView) {
- [self.initialLoadingView removeFromSuperview];
- }
- if ([self.delegate respondsToSelector:@selector(playerViewDidBecomeReady:)]) {
- [self.delegate playerViewDidBecomeReady:self];
- }
- } else if ([action isEqual:kYTPlayerCallbackOnStateChange]) {
- if ([self.delegate respondsToSelector:@selector(playerView:didChangeToState:)]) {
- YTPlayerState state = kYTPlayerStateUnknown;
-
- if ([data isEqual:kYTPlayerStateEndedCode]) {
- state = kYTPlayerStateEnded;
- } else if ([data isEqual:kYTPlayerStatePlayingCode]) {
- state = kYTPlayerStatePlaying;
- } else if ([data isEqual:kYTPlayerStatePausedCode]) {
- state = kYTPlayerStatePaused;
- } else if ([data isEqual:kYTPlayerStateBufferingCode]) {
- state = kYTPlayerStateBuffering;
- } else if ([data isEqual:kYTPlayerStateCuedCode]) {
- state = kYTPlayerStateQueued;
- } else if ([data isEqual:kYTPlayerStateUnstartedCode]) {
- state = kYTPlayerStateUnstarted;
- }
-
- [self.delegate playerView:self didChangeToState:state];
- }
- } else if ([action isEqual:kYTPlayerCallbackOnPlaybackQualityChange]) {
- if ([self.delegate respondsToSelector:@selector(playerView:didChangeToQuality:)]) {
- YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:data];
- [self.delegate playerView:self didChangeToQuality:quality];
- }
- } else if ([action isEqual:kYTPlayerCallbackOnError]) {
- if ([self.delegate respondsToSelector:@selector(playerView:receivedError:)]) {
- YTPlayerError error = kYTPlayerErrorUnknown;
-
- if ([data isEqual:kYTPlayerErrorInvalidParamErrorCode]) {
- error = kYTPlayerErrorInvalidParam;
- } else if ([data isEqual:kYTPlayerErrorHTML5ErrorCode]) {
- error = kYTPlayerErrorHTML5Error;
- } else if ([data isEqual:kYTPlayerErrorNotEmbeddableErrorCode] ||
- [data isEqual:kYTPlayerErrorSameAsNotEmbeddableErrorCode]) {
- error = kYTPlayerErrorNotEmbeddable;
- } else if ([data isEqual:kYTPlayerErrorVideoNotFoundErrorCode] ||
- [data isEqual:kYTPlayerErrorCannotFindVideoErrorCode]) {
- error = kYTPlayerErrorVideoNotFound;
- }
-
- [self.delegate playerView:self receivedError:error];
- }
- } else if ([action isEqualToString:kYTPlayerCallbackOnPlayTime]) {
- if ([self.delegate respondsToSelector:@selector(playerView:didPlayTime:)]) {
- float time = [data floatValue];
- [self.delegate playerView:self didPlayTime:time];
- }
- } else if ([action isEqualToString:kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad]) {
- if (self.initialLoadingView) {
- [self.initialLoadingView removeFromSuperview];
- }
- }
- else if ([action isEqualToString:@"restriction"])
- {
- self.webView.hidden = true;
- }
- }
- -(NSString*) Embed2VID:(NSString*) iframe_embed
- {
-
- NSString* content = iframe_embed;
- NSString* pattern = @"(\\w+):\\/\\/([^/:]+)(:\\d*)?([^# ]*\\b)";
-
- // NSLog(@"content: %@",content);
- // NSLog(@"pattern: %@",pattern);
-
- if(content==nil||pattern==nil)
- return nil;
-
- NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
-
- NSTextCheckingResult *match = [regex firstMatchInString:content options:nil range:NSMakeRange(0, content.length)];
-
- int i=0;
- // NSLog(@"%d->range with richtext label: %d,%d",i, [match rangeAtIndex:i].location,[match rangeAtIndex:i].length);
- // NSLog(@"%d-> range of text: %d,%d ",i+1, [match rangeAtIndex:i+1].location,[match rangeAtIndex:i+1].length);
- // NSLog(@"text: %@ ",[content substringWithRange:[match rangeAtIndex:i]]);
-
- return [[content substringWithRange:[match rangeAtIndex:i]] lastPathComponent];
- }
- - (BOOL)handleHttpNavigationToUrl:(NSURL *) url {
- // Usually this means the user has clicked on the YouTube logo or an error message in the
- // player. Most URLs should open in the browser. The only http(s) URL that should open in this
- // UIWebView is the URL for the embed, which is of the format:
- // http(s)://www.youtube.com/embed/[VIDEO ID]?[PARAMETERS]
- NSError *error = NULL;
- NSRegularExpression *ytRegex =
- [NSRegularExpression regularExpressionWithPattern:kYTPlayerEmbedUrlRegexPattern
- options:NSRegularExpressionCaseInsensitive
- error:&error];
- NSTextCheckingResult *ytMatch =
- [ytRegex firstMatchInString:url.absoluteString
- options:0
- range:NSMakeRange(0, [url.absoluteString length])];
-
- NSRegularExpression *adRegex =
- [NSRegularExpression regularExpressionWithPattern:kYTPlayerAdUrlRegexPattern
- options:NSRegularExpressionCaseInsensitive
- error:&error];
- NSTextCheckingResult *adMatch =
- [adRegex firstMatchInString:url.absoluteString
- options:0
- range:NSMakeRange(0, [url.absoluteString length])];
-
- NSRegularExpression *syndicationRegex =
- [NSRegularExpression regularExpressionWithPattern:kYTPlayerSyndicationRegexPattern
- options:NSRegularExpressionCaseInsensitive
- error:&error];
- NSTextCheckingResult *syndicationMatch =
- [syndicationRegex firstMatchInString:url.absoluteString
- options:0
- range:NSMakeRange(0, [url.absoluteString length])];
- NSRegularExpression *oauthRegex =
- [NSRegularExpression regularExpressionWithPattern:kYTPlayerOAuthRegexPattern
- options:NSRegularExpressionCaseInsensitive
- error:&error];
- NSTextCheckingResult *oauthMatch =
- [oauthRegex firstMatchInString:url.absoluteString
- options:0
- range:NSMakeRange(0, [url.absoluteString length])];
-
- NSRegularExpression *staticProxyRegex =
- [NSRegularExpression regularExpressionWithPattern:kYTPlayerStaticProxyRegexPattern
- options:NSRegularExpressionCaseInsensitive
- error:&error];
- NSTextCheckingResult *staticProxyMatch =
- [staticProxyRegex firstMatchInString:url.absoluteString
- options:0
- range:NSMakeRange(0, [url.absoluteString length])];
- if (ytMatch || adMatch || oauthMatch || staticProxyMatch || syndicationMatch) {
- return YES;
- } else {
- [[UIApplication sharedApplication] openURL:url];
- return NO;
- }
- }
- /**
- * Private helper method to load an iframe player with the given player parameters.
- *
- * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters
- * to instantiate the HTML5 player with. This differs depending on
- * whether a single video or playlist is being loaded.
- * @return YES if successful, NO if not.
- */
- - (BOOL)loadWithPlayerParams:(NSDictionary *)additionalPlayerParams {
- NSDictionary *playerCallbacks = @{
- @"onReady" : @"onReady",
- @"onStateChange" : @"onStateChange",
- @"onPlaybackQualityChange" : @"onPlaybackQualityChange",
- @"onError" : @"onPlayerError"
- };
- NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init];
- if (additionalPlayerParams) {
- [playerParams addEntriesFromDictionary:additionalPlayerParams];
- }
- if (![playerParams objectForKey:@"height"]) {
- [playerParams setValue:@"100%" forKey:@"height"];
- }
- if (![playerParams objectForKey:@"width"]) {
- [playerParams setValue:@"100%" forKey:@"width"];
- }
- [playerParams setValue:playerCallbacks forKey:@"events"];
- if ([playerParams objectForKey:@"playerVars"]) {
- NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init];
- [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]];
-
- if (![playerVars objectForKey:@"origin"]) {
- self.originURL = [NSURL URLWithString:@"about:blank"];
- } else {
- self.originURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]];
- }
- } else {
- // This must not be empty so we can render a '{}' in the output JSON
- [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"];
- }
- // Remove the existing webView to reset any state
- [self.webView removeFromSuperview];
- _webView = [self createNewWebView];
- [self addSubview:self.webView];
- NSError *error = nil;
- NSString *path = [[NSBundle bundleForClass:[YTPlayerView class]] pathForResource:@"YTPlayerView-iframe-player"
- ofType:@"html"
- inDirectory:@"Assets"];
-
- // in case of using Swift and embedded frameworks, resources included not in main bundle,
- // but in framework bundle
- if (!path) {
- path = [[[self class] frameworkBundle] pathForResource:@"YTPlayerView-iframe-player"
- ofType:@"html"
- inDirectory:@"Assets"];
- }
-
- NSString *embedHTMLTemplate =
- [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
- if (error) {
- NSLog(@"Received error rendering template: %@", error);
- return NO;
- }
- // Render the playerVars as a JSON dictionary.
- NSError *jsonRenderingError = nil;
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams
- options:NSJSONWritingPrettyPrinted
- error:&jsonRenderingError];
- if (jsonRenderingError) {
- NSLog(@"Attempted configuration of player with invalid playerVars: %@ \tError: %@",
- playerParams,
- jsonRenderingError);
- return NO;
- }
- NSString *playerVarsJsonString =
- [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString];
- [self.webView loadHTMLString:embedHTML baseURL: self.originURL];
- [self.webView setDelegate:self];
- self.webView.allowsInlineMediaPlayback = YES;
- self.webView.mediaPlaybackRequiresUserAction = NO;
-
- if ([self.delegate respondsToSelector:@selector(playerViewPreferredInitialLoadingView:)]) {
- UIView *initialLoadingView = [self.delegate playerViewPreferredInitialLoadingView:self];
- if (initialLoadingView) {
- initialLoadingView.frame = self.bounds;
- initialLoadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [self addSubview:initialLoadingView];
- self.initialLoadingView = initialLoadingView;
- }
- }
-
- return YES;
- }
- /**
- * Private method for cueing both cases of playlist ID and array of video IDs. Cueing
- * a playlist does not start playback.
- *
- * @param cueingString A JavaScript string representing an array, playlist ID or list of
- * video IDs to play with the playlist player.
- * @param index 0-index position of video to start playback on.
- * @param startSeconds Seconds after start of video to begin playback.
- * @param suggestedQuality Suggested YTPlaybackQuality to play the videos.
- * @return The result of cueing the playlist.
- */
- - (void)cuePlaylist:(NSString *)cueingString
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *indexValue = [NSNumber numberWithInt:index];
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.cuePlaylist(%@, %@, %@, '%@');",
- cueingString, indexValue, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- /**
- * Private method for loading both cases of playlist ID and array of video IDs. Loading
- * a playlist automatically starts playback.
- *
- * @param cueingString A JavaScript string representing an array, playlist ID or list of
- * video IDs to play with the playlist player.
- * @param index 0-index position of video to start playback on.
- * @param startSeconds Seconds after start of video to begin playback.
- * @param suggestedQuality Suggested YTPlaybackQuality to play the videos.
- * @return The result of cueing the playlist.
- */
- - (void)loadPlaylist:(NSString *)cueingString
- index:(int)index
- startSeconds:(float)startSeconds
- suggestedQuality:(YTPlaybackQuality)suggestedQuality {
- NSNumber *indexValue = [NSNumber numberWithInt:index];
- NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
- NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
- NSString *command = [NSString stringWithFormat:@"player.loadPlaylist(%@, %@, %@, '%@');",
- cueingString, indexValue, startSecondsValue, qualityValue];
- [self stringFromEvaluatingJavaScript:command];
- }
- /**
- * Private helper method for converting an NSArray of video IDs into its JavaScript equivalent.
- *
- * @param videoIds An array of video ID strings to convert into JavaScript format.
- * @return A JavaScript array in String format containing video IDs.
- */
- - (NSString *)stringFromVideoIdArray:(NSArray *)videoIds {
- NSMutableArray *formattedVideoIds = [[NSMutableArray alloc] init];
- for (id unformattedId in videoIds) {
- [formattedVideoIds addObject:[NSString stringWithFormat:@"'%@'", unformattedId]];
- }
- return [NSString stringWithFormat:@"[%@]", [formattedVideoIds componentsJoinedByString:@", "]];
- }
- /**
- * Private method for evaluating JavaScript in the WebView.
- *
- * @param jsToExecute The JavaScript code in string format that we want to execute.
- * @return JavaScript response from evaluating code.
- */
- - (NSString *)stringFromEvaluatingJavaScript:(NSString *)jsToExecute {
- return [self.webView stringByEvaluatingJavaScriptFromString:jsToExecute];
- }
- /**
- * Private method to convert a Objective-C BOOL value to JS boolean value.
- *
- * @param boolValue Objective-C BOOL value.
- * @return JavaScript Boolean value, i.e. "true" or "false".
- */
- - (NSString *)stringForJSBoolean:(BOOL)boolValue {
- return boolValue ? @"true" : @"false";
- }
- #pragma mark - Exposed for Testing
- - (void)setWebView:(UIWebView *)webView {
- _webView = webView;
- }
- - (UIWebView *)createNewWebView {
- UIWebView *webView = [[UIWebView alloc] initWithFrame:self.bounds];
- webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
- webView.scrollView.scrollEnabled = NO;
- webView.scrollView.bounces = NO;
-
- if ([self.delegate respondsToSelector:@selector(playerViewPreferredWebViewBackgroundColor:)]) {
- webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self];
- if (webView.backgroundColor == [UIColor clearColor]) {
- webView.opaque = NO;
- }
- }
-
- return webView;
- }
- - (void)removeWebView {
- [self.webView removeFromSuperview];
- self.webView = nil;
- }
- + (NSBundle *)frameworkBundle {
- static NSBundle* frameworkBundle = nil;
- static dispatch_once_t predicate;
- dispatch_once(&predicate, ^{
- NSString* mainBundlePath = [[NSBundle bundleForClass:[self class]] resourcePath];
- NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"Assets.bundle"];
- frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
- });
- return frameworkBundle;
- }
- @end
|