1
0
mirror of https://github.com/videojs/video.js.git synced 2025-09-16 09:26:56 +02:00

feat: Allow techs to change poster if player option techCanOverridePoster is set (#4921)

The option for the player techCanOverridePoster is introduced in this commit. It allows techs to update the post whenever they like. isPosterFromTech_ is introduced as a private player field in order to track when a poster was set by a tech. This allows us to clear the poster whenever the tech is disposed of by the player.

Additionally, attempting to set the same poster more than once will have no effect / no changes will be made, since the poster is the same. This was done in order to stop triggering multiple posterchange events when calling player.poster(aPoster) with techCanOverridePoster set to true.

When a tech is disposed and a poster was set by it, unset the poster.

Pass a `canOverridePoster` option to techs to know whether techCanOverridePoster was set.

Fixes #4910.
This commit is contained in:
Michael Vogel
2018-03-07 20:31:50 +01:00
committed by Gary Katsevman
parent df96a74f6b
commit 8706941573
5 changed files with 151 additions and 5 deletions

View File

@@ -30,6 +30,7 @@
* [plugins](#plugins)
* [sourceOrder](#sourceorder)
* [sources](#sources)
* [techCanOverridePoster](#techCanOverridePoster)
* [techOrder](#techorder)
* [vtt.js](#vttjs)
* [Component Options](#component-options)
@@ -254,6 +255,15 @@ Using `<source>` elements will have the same effect:
</video>
```
### `techCanOverridePoster`
> Type: `boolean`
Gives the possibility to techs to override the player's poster
and integrate into the player's poster life-cycle.
This can be useful when multiple techs are used and each has to set their own poster
any time a new source is played.
### `techOrder`
> Type: `Array`, Default: `['html5']`

View File

@@ -57,6 +57,21 @@ videojs("videoID", {
});
```
### Posters
By default, techs will have to handle their own posters and are somewhat locked out of the player's poster lifecycle.
However, when the player is initialized with the `techCanOverridePoster` option
it will be possible for techs to integrate into that lifecycle and the player's `PosterImage` component to be used.
Techs can check if they have this capability by checking the `canOverridePoster` boolean in their options.
**`techCanOverridePoster` requirements**
- `poster()` which returns the tech's current poster url
- `setPoster()` which updates the tech's poster url and triggers a `posterchange` event
which the player will handle
## Technology Ordering
When Video.js is given an array of sources, which to use is determined by finding the first supported source / tech combination. Each tech will be queried in the order specified in `techOrder` whether it can play the first source. The first match wins. If no tech can play the first source, then the next will be tested. It's important to set the `type` of each source correctly for this test to be accurate.

View File

@@ -317,6 +317,9 @@ class Player extends Component {
// Run base component initializing with new options
super(null, options, ready);
// Tracks when a tech changes the poster
this.isPosterFromTech_ = false;
// Turn off API access because we're loading a new tech that might load asynchronously
this.isReady_ = false;
@@ -513,6 +516,8 @@ class Player extends Component {
if (this.tech_) {
this.tech_.dispose();
this.isPosterFromTech_ = false;
this.poster_ = '';
}
if (this.playerElIngest_) {
@@ -924,7 +929,8 @@ class Player extends Component {
'poster': this.poster(),
'language': this.language(),
'playerElIngest': this.playerElIngest_ || false,
'vtt.js': this.options_['vtt.js']
'vtt.js': this.options_['vtt.js'],
'canOverridePoster': !!this.options_.techCanOverridePoster
};
TRACK_TYPES.names.forEach((name) => {
@@ -1020,6 +1026,13 @@ class Player extends Component {
this.tech_.dispose();
this.tech_ = false;
if (this.isPosterFromTech_) {
this.poster_ = '';
this.trigger('posterchange');
}
this.isPosterFromTech_ = false;
}
/**
@@ -2692,12 +2705,18 @@ class Player extends Component {
src = '';
}
if (src === this.poster_) {
return;
}
// update the internal poster variable
this.poster_ = src;
// update the tech's poster
this.techCall_('setPoster', src);
this.isPosterFromTech_ = false;
// alert components that the poster has been set
/**
* This event fires when the poster image is changed on the player.
@@ -2721,11 +2740,16 @@ class Player extends Component {
* @private
*/
handleTechPosterChange_() {
if (!this.poster_ && this.tech_ && this.tech_.poster) {
this.poster_ = this.tech_.poster() || '';
if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
const newPoster = this.tech_.poster() || '';
// Let components know the poster has changed
this.trigger('posterchange');
if (newPoster !== this.poster_) {
this.poster_ = newPoster;
this.isPosterFromTech_ = true;
// Let components know the poster has changed
this.trigger('posterchange');
}
}
}

View File

@@ -1709,3 +1709,99 @@ QUnit.test('player.duration() sets the value of player.cache_.duration', functio
player.duration(200);
assert.equal(player.duration(), 200, 'duration() set and get integer duration value');
});
QUnit.test('setPoster in tech with `techCanOverridePoster` in player should override poster', function(assert) {
const player = TestHelpers.makePlayer({
techCanOverridePoster: true
});
const posterchangeSpy = sinon.spy();
const firstPosterUrl = 'https://wherever.test/test.jpg';
const techPosterUrl = 'https://somewhere.text/my/image.png';
assert.equal(player.options_.techCanOverridePoster, true, 'make sure player option was passed correctly');
assert.equal(player.tech_.options_.canOverridePoster, true, 'make sure tech option was passed correctly');
player.on('posterchange', posterchangeSpy);
player.poster('');
assert.ok(posterchangeSpy.notCalled, 'posterchangeSpy not called when no change of poster');
assert.equal(player.isPosterFromTech_, false, "ensure tech didn't change poster after empty call from player");
player.poster(firstPosterUrl);
assert.ok(posterchangeSpy.calledOnce, 'posterchangeSpy only called once on update');
assert.equal(player.poster(), firstPosterUrl, "ensure tech didn't change poster after setting from player");
assert.equal(player.isPosterFromTech_, false, "ensure player didn't mark poster as changed by the tech");
posterchangeSpy.reset();
player.tech_.setPoster(techPosterUrl);
assert.ok(posterchangeSpy.calledOnce, "posterchangeSpy should've been called");
assert.equal(player.isPosterFromTech_, true, 'ensure player marked poster as set by tech after the fact');
player.dispose();
});
QUnit.test('setPoster in tech WITHOUT `techCanOverridePoster` in player should NOT override poster', function(assert) {
const player = TestHelpers.makePlayer();
const posterchangeSpy = sinon.spy();
const firstPosterUrl = 'https://wherever.test/test.jpg';
const techPosterUrl = 'https://somewhere.test/my/image.png';
assert.equal(player.options_.techCanOverridePoster, undefined, "ensure player option wasn't unwittingly set");
assert.equal(player.tech_.options_.canOverridePoster, false, "ensure tech option wasn't unwittinyly set");
player.on('posterchange', posterchangeSpy);
player.poster(firstPosterUrl);
assert.ok(posterchangeSpy.calledOnce, 'posterchangeSpy only called once on update');
assert.equal(player.poster(), firstPosterUrl, "ensure tech didn't change poster after setting from player");
assert.equal(player.isPosterFromTech_, false, "ensure player didn't mark poster as changed by the tech");
posterchangeSpy.reset();
player.tech_.setPoster(techPosterUrl);
assert.ok(posterchangeSpy.notCalled, "posterchangeSpy shouldn't have been called");
assert.equal(player.isPosterFromTech_, false, "ensure tech didn't change poster because player option was false");
player.dispose();
});
QUnit.test('disposing a tech that set a poster, should unset the poster', function(assert) {
const player = TestHelpers.makePlayer({
techCanOverridePoster: true
});
const techPosterUrl = 'https://somewhere.test/my/image.png';
assert.equal(player.options_.techCanOverridePoster, true, 'make sure player option was passed correctly');
assert.equal(player.tech_.options_.canOverridePoster, true, 'make sure tech option was passed correctly');
player.tech_.setPoster(techPosterUrl);
assert.equal(player.poster(), techPosterUrl, 'player poster should equal tech poster');
assert.equal(player.isPosterFromTech_, true, 'setting the poster with the tech should be remembered in the player');
player.unloadTech_();
assert.equal(player.poster(), '', 'ensure poster set by poster is unset after tech disposal');
player.dispose();
});
QUnit.test('disposing a tech that dit NOT set a poster, should keep the poster', function(assert) {
const player = TestHelpers.makePlayer({
techCanOverridePoster: true
});
const posterUrl = 'https://myposter.test/lol.jpg';
assert.equal(player.options_.techCanOverridePoster, true, 'make sure player option was passed correctly');
assert.equal(player.tech_.options_.canOverridePoster, true, 'make sure tech option was passed correctly');
player.poster(posterUrl);
assert.equal(player.poster(), posterUrl, 'player poster should NOT have changed');
assert.equal(player.isPosterFromTech_, false, 'player should mark poster as set by itself');
player.unloadTech_();
assert.equal(player.poster(), posterUrl, 'player poster should stay the same after unloading / dispoing tech');
player.dispose();
});

View File

@@ -28,6 +28,7 @@ class TechFaker extends Tech {
}
setPoster(val) {
this.el().poster = val;
this.trigger('posterchange');
}
setControls(val) {}