Przeglądaj źródła

support segmented videos

DIYgod 8 lat temu
rodzic
commit
a042b39550
11 zmienionych plików z 294 dodań i 119 usunięć
  1. 0 1
      .eslintrc
  2. 25 1
      demo/index.html
  3. 0 0
      dist/DPlayer.min.css
  4. 0 0
      dist/DPlayer.min.js
  5. 0 0
      dist/DPlayer.min.js.map
  6. 81 113
      src/DPlayer.js
  7. 5 0
      src/DPlayer.scss
  8. 9 4
      src/html.js
  9. 3 0
      src/option.js
  10. 38 0
      src/utils.js
  11. 133 0
      src/video.js

+ 0 - 1
.eslintrc

@@ -17,7 +17,6 @@
         "no-implicit-globals": 1,
         "no-labels": 1,
         "no-multi-str": 1,
-        "no-unused-expressions": 1,
         "comma-spacing": 1,
         "comma-style": 1,
         "func-call-spacing": 1,

+ 25 - 1
demo/index.html

@@ -46,7 +46,6 @@
     <link rel="stylesheet" href="../dist/DPlayer.min.css">
 </head>
 <body>
-<a href="https://github.com/DIYgod/DPlayer" target="_blank" class="github-corner"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
 <div class="container">
     <h1>DPlayer</h1>
     <h2>Wow, such a lovely HTML5 danmaku video player</h2>
@@ -61,6 +60,8 @@
     <div class="dplayer" id="dplayer3"></div>
     <h3>FLV format support</h3>
     <div class="dplayer" id="dplayer4"></div>
+    <h3>Segmented videos</h3>
+    <div class="dplayer" id="dplayer5"></div>
 </div>
 <script src="https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js"></script>
 <script src="../plugin/flv.min.js"></script>
@@ -180,6 +181,28 @@
         }
     });
 
+    // Segmented videos
+    var dp5 = new DPlayer({
+        element: document.getElementById('dplayer5'),
+        autoplay: false,
+        theme: '#FADFA3',
+        loop: true,
+        screenshot: true,
+        hotkey: true,
+        preload: 'metadata',
+        video: {
+            url: ['http://devtest.qiniudn.com/若能绽放光芒0.mp4', 'http://devtest.qiniudn.com/若能绽放光芒1.mp4', 'http://devtest.qiniudn.com/若能绽放光芒2.mp4', 'http://devtest.qiniudn.com/若能绽放光芒3.mp4', 'http://devtest.qiniudn.com/若能绽放光芒4.mp4', 'http://devtest.qiniudn.com/若能绽放光芒5.mp4', 'http://devtest.qiniudn.com/若能绽放光芒6.mp4', 'http://devtest.qiniudn.com/若能绽放光芒7.mp4', 'http://devtest.qiniudn.com/若能绽放光芒8.mp4'],
+            pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
+            type: 'normal',
+        },
+        danmaku: {
+            id: '9E2E3368B56CDBB40',
+            api: 'https://api.prprpr.me/dplayer/',
+            token: 'tokendemo',
+            maximum: 3000
+        }
+    });
+
     // stats.js: JavaScript Performance Monitor
     var stats = new Stats();
     stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
@@ -193,5 +216,6 @@
     }
     requestAnimationFrame(animate);
 </script>
+<a href="https://github.com/DIYgod/DPlayer" target="_blank" class="github-corner"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
 </body>
 </html>

Plik diff jest za duży
+ 0 - 0
dist/DPlayer.min.css


Plik diff jest za duży
+ 0 - 0
dist/DPlayer.min.js


Plik diff jest za duży
+ 0 - 0
dist/DPlayer.min.js.map


+ 81 - 113
src/DPlayer.js

@@ -1,10 +1,12 @@
 console.log('\n %c DPlayer 1.2.0 %c http://dplayer.js.org \n\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
 
 require('./DPlayer.scss');
+const utils = require('./utils.js');
 const svg = require('./svg.js');
 const handleOption = require('./option.js');
 const i18n = require('./i18n.js');
 const html = require('./html.js');
+const Video = require('./video.js');
 const isMobile = /mobile/i.test(window.navigator.userAgent);
 
 let index = 0;
@@ -65,8 +67,8 @@ class DPlayer {
             document.head.appendChild(arrowStyle);
         }
 
-        // get this video object
-        this.video = this.element.getElementsByClassName('dplayer-video')[0];
+        // get this video manager
+        this.video = new Video(this.element.getElementsByClassName('dplayer-video'));
 
         // Support HTTP Live Streaming
         let enablehls;
@@ -82,7 +84,7 @@ class DPlayer {
         if (enablehls && Hls.isSupported()) {
             // this.element.getElementsByClassName('dplayer-time')[0].style.display = 'none';
             const hls = new Hls();
-            hls.attachMedia(this.video);
+            hls.attachMedia(this.video.current);
             hls.on(Hls.Events.MEDIA_ATTACHED, () => {
                 hls.loadSource(this.option.video.url);
                 hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
@@ -107,7 +109,7 @@ class DPlayer {
                 type: 'flv',
                 url: this.option.video.url
             });
-            flvPlayer.attachMediaElement(this.video);
+            flvPlayer.attachMediaElement(this.video.current);
             flvPlayer.load();
         }
 
@@ -146,43 +148,6 @@ class DPlayer {
             conMask.addEventListener('click', toggleController);
         }
 
-
-        /**
-         * Parse second to 00:00 format
-         *
-         * @param {Number} second
-         * @return {String} 00:00 format
-         */
-        const secondToTime = (second) => {
-            const add0 = (num) => num < 10 ? '0' + num : '' + num;
-            const min = parseInt(second / 60);
-            const sec = parseInt(second - min * 60);
-            return add0(min) + ':' + add0(sec);
-        };
-
-        /**
-         * control play progress
-         */
-        // get element's view position
-        const getElementViewLeft = (element) => {
-            let actualLeft = element.offsetLeft;
-            let current = element.offsetParent;
-            const elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
-            if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
-                while (current !== null) {
-                    actualLeft += current.offsetLeft;
-                    current = current.offsetParent;
-                }
-            }
-            else {
-                while (current !== null && current !== this.element) {
-                    actualLeft += current.offsetLeft;
-                    current = current.offsetParent;
-                }
-            }
-            return actualLeft - elementScrollLeft;
-        };
-
         const bar = {};
         bar.playedBar = this.element.getElementsByClassName('dplayer-played')[0];
         bar.loadedBar = this.element.getElementsByClassName('dplayer-loaded')[0];
@@ -191,9 +156,9 @@ class DPlayer {
         let barWidth;
 
         if (this.option.danmaku) {
-            this.video.addEventListener('seeking', () => {
+            this.video.on('all', 'seeking', () => {
                 for (let i = 0; i < this.dan.length; i++) {
-                    if (this.dan[i].time >= this.video.currentTime) {
+                    if (this.dan[i].time >= this.video.currentTime()) {
                         this.danIndex = i;
                         return;
                     }
@@ -209,30 +174,30 @@ class DPlayer {
         this.setTime = () => {
             this.playedTime = setInterval(() => {
                 // whether the video is buffering
-                currentPlayPos = this.video.currentTime;
+                currentPlayPos = this.video.currentTime();
                 if (!bufferingDetected
                     && currentPlayPos < lastPlayPos + 0.01
-                    && !this.video.paused) {
+                    && !this.video.attr('paused')) {
                     this.element.classList.add('dplayer-loading');
                     bufferingDetected = true;
                 }
                 if (bufferingDetected
                     && currentPlayPos > lastPlayPos + 0.01
-                    && !this.video.paused) {
+                    && !this.video.attr('paused')) {
                     this.element.classList.remove('dplayer-loading');
                     bufferingDetected = false;
                 }
                 lastPlayPos = currentPlayPos;
 
-                this.updateBar('played', this.video.currentTime / this.video.duration, 'width');
-                this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = secondToTime(this.video.currentTime);
+                this.updateBar('played', this.video.currentTime() / this.video.duration, 'width');
+                this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = utils.secondToTime(this.video.currentTime());
                 this.trigger('playing');
             }, 100);
             if (this.option.danmaku && showdan) {
                 danmakuTime = setInterval(() => {
                     if (this.dan) {
                         let item = this.dan[this.danIndex];
-                        while (item && this.video.currentTime > parseFloat(item.time)) {
+                        while (item && this.video.currentTime() > parseFloat(item.time)) {
                             this.pushDanmaku(item.text, item.color, item.type);
                             item = this.dan[++this.danIndex];
                         }
@@ -250,16 +215,16 @@ class DPlayer {
         pbar.addEventListener('click', (event) => {
             const e = event || window.event;
             barWidth = pbar.clientWidth;
-            let percentage = (e.clientX - getElementViewLeft(pbar)) / barWidth;
+            let percentage = (e.clientX - utils.getElementViewLeft(pbar)) / barWidth;
             percentage = percentage > 0 ? percentage : 0;
             percentage = percentage < 1 ? percentage : 1;
             this.updateBar('played', percentage, 'width');
-            this.video.currentTime = parseFloat(bar.playedBar.style.width) / 100 * this.video.duration;
+            this.video.seek(parseFloat(bar.playedBar.style.width) / 100 * this.video.duration);
         });
 
         this.isTipsShow = false;
         this.timeTipsHandler = this.timeTipsHandler(
-            pbar, pbarTimeTips, secondToTime).bind(this);
+            pbar, pbarTimeTips).bind(this);
         pbar.addEventListener('mousemove', this.timeTipsHandler);
         pbar.addEventListener('mouseover', this.timeTipsHandler);
         pbar.addEventListener('mouseenter', this.timeTipsHandler);
@@ -268,17 +233,17 @@ class DPlayer {
 
         const thumbMove = (event) => {
             const e = event || window.event;
-            let percentage = (e.clientX - getElementViewLeft(pbar)) / barWidth;
+            let percentage = (e.clientX - utils.getElementViewLeft(pbar)) / barWidth;
             percentage = percentage > 0 ? percentage : 0;
             percentage = percentage < 1 ? percentage : 1;
             this.updateBar('played', percentage, 'width');
-            this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = secondToTime(percentage * this.video.duration);
+            this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = utils.secondToTime(percentage * this.video.duration);
         };
 
         const thumbUp = () => {
             document.removeEventListener('mouseup', thumbUp);
             document.removeEventListener('mousemove', thumbMove);
-            this.video.currentTime = parseFloat(bar.playedBar.style.width) / 100 * this.video.duration;
+            this.video.seek(parseFloat(bar.playedBar.style.width) / 100 * this.video.duration);
             this.setTime();
         };
 
@@ -302,10 +267,10 @@ class DPlayer {
 
         this.switchVolumeIcon = () => {
             const volumeicon = this.element.getElementsByClassName('dplayer-volume-icon')[0];
-            if (this.video.volume >= 0.8) {
+            if (this.video.attr('volume') >= 0.8) {
                 volumeicon.innerHTML = svg('volume-up');
             }
-            else if (this.video.volume > 0) {
+            else if (this.video.attr('volume') > 0) {
                 volumeicon.innerHTML = svg('volume-down');
             }
             else {
@@ -314,7 +279,7 @@ class DPlayer {
         };
         const volumeMove = (event) => {
             const e = event || window.event;
-            const percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
+            const percentage = (e.clientX - utils.getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
             this.volume(percentage);
         };
         const volumeUp = () => {
@@ -325,7 +290,7 @@ class DPlayer {
 
         volumeBarWrapWrap.addEventListener('click', (event) => {
             const e = event || window.event;
-            const percentage = (e.clientX - getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
+            const percentage = (e.clientX - utils.getElementViewLeft(volumeBarWrap) - 5.5) / vWidth;
             this.volume(percentage);
         });
         volumeBarWrapWrap.addEventListener('mousedown', () => {
@@ -334,13 +299,13 @@ class DPlayer {
             volumeEle.classList.add('dplayer-volume-active');
         });
         volumeicon.addEventListener('click', () => {
-            if (this.video.muted) {
-                this.video.muted = false;
+            if (this.video.attr('muted')) {
+                this.video.attr('muted', false);
                 this.switchVolumeIcon();
-                this.updateBar('volume', this.video.volume, 'width');
+                this.updateBar('volume', this.video.attr('volume'), 'width');
             }
             else {
-                this.video.muted = true;
+                this.video.attr('muted', true);
                 volumeicon.innerHTML = svg('volume-off');
                 this.updateBar('volume', 0, 'width');
             }
@@ -356,7 +321,7 @@ class DPlayer {
                 this.element.classList.remove('dplayer-hide-controller');
                 clearTimeout(hideTime);
                 hideTime = setTimeout(() => {
-                    if (this.video.played.length) {
+                    if (this.video.attr('played').length) {
                         this.element.classList.add('dplayer-hide-controller');
                         closeSetting();
                         closeComment();
@@ -417,11 +382,9 @@ class DPlayer {
                 loopToggle.checked = !loopToggle.checked;
                 if (loopToggle.checked) {
                     loop = true;
-                    this.video.loop = loop;
                 }
                 else {
                     loop = false;
-                    this.video.loop = loop;
                 }
                 closeSetting();
             });
@@ -438,7 +401,7 @@ class DPlayer {
                     showdan = true;
                     if (this.option.danmaku) {
                         for (let i = 0; i < this.dan.length; i++) {
-                            if (this.dan[i].time >= this.video.currentTime) {
+                            if (this.dan[i].time >= this.video.currentTime()) {
                                 this.danIndex = i;
                                 break;
                             }
@@ -446,7 +409,7 @@ class DPlayer {
                         }
                         danmakuTime = setInterval(() => {
                             let item = this.dan[this.danIndex];
-                            while (item && this.video.currentTime >= parseFloat(item.time)) {
+                            while (item && this.video.currentTime() >= parseFloat(item.time)) {
                                 this.pushDanmaku(item.text, item.color, item.type);
                                 item = this.dan[++this.danIndex];
                             }
@@ -478,7 +441,7 @@ class DPlayer {
                 const speedItem = settingBox.getElementsByClassName('dplayer-setting-speed-item');
                 for (let i = 0; i < speedItem.length; i++) {
                     speedItem[i].addEventListener('click', () => {
-                        this.video.playbackRate = speedItem[i].dataset.speed;
+                        this.video.attr('playbackRate', speedItem[i].dataset.speed);
                         closeSetting();
                     });
                 }
@@ -495,7 +458,7 @@ class DPlayer {
 
                 const danmakuMove = (event) => {
                     const e = event || window.event;
-                    let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
+                    let percentage = (e.clientX - utils.getElementViewLeft(danmakuBarWrap)) / dWidth;
                     percentage = percentage > 0 ? percentage : 0;
                     percentage = percentage < 1 ? percentage : 1;
                     this.updateBar('danmaku', percentage, 'width');
@@ -514,7 +477,7 @@ class DPlayer {
 
                 danmakuBarWrapWrap.addEventListener('click', (event) => {
                     const e = event || window.event;
-                    let percentage = (e.clientX - getElementViewLeft(danmakuBarWrap)) / dWidth;
+                    let percentage = (e.clientX - utils.getElementViewLeft(danmakuBarWrap)) / dWidth;
                     percentage = percentage > 0 ? percentage : 0;
                     percentage = percentage < 1 ? percentage : 1;
                     this.updateBar('danmaku', percentage, 'width');
@@ -539,61 +502,65 @@ class DPlayer {
          * video events
          */
         // show video time: the metadata has loaded or changed
-        this.video.addEventListener('durationchange', () => {
-            if (this.video.duration !== 1) {           // compatibility: Android browsers will output 1 at first
-                this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = secondToTime(this.video.duration);
+        this.video.on('all', 'durationchange', (i, video) => {
+            if (video.duration !== 1) {           // compatibility: Android browsers will output 1 at first
+                this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = utils.secondToTime(this.video.duration);
             }
         });
 
         // show video loaded bar: to inform interested parties of progress downloading the media
-        this.video.addEventListener('progress', () => {
-            const percentage = this.video.buffered.length ? this.video.buffered.end(this.video.buffered.length - 1) / this.video.duration : 0;
+        this.video.on('current', 'progress', (i, video) => {
+            const percentage = video.buffered.length ? video.buffered.end(video.buffered.length - 1) / video.duration : 0;
             this.updateBar('loaded', percentage, 'width');
         });
 
         // video download error: an error occurs
-        this.video.addEventListener('error', () => {
+        this.video.on('all', 'error', () => {
             this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`;
             this.trigger('pause');
         });
 
         // video can play: enough data is available that the media can be played
-        this.video.addEventListener('canplay', () => {
+        this.video.on('current', 'canplay', () => {
             this.trigger('canplay');
         });
 
         // music end
         this.ended = false;
-        this.video.addEventListener('ended', () => {
-            this.updateBar('played', 1, 'width');
-            if (!loop) {
-                this.ended = true;
-                this.pause();
-                this.trigger('ended');
+        this.video.on('all', 'ended', (i) => {
+            if (i === this.video.videos.length - 1) {
+                this.updateBar('played', 1, 'width');
+                console.log(loop);
+                if (!loop) {
+                    this.ended = true;
+                    this.pause();
+                    this.trigger('ended');
+                }
+                else {
+                    this.video.switch(0);
+                    this.video.play();
+                }
             }
         });
 
-        this.video.addEventListener('play', () => {
+        this.video.on('current', 'play', () => {
             if (this.paused) {
                 this.play();
             }
         });
 
-        this.video.addEventListener('pause', () => {
+        this.video.on('current', 'pause', () => {
             if (!this.paused) {
                 this.pause();
             }
         });
 
         // control volume
-        this.video.volume = parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100;
-
-        // loop
-        this.video.loop = loop;
+        this.video.attr('volume', parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100);
 
         // set duration time
         if (this.video.duration !== 1) {           // compatibility: Android browsers will output 1 at first
-            this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.video.duration ? secondToTime(this.video.duration) : '00:00';
+            this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = this.video.duration ? utils.secondToTime(this.video.duration) : '00:00';
         }
 
         // danmaku
@@ -650,7 +617,7 @@ class DPlayer {
                 token: this.option.danmaku.token,
                 player: this.option.danmaku.id,
                 author: this.option.danmaku.user,
-                time: this.video.currentTime,
+                time: this.video.currentTime(),
                 text: commentInput.value,
                 color: this.element.querySelector('.dplayer-comment-setting-color input:checked').value,
                 type: this.element.querySelector('.dplayer-comment-setting-type input:checked').value
@@ -774,8 +741,8 @@ class DPlayer {
                 else if (this.element.webkitRequestFullscreen) {
                     this.element.webkitRequestFullscreen();
                 }
-                else if (this.video.webkitEnterFullscreen) {   // Safari for iOS
-                    this.video.webkitEnterFullscreen();
+                else if (this.video.attr('webkitEnterFullscreen')) {   // Safari for iOS
+                    this.video.current.webkitEnterFullscreen();
                 }
             }
             else {
@@ -818,20 +785,20 @@ class DPlayer {
                     break;
                 case 37:
                     event.preventDefault();
-                    this.video.currentTime = this.video.currentTime - 5;
+                    this.video.seek(this.video.currentTime() - 5);
                     break;
                 case 39:
                     event.preventDefault();
-                    this.video.currentTime = this.video.currentTime + 5;
+                    this.video.seek(this.video.currentTime() + 5);
                     break;
                 case 38:
                     event.preventDefault();
-                    percentage = this.video.volume + 0.1;
+                    percentage = this.video.attr('volume') + 0.1;
                     this.volume(percentage);
                     break;
                 case 40:
                     event.preventDefault();
-                    percentage = this.video.volume - 0.1;
+                    percentage = this.video.attr('volume') - 0.1;
                     this.volume(percentage);
                     break;
                 }
@@ -896,9 +863,9 @@ class DPlayer {
             const camareIcon = this.element.getElementsByClassName('dplayer-camera-icon')[0];
             camareIcon.addEventListener('click', () => {
                 const canvas = document.createElement("canvas");
-                canvas.width = this.video.videoWidth;
-                canvas.height = this.video.videoHeight;
-                canvas.getContext('2d').drawImage(this.video, 0, 0, canvas.width, canvas.height);
+                canvas.width = this.video.attr('videoWidth');
+                canvas.height = this.video.attr('videoHeight');
+                canvas.getContext('2d').drawImage(this.video.current, 0, 0, canvas.width, canvas.height);
 
                 camareIcon.href = canvas.toDataURL();
                 camareIcon.download = "DPlayer.png";
@@ -913,10 +880,10 @@ class DPlayer {
      */
     play (time) {
         if (Object.prototype.toString.call(time) === '[object Number]') {
-            this.video.currentTime = time;
+            this.video.seek(time);
         }
         this.paused = false;
-        if (this.video.paused) {
+        if (this.video.attr('paused')) {
             this.bezel.innerHTML = svg('play');
             this.bezel.classList.add('dplayer-bezel-transition');
         }
@@ -939,7 +906,7 @@ class DPlayer {
         this.paused = true;
         this.element.classList.remove('dplayer-loading');
 
-        if (!this.video.paused) {
+        if (!this.video.attr('paused')) {
             this.bezel.innerHTML = svg('pause');
             this.bezel.classList.add('dplayer-bezel-transition');
         }
@@ -959,9 +926,9 @@ class DPlayer {
         percentage = percentage > 0 ? percentage : 0;
         percentage = percentage < 1 ? percentage : 1;
         this.updateBar('volume', percentage, 'width');
-        this.video.volume = percentage;
-        if (this.video.muted) {
-            this.video.muted = false;
+        this.video.attr('volume', percentage);
+        if (this.video.attr('muted')) {
+            this.video.attr('muted', false);
         }
         this.switchVolumeIcon();
     }
@@ -970,7 +937,7 @@ class DPlayer {
      * Toggle between play and pause
      */
     toggle () {
-        if (this.video.paused) {
+        if (this.video.attr('paused')) {
             this.play();
         }
         else {
@@ -981,9 +948,9 @@ class DPlayer {
     /**
      * attach event
      */
-    on (name, func) {
-        if (typeof func === 'function') {
-            this.event[name].push(func);
+    on (event, callback) {
+        if (typeof callback === 'function') {
+            this.event[event].push(callback);
         }
     }
 
@@ -1147,6 +1114,7 @@ class DPlayer {
      * @param {Object} video - new video info
      * @param {Object} danmaku - new danmaku info
      */
+    // TODO
     switchVideo (video, danmaku) {
         this.video.src = video.url;
         this.video.poster = video.pic ? video.pic : '';
@@ -1171,7 +1139,7 @@ class DPlayer {
         }
     }
 
-    timeTipsHandler (pbar, timeTips, secondToTime) {
+    timeTipsHandler (pbar, timeTips) {
         // http://stackoverflow.com/questions/1480133/how-can-i-get-an-objects-absolute-position-on-the-page-in-javascript
         const cumulativeOffset = (element) => {
             let top = 0, left = 0;
@@ -1194,7 +1162,7 @@ class DPlayer {
             const { clientX } = e;
             const px = cumulativeOffset(pbar).left;
             const tx = clientX - px;
-            timeTips.innerText = secondToTime(this.video.duration * (tx / pbar.offsetWidth));
+            timeTips.innerText = utils.secondToTime(this.video.duration * (tx / pbar.offsetWidth));
             timeTips.style.left = `${(tx - 20)}px`;
             switch (e.type) {
             case 'mouseenter':

+ 5 - 0
src/DPlayer.scss

@@ -161,6 +161,11 @@
         .dplayer-video {
             width: 100%;
             height: 100%;
+            display: none;
+        }
+
+        .dplayer-video-current {
+            display: block;
         }
     }
 

+ 9 - 4
src/html.js

@@ -1,10 +1,15 @@
 const svg = require('./svg.js');
 
 module.exports = {
-    main: (option, index, tran) => `
+    main: (option, index, tran) => {
+        let videos = ``;
+        for (let i = 0; i < option.video.url.length; i++) {
+            videos += `<video class="dplayer-video ${i === 0 ? `dplayer-video-current"` : ``}" ${option.video.pic ? `poster="${option.video.pic}"` : ``} webkit-playsinline playsinline ${option.screenshot ? `crossorigin="anonymous"` : ``} preload="${option.video.url.length ? 'metadata' : option.preload}" src="${option.video.url[i]}"></video>`;
+        }
+        return `
         <div class="dplayer-mask"></div>
         <div class="dplayer-video-wrap">
-            <video class="dplayer-video" ${option.video.pic ? `poster="${option.video.pic}"` : ``} webkit-playsinline playsinline ${option.screenshot ? `crossorigin="anonymous"` : ``} preload="${option.preload}" src="${option.video.url}"></video>
+            ${videos}
             <div class="dplayer-danmaku">
                 <div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>
             </div>
@@ -154,8 +159,8 @@ module.exports = {
             <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="http://diygod.me/">${tran('About author')}</a></span></div>
             <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer/issues">${tran('DPlayer feedback')}</a></span></div>
             <div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer">${tran('About DPlayer')}</a></span></div>
-        </div>
-    `,
+        </div>`;
+    },
 
     setting: (tran) => ({
         'original': `

+ 3 - 0
src/option.js

@@ -24,6 +24,9 @@ module.exports = (option) => {
             option[defaultKey] = defaultOption[defaultKey];
         }
     }
+    if (Object.prototype.toString.call(option.video.url) !== '[object Array]') {
+        option.video.url = [option.video.url];
+    }
     if (option.video && !option.video.hasOwnProperty('type')) {
         option.video.type = 'auto';
     }

+ 38 - 0
src/utils.js

@@ -0,0 +1,38 @@
+module.exports = {
+
+    /**
+    * Parse second to 00:00 format
+    *
+    * @param {Number} second
+    * @return {String} 00:00 format
+    */
+    secondToTime: (second) => {
+        const add0 = (num) => num < 10 ? '0' + num : '' + num;
+        const min = parseInt(second / 60);
+        const sec = parseInt(second - min * 60);
+        return add0(min) + ':' + add0(sec);
+    },
+
+    /**
+     * control play progress
+     */
+    // get element's view position
+    getElementViewLeft: (element) => {
+        let actualLeft = element.offsetLeft;
+        let current = element.offsetParent;
+        const elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
+        if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
+            while (current !== null) {
+                actualLeft += current.offsetLeft;
+                current = current.offsetParent;
+            }
+        }
+        else {
+            while (current !== null && current !== this.element) {
+                actualLeft += current.offsetLeft;
+                current = current.offsetParent;
+            }
+        }
+        return actualLeft - elementScrollLeft;
+    },
+};

+ 133 - 0
src/video.js

@@ -0,0 +1,133 @@
+class Video {
+    constructor (videos) {
+        this.videos = videos;
+        this.multi = this.videos.length > 1;
+        this.index = 0;
+        this.current = this.videos[this.index];
+
+        this.duration = 0;
+        this.durationArr = [];
+        this.eventAll = [];
+        this.eventCurrent = [];
+
+        this.on('all', 'durationchange', (i, video) => {
+            if (video.duration !== 1) {           // some Android browsers will output 1 at first
+                this.durationArr[i] = video.duration;
+                this.duration = this.durationArr.reduce((sum, cur) => sum + cur);
+            }
+        });
+        this.on('current', 'end', () => {
+            this.switch(this.index + 1);
+        });
+    }
+
+    switch (index, time) {
+        if (this.index !== index) {
+            this.videos[index].classList.add('dplayer-video-current');
+            if (!this.current.paused) {
+                this.videos[index].play();
+            }
+            this.current.classList.remove('dplayer-video-current');
+            this.current.pause();
+
+            this.index = index;
+            this.current = this.videos[this.index];
+            this.videos[index].currentTime = time ? time : 0;
+        }
+        else {
+            this.videos[index].currentTime = time ? time : 0;
+        }
+    }
+
+    // bind event    
+    on (type, event, callback) {
+        if (typeof callback === 'function') {
+            if (type === 'all') {
+                if (!this.eventAll[event]) {
+                    this.eventAll[event] = [];
+                }
+                this.eventAll[event].push(callback);
+            }
+            else {
+                if (!this.eventCurrent[event]) {
+                    this.eventCurrent[event] = [];
+                }
+                this.eventCurrent[event].push(callback);
+            }
+
+            if (['seeking'].indexOf(event) === -1) {
+                for (let i = 0; i < this.videos.length; i++) {
+                    this.videos[i].addEventListener(event, () => {
+                        if (type === 'all' || this.videos[i] === this.current) {
+                            callback(i, this.videos[i]);
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    // trigger event
+    trigger (type, event) {
+        const events = type === 'all' ? this.eventAll : this.eventCurrent;
+        for (let i = 0; i < events[event].length; i++) {
+            events[event][i]();
+        }
+    }
+
+    currentTime () {
+        if (this.durationArr.slice(0, this.index).length) {
+            return this.durationArr.slice(0, this.index).reduce((sum, cur) => sum + cur) + this.current.currentTime;
+        }
+        else {
+            return this.current.currentTime;
+        }
+    }
+
+    seek (time) {
+        time = Math.max(time, 0);
+        time = Math.min(time, this.duration);
+
+        let i = 0;
+        let tmptime = 0;
+        while (tmptime <= time) {
+            tmptime += this.durationArr[i];
+            i++;
+        }
+
+        let currentTime;
+        if (this.durationArr.slice(0, this.index).length) {
+            currentTime = time - this.durationArr.slice(0, i - 1).reduce((sum, cur) => sum + cur);
+        }
+        else {
+            currentTime = time;
+        }
+        
+        this.switch(i - 1, currentTime);
+
+        this.trigger('all', 'seeking');
+    }
+
+    attr (option, value) {
+        if (value !== undefined) {
+            for (let i = 0; i < this.videos.length; i++) {
+                this.videos[i][option] = value;
+            }
+        }
+        return this.current[option];
+    }
+
+    play () {
+        this.current.play();
+    }
+
+    pause () {
+        this.current.pause();
+    }
+
+    toggle () {
+        this.current.paused ? this.play() : this.pause();
+    }
+}
+
+module.exports = Video;

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików