7

Everyone who tried to make Youtube/Vimeo videos start play automatically on iOS knows that it could be a painful task. Apple blocked the 'autoplay' parameter for the right reasons, but sometimes you still need to get this functionality working.

I had the same issue with auto playing youtube videos, apparently, to get the autoplay to work, you need to do some javascript magic, listen for the player's 'onReady' event, than when the player is loaded and ready to play you call 'player.play()' to start it without any another user intervention.

Vimeo also got some javascript API, and I'm pretty sure you can do the autoplay trick with it, I just can't figure it up how to use it.

They have JS mini-library called Froogaloop to make things easier, I saw this answer by @ila who use it in conjunction with the following html string:

NSString *htmlString = [NSString stringWithFormat:
                                                @"<html><head>"
                                                "<script src=\"froogaloop.js\"></script>"
                                                " <script>"
                                                    "function ready()"
                                                        "{$f(document.getElementById(\"player\")).api(\"play\");}"
                                                    "function func1() "
                                                        "{$f(document.getElementById(\"player\")).addEvent(\"ready\", ready);}"
                                                    "window.onload=func1;"
                                                "</script></head><body>"
                                                       "<iframe id=\"player\" src=\"http://player.vimeo.com/video/39287488?api=1&amp;player_id=player\" width=\"315\" height=\"455\" frameborder=\"0\" webkit-playsinline>"
                                               " </iframe></body></html>"];    

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSLog(@"webview loaded");
    NSString *path = [[NSBundle mainBundle] pathForResource:@"froogaloop" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [webView stringByEvaluatingJavaScriptFromString:jsCode];
}

But this doesn't work for me, after adding alerts to this code, I can see that func1() get called and executing the 'addEvent' line, BUT it seams that the ready() method never gets called because the 'ready' event never fired (?)...

Any ideas?

Community
  • 1
  • 1
Eyal
  • 10,777
  • 18
  • 78
  • 130

2 Answers2

8

My first answer to the question is a less than ideal brute force method. I'd suggest looking at my second method using the Vimeo Javascript API and froogaloop.

I'm going to assume you're using the Vimeo iframe inside a html page and that you're using this page inside a UIWebView inside an iOS app or inside Safari.

A little bit on introspection into the Vimeo iframe shows that it doesn't load the html video tag initially. You're going to have to programatically tap the 'play' button for it to load the video.

In order to do that today (April 2013, it may change) you can just grab the right elements and call the right functions. It's best demonstrated with some working sample code.

// You are loading this page inside a UIWebView
<html>
<head></head>
<body>
    <iframe width="" height="" src="http://player.vimeo.com/video/62207569 " frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
    <script>
    // Add a listener for the window's load event
    window.addEventListener('load', onWindowLoad, false);

    function onWindowLoad(event) {
        //use a loop to continue checking the vimeo iframe for the 'play' button
        checkForTrue(isIframeButtonLoaded, iFrameButtonLoaded);
    }

    function checkForTrue(func, callback) {
        setTimeout(function() {
            if (func()) {
                callback( iFrameButton() );
            } else {
                checkForTrue(func, callback);
            };
        }, 1000);
    }

    //finds the 'play' button within the Vimeo iframe
    function iFrameButton() {
                    //window.frames[0].document.getElementsByTagName('div')[1]; // this also works...
        return window.frames[0].document.getElementsByTagName('button')[5];
    }

    //simple boolean condition to check for the iframe button
    function isIframeButtonLoaded() {
        return ( iFrameButton() != null );
    }

    //once the button is loaded, click it
    function iFrameButtonLoaded(iFrameButton) {
        iFrameButton.click();
    }

    </script>
</body>
</html>

This source code is also available as a gist

A sane person may say this is not the best way to do it, but for the purpose of automatically playing a vimeo video within your iOS app it does seem to work.

The code to load the html file from your bundle and load it into a UIWebView is boilerplate:

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"VimeoTrial" ofType:@"html"];
    NSString *htmlString = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:nil];

    [self.webView loadHTMLString:htmlString baseURL:nil];
}
Jessedc
  • 12,320
  • 3
  • 50
  • 63
  • hi, thanks for your reply :) It seems to work, but I do have two problems with this solution: 1. As you mentioned, this is not the best approach, I prefer an answer that handle this issue using the official Vimeo JS API. Things like the button layout can change with time and break my code. 2. When I tap to play, the play botton is shown for a few secondes and only then the spinner starts. I try to change the 1000 value in 'setTimeout' but it doesn't help. I want that the spinner will begin the second I tap the play button (or at least that the play button wont be visible after tapping it) – Eyal Apr 09 '13 at 16:39
  • According to the JS API there is a 'play' method you can send to the player (See API Reference). I'll update my answer when I have a chance. https://developer.vimeo.com/player/js-api – Jessedc Apr 09 '13 at 21:28
  • Your code is not working in my case. Can you help me a bit? @Jessedc – Avijit Jan 11 '14 at 07:29
2

Loading a file locally or via a HTML string won't work due to the Same Origin Policy enforced by the web browser.

The iframe is unable to propagate it's postEvent() calls back to the main window because the Vimeo iframe is being accessed via the http:// protocol and the local file is via the file:// protocol. The 'ready' event is sent via this mechanism.

It's possible to use the Vimeo API in this instance but in order to automatically play the video you need to know when the iframe has loaded but the "ready" event is sent from the iframe up to the window via postEvent.

The fact that it's using postEvent is is made clear by the code provided in the Vimeo API Documentation. Froogaloop also uses the same code

window.addEventListener('message', onMessageReceived, false);

If you run the most basic example of the Vimeo API in an HTML file on your machine Google Chrome will throw a descriptive message: (Safari does not show this)

Unsafe JavaScript attempt to access frame with URL file:///...index.html from frame with URL http://player.vimeo.com... The frame requesting access has a protocol of 'http', the frame being accessed has a protocol of 'file'. Protocols must match.

The code in question is simply: (See this gist for the full source)

<iframe id="player" width="" height="" src="http://player.vimeo.com/video/62207569?api=1&player_id=player" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>

<script src="http://a.vimeocdn.com/js/froogaloop2.min.js"></script>

<script>    
  var player = $f(document.getElementById('player'));
  player.addEvent('ready', function() { 
  player.api('play');
});
</script>

The proper solution is to host the HTML page on a remote service that has the same protocol as the Vimeo iframe. In this case anything from a http:// address.

If you load the above from http://(insert your host here) it works instantly in the iOS simulator, Safari and Google chrome.

If you wanted to do more on device, you'd have to merge the two answers I've given; Poll the iframe until you deem it ready, then tell the player object to play.

Jessedc
  • 12,320
  • 3
  • 50
  • 63
  • have you seen my solution for same problem with youtube videos? http://stackoverflow.com/questions/15717754/ can't I use the same approche with vimeo? – Eyal Apr 10 '13 at 10:14
  • The answers you're pointing at don't hint at how you would get Vimeo videos to work the same way. By looking at the two APIs it's clear they operate completely differently. The YouTube iframe API creates the iframe element on the page, whereas the Vimeo API is embedded in the iframe itself. Youtube no doubt binds a handful of events with javascript directly through to the iframe (similar to the deep diving in my first answer), and Vimeo only uses the standard postEvent() method. – Jessedc Apr 10 '13 at 11:50