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