From 583b1c9771f8df794aadff29d8ceb2724183ff33 Mon Sep 17 00:00:00 2001 From: Aladdin Date: Tue, 5 May 2020 05:04:58 +0800 Subject: [PATCH] Add AirPlay Support on iOS / Mac Safari --- demo/demo.js | 11 ++++++----- src/assets/airplay.svg | 1 + src/css/player.scss | 6 +++--- src/js/controller.js | 32 ++++++++++++++++++++++++++++++++ src/js/i18n.js | 2 ++ src/js/icons.js | 2 ++ src/js/options.js | 1 + src/js/template.js | 2 ++ src/js/utils.js | 4 ++++ src/template/player.art | 5 +++++ src/template/video.art | 1 + 11 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 src/assets/airplay.svg diff --git a/demo/demo.js b/demo/demo.js index b4a7d1e..acc7112 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -2,7 +2,7 @@ const stats = new Stats(); stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom document.body.appendChild(stats.dom); -function animate () { +function animate() { stats.begin(); // monitored code goes here stats.end(); @@ -14,7 +14,7 @@ requestAnimationFrame(animate); initPlayers(); handleEvent(); -function handleEvent () { +function handleEvent() { document.getElementById('dplayer-dialog').addEventListener('click', (e) => { const $clickDom = e.currentTarget; const isShowStatus = $clickDom.getAttribute('data-show'); @@ -35,7 +35,7 @@ function handleEvent () { }); } -function initPlayers () { +function initPlayers() { // dplayer-float window.dpFloat = new DPlayer({ container: document.getElementById('dplayer-container'), @@ -82,6 +82,7 @@ function initPlayers () { theme: '#FADFA3', loop: true, screenshot: true, + airplay: true, hotkey: true, logo: 'https://i.loli.net/2019/06/06/5cf8c5d94521136430.png', volume: 0.2, @@ -235,14 +236,14 @@ function initPlayers () { // }); } -function clearPlayers () { +function clearPlayers() { for (let i = 0; i < 6; i++) { window['dp' + (i + 1)].pause(); document.getElementById('dplayer' + (i + 1)).innerHTML = ''; } } -function switchDPlayer () { +function switchDPlayer() { if (dp2.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') { dp2.switchVideo({ url: 'http://static.smartisanos.cn/common/video/t1-ui.mp4', diff --git a/src/assets/airplay.svg b/src/assets/airplay.svg new file mode 100644 index 0000000..aed0d3f --- /dev/null +++ b/src/assets/airplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/css/player.scss b/src/css/player.scss index 3eed575..c80d9a8 100644 --- a/src/css/player.scss +++ b/src/css/player.scss @@ -11,7 +11,7 @@ svg { width: 100%; height: 100%; - + path, circle { fill: #fff; @@ -89,7 +89,7 @@ &.dplayer-arrow { .dplayer-danmaku { - font-size: 18px + font-size: 18px; } .dplayer-icon { margin: 0 -3px; @@ -97,7 +97,6 @@ } &.dplayer-playing { - .dplayer-danmaku .dplayer-danmaku-move { animation-play-state: running; } @@ -167,6 +166,7 @@ .dplayer-controller .dplayer-icons { .dplayer-volume, .dplayer-camera-icon, + .dplayer-airplay-icon, .dplayer-play-icon { display: none; } diff --git a/src/js/controller.js b/src/js/controller.js index 4bf8d3a..89e5640 100644 --- a/src/js/controller.js +++ b/src/js/controller.js @@ -30,6 +30,9 @@ class Controller { this.initScreenshotButton(); this.initSubtitleButton(); this.initHighlights(); + if (utils.isSafari) { + this.initAirplayButton(); + } if (!utils.isMobile) { this.initVolumeButton(); } @@ -249,6 +252,35 @@ class Controller { } } + initAirplayButton() { + if (this.player.options.airplay) { + if (window.WebKitPlaybackTargetAvailabilityEvent) { + this.player.video.addEventListener( + 'webkitplaybacktargetavailabilitychanged', + function (event) { + switch (event.availability) { + case 'available': + this.template.airplayButton.disable = false; + break; + + default: + this.template.airplayButton.disable = true; + } + + this.template.airplayButton.addEventListener( + 'click', + function () { + this.video.webkitShowPlaybackTargetPicker(); + }.bind(this) + ); + }.bind(this.player) + ); + } else { + this.player.template.airplayButton.style.display = 'none'; + } + } + } + initSubtitleButton() { if (this.player.options.subtitle) { this.player.events.on('subtitle_show', () => { diff --git a/src/js/i18n.js b/src/js/i18n.js index 3e694c4..d18de32 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -52,6 +52,7 @@ const tranTxt = { 'Web full screen': '页面全屏', Send: '发送', Screenshot: '截图', + AirPlay: '无线投屏', s: '秒', 'Show subtitle': '显示字幕', 'Hide subtitle': '隐藏字幕', @@ -91,6 +92,7 @@ const tranTxt = { 'Web full screen': '頁面全螢幕', Send: '發送', Screenshot: '截圖', + AirPlay: '無線投屏', s: '秒', 'Show subtitle': '顯示字幕', 'Hide subtitle': '隱藏字幕', diff --git a/src/js/icons.js b/src/js/icons.js index a42f9c8..45233af 100644 --- a/src/js/icons.js +++ b/src/js/icons.js @@ -12,6 +12,7 @@ import commentOff from '../assets/comment-off.svg'; import send from '../assets/send.svg'; import pallette from '../assets/pallette.svg'; import camera from '../assets/camera.svg'; +import airplay from '../assets/airplay.svg'; import subtitle from '../assets/subtitle.svg'; import loading from '../assets/loading.svg'; @@ -32,6 +33,7 @@ const Icons = { camera: camera, subtitle: subtitle, loading: loading, + airplay: airplay, }; export default Icons; diff --git a/src/js/options.js b/src/js/options.js index 10c237b..7925236 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -11,6 +11,7 @@ export default (options) => { loop: false, lang: (navigator.language || navigator.browserLanguage).toLowerCase(), screenshot: false, + airplay: false, hotkey: true, preload: 'metadata', volume: 0.7, diff --git a/src/js/template.js b/src/js/template.js index ea2aad4..7f5b5ad 100644 --- a/src/js/template.js +++ b/src/js/template.js @@ -22,6 +22,7 @@ class Template { current: true, pic: this.options.video.pic, screenshot: this.options.screenshot, + airplay: this.options.airplay, preload: this.options.preload, url: this.options.video.url, subtitle: this.options.subtitle, @@ -78,6 +79,7 @@ class Template { this.menuItem = this.container.querySelectorAll('.dplayer-menu-item'); this.qualityList = this.container.querySelector('.dplayer-quality-list'); this.camareButton = this.container.querySelector('.dplayer-camera-icon'); + this.airplayButton = this.container.querySelector('.dplayer-airplay-icon'); this.subtitleButton = this.container.querySelector('.dplayer-subtitle-icon'); this.subtitleButtonInner = this.container.querySelector('.dplayer-subtitle-icon .dplayer-icon-content'); this.subtitle = this.container.querySelector('.dplayer-subtitle'); diff --git a/src/js/utils.js b/src/js/utils.js index b2ec662..c574831 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -1,5 +1,7 @@ const isMobile = /mobile/i.test(window.navigator.userAgent); +const isSafari = /Safari/i.test(window.navigator.userAgent); + const utils = { /** * Parse second to time string @@ -89,6 +91,8 @@ const utils = { isMobile: isMobile, + isSafari: isSafari, + isFirefox: /firefox/i.test(window.navigator.userAgent), isChrome: /chrome/i.test(window.navigator.userAgent), diff --git a/src/template/player.art b/src/template/player.art index 252a7d9..87122e1 100644 --- a/src/template/player.art +++ b/src/template/player.art @@ -115,6 +115,11 @@ {{@ icons.camera }} {{ /if }} + {{ if options.airplay }} +
+ {{@ icons.airplay }} +
+ {{ /if }}