diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 2bf29a2a9c..b25097e325 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -32,6 +32,7 @@ import { appRouter } from '../appRouter'; let volumeSliderContainer; let playPauseButtons; let positionSlider; + let toggleAirPlayButton; let toggleRepeatButton; let toggleRepeatButtonIcon; @@ -80,6 +81,8 @@ import { appRouter } from '../appRouter'; html += ''; html += ''; + html += ''; + html += ''; html += ''; @@ -190,6 +193,13 @@ import { appRouter } from '../appRouter'; } }); + toggleAirPlayButton = elem.querySelector('.btnAirPlay'); + toggleAirPlayButton.addEventListener('click', function () { + if (currentPlayer) { + playbackManager.toggleAirPlay(currentPlayer); + } + }); + elem.querySelector('.btnShuffleQueue').addEventListener('click', function () { if (currentPlayer) { playbackManager.toggleQueueShuffleMode(); @@ -326,6 +336,12 @@ import { appRouter } from '../appRouter'; toggleRepeatButton.classList.remove('hide'); } + if (supportedCommands.indexOf('AirPlay') === -1) { + toggleAirPlayButton.classList.add('hide'); + } else { + toggleAirPlayButton.classList.remove('hide'); + } + updateRepeatModeDisplay(playbackManager.getRepeatMode()); onQueueShuffleModeChange(); diff --git a/src/plugins/htmlAudioPlayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js index 585d935f05..30e1cb4880 100644 --- a/src/plugins/htmlAudioPlayer/plugin.js +++ b/src/plugins/htmlAudioPlayer/plugin.js @@ -347,6 +347,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; @@ -488,6 +492,33 @@ class HtmlAudioPlayer { return false; } + isAirPlayEnabled() { + if (document.AirPlayEnabled) { + return !!document.AirplayElement; + } + return false; + } + + setAirPlayEnabled(isEnabled) { + const mediaElement = this._mediaElement; + + if (document.AirPlayEnabled) { + if (mediaElement) { + 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(); @@ -507,6 +538,10 @@ function getSupportedFeatures() { list.push('PlaybackRate'); } + if (browser.safari || browser.iOS || browser.iPad) { + list.push('AirPlay'); + } + return list; }