Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 61285579eeb402866c4783a0c62c9a653e479989 0 parents
Peter Steinberger authored
15 .gitignore
@@ -0,0 +1,15 @@
+# Xcode
+build/*
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+*.xcworkspace
+!default.xcworkspace
+xcuserdata
+profile
+*.moved-aside
297 Example/BetterYouTube.xcodeproj/project.pbxproj
@@ -0,0 +1,297 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 781CFF5614E4583900BD5D50 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 781CFF5514E4583900BD5D50 /* UIKit.framework */; };
+ 781CFF5814E4583900BD5D50 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 781CFF5714E4583900BD5D50 /* Foundation.framework */; };
+ 781CFF5A14E4583900BD5D50 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 781CFF5914E4583900BD5D50 /* CoreGraphics.framework */; };
+ 781CFF6014E4583900BD5D50 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 781CFF5E14E4583900BD5D50 /* InfoPlist.strings */; };
+ 781CFF6214E4583900BD5D50 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 781CFF6114E4583900BD5D50 /* main.m */; };
+ 781CFF6614E4583900BD5D50 /* PSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 781CFF6514E4583900BD5D50 /* PSAppDelegate.m */; };
+ 781CFF6E14E4586000BD5D50 /* PSYouTubeTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 781CFF6D14E4586000BD5D50 /* PSYouTubeTestViewController.m */; };
+ 781CFF7014E458AA00BD5D50 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 781CFF6F14E458AA00BD5D50 /* MediaPlayer.framework */; };
+ 781CFFA914E48E9900BD5D50 /* PSYouTubeExtractor.m in Sources */ = {isa = PBXBuildFile; fileRef = 781CFFA814E48E9900BD5D50 /* PSYouTubeExtractor.m */; };
+ 781CFFAC14E4BCFA00BD5D50 /* PSYouTubeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 781CFFAB14E4BCFA00BD5D50 /* PSYouTubeView.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 781CFF5114E4583900BD5D50 /* BetterYouTube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BetterYouTube.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 781CFF5514E4583900BD5D50 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 781CFF5714E4583900BD5D50 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 781CFF5914E4583900BD5D50 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 781CFF5D14E4583900BD5D50 /* BetterYouTube-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BetterYouTube-Info.plist"; sourceTree = "<group>"; };
+ 781CFF5F14E4583900BD5D50 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 781CFF6114E4583900BD5D50 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 781CFF6314E4583900BD5D50 /* BetterYouTube-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BetterYouTube-Prefix.pch"; sourceTree = "<group>"; };
+ 781CFF6414E4583900BD5D50 /* PSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PSAppDelegate.h; sourceTree = "<group>"; };
+ 781CFF6514E4583900BD5D50 /* PSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PSAppDelegate.m; sourceTree = "<group>"; };
+ 781CFF6C14E4586000BD5D50 /* PSYouTubeTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSYouTubeTestViewController.h; sourceTree = "<group>"; };
+ 781CFF6D14E4586000BD5D50 /* PSYouTubeTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSYouTubeTestViewController.m; sourceTree = "<group>"; };
+ 781CFF6F14E458AA00BD5D50 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
+ 781CFFA714E48E9900BD5D50 /* PSYouTubeExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSYouTubeExtractor.h; path = ../PSYouTubeExtractor.h; sourceTree = "<group>"; };
+ 781CFFA814E48E9900BD5D50 /* PSYouTubeExtractor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSYouTubeExtractor.m; path = ../PSYouTubeExtractor.m; sourceTree = "<group>"; };
+ 781CFFAA14E4BCFA00BD5D50 /* PSYouTubeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSYouTubeView.h; sourceTree = "<group>"; };
+ 781CFFAB14E4BCFA00BD5D50 /* PSYouTubeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSYouTubeView.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 781CFF4E14E4583900BD5D50 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 781CFF7014E458AA00BD5D50 /* MediaPlayer.framework in Frameworks */,
+ 781CFF5614E4583900BD5D50 /* UIKit.framework in Frameworks */,
+ 781CFF5814E4583900BD5D50 /* Foundation.framework in Frameworks */,
+ 781CFF5A14E4583900BD5D50 /* CoreGraphics.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 781CFF4614E4583900BD5D50 = {
+ isa = PBXGroup;
+ children = (
+ 781CFF5B14E4583900BD5D50 /* BetterYouTube */,
+ 781CFF5414E4583900BD5D50 /* Frameworks */,
+ 781CFF5214E4583900BD5D50 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 781CFF5214E4583900BD5D50 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 781CFF5114E4583900BD5D50 /* BetterYouTube.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 781CFF5414E4583900BD5D50 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 781CFFA614E48E8E00BD5D50 /* PSYouTubeExtractor */,
+ 781CFF6F14E458AA00BD5D50 /* MediaPlayer.framework */,
+ 781CFF5514E4583900BD5D50 /* UIKit.framework */,
+ 781CFF5714E4583900BD5D50 /* Foundation.framework */,
+ 781CFF5914E4583900BD5D50 /* CoreGraphics.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 781CFF5B14E4583900BD5D50 /* BetterYouTube */ = {
+ isa = PBXGroup;
+ children = (
+ 781CFF6414E4583900BD5D50 /* PSAppDelegate.h */,
+ 781CFF6514E4583900BD5D50 /* PSAppDelegate.m */,
+ 781CFF6C14E4586000BD5D50 /* PSYouTubeTestViewController.h */,
+ 781CFF6D14E4586000BD5D50 /* PSYouTubeTestViewController.m */,
+ 781CFF5C14E4583900BD5D50 /* Supporting Files */,
+ );
+ path = BetterYouTube;
+ sourceTree = "<group>";
+ };
+ 781CFF5C14E4583900BD5D50 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 781CFF5D14E4583900BD5D50 /* BetterYouTube-Info.plist */,
+ 781CFF5E14E4583900BD5D50 /* InfoPlist.strings */,
+ 781CFF6114E4583900BD5D50 /* main.m */,
+ 781CFF6314E4583900BD5D50 /* BetterYouTube-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 781CFFA614E48E8E00BD5D50 /* PSYouTubeExtractor */ = {
+ isa = PBXGroup;
+ children = (
+ 781CFFA714E48E9900BD5D50 /* PSYouTubeExtractor.h */,
+ 781CFFA814E48E9900BD5D50 /* PSYouTubeExtractor.m */,
+ 781CFFAA14E4BCFA00BD5D50 /* PSYouTubeView.h */,
+ 781CFFAB14E4BCFA00BD5D50 /* PSYouTubeView.m */,
+ );
+ name = PSYouTubeExtractor;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 781CFF5014E4583900BD5D50 /* BetterYouTube */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 781CFF6914E4583900BD5D50 /* Build configuration list for PBXNativeTarget "BetterYouTube" */;
+ buildPhases = (
+ 781CFF4D14E4583900BD5D50 /* Sources */,
+ 781CFF4E14E4583900BD5D50 /* Frameworks */,
+ 781CFF4F14E4583900BD5D50 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = BetterYouTube;
+ productName = BetterYouTube;
+ productReference = 781CFF5114E4583900BD5D50 /* BetterYouTube.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 781CFF4814E4583900BD5D50 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0420;
+ ORGANIZATIONNAME = "Peter Steinberger";
+ };
+ buildConfigurationList = 781CFF4B14E4583900BD5D50 /* Build configuration list for PBXProject "BetterYouTube" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 781CFF4614E4583900BD5D50;
+ productRefGroup = 781CFF5214E4583900BD5D50 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 781CFF5014E4583900BD5D50 /* BetterYouTube */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 781CFF4F14E4583900BD5D50 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 781CFF6014E4583900BD5D50 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 781CFF4D14E4583900BD5D50 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 781CFF6214E4583900BD5D50 /* main.m in Sources */,
+ 781CFF6614E4583900BD5D50 /* PSAppDelegate.m in Sources */,
+ 781CFF6E14E4586000BD5D50 /* PSYouTubeTestViewController.m in Sources */,
+ 781CFFA914E48E9900BD5D50 /* PSYouTubeExtractor.m in Sources */,
+ 781CFFAC14E4BCFA00BD5D50 /* PSYouTubeView.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 781CFF5E14E4583900BD5D50 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 781CFF5F14E4583900BD5D50 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 781CFF6714E4583900BD5D50 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ 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_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 781CFF6814E4583900BD5D50 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 781CFF6A14E4583900BD5D50 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "BetterYouTube/BetterYouTube-Prefix.pch";
+ INFOPLIST_FILE = "BetterYouTube/BetterYouTube-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 781CFF6B14E4583900BD5D50 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "BetterYouTube/BetterYouTube-Prefix.pch";
+ INFOPLIST_FILE = "BetterYouTube/BetterYouTube-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 781CFF4B14E4583900BD5D50 /* Build configuration list for PBXProject "BetterYouTube" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 781CFF6714E4583900BD5D50 /* Debug */,
+ 781CFF6814E4583900BD5D50 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 781CFF6914E4583900BD5D50 /* Build configuration list for PBXNativeTarget "BetterYouTube" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 781CFF6A14E4583900BD5D50 /* Debug */,
+ 781CFF6B14E4583900BD5D50 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 781CFF4814E4583900BD5D50 /* Project object */;
+}
39 Example/BetterYouTube/BetterYouTube-Info.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFiles</key>
+ <array/>
+ <key>CFBundleIdentifier</key>
+ <string>com.petersteinberger.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
14 Example/BetterYouTube/BetterYouTube-Prefix.pch
@@ -0,0 +1,14 @@
+//
+// Prefix header for all source files of the 'BetterYouTube' target in the 'BetterYouTube' project
+//
+
+#import <Availability.h>
+
+#ifndef __IPHONE_3_0
+#warning "This project uses features only available in iOS SDK 3.0 and later."
+#endif
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+#endif
15 Example/BetterYouTube/PSAppDelegate.h
@@ -0,0 +1,15 @@
+//
+// PSAppDelegate.h
+// BetterYouTube
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface PSAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
24 Example/BetterYouTube/PSAppDelegate.m
@@ -0,0 +1,24 @@
+//
+// PSAppDelegate.m
+// BetterYouTube
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import "PSAppDelegate.h"
+#import "PSYouTubeTestViewController.h"
+
+@implementation PSAppDelegate
+
+@synthesize window = _window;
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ self.window.backgroundColor = [UIColor whiteColor];
+ self.window.rootViewController = [[PSYouTubeTestViewController alloc] init];
+ [self.window makeKeyAndVisible];
+ return YES;
+}
+
+@end
16 Example/BetterYouTube/PSYouTubeTestViewController.h
@@ -0,0 +1,16 @@
+//
+// PSYouTubeTestViewController.h
+// BetterYouTube
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import <MediaPlayer/MediaPlayer.h>
+
+@interface PSYouTubeTestViewController : UIViewController
+
+@property (nonatomic, strong) MPMoviePlayerController *moviePlayerController;
+
+@end
33 Example/BetterYouTube/PSYouTubeTestViewController.m
@@ -0,0 +1,33 @@
+//
+// PSYouTubeTestViewController.m
+// BetterYouTube
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import "PSYouTubeTestViewController.h"
+#import "PSYouTubeView.h"
+
+@implementation PSYouTubeTestViewController
+
+@synthesize moviePlayerController = moviePlayerController_;
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ NSURL *youTubeURL = [NSURL URLWithString:@"http://www.youtube.com/watch?v=Vo0Cazxj_yc"];
+ CGFloat size = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) ? 250 : 500;
+ CGRect videoRect = CGRectMake(0, 0, size, size);
+
+ PSYouTubeView *youTubeView = [[PSYouTubeView alloc] initWithYouTubeURL:youTubeURL frame:videoRect showNativeFirst:YES];
+ youTubeView.center = self.view.center;
+ youTubeView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
+ [self.view addSubview:youTubeView];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+ return YES;
+}
+
+@end
2  Example/BetterYouTube/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
16 Example/BetterYouTube/main.m
@@ -0,0 +1,16 @@
+//
+// main.m
+// BetterYouTube
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "PSAppDelegate.h"
+
+int main(int argc, char *argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([PSAppDelegate class]));
+ }
+}
50 Example/PSYouTubeView.h
@@ -0,0 +1,50 @@
+//
+// PSYouTubeView.h
+// PSYouTubeExtractor
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import <MediaPlayer/MediaPlayer.h>
+
+/// Uses MPMoviePlayerController whenever possible, else falls back to the UIWebView YouTube plugin.
+/// Note: The YouTube plugin doesn't show up in the Simulator. Test on the device!
+@interface PSYouTubeView : UIView
+
+/// Init with YouTube URL and desired frame.
+/// Note: When we have to fall back to UIWebView, the frame cannot be changed later on.
+/// Enable showNativeFirst to first show an empty MPMoviePlayerController.
+- (id)initWithYouTubeURL:(NSURL *)youTubeURL frame:(CGRect)frame showNativeFirst:(BOOL)showNativeFirst;
+
+/// Access the original YouTube URL (e.g. http://www.youtube.com/watch?v=Vo0Cazxj_yc)
+@property(nonatomic, strong, readonly) NSURL *youTubeURL;
+
+/// Raw mp4 URL, if it could be extracted.
+@property(nonatomic, strong, readonly) NSURL *youTubeMovieURL;
+
+/// Set if extracting the YouTube mp4 fails.
+@property(nonatomic, strong, readonly) NSError *error;
+
+/// YES if MPMoviePlayerController is used. NO if we had to fallback to UIWebView.
+@property(nonatomic, assign, readonly, getter=isNativeView) BOOL nativeView;
+
+/// Animates view changes. Defaults to YES. Fades windows on a change.
+@property(nonatomic, assign, getter=isAnimated) BOOL animated;
+
+
+/// Is called initially, and once if a MP4 is found. Default implementation is provided.
+@property(nonatomic, strong) void (^setupNativeView)(void);
+
+/// Used in the default implementation.
+@property (nonatomic, strong) MPMoviePlayerController *moviePlayerController;
+
+
+// Called only if MP4 could not be found. Default implementation is provided.
+@property(nonatomic, strong) void (^setupWebView)(void);
+
+/// Used in the default implementation.
+@property (nonatomic, strong) UIWebView *webView;
+
+@end
134 Example/PSYouTubeView.m
@@ -0,0 +1,134 @@
+//
+// PSYouTubeView.m
+// PSYouTubeExtractor
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import "PSYouTubeView.h"
+#import "PSYouTubeExtractor.h"
+
+@interface PSYouTubeView() {
+ BOOL showNativeFirst_;
+ PSYouTubeExtractor *extractor_;
+}
+@end
+
+@implementation PSYouTubeView
+
+@synthesize youTubeURL = youTubeURL_;
+@synthesize youTubeMovieURL = youTubeMovieURL_;
+@synthesize nativeView = nativeView_;
+@synthesize setupNativeView = setupNativeView_;
+@synthesize setupWebView = setupWebView_;
+@synthesize moviePlayerController = moviePlayerController_;
+@synthesize webView = webView_;
+@synthesize error = error_;
+@synthesize animated = animated_;
+
+- (id)initWithYouTubeURL:(NSURL *)youTubeURL frame:(CGRect)frame showNativeFirst:(BOOL)showNativeFirst {
+ if ((self = [super initWithFrame:frame])) {
+ youTubeURL_ = youTubeURL;
+ showNativeFirst_ = showNativeFirst;
+ animated_ = YES;
+
+ __unsafe_unretained PSYouTubeView *weakSelf = self;
+ setupNativeView_ = ^{
+ if (!weakSelf.moviePlayerController) {
+ MPMoviePlayerController *movieController = [[MPMoviePlayerController alloc] initWithContentURL:weakSelf.youTubeMovieURL];
+ movieController.view.frame = weakSelf.bounds;
+ movieController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [weakSelf insertSubview:movieController.view atIndex:0];
+ weakSelf->moviePlayerController_ = movieController;
+ }else {
+ weakSelf.moviePlayerController.contentURL = weakSelf.youTubeMovieURL;
+ }
+
+ if (weakSelf.youTubeMovieURL) {
+ [weakSelf.moviePlayerController prepareToPlay];
+ [weakSelf.moviePlayerController setShouldAutoplay:YES];
+ }
+
+ // if there is a webview, remove it!
+ if (weakSelf.webView) {
+ [UIView animateWithDuration:weakSelf.isAnimated ? 0.3f : 0.f delay:0.f options:UIViewAnimationOptionAllowUserInteraction animations:^{
+ weakSelf.webView.alpha = 0.f;
+ } completion:^(BOOL finished) {
+ [weakSelf.webView removeFromSuperview];
+ weakSelf.webView.delegate = nil;
+ weakSelf.webView = nil;
+ }];
+ }
+ };
+
+ setupWebView_ = ^{
+ if (!weakSelf.webView) {
+ UIWebView *webView = [[UIWebView alloc] initWithFrame:weakSelf.bounds];
+ webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [weakSelf insertSubview:webView atIndex:0];
+ // allow inline playback, even on iPhone
+ webView.allowsInlineMediaPlayback = YES;
+ weakSelf->webView_ = webView;
+
+ // load plugin
+ NSString *embedHTML = @"<html><head><style type=\"text/css\"> \
+ body {background-color:transparent;color:white;}</style> \
+ </head><body style=\"margin:0\"> \
+ <embed id=\"yt\" src=\"%@\" type=\"application/x-shockwave-flash\" \
+ width=\"%0.0f\" height=\"%0.0f\"></embed></body></html>";
+ NSString *html = [NSString stringWithFormat:embedHTML, [weakSelf.youTubeURL absoluteString], weakSelf.frame.size.width, weakSelf.frame.size.height];
+ [webView loadHTMLString:html baseURL:nil];
+ }
+
+ // remove MPMoviePlayerController
+ if(weakSelf.moviePlayerController) {
+ [UIView animateWithDuration:weakSelf.isAnimated ? 0.3f : 0.f delay:0.f options:UIViewAnimationOptionAllowUserInteraction animations:^{
+ weakSelf.moviePlayerController.view.alpha = 0.f;
+ } completion:^(BOOL finished) {
+ [weakSelf.moviePlayerController.view removeFromSuperview];
+ weakSelf.moviePlayerController = nil;
+ }];
+ }
+ };
+
+ // retains itself until either success or failure is called
+ extractor_ = [PSYouTubeExtractor extractorForYouTubeURL:self.youTubeURL success:^(NSURL *URL) {
+ //NSLog(@"Finished extracting: %@", URL);
+ youTubeMovieURL_ = URL;
+ if (setupNativeView_) {
+ setupNativeView_();
+ }
+ } failure:^(NSError *error) {
+ //NSLog(@"Failed to query mp4: %@", error);
+ error_ = error;
+ if (setupWebView_) {
+ setupWebView_();
+ }
+ }];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [extractor_ cancel];
+ webView_ .delegate = nil;
+}
+
+// invoke the view generation as soon as the view will be added to the screen
+// (don't do that in init to allow replacement of the blocks)
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+ if (!self.webView && !self.moviePlayerController) {
+ if (showNativeFirst_) {
+ if (setupNativeView_) {
+ setupNativeView_();
+ }
+ }else {
+ if (setupWebView_) {
+ setupWebView_();
+ }
+ }
+ }
+}
+
+@end
9 LICENSE
@@ -0,0 +1,9 @@
+If not noted otherwise in the file header, the project uses the MIT license.
+
+Copyright (c) 2012, Peter Steinberger
+
+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.
26 PSYouTubeExtractor.h
@@ -0,0 +1,26 @@
+//
+// PSYouTubeExtractor.h
+// PSYouTubeExtractor
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/// This class opens a hidden UIWebView and extracts the mobile YouTube URL if possible.
+/// It's not a subclass of NSOperation because of the UIWebView interaction.
+/// While running, the class retains itself. Call cancel to stop and release the internal retain.
+@interface PSYouTubeExtractor : NSObject
+
+/// Tries to extract the actual mp4 used to play a YouTube movie.
+/// There are quite some movies out there that don't support mobile and don't have a mp4 version set.
++ (PSYouTubeExtractor *)extractorForYouTubeURL:(NSURL *)youTubeURL success:(void(^)(NSURL *URL))success failure:(void(^)(NSError *error))failure;
+
+/// Cancels a potential running request. Returns NO if request already finished.
+- (BOOL)cancel;
+
+/// Access the original YouTube URL.
+@property(nonatomic, strong, readonly) NSURL *youTubeURL;
+
+@end
188 PSYouTubeExtractor.m
@@ -0,0 +1,188 @@
+//
+// PSYouTubeExtractor.m
+// PSYouTubeExtractor
+//
+// Created by Peter Steinberger on 2/9/12.
+// Copyright (c) 2012 Peter Steinberger. All rights reserved.
+//
+
+#import "PSYouTubeExtractor.h"
+#import <UIKit/UIKit.h>
+
+@interface PSYouTubeExtractor() <UIWebViewDelegate> {
+ BOOL testedDOM_;
+ NSUInteger retryCount_;
+ UIWebView *webView_;
+ NSURLRequest *lastRequest_;
+ PSYouTubeExtractor *selfReference_;
+ void (^successBlock_) (NSURL *URL);
+ void (^failureBlock_) (NSError *error);
+}
+- (void)DOMLoaded_;
+- (void)cleanup_;
+@end
+
+@implementation PSYouTubeExtractor
+
+@synthesize youTubeURL = youTubeURL_;
+
+#define kMaxNumberOfRetries 3 // numbers of retries
+#define kWatchdogDelay 0.7f // seconds we wait for the DOM
+
+// uncomment to enable logging
+#define PSLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+//#define PSLog(fmt, ...)
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - NSObject
+
+- (id)initWithYouTubeURL:(NSURL *)youTubeURL success:(void(^)(NSURL *URL))success failure:(void(^)(NSError *error))failure {
+ if ((self = [super init])) {
+ successBlock_ = success;
+ failureBlock_ = failure;
+ youTubeURL_ = youTubeURL;
+ selfReference_ = self; // retain while running!
+ webView_ = [[UIWebView alloc] init];
+ webView_.delegate = self;
+ [webView_ loadRequest:[NSURLRequest requestWithURL:youTubeURL]];
+ PSLog(@"Starting YouTube extractor for %@", youTubeURL);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self cleanup_];
+ webView_.delegate = nil;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - Static
+
++ (PSYouTubeExtractor *)extractorForYouTubeURL:(NSURL *)youTubeURL success:(void(^)(NSURL *URL))success failure:(void(^)(NSError *error))failure {
+ PSYouTubeExtractor *extractor = [[PSYouTubeExtractor alloc] initWithYouTubeURL:youTubeURL success:success failure:failure];
+ return extractor;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - Public
+
+- (void)cleanup_ {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(DOMLoaded_) object:nil]; // cancel watchdog
+ successBlock_ = nil;
+ failureBlock_ = nil;
+ selfReference_ = nil;
+ [webView_ stopLoading];
+}
+
+- (BOOL)cancel {
+ PSLog(@"Cancel called.");
+ if (selfReference_) {
+ [self cleanup_];
+ return YES;
+ }
+ return NO;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - Private
+
+// very possible that the DOM isn't really loaded after all or sth failed. Try to load website again.
+- (BOOL)doRetry_ {
+ if (retryCount_ <= kMaxNumberOfRetries) {
+ retryCount_++;
+ PSLog(@"Trying again to load page...");
+ [webView_ loadRequest:[NSURLRequest requestWithURL:lastRequest_.URL]];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(DOMLoaded_) object:nil];
+ return YES;
+ }
+ return NO;
+}
+
+- (void)DOMLoaded_ {
+ PSLog(@"DOMLoaded_ / watchdog hit");
+
+ // figure out if we can extract the youtube url!
+ NSString *youTubeMP4URL = [webView_ stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('video')[0].getAttribute('src')"];
+
+ if ([youTubeMP4URL hasPrefix:@"http"]) {
+ // probably ok
+ if (successBlock_) {
+ NSURL *URL = [NSURL URLWithString:youTubeMP4URL];
+ successBlock_(URL);
+ }
+ [self cleanup_];
+ }else {
+ if (![self doRetry_]) {
+ NSError *error = [NSError errorWithDomain:@"com.petersteinberger.betteryoutube" code:100 userInfo:[NSDictionary dictionaryWithObject:@"MP4 URL could not be found." forKey:NSLocalizedDescriptionKey]];
+ if (failureBlock_) {
+ failureBlock_(error);
+ }
+ [self cleanup_];
+ }
+ }
+}
+
+- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)aRequest navigationType:(UIWebViewNavigationType)navigationType {
+ BOOL should = YES;
+ NSURL *url = [aRequest URL];
+ NSString *scheme = [url scheme];
+
+ // Check for DOM load message
+ if ([scheme isEqualToString:@"x-sswebview"]) {
+ NSString *host = [url host];
+ if ([host isEqualToString:@"dom-loaded"]) {
+ PSLog(@"DOM load detected!");
+ [self DOMLoaded_];
+ }
+ return NO;
+ }
+
+ // Only load http or http requests if delegate doesn't care
+ else {
+ should = [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"];
+ }
+
+ // Stop if we shouldn't load it
+ if (should == NO) {
+ return NO;
+ }
+
+ // Starting a new request
+ if ([[aRequest mainDocumentURL] isEqual:[lastRequest_ mainDocumentURL]] == NO) {
+ lastRequest_ = aRequest;
+ testedDOM_ = NO;
+ }
+
+ return should;
+}
+
+// With some guidance of SSToolKit this was pretty easy. Thanks Sam!
+- (void)webViewDidFinishLoad:(UIWebView *)webView {
+ PSLog(@"webViewDidFinishLoad");
+
+ // Check DOM
+ if (testedDOM_ == NO) {
+ testedDOM_ = YES;
+
+ // The internal delegate will intercept this load and forward the event to the real delegate
+ // Crazy javascript from http://dean.edwards.name/weblog/2006/06/again
+ static NSString *testDOM = @"var _SSWebViewDOMLoadTimer=setInterval(function(){if(/loaded|complete/.test(document.readyState)){clearInterval(_SSWebViewDOMLoadTimer);location.href='x-sswebview://dom-loaded'}},10);";
+ [webView_ stringByEvaluatingJavaScriptFromString:testDOM];
+ }
+
+ // add watchdog in case DOM never get initialized
+ [self performSelector:@selector(DOMLoaded_) withObject:nil afterDelay:kWatchdogDelay];
+}
+
+- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
+ PSLog(@"didFailLoadWithError");
+
+ if (![self doRetry_]) {
+ if (failureBlock_) {
+ failureBlock_(error);
+ }
+ [self cleanup_];
+ }
+}
+
+@end
43 README.md
@@ -0,0 +1,43 @@
+## PSYouTubeExtractor ("BetterYouTube")
+
+Displaying YouTube is a pain in the ass. This class makes it a lot more bearable by trying to extract the native mp4 when available. If that doesn't work, we fall back to the UIWebView YouTube plugin.
+
+There are two classes available:
+
+### PSYouTubeExtractor
+
+Does some crazy things behind the scenes and extracts the mp4 of a YouTube video. I use a UIWebView to get the data, as Google does a pretty good job of obfuscating their html content.
+
+ [PSYouTubeExtractor extractorForYouTubeURL:self.youTubeURL success:^(NSURL *URL) {
+ NSLog(@"Finished extracting: %@", URL);
+ // show the movie!
+ } failure:^(NSError *error) {
+ NSLog(@"Failed to query mp4: %@", error);
+ }];
+
+Note that PSYouTubeExtractor is *not* a NSOperation, as there's some craziness behind the scenes that need a RunLoop (and I didn't want to mess around with Runloops in NSOperation). The class retains itself until either success or failure is called, or until you send cancel to it. The blocks are nullified afterwards, so don't worry about retain cycles. (You still have to worry about Xcode bit chin' about it.)
+
+### PSTouTubeView
+
+Woohoo! That's where the awesomeness is. Just use this instead of your UIWebView and you're good.
+
+ NSURL *youTubeURL = [NSURL URLWithString:@"http://www.youtube.com/watch?v=Vo0Cazxj_yc"];
+ PSYouTubeView *youTubeView = [[PSYouTubeView alloc] initWithYouTubeURL:youTubeURL frame:CGRectMake(0,0,200,200) showNativeFirst:YES];
+ [self.view addSubview:youTubeView];
+
+Note that you should set the correct frame right away. If we need to fallback to UIWebView, the YouTube plugin can't resize. (You can recreate it, but that would kill a running video). However, in most cases it should extract the mp4 successfully and you don't need to worry about that crap.
+
+The setting 'showNativeFirst' decides if you want to start with a MPMoviePlayerController or a UIWebView. As we are optimistic, I suggest you set this to YES per default.
+
+I am using this class for [PSPDFKit](http://pspdfkit.com), my pdf framework where you can add interactive elements, and YouTube just sucked too much, so I wrote this helper. That's also why there is a block for "setupNativeView" and "setupWebView", you can override those and do your own custom stuff with it.
+
+### Help wanted!
+
+If anyone has a better way of extracting the final YouTube mp4 url (maybe some crazy regex magic), it would make the class a lot faster (we could get rid of the UIWebView). I am kinda ok with the UIWebView solution though, as this one will be pretty robust. I look for a <video> tag, and as long as Google shows a video on a YouTube page, we find the source.
+
+Also, we could add support for Reachability to re-try the extracting in case we didn't had network when the view was created. Feel free to send a pull request!
+
+
+### License
+
+MIT! See LICENSE file for the legal stuff.
Please sign in to comment.
Something went wrong with that request. Please try again.