diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js
index 239b366a31..37400559f2 100644
--- a/src/components/nowPlayingBar/nowPlayingBar.js
+++ b/src/components/nowPlayingBar/nowPlayingBar.js
@@ -30,6 +30,7 @@ let volumeSlider;
let volumeSliderContainer;
let playPauseButtons;
let positionSlider;
+let toggleAirPlayButton;
let toggleRepeatButton;
let toggleRepeatButtonIcon;
@@ -78,6 +79,8 @@ function getNowPlayingBarHtml() {
html += '';
html += '';
+ html += '';
+
html += '';
html += '';
@@ -192,6 +195,13 @@ function bindEvents(elem) {
}
});
+ toggleAirPlayButton = elem.querySelector('.btnAirPlay');
+ toggleAirPlayButton.addEventListener('click', function () {
+ if (currentPlayer) {
+ playbackManager.toggleAirPlay(currentPlayer);
+ }
+ });
+
elem.querySelector('.btnShuffleQueue').addEventListener('click', function () {
if (currentPlayer) {
playbackManager.toggleQueueShuffleMode();
@@ -328,6 +338,9 @@ function updatePlayerStateInternal(event, state, player) {
toggleRepeatButton.classList.remove('hide');
}
+ const hideAirPlayButton = supportedCommands.indexOf('AirPlay') === -1;
+ toggleAirPlayButton.classList.toggle('hide', hideAirPlayButton);
+
updateRepeatModeDisplay(playbackManager.getRepeatMode());
onQueueShuffleModeChange();
diff --git a/src/plugins/htmlAudioPlayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js
index 2d0c6cceb1..2742e8d8cc 100644
--- a/src/plugins/htmlAudioPlayer/plugin.js
+++ b/src/plugins/htmlAudioPlayer/plugin.js
@@ -379,6 +379,10 @@ class HtmlAudioPlayer {
return getDefaultProfile();
}
+ toggleAirPlay() {
+ return this.setAirPlayEnabled(!this.isAirPlayEnabled());
+ }
+
// Save this for when playback stops, because querying the time at that point might return 0
currentTime(val) {
const mediaElement = this._mediaElement;
@@ -520,6 +524,33 @@ class HtmlAudioPlayer {
return false;
}
+ isAirPlayEnabled() {
+ if (document.AirPlayEnabled) {
+ return !!document.AirplayElement;
+ }
+ return false;
+ }
+
+ setAirPlayEnabled(isEnabled) {
+ const mediaElement = this._mediaElement;
+
+ if (mediaElement) {
+ if (document.AirPlayEnabled) {
+ if (isEnabled) {
+ mediaElement.requestAirPlay().catch(function(err) {
+ console.error('Error requesting AirPlay', err);
+ });
+ } else {
+ document.exitAirPLay().catch(function(err) {
+ console.error('Error exiting AirPlay', err);
+ });
+ }
+ } else {
+ mediaElement.webkitShowPlaybackTargetPicker();
+ }
+ }
+ }
+
supports(feature) {
if (!supportedFeatures) {
supportedFeatures = getSupportedFeatures();
@@ -539,6 +570,10 @@ function getSupportedFeatures() {
list.push('PlaybackRate');
}
+ if (browser.safari) {
+ list.push('AirPlay');
+ }
+
return list;
}