浏览代码

Merge branch 'pr/dpgiakatos/943'

DIYgod 2 年之前
父节点
当前提交
fccdda6d47
共有 9 个文件被更改,包括 107 次插入1 次删除
  1. 2 1
      package.json
  2. 1 0
      src/assets/chromecast.svg
  3. 1 0
      src/css/player.less
  4. 90 0
      src/js/controller.js
  5. 2 0
      src/js/i18n.js
  6. 2 0
      src/js/icons.js
  7. 2 0
      src/js/options.js
  8. 2 0
      src/js/template.js
  9. 5 0
      src/template/player.art

+ 2 - 1
package.json

@@ -79,6 +79,7 @@
     "dependencies": {
     "dependencies": {
         "axios": "0.27.2",
         "axios": "0.27.2",
         "balloon-css": "^1.0.3",
         "balloon-css": "^1.0.3",
-        "promise-polyfill": "8.2.3"
+        "promise-polyfill": "8.2.3",
+        "rxjs": "^6.6.0"
     }
     }
 }
 }

+ 1 - 0
src/assets/chromecast.svg

@@ -0,0 +1 @@
+<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="chromecast" class="svg-inline--fa fa-chromecast fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M447.8,64H64c-23.6,0-42.7,19.1-42.7,42.7v63.9H64v-63.9h383.8v298.6H298.6V448H448c23.6,0,42.7-19.1,42.7-42.7V106.7 C490.7,83.1,471.4,64,447.8,64z M21.3,383.6L21.3,383.6l0,63.9h63.9C85.2,412.2,56.6,383.6,21.3,383.6L21.3,383.6z M21.3,298.6V341 c58.9,0,106.6,48.1,106.6,107h42.7C170.7,365.6,103.7,298.7,21.3,298.6z M213.4,448h42.7c-0.5-129.5-105.3-234.3-234.8-234.6l0,42.4 C127.3,255.6,213.3,342,213.4,448z"></path></svg>

+ 1 - 0
src/css/player.less

@@ -167,6 +167,7 @@
             .dplayer-volume,
             .dplayer-volume,
             .dplayer-camera-icon,
             .dplayer-camera-icon,
             .dplayer-airplay-icon,
             .dplayer-airplay-icon,
+            .dplayer-chromecast-icon,
             .dplayer-play-icon {
             .dplayer-play-icon {
                 display: none;
                 display: none;
             }
             }

+ 90 - 0
src/js/controller.js

@@ -1,6 +1,11 @@
 import utils from './utils';
 import utils from './utils';
 import Thumbnails from './thumbnails';
 import Thumbnails from './thumbnails';
 import Icons from './icons';
 import Icons from './icons';
+import { Subject } from 'rxjs';
+
+let cast;
+let runOnce = true;
+let isCasting = false;
 
 
 class Controller {
 class Controller {
     constructor(player) {
     constructor(player) {
@@ -31,6 +36,7 @@ class Controller {
         this.initSubtitleButton();
         this.initSubtitleButton();
         this.initHighlights();
         this.initHighlights();
         this.initAirplayButton();
         this.initAirplayButton();
+        this.initChromecastButton();
         if (!utils.isMobile) {
         if (!utils.isMobile) {
             this.initVolumeButton();
             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() {
     initSubtitleButton() {
         if (this.player.options.subtitle) {
         if (this.player.options.subtitle) {
             this.player.events.on('subtitle_show', () => {
             this.player.events.on('subtitle_show', () => {

+ 2 - 0
src/js/i18n.js

@@ -53,6 +53,7 @@ const tranTxt = {
         Send: '发送',
         Send: '发送',
         Screenshot: '截图',
         Screenshot: '截图',
         AirPlay: '无线投屏',
         AirPlay: '无线投屏',
+        ChromeCast: 'ChromeCast',
         s: '秒',
         s: '秒',
         'Show subtitle': '显示字幕',
         'Show subtitle': '显示字幕',
         'Hide subtitle': '隐藏字幕',
         'Hide subtitle': '隐藏字幕',
@@ -93,6 +94,7 @@ const tranTxt = {
         Send: '發送',
         Send: '發送',
         Screenshot: '截圖',
         Screenshot: '截圖',
         AirPlay: '無線投屏',
         AirPlay: '無線投屏',
+        ChromeCast: 'ChromeCast',
         s: '秒',
         s: '秒',
         'Show subtitle': '顯示字幕',
         'Show subtitle': '顯示字幕',
         'Hide subtitle': '隱藏字幕',
         'Hide subtitle': '隱藏字幕',

+ 2 - 0
src/js/icons.js

@@ -15,6 +15,7 @@ import camera from '../assets/camera.svg';
 import airplay from '../assets/airplay.svg';
 import airplay from '../assets/airplay.svg';
 import subtitle from '../assets/subtitle.svg';
 import subtitle from '../assets/subtitle.svg';
 import loading from '../assets/loading.svg';
 import loading from '../assets/loading.svg';
+import chromecast from '../assets/chromecast.svg';
 
 
 const Icons = {
 const Icons = {
     play: play,
     play: play,
@@ -34,6 +35,7 @@ const Icons = {
     subtitle: subtitle,
     subtitle: subtitle,
     loading: loading,
     loading: loading,
     airplay: airplay,
     airplay: airplay,
+    chromecast: chromecast,
 };
 };
 
 
 export default Icons;
 export default Icons;

+ 2 - 0
src/js/options.js

@@ -1,5 +1,6 @@
 /* global DPLAYER_VERSION */
 /* global DPLAYER_VERSION */
 import defaultApiBackend from './api.js';
 import defaultApiBackend from './api.js';
+import utils from './utils';
 
 
 export default (options) => {
 export default (options) => {
     // default options
     // default options
@@ -12,6 +13,7 @@ export default (options) => {
         lang: (navigator.language || navigator.browserLanguage).toLowerCase(),
         lang: (navigator.language || navigator.browserLanguage).toLowerCase(),
         screenshot: false,
         screenshot: false,
         airplay: true,
         airplay: true,
+        chromecast: utils.isChrome,
         hotkey: true,
         hotkey: true,
         preload: 'metadata',
         preload: 'metadata',
         volume: 0.7,
         volume: 0.7,

+ 2 - 0
src/js/template.js

@@ -23,6 +23,7 @@ class Template {
                 pic: this.options.video.pic,
                 pic: this.options.video.pic,
                 screenshot: this.options.screenshot,
                 screenshot: this.options.screenshot,
                 airplay: this.options.airplay,
                 airplay: this.options.airplay,
+                chromecast: this.options.chromecast,
                 preload: this.options.preload,
                 preload: this.options.preload,
                 url: this.options.video.url,
                 url: this.options.video.url,
                 subtitle: this.options.subtitle,
                 subtitle: this.options.subtitle,
@@ -80,6 +81,7 @@ class Template {
         this.qualityList = this.container.querySelector('.dplayer-quality-list');
         this.qualityList = this.container.querySelector('.dplayer-quality-list');
         this.camareButton = this.container.querySelector('.dplayer-camera-icon');
         this.camareButton = this.container.querySelector('.dplayer-camera-icon');
         this.airplayButton = this.container.querySelector('.dplayer-airplay-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.subtitleButton = this.container.querySelector('.dplayer-subtitle-icon');
         this.subtitleButtonInner = this.container.querySelector('.dplayer-subtitle-icon .dplayer-icon-content');
         this.subtitleButtonInner = this.container.querySelector('.dplayer-subtitle-icon .dplayer-icon-content');
         this.subtitle = this.container.querySelector('.dplayer-subtitle');
         this.subtitle = this.container.querySelector('.dplayer-subtitle');

+ 5 - 0
src/template/player.art

@@ -120,6 +120,11 @@
             <span class="dplayer-icon-content">{{@ icons.airplay }}</span>
             <span class="dplayer-icon-content">{{@ icons.airplay }}</span>
         </div>
         </div>
         {{ /if }}
         {{ /if }}
+        {{ if options.chromecast }}
+        <div class="dplayer-icon dplayer-chromecast-icon" data-balloon="{{ tran('ChromeCast') }}" data-balloon-pos="up">
+            <span class="dplayer-icon-content">{{@ icons.chromecast }}</span>
+        </div>
+        {{ /if }}
         <div class="dplayer-comment">
         <div class="dplayer-comment">
             <button class="dplayer-icon dplayer-comment-icon" data-balloon="{{ tran('Send danmaku') }}" data-balloon-pos="up">
             <button class="dplayer-icon dplayer-comment-icon" data-balloon="{{ tran('Send danmaku') }}" data-balloon-pos="up">
                 <span class="dplayer-icon-content">{{@ icons.comment }}</span>
                 <span class="dplayer-icon-content">{{@ icons.comment }}</span>