8

I'm trying to determine whether the browser supports autoplay on load.

I'm using the following code, and it works fine on Android chrome, but for Desktop Chrome none of the lines in .catch or .then get executed. The promise seems to just return Pending promises ad infinitum.

Is this an actual Chrome bug or am I not understanding how Promises work here?

const promise = document.createElement('video').play();

if (promise instanceof Promise) {
 promise.catch((error) => {
  // Check if it is the right error
  if (error.name === 'NotAllowedError') {
   autoPlayAllowed = false;
  } else {
   throw error;
  }
 }).then(() => {
  if (autoPlayAllowed) {
   // Autoplay is allowed - continue with initialization
   console.log('autoplay allowed')
  } else {
   // Autoplay is not allowed - wait for the user to trigger the play button manually
   console.log('autoplay NOT allowed')
  }
 });

} else {
 // Unknown if allowed
 console.log('autoplay unknown')
}

Thanks!

Grandas
  • 519
  • 1
  • 5
  • 13
  • You never set an src to your video ? What kind of error do you expect then ? – Kaiido Dec 23 '16 at 01:07
  • Thanks for the reply @Kaiido I seem to get the same outcome after I add src as well, am I missing something else? var test = document.createElement('video'); test.setAttribute("src", "http://vjs.zencdn.net/v/oceans.mp4"); var promise = test.play(); ... (Same code as in question) Result: Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} – Grandas Dec 23 '16 at 01:20
  • That's really weird. Good find on defining autoPlayAllowed, with it I can at least return some value from the function (AND `.catch` seems to catch error successfully on Android Chrome which is exactly what I need) – Grandas Dec 23 '16 at 01:40

2 Answers2

18

Which browsers Promisify MediaElement.play()?

The WHATWG spec has only recommended that MediaElement.play() be Promisified since a Feb 2016 pull request following on from an issue submitted on Jan 2016. It is connected to the recent decision to prevent MediaElements from autoplaying (except under special conditions, such as these, for iOS Safari).

The W3C spec still (as of Nov 2017) does not appear to mention that MediaElement.play() should return a Promise.

MediaElement.play() returns a Promise as of version:

The status of this change is unknown for Edge (feel free to comment).

How can I test whether my browser supports it?

The Chrome team have released a test page to check whether your own browser supports Promisified MediaElement.play().

If supported, the logging box will say:

Attempting to play automatically...
The play() Promise fulfilled! Rock on!

Otherwise, it will only print the first line of those two.

How should I universally support MediaElement.play()?

Here are the best practices recommended by Jeff Posnick in a Google Developer blog post. Similar code is recommended in a WebKit blog post and by BitMovin, and they all derive from the original issue filing.

var playPromise = document.querySelector('video').play();

// In browsers that don’t yet support this functionality,
// playPromise won’t be defined.
if (playPromise !== undefined) {
  playPromise.then(function() {
    // Automatic playback started!
  }).catch(function(error) {
    // Automatic playback failed.
    // Show a UI element to let the user manually start playback.
  });
}

Basically: store the result of invoking MediaElement.play(), and only act on it as a Promise chain if the result turns out to be non-undefined (eg. of type object).

Jamie Birch
  • 5,839
  • 1
  • 46
  • 60
1

I am not sure if this is a bug or not, but you need to set an src to your video before being able to fulfill or reject the play Promise.

Setting it to an empty string "" will throw an "no supported source was found" error, but since in your original code you thrown unrecognized errors, the then never fired. So you could either rely on this "not found error" to tell that the autoplay works, or don't throw the error to go to the then.

let autoPlayAllowed = true;
let v = document.createElement('video');
v.src = ""; // we need this

const promise = v.play();

if (promise instanceof Promise) {
  promise.catch(function(error) {
    console.log(error.message);
    // Check if it is the right error
    if (error.name === 'NotAllowedError') {
      autoPlayAllowed = false;
    } else {
      // Don't throw the error so that we get to the then
      // or throw it but set the autoPlayAllowed to true in here
    }
  }).then(function() {
    if (autoPlayAllowed) {
      // Autoplay is allowed - continue with initialization
      console.log('autoplay allowed')
    } else {
      // Autoplay is not allowed - wait for the user to trigger the play button manually
      console.log('autoplay NOT allowed')
    }
  });

} else {
  // Unknown if allowed
  // Note: you could fallback to simple event listeners in this case
  console.log('autoplay unknown')
}

Note that only WHATWG specs have included the promises in MediaElement.play(), W3C didn't. So you may want to fallback to simple event listeners, at least for the time being.

Kaiido
  • 123,334
  • 13
  • 219
  • 285