public
Last active

How to hack the UIWebView YouTube-view to control play/pause and make it resizable

  • Download Gist
YouTubeHacks.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
// 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
}

how to use this?

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?

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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.