-
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: add mediator middleware type for play() #4868
Conversation
@@ -1632,6 +1632,9 @@ class Player extends Component { | |||
this.ready(function() { | |||
if (method in middleware.allowedSetters) { | |||
return middleware.set(this.middleware_, this.tech_, method, arg); | |||
|
|||
} else if (method in middleware.allowedMediators) { |
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.
player.play()
seems to be done via techGet
for the html5 tech, but it's conceivable that a future mediator will use techCall
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 we should just make a techCallAndGet
for stuff like 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.
That might make sense; I don't feel strongly about it one way or another.
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's get everything else ready and then come back to this.
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.
eh, I think we can just leave it like this.
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, doesn't feel like there's a pressing need to change this yet
src/js/tech/middleware.js
Outdated
@@ -2,6 +2,8 @@ import { assign } from '../utils/obj.js'; | |||
|
|||
const middlewares = {}; | |||
|
|||
export const TERMINATOR = 'TERMINATOR'; |
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 make this be an object instance rather than a string since objects only equal themselves. Then the string will also be a valid 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.
what do you mean by "the string will also be a valid 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.
Did you mean change this to export const TERMINATOR = () => { return 'TERMINATOR'; }
and then here:
change it to be videojs.TERMINATOR = TERMINATOR()
?
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.
nope, literally just TERMINATOR = {}
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.
Oh, you meant "the string will also be a valid value" to mean that an allowed method could return the string 'TERMINATOR'! 👍 will do
6684706
to
dde95c6
Compare
play()
to middlewareplay()
to middleware
src/js/video.js
Outdated
@@ -749,5 +749,7 @@ videojs.dom = Dom; | |||
*/ | |||
videojs.url = Url; | |||
|
|||
videojs.TERMINATOR = TERMINATOR; |
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 have access to Object.defineProperty
we should probably use it here to prevent overwriting this value. Others may think that's overkill, though.
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 reasonable, I'll do that
src/js/video.js
Outdated
@@ -749,7 +749,9 @@ videojs.dom = Dom; | |||
*/ | |||
videojs.url = Url; | |||
|
|||
videojs.TERMINATOR = TERMINATOR; | |||
Object.defineProperty(videojs, 'TERMINATOR', { |
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.
@misteroneill does this seem right?
src/js/video.js
Outdated
@@ -749,5 +749,9 @@ videojs.dom = Dom; | |||
*/ | |||
videojs.url = Url; | |||
|
|||
Object.defineProperty(videojs, 'TERMINATOR', { |
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 will break in IE8 :)
Also, I think it's reasonable to make it enumerable and it might be nice to make writeable false be explicit.
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.
Is there an example somewhere else in the project of how to do this for IE8 as well? I tried to find one earlier and came up empty.
🆗 explicit writeable: false
and enumerable: true
sound good to me
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.
something like
if (Object.defineProperty && !browser.IS_IE8) {
Object.defineProperty(...);
} else {
videojs.TERMINATOR = TERMINATOR;
}
src/js/video.js
Outdated
@@ -749,5 +749,9 @@ videojs.dom = Dom; | |||
*/ | |||
videojs.url = Url; | |||
|
|||
Object.defineProperty(videojs, 'TERMINATOR', { |
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 directly on videojs
? Maybe namespaced videojs.middleware.TERMINATOR
?
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 like the idea of it being namespaced to videojs.middleware.TERMINATOR
src/js/tech/middleware.js
Outdated
}; | ||
|
||
export const allowedSetters = { | ||
setCurrentTime: 1 | ||
}; | ||
|
||
export const allowedMediators = { | ||
play: 1 |
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.
are we adding pause
here 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.
Oh, I should. Thanks for catching that
src/js/tech/middleware.js
Outdated
for (let i = mws.length - 1; i >= 0; i--) { | ||
const mw = mws[i]; | ||
|
||
mw[method](value, terminated); |
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.
can we swap the argument order so it's similar to a node-style callback?
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 idea, we can do that.
@@ -1632,6 +1632,9 @@ class Player extends Component { | |||
this.ready(function() { | |||
if (method in middleware.allowedSetters) { | |||
return middleware.set(this.middleware_, this.tech_, method, arg); | |||
|
|||
} else if (method in middleware.allowedMediators) { |
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's get everything else ready and then come back to this.
src/js/big-play-button.js
Outdated
@@ -59,7 +59,7 @@ class BigPlayButton extends Button { | |||
|
|||
const playFocus = () => playToggle.focus(); | |||
|
|||
if (isPromise(playPromise)) { | |||
if (playPromise && isPromise(playPromise)) { |
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.
isPromise
should be doing a null check as well, no, this shouldn't be strictly necessary.
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 did notice that isPromise
was checking for undefined
specifically. I thought that was for a specific reason, but maybe I was wrong?
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 play()
is called and no promise was returned, we end up with undefined
. Though, we'll never go into this if statement if playPromise
doesn't duck-type into a promise.
I'm ok with leaving it if you want, but it shouldn't be necessary.
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.
Ah, while I was testing, if the play was terminated then I saw an error coming from this if
statement. Perhaps the right way is to return something other than null
in the cases where a middleware terminates.
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.
Ah, I see. Maybe we should just loosen the check in isPromise
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'm fine with going either way - I'll check for null
in isPromise for now
src/js/video.js
Outdated
@@ -751,8 +751,8 @@ videojs.url = Url; | |||
|
|||
// Object.defineProperty is not available in IE8 | |||
if (!browser.IS_IE8 && Object.defineProperty) { | |||
Object.defineProperty(videojs, 'middleware.TERMINATOR', { | |||
value: TERMINATOR, | |||
Object.defineProperty(videojs, 'middleware', { |
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.
you'd want defineProperty each step because now you could overwrite videojs.middleware.TERMINATOR = false
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 point
2ef76c1
to
27c91c1
Compare
docs/guides/middleware.md
Outdated
@@ -0,0 +1,116 @@ | |||
# Middleware |
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 will have to be rebased once the current middleware guide is merged
9abacdd
to
b1629ee
Compare
Looking at this version of the middleware guide (https://deploy-preview-4868--videojs-docs.netlify.com/tutorial-middleware.html), I realized we forgot to document the "special" |
sandbox/middleware-play.example
Outdated
@@ -0,0 +1,184 @@ | |||
<!DOCTYPE html> |
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 file should be middleware-play.html.example
sandbox/middleware-play.example
Outdated
margin: 2em 1em; | ||
} | ||
|
||
.vjs-progress-control.vjs-control .vjs-play-progress.vjs-slider-bar.terminated { |
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.
why not look for the terminated
class on the player 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.
What do you mean? I think I needed to be specific to get the right component
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.
.terminated .vjs-process-control .vjs-play-progress
Then below (line 44), you can just do player.addClass('terminated')
instead of going through the entire child heirarchy to get the proper element. Or something like that.
sandbox/middleware-play.example
Outdated
console.log('Middleware 1: play has been cancelled prior to this middleware'); | ||
} | ||
|
||
return 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.
is this required?
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, I can remove it
sandbox/middleware-play.example
Outdated
var vid = document.getElementById("vid1"); | ||
var player = videojs(vid, { | ||
controlBar: { | ||
children: [ |
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.
is there a reason for these?
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.
heh, copypasta.
sandbox/middleware-play.example
Outdated
}, | ||
// Mediating the results back to the player | ||
play: function(cancelled, value) { | ||
console.log('Middleware 1: play got from tech. What is the value passed?', 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.
would be nice to have it .then
and .catch
in here.
src/js/video.js
Outdated
@@ -749,5 +755,30 @@ videojs.dom = Dom; | |||
*/ | |||
videojs.url = Url; | |||
|
|||
/** |
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.
can we put this block immediately after videojs.use
so middleware related stuff are grouped together?
docs/guides/middleware.md
Outdated
* [Using Middleware](#using-middleware) | ||
* [setSource](#setsource) | ||
|
||
## Understanding Middleware | ||
|
||
Middleware are functions that return an object with methods matching those on the `Tech`. There are currently a limited set of allowed methods that will be understood by middleware. These are: `buffered`, `currentTime`, `setCurrentTime`, `duration`, `seekable` and `played`. | ||
Middleware are functions that return an object with methods matching those on the `Tech`. There are currently a limited set of allowed methods that will be understood by middleware. These are: `buffered`, `currentTime`, `setCurrentTime`, `duration`, `seekable`, `played`, `play` and `pause`. |
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 paused
?
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 catch
docs/guides/middleware.md
Outdated
|
||
These allowed methods are split into two categories: `getters` and `setters`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. | ||
These allowed methods are split into three categories: `getters`, `setters` and `mediators`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. |
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're missing a short description of mediators
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.
Think it's enough to link mediators to the next section "Termination and Mediators"?
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.
Probably. Would be nice to get a one-sentence description, though, if we can.
docs/guides/middleware.md
Outdated
@@ -56,6 +82,24 @@ var myMiddleware = function(player) { | |||
videojs.use('*', myMiddleware); | |||
``` | |||
|
|||
Mediator methods can terminate, by doing the following: |
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.
would be good to make this a subsection so it can be linked to from above.
docs/guides/middleware.md
Outdated
|
||
Mediators are the third category of allowed methods. These are methods that not only change the state of the Tech, but also return some value back to the Player. Currently, these are `play` and `pause`. | ||
|
||
Mediators make a round trip: starting at the `Player`, mediating to the `Tech` and returning the result to the `Player` again. A `call{method}` method must be supplied by the middleware which is used when mediating to the `Tech`. For example: `callPlay`. On the way back to the `Player`, the `{method}` will be called instead, with 2 arguments: `terminated`, a Boolean indicating whether a middleware terminated during the mediation to the tech portion, and `value`, which is the value returned from the `Tech`. |
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 should have simple examples here to better illustrate the text.
@@ -1632,6 +1632,9 @@ class Player extends Component { | |||
this.ready(function() { | |||
if (method in middleware.allowedSetters) { | |||
return middleware.set(this.middleware_, this.tech_, method, arg); | |||
|
|||
} else if (method in middleware.allowedMediators) { |
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.
eh, I think we can just leave it like this.
bbe61e1
to
36b1835
Compare
@gkatsev I think I've now addressed all your comments. I agree that the corrections to the middleware guide can be done in a separate PR. And I removed the package-lock so we should be good now 😄 |
src/js/tech/middleware.js
Outdated
/** | ||
* Calls a getter on the tech first, through each middleware | ||
* from right to left to the player. | ||
**/ |
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.
nit: only one *
for closing :)
docs/guides/middleware.md
Outdated
|
||
These allowed methods are split into two categories: `getters` and `setters`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. | ||
These allowed methods are split into three categories: `getters`, `setters` and `mediators`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. |
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.
Probably. Would be nice to get a one-sentence description, though, if we can.
src/js/video.js
Outdated
enumerable: true | ||
}); | ||
} else { | ||
videojs.middleware.TERMINATOR = TERMINATOR; |
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 will fail because videojs.middleware
isn't an object.
Should be videojs.middleware = {TERMINATOR}
Oh, the commit message should probably be more like |
play()
to middleware
docs/guides/middleware.md
Outdated
|
||
These allowed methods are split into two categories: `getters` and `setters`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. | ||
These allowed methods are split into three categories: `getters`, `setters` and `mediators`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. Mediators are called on the `Player` first, run through middleware from left to right, then called on the `Tech` and the result is returned to the `Player` unchanged, while calling the middleware from right to left. For more information, check out the [mediator section](#termination-and-mediators). |
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.
@gkatsev Added a sentence for Mediators, but it's more confusing in my opinion than reading the full section
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 is ok. Maybe @videojs/core-committers has any other thoughts?
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 paragraph should be split up. I propose breaking it into three distinct sections. Also, made some grammatical improvements.
These allowed methods are split into three categories: getters, setters, and mediators.
Middleware Setters
Setters will be called on the Player
first and run through middleware (from left to right) before calling the method, with its arguments, on the Tech
.
Middleware Getters
Getters are called on the Tech
first and are run though middleware (from right to left) before returning the result to the Player
.
Middleware Mediators
Mediators are called on the Player
first, run through middleware (from left to right), then called on the Tech
. The result is returned to the Player
unchanged, while calling the middleware from right to left. For more information on mediators, check out the mediator section.
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.
LGTM!
docs/guides/middleware.md
Outdated
|
||
These allowed methods are split into two categories: `getters` and `setters`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. | ||
These allowed methods are split into three categories: `getters`, `setters` and `mediators`. Setters will be called on the `Player` first and run through middleware(from left to right) before calling the method, with its arguments, on the `Tech`. Getters are called on the `Tech` first and are run though middleware(from right to left) before returning the result to the `Player`. Mediators are called on the `Player` first, run through middleware from left to right, then called on the `Tech` and the result is returned to the `Player` unchanged, while calling the middleware from right to left. For more information, check out the [mediator section](#termination-and-mediators). |
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 paragraph should be split up. I propose breaking it into three distinct sections. Also, made some grammatical improvements.
These allowed methods are split into three categories: getters, setters, and mediators.
Middleware Setters
Setters will be called on the Player
first and run through middleware (from left to right) before calling the method, with its arguments, on the Tech
.
Middleware Getters
Getters are called on the Tech
first and are run though middleware (from right to left) before returning the result to the Player
.
Middleware Mediators
Mediators are called on the Player
first, run through middleware (from left to right), then called on the Tech
. The result is returned to the Player
unchanged, while calling the middleware from right to left. For more information on mediators, check out the mediator section.
@@ -9,7 +9,7 @@ | |||
* Whether or not the object is `Promise`-like. | |||
*/ | |||
export function isPromise(value) { | |||
return value !== undefined && typeof value.then === 'function'; | |||
return value !== undefined && value !== null && typeof value.then === 'function'; |
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.
Mabye we should just do return Boolean(value) && typeof value.then === 'function';
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.
it's nice to be explicit here too 🤔
if (!browser.IS_IE8 && Object.defineProperty) { | ||
Object.defineProperty(videojs, 'middleware', { | ||
value: {}, | ||
writeable: false, |
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 believe writable: false
is the default, so this shouldn't be necessary.
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 talked about this elsewhere, but since having writeable: false
is very explicit, I plan to keep it 😄
Description
This will allow middleware to interact with calls to play() from the tech. This will require a method of indicating to middleware previously run that a middleware down the chain has terminated or stopped execution.
Specific Changes proposed
mediator
method that runs middleware from the player to the tech and a second time back up to the player. This category was created becauseplay
is both a setter(changes the playback state) and a getter(gets a native play promise if available). This also has the ability to tell whether a middleware has terminated before reaching the tech.middleware.TERMINATOR
sentinel value that is available on thevideojs
objectplay
to the allowedMediatorspaused
to the allowedGettersChecklist