├── LICENSE ├── README.md ├── dist └── DPlayer.min.js ├── src ├── assets │ ├── full-web.svg │ ├── full.svg │ ├── loading.svg │ ├── loop.svg │ ├── pause.svg │ ├── play.svg │ ├── volume-down.svg │ ├── volume-off.svg │ └── volume-up.svg ├── css │ ├── bezel.scss │ ├── controller.scss │ ├── index.scss │ ├── notice.scss │ ├── player.scss │ └── video.scss ├── js │ ├── bar.js │ ├── bezel.js │ ├── controller.js │ ├── events.js │ ├── fullscreen.js │ ├── hotkey.js │ ├── i18n.js │ ├── icons.js │ ├── index.js │ ├── options.js │ ├── player.js │ ├── template.js │ ├── timer.js │ └── utils.js └── template │ ├── player.art │ └── video.art └── webpack └── prod.config.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Karl Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | DPlayer Lite, based on [DPlayer](https://github.com/MoePlayer/DPlayer) 1.26.0 [808dd62](https://github.com/MoePlayer/DPlayer/commit/808dd623d6b3facf321f27f9358c5fe97b3931a3). 3 | 4 | ![dplayer-lite-preview](https://user-images.githubusercontent.com/6196903/33885965-eed74a92-df7f-11e7-9a18-d8e53b522c3f.png) 5 | 6 | **DPlayer Lite Supports:** 7 | 8 | - Streaming Formats 9 | - [HLS](https://github.com/video-dev/hls.js) 10 | - [FLV](https://github.com/Bilibili/flv.js) 11 | - [MPEG DASH](https://github.com/Dash-Industry-Forum/dash.js) 12 | - [WebTorrent](https://github.com/webtorrent/webtorrent) 13 | - Any other custom streaming formats 14 | - Media Formats 15 | - MP4 H.264 16 | - WebM 17 | - Ogg Theora Vorbis 18 | - Features 19 | - Hotkeys 20 | - Loop Control 21 | 22 | ## Usage 23 | 24 | Name|Default|Note 25 | ----|-------|---- 26 | container | document.getElementsByClassName('dplayer')[0] | player container 27 | autoplay | false | not supported in mobile browsers 28 | theme | '#b7daff' | main color 29 | live | false | enable live mode 30 | loop | false | upon reaching the end of the video, automatically seek back to the start 31 | lang | navigator.language.toLowerCase() | values: 'en', 'zh-cn', 'zh-tw' 32 | preload | 'auto' | values: 'none', 'metadata', 'auto' 33 | volume | 1 | default volume 34 | video | undefined | video info 35 | video.url | undefined | video link 36 | video.pic | undefined | video poster 37 | video.type | 'auto' | [HLS support](http://dplayer.js.org/docs/#/?id=hls-support) [FLV support](http://dplayer.js.org/docs/#/?id=flv-support) [MPEG DASH support](http://dplayer.js.org/docs/#/?id=mpeg-dash-support) [WebTorrent support](http://dplayer.js.org/docs/#/?id=webtorrent-support) 38 | mutex | true | pause other players when this player start play 39 | 40 | ## Other 41 | 42 | - [DPlayerHandle for Wordpress](https://github.com/kn007/DPlayerHandle) 43 | - [Demo](https://kn007.net/topics/legends-never-die/) 44 | 45 | 46 | -------------------------------------------------------------------------------- /dist/DPlayer.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("DPlayer",[],t):"object"==typeof exports?exports.DPlayer=t():e.DPlayer=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=23)}([function(e,t){function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"===("undefined"==typeof window?"undefined":n(window))&&(r=window)}e.exports=r},function(e,t,n){"use strict";var r=n(4),i=n.n(r)()((function(e){return e[1]}));i.push([e.i,'.dplayer{position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;line-height:1}.dplayer *{box-sizing:content-box}.dplayer svg{width:100%;height:100%}.dplayer svg circle,.dplayer svg path{fill:#fff}.dplayer:-webkit-full-screen{width:100%;height:100%;background:#000;position:fixed;z-index:100000;left:0;top:0;margin:0;padding:0;transform:translate(0)}.dplayer.dplayer-live .dplayer-bar-wrap,.dplayer.dplayer-live .dplayer-controller .dplayer-icons .dplayer-loop,.dplayer.dplayer-live .dplayer-time{display:none}@media (min-width:900px){.dplayer.dplayer-playing .dplayer-controller,.dplayer.dplayer-playing .dplayer-controller-mask{opacity:0}.dplayer.dplayer-playing:hover .dplayer-controller,.dplayer.dplayer-playing:hover .dplayer-controller-mask{opacity:1}}.dplayer.dplayer-loading .dplayer-bezel .diplayer-loading-icon{display:block}.dplayer.dplayer-hide-controller{cursor:none}.dplayer.dplayer-hide-controller .dplayer-controller,.dplayer.dplayer-hide-controller .dplayer-controller-mask{opacity:0;transform:translateY(100%)}.dplayer.dplayer-fulled{position:fixed;z-index:100000;left:0;top:0;width:100%!important;height:100%!important}.dplayer.dplayer-mobile .dplayer-bar-time,.dplayer.dplayer-mobile .dplayer-controller .dplayer-icons .dplayer-full-in,.dplayer.dplayer-mobile .dplayer-controller .dplayer-icons .dplayer-volume{display:none}.dplayer-web-fullscreen-fix{position:fixed;top:0;left:0;margin:0;padding:0}.dplayer-bezel{position:absolute;left:0;right:0;top:0;bottom:0;font-size:22px;color:#fff;pointer-events:none}.dplayer-bezel .dplayer-bezel-icon{position:absolute;top:50%;left:50%;margin:-26px 0 0 -26px;height:52px;width:52px;padding:12px;box-sizing:border-box;background:rgba(0,0,0,.5);border-radius:50%;opacity:0;pointer-events:none}.dplayer-bezel .dplayer-bezel-icon.dplayer-bezel-transition{-webkit-animation:bezel-hide .5s linear;animation:bezel-hide .5s linear}@-webkit-keyframes bezel-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(2)}}@keyframes bezel-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(2)}}.dplayer-bezel .diplayer-loading-icon{display:none;position:absolute;top:50%;left:50%;margin:-18px 0 0 -18px;height:36px;width:36px;pointer-events:none}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-hide{display:none}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot{-webkit-animation:diplayer-loading-dot-fade .8s ease infinite;animation:diplayer-loading-dot-fade .8s ease infinite;opacity:0;transform-origin:4px 4px}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-7{-webkit-animation-delay:.7s;animation-delay:.7s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-6{-webkit-animation-delay:.6s;animation-delay:.6s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-5{-webkit-animation-delay:.5s;animation-delay:.5s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-4{-webkit-animation-delay:.4s;animation-delay:.4s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-3{-webkit-animation-delay:.3s;animation-delay:.3s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-2{-webkit-animation-delay:.2s;animation-delay:.2s}.dplayer-bezel .diplayer-loading-icon .diplayer-loading-dot.diplayer-loading-dot-1{-webkit-animation-delay:.1s;animation-delay:.1s}@-webkit-keyframes diplayer-loading-dot-fade{0%{opacity:.7;transform:scale(1.2)}50%{opacity:.25;transform:scale(.9)}to{opacity:.25;transform:scale(.85)}}@keyframes diplayer-loading-dot-fade{0%{opacity:.7;transform:scale(1.2)}50%{opacity:.25;transform:scale(.9)}to{opacity:.25;transform:scale(.85)}}.dplayer-controller-mask{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom;height:98px;width:100%}.dplayer-controller,.dplayer-controller-mask{position:absolute;bottom:0;transition:all .3s ease}.dplayer-controller{left:0;right:0;height:41px;padding:0 20px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.dplayer-controller .dplayer-bar-wrap{padding:5px 0;cursor:pointer;position:absolute;bottom:33px;width:calc(100% - 40px);height:3px}.dplayer-controller .dplayer-bar-wrap:hover .dplayer-bar .dplayer-played .dplayer-thumb{transform:scale(1)}.dplayer-controller .dplayer-bar-wrap .dplayer-bar-time{position:absolute;left:0;top:-20px;border-radius:4px;padding:5px 7px;background-color:rgba(0,0,0,.62);color:#fff;font-size:12px;text-align:center;opacity:1;transition:opacity .1s ease-in-out;word-wrap:normal;word-break:normal;z-index:2;pointer-events:none}.dplayer-controller .dplayer-bar-wrap .dplayer-bar-time.hidden{opacity:0}.dplayer-controller .dplayer-bar-wrap .dplayer-bar{position:relative;height:3px;width:100%;background:hsla(0,0%,100%,.2);cursor:pointer}.dplayer-controller .dplayer-bar-wrap .dplayer-bar .dplayer-loaded{background:hsla(0,0%,100%,.4);transition:all .5s ease}.dplayer-controller .dplayer-bar-wrap .dplayer-bar .dplayer-loaded,.dplayer-controller .dplayer-bar-wrap .dplayer-bar .dplayer-played{position:absolute;left:0;top:0;bottom:0;height:3px;will-change:width}.dplayer-controller .dplayer-bar-wrap .dplayer-bar .dplayer-played .dplayer-thumb{position:absolute;top:0;right:5px;margin-top:-4px;margin-right:-10px;height:11px;width:11px;border-radius:50%;cursor:pointer;transition:all .3s ease-in-out;transform:scale(0)}.dplayer-controller .dplayer-icons{height:38px;position:absolute;bottom:0}.dplayer-controller .dplayer-icons.dplayer-icons-left .dplayer-icon{padding:7px}.dplayer-controller .dplayer-icons.dplayer-icons-right{right:20px}.dplayer-controller .dplayer-icons.dplayer-icons-right .dplayer-icon{padding:8px}.dplayer-controller .dplayer-icons .dplayer-live-badge,.dplayer-controller .dplayer-icons .dplayer-time{line-height:38px;color:#eee;text-shadow:0 0 2px rgba(0,0,0,.5);vertical-align:middle;font-size:13px;cursor:default}.dplayer-controller .dplayer-icons .dplayer-live-dot{display:inline-block;width:6px;height:6px;vertical-align:4%;margin-right:5px;content:"";border-radius:6px}.dplayer-controller .dplayer-icons .dplayer-icon{width:40px;height:100%;border:none;background-color:transparent;outline:none;position:relative;cursor:pointer;vertical-align:middle;box-sizing:border-box;display:inline-block}.dplayer-controller .dplayer-icons .dplayer-icon .dplayer-icon-content{transition:all .2s ease-in-out;opacity:.8}.dplayer-controller .dplayer-icons .dplayer-icon:hover .dplayer-icon-content{opacity:1}.dplayer-controller .dplayer-icons .dplayer-icon.dplayer-volume-icon{width:43px}.dplayer-controller .dplayer-icons .dplayer-volume{position:relative;display:inline-block;cursor:pointer;height:100%}.dplayer-controller .dplayer-icons .dplayer-volume:hover .dplayer-volume-bar-wrap .dplayer-volume-bar{width:45px}.dplayer-controller .dplayer-icons .dplayer-volume:hover .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner .dplayer-thumb{transform:scale(1)}.dplayer-controller .dplayer-icons .dplayer-volume.dplayer-volume-active .dplayer-volume-bar-wrap .dplayer-volume-bar{width:45px}.dplayer-controller .dplayer-icons .dplayer-volume.dplayer-volume-active .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner .dplayer-thumb{transform:scale(1)}.dplayer-controller .dplayer-icons .dplayer-volume .dplayer-volume-bar-wrap{display:inline-block;margin:0 15px 0 -5px;vertical-align:middle;height:100%}.dplayer-controller .dplayer-icons .dplayer-volume .dplayer-volume-bar-wrap .dplayer-volume-bar{position:relative;top:17px;width:0;height:3px;background:#aaa;transition:all .3s ease-in-out}.dplayer-controller .dplayer-icons .dplayer-volume .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner{position:absolute;bottom:0;left:0;height:100%;transition:all .1s ease;will-change:width}.dplayer-controller .dplayer-icons .dplayer-volume .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner .dplayer-thumb{position:absolute;top:0;right:5px;margin-top:-4px;margin-right:-10px;height:11px;width:11px;border-radius:50%;cursor:pointer;transition:all .3s ease-in-out;transform:scale(0)}.dplayer-controller .dplayer-icons .dplayer-full,.dplayer-controller .dplayer-icons .dplayer-full-in,.dplayer-controller .dplayer-icons .dplayer-loop{display:inline-block;height:100%}.dplayer-notice{opacity:0;position:absolute;bottom:60px;left:20px;font-size:14px;border-radius:2px;background:rgba(28,28,28,.9);padding:7px 20px;transition:all .3s ease-in-out;overflow:hidden;color:#fff;pointer-events:none}.dplayer-mask{position:absolute;top:0;bottom:0;left:0;right:0;z-index:1;display:none}.dplayer-mask.dplayer-mask-show{display:block}.dplayer-video-wrap{position:relative;background:#000;font-size:0;width:100%;height:100%}.dplayer-video-wrap .dplayer-video{width:100%;height:100%;display:none}.dplayer-video-wrap .dplayer-video-current{display:block}',""]),t.a=i},function(e,t,n){"use strict";e.exports=n(21)},function(e,t,n){"use strict";var r,i=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),a=[];function l(e){for(var t=-1,n=0;n'},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t,n){var r=n(2);e.exports=function(e){"use strict";var t="",i=function(e){return t+=e},o=(e=e||{}).video,a=e.icons,l=r.$escape,s=e.options,c=e.tran;return t+='
\n
\n ',i(n(22)(o)),t+='\n
\n \n ',t+=a.loading,t+='\n
\n
\n
\n
\n
\n \n
\n \n
\n
\n
\n \n
\n
\n
\n
\n \n 0:00 / 0:00\n \n ',s.live&&(t+='\n ',t+=l(c("Live")),t+="\n "),t+='\n
\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n \n
\n
\n
\n \n
\n
\n
\n
\n
'}},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new o(i.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new o(i.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(19),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(0))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,i,o,a,l,s=1,c={},d=!1,p=e.document,u=Object.getPrototypeOf&&Object.getPrototypeOf(e);u=u&&u.setTimeout?u:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick((function(){f(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){f(e.data)},r=function(e){o.port2.postMessage(e)}):p&&"onreadystatechange"in p.createElement("script")?(i=p.documentElement,r=function(e){var t=p.createElement("script");t.onreadystatechange=function(){f(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):r=function(e){setTimeout(f,0,e)}:(a="setImmediate$"+Math.random()+"$",l=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&f(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",l,!1):e.attachEvent("onmessage",l),r=function(t){e.postMessage(a+t,"*")}),u.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n]/;r.$escape=function(e){return function(e){var t=""+e,n=i.exec(t);if(!n)return e;var r="",o=void 0,a=void 0,l=void 0;for(o=n.index,a=0;o"}},function(e,t,n){"use strict";n.r(t);var r=n(3),i=n.n(r),o=n(1),a={insert:"head",singleton:!1},l=(i()(o.a,a),o.a.locals,n(5)),s=/mobile/i.test(window.navigator.userAgent),c={secondToTime:function(e){if(0===(e=e||0)||e===1/0||"NaN"===e.toString())return"00:00";var t=Math.floor(e/3600),n=Math.floor((e-3600*t)/60),r=Math.floor(e-3600*t-60*n);return(t>0?[t,n,r]:[n,r]).map((function(e){return e<10?"0"+e:""+e})).join(":")},getElementViewLeft:function(e){var t=e.offsetLeft,n=e.offsetParent,r=document.body.scrollLeft+document.documentElement.scrollLeft;if(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement)for(;null!==n&&n!==e;)t+=n.offsetLeft,n=n.offsetParent;else for(;null!==n;)t+=n.offsetLeft,n=n.offsetParent;return t-r},getBoundingClientRectViewLeft:function(e){var t=window.scrollY||window.pageYOffset||document.body.scrollTop+(document.documentElement&&document.documentElement.scrollTop||0);if(e.getBoundingClientRect){if("number"!=typeof this.getBoundingClientRectViewLeft.offset){var n=document.createElement("div");n.style.cssText="position:absolute;top:0;left:0;",document.body.appendChild(n),this.getBoundingClientRectViewLeft.offset=-n.getBoundingClientRect().top-t,document.body.removeChild(n),n=null}var r=e.getBoundingClientRect(),i=this.getBoundingClientRectViewLeft.offset;return r.left+i}return this.getElementViewLeft(e)},getScrollPosition:function(){return{left:window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0,top:window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0}},setScrollPosition:function(e){var t=e.left,n=void 0===t?0:t,r=e.top,i=void 0===r?0:r;this.isFirefox?(document.documentElement.scrollLeft=n,document.documentElement.scrollTop=i):window.scrollTo(n,i)},isMobile:s,isFirefox:/firefox/i.test(window.navigator.userAgent),isChrome:/chrome/i.test(window.navigator.userAgent),nameMap:{dragStart:s?"touchstart":"mousedown",dragMove:s?"touchmove":"mousemove",dragEnd:s?"touchend":"mouseup"}};var d={"zh-cn":{"Video load failed":"视频加载失败","Go forward":"快进","Go back":"快退",seconds:"秒",Volume:"音量",Live:"直播"},"zh-tw":{"Video load failed":"視頻加載失敗","Go forward":"快進","Go back":"快退",seconds:"秒",Volume:"音量",Live:"直播"}},p=function(e){var t=this;this.lang=e,this.tran=function(e){return d[t.lang]&&d[t.lang][e]?d[t.lang][e]:e}},u=n(8),y=n.n(u),f=n(9),h=n.n(f),v=n(10),m=n.n(v),g=n(11),b=n.n(g),w=n(12),k=n.n(w),x=n(13),T=n.n(x),E=n(14),L=n.n(E),S=n(15),M=n.n(S),z=n(16),B=n.n(z),q={play:y.a,pause:h.a,loop:m.a,volumeUp:b.a,volumeDown:k.a,volumeOff:T.a,full:L.a,fullWeb:M.a,loading:B.a},F=n(17),j=n.n(F);function _(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"browser";switch(e){case"browser":return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement;case"web":return this.player.container.classList.contains("dplayer-fulled")}}},{key:"request",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"browser",t="browser"===e?"web":"browser",n=this.isFullScreen(t);switch(n||(this.lastScrollPosition=c.getScrollPosition()),e){case"browser":this.player.container.requestFullscreen?this.player.container.requestFullscreen():this.player.container.mozRequestFullScreen?this.player.container.mozRequestFullScreen():this.player.container.webkitRequestFullscreen?this.player.container.webkitRequestFullscreen():this.player.video.webkitEnterFullscreen?this.player.video.webkitEnterFullscreen():this.player.video.webkitEnterFullScreen?this.player.video.webkitEnterFullScreen():this.player.container.msRequestFullscreen&&this.player.container.msRequestFullscreen();break;case"web":this.player.container.classList.add("dplayer-fulled"),document.body.classList.add("dplayer-web-fullscreen-fix"),this.player.events.trigger("webfullscreen")}n&&this.cancel(t)}},{key:"cancel",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"browser";switch(e){case"browser":document.cancelFullScreen?document.cancelFullScreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitCancelFullScreen?document.webkitCancelFullScreen():document.webkitCancelFullscreen?document.webkitCancelFullscreen():document.msCancelFullScreen?document.msCancelFullScreen():document.msExitFullscreen&&document.msExitFullscreen();break;case"web":this.player.container.classList.remove("dplayer-fulled"),document.body.classList.remove("dplayer-web-fullscreen-fix"),this.player.events.trigger("webfullscreen_cancel")}}},{key:"toggle",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"browser";this.isFullScreen(e)?this.cancel(e):this.request(e)}}])&&P(t.prototype,n),r&&P(t,r),e}();function W(e,t){for(var n=0;nt&&!e.player.video.paused&&(e.player.container.classList.remove("dplayer-loading"),r=!1),t=n)}),100)}},{key:"enable",value:function(e){this["enable".concat(e,"Checker")]=!0}},{key:"disable",value:function(e){this["enable".concat(e,"Checker")]=!1}},{key:"destroy",value:function(){var e=this;this.types.map((function(t){return e["enable".concat(t,"Checker")]=!1,e["".concat(t,"Checker")]&&clearInterval(e["".concat(t,"Checker")]),t}))}}])&&H(t.prototype,n),r&&H(t,r),e}();function D(e,t){for(var n=0;ne.player.template.playedBarWrap.offsetWidth)return;var i=e.player.video.duration*(r/e.player.template.playedBarWrap.offsetWidth);e.player.template.playedBarTime.style.left="".concat(r-(i>=3600?25:20),"px"),e.player.template.playedBarTime.innerText=c.secondToTime(i),e.player.template.playedBarTime.classList.remove("hidden")}e.setAutoHide()})),c.isMobile||(this.player.template.playedBarWrap.addEventListener("mouseenter",(function(){e.player.video.duration&&e.player.template.playedBarTime.classList.remove("hidden")})),this.player.template.playedBarWrap.addEventListener("mouseleave",(function(){e.player.video.duration&&e.player.template.playedBarTime.classList.add("hidden")})))}},{key:"initFullButton",value:function(){var e=this;this.player.template.browserFullButton.addEventListener("click",(function(){e.player.fullScreen.toggle("browser")})),this.player.template.webFullButton.addEventListener("click",(function(){e.player.fullScreen.toggle("web")}))}},{key:"initVolumeButton",value:function(){var e=this,t=function(t){var n=t||window.event,r=((n.clientX||n.changedTouches[0].clientX)-c.getBoundingClientRectViewLeft(e.player.template.volumeBarWrap)-5.5)/35;e.player.volume(r)},n=function n(){document.removeEventListener(c.nameMap.dragEnd,n),document.removeEventListener(c.nameMap.dragMove,t),e.player.template.volumeButton.classList.remove("dplayer-volume-active")};this.player.template.volumeBarWrapWrap.addEventListener("click",(function(t){var n=t||window.event,r=((n.clientX||n.changedTouches[0].clientX)-c.getBoundingClientRectViewLeft(e.player.template.volumeBarWrap)-5.5)/35;e.player.volume(r)})),this.player.template.volumeBarWrapWrap.addEventListener(c.nameMap.dragStart,(function(){document.addEventListener(c.nameMap.dragMove,t),document.addEventListener(c.nameMap.dragEnd,n),e.player.template.volumeButton.classList.add("dplayer-volume-active")})),this.player.template.volumeButtonIcon.addEventListener("click",(function(){e.player.video.muted?(e.player.video.muted=!1,e.player.switchVolumeIcon(),e.player.bar.set("volume",e.player.volume(),"width")):(e.player.video.muted=!0,e.player.template.volumeIcon.innerHTML=q.volumeOff,e.player.bar.set("volume",0,"width"))}))}},{key:"setAutoHide",value:function(){var e=this;this.show(),clearTimeout(this.autoHideTimer),this.autoHideTimer=setTimeout((function(){e.player.video.played.length&&e.hide()}),3e3)}},{key:"show",value:function(){this.player.container.classList.remove("dplayer-hide-controller")}},{key:"hide",value:function(){this.player.container.classList.add("dplayer-hide-controller")}},{key:"isShow",value:function(){return!this.player.container.classList.contains("dplayer-hide-controller")}},{key:"toggle",value:function(){this.isShow()?this.hide():this.show()}},{key:"destroy",value:function(){clearTimeout(this.autoHideTimer)}}])&&N(t.prototype,n),r&&N(t,r),e}();var $=function e(t){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),document.addEventListener("keydown",(function(e){if(t.focus){var n,r=e||window.event;switch(r.keyCode){case 27:t.fullScreen.isFullScreen("web")&&t.fullScreen.cancel("web");break;case 32:r.preventDefault(),t.toggle(),t.controller.setAutoHide();break;case 37:if(r.preventDefault(),t.options.live)break;t.seek(t.video.currentTime-3,!0);break;case 39:if(r.preventDefault(),t.options.live)break;t.seek(t.video.currentTime+3,!0);break;case 38:r.preventDefault(),n=t.volume()+.01,t.volume(n,!0);break;case 40:r.preventDefault(),n=t.volume()-.01,t.volume(n,!0)}}}))};function G(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Y(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function J(e,t){for(var n=0;ne&&t&&this.notice("".concat(this.tran("Go back")," ").concat((this.video.currentTime-e).toFixed(0)," ").concat(this.tran("seconds"))),this.video.currentTime=e,this.bar.set("played",e/this.video.duration,"width"),this.template.ptime.innerHTML=c.secondToTime(e)}},{key:"play",value:function(e){var t=this;if(this.paused=!1,this.video.paused&&this.bezel.switch(q.play),this.template.playButton.innerHTML=q.pause,e||l.a.resolve(this.video.play()).catch((function(){t.pause()})).then((function(){})),this.timer.enable("loading"),this.container.classList.add("dplayer-playing"),this.options.mutex)for(var n=0;n=.8?this.template.volumeIcon.innerHTML=q.volumeUp:this.volume()>0?this.template.volumeIcon.innerHTML=q.volumeDown:this.template.volumeIcon.innerHTML=q.volumeOff}},{key:"volume",value:function(e,t){return e=parseFloat(e),isNaN(e)||(e=Math.max(e,0),e=Math.min(e,1),this.bar.set("volume",e,"width"),t&&this.notice("".concat(this.tran("Volume")," ").concat((100*e).toFixed(0),"%")),this.video.volume=e,this.video.muted&&(this.video.muted=!1),this.switchVolumeIcon()),this.video.volume}},{key:"toggle",value:function(){this.video.paused?this.play():this.pause()}},{key:"on",value:function(e,t){this.events.on(e,t)}},{key:"initMSE",value:function(e,t){var n=this;if(this.type=t,this.options.video.customType&&this.options.video.customType[t])"[object Function]"===Object.prototype.toString.call(this.options.video.customType[t])?this.options.video.customType[t](this.video,this):console.error("Illegal customType: ".concat(t));else switch("auto"===this.type&&(/m3u8(#|\?|$)/i.exec(e.src)?this.type="hls":/.flv(#|\?|$)/i.exec(e.src)?this.type="flv":/.mpd(#|\?|$)/i.exec(e.src)?this.type="dash":this.type="normal"),"hls"===this.type&&(e.canPlayType("application/x-mpegURL")||e.canPlayType("application/vnd.apple.mpegURL"))&&(this.type="normal"),this.type){case"hls":if(window.Hls)if(window.Hls.isSupported()){var r=this.options.pluginOptions.hls,i=new window.Hls(r);this.plugins.hls=i,i.loadSource(e.src),i.attachMedia(e),this.events.on("destroy",(function(){i.destroy(),delete n.plugins.hls}))}else this.notice("Error: Hls is not supported.");else this.notice("Error: Can't find Hls.");break;case"flv":if(window.flvjs)if(window.flvjs.isSupported()){var o=window.flvjs.createPlayer(Object.assign(this.options.pluginOptions.flv.mediaDataSource||{},{type:"flv",url:e.src}),this.options.pluginOptions.flv.config);this.plugins.flvjs=o,o.attachMediaElement(e),o.load(),this.events.on("destroy",(function(){o.unload(),o.detachMediaElement(),o.destroy(),delete n.plugins.flvjs}))}else this.notice("Error: flvjs is not supported.");else this.notice("Error: Can't find flvjs.");break;case"dash":if(window.dashjs){var a=window.dashjs.MediaPlayer().create().initialize(e,e.src,!1),l=this.options.pluginOptions.dash;a.updateSettings(l),this.plugins.dash=a,this.events.on("destroy",(function(){window.dashjs.MediaPlayer().reset(),delete n.plugins.dash}))}else this.notice("Error: Can't find dashjs.");break;case"webtorrent":if(window.WebTorrent)if(window.WebTorrent.WEBRTC_SUPPORT){this.container.classList.add("dplayer-loading");var s=this.options.pluginOptions.webtorrent,c=new window.WebTorrent(s);this.plugins.webtorrent=c;var d=e.src;e.src="",e.preload="metadata",e.addEventListener("durationchange",(function(){return n.container.classList.remove("dplayer-loading")}),{once:!0}),c.add(d,(function(e){e.files.find((function(e){return e.name.endsWith(".mp4")})).renderTo(n.video,{autoplay:n.options.autoplay,controls:!1})})),this.events.on("destroy",(function(){c.remove(d),c.destroy(),delete n.plugins.webtorrent}))}else this.notice("Error: Webtorrent is not supported.");else this.notice("Error: Can't find Webtorrent.")}}},{key:"initVideo",value:function(e,t){var n=this;this.initMSE(e,t),this.on("durationchange",(function(){1!==e.duration&&e.duration!==1/0&&(n.template.dtime.innerHTML=c.secondToTime(e.duration))})),this.on("progress",(function(){var t=e.buffered.length?e.buffered.end(e.buffered.length-1)/e.duration:0;n.bar.set("loaded",t,"width")})),this.on("error",(function(){n.video.error&&n.tran&&n.notice&&"webtorrent"!==n.type&&n.notice(n.tran("Video load failed"),-1)})),this.on("ended",(function(){n.bar.set("played",1,"width"),n.loop?(n.seek(0),n.play()):n.pause()})),this.on("play",(function(){n.paused&&n.play(!0)})),this.on("pause",(function(){n.paused||n.pause(!0)})),this.on("timeupdate",(function(){n.bar.set("played",n.video.currentTime/n.video.duration,"width");var e=c.secondToTime(n.video.currentTime);n.template.ptime.innerHTML!==e&&(n.template.ptime.innerHTML=e)}));for(var r=function(t){e.addEventListener(n.events.videoEvents[t],(function(){n.events.trigger(n.events.videoEvents[t])}))},i=0;i1&&void 0!==arguments[1]?arguments[1]:1500,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:.8;this.template.notice.innerHTML=e,this.template.notice.style.opacity=r,this.noticeTime&&clearTimeout(this.noticeTime),this.events.trigger("notice_show",e),n>0&&(this.noticeTime=setTimeout((function(){t.template.notice.style.opacity=0,t.events.trigger("notice_hide")}),n))}},{key:"resize",value:function(){this.events.trigger("resize")}},{key:"destroy",value:function(){Q.splice(Q.indexOf(this),1),this.pause(),this.controller.destroy(),this.timer.destroy(),this.video.src="",this.container.innerHTML="",this.events.trigger("destroy")}}])&&J(t.prototype,n),r&&J(t,r),e}();t.default=Z}]).default})); -------------------------------------------------------------------------------- /src/assets/full-web.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/loop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/volume-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/volume-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/volume-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/css/bezel.scss: -------------------------------------------------------------------------------- 1 | .dplayer-bezel { 2 | position: absolute; 3 | left: 0; 4 | right: 0; 5 | top: 0; 6 | bottom: 0; 7 | font-size: 22px; 8 | color: #fff; 9 | pointer-events: none; 10 | .dplayer-bezel-icon { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | margin: -26px 0 0 -26px; 15 | height: 52px; 16 | width: 52px; 17 | padding: 12px; 18 | box-sizing: border-box; 19 | background: rgba(0, 0, 0, .5); 20 | border-radius: 50%; 21 | opacity: 0; 22 | pointer-events: none; 23 | &.dplayer-bezel-transition { 24 | animation: bezel-hide .5s linear; 25 | } 26 | @keyframes bezel-hide { 27 | from { 28 | opacity: 1; 29 | transform: scale(1); 30 | } 31 | to { 32 | opacity: 0; 33 | transform: scale(2); 34 | } 35 | } 36 | } 37 | .diplayer-loading-icon { 38 | display: none; 39 | position: absolute; 40 | top: 50%; 41 | left: 50%; 42 | margin: -18px 0 0 -18px; 43 | height: 36px; 44 | width: 36px; 45 | pointer-events: none; 46 | .diplayer-loading-hide { 47 | display: none; 48 | } 49 | .diplayer-loading-dot { 50 | animation: diplayer-loading-dot-fade .8s ease infinite; 51 | opacity: 0; 52 | transform-origin: 4px 4px; 53 | @for $i from 7 through 1 { 54 | &.diplayer-loading-dot-#{$i} { 55 | animation-delay: .1s * $i; 56 | } 57 | } 58 | } 59 | @keyframes diplayer-loading-dot-fade { 60 | 0% { 61 | opacity: .7; 62 | transform: scale(1.2, 1.2) 63 | } 64 | 50% { 65 | opacity: .25; 66 | transform: scale(.9, .9) 67 | } 68 | to { 69 | opacity: .25; 70 | transform: scale(.85, .85) 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/css/controller.scss: -------------------------------------------------------------------------------- 1 | .dplayer-controller-mask { 2 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==) repeat-x bottom; 3 | height: 98px; 4 | width: 100%; 5 | position: absolute; 6 | bottom: 0; 7 | transition: all 0.3s ease; 8 | } 9 | 10 | .dplayer-controller { 11 | position: absolute; 12 | bottom: 0; 13 | left: 0; 14 | right: 0; 15 | height: 41px; 16 | padding: 0 20px; 17 | user-select: none; 18 | transition: all 0.3s ease; 19 | .dplayer-bar-wrap { 20 | padding: 5px 0; 21 | cursor: pointer; 22 | position: absolute; 23 | bottom: 33px; 24 | width: calc(100% - 40px); 25 | height: 3px; 26 | &:hover { 27 | .dplayer-bar .dplayer-played .dplayer-thumb { 28 | transform: scale(1); 29 | } 30 | } 31 | .dplayer-bar-time { 32 | &.hidden { 33 | opacity: 0; 34 | } 35 | position: absolute; 36 | left: 0px; 37 | top: -20px; 38 | border-radius: 4px; 39 | padding: 5px 7px; 40 | background-color: rgba(0, 0, 0, 0.62); 41 | color: #fff; 42 | font-size: 12px; 43 | text-align: center; 44 | opacity: 1; 45 | transition: opacity .1s ease-in-out; 46 | word-wrap: normal; 47 | word-break: normal; 48 | z-index: 2; 49 | pointer-events: none; 50 | } 51 | .dplayer-bar { 52 | position: relative; 53 | height: 3px; 54 | width: 100%; 55 | background: rgba(255, 255, 255, .2); 56 | cursor: pointer; 57 | .dplayer-loaded { 58 | position: absolute; 59 | left: 0; 60 | top: 0; 61 | bottom: 0; 62 | background: rgba(255, 255, 255, .4); 63 | height: 3px; 64 | transition: all 0.5s ease; 65 | will-change: width; 66 | } 67 | .dplayer-played { 68 | position: absolute; 69 | left: 0; 70 | top: 0; 71 | bottom: 0; 72 | height: 3px; 73 | will-change: width; 74 | .dplayer-thumb { 75 | position: absolute; 76 | top: 0; 77 | right: 5px; 78 | margin-top: -4px; 79 | margin-right: -10px; 80 | height: 11px; 81 | width: 11px; 82 | border-radius: 50%; 83 | cursor: pointer; 84 | transition: all .3s ease-in-out; 85 | transform: scale(0); 86 | } 87 | } 88 | } 89 | } 90 | .dplayer-icons { 91 | height: 38px; 92 | position: absolute; 93 | bottom: 0; 94 | &.dplayer-icons-left { 95 | .dplayer-icon { 96 | padding: 7px; 97 | } 98 | } 99 | &.dplayer-icons-right { 100 | right: 20px; 101 | .dplayer-icon { 102 | padding: 8px; 103 | } 104 | } 105 | .dplayer-time, 106 | .dplayer-live-badge { 107 | line-height: 38px; 108 | color: #eee; 109 | text-shadow: 0 0 2px rgba(0, 0, 0, .5); 110 | vertical-align: middle; 111 | font-size: 13px; 112 | cursor: default; 113 | } 114 | .dplayer-live-dot { 115 | display: inline-block; 116 | width: 6px; 117 | height: 6px; 118 | vertical-align: 4%; 119 | margin-right: 5px; 120 | content: ''; 121 | border-radius: 6px; 122 | } 123 | .dplayer-icon { 124 | width: 40px; 125 | height: 100%; 126 | border: none; 127 | background-color: transparent; 128 | outline: none; 129 | position: relative; 130 | cursor: pointer; 131 | vertical-align: middle; 132 | box-sizing: border-box; 133 | display: inline-block; 134 | .dplayer-icon-content { 135 | transition: all .2s ease-in-out; 136 | opacity: .8; 137 | } 138 | &:hover { 139 | .dplayer-icon-content { 140 | opacity: 1; 141 | } 142 | } 143 | &.dplayer-volume-icon { 144 | width: 43px; 145 | } 146 | } 147 | .dplayer-volume { 148 | position: relative; 149 | display: inline-block; 150 | cursor: pointer; 151 | height: 100%; 152 | &:hover { 153 | .dplayer-volume-bar-wrap .dplayer-volume-bar { 154 | width: 45px; 155 | } 156 | .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner .dplayer-thumb { 157 | transform: scale(1); 158 | } 159 | } 160 | &.dplayer-volume-active { 161 | .dplayer-volume-bar-wrap .dplayer-volume-bar { 162 | width: 45px; 163 | } 164 | .dplayer-volume-bar-wrap .dplayer-volume-bar .dplayer-volume-bar-inner .dplayer-thumb { 165 | transform: scale(1); 166 | } 167 | } 168 | .dplayer-volume-bar-wrap { 169 | display: inline-block; 170 | margin: 0 15px 0 -5px; 171 | vertical-align: middle; 172 | height: 100%; 173 | .dplayer-volume-bar { 174 | position: relative; 175 | top: 17px; 176 | width: 0; 177 | height: 3px; 178 | background: #aaa; 179 | transition: all 0.3s ease-in-out; 180 | .dplayer-volume-bar-inner { 181 | position: absolute; 182 | bottom: 0; 183 | left: 0; 184 | height: 100%; 185 | transition: all 0.1s ease; 186 | will-change: width; 187 | .dplayer-thumb { 188 | position: absolute; 189 | top: 0; 190 | right: 5px; 191 | margin-top: -4px; 192 | margin-right: -10px; 193 | height: 11px; 194 | width: 11px; 195 | border-radius: 50%; 196 | cursor: pointer; 197 | transition: all .3s ease-in-out; 198 | transform: scale(0); 199 | } 200 | } 201 | } 202 | } 203 | } 204 | .dplayer-loop, 205 | .dplayer-full-in, 206 | .dplayer-full { 207 | display: inline-block; 208 | height: 100%; 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/css/index.scss: -------------------------------------------------------------------------------- 1 | @import './player'; 2 | @import './bezel'; 3 | @import './controller'; 4 | @import './notice'; 5 | @import './video'; -------------------------------------------------------------------------------- /src/css/notice.scss: -------------------------------------------------------------------------------- 1 | .dplayer-notice { 2 | opacity: 0; 3 | position: absolute; 4 | bottom: 60px; 5 | left: 20px; 6 | font-size: 14px; 7 | border-radius: 2px; 8 | background: rgba(28, 28, 28, 0.9); 9 | padding: 7px 20px; 10 | transition: all .3s ease-in-out; 11 | overflow: hidden; 12 | color: #fff; 13 | pointer-events: none; 14 | } -------------------------------------------------------------------------------- /src/css/player.scss: -------------------------------------------------------------------------------- 1 | .dplayer { 2 | position: relative; 3 | overflow: hidden; 4 | user-select: none; 5 | line-height: 1; 6 | 7 | * { 8 | box-sizing: content-box; 9 | } 10 | 11 | svg { 12 | width: 100%; 13 | height: 100%; 14 | 15 | path, 16 | circle { 17 | fill: #fff; 18 | } 19 | } 20 | 21 | &:-webkit-full-screen { 22 | width: 100%; 23 | height: 100%; 24 | background: #000; 25 | position: fixed; 26 | z-index: 100000; 27 | left: 0; 28 | top: 0; 29 | margin: 0; 30 | padding: 0; 31 | transform: translate(0, 0); 32 | } 33 | 34 | &.dplayer-live { 35 | .dplayer-time { 36 | display: none; 37 | } 38 | .dplayer-bar-wrap { 39 | display: none; 40 | } 41 | .dplayer-controller .dplayer-icons { 42 | .dplayer-loop { 43 | display: none; 44 | } 45 | } 46 | } 47 | 48 | &.dplayer-playing { 49 | @media (min-width: 900px) { 50 | .dplayer-controller-mask { 51 | opacity: 0; 52 | } 53 | .dplayer-controller { 54 | opacity: 0; 55 | } 56 | 57 | &:hover { 58 | .dplayer-controller-mask { 59 | opacity: 1; 60 | } 61 | .dplayer-controller { 62 | opacity: 1; 63 | } 64 | } 65 | } 66 | } 67 | 68 | &.dplayer-loading { 69 | .dplayer-bezel .diplayer-loading-icon { 70 | display: block; 71 | } 72 | } 73 | 74 | &.dplayer-hide-controller { 75 | cursor: none; 76 | 77 | .dplayer-controller-mask { 78 | opacity: 0; 79 | transform: translateY(100%); 80 | } 81 | .dplayer-controller { 82 | opacity: 0; 83 | transform: translateY(100%); 84 | } 85 | } 86 | 87 | &.dplayer-fulled { 88 | position: fixed; 89 | z-index: 100000; 90 | left: 0; 91 | top: 0; 92 | width: 100% !important; 93 | height: 100% !important; 94 | } 95 | 96 | &.dplayer-mobile { 97 | .dplayer-controller .dplayer-icons { 98 | .dplayer-volume, 99 | .dplayer-full-in { 100 | display: none; 101 | } 102 | } 103 | 104 | .dplayer-bar-time { 105 | display: none; 106 | } 107 | } 108 | } 109 | 110 | // To hide scroll bar, apply this class to 111 | .dplayer-web-fullscreen-fix { 112 | position: fixed; 113 | top: 0; 114 | left: 0; 115 | margin: 0; 116 | padding: 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/css/video.scss: -------------------------------------------------------------------------------- 1 | .dplayer-mask { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | z-index: 1; 8 | display: none; 9 | &.dplayer-mask-show { 10 | display: block; 11 | } 12 | } 13 | 14 | .dplayer-video-wrap { 15 | position: relative; 16 | background: #000; 17 | font-size: 0; 18 | width: 100%; 19 | height: 100%; 20 | .dplayer-video { 21 | width: 100%; 22 | height: 100%; 23 | display: none; 24 | } 25 | .dplayer-video-current { 26 | display: block; 27 | } 28 | } -------------------------------------------------------------------------------- /src/js/bar.js: -------------------------------------------------------------------------------- 1 | class Bar { 2 | constructor(template) { 3 | this.elements = {}; 4 | this.elements.volume = template.volumeBar; 5 | this.elements.played = template.playedBar; 6 | this.elements.loaded = template.loadedBar; 7 | } 8 | 9 | /** 10 | * Update progress 11 | * 12 | * @param {String} type - Point out which bar it is 13 | * @param {Number} percentage 14 | * @param {String} direction - Point out the direction of this bar, Should be height or width 15 | */ 16 | set(type, percentage, direction) { 17 | percentage = Math.max(percentage, 0); 18 | percentage = Math.min(percentage, 1); 19 | this.elements[type].style[direction] = percentage * 100 + '%'; 20 | } 21 | 22 | get(type) { 23 | return parseFloat(this.elements[type].style.width) / 100; 24 | } 25 | } 26 | 27 | export default Bar; -------------------------------------------------------------------------------- /src/js/bezel.js: -------------------------------------------------------------------------------- 1 | class Bezel { 2 | constructor(container) { 3 | this.container = container; 4 | 5 | this.container.addEventListener('animationend', () => { 6 | this.container.classList.remove('dplayer-bezel-transition'); 7 | }); 8 | } 9 | 10 | switch(icon) { 11 | this.container.innerHTML = icon; 12 | this.container.classList.add('dplayer-bezel-transition'); 13 | } 14 | } 15 | 16 | export default Bezel; -------------------------------------------------------------------------------- /src/js/controller.js: -------------------------------------------------------------------------------- 1 | import utils from './utils'; 2 | import Icons from './icons'; 3 | 4 | class Controller { 5 | constructor(player) { 6 | this.player = player; 7 | 8 | this.autoHideTimer = 0; 9 | if (!utils.isMobile) { 10 | this.player.container.addEventListener('mousemove', () => { 11 | this.setAutoHide(); 12 | }); 13 | } 14 | this.player.container.addEventListener('click', () => { 15 | this.setAutoHide(); 16 | }); 17 | this.player.on('play', () => { 18 | this.setAutoHide(); 19 | }); 20 | this.player.on('pause', () => { 21 | this.setAutoHide(); 22 | }); 23 | 24 | this.initPlayButton(); 25 | this.initPlayedBar(); 26 | this.initFullButton(); 27 | if (!utils.isMobile) { 28 | this.initVolumeButton(); 29 | } 30 | } 31 | 32 | initPlayButton() { 33 | this.player.template.playButton.addEventListener('click', () => { 34 | this.player.toggle(); 35 | }); 36 | 37 | if (!utils.isMobile) { 38 | this.player.template.videoWrap.addEventListener('click', () => { 39 | this.player.toggle(); 40 | }); 41 | this.player.template.controllerMask.addEventListener('click', () => { 42 | this.player.toggle(); 43 | }); 44 | } else { 45 | this.player.template.videoWrap.addEventListener('click', () => { 46 | this.toggle(); 47 | }); 48 | this.player.template.controllerMask.addEventListener('click', () => { 49 | this.toggle(); 50 | }); 51 | } 52 | } 53 | 54 | initPlayedBar() { 55 | const thumbMove = (e) => { 56 | let percentage = ((e.clientX || e.changedTouches[0].clientX) - utils.getBoundingClientRectViewLeft(this.player.template.playedBarWrap)) / this.player.template.playedBarWrap.clientWidth; 57 | percentage = Math.max(percentage, 0); 58 | percentage = Math.min(percentage, 1); 59 | this.player.bar.set('played', percentage, 'width'); 60 | this.player.template.ptime.innerHTML = utils.secondToTime(percentage * this.player.video.duration); 61 | }; 62 | 63 | const thumbUp = (e) => { 64 | document.removeEventListener(utils.nameMap.dragEnd, thumbUp); 65 | document.removeEventListener(utils.nameMap.dragMove, thumbMove); 66 | let percentage = ((e.clientX || e.changedTouches[0].clientX) - utils.getBoundingClientRectViewLeft(this.player.template.playedBarWrap)) / this.player.template.playedBarWrap.clientWidth; 67 | percentage = Math.max(percentage, 0); 68 | percentage = Math.min(percentage, 1); 69 | this.player.bar.set('played', percentage, 'width'); 70 | this.player.seek(this.player.bar.get('played') * this.player.video.duration); 71 | this.player.timer.enable('progress'); 72 | }; 73 | 74 | this.player.template.playedBarWrap.addEventListener(utils.nameMap.dragStart, () => { 75 | this.player.timer.disable('progress'); 76 | document.addEventListener(utils.nameMap.dragMove, thumbMove); 77 | document.addEventListener(utils.nameMap.dragEnd, thumbUp); 78 | }); 79 | 80 | this.player.template.playedBarWrap.addEventListener(utils.nameMap.dragMove, (e) => { 81 | if (this.player.video.duration) { 82 | const px = this.player.template.playedBarWrap.getBoundingClientRect().left; 83 | const tx = (e.clientX || e.changedTouches[0].clientX) - px; 84 | if (tx < 0 || tx > this.player.template.playedBarWrap.offsetWidth) { 85 | return; 86 | } 87 | const time = this.player.video.duration * (tx / this.player.template.playedBarWrap.offsetWidth); 88 | this.player.template.playedBarTime.style.left = `${tx - (time >= 3600 ? 25 : 20)}px`; 89 | this.player.template.playedBarTime.innerText = utils.secondToTime(time); 90 | this.player.template.playedBarTime.classList.remove('hidden'); 91 | } 92 | this.setAutoHide(); 93 | }); 94 | 95 | if (!utils.isMobile) { 96 | this.player.template.playedBarWrap.addEventListener('mouseenter', () => { 97 | if (this.player.video.duration) { 98 | this.player.template.playedBarTime.classList.remove('hidden'); 99 | } 100 | }); 101 | 102 | this.player.template.playedBarWrap.addEventListener('mouseleave', () => { 103 | if (this.player.video.duration) { 104 | this.player.template.playedBarTime.classList.add('hidden'); 105 | } 106 | }); 107 | } 108 | } 109 | 110 | initFullButton() { 111 | this.player.template.browserFullButton.addEventListener('click', () => { 112 | this.player.fullScreen.toggle('browser'); 113 | }); 114 | 115 | this.player.template.webFullButton.addEventListener('click', () => { 116 | this.player.fullScreen.toggle('web'); 117 | }); 118 | } 119 | 120 | initVolumeButton() { 121 | const vWidth = 35; 122 | 123 | const volumeMove = (event) => { 124 | const e = event || window.event; 125 | const percentage = ((e.clientX || e.changedTouches[0].clientX) - utils.getBoundingClientRectViewLeft(this.player.template.volumeBarWrap) - 5.5) / vWidth; 126 | this.player.volume(percentage); 127 | }; 128 | const volumeUp = () => { 129 | document.removeEventListener(utils.nameMap.dragEnd, volumeUp); 130 | document.removeEventListener(utils.nameMap.dragMove, volumeMove); 131 | this.player.template.volumeButton.classList.remove('dplayer-volume-active'); 132 | }; 133 | 134 | this.player.template.volumeBarWrapWrap.addEventListener('click', (event) => { 135 | const e = event || window.event; 136 | const percentage = ((e.clientX || e.changedTouches[0].clientX) - utils.getBoundingClientRectViewLeft(this.player.template.volumeBarWrap) - 5.5) / vWidth; 137 | this.player.volume(percentage); 138 | }); 139 | this.player.template.volumeBarWrapWrap.addEventListener(utils.nameMap.dragStart, () => { 140 | document.addEventListener(utils.nameMap.dragMove, volumeMove); 141 | document.addEventListener(utils.nameMap.dragEnd, volumeUp); 142 | this.player.template.volumeButton.classList.add('dplayer-volume-active'); 143 | }); 144 | this.player.template.volumeButtonIcon.addEventListener('click', () => { 145 | if (this.player.video.muted) { 146 | this.player.video.muted = false; 147 | this.player.switchVolumeIcon(); 148 | this.player.bar.set('volume', this.player.volume(), 'width'); 149 | } else { 150 | this.player.video.muted = true; 151 | this.player.template.volumeIcon.innerHTML = Icons.volumeOff; 152 | this.player.bar.set('volume', 0, 'width'); 153 | } 154 | }); 155 | } 156 | 157 | setAutoHide() { 158 | this.show(); 159 | clearTimeout(this.autoHideTimer); 160 | this.autoHideTimer = setTimeout(() => { 161 | if (this.player.video.played.length) { 162 | this.hide(); 163 | } 164 | }, 3000); 165 | } 166 | 167 | show() { 168 | this.player.container.classList.remove('dplayer-hide-controller'); 169 | } 170 | 171 | hide() { 172 | this.player.container.classList.add('dplayer-hide-controller'); 173 | } 174 | 175 | isShow() { 176 | return !this.player.container.classList.contains('dplayer-hide-controller'); 177 | } 178 | 179 | toggle() { 180 | if (this.isShow()) { 181 | this.hide(); 182 | } else { 183 | this.show(); 184 | } 185 | } 186 | 187 | destroy() { 188 | clearTimeout(this.autoHideTimer); 189 | } 190 | } 191 | 192 | export default Controller; -------------------------------------------------------------------------------- /src/js/events.js: -------------------------------------------------------------------------------- 1 | class Events { 2 | constructor() { 3 | this.events = {}; 4 | 5 | this.videoEvents = [ 6 | 'abort', 7 | 'canplay', 8 | 'canplaythrough', 9 | 'durationchange', 10 | 'emptied', 11 | 'ended', 12 | 'error', 13 | 'loadeddata', 14 | 'loadedmetadata', 15 | 'loadstart', 16 | 'mozaudioavailable', 17 | 'pause', 18 | 'play', 19 | 'playing', 20 | 'progress', 21 | 'ratechange', 22 | 'seeked', 23 | 'seeking', 24 | 'stalled', 25 | 'suspend', 26 | 'timeupdate', 27 | 'volumechange', 28 | 'waiting', 29 | ]; 30 | this.playerEvents = [ 31 | 'loop_enable', 32 | 'loop_disable', 33 | 'notice_show', 34 | 'notice_hide', 35 | 'destroy', 36 | 'resize', 37 | 'fullscreen', 38 | 'fullscreen_cancel', 39 | 'webfullscreen', 40 | 'webfullscreen_cancel' 41 | ]; 42 | } 43 | 44 | on(name, callback) { 45 | if (this.type(name) && typeof callback === 'function') { 46 | if (!this.events[name]) { 47 | this.events[name] = []; 48 | } 49 | this.events[name].push(callback); 50 | } 51 | } 52 | 53 | trigger(name, info) { 54 | if (this.events[name] && this.events[name].length) { 55 | for (let i = 0; i < this.events[name].length; i++) { 56 | this.events[name][i](info); 57 | } 58 | } 59 | } 60 | 61 | type(name) { 62 | if (this.playerEvents.indexOf(name) !== -1) { 63 | return 'player'; 64 | } else if (this.videoEvents.indexOf(name) !== -1) { 65 | return 'video'; 66 | } 67 | 68 | console.error(`Unknown event name: ${name}`); 69 | return null; 70 | } 71 | } 72 | 73 | export default Events; 74 | -------------------------------------------------------------------------------- /src/js/fullscreen.js: -------------------------------------------------------------------------------- 1 | import utils from './utils'; 2 | 3 | class FullScreen { 4 | constructor(player) { 5 | this.player = player; 6 | this.lastScrollPosition = { left: 0, top: 0 }; 7 | this.player.events.on('webfullscreen', () => { 8 | this.player.resize(); 9 | }); 10 | this.player.events.on('webfullscreen_cancel', () => { 11 | this.player.resize(); 12 | utils.setScrollPosition(this.lastScrollPosition); 13 | }); 14 | 15 | const fullscreenchange = () => { 16 | this.player.resize(); 17 | if (this.isFullScreen('browser')) { 18 | this.player.events.trigger('fullscreen'); 19 | } else { 20 | utils.setScrollPosition(this.lastScrollPosition); 21 | this.player.events.trigger('fullscreen_cancel'); 22 | } 23 | }; 24 | const docfullscreenchange = () => { 25 | const fullEle = document.fullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; 26 | if (fullEle && fullEle !== this.player.container) { 27 | return; 28 | } 29 | this.player.resize(); 30 | if (fullEle) { 31 | this.player.events.trigger('fullscreen'); 32 | } else { 33 | utils.setScrollPosition(this.lastScrollPosition); 34 | this.player.events.trigger('fullscreen_cancel'); 35 | } 36 | }; 37 | if (/Firefox/.test(navigator.userAgent)) { 38 | document.addEventListener('mozfullscreenchange', docfullscreenchange); 39 | document.addEventListener('fullscreenchange', docfullscreenchange); 40 | } else { 41 | this.player.container.addEventListener('fullscreenchange', fullscreenchange); 42 | this.player.container.addEventListener('webkitfullscreenchange', fullscreenchange); 43 | document.addEventListener('msfullscreenchange', docfullscreenchange); 44 | document.addEventListener('MSFullscreenChange', docfullscreenchange); 45 | } 46 | } 47 | 48 | isFullScreen(type = 'browser') { 49 | switch (type) { 50 | case 'browser': 51 | return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; 52 | case 'web': 53 | return this.player.container.classList.contains('dplayer-fulled'); 54 | } 55 | } 56 | 57 | request(type = 'browser') { 58 | const anotherType = type === 'browser' ? 'web' : 'browser'; 59 | const anotherTypeOn = this.isFullScreen(anotherType); 60 | if (!anotherTypeOn) { 61 | this.lastScrollPosition = utils.getScrollPosition(); 62 | } 63 | 64 | switch (type) { 65 | case 'browser': 66 | if (this.player.container.requestFullscreen) { 67 | this.player.container.requestFullscreen(); 68 | } else if (this.player.container.mozRequestFullScreen) { 69 | this.player.container.mozRequestFullScreen(); 70 | } else if (this.player.container.webkitRequestFullscreen) { 71 | this.player.container.webkitRequestFullscreen(); 72 | } else if (this.player.video.webkitEnterFullscreen) { 73 | // Safari for iOS 74 | this.player.video.webkitEnterFullscreen(); 75 | } else if (this.player.video.webkitEnterFullScreen) { 76 | this.player.video.webkitEnterFullScreen(); 77 | } else if (this.player.container.msRequestFullscreen) { 78 | this.player.container.msRequestFullscreen(); 79 | } 80 | break; 81 | case 'web': 82 | this.player.container.classList.add('dplayer-fulled'); 83 | document.body.classList.add('dplayer-web-fullscreen-fix'); 84 | this.player.events.trigger('webfullscreen'); 85 | break; 86 | } 87 | 88 | if (anotherTypeOn) { 89 | this.cancel(anotherType); 90 | } 91 | } 92 | 93 | cancel(type = 'browser') { 94 | switch (type) { 95 | case 'browser': 96 | if (document.cancelFullScreen) { 97 | document.cancelFullScreen(); 98 | } else if (document.mozCancelFullScreen) { 99 | document.mozCancelFullScreen(); 100 | } else if (document.webkitCancelFullScreen) { 101 | document.webkitCancelFullScreen(); 102 | } else if (document.webkitCancelFullscreen) { 103 | document.webkitCancelFullscreen(); 104 | } else if (document.msCancelFullScreen) { 105 | document.msCancelFullScreen(); 106 | } else if (document.msExitFullscreen) { 107 | document.msExitFullscreen(); 108 | } 109 | break; 110 | case 'web': 111 | this.player.container.classList.remove('dplayer-fulled'); 112 | document.body.classList.remove('dplayer-web-fullscreen-fix'); 113 | this.player.events.trigger('webfullscreen_cancel'); 114 | break; 115 | } 116 | } 117 | 118 | toggle(type = 'browser') { 119 | if (this.isFullScreen(type)) { 120 | this.cancel(type); 121 | } else { 122 | this.request(type); 123 | } 124 | } 125 | } 126 | 127 | export default FullScreen; 128 | -------------------------------------------------------------------------------- /src/js/hotkey.js: -------------------------------------------------------------------------------- 1 | class HotKey { 2 | constructor(player) { 3 | document.addEventListener('keydown', (e) => { 4 | if (player.focus) { 5 | const event = e || window.event; 6 | let percentage; 7 | switch (event.keyCode) { 8 | case 27: 9 | if (player.fullScreen.isFullScreen('web')) { 10 | player.fullScreen.cancel('web'); 11 | } 12 | break; 13 | case 32: 14 | event.preventDefault(); 15 | player.toggle(); 16 | player.controller.setAutoHide(); 17 | break; 18 | case 37: 19 | event.preventDefault(); 20 | if (player.options.live) { 21 | break; 22 | } 23 | player.seek(player.video.currentTime - 3, true); 24 | break; 25 | case 39: 26 | event.preventDefault(); 27 | if (player.options.live) { 28 | break; 29 | } 30 | player.seek(player.video.currentTime + 3, true); 31 | break; 32 | case 38: 33 | event.preventDefault(); 34 | percentage = player.volume() + 0.01; 35 | player.volume(percentage, true); 36 | break; 37 | case 40: 38 | event.preventDefault(); 39 | percentage = player.volume() - 0.01; 40 | player.volume(percentage, true); 41 | break; 42 | } 43 | } 44 | }); 45 | } 46 | } 47 | 48 | export default HotKey; -------------------------------------------------------------------------------- /src/js/i18n.js: -------------------------------------------------------------------------------- 1 | /* 2 | W3C def language codes is : 3 | language-code = primary-code ( "-" subcode ) 4 | primary-code ISO 639-1 ( the names of language with 2 code ) 5 | subcode ISO 3166 ( the names of countries ) 6 | 7 | NOTE: use lowercase to prevent case typo from user! 8 | Use this as shown below..... */ 9 | 10 | function i18n(lang) { 11 | this.lang = lang; 12 | this.tran = (text) => { 13 | if (tranTxt[this.lang] && tranTxt[this.lang][text]) { 14 | return tranTxt[this.lang][text]; 15 | } else { 16 | return text; 17 | } 18 | }; 19 | } 20 | 21 | // add translation text here 22 | const tranTxt = { 23 | 'zh-cn': { 24 | 'Video load failed': '视频加载失败', 25 | 'Go forward': '快进', 26 | 'Go back': '快退', 27 | 'seconds': '秒', 28 | 'Volume': '音量', 29 | 'Live': '直播', 30 | }, 31 | 'zh-tw': { 32 | 'Video load failed': '視頻加載失敗', 33 | 'Go forward': '快進', 34 | 'Go back': '快退', 35 | 'seconds': '秒', 36 | 'Volume': '音量', 37 | 'Live': '直播', 38 | } 39 | }; 40 | 41 | export default i18n; -------------------------------------------------------------------------------- /src/js/icons.js: -------------------------------------------------------------------------------- 1 | import play from '../assets/play.svg'; 2 | import pause from '../assets/pause.svg'; 3 | import loop from '../assets/loop.svg'; 4 | import volumeUp from '../assets/volume-up.svg'; 5 | import volumeDown from '../assets/volume-down.svg'; 6 | import volumeOff from '../assets/volume-off.svg'; 7 | import full from '../assets/full.svg'; 8 | import fullWeb from '../assets/full-web.svg'; 9 | import loading from '../assets/loading.svg'; 10 | 11 | const Icons = { 12 | play: play, 13 | pause: pause, 14 | loop: loop, 15 | volumeUp: volumeUp, 16 | volumeDown: volumeDown, 17 | volumeOff: volumeOff, 18 | full: full, 19 | fullWeb: fullWeb, 20 | loading: loading, 21 | }; 22 | 23 | export default Icons; 24 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import '../css/index.scss'; 2 | import DPlayer from './player'; 3 | export default DPlayer; -------------------------------------------------------------------------------- /src/js/options.js: -------------------------------------------------------------------------------- 1 | export default (options) => { 2 | // default options 3 | const defaultOption = { 4 | container: options.element || document.getElementsByClassName('dplayer')[0], 5 | live: false, 6 | autoplay: false, 7 | theme: '#b7daff', 8 | loop: false, 9 | lang: (navigator.language || navigator.browserLanguage).toLowerCase(), 10 | preload: 'auto', 11 | volume: 1, 12 | video: {}, 13 | mutex: true, 14 | pluginOptions: { hls: {}, flv: {}, dash: {}, webtorrent: {} }, 15 | }; 16 | 17 | for (const defaultKey in defaultOption) { 18 | if (defaultOption.hasOwnProperty(defaultKey) && !options.hasOwnProperty(defaultKey)) { 19 | options[defaultKey] = defaultOption[defaultKey]; 20 | } 21 | } 22 | if (options.video) { 23 | !options.video.type && (options.video.type = 'auto'); 24 | } 25 | 26 | if (options.lang) { 27 | options.lang = options.lang.toLowerCase(); 28 | } 29 | 30 | return options; 31 | }; 32 | -------------------------------------------------------------------------------- /src/js/player.js: -------------------------------------------------------------------------------- 1 | import Promise from 'promise-polyfill'; 2 | 3 | import utils from './utils'; 4 | import handleOption from './options'; 5 | import i18n from './i18n'; 6 | import Template from './template'; 7 | import Icons from './icons'; 8 | import Events from './events'; 9 | import FullScreen from './fullscreen'; 10 | import Bar from './bar'; 11 | import Timer from './timer'; 12 | import Bezel from './bezel'; 13 | import Controller from './controller'; 14 | import HotKey from './hotkey'; 15 | 16 | const instances = []; 17 | 18 | class DPlayer { 19 | /** 20 | * DPlayer constructor function 21 | * 22 | * @param {Object} options - See README 23 | * @constructor 24 | */ 25 | constructor(options) { 26 | this.options = handleOption({ preload: options.video.type === 'webtorrent' ? 'none' : 'metadata', ...options }); 27 | 28 | this.tran = new i18n(this.options.lang).tran; 29 | 30 | this.events = new Events(); 31 | 32 | this.container = this.options.container; 33 | 34 | this.container.classList.add('dplayer'); 35 | 36 | if (this.options.live) { 37 | this.container.classList.add('dplayer-live'); 38 | } else { 39 | this.container.classList.remove('dplayer-live'); 40 | } 41 | if (utils.isMobile) { 42 | this.container.classList.add('dplayer-mobile'); 43 | } 44 | 45 | this.template = new Template({ 46 | container: this.container, 47 | options: this.options, 48 | tran: this.tran 49 | }); 50 | 51 | this.video = this.template.video; 52 | 53 | this.bar = new Bar(this.template); 54 | 55 | this.bezel = new Bezel(this.template.bezel); 56 | 57 | this.fullScreen = new FullScreen(this); 58 | 59 | this.controller = new Controller(this); 60 | 61 | this.plugins = {}; 62 | 63 | document.addEventListener( 64 | 'click', 65 | () => { 66 | this.focus = false; 67 | }, 68 | true 69 | ); 70 | this.container.addEventListener( 71 | 'click', 72 | () => { 73 | this.focus = true; 74 | }, 75 | true 76 | ); 77 | 78 | this.paused = true; 79 | 80 | this.timer = new Timer(this); 81 | 82 | this.hotkey = new HotKey(this); 83 | 84 | this.initVideo(this.video, this.options.video.type); 85 | 86 | /** 87 | * loop control 88 | */ 89 | this.loop = this.options.loop; 90 | const loopEvent = () => { 91 | this.events.on('loop_enable', () => { 92 | this.template.loopIcon.style.opacity = ''; 93 | }); 94 | this.events.on('loop_disable', () => { 95 | this.template.loopIcon.style.opacity = '0.4'; 96 | }); 97 | 98 | this.template.loopButton.addEventListener('click', () => { 99 | this.loop = !this.loop; 100 | if (this.loop) { 101 | this.events.trigger('loop_enable'); 102 | } 103 | else { 104 | this.events.trigger('loop_disable'); 105 | } 106 | }); 107 | }; 108 | loopEvent(); 109 | if (this.loop) { 110 | this.events.trigger('loop_enable'); 111 | } 112 | else { 113 | this.events.trigger('loop_disable'); 114 | } 115 | 116 | if (this.options.autoplay) { 117 | this.play(); 118 | } 119 | 120 | /** 121 | * right key 122 | */ 123 | this.container.addEventListener('contextmenu', (e) => { 124 | const event = e || window.event; 125 | event.preventDefault(); 126 | }); 127 | 128 | instances.push(this); 129 | } 130 | 131 | /** 132 | * Seek video 133 | */ 134 | seek (time, send_notice) { 135 | time = Math.max(time, 0); 136 | if (this.video.duration) { 137 | time = Math.min(time, this.video.duration); 138 | } 139 | if (this.video.currentTime < time && send_notice) { 140 | this.notice(`${this.tran('Go forward')} ${(time - this.video.currentTime).toFixed(0)} ${this.tran('seconds')}`); 141 | } 142 | else if (this.video.currentTime > time && send_notice) { 143 | this.notice(`${this.tran('Go back')} ${(this.video.currentTime - time).toFixed(0)} ${this.tran('seconds')}`); 144 | } 145 | 146 | this.video.currentTime = time; 147 | 148 | this.bar.set('played', time / this.video.duration, 'width'); 149 | this.template.ptime.innerHTML = utils.secondToTime(time); 150 | } 151 | 152 | /** 153 | * Play video 154 | */ 155 | play(fromNative) { 156 | this.paused = false; 157 | if (this.video.paused) { 158 | this.bezel.switch(Icons.play); 159 | } 160 | 161 | this.template.playButton.innerHTML = Icons.pause; 162 | 163 | if (!fromNative) { 164 | const playedPromise = Promise.resolve(this.video.play()); 165 | playedPromise 166 | .catch(() => { 167 | this.pause(); 168 | }) 169 | .then(() => {}); 170 | } 171 | this.timer.enable('loading'); 172 | this.container.classList.add('dplayer-playing'); 173 | if (this.options.mutex) { 174 | for (let i = 0; i < instances.length; i++) { 175 | if (this !== instances[i]) { 176 | instances[i].pause(); 177 | } 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * Pause video 184 | */ 185 | pause(fromNative) { 186 | this.paused = true; 187 | this.container.classList.remove('dplayer-loading'); 188 | 189 | if (!this.video.paused) { 190 | this.bezel.switch(Icons.pause); 191 | } 192 | 193 | this.template.playButton.innerHTML = Icons.play; 194 | if (!fromNative) { 195 | this.video.pause(); 196 | } 197 | this.timer.disable('loading'); 198 | this.container.classList.remove('dplayer-playing'); 199 | } 200 | 201 | switchVolumeIcon() { 202 | if (this.volume() >= 0.8) { 203 | this.template.volumeIcon.innerHTML = Icons.volumeUp; 204 | } else if (this.volume() > 0) { 205 | this.template.volumeIcon.innerHTML = Icons.volumeDown; 206 | } else { 207 | this.template.volumeIcon.innerHTML = Icons.volumeOff; 208 | } 209 | } 210 | 211 | /** 212 | * Set volume 213 | */ 214 | volume (percentage, send_notice) { 215 | percentage = parseFloat(percentage); 216 | if (!isNaN(percentage)) { 217 | percentage = Math.max(percentage, 0); 218 | percentage = Math.min(percentage, 1); 219 | this.bar.set('volume', percentage, 'width'); 220 | if (send_notice) { 221 | this.notice(`${this.tran('Volume')} ${(percentage * 100).toFixed(0)}%`); 222 | } 223 | 224 | this.video.volume = percentage; 225 | if (this.video.muted) { 226 | this.video.muted = false; 227 | } 228 | this.switchVolumeIcon(); 229 | } 230 | 231 | return this.video.volume; 232 | } 233 | 234 | /** 235 | * Toggle between play and pause 236 | */ 237 | toggle() { 238 | if (this.video.paused) { 239 | this.play(); 240 | } else { 241 | this.pause(); 242 | } 243 | } 244 | 245 | /** 246 | * attach event 247 | */ 248 | on(name, callback) { 249 | this.events.on(name, callback); 250 | } 251 | 252 | /** 253 | * init Video 254 | */ 255 | initMSE(video, type) { 256 | this.type = type; 257 | if (this.options.video.customType && this.options.video.customType[type]) { 258 | if (Object.prototype.toString.call(this.options.video.customType[type]) === '[object Function]') { 259 | this.options.video.customType[type](this.video, this); 260 | } else { 261 | console.error(`Illegal customType: ${type}`); 262 | } 263 | } else { 264 | if (this.type === 'auto') { 265 | if (/m3u8(#|\?|$)/i.exec(video.src)) { 266 | this.type = 'hls'; 267 | } else if (/.flv(#|\?|$)/i.exec(video.src)) { 268 | this.type = 'flv'; 269 | } else if (/.mpd(#|\?|$)/i.exec(video.src)) { 270 | this.type = 'dash'; 271 | } else { 272 | this.type = 'normal'; 273 | } 274 | } 275 | 276 | if (this.type === 'hls' && (video.canPlayType('application/x-mpegURL') || video.canPlayType('application/vnd.apple.mpegURL'))) { 277 | this.type = 'normal'; 278 | } 279 | 280 | switch (this.type) { 281 | // https://github.com/video-dev/hls.js 282 | case 'hls': 283 | if (window.Hls) { 284 | if (window.Hls.isSupported()) { 285 | const options = this.options.pluginOptions.hls; 286 | const hls = new window.Hls(options); 287 | this.plugins.hls = hls; 288 | hls.loadSource(video.src); 289 | hls.attachMedia(video); 290 | this.events.on('destroy', () => { 291 | hls.destroy(); 292 | delete this.plugins.hls; 293 | }); 294 | } else { 295 | this.notice('Error: Hls is not supported.'); 296 | } 297 | } else { 298 | this.notice("Error: Can't find Hls."); 299 | } 300 | break; 301 | 302 | // https://github.com/Bilibili/flv.js 303 | case 'flv': 304 | if (window.flvjs) { 305 | if (window.flvjs.isSupported()) { 306 | const flvPlayer = window.flvjs.createPlayer( 307 | Object.assign(this.options.pluginOptions.flv.mediaDataSource || {}, { 308 | type: 'flv', 309 | url: video.src, 310 | }), 311 | this.options.pluginOptions.flv.config 312 | ); 313 | this.plugins.flvjs = flvPlayer; 314 | flvPlayer.attachMediaElement(video); 315 | flvPlayer.load(); 316 | this.events.on('destroy', () => { 317 | flvPlayer.unload(); 318 | flvPlayer.detachMediaElement(); 319 | flvPlayer.destroy(); 320 | delete this.plugins.flvjs; 321 | }); 322 | } else { 323 | this.notice('Error: flvjs is not supported.'); 324 | } 325 | } else { 326 | this.notice("Error: Can't find flvjs."); 327 | } 328 | break; 329 | 330 | // https://github.com/Dash-Industry-Forum/dash.js 331 | case 'dash': 332 | if (window.dashjs) { 333 | const dashjsPlayer = window.dashjs.MediaPlayer().create().initialize(video, video.src, false); 334 | const options = this.options.pluginOptions.dash; 335 | dashjsPlayer.updateSettings(options); 336 | this.plugins.dash = dashjsPlayer; 337 | this.events.on('destroy', () => { 338 | window.dashjs.MediaPlayer().reset(); 339 | delete this.plugins.dash; 340 | }); 341 | } else { 342 | this.notice("Error: Can't find dashjs."); 343 | } 344 | break; 345 | 346 | // https://github.com/webtorrent/webtorrent 347 | case 'webtorrent': 348 | if (window.WebTorrent) { 349 | if (window.WebTorrent.WEBRTC_SUPPORT) { 350 | this.container.classList.add('dplayer-loading'); 351 | const options = this.options.pluginOptions.webtorrent; 352 | const client = new window.WebTorrent(options); 353 | this.plugins.webtorrent = client; 354 | const torrentId = video.src; 355 | video.src = ''; 356 | video.preload = 'metadata'; 357 | video.addEventListener('durationchange', () => this.container.classList.remove('dplayer-loading'), { once: true }); 358 | client.add(torrentId, (torrent) => { 359 | const file = torrent.files.find((file) => file.name.endsWith('.mp4')); 360 | file.renderTo(this.video, { 361 | autoplay: this.options.autoplay, 362 | controls: false, 363 | }); 364 | }); 365 | this.events.on('destroy', () => { 366 | client.remove(torrentId); 367 | client.destroy(); 368 | delete this.plugins.webtorrent; 369 | }); 370 | } else { 371 | this.notice('Error: Webtorrent is not supported.'); 372 | } 373 | } else { 374 | this.notice("Error: Can't find Webtorrent."); 375 | } 376 | break; 377 | } 378 | } 379 | } 380 | 381 | initVideo(video, type) { 382 | this.initMSE(video, type); 383 | 384 | /** 385 | * video events 386 | */ 387 | // show video time: the metadata has loaded or changed 388 | this.on('durationchange', () => { 389 | // compatibility: Android browsers will output 1 or Infinity at first 390 | if (video.duration !== 1 && video.duration !== Infinity) { 391 | this.template.dtime.innerHTML = utils.secondToTime(video.duration); 392 | } 393 | }); 394 | 395 | // show video loaded bar: to inform interested parties of progress downloading the media 396 | this.on('progress', () => { 397 | const percentage = video.buffered.length ? video.buffered.end(video.buffered.length - 1) / video.duration : 0; 398 | this.bar.set('loaded', percentage, 'width'); 399 | }); 400 | 401 | // video download error: an error occurs 402 | this.on('error', () => { 403 | if (!this.video.error) { 404 | // Not a video load error, may be poster load failed, see #307 405 | return; 406 | } 407 | this.tran && this.notice && this.type !== 'webtorrent' && this.notice(this.tran('Video load failed'), -1); 408 | }); 409 | 410 | // video end 411 | this.on('ended', () => { 412 | this.bar.set('played', 1, 'width'); 413 | if (!this.loop) { 414 | this.pause(); 415 | } else { 416 | this.seek(0); 417 | this.play(); 418 | } 419 | }); 420 | 421 | this.on('play', () => { 422 | if (this.paused) { 423 | this.play(true); 424 | } 425 | }); 426 | 427 | this.on('pause', () => { 428 | if (!this.paused) { 429 | this.pause(true); 430 | } 431 | }); 432 | 433 | this.on('timeupdate', () => { 434 | this.bar.set('played', this.video.currentTime / this.video.duration, 'width'); 435 | const currentTime = utils.secondToTime(this.video.currentTime); 436 | if (this.template.ptime.innerHTML !== currentTime) { 437 | this.template.ptime.innerHTML = currentTime; 438 | } 439 | }); 440 | 441 | for (let i = 0; i < this.events.videoEvents.length; i++) { 442 | video.addEventListener(this.events.videoEvents[i], () => { 443 | this.events.trigger(this.events.videoEvents[i]); 444 | }); 445 | } 446 | 447 | this.volume(this.options.volume); 448 | } 449 | 450 | notice (text, time = 1500, opacity = 0.8) { 451 | this.template.notice.innerHTML = text; 452 | this.template.notice.style.opacity = opacity; 453 | if (this.noticeTime) { 454 | clearTimeout(this.noticeTime); 455 | } 456 | this.events.trigger('notice_show', text); 457 | if (time > 0) { 458 | this.noticeTime = setTimeout(() => { 459 | this.template.notice.style.opacity = 0; 460 | this.events.trigger('notice_hide'); 461 | }, time); 462 | } 463 | } 464 | 465 | resize() { 466 | this.events.trigger('resize'); 467 | } 468 | 469 | destroy() { 470 | instances.splice(instances.indexOf(this), 1); 471 | this.pause(); 472 | this.controller.destroy(); 473 | this.timer.destroy(); 474 | this.video.src = ''; 475 | this.container.innerHTML = ''; 476 | this.events.trigger('destroy'); 477 | } 478 | } 479 | 480 | export default DPlayer; 481 | -------------------------------------------------------------------------------- /src/js/template.js: -------------------------------------------------------------------------------- 1 | import Icons from './icons'; 2 | import tplPlayer from '../template/player.art'; 3 | 4 | class Template { 5 | constructor(options) { 6 | this.container = options.container; 7 | this.options = options.options; 8 | this.tran = options.tran; 9 | this.init(); 10 | } 11 | 12 | init() { 13 | this.container.innerHTML = tplPlayer({ 14 | options: this.options, 15 | tran: this.tran, 16 | icons: Icons, 17 | video: { 18 | pic: this.options.video.pic, 19 | preload: this.options.preload, 20 | url: this.options.video.url 21 | } 22 | }); 23 | 24 | this.volumeBar = this.container.querySelector('.dplayer-volume-bar-inner'); 25 | this.volumeBarWrap = this.container.querySelector('.dplayer-volume-bar'); 26 | this.volumeBarWrapWrap = this.container.querySelector('.dplayer-volume-bar-wrap'); 27 | this.volumeButton = this.container.querySelector('.dplayer-volume'); 28 | this.volumeButtonIcon = this.container.querySelector('.dplayer-volume-icon'); 29 | this.volumeIcon = this.container.querySelector('.dplayer-volume-icon .dplayer-icon-content'); 30 | this.playedBar = this.container.querySelector('.dplayer-played'); 31 | this.loadedBar = this.container.querySelector('.dplayer-loaded'); 32 | this.playedBarWrap = this.container.querySelector('.dplayer-bar-wrap'); 33 | this.playedBarTime = this.container.querySelector('.dplayer-bar-time'); 34 | this.video = this.container.querySelector('.dplayer-video-current'); 35 | this.bezel = this.container.querySelector('.dplayer-bezel-icon'); 36 | this.playButton = this.container.querySelector('.dplayer-play-icon'); 37 | this.videoWrap = this.container.querySelector('.dplayer-video-wrap'); 38 | this.controllerMask = this.container.querySelector('.dplayer-controller-mask'); 39 | this.ptime = this.container.querySelector('.dplayer-ptime'); 40 | this.loopButton = this.container.querySelector('.dplayer-loop-icon'); 41 | this.loopIcon = this.container.querySelector('.dplayer-loop-icon .dplayer-icon-content'); 42 | this.dtime = this.container.querySelector('.dplayer-dtime'); 43 | this.browserFullButton = this.container.querySelector('.dplayer-full-icon'); 44 | this.webFullButton = this.container.querySelector('.dplayer-full-in-icon'); 45 | this.notice = this.container.querySelector('.dplayer-notice'); 46 | } 47 | } 48 | 49 | export default Template; 50 | -------------------------------------------------------------------------------- /src/js/timer.js: -------------------------------------------------------------------------------- 1 | class Timer { 2 | constructor(player) { 3 | this.player = player; 4 | 5 | window.requestAnimationFrame = (() => 6 | window.requestAnimationFrame || 7 | window.webkitRequestAnimationFrame || 8 | window.mozRequestAnimationFrame || 9 | window.oRequestAnimationFrame || 10 | window.msRequestAnimationFrame || 11 | function(callback) { 12 | window.setTimeout(callback, 1000 / 60); 13 | })(); 14 | 15 | this.types = ['loading']; 16 | 17 | this.init(); 18 | } 19 | 20 | init() { 21 | this.types.map((item) => { 22 | this[`init${item}Checker`](); 23 | return item; 24 | }); 25 | } 26 | 27 | initloadingChecker() { 28 | let lastPlayPos = 0; 29 | let currentPlayPos = 0; 30 | let bufferingDetected = false; 31 | this.loadingChecker = setInterval(() => { 32 | if (this.enableloadingChecker) { 33 | // whether the video is buffering 34 | currentPlayPos = this.player.video.currentTime; 35 | if (!bufferingDetected && currentPlayPos === lastPlayPos && !this.player.video.paused) { 36 | this.player.container.classList.add('dplayer-loading'); 37 | bufferingDetected = true; 38 | } 39 | if (bufferingDetected && currentPlayPos > lastPlayPos && !this.player.video.paused) { 40 | this.player.container.classList.remove('dplayer-loading'); 41 | bufferingDetected = false; 42 | } 43 | lastPlayPos = currentPlayPos; 44 | } 45 | }, 100); 46 | } 47 | 48 | enable(type) { 49 | this[`enable${type}Checker`] = true; 50 | } 51 | 52 | disable(type) { 53 | this[`enable${type}Checker`] = false; 54 | } 55 | 56 | destroy() { 57 | this.types.map((item) => { 58 | this[`enable${item}Checker`] = false; 59 | this[`${item}Checker`] && clearInterval(this[`${item}Checker`]); 60 | return item; 61 | }); 62 | } 63 | } 64 | 65 | export default Timer; -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | const isMobile = /mobile/i.test(window.navigator.userAgent); 2 | 3 | const utils = { 4 | /** 5 | * Parse second to time string 6 | * 7 | * @param {Number} second 8 | * @return {String} 00:00 or 00:00:00 9 | */ 10 | secondToTime: (second) => { 11 | second = second || 0; 12 | if (second === 0 || second === Infinity || second.toString() === 'NaN') { 13 | return '00:00'; 14 | } 15 | const add0 = (num) => (num < 10 ? '0' + num : '' + num); 16 | const hour = Math.floor(second / 3600); 17 | const min = Math.floor((second - hour * 3600) / 60); 18 | const sec = Math.floor(second - hour * 3600 - min * 60); 19 | return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(':'); 20 | }, 21 | 22 | /** 23 | * control play progress 24 | */ 25 | // get element's view position 26 | getElementViewLeft: (element) => { 27 | let actualLeft = element.offsetLeft; 28 | let current = element.offsetParent; 29 | const elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft; 30 | if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) { 31 | while (current !== null) { 32 | actualLeft += current.offsetLeft; 33 | current = current.offsetParent; 34 | } 35 | } else { 36 | while (current !== null && current !== element) { 37 | actualLeft += current.offsetLeft; 38 | current = current.offsetParent; 39 | } 40 | } 41 | return actualLeft - elementScrollLeft; 42 | }, 43 | 44 | /** 45 | * optimize control play progress 46 | 47 | * optimize get element's view position,for float dialog video player 48 | * getBoundingClientRect 在 IE8 及以下返回的值缺失 width、height 值 49 | * getBoundingClientRect 在 Firefox 11 及以下返回的值会把 transform 的值也包含进去 50 | * getBoundingClientRect 在 Opera 10.5 及以下返回的值缺失 width、height 值 51 | */ 52 | getBoundingClientRectViewLeft(element) { 53 | const scrollTop = window.scrollY || window.pageYOffset || document.body.scrollTop + ((document.documentElement && document.documentElement.scrollTop) || 0); 54 | 55 | if (element.getBoundingClientRect) { 56 | if (typeof this.getBoundingClientRectViewLeft.offset !== 'number') { 57 | let temp = document.createElement('div'); 58 | temp.style.cssText = 'position:absolute;top:0;left:0;'; 59 | document.body.appendChild(temp); 60 | this.getBoundingClientRectViewLeft.offset = -temp.getBoundingClientRect().top - scrollTop; 61 | document.body.removeChild(temp); 62 | temp = null; 63 | } 64 | const rect = element.getBoundingClientRect(); 65 | const offset = this.getBoundingClientRectViewLeft.offset; 66 | 67 | return rect.left + offset; 68 | } else { 69 | // not support getBoundingClientRect 70 | return this.getElementViewLeft(element); 71 | } 72 | }, 73 | 74 | getScrollPosition() { 75 | return { 76 | left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0, 77 | top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 78 | }; 79 | }, 80 | 81 | setScrollPosition({ left = 0, top = 0 }) { 82 | if (this.isFirefox) { 83 | document.documentElement.scrollLeft = left; 84 | document.documentElement.scrollTop = top; 85 | } else { 86 | window.scrollTo(left, top); 87 | } 88 | }, 89 | 90 | isMobile: isMobile, 91 | 92 | isFirefox: /firefox/i.test(window.navigator.userAgent), 93 | 94 | isChrome: /chrome/i.test(window.navigator.userAgent), 95 | 96 | nameMap: { 97 | dragStart: isMobile ? 'touchstart' : 'mousedown', 98 | dragMove: isMobile ? 'touchmove' : 'mousemove', 99 | dragEnd: isMobile ? 'touchend' : 'mouseup' 100 | } 101 | }; 102 | 103 | export default utils; -------------------------------------------------------------------------------- /src/template/player.art: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ include './video.art' video }} 4 |
5 | 6 | {{@ icons.loading }} 7 |
8 |
9 |
10 |
11 |
12 | 15 |
16 | 19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 | 0:00 / 0:00 29 | 30 | {{ if options.live }} 31 | {{ tran('Live') }} 32 | {{ /if }} 33 |
34 |
35 |
36 | 39 |
40 |
41 | 44 |
45 |
46 | 49 |
50 |
51 |
52 | 53 |
54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
-------------------------------------------------------------------------------- /src/template/video.art: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const autoprefixer = require('autoprefixer'); 4 | const cssnano = require('cssnano'); 5 | 6 | module.exports = { 7 | 8 | mode: 'production', 9 | 10 | bail: true, 11 | 12 | entry: { 13 | DPlayer: './src/js/index.js' 14 | }, 15 | 16 | output: { 17 | path: path.resolve(__dirname, '..', 'dist'), 18 | filename: '[name].min.js', 19 | library: '[name]', 20 | libraryTarget: 'umd', 21 | libraryExport: 'default', 22 | umdNamedDefine: true, 23 | publicPath: '/' 24 | }, 25 | 26 | resolve: { 27 | modules: ['node_modules'], 28 | extensions: ['.js', '.scss'] 29 | }, 30 | 31 | module: { 32 | strictExportPresence: true, 33 | rules: [ 34 | { 35 | test: /\.js$/, 36 | use: [ 37 | 'template-string-optimize-loader', 38 | { 39 | loader: 'babel-loader', 40 | options: { 41 | cacheDirectory: true, 42 | presets: ['@babel/preset-env'] 43 | } 44 | } 45 | ] 46 | }, 47 | { 48 | test: /\.scss$/, 49 | use: [ 50 | 'style-loader', 51 | { 52 | loader: 'css-loader', 53 | options: { 54 | importLoaders: 1, 55 | sourceMap: false 56 | } 57 | }, 58 | { 59 | loader: 'postcss-loader', 60 | options: { 61 | plugins: [autoprefixer, cssnano] 62 | } 63 | }, 64 | 'sass-loader', 65 | ] 66 | }, 67 | { 68 | test: /\.(png|jpg)$/, 69 | loader: 'url-loader', 70 | options: { 71 | limit: 40000 72 | } 73 | }, 74 | { 75 | test: /\.svg$/, 76 | loader: 'svg-inline-loader' 77 | }, 78 | { 79 | test: /\.art$/, 80 | loader: 'art-template-loader' 81 | } 82 | ] 83 | }, 84 | 85 | plugins: [ 86 | ], 87 | 88 | node: { 89 | dgram: 'empty', 90 | fs: 'empty', 91 | net: 'empty', 92 | tls: 'empty', 93 | } 94 | 95 | }; 96 | --------------------------------------------------------------------------------