From 44a3cd923dba0755f832b6b25af883c5002aee3a Mon Sep 17 00:00:00 2001 From: Dimitrios Panteleimon Giakatos Date: Thu, 23 Jul 2020 21:51:03 +0300 Subject: [PATCH 1/4] added chromecast support --- demo/demo.js | 1 + package.json | 3 +- src/assets/chromecast.svg | 1 + src/css/player.scss | 1 + src/js/controller.js | 90 +++++++++++++++++++++++++++++++++++++++ src/js/i18n.js | 2 + src/js/icons.js | 2 + src/js/options.js | 1 + src/js/template.js | 2 + src/template/player.art | 5 +++ 10 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/assets/chromecast.svg diff --git a/demo/demo.js b/demo/demo.js index acc7112..5746fbf 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -59,6 +59,7 @@ function initPlayers() { container: document.getElementById('dplayer1'), preload: 'none', screenshot: true, + chromecast: true, video: { url: 'https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4', pic: 'https://i.loli.net/2019/06/06/5cf8c5d9c57b510947.png', diff --git a/package.json b/package.json index 0b154cc..0087078 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "dependencies": { "axios": "0.19.2", "balloon-css": "^1.0.3", - "promise-polyfill": "8.1.3" + "promise-polyfill": "8.1.3", + "rxjs": "^6.6.0" } } diff --git a/src/assets/chromecast.svg b/src/assets/chromecast.svg new file mode 100644 index 0000000..ee3c51e --- /dev/null +++ b/src/assets/chromecast.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/css/player.scss b/src/css/player.scss index a6a240e..62952c5 100644 --- a/src/css/player.scss +++ b/src/css/player.scss @@ -167,6 +167,7 @@ .dplayer-volume, .dplayer-camera-icon, .dplayer-airplay-icon, + .dplayer-chromecast-icon, .dplayer-play-icon { display: none; } diff --git a/src/js/controller.js b/src/js/controller.js index cec22f0..d26982e 100644 --- a/src/js/controller.js +++ b/src/js/controller.js @@ -1,6 +1,11 @@ import utils from './utils'; import Thumbnails from './thumbnails'; import Icons from './icons'; +import { Subject } from 'rxjs'; + +let cast; +let runOnce = true; +let isCasting = false; class Controller { constructor(player) { @@ -31,6 +36,7 @@ class Controller { this.initSubtitleButton(); this.initHighlights(); this.initAirplayButton(); + this.initChromecastButton(); if (!utils.isMobile) { this.initVolumeButton(); } @@ -279,6 +285,90 @@ class Controller { } } + initChromecast() { + const script = window.document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script.setAttribute('src', 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1'); + window.document.body.appendChild(script); + + window.__onGCastApiAvailable = (isAvailable) => { + if (isAvailable) { + cast = window.chrome.cast; + const sessionRequest = new cast.SessionRequest(cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID); + const apiConfig = new cast.ApiConfig( + sessionRequest, + () => {}, + (status) => { + if (status === cast.ReceiverAvailability.AVAILABLE) { + console.log('chromecast: ', status); + } + } + ); + cast.initialize(apiConfig, () => {}); + } + }; + } + + initChromecastButton() { + if (this.player.options.chromecast) { + if (runOnce) { + runOnce = false; + this.initChromecast(); + } + const discoverDevices = () => { + const subj = new Subject(); + cast.requestSession( + (s) => { + this.session = s; + subj.next('CONNECTED'); + launchMedia(this.player.options.video.url); + }, + (err) => { + if (err.code === 'cancel') { + this.session = undefined; + subj.next('CANCEL'); + } else { + console.error('Error selecting a cast device', err); + } + } + ); + return subj; + }; + + const launchMedia = (media) => { + const mediaInfo = new cast.media.MediaInfo(media); + const request = new cast.media.LoadRequest(mediaInfo); + + if (!this.session) { + window.open(media); + return false; + } + this.session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError).play(); + return true; + }; + + const onMediaDiscovered = (how, media) => { + this.currentMedia = media; + }; + + const onMediaError = (err) => { + console.error('Error launching media', err); + }; + + this.player.template.chromecastButton.addEventListener('click', () => { + if (isCasting) { + isCasting = false; + this.currentMedia.stop(); + this.session.stop(); + this.initChromecast(); + } else { + isCasting = true; + discoverDevices(); + } + }); + } + } + 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 d18de32..79fecd9 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -53,6 +53,7 @@ const tranTxt = { Send: '发送', Screenshot: '截图', AirPlay: '无线投屏', + ChromeCast: 'ChromeCast', s: '秒', 'Show subtitle': '显示字幕', 'Hide subtitle': '隐藏字幕', @@ -93,6 +94,7 @@ const tranTxt = { Send: '發送', Screenshot: '截圖', AirPlay: '無線投屏', + ChromeCast: 'ChromeCast', s: '秒', 'Show subtitle': '顯示字幕', 'Hide subtitle': '隱藏字幕', diff --git a/src/js/icons.js b/src/js/icons.js index 45233af..bdb3c0c 100644 --- a/src/js/icons.js +++ b/src/js/icons.js @@ -15,6 +15,7 @@ import camera from '../assets/camera.svg'; import airplay from '../assets/airplay.svg'; import subtitle from '../assets/subtitle.svg'; import loading from '../assets/loading.svg'; +import chromecast from '../assets/chromecast.svg'; const Icons = { play: play, @@ -34,6 +35,7 @@ const Icons = { subtitle: subtitle, loading: loading, airplay: airplay, + chromecast: chromecast, }; export default Icons; diff --git a/src/js/options.js b/src/js/options.js index 6a9a87b..529f1ef 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -12,6 +12,7 @@ export default (options) => { lang: (navigator.language || navigator.browserLanguage).toLowerCase(), screenshot: false, airplay: true, + chromecast: false, hotkey: true, preload: 'metadata', volume: 0.7, diff --git a/src/js/template.js b/src/js/template.js index 7f5b5ad..9961dac 100644 --- a/src/js/template.js +++ b/src/js/template.js @@ -23,6 +23,7 @@ class Template { pic: this.options.video.pic, screenshot: this.options.screenshot, airplay: this.options.airplay, + chromecast: this.options.chromecast, preload: this.options.preload, url: this.options.video.url, subtitle: this.options.subtitle, @@ -80,6 +81,7 @@ class Template { 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.chromecastButton = this.container.querySelector('.dplayer-chromecast-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/template/player.art b/src/template/player.art index ece5234..d06db93 100644 --- a/src/template/player.art +++ b/src/template/player.art @@ -120,6 +120,11 @@ {{@ icons.airplay }} {{ /if }} + {{ if options.chromecast }} +
+ {{@ icons.chromecast }} +
+ {{ /if }}