Skip to content

Commit

Permalink
feat: Audio Only Mode (videojs#7647)
Browse files Browse the repository at this point in the history
* audioOnlyMode wip

* fix incorrect logs

* add tests

* minor code changes and add another test

* update docs

* fix formatting

* fix typo

* Consolidate conditions

Co-authored-by: Pat O'Neill <pgoneill@gmail.com>

* Compare objects instead of name string

Co-authored-by: Pat O'Neill <pgoneill@gmail.com>

* code review changes

* remove unnecessary equivalence check

Co-authored-by: Gary Katsevman <git@gkatsev.com>

* replace height() with currentHeight()

Co-authored-by: Gary Katsevman <git@gkatsev.com>

* rewrite for async pip and fs handling

* asyncify tests

* update doc

* add test

Co-authored-by: Pat O'Neill <pgoneill@gmail.com>
Co-authored-by: Gary Katsevman <git@gkatsev.com>
  • Loading branch information
3 people authored and edirub committed Jun 8, 2023
1 parent 2c3c6ae commit 869a88f
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 4 deletions.
8 changes: 8 additions & 0 deletions docs/guides/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* [width](#width)
* [Video.js-specific Options](#videojs-specific-options)
* [aspectRatio](#aspectratio)
* [audioOnlyMode](#audioonlymode)
* [audioPosterMode](#audiopostermode)
* [autoSetup](#autosetup)
* [breakpoints](#breakpoints)
Expand Down Expand Up @@ -182,6 +183,13 @@ Puts the player in [fluid](#fluid) mode and the value is used when calculating t

Alternatively, the classes `vjs-16-9`, `vjs-9-16`, `vjs-4-3` or `vjs-1-1` can be added to the player.

### `audioOnlyMode`

> Type: `boolean`
> Default: `false`
If set to true, it asynchronously hides all player components except the control bar, as well as any specific controls that are needed only for video. This option can be set to `true` or `false` by calling `audioOnlyMode([true|false])` at runtime. When used as a setter, it returns a Promise. When used as a getter, it returns a Boolean.

### `audioPosterMode`

> Type: `boolean`
Expand Down
4 changes: 4 additions & 0 deletions src/css/components/_captions.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.video-js .vjs-captions-button .vjs-icon-placeholder {
@extend .vjs-icon-captions;
}

.video-js.vjs-audio-only-mode .vjs-captions-button {
display: none;
}
10 changes: 6 additions & 4 deletions src/css/components/_control-bar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
@include background-color-with-alpha($primary-background-color, $primary-background-transparency);
}

// Video has started playing
.vjs-has-started .vjs-control-bar {
// Video has started playing or we are in audioOnlyMode
.vjs-has-started .vjs-control-bar,
.vjs-audio-only-mode .vjs-control-bar {
@include display-flex;
visibility: visible;
opacity: 1;
Expand Down Expand Up @@ -41,8 +42,9 @@
display: none !important;
}

// Don't hide the control bar if it's audio
.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
// Don't hide the control bar if it's audio or in audioOnlyMode
.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar,
.vjs-audio-only-mode.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
opacity: 1;
visibility: visible;
}
Expand Down
4 changes: 4 additions & 0 deletions src/css/components/_descriptions.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.video-js .vjs-descriptions-button .vjs-icon-placeholder {
@extend .vjs-icon-audio-description;
}

.video-js.vjs-audio-only-mode .vjs-descriptions-button {
display: none;
}
5 changes: 5 additions & 0 deletions src/css/components/_fullscreen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
@extend .vjs-icon-fullscreen-enter;
}
}

.video-js.vjs-audio-only-mode .vjs-fullscreen-control {
display: none;
}

// Switch to the exit icon when the player is in fullscreen
.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder {
@extend .vjs-icon-fullscreen-exit;
Expand Down
4 changes: 4 additions & 0 deletions src/css/components/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@
height: 100%;
}

.video-js.vjs-audio-only-mode .vjs-tech {
display: none;
}

// Fullscreen Styles
body.vjs-full-window {
padding: 0;
Expand Down
5 changes: 5 additions & 0 deletions src/css/components/_picture-in-picture.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
@extend .vjs-icon-picture-in-picture-enter;
}
}

.video-js.vjs-audio-only-mode .vjs-picture-in-picture-control {
display: none;
}

// Switch to the exit icon when the player is in Picture-in-Picture
.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder {
@extend .vjs-icon-picture-in-picture-exit;
Expand Down
4 changes: 4 additions & 0 deletions src/css/components/_subs-caps.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@
font-size: 1.5em;
line-height: inherit;
}

.video-js.vjs-audio-only-mode .vjs-subs-caps-button {
display: none;
}
112 changes: 112 additions & 0 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,15 @@ class Player extends Component {
// Init debugEnabled_
this.debugEnabled_ = false;

// Init state audioOnlyMode_
this.audioOnlyMode_ = false;

// Init state audioOnlyCache_
this.audioOnlyCache_ = {
playerHeight: null,
hiddenChildren: []
};

// if the global option object was accidentally blown away by
// someone, bail early with an informative error
if (!this.options_ ||
Expand Down Expand Up @@ -574,6 +583,7 @@ class Player extends Component {

this.breakpoints(this.options_.breakpoints);
this.responsive(this.options_.responsive);
this.audioOnlyMode(this.options_.audioOnlyMode);
}

/**
Expand Down Expand Up @@ -4293,6 +4303,107 @@ class Player extends Component {
return !!this.isAudio_;
}

updateAudioOnlyModeState_(value) {
this.audioOnlyMode_ = value;
this.trigger('audioonlymodechange');
}

enableAudioOnlyUI_() {
// Update styling immediately to show the control bar so we can get its height
this.addClass('vjs-audio-only-mode');

const playerChildren = this.children();
const controlBar = this.getChild('ControlBar');
const controlBarHeight = controlBar && controlBar.currentHeight();

// Hide all player components except the control bar. Control bar components
// needed only for video are hidden with CSS
playerChildren.forEach(child => {
if (child === controlBar) {
return;
}

if (child.el_ && !child.hasClass('vjs-hidden')) {
child.hide();

this.audioOnlyCache_.hiddenChildren.push(child);
}
});

this.audioOnlyCache_.playerHeight = this.currentHeight();

// Set the player height the same as the control bar
this.height(controlBarHeight);
this.updateAudioOnlyModeState_(true);
}

disableAudioOnlyUI_() {
this.removeClass('vjs-audio-only-mode');

// Show player components that were previously hidden
this.audioOnlyCache_.hiddenChildren.forEach(child => child.show());

// Reset player height
this.height(this.audioOnlyCache_.playerHeight);
this.updateAudioOnlyModeState_(false);
}

/**
* Get the current audioOnlyMode state or set audioOnlyMode to true or false.
*
* Setting this to `true` will hide all player components except the control bar,
* as well as control bar components needed only for video.
*
* @param {boolean} [value]
* The value to set audioOnlyMode to.
*
* @return {Promise|boolean}
* A Promise is returned when setting the state, and a boolean when getting
* the present state
*/
audioOnlyMode(value) {
if (typeof value !== 'boolean' || value === this.audioOnlyMode_) {
return this.audioOnlyMode_;
}

const PromiseClass = this.options_.Promise || window.Promise;

if (PromiseClass) {
// Enable Audio Only Mode
if (value) {
const exitPromises = [];

// Fullscreen and PiP are not supported in audioOnlyMode, so exit if we need to.
if (this.isInPictureInPicture()) {
exitPromises.push(this.exitPictureInPicture());
}

if (this.isFullscreen()) {
exitPromises.push(this.exitFullscreen());
}

return PromiseClass.all(exitPromises).then(() => this.enableAudioOnlyUI_());
}

// Disable Audio Only Mode
return PromiseClass.resolve().then(() => this.disableAudioOnlyUI_());
}

if (value) {
if (this.isInPictureInPicture()) {
this.exitPictureInPicture();
}

if (this.isFullscreen()) {
this.exitFullscreen();
}

this.enableAudioOnlyUI_();
} else {
this.disableAudioOnlyUI_();
}
}

/**
* Get the current audioPosterMode state or set audioPosterMode to true or false
*
Expand Down Expand Up @@ -5131,6 +5242,7 @@ Player.prototype.options_ = {

breakpoints: {},
responsive: false,
audioOnlyMode: false,
audioPosterMode: false
};

Expand Down
Loading

0 comments on commit 869a88f

Please sign in to comment.