Created
April 5, 2012 07:07
-
-
Save romainbriche/2308668 to your computer and use it in GitHub Desktop.
How to hack the UIWebView YouTube-view to control play/pause and make it resizable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Ok, this stuff is straight out of http://pspdfkit.com/ | |
// Business-like it's probably not a good idea to share this | |
// but I just really love helping people and open sourcing stuff. | |
// Consider this snipped licensed under MIT, by Peter Steinberger. | |
// http://www.opensource.org/licenses/mit-license.php | |
// This uses all kinds of nasty private API, but I don't see another way. | |
// I tried to extract the mp4 URL in this project, https://github.com/steipete/PSYouTubeExtractor, | |
// but it required *lots* of hacks, is slow, and doesn't worked very well on iOS5. | |
// Turns out, Google _really_ doesn't want people access to their mp4 sources. | |
// (which is understandable, since they make money with putting ads on it.) | |
// So either you do some crazy, crazy workarounds like this 5000LOC python script: | |
// (https://github.com/rg3/youtube-dl/blob/master/youtube-dl) to fetch the URL | |
// (and it's still against Google's Terms of Service), or you go the way they intended it, | |
// and just tweak it a bit. | |
// Internally, the YouTube view uses some custom stuff that looks pretty similar to a MPMovieController, | |
// so sending play/pause is very straightforward. | |
// (see https://github.com/steipete/iOS-Runtime-Headers/blob/master/Frameworks/MediaPlayer.framework/MPAVController.h). | |
// Why the YouTube-view doesn't resize itself is beyond me, but that's also easily fixable, | |
// just a matter of finding the right views. | |
// I'm aware that it's horrible code, and if anyone has a better solution, | |
// I'm very willing to update it. | |
// BUT, it works on iOS4 and iOS5 and it'll pass the AppStore review. | |
UIView *PSPDFGetViewInsideView(UIView *view, NSString *classNamePrefix) { | |
UIView *webBrowserView = nil; | |
for (UIView *subview in view.subviews) { | |
if ([NSStringFromClass([subview class]) hasPrefix:classNamePrefix]) { | |
return subview; | |
}else { | |
if((webBrowserView = PSPDFGetViewInsideView(subview, classNamePrefix))) { | |
break; | |
} | |
} | |
} | |
return webBrowserView; | |
} | |
// get the class called MPAVController to play/pause the YouTube video. | |
// https://github.com/steipete/iOS-Runtime-Headers/blob/master/Frameworks/MediaPlayer.framework/MPAVController.h | |
- (UIViewController *)movieController { | |
UIViewController *movieController = nil; | |
#ifndef _PSPDFKIT_DONT_USE_OBFUSCATED_PRIVATE_API_ | |
@try { | |
UIView *movieView = PSPDFGetViewInsideView(self.webView, [NSString stringWithFormat:@"MP%@oView", @"Vide"]); | |
SEL mpavControllerSel = NSSelectorFromString([NSString stringWithFormat:@"mp%@roller", @"avCont"]); | |
if ([movieView respondsToSelector:mpavControllerSel]) { | |
#pragma clang diagnostic push | |
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" | |
movieController = (UIViewController *)[movieView performSelector:mpavControllerSel]; | |
#pragma clang diagnostic pop | |
} | |
} | |
@catch (NSException *exception) { | |
PSPDFLogWarning(@"Failed to get movieController: %@", exception); | |
} | |
#endif | |
return movieController; | |
} | |
/// page is displayed | |
- (void)didShowPage:(NSUInteger)page { | |
if (!isShowing_) { | |
isShowing_ = YES; | |
// more stuff happening that's not important here | |
#ifndef _PSPDFKIT_DONT_USE_OBFUSCATED_PRIVATE_API_ | |
if (wasPlaying_) { | |
@try { | |
UIViewController *movieController = [self movieController]; | |
if([movieController respondsToSelector:@selector(play)]) { | |
[movieController performSelector:@selector(play)]; | |
} | |
} | |
@catch (NSException *exception) { | |
PSPDFLogWarning(@"Failed to play YouTube view: %@", exception); | |
} | |
} | |
#endif | |
} | |
} | |
/// page is hidden | |
- (void)didHidePage:(NSUInteger)page { | |
if (isShowing_) { | |
isShowing_ = NO; | |
#ifndef _PSPDFKIT_DONT_USE_OBFUSCATED_PRIVATE_API_ | |
@try { | |
UIViewController *movieController = [self movieController]; | |
if([movieController respondsToSelector:NSSelectorFromString(@"isPlaying")]) { | |
wasPlaying_ = [[movieController valueForKey:@"isPlaying"] boolValue]; | |
} | |
if (wasPlaying_) { | |
if([movieController respondsToSelector:@selector(pause)]) { | |
[movieController performSelector:@selector(pause)]; | |
} | |
} | |
} | |
@catch (NSException *exception) { | |
PSPDFLogWarning(@"Failed to pause YouTube view: %@", exception); | |
} | |
#endif | |
} | |
} | |
- (void)didChangePageFrame:(CGRect)frame { | |
// all this custom hacking, again. but we need to make the YouTube view to resize. | |
#ifndef _PSPDFKIT_DONT_USE_OBFUSCATED_PRIVATE_API_ | |
CGRect targetRect = (CGRect){CGPointZero, self.frame.size}; | |
// we need to delay that... oh well | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
@try { | |
UIView *webBrowserView = PSPDFGetViewInsideView(self.webView, @"UIWebB"); | |
webBrowserView.frame = targetRect; | |
UIView *youTubeView = PSPDFGetViewInsideView(self.webView, @"YouTubeP"); | |
youTubeView.frame = targetRect; | |
} | |
@catch (NSException *exception) { | |
PSPDFLogWarning(@"Whoops, subview hacking failed. Won't resize view then. %@", exception); | |
} | |
}); | |
#endif | |
} |
Looks like Apple recently broke this by overriding the subviews
method of UIWebBrowserView
to return an empty array 😱
This is causing the recursive search for a class to break as it can't get inside of the UIWebBrowserView
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@damienromito do you have any sample code of that interface and use of it? What would be the approach, if any, to get the content URL of that UIMovieView?