diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..65f5e653c --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013 Cédric Luthi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 000000000..f006489a1 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +## About + +**XCDYouTubeVideoPlayerViewController** is a YouTube video player for iPhone and iPad. + + + +To the best of my knowledge, the only *official* way of playing a YouTube video on iOS is with a UIWebView and the [iframe player API](https://developers.google.com/youtube/iframe_api_reference). Unfortunately, this is very slow and quite ugly, so I wrote XCDYouTubeVideoPlayerViewController which gives the user a better viewing experience. + +XCDYouTubeVideoPlayerViewController uses progressive download, so remember that some restrictions apply if you submit your app to the App Store, as stated in +[HTTP Live Streaming — Requirements for Apps](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html#//apple_ref/doc/uid/TP40008332-CH102-SW5): +> **Warning**: iOS apps submitted for distribution in the App Store must conform to these requirements. +> +> If your app delivers video over cellular networks, and the video exceeds either 10 minutes duration or 5 MB of data in a five minute period, you are required to use HTTP Live Streaming. (Progressive download may be used for smaller clips.) + +## Requirements + +- Runs on iOS 5.0 and later +- Must be compiled with ARC + +## Installation + +XCDYouTubeVideoPlayerViewController is available through CocoaPods. + +Alternatively, you can install it manually: + +1. Copy the `XCDYouTubeVideoPlayerViewController.h` and `XCDYouTubeVideoPlayerViewController.m` files in your project. +2. Add `MediaPlayer.framework` and `AVFoundation.framework` in your project. + +## Usage + +Use `XCDYouTubeVideoPlayerViewController` the same way you use a `MPMoviePlayerViewController`, except you initialize it with a YouTube video identifier instead of a content URL. + +#### Present the video in full-screen + +``` +XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; +[self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; +``` + +#### Fetch the video identifier asynchronously + +``` +XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [XCDYouTubeVideoPlayerViewController new]; +[self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; + +NSURL *url = [NSURL URLWithString:@"https://gdata.youtube.com/feeds/api/standardfeeds/on_the_web?v=2&alt=json&max-results=1"]; +[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + id json = [NSJSONSerialization JSONObjectWithData:data ?: [NSData new] options:0 error:NULL]; + NSString *videoIdentifier = [[[json valueForKeyPath:@"feed.entry.media$group.yt$videoid.$t"] lastObject] description]; + videoPlayerViewController.videoIdentifier = videoIdentifier; +}]; +``` + +#### Present the video in a non full-screen view + +``` +XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; +[videoPlayerViewController presentInView:self.videoContainerView]; +``` + +See the demo project for more sample code. + +## Contact + +Cédric Luthi + +- http://github.com/0xced +- http://twitter.com/0xced +- cedric.luthi@gmail.com + +## License + +XCDYouTubeVideoPlayerViewController is available under the MIT license. See the LICENSE file for more information. \ No newline at end of file diff --git a/Screenshots/XCDYouTubeVideoPlayerViewController.png b/Screenshots/XCDYouTubeVideoPlayerViewController.png new file mode 100644 index 000000000..9104a5932 Binary files /dev/null and b/Screenshots/XCDYouTubeVideoPlayerViewController.png differ diff --git a/XCDYouTubeVideoPlayerViewController.podspec b/XCDYouTubeVideoPlayerViewController.podspec new file mode 100644 index 000000000..599c4ef05 --- /dev/null +++ b/XCDYouTubeVideoPlayerViewController.podspec @@ -0,0 +1,13 @@ +Pod::Spec.new do |s| + s.name = "XCDYouTubeVideoPlayerViewController" + s.version = "1.0.0" + s.summary = "YouTube video player for iPhone and iPad." + s.homepage = "https://github.com/0xced/XCDYouTubeVideoPlayerViewController" + s.license = 'MIT' + s.author = { "Cédric Luthi" => "cedric.luthi@gmail.com" } + s.source = { :git => "https://github.com/0xced/XCDYouTubeVideoPlayerViewController.git", :tag => "1.0.0" } + s.platform = :ios, '5.0' + s.source_files = 'XCDYouTubeVideoPlayerViewController' + s.frameworks = 'AVFoundation', 'MediaPlayer' + s.requires_arc = true +end diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h new file mode 100644 index 000000000..fcabfe631 --- /dev/null +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h @@ -0,0 +1,35 @@ +// +// XCDYouTubeVideoPlayerViewController.h +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { + XCDYouTubeVideoQualitySmall240 = 36, + XCDYouTubeVideoQualityMedium360 = 18, + XCDYouTubeVideoQualityHD720 = 22, + XCDYouTubeVideoQualityHD1080 = 37, +}; + +MP_EXTERN NSString *const XCDYouTubeVideoErrorDomain; +MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; // NSError + +@interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController + +- (id) initWithVideoIdentifier:(NSString *)videoIdentifier; + +@property (nonatomic, copy) NSString *videoIdentifier; + +// On iPhone, defaults to @[ @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ] +// On iPad, defaults to @[ @(XCDYouTubeVideoQualityHD1080), @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ] +// If you really know what you are doing, you can use the `itag` values as described on http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs +@property (nonatomic, copy) NSArray *preferredVideoQualities; + +// Ownership of the `moviePlayer` property is transferred to the view. +- (void) presentInView:(UIView *)view; + +@end diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m new file mode 100644 index 000000000..23beb15c1 --- /dev/null +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -0,0 +1,223 @@ +// +// XCDYouTubeVideoPlayerViewController.m +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeVideoPlayerViewController.h" + +#import +#import + +NSString *const XCDYouTubeVideoErrorDomain = @"XCDYouTubeVideoErrorDomain"; +NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey = @"XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey"; + +static NSDictionary *DictionaryWithQueryString(NSString *string, NSStringEncoding encoding) +{ + NSMutableDictionary *dictionary = [NSMutableDictionary new]; + NSArray *fields = [string componentsSeparatedByString:@"&"]; + for (NSString *field in fields) + { + NSArray *pair = [field componentsSeparatedByString:@"="]; + if (pair.count == 2) + { + NSString *key = pair[0]; + NSString *value = [pair[1] stringByReplacingPercentEscapesUsingEncoding:encoding]; + value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; + dictionary[key] = value; + } + } + return dictionary; +} + +@interface XCDYouTubeVideoPlayerViewController () +@property (nonatomic, strong) NSURLConnection *connection; +@property (nonatomic, strong) NSMutableData *connectionData; +@property (nonatomic, strong) NSMutableArray *elFields; +@property (nonatomic, assign, getter = isEmbedded) BOOL embedded; +@end + +@implementation XCDYouTubeVideoPlayerViewController + +static void *MoviePlayerKey = &MoviePlayerKey; + +- (id) init +{ + return [self initWithVideoIdentifier:nil]; +} + +- (id) initWithContentURL:(NSURL *)contentURL +{ + @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:` method instead." userInfo:nil]; +} + +- (id) initWithVideoIdentifier:(NSString *)videoIdentifier +{ + if (!(self = [super init])) + return nil; + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) + _preferredVideoQualities = @[ @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ]; + else + _preferredVideoQualities = @[ @(XCDYouTubeVideoQualityHD1080), @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ]; + + if (videoIdentifier) + self.videoIdentifier = videoIdentifier; + + return self; +} + +- (void) setVideoIdentifier:(NSString *)videoIdentifier +{ + if (![NSThread isMainThread]) + { + [self performSelectorOnMainThread:_cmd withObject:videoIdentifier waitUntilDone:NO]; + return; + } + + if ([videoIdentifier isEqual:self.videoIdentifier]) + return; + + _videoIdentifier = [videoIdentifier copy]; + + self.elFields = [[NSMutableArray alloc] initWithArray:@[ @"embedded", @"detailpage", @"vevo", @"" ]]; + + [self startVideoInfoRequest]; +} + +- (void) presentInView:(UIView *)view +{ + self.embedded = YES; + + self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; + self.moviePlayer.view.frame = CGRectMake(0.f, 0.f, view.bounds.size.width, view.bounds.size.height); + self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [view addSubview:self.moviePlayer.view]; + objc_setAssociatedObject(view, MoviePlayerKey, self.moviePlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void) startVideoInfoRequest +{ + NSString *elField = [self.elFields objectAtIndex:0]; + [self.elFields removeObjectAtIndex:0]; + if (elField.length > 0) + elField = [@"&el=" stringByAppendingString:elField]; + + NSURL *videoInfoURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.youtube.com/get_video_info?video_id=%@%@&ps=default&eurl=&gl=US&hl=en", self.videoIdentifier ?: @"", elField]]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:videoInfoURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; + [self.connection cancel]; + self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; +} + +- (void) finishWithError:(NSError *)error +{ + NSDictionary *userInfo = @{ MPMoviePlayerPlaybackDidFinishReasonUserInfoKey: @(MPMovieFinishReasonPlaybackError), + XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey: error }; + [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer userInfo:userInfo]; + + if (self.isEmbedded) + [self.moviePlayer.view removeFromSuperview]; + else + [self.presentingViewController dismissMoviePlayerViewControllerAnimated]; +} + +#pragma mark - UIViewController + +- (void) viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if ([self isBeingPresented]) + self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; +} + +- (void) viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + if ([self isBeingDismissed]) + [self.connection cancel]; +} + +#pragma mark - NSURLConnectionDataDelegate / NSURLConnectionDelegate + +- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + NSUInteger capacity = response.expectedContentLength == NSURLResponseUnknownLength ? 0 : response.expectedContentLength; + self.connectionData = [[NSMutableData alloc] initWithCapacity:capacity]; +} + +- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.connectionData appendData:data]; +} + +- (void) connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSError *error = nil; + NSURL *videoURL = [self videoURLWithData:self.connectionData error:&error]; + if (videoURL) + { + self.moviePlayer.contentURL = videoURL; + [self.moviePlayer prepareToPlay]; + } + else if (self.elFields.count > 0) + { + [self startVideoInfoRequest]; + } + else + { + [self finishWithError:error]; + } +} + +- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + [self finishWithError:error]; +} + +#pragma mark - URL Parsing + +- (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error +{ + NSString *videoQuery = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; + NSStringEncoding queryEncoding = NSUTF8StringEncoding; + NSDictionary *video = DictionaryWithQueryString(videoQuery, queryEncoding); + NSArray *streamQueries = [video[@"url_encoded_fmt_stream_map"] componentsSeparatedByString:@","]; + + NSMutableDictionary *streamURLs = [NSMutableDictionary new]; + for (NSString *streamQuery in streamQueries) + { + NSDictionary *stream = DictionaryWithQueryString(streamQuery, queryEncoding); + NSString *type = stream[@"type"]; + if ([AVURLAsset isPlayableExtendedMIMEType:type]) + { + NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", stream[@"url"], stream[@"sig"]]]; + streamURLs[@([stream[@"itag"] integerValue])] = streamURL; + } + } + + for (NSNumber *videoQuality in self.preferredVideoQualities) + { + NSURL *streamURL = streamURLs[videoQuality]; + if (streamURL) + return streamURL; + } + + if (error) + { + NSMutableDictionary *userInfo = [@{ NSURLErrorKey: self.connection.originalRequest.URL } mutableCopy]; + NSString *reason = video[@"reason"]; + if (reason) + userInfo[NSLocalizedDescriptionKey] = reason; + + NSInteger code = [video[@"errorcode"] integerValue]; + *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:code userInfo:userInfo]; + } + + return nil; +} + +@end diff --git a/YouTube Video Player Demo.xcodeproj/project.pbxproj b/YouTube Video Player Demo.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1af531d39 --- /dev/null +++ b/YouTube Video Player Demo.xcodeproj/project.pbxproj @@ -0,0 +1,310 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + C261FBDD17444FF2005FDFCA /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C261FBDC17444FF2005FDFCA /* AVFoundation.framework */; }; + C285F6D717324F7500D81C59 /* DemoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C285F6D617324F7500D81C59 /* DemoViewController.xib */; }; + C285F6DC1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C285F6DB1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.m */; }; + C285F6DE1732523400D81C59 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C285F6DD1732523400D81C59 /* MediaPlayer.framework */; }; + C29CC80D17324DD400581C7C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C29CC80C17324DD400581C7C /* UIKit.framework */; }; + C29CC80F17324DD400581C7C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C29CC80E17324DD400581C7C /* Foundation.framework */; }; + C29CC81917324DD400581C7C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C29CC81817324DD400581C7C /* main.m */; }; + C29CC81D17324DD400581C7C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C29CC81C17324DD400581C7C /* AppDelegate.m */; }; + C29CC81F17324DD400581C7C /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = C29CC81E17324DD400581C7C /* Default.png */; }; + C29CC82117324DD500581C7C /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C29CC82017324DD500581C7C /* Default@2x.png */; }; + C29CC82317324DD500581C7C /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C29CC82217324DD500581C7C /* Default-568h@2x.png */; }; + C29CC82617324DD500581C7C /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C29CC82517324DD500581C7C /* DemoViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + C261FBDC17444FF2005FDFCA /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + C285F6D617324F7500D81C59 /* DemoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DemoViewController.xib; sourceTree = ""; }; + C285F6DA1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCDYouTubeVideoPlayerViewController.h; sourceTree = ""; }; + C285F6DB1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCDYouTubeVideoPlayerViewController.m; sourceTree = ""; }; + C285F6DD1732523400D81C59 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + C29CC80917324DD400581C7C /* YouTube Video Player Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "YouTube Video Player Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C29CC80C17324DD400581C7C /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + C29CC80E17324DD400581C7C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + C29CC81417324DD400581C7C /* YouTube Video Player Demo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "YouTube Video Player Demo-Info.plist"; sourceTree = ""; }; + C29CC81817324DD400581C7C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + C29CC81A17324DD400581C7C /* YouTube Video Player Demo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YouTube Video Player Demo-Prefix.pch"; sourceTree = ""; }; + C29CC81B17324DD400581C7C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + C29CC81C17324DD400581C7C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + C29CC81E17324DD400581C7C /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; + C29CC82017324DD500581C7C /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; + C29CC82217324DD500581C7C /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + C29CC82417324DD500581C7C /* DemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + C29CC82517324DD500581C7C /* DemoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C29CC80617324DD400581C7C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C29CC80D17324DD400581C7C /* UIKit.framework in Frameworks */, + C29CC80F17324DD400581C7C /* Foundation.framework in Frameworks */, + C285F6DE1732523400D81C59 /* MediaPlayer.framework in Frameworks */, + C261FBDD17444FF2005FDFCA /* AVFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C285F6D91732520500D81C59 /* XCDYouTubeVideoPlayerViewController */ = { + isa = PBXGroup; + children = ( + C285F6DA1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.h */, + C285F6DB1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.m */, + ); + path = XCDYouTubeVideoPlayerViewController; + sourceTree = ""; + }; + C29CC80017324DD400581C7C = { + isa = PBXGroup; + children = ( + C285F6D91732520500D81C59 /* XCDYouTubeVideoPlayerViewController */, + C29CC81217324DD400581C7C /* YouTube Video Player Demo */, + C29CC80B17324DD400581C7C /* Frameworks */, + C29CC80A17324DD400581C7C /* Products */, + ); + indentWidth = 4; + sourceTree = ""; + tabWidth = 4; + usesTabs = 1; + }; + C29CC80A17324DD400581C7C /* Products */ = { + isa = PBXGroup; + children = ( + C29CC80917324DD400581C7C /* YouTube Video Player Demo.app */, + ); + name = Products; + sourceTree = ""; + }; + C29CC80B17324DD400581C7C /* Frameworks */ = { + isa = PBXGroup; + children = ( + C261FBDC17444FF2005FDFCA /* AVFoundation.framework */, + C29CC80E17324DD400581C7C /* Foundation.framework */, + C285F6DD1732523400D81C59 /* MediaPlayer.framework */, + C29CC80C17324DD400581C7C /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C29CC81217324DD400581C7C /* YouTube Video Player Demo */ = { + isa = PBXGroup; + children = ( + C29CC81B17324DD400581C7C /* AppDelegate.h */, + C29CC81C17324DD400581C7C /* AppDelegate.m */, + C29CC82417324DD500581C7C /* DemoViewController.h */, + C29CC82517324DD500581C7C /* DemoViewController.m */, + C285F6D617324F7500D81C59 /* DemoViewController.xib */, + C29CC81317324DD400581C7C /* Supporting Files */, + ); + path = "YouTube Video Player Demo"; + sourceTree = ""; + }; + C29CC81317324DD400581C7C /* Supporting Files */ = { + isa = PBXGroup; + children = ( + C29CC81417324DD400581C7C /* YouTube Video Player Demo-Info.plist */, + C29CC81817324DD400581C7C /* main.m */, + C29CC81A17324DD400581C7C /* YouTube Video Player Demo-Prefix.pch */, + C29CC81E17324DD400581C7C /* Default.png */, + C29CC82017324DD500581C7C /* Default@2x.png */, + C29CC82217324DD500581C7C /* Default-568h@2x.png */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C29CC80817324DD400581C7C /* YouTube Video Player Demo */ = { + isa = PBXNativeTarget; + buildConfigurationList = C29CC82F17324DD500581C7C /* Build configuration list for PBXNativeTarget "YouTube Video Player Demo" */; + buildPhases = ( + C29CC80517324DD400581C7C /* Sources */, + C29CC80617324DD400581C7C /* Frameworks */, + C29CC80717324DD400581C7C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "YouTube Video Player Demo"; + productName = "YouTube Video Player Demo"; + productReference = C29CC80917324DD400581C7C /* YouTube Video Player Demo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C29CC80117324DD400581C7C /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = "Cédric Luthi"; + }; + buildConfigurationList = C29CC80417324DD400581C7C /* Build configuration list for PBXProject "YouTube Video Player Demo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = C29CC80017324DD400581C7C; + productRefGroup = C29CC80A17324DD400581C7C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C29CC80817324DD400581C7C /* YouTube Video Player Demo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C29CC80717324DD400581C7C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C29CC81F17324DD400581C7C /* Default.png in Resources */, + C29CC82117324DD500581C7C /* Default@2x.png in Resources */, + C29CC82317324DD500581C7C /* Default-568h@2x.png in Resources */, + C285F6D717324F7500D81C59 /* DemoViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C29CC80517324DD400581C7C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C29CC81917324DD400581C7C /* main.m in Sources */, + C29CC81D17324DD400581C7C /* AppDelegate.m in Sources */, + C29CC82617324DD500581C7C /* DemoViewController.m in Sources */, + C285F6DC1732522300D81C59 /* XCDYouTubeVideoPlayerViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + C29CC82D17324DD500581C7C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + C29CC82E17324DD500581C7C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "NS_BLOCK_ASSERTIONS=1"; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + C29CC83017324DD500581C7C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "YouTube Video Player Demo/YouTube Video Player Demo-Prefix.pch"; + INFOPLIST_FILE = "YouTube Video Player Demo/YouTube Video Player Demo-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + C29CC83117324DD500581C7C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "YouTube Video Player Demo/YouTube Video Player Demo-Prefix.pch"; + INFOPLIST_FILE = "YouTube Video Player Demo/YouTube Video Player Demo-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C29CC80417324DD400581C7C /* Build configuration list for PBXProject "YouTube Video Player Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C29CC82D17324DD500581C7C /* Debug */, + C29CC82E17324DD500581C7C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C29CC82F17324DD500581C7C /* Build configuration list for PBXNativeTarget "YouTube Video Player Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C29CC83017324DD500581C7C /* Debug */, + C29CC83117324DD500581C7C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C29CC80117324DD400581C7C /* Project object */; +} diff --git a/YouTube Video Player Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/YouTube Video Player Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..ead1608a0 --- /dev/null +++ b/YouTube Video Player Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/YouTube Video Player Demo/AppDelegate.h b/YouTube Video Player Demo/AppDelegate.h new file mode 100644 index 000000000..d97eaf8a2 --- /dev/null +++ b/YouTube Video Player Demo/AppDelegate.h @@ -0,0 +1,11 @@ +// +// AppDelegate.h +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +@interface AppDelegate : UIResponder + +@end diff --git a/YouTube Video Player Demo/AppDelegate.m b/YouTube Video Player Demo/AppDelegate.m new file mode 100644 index 000000000..33bfefccf --- /dev/null +++ b/YouTube Video Player Demo/AppDelegate.m @@ -0,0 +1,25 @@ +// +// AppDelegate.m +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +#import "AppDelegate.h" + +#import "DemoViewController.h" + +@implementation AppDelegate + +@synthesize window = _window; + +- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[DemoViewController new]]; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/YouTube Video Player Demo/Default-568h@2x.png b/YouTube Video Player Demo/Default-568h@2x.png new file mode 100644 index 000000000..0891b7aab Binary files /dev/null and b/YouTube Video Player Demo/Default-568h@2x.png differ diff --git a/YouTube Video Player Demo/Default.png b/YouTube Video Player Demo/Default.png new file mode 100644 index 000000000..4c8ca6f69 Binary files /dev/null and b/YouTube Video Player Demo/Default.png differ diff --git a/YouTube Video Player Demo/Default@2x.png b/YouTube Video Player Demo/Default@2x.png new file mode 100644 index 000000000..35b84cffe Binary files /dev/null and b/YouTube Video Player Demo/Default@2x.png differ diff --git a/YouTube Video Player Demo/DemoViewController.h b/YouTube Video Player Demo/DemoViewController.h new file mode 100644 index 000000000..1cf58425e --- /dev/null +++ b/YouTube Video Player Demo/DemoViewController.h @@ -0,0 +1,11 @@ +// +// ViewController.h +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +@interface DemoViewController : UIViewController + +@end diff --git a/YouTube Video Player Demo/DemoViewController.m b/YouTube Video Player Demo/DemoViewController.m new file mode 100644 index 000000000..deef9a8e4 --- /dev/null +++ b/YouTube Video Player Demo/DemoViewController.m @@ -0,0 +1,166 @@ +// +// ViewController.m +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +#import "DemoViewController.h" + +#import "XCDYouTubeVideoPlayerViewController.h" + +@interface DemoViewController () +@property (nonatomic, weak) IBOutlet UITextField *videoIdentifierTextField; +@property (nonatomic, weak) IBOutlet UISwitch *lowQualitySwitch; +@property (nonatomic, weak) IBOutlet UIView *videoContainerView; +@property (nonatomic, weak) IBOutlet UISwitch *fullScreenSwitch; + +@property (nonatomic, strong) XCDYouTubeVideoPlayerViewController *videoPlayerViewController; +@end + +@implementation DemoViewController + +- (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle +{ + if (!(self = [super initWithNibName:nibName bundle:nibBundle])) + return nil; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; + + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (NSString *) title +{ + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; +} + +- (void) viewDidLoad +{ + self.videoIdentifierTextField.superview.layer.cornerRadius = 10.f; +} + +- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self.view endEditing:YES]; +} + +- (IBAction) playYouTubeVideo:(id)sender +{ + if (!self.videoPlayerViewController) + self.videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:self.videoIdentifierTextField.text]; + else + self.videoPlayerViewController.videoIdentifier = self.videoIdentifierTextField.text; + + if (self.lowQualitySwitch.on) + self.videoPlayerViewController.preferredVideoQualities = @[ @(XCDYouTubeVideoQualitySmall240), @(XCDYouTubeVideoQualityMedium360) ]; + + if (self.fullScreenSwitch.on) + [self presentMoviePlayerViewControllerAnimated:self.videoPlayerViewController]; + else if (![self.videoContainerView.subviews containsObject:self.videoPlayerViewController.moviePlayer.view]) + [self.videoPlayerViewController presentInView:self.videoContainerView]; +} + +- (IBAction) playTrendingVideo:(id)sender +{ + XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [XCDYouTubeVideoPlayerViewController new]; + if (self.fullScreenSwitch.on) + { + [self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; + } + else + { + [self.videoContainerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [videoPlayerViewController presentInView:self.videoContainerView]; + } + + // https://developers.google.com/youtube/2.0/developers_guide_protocol_video_feeds#Standard_feeds + NSURL *url = [NSURL URLWithString:@"https://gdata.youtube.com/feeds/api/standardfeeds/on_the_web?v=2&alt=json&max-results=1"]; + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + id json = [NSJSONSerialization JSONObjectWithData:data ?: [NSData new] options:0 error:NULL]; + NSString *videoIdentifier = [[[json valueForKeyPath:@"feed.entry.media$group.yt$videoid.$t"] lastObject] description]; + videoPlayerViewController.videoIdentifier = videoIdentifier; + }]; +} + +- (BOOL) textFieldShouldReturn:(UITextField *)textField +{ + [self playYouTubeVideo:textField]; + return YES; +} + +#pragma mark - Notifications + +- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification +{ + MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; + NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; + NSString *reason = @"Unknown"; + switch (finishReason) + { + case MPMovieFinishReasonPlaybackEnded: + reason = @"Playback Ended"; + break; + case MPMovieFinishReasonPlaybackError: + reason = @"Playback Error"; + break; + case MPMovieFinishReasonUserExited: + reason = @"User Exited"; + break; + } + NSLog(@"Finish Reason: %@%@", reason, error ? [@"\n" stringByAppendingString:[error description]] : @""); +} + +- (void) moviePlayerPlaybackStateDidChange:(NSNotification *)notification +{ + MPMoviePlayerController *moviePlayerController = notification.object; + NSString *playbackState = @"Unknown"; + switch (moviePlayerController.playbackState) + { + case MPMoviePlaybackStateStopped: + playbackState = @"Stopped"; + break; + case MPMoviePlaybackStatePlaying: + playbackState = @"Playing"; + break; + case MPMoviePlaybackStatePaused: + playbackState = @"Paused"; + break; + case MPMoviePlaybackStateInterrupted: + playbackState = @"Interrupted"; + break; + case MPMoviePlaybackStateSeekingForward: + playbackState = @"Seeking Forward"; + break; + case MPMoviePlaybackStateSeekingBackward: + playbackState = @"Seeking Backward"; + break; + } + NSLog(@"Playback State: %@", playbackState); +} + +- (void) moviePlayerLoadStateDidChange:(NSNotification *)notification +{ + MPMoviePlayerController *moviePlayerController = notification.object; + + NSMutableString *loadState = [NSMutableString new]; + MPMovieLoadState state = moviePlayerController.loadState; + if (state & MPMovieLoadStatePlayable) + [loadState appendString:@" | Playable"]; + if (state & MPMovieLoadStatePlaythroughOK) + [loadState appendString:@" | Playthrough OK"]; + if (state & MPMovieLoadStateStalled) + [loadState appendString:@" | Stalled"]; + + NSLog(@"Load State: %@", loadState.length > 0 ? [loadState substringFromIndex:3] : @"N/A"); +} + +@end diff --git a/YouTube Video Player Demo/DemoViewController.xib b/YouTube Video Player Demo/DemoViewController.xib new file mode 100644 index 000000000..913501f58 --- /dev/null +++ b/YouTube Video Player Demo/DemoViewController.xib @@ -0,0 +1,552 @@ + + + + 1552 + 12D78 + 3084 + 1187.37 + 626.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 2083 + + + IBProxyObject + IBUIButton + IBUILabel + IBUISwitch + IBUITextField + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 293 + + + + 269 + + + + 300 + {{10, 19}, {104, 21}} + + + + _NS:9 + NO + YES + 7 + NO + IBCocoaTouchFramework + Low Quality: + + 3 + MQA + + + 0 + 2 + + 1 + 17 + + + Helvetica + 17 + 16 + + NO + + + + 297 + {{118, 16}, {94, 27}} + + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + + + {{36, 63}, {230, 60}} + + + + _NS:10 + + 3 + MCAwAA + + IBCocoaTouchFramework + + + + 293 + {{20, 20}, {100, 44}} + + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + 1 + Play Video + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + + 293 + {{128, 27}, {152, 30}} + + + + _NS:9 + NO + YES + IBCocoaTouchFramework + 0 + 9bZkp7q19f0 + 3 + + 3 + MAA + + 2 + + + 1 + YES + 17 + + IBCocoaTouchFramework + + 1 + + + + + {{10, 10}, {300, 131}} + + + + _NS:10 + + 3 + MC4zMzMzMzMzMzMzAA + + IBCocoaTouchFramework + + + + 293 + {{76, 149}, {169, 44}} + + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + 1 + Play Trending Video + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + + + + + + 282 + + {{20, 200}, {280, 174}} + + + + _NS:9 + + 3 + MC42NjY2NjY2NjY3AA + + IBCocoaTouchFramework + + + + 269 + + + + 300 + {{10, 11}, {104, 21}} + + + + _NS:9 + NO + YES + 7 + NO + IBCocoaTouchFramework + Full Screen: + + 0 + 2 + + + NO + + + + 297 + {{118, 8}, {94, 27}} + + + _NS:9 + NO + IBCocoaTouchFramework + 0 + 0 + YES + + + {{45, 373}, {230, 43}} + + + + _NS:10 + + IBCocoaTouchFramework + + + {{0, 64}, {320, 416}} + + + + + 3 + MC43NQA + + + NO + + + NO + + + IBUIScreenMetrics + + YES + + + + + + {320, 480} + {480, 320} + + + IBCocoaTouchFramework + Retina 3.5 Full Screen + 0 + + IBCocoaTouchFramework + + + + + + + view + + + + 7 + + + + lowQualitySwitch + + + + 20 + + + + videoIdentifierTextField + + + + 17 + + + + videoContainerView + + + + 32 + + + + fullScreenSwitch + + + + 38 + + + + playYouTubeVideo: + + + 7 + + 16 + + + + delegate + + + + 24 + + + + playTrendingVideo: + + + 7 + + 30 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + + + + + + + + + 27 + + + + + 29 + + + + + + + + + + 21 + + + + + + + + + 18 + + + + + 19 + + + + + 14 + + + + + 15 + + + + + 31 + + + + + + 35 + + + + + + + + + 37 + + + + + 36 + + + + + + + DemoViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 38 + + + + + DemoViewController + UIViewController + + id + id + + + + playTrendingVideo: + id + + + playYouTubeVideo: + id + + + + UISwitch + UISwitch + UIView + UITextField + + + + fullScreenSwitch + UISwitch + + + lowQualitySwitch + UISwitch + + + videoContainerView + UIView + + + videoIdentifierTextField + UITextField + + + + IBProjectSource + ./Classes/DemoViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 2083 + + diff --git a/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist b/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist new file mode 100644 index 000000000..12528ffb4 --- /dev/null +++ b/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ch.pitaya.xcdyoutubevideoplayerviewcontroller.demo + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/YouTube Video Player Demo/YouTube Video Player Demo-Prefix.pch b/YouTube Video Player Demo/YouTube Video Player Demo-Prefix.pch new file mode 100644 index 000000000..7148aca03 --- /dev/null +++ b/YouTube Video Player Demo/YouTube Video Player Demo-Prefix.pch @@ -0,0 +1,15 @@ +// +// Prefix header for all source files of the 'YouTube Video Player Demo' target in the 'YouTube Video Player Demo' project +// + +#import + +#ifndef __IPHONE_4_0 +#warning "This project uses features only available in iOS SDK 4.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import + #import +#endif diff --git a/YouTube Video Player Demo/main.m b/YouTube Video Player Demo/main.m new file mode 100644 index 000000000..dfd7a74ce --- /dev/null +++ b/YouTube Video Player Demo/main.m @@ -0,0 +1,17 @@ +// +// main.m +// YouTube Video Player Demo +// +// Created by Cédric Luthi on 02.05.13. +// Copyright (c) 2013 Cédric Luthi. All rights reserved. +// + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool + { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +}