Skip to content

Instantly share code, notes, and snippets.

@romainbriche
Created April 5, 2012 07:07
Show Gist options
  • Save romainbriche/2308668 to your computer and use it in GitHub Desktop.
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
// 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
}
@damienromito
Copy link

This code is interesting to catch de MPMoviePlayerController. But now on IOS7, I think this one is no longer in the UIWebView. @Romain, did you updated this code?

@damienromito
Copy link

In IOS7 the UIView is called UIMovieView but without interface and not in the UIWebView but near. You can Use the same loop to find this in all the Parent subviews.
Then, the solution is to create an interface similar to MPMoviePlayer and get(or not) the avplayer.
It's tricky but it's work for me.

@sergioabril
Copy link

@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?

@KiranPanesar
Copy link

KiranPanesar commented Jul 19, 2016

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