Skip to content

Commit eeff79c

Browse files
refactor: Unify audioOnly mode and audioPoster mode (videojs#7678)
Co-authored-by: Alex Barstow <alexander.barstow@gmail.com>
1 parent b275a15 commit eeff79c

File tree

4 files changed

+213
-33
lines changed

4 files changed

+213
-33
lines changed

src/js/control-bar/picture-in-picture-toggle.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ class PictureInPictureToggle extends Button {
2929
this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], (e) => this.handlePictureInPictureChange(e));
3030
this.on(player, ['disablepictureinpicturechanged', 'loadedmetadata'], (e) => this.handlePictureInPictureEnabledChange(e));
3131

32+
this.on(player, ['loadedmetadata', 'audioonlymodechange', 'audiopostermodechange'], () => {
33+
// This audio detection will not detect HLS or DASH audio-only streams because there was no reliable way to detect them at the time
34+
const isSourceAudio = player.currentType().substring(0, 5) === 'audio';
35+
36+
if (isSourceAudio || player.audioPosterMode() || player.audioOnlyMode()) {
37+
if (player.isInPictureInPicture()) {
38+
player.exitPictureInPicture();
39+
}
40+
this.hide();
41+
} else {
42+
this.show();
43+
}
44+
45+
});
46+
3247
// TODO: Deactivate button on player emptied event.
3348
this.disable();
3449
}

src/js/player.js

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ class Player extends Component {
398398
// Init state audioOnlyMode_
399399
this.audioOnlyMode_ = false;
400400

401+
// Init state audioPosterMode_
402+
this.audioPosterMode_ = false;
403+
401404
// Init state audioOnlyCache_
402405
this.audioOnlyCache_ = {
403406
playerHeight: null,
@@ -583,7 +586,15 @@ class Player extends Component {
583586

584587
this.breakpoints(this.options_.breakpoints);
585588
this.responsive(this.options_.responsive);
586-
this.audioOnlyMode(this.options_.audioOnlyMode);
589+
590+
// Calling both the audio mode methods after the player is fully
591+
// setup to be able to listen to the events triggered by them
592+
this.on('ready', () => {
593+
// Calling the audioPosterMode method first so that
594+
// the audioOnlyMode can take precedence when both options are set to true
595+
this.audioPosterMode(this.options_.audioPosterMode);
596+
this.audioOnlyMode(this.options_.audioOnlyMode);
597+
});
587598
}
588599

589600
/**
@@ -4303,11 +4314,6 @@ class Player extends Component {
43034314
return !!this.isAudio_;
43044315
}
43054316

4306-
updateAudioOnlyModeState_(value) {
4307-
this.audioOnlyMode_ = value;
4308-
this.trigger('audioonlymodechange');
4309-
}
4310-
43114317
enableAudioOnlyUI_() {
43124318
// Update styling immediately to show the control bar so we can get its height
43134319
this.addClass('vjs-audio-only-mode');
@@ -4334,7 +4340,7 @@ class Player extends Component {
43344340

43354341
// Set the player height the same as the control bar
43364342
this.height(controlBarHeight);
4337-
this.updateAudioOnlyModeState_(true);
4343+
this.trigger('audioonlymodechange');
43384344
}
43394345

43404346
disableAudioOnlyUI_() {
@@ -4345,7 +4351,7 @@ class Player extends Component {
43454351

43464352
// Reset player height
43474353
this.height(this.audioOnlyCache_.playerHeight);
4348-
this.updateAudioOnlyModeState_(false);
4354+
this.trigger('audioonlymodechange');
43494355
}
43504356

43514357
/**
@@ -4366,6 +4372,8 @@ class Player extends Component {
43664372
return this.audioOnlyMode_;
43674373
}
43684374

4375+
this.audioOnlyMode_ = value;
4376+
43694377
const PromiseClass = this.options_.Promise || window.Promise;
43704378

43714379
if (PromiseClass) {
@@ -4382,6 +4390,10 @@ class Player extends Component {
43824390
exitPromises.push(this.exitFullscreen());
43834391
}
43844392

4393+
if (this.audioPosterMode()) {
4394+
exitPromises.push(this.audioPosterMode(false));
4395+
}
4396+
43854397
return PromiseClass.all(exitPromises).then(() => this.enableAudioOnlyUI_());
43864398
}
43874399

@@ -4404,35 +4416,80 @@ class Player extends Component {
44044416
}
44054417
}
44064418

4419+
enablePosterModeUI_() {
4420+
// Hide the video element and show the poster image to enable posterModeUI
4421+
const tech = this.tech_ && this.tech_;
4422+
4423+
tech.hide();
4424+
this.addClass('vjs-audio-poster-mode');
4425+
this.trigger('audiopostermodechange');
4426+
}
4427+
4428+
disablePosterModeUI_() {
4429+
// Show the video element and hide the poster image to disable posterModeUI
4430+
const tech = this.tech_ && this.tech_;
4431+
4432+
tech.show();
4433+
this.removeClass('vjs-audio-poster-mode');
4434+
this.trigger('audiopostermodechange');
4435+
}
4436+
44074437
/**
44084438
* Get the current audioPosterMode state or set audioPosterMode to true or false
44094439
*
44104440
* @param {boolean} [value]
44114441
* The value to set audioPosterMode to.
44124442
*
4413-
* @return {boolean}
4414-
* True if audioPosterMode is on, false otherwise.
4443+
* @return {Promise|boolean}
4444+
* A Promise is returned when setting the state, and a boolean when getting
4445+
* the present state
44154446
*/
44164447
audioPosterMode(value) {
44174448

4418-
if (this.audioPosterMode_ === undefined) {
4419-
this.audioPosterMode_ = this.options_.audioPosterMode;
4420-
}
4421-
44224449
if (typeof value !== 'boolean' || value === this.audioPosterMode_) {
44234450
return this.audioPosterMode_;
44244451
}
44254452

44264453
this.audioPosterMode_ = value;
44274454

4428-
if (this.audioPosterMode_) {
4429-
this.tech_.hide();
4430-
this.addClass('vjs-audio-poster-mode');
4455+
const PromiseClass = this.options_.Promise || window.Promise;
4456+
4457+
if (PromiseClass) {
4458+
4459+
if (value) {
4460+
4461+
if (this.audioOnlyMode()) {
4462+
const audioOnlyModePromise = this.audioOnlyMode(false);
4463+
4464+
return audioOnlyModePromise.then(() => {
4465+
// enable audio poster mode after audio only mode is disabled
4466+
this.enablePosterModeUI_();
4467+
});
4468+
}
4469+
4470+
return PromiseClass.resolve().then(() => {
4471+
// enable audio poster mode
4472+
this.enablePosterModeUI_();
4473+
});
4474+
}
4475+
4476+
return PromiseClass.resolve().then(() => {
4477+
// disable audio poster mode
4478+
this.disablePosterModeUI_();
4479+
});
4480+
}
4481+
4482+
if (value) {
4483+
4484+
if (this.audioOnlyMode()) {
4485+
this.audioOnlyMode(false);
4486+
}
4487+
4488+
this.enablePosterModeUI_();
44314489
return;
44324490
}
4433-
// Show the video element and hide the poster image if audioPosterMode is set to false
4434-
this.tech_.show();
4435-
this.removeClass('vjs-audio-poster-mode');
4491+
4492+
this.disablePosterModeUI_();
44364493
}
44374494

44384495
/**

test/unit/controls.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,23 @@ QUnit.test('Picture-in-Picture control enabled property value should be correct
283283
pictureInPictureToggle.dispose();
284284
});
285285

286+
QUnit.test('Picture-in-Picture control is hidden when the source is audio', function(assert) {
287+
const player = TestHelpers.makePlayer({});
288+
const pictureInPictureToggle = new PictureInPictureToggle(player);
289+
290+
player.src({src: 'example.mp4', type: 'video/mp4'});
291+
player.trigger('loadedmetadata');
292+
293+
assert.notOk(pictureInPictureToggle.hasClass('vjs-hidden'), 'pictureInPictureToggle button is not hidden initially');
294+
295+
player.src({src: 'example1.mp3', type: 'audio/mp3'});
296+
player.trigger('loadedmetadata');
297+
assert.ok(pictureInPictureToggle.hasClass('vjs-hidden'), 'pictureInPictureToggle button is hidden whenh the source is audio');
298+
299+
player.dispose();
300+
pictureInPictureToggle.dispose();
301+
});
302+
286303
QUnit.test('Fullscreen control text should be correct when fullscreenchange is triggered', function(assert) {
287304
const player = TestHelpers.makePlayer({controlBar: false});
288305
const fullscreentoggle = new FullscreenToggle(player);

0 commit comments

Comments
 (0)