-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: manual autoplay #5209
feat: manual autoplay #5209
Conversation
src/js/player.js
Outdated
// - truthy value: will just call play | ||
// - any: Will call play and if the promise fails, it will try to mute | ||
// and then play | ||
if (this.options_.manualAutoplay) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think that maybe autoplay
is best and have these options:
muted
- will mute the video and then playmanual
- will call play manuallyany
- (or maybe another name?) try to play unmuted and then mute if it fails- any other value do current behavior
then the autoplay getter and setter can also handle this whenever it gets called and the autoplay getter can know whether this option was set and which option.
then this line will just turn to something like this.autoplay(this.options_.autoplay)
or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize that this overloads the values for the autoplay
option, but it really is the best name and we can always deprecate the old values and go forward with just the new stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, we could theoretically, have the setter return the play promise in that way if called later on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK we are going to go this route, let me re-work this pr
Late to the comment party but I feel like the 'new autoplay' should have states/settings that correspond to the browser options in those that allow users to choose autoplay options. Something like:
I'm not sure about
And any other value defaults to current behavior... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good. Could probably use some JSDoc and documentation updates, though.
@marguinbc Also, we can't return the play promise with these because it happens in a |
I suppose I see
Which implies none/default = current behavior and would be dependent on whether the Fair point on the promise... should have looked a the code. |
4440097
to
a99a1de
Compare
docs/guides/faq.md
Outdated
Do to recent changes in `autoplay` behavior we no longer recommend using the `autoplay` attribute | ||
on the video element. It's still supported by video.js but many browsers, including Chrome, are changing their | ||
`autoplay` attribute behavior. | ||
For more info on that see: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to link to our blogpost :)
docs/guides/options.md
Outdated
> **Note:** As of iOS 10, Apple offers `autoplay` support in Safari. For details, refer to ["New <video> Policies for iOS"][ios-10-updates]. | ||
#### More info on autoplay support and changes: | ||
* As of iOS 10, Apple offers `autoplay` support in Safari. For details, refer to ["New <video> Policies for iOS"][ios-10-updates]. | ||
* The autoplay policy in Chrome is changing: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blogpost again
@@ -379,6 +379,13 @@ class Player extends Component { | |||
tag.controls = false; | |||
tag.removeAttribute('controls'); | |||
|
|||
// the attribute overrides the option | |||
if (tag.hasAttribute('autoplay')) { | |||
this.options_.autoplay = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is so that the following case is covered?
<video autoplay data-setup='{"autoplay": "play"}'>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, basically we would autoplay
: true
here and not autoplay
: play
src/js/player.js
Outdated
// Grab tech-specific options from player options and add source and parent element to use. | ||
const techOptions = { | ||
source, | ||
'nativeControlsForTouch': this.options_.nativeControlsForTouch, | ||
'playerId': this.id(), | ||
'techId': `${this.id()}_${titleTechName}_api`, | ||
'autoplay': this.options_.autoplay, | ||
autoplay, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should move this up next to source
, so, the object is consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
src/js/player.js
Outdated
* @param {boolean|string} [value] | ||
* - true: autoplay using the browser behavior | ||
* - false: do not autoplay | ||
* - See `manualAutoplay_` for valid string values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
best to enumerate the options here instead of in manualAutoplay_
src/js/player.js
Outdated
// a setAutoplay call. This option will be passed | ||
// in the constructor for the tech | ||
if (this.tech_) { | ||
this.techCall_('setAutoplay', techAutoplay); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we only want to do this if autoplay is boolean otherwise, it will add the autoplay attribute in the html5 tech which we don't want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I did this because in the case where autoplay is currently true
on the tech, we want to set it to false
, maybe that doesn't remove the attribute like I think that it should, I will test this out as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the attribute will be removed by setting the property to false. I think we do want this.
src/js/player.js
Outdated
this.options_.autoplay = value; | ||
// any other value sets autoplay to true | ||
} else { | ||
this.options_.autoplay = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is probably fine, alternatively, we can cast the value into a boolean and use that instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me see what the video element does, I think any value on autoplay is treated as true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK so it seems that the browsers cast it to a bool, so I will change that here.
src/js/player.js
Outdated
this.options_.autoplay = true; | ||
} | ||
|
||
techAutoplay = techAutoplay || value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be techAutoplay || this.options_.autoplay
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah
test/unit/player.test.js
Outdated
@@ -1904,3 +1904,253 @@ QUnit.test('disposing a tech that dit NOT set a poster, should keep the poster', | |||
|
|||
player.dispose(); | |||
}); | |||
|
|||
QUnit.module('Player autoplay', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe better to move this into a separate file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I will do that.
let techAutoplay; | ||
|
||
// if the value is a valid string set it to that | ||
if (typeof value === 'string' && (/(any|play|muted)/).test(value)) { | ||
this.options_.autoplay = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we change how we cache this value? i.e., switch to using this.cache_.autoplay
instead and keep the options autoplay what it was initially?
We can also keep updating the option but use the cache until a breaking change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that autoplay is actually an option, and every time we have to switch techs we have to pass it to the tech as such. I see the cache as more of a cache of the techs value on our end so maybe we should cache something like techAutoplay_
?. I think autoplay as a whole is more of an option, but I do see the value in being able to see what the tech has autoplay set to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically, I think we should keep this.options_
set to whatever is the initial set of options passed it and use other places to cache transient or instance properties. However, I don't think this is necessarily something for this PR.
4c28774
to
c8d2dba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks solid to me. Some documentation cleanup, though.
docs/guides/faq.md
Outdated
```html | ||
<video autoplay controls class="video-js"> | ||
``` | ||
Do to recent changes in `autoplay` behavior we no longer recommend using the `autoplay` attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do
->Due
- I don't think the first autoplay should use backticks. It's not referencing the attribute or any code, but the behavior.
docs/guides/faq.md
Outdated
<video autoplay controls class="video-js"> | ||
``` | ||
Do to recent changes in `autoplay` behavior we no longer recommend using the `autoplay` attribute | ||
on the video element. It's still supported by video.js but many browsers, including Chrome, are changing their |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
video
should probably have backticks as it refers to the element.video.js
should be capitalized.but
should be preceded by a comma.
docs/guides/faq.md
Outdated
|
||
player.autoplay(true); | ||
``` | ||
Instead we recommend using the `autoplay` option rather than the `autoplay` attribute. For more information on using that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The period at the end should be a comma.
docs/guides/faq.md
Outdated
player.autoplay(true); | ||
``` | ||
Instead we recommend using the `autoplay` option rather than the `autoplay` attribute. For more information on using that. | ||
See the [autoplay option][autoplay-option] in the videojs options guide. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See
should besee
videojs
should beVideo.js
docs/guides/faq.md
Outdated
```html | ||
<video muted autoplay playsinline> | ||
``` | ||
We do not recommend doing this manually using attributes on the video element. Instead you should pass the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
video
should probably have backticks as it refers to the element.Instead
should be followed by a comma.
docs/guides/faq.md
Outdated
|
||
Will make an inline, muted, autoplaying video on an iPhone with iOS10. | ||
> NOTE: At this point the autoplay attribute and option are NOT a guarantee that your video will autoplay. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point
should be followed by a comma.
docs/guides/options.md
Outdated
@@ -53,11 +53,34 @@ Each of these options is also available as a [standard `<video>` element attribu | |||
|
|||
### `autoplay` | |||
|
|||
> Type: `boolean` | |||
> Type: `boolean|string` | |||
> NOTE: At this point the autoplay attribute and option are NOT a guarantee that your video will autoplay. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point
should be followed by a comma.
docs/guides/options.md
Outdated
> NOTE2: If there is an attribute on the media element the option will be ignored. | ||
> NOTE3: You cannot pass a string value in the attribute, you must pass it in the videojs options | ||
|
||
Instead of using the `autoplay` attribute you should pass an `autoplay` option to videojs. The following values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think videojs
should have backticks here as it refers to the function rather than the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Knowing whether autoplay succeeded and whether muting was necessary will be important for some use cases (analytics, UX changes). Should there be an event or a property with this info for convenience?
docs/guides/faq.md
Outdated
var player = videojs('my-video', { | ||
autoplay: true | ||
}); | ||
For more information on the autoplay changes see our blog post: https://blog.videojs.com/autoplay-best-practices-with-video-js/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this section flows better with this line at the end.
docs/guides/faq.md
Outdated
player.autoplay(true); | ||
``` | ||
Instead we recommend using the `autoplay` option rather than the `autoplay` attribute, for more information on using that. | ||
see the [autoplay option][autoplay-option] in the Video.js options guide. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no . after that
src/js/player.js
Outdated
this.techCall_('setAutoplay', value); | ||
// getter usage | ||
if (value === undefined) { | ||
return this.options_.autoplay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns undefined
rather than false
if not configured.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call
hmmm yeah it could be a good idea to have something like |
On thinking about this some more, I think we may want to do this for |
I think that idea has merit for sure. Maybe a property as well, e.g. |
let techAutoplay; | ||
|
||
// if the value is a valid string set it to that | ||
if (typeof value === 'string' && (/(any|play|muted)/).test(value)) { | ||
this.options_.autoplay = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically, I think we should keep this.options_
set to whatever is the initial set of options passed it and use other places to cache transient or instance properties. However, I don't think this is necessarily something for this PR.
let techAutoplay; | ||
|
||
// if the value is a valid string set it to that | ||
if (typeof value === 'string' && (/(any|play|muted)/).test(value)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we call this.manualAutoplay_
in here? Use case would be something like:
var player = videojs('vid');
player.autoplay('any');
I'd expect the player to autoplay in this case. This would also match current behavior (in browsers that support autoplay). Main risk is that we'd want to be able to do something like the following too
var player = videojs('vid', { autoplay: 'any' );
player.autoplay(false);
and have it not autoplay.
Also, should we have a string state "off" and deprecate boolean values for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just do this in a followup PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should player.autoplay('any')
also return a promise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably just want events rather than a promise
Autoplay events probably make sense. We probably can have just two events I'm hesitant to map the play promise into events unless we have an actual use-case. Most of what we talked about it has been theoretical so far. This is especially since we have moved towards recommending people call |
See #5227 |
3565859
to
e74c1fd
Compare
src/js/player.js
Outdated
// if we are muted or not set to any here there is nothing we can do | ||
if (type !== 'any' || this.muted()) { | ||
this.trigger('autoplay-failure'); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we've muted the player and the autoplay still fails, shouldn't we unmute again so a subsequent play triggered by a human has sound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds reasonable to me.
src/js/player.js
Outdated
} | ||
|
||
playPromise.then(() => { | ||
this.trigger('autoplay-success'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we add the autoplay
value to the event or are we expecting users to call player.autoplay()
to get the current value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can add the autoplay value to the event
src/js/player.js
Outdated
// if we are muted or not set to any here there is nothing we can do | ||
if (type !== 'any' || this.muted()) { | ||
this.trigger('autoplay-failure'); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds reasonable to me.
This PR extends the `autoplay` to the player with a few options that should hopefully make working with browsers that disable unmuted autoplay by default easier. The current boolean option will match current behavior and any unknown option will be treated as it does now. The new options are the string values `muted`, `play`, and `any`. - `muted` will mute the element and call `play()` on `loadstart`, - `play` will call `play()` on `loadstart()`, this is similar to the `autoplay` attribute - `any` will call `play()` on `loadstart()` but if it fails it will try muting the video and calling `play()` again.
Due to some of the policy changes around autoplay we are going to add a new option. The name of the option and the parameters that the option can be can change in this pr, but I think the general functionality is going to remain the same. Basically you have three paths
play
and if that doesn't work do nothing, mostly the same as using theautoplay
attribute right now.play
, this will work in most casesplay
and if that doesnt work try to mute and then autoplay, which we know will work.Questions:
autoplay
attribute and accept a string?Coming Soon