Compare commits

..

No commits in common. "master" and "v1.26.0" have entirely different histories.

62 changed files with 10259 additions and 18177 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
dist
demo

56
.eslintrc Normal file
View File

@ -0,0 +1,56 @@
{
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"env": {
"es6": true,
"browser": true,
"node": true,
},
"rules": {
"block-scoped-var": 1,
"curly": 1,
"eqeqeq": 1,
"no-global-assign": 1,
"no-implicit-globals": 1,
"no-labels": 1,
"no-multi-str": 1,
"comma-spacing": 1,
"comma-style": 1,
"func-call-spacing": 1,
"keyword-spacing": 1,
"linebreak-style": 1,
"no-multiple-empty-lines": 1,
"space-infix-ops": 1,
"arrow-spacing": 1,
"no-var": 1,
"prefer-const": 1,
"no-unsafe-negation": 1,
"array-callback-return": 1,
"dot-notation": 1,
"no-eval": 1,
"no-extend-native": 1,
"no-extra-label": 1,
"semi": 1,
"space-before-blocks": 1,
"space-in-parens": 1,
"space-unary-ops": 1,
"spaced-comment": 1,
"arrow-body-style": 1,
"arrow-parens": 1,
"no-restricted-imports": 1,
"no-duplicate-imports": 1,
"no-useless-computed-key": 1,
"no-useless-rename": 1,
"rest-spread-spacing": 1,
"no-trailing-spaces": 1,
"no-control-regex": 0,
"prettier/prettier": 0,
"no-await-in-loop": 1,
"require-atomic-updates": 0,
"no-prototype-builtins": 0
}
}

View File

@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm exec lint-staged

View File

@ -8,11 +8,13 @@
[![npm](https://img.shields.io/npm/v/dplayer.svg?style=flat-square)](https://www.npmjs.com/package/dplayer) [![npm](https://img.shields.io/npm/v/dplayer.svg?style=flat-square)](https://www.npmjs.com/package/dplayer)
[![npm](https://img.shields.io/npm/l/dplayer.svg?style=flat-square)](https://github.com/MoePlayer/DPlayer/blob/master/LICENSE) [![npm](https://img.shields.io/npm/l/dplayer.svg?style=flat-square)](https://github.com/MoePlayer/DPlayer/blob/master/LICENSE)
[![npm](https://img.shields.io/npm/dt/dplayer.svg?style=flat-square)](https://www.npmjs.com/package/dplayer) [![npm](https://img.shields.io/npm/dt/dplayer.svg?style=flat-square)](https://www.npmjs.com/package/dplayer)
[![](https://data.jsdelivr.com/v1/package/npm/dplayer/badge)](https://www.jsdelivr.com/package/npm/dplayer) [![size](https://badge-size.herokuapp.com/MoePlayer/DPlayer/master/dist/DPlayer.min.js?compression=gzip&style=flat-square)](https://github.com/MoePlayer/DPlayer/tree/master/dist)
[![Travis](https://img.shields.io/travis/MoePlayer/DPlayer.svg?style=flat-square)](https://travis-ci.org/MoePlayer/DPlayer)
[![devDependency Status](https://img.shields.io/david/dev/MoePlayer/dplayer.svg?style=flat-square)](https://david-dm.org/MoePlayer/DPlayer#info=devDependencies)
## Introduction ## Introduction
![image](https://i.imgur.com/207ch36.jpg) ![image](http://i.imgur.com/207ch36.jpg)
DPlayer is a lovely HTML5 danmaku video player to help people build video and danmaku easily. DPlayer is a lovely HTML5 danmaku video player to help people build video and danmaku easily.
@ -38,24 +40,40 @@ DPlayer is a lovely HTML5 danmaku video player to help people build video and da
Using DPlayer on your project? [Let me know!](https://github.com/DIYgod/DPlayer/issues/31) Using DPlayer on your project? [Let me know!](https://github.com/DIYgod/DPlayer/issues/31)
**[Docs](https://dplayer.diygod.dev/)** **[Docs](http://dplayer.js.org)**
**[中文文档](https://dplayer.diygod.dev/zh/)** **[中文文档](http://dplayer.js.org/#/zh-Hans/)**
## Thanks ## Thanks
### Sponsors ### Special Sponsors
<div>
<a href="https://www.cdnbye.com" target="_blank">
<img height="60px" src="https://cdnbye.oss-cn-beijing.aliyuncs.com/pic/cdnbye-dp.jpeg">
</a>
</div>
<div> <div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank"> <a href="https://www.dogecloud.com/?ref=dplayer" target="_blank">
<img height="60px" src="https://player.dogecloud.com/img/logo_with_product3.png"> <img height="60px" src="https://i.imgur.com/C2NgugY.png">
</a> </a>
</div> </div>
### Sponsors
| [极酷社](https://www.acg.app) |
| :---------------------------: |
### Contributors ### Contributors
<a href="https://github.com/MoePlayer/DPlayer/graphs/contributors"><img src="https://opencollective.com/DPlayer/contributors.svg?width=890" /></a> <a href="https://github.com/MoePlayer/DPlayer/graphs/contributors"><img src="https://opencollective.com/DPlayer/contributors.svg?width=890" /></a>
## Join the Discussion
- [Telegram Group](https://t.me/adplayer)
## Related Projects ## Related Projects
Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31) Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31)
@ -82,13 +100,11 @@ Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlay
- [Selection](https://github.com/GreatSatan79/Selection): WordPress - [Selection](https://github.com/GreatSatan79/Selection): WordPress
- [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue - [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue
- [react-dplayer](https://github.com/hnsylitao/react-dplayer): React - [react-dplayer](https://github.com/hnsylitao/react-dplayer): React
- [rc-dplayer](https://github.com/tianfeng98/rc-dplayer): React
### Other ### Other
- [DPlayer-Lite](https://github.com/kn007/DPlayer-Lite): lite version - [DPlayer-Lite](https://github.com/kn007/DPlayer-Lite): lite version
- [hlsjs-p2p-engine](https://github.com/cdnbye/hlsjs-p2p-engine): Let your viewers become your unlimitedly scalable CDN - [hlsjs-p2p-engine](https://github.com/cdnbye/hlsjs-p2p-engine)
- [CBPlayer](https://github.com/cdnbye/CBPlayer): Dplayer with CDNBye P2P plugin built in, supporting HLS, MP4 and MPEG-DASH P2P streaming.
- Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31) - Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31)
## Who use DPlayer? ## Who use DPlayer?
@ -103,34 +119,26 @@ Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlay
- [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站 - [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站
- [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统 - [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统
- [oneindex](https://github.com/donwa/oneindex): Onedrive Directory Index - [oneindex](https://github.com/donwa/oneindex): Onedrive Directory Index
- [arozos](https://github.com/tobychui/arozos): General purposed Web Desktop Operating Platform / OS for Raspberry Pis
- [新东方云教室](https://roombox.xdf.cn/)
- [BBHouse](https://github.com/endcloud/bbhouse-tauri): A Bilibili Cross-Platform Desktop Client Powered By Tauri
- [Tampermonkey 阿里云盘](https://greasyfork.org/zh-CN/scripts/425955-%E9%98%BF%E9%87%8C%E4%BA%91%E7%9B%98)
- [arozos](https://github.com/tobychui/arozos)
- [GBCLStudio/fof-upload-qcloud](https://github.com/GBCLStudio/FoF-Upload-Qcloud)
- Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31) - Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31)
## Donate ## Donate
DPlayer is an MIT licensed open source project and completely free to use. However, the amount of effort needed to maintain and develop new features for the project is not sustainable without proper financial backing. DPlayer is an MIT licensed open source project and completely free to use. However, the amount of effort needed to maintain and develop new features for the project is not sustainable without proper financial backing.
You can support DPlayer via donations. ## One-time Donations
### Recurring Donation We accept donations through these channels:
- Become a Sponser on [GitHub](https://github.com/sponsors/DIYgod)
- Become a Sponser on [Patreon](https://www.patreon.com/DIYgod)
- Become a Sponser on [爱发电](https://afdian.net/@diygod)
- Contact us directly: i@diygod.me
### One-time Donation
We accept donations via the following ways:
- [WeChat Pay](https://diygod.me/images/wx.jpg)
- [Alipay](https://diygod.me/images/zfb.jpg)
- [Paypal](https://www.paypal.me/DIYgod) - [Paypal](https://www.paypal.me/DIYgod)
- [WeChat Pay](https://i.imgur.com/aq6PtWa.png)
- [Alipay](https://i.imgur.com/wv1Pj2k.png)
## Recurring Pledges
Recurring pledges come with exclusive perks, e.g. enabling faster GitHub response, having your name or your company logo listed in the DPlayer GitHub repository and this website.
- Become a backer or sponsor via [Patreon](https://www.patreon.com/DIYgod)
- E-mail us: i#html.love
## Author ## Author

View File

@ -65,23 +65,7 @@ function initPlayers() {
thumbnails: 'https://i.loli.net/2019/06/06/5cf8c5d9cec8510758.jpg' thumbnails: 'https://i.loli.net/2019/06/06/5cf8c5d9cec8510758.jpg'
}, },
subtitle: { subtitle: {
url: [ url: 'https://s-sh-17-dplayercdn.oss.dogecdn.com/hikarunara.vtt'
{
url: 'https://s-sh-17-dplayercdn.oss.dogecdn.com/hikarunara.vtt',
lang: 'zh-cn',
name: '光',
},
{
url: 'https://gist.githubusercontent.com/samdutton/ca37f3adaf4e23679957b8083e061177/raw/e19399fbccbc069a2af4266e5120ae6bad62699a/sample.vtt',
lang: 'en-us',
name: 'github',
},
],
defaultSubtitle: 7,
type: 'webvtt',
fontSize: '25px',
bottom: '10%',
color: '#b7daff'
}, },
danmaku: { danmaku: {
id: '9E2E3368B56CDBB4', id: '9E2E3368B56CDBB4',
@ -99,12 +83,10 @@ function initPlayers() {
loop: true, loop: true,
screenshot: true, screenshot: true,
airplay: true, airplay: true,
chromecast: true,
hotkey: true, hotkey: true,
logo: 'https://i.loli.net/2019/06/06/5cf8c5d94521136430.png', logo: 'https://i.loli.net/2019/06/06/5cf8c5d94521136430.png',
volume: 0.2, volume: 0.2,
mutex: true, mutex: true,
lang: 'zh-cn',
video: { video: {
url: 'https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4', url: 'https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4',
pic: 'https://i.loli.net/2019/06/06/5cf8c5d9c57b510947.png', pic: 'https://i.loli.net/2019/06/06/5cf8c5d9c57b510947.png',
@ -121,13 +103,11 @@ function initPlayers() {
danmaku: { danmaku: {
id: '9E2E3368B56CDBB4', id: '9E2E3368B56CDBB4',
api: 'https://api.prprpr.me/dplayer/', api: 'https://api.prprpr.me/dplayer/',
addition: ['https://s-sh-17-dplayercdn.oss.dogecdn.com/1678963.json'],
token: 'tokendemo', token: 'tokendemo',
maximum: 3000, maximum: 3000,
user: 'DIYgod', user: 'DIYgod',
bottom: '15%', bottom: '15%',
unlimited: true, unlimited: true
speedRate: 0.5,
}, },
contextmenu: [ contextmenu: [
{ {
@ -157,7 +137,7 @@ function initPlayers() {
const eventsEle = document.getElementById('events'); const eventsEle = document.getElementById('events');
for (let i = 0; i < events.length; i++) { for (let i = 0; i < events.length; i++) {
dp2.on(events[i], (info) => { dp2.on(events[i], (info) => {
eventsEle.innerHTML += `<p>Event: ${events[i]} ${info?`Data: <span>${JSON.stringify(info)}</span>`:''}</p>`; eventsEle.innerHTML += '<p>Event: ' + events[i] + '</p>';
eventsEle.scrollTop = eventsEle.scrollHeight; eventsEle.scrollTop = eventsEle.scrollHeight;
}); });
} }
@ -264,7 +244,7 @@ function clearPlayers() {
} }
function switchDPlayer() { function switchDPlayer() {
if (dp2.options.danmaku.id !== '5rGf5Y2X55qu6Z2p') { if (dp2.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') {
dp2.switchVideo({ dp2.switchVideo({
url: 'http://static.smartisanos.cn/common/video/t1-ui.mp4', url: 'http://static.smartisanos.cn/common/video/t1-ui.mp4',
pic: 'http://static.smartisanos.cn/pr/img/video/video_03_cc87ce5bdb.jpg', pic: 'http://static.smartisanos.cn/pr/img/video/video_03_cc87ce5bdb.jpg',

2
dist/DPlayer.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -71,16 +71,12 @@ export default {
methods: { methods: {
load: function () { load: function () {
this.options.container = this.$refs.dplayer; this.options.container = this.$refs.dplayer;
setTimeout(() => { this.dplayer = new window.DPlayer(this.options);
this.dplayer = new window.DPlayer(this.options);
}, 0);
} }
}, },
mounted: function () { mounted: function () {
if (this.immediate) { if (this.immediate) {
setTimeout(() => { this.dplayer = new window.DPlayer(this.options);
this.dplayer = new window.DPlayer(this.options);
}, 0);
} }
}, },
beforeDestroy: function () { beforeDestroy: function () {

View File

@ -1,10 +1,22 @@
module.exports = { module.exports = {
plugins: { plugins: {
'@vuepress/back-to-top': true, '@vuepress/google-analytics': {
umami: { ga: 'UA-48084758-9',
trackerUrl: 'https://umami.diygod.dev',
siteId: 'f3b469a2-8054-4bbb-8873-4035761aa4e3',
}, },
'@vuepress/pwa': {
serviceWorker: true,
updatePopup: {
'/zh/': {
message: '发现新内容可用',
buttonText: '刷新',
},
'/': {
message: 'New content is available',
buttonText: 'Refresh',
},
},
},
'@vuepress/back-to-top': true,
}, },
locales: { locales: {
'/zh/': { '/zh/': {
@ -26,11 +38,10 @@ module.exports = {
['script', { src: 'https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js' }], ['script', { src: 'https://cdn.jsdelivr.net/webtorrent/latest/webtorrent.min.js' }],
['script', { src: 'https://cdn.jsdelivr.net/npm/dplayer/dist/DPlayer.min.js' }], ['script', { src: 'https://cdn.jsdelivr.net/npm/dplayer/dist/DPlayer.min.js' }],
], ],
theme: 'vuepress-theme-dplayer',
themeConfig: { themeConfig: {
repo: 'MoePlayer/DPlayer', repo: 'MoePlayer/DPlayer',
editLinks: true, editLinks: true,
docsDir: '.', docsDir: 'docs',
locales: { locales: {
'/zh/': { '/zh/': {
lang: 'zh-CN', lang: 'zh-CN',

View File

@ -0,0 +1 @@
dplayer.js.org

View File

@ -1,65 +0,0 @@
<script>
export default {
name: 'CarbonAds',
watch: {
'$route' (to, from) {
if (
to.path !== from.path
// Only reload if the ad has been loaded
// otherwise it's possible that the script is appended but
// the ads are not loaded yet. This would result in duplicated ads.
&& this.$el.querySelector('#carbonads')
) {
this.$el.innerHTML = ''
this.load()
}
}
},
mounted () {
this.load()
},
methods: {
load () {
const s = document.createElement('script')
s.id = '_carbonads_js'
s.src = `//cdn.carbonads.com/carbon.js?serve=CEAI6277&placement=dplayerjsorg`
this.$el.appendChild(s)
}
},
render (h) {
return h('div', { class: 'carbon-ads' })
}
}
</script>
<style lang="stylus">
.carbon-ads
min-height 102px
padding 1.5rem 1.5rem 0
margin-bottom -0.5rem
font-size 0.75rem
a
color #444
font-weight normal
display inline
.carbon-img
float left
margin-right 1rem
border 1px solid $borderColor
img
display block
.carbon-poweredby
color #999
display block
margin-top 0.5em
@media (max-width: $MQMobile)
.carbon-ads
.carbon-img img
width 100px
height 77px
</style>

View File

@ -1,3 +0,0 @@
module.exports = {
extend: '@vuepress/theme-default',
};

View File

@ -1,21 +0,0 @@
<template>
<ParentLayout>
<template #sidebar-top>
<CarbonAds />
</template>
</ParentLayout>
</template>
<script>
import ParentLayout from '@parent-theme/layouts/Layout.vue'
import CarbonAds from '@theme/components/CarbonAds.vue'
export default {
name: 'Layout',
components: {
ParentLayout,
CarbonAds,
}
}
</script>

View File

@ -1,4 +0,0 @@
{
"name": "vuepress-theme-dplayer",
"main": "index.js"
}

View File

@ -8,6 +8,10 @@ Let's make DPlayer better, feel free to submit yours in [`Let me know!`](https:/
## Help ## Help
### Joining the Discussion
- [Telegram Group](https://t.me/adplayer)
### Creating issue ### Creating issue
- [MoePlayer/DPlayer/issues](https://github.com/MoePlayer/DPlayer/issues) - [MoePlayer/DPlayer/issues](https://github.com/MoePlayer/DPlayer/issues)
@ -36,7 +40,6 @@ Let's make DPlayer better, feel free to submit yours in [`Let me know!`](https:/
- [Selection](https://github.com/GreatSatan79/Selection): WordPress - [Selection](https://github.com/GreatSatan79/Selection): WordPress
- [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue - [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue
- [react-dplayer](https://github.com/hnsylitao/react-dplayer): React - [react-dplayer](https://github.com/hnsylitao/react-dplayer): React
- [rc-dplayer](https://github.com/tianfeng98/rc-dplayer): React
### Other ### Other
@ -54,13 +57,6 @@ Let's make DPlayer better, feel free to submit yours in [`Let me know!`](https:/
- [纸飞机南航青年网络社区](http://my.nuaa.edu.cn/video-video.html): 南京航空航天大学门户网站 - [纸飞机南航青年网络社区](http://my.nuaa.edu.cn/video-video.html): 南京航空航天大学门户网站
- [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站 - [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站
- [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统 - [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统
- [arozos](https://github.com/tobychui/arozos): General purposed Web Desktop Operating Platform / OS for Raspberry Pis
- [新东方云教室](https://roombox.xdf.cn/)
- [BBHouse](https://github.com/endcloud/bbhouse-tauri): A Bilibili Cross-Platform Desktop Client Powered By Tauri
- [Tampermonkey 阿里云盘](https://greasyfork.org/zh-CN/scripts/425955-%E9%98%BF%E9%87%8C%E4%BA%91%E7%9B%98)
- [arozos](https://github.com/tobychui/arozos)
- [GBCLStudio/fof-upload-qcloud](https://github.com/GBCLStudio/FoF-Upload-Qcloud)
- Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31)
## Contributors ## Contributors

View File

@ -14,16 +14,28 @@ sidebar: auto
## Special Thanks ## Special Thanks
### Sponsors ### Special Sponsors
&nbsp; &nbsp;
<div> <div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank"> <a href="https://www.cdnbye.com" target="_blank">
<img height="60px" src="https://player.dogecloud.com/img/logo_with_product3.png"> <img height="60px" src="https://cdnbye.oss-cn-beijing.aliyuncs.com/pic/cdnbye-dp.jpeg">
</a> </a>
</div> </div>
<div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank">
<img height="60px" src="https://i.imgur.com/C2NgugY.png">
</a>
</div>
### Sponsors
| [极酷社](https://www.acg.app) |
| :---------------------------: |
## Installation ## Installation
Using npm: Using npm:
@ -91,15 +103,13 @@ You can custom your player instance by those options
| loop | false | video loop | | loop | false | video loop |
| lang | navigator.language.toLowerCase() | values: 'en', 'zh-cn', 'zh-tw' | | lang | navigator.language.toLowerCase() | values: 'en', 'zh-cn', 'zh-tw' |
| screenshot | false | enable screenshot, if true, video and video poster must enable Cross-Origin | | screenshot | false | enable screenshot, if true, video and video poster must enable Cross-Origin |
| airplay | false | enable airplay in Safari | | airplay | true | enable airplay in Safari |
| chromecast | false | enable Chromecast |
| hotkey | true | enable hotkey, support FF, FR, volume control, play & pause | | hotkey | true | enable hotkey, support FF, FR, volume control, play & pause |
| preload | 'auto' | values: 'none', 'metadata', 'auto' | | preload | 'auto' | values: 'none', 'metadata', 'auto' |
| volume | 0.7 | default volume, notice that player will remember user setting, default volume will not work after user set volume themselves | | volume | 0.7 | default volume, notice that player will remember user setting, default volume will not work after user set volume themselves |
| playbackSpeed | [0.5, 0.75, 1, 1.25, 1.5, 2] | optional playback speed, or or you can set a custom one | | playbackSpeed | [0.5, 0.75, 1, 1.25, 1.5, 2] | optional playback speed, or or you can set a custom one |
| logo | - | showing logo in the top left corner, you can adjust its size and position by CSS | | logo | - | showing logo in the top left corner, you can adjust its size and position by CSS |
| apiBackend | - | getting and sending danmaku in your way, see [#live](#live) | | apiBackend | - | getting and sending danmaku in your way, see [#live](#live) |
| preventClickToggle | false | prevent toggle video play/pause status when click player |
| video | - | video info | | video | - | video info |
| video.quality | - | see [#Quality switching](#quality-switching) | | video.quality | - | see [#Quality switching](#quality-switching) |
| video.defaultQuality | - | see [#Quality switching](#quality-switching) | | video.defaultQuality | - | see [#Quality switching](#quality-switching) |
@ -123,7 +133,6 @@ You can custom your player instance by those options
| danmaku.user | 'DIYgod' | danmaku user name | | danmaku.user | 'DIYgod' | danmaku user name |
| danmaku.bottom | - | values like: '10px' '10%', the distance between the danmaku bottom and player bottom, in order to prevent warding off subtitle | | danmaku.bottom | - | values like: '10px' '10%', the distance between the danmaku bottom and player bottom, in order to prevent warding off subtitle |
| danmaku.unlimited | false | display all danmaku even though danmaku overlap, notice that player will remember user setting, default setting will not work after user set it themselves | | danmaku.unlimited | false | display all danmaku even though danmaku overlap, notice that player will remember user setting, default setting will not work after user set it themselves |
| danmaku.speedRate | 1 | danmaku speed multiplier, the larger the faster |
| contextmenu | [] | custom contextmenu | | contextmenu | [] | custom contextmenu |
| highlight | [] | custom time markers upon progress bar | | highlight | [] | custom time markers upon progress bar |
| mutex | true | prevent to play multiple player at the same time, pause other players when this player start play | | mutex | true | prevent to play multiple player at the same time, pause other players when this player start play |
@ -163,7 +172,6 @@ const dp = new DPlayer({
user: 'DIYgod', user: 'DIYgod',
bottom: '15%', bottom: '15%',
unlimited: true, unlimited: true,
speedRate: 0.5,
}, },
contextmenu: [ contextmenu: [
{ {
@ -204,7 +212,7 @@ const dp = new DPlayer({
- `dp.toggle()`: toggle between play and pause - `dp.toggle()`: toggle between play and pause
- `dp.on(event: string, handler: function)`: bind video and player events, [see more details](https://dplayer.diygod.dev/guide.html#event-binding) - `dp.on(event: string, handler: function)`: bind video and player events, [see more details](http://dplayer.js.org/#/home?id=event-binding)
- `dp.switchVideo(video, danmaku)`: switch to a new video - `dp.switchVideo(video, danmaku)`: switch to a new video
@ -412,56 +420,15 @@ const dp = new DPlayer({
}); });
``` ```
When using `customType` ([Work with other MSE library](#work-with-other-mse-library)), you should listen for the `destroy` and `quality_end` events to destroy the MSE library instance in a timely manner to avoid memory leaks.
```js
const dp = new DPlayer({
container: document.getElementById('dplayer'),
video: {
quality: [
{
name: 'HD',
url: 'demo_hd.m3u8',
type: 'Hls',
},
{
name: 'SD',
url: 'demo_sd.m3u8',
type: 'Hls',
},
],
defaultQuality: 0,
customType: {
Hls: (video, player) => {
const hls = new window.Hls();
hls.loadSource(video.src);
hls.attachMedia(video);
hls.sessionId = crypto.randomUUID();
player.sessionId = hls.sessionId;
player.events.on('destroy', () => {
hls.destroy();
});
player.events.on('quality_end', () => {
if (hls.sessionId !== player.sessionId) {
hls.destroy();
}
});
},
},
},
});
```
## Danmaku ## Danmaku
### Danmaku API ### Danmaku API
`danmaku.api` `danmaku.api`
<!-- **Ready-made API** **Ready-made API**
url: https://api.prprpr.me/dplayer/ --> url: https://api.prprpr.me/dplayer/
Daily backup data: [DPlayer-data](https://github.com/DIYgod/DPlayer-data) Daily backup data: [DPlayer-data](https://github.com/DIYgod/DPlayer-data)
@ -736,32 +703,22 @@ DPlayer can work with any MSE library via `customType` option.
```html ```html
<div id="dplayer"></div> <div id="dplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/cdnbye@latest"></script> <script src="pearplayer.js"></script>
<script src="DPlayer.min.js"></script> <script src="DPlayer.min.js"></script>
``` ```
```js ```js
var type = 'normal';
if (Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
type = 'customHls';
}
const dp = new DPlayer({ const dp = new DPlayer({
container: document.getElementById('dplayer'), container: document.getElementById('dplayer'),
video: { video: {
url: 'demo.m3u8', url: 'https://qq.webrtc.win/tv/Pear-Demo-Yosemite_National_Park.mp4',
type: type, type: 'pearplayer',
customType: { customType: {
customHls: function (video, player) { pearplayer: function (video, player) {
const hls = new Hls({ new PearPlayer(video, {
debug: false, src: video.src,
// Other hlsjsConfig options provided by hls.js autoplay: player.options.autoplay,
p2pConfig: {
live: false,
// Other p2pConfig options provided by CDNBye http://www.cdnbye.com/en/
},
}); });
hls.loadSource(video.src);
hls.attachMedia(video);
}, },
}, },
}, },
@ -790,11 +747,11 @@ const dp = new DPlayer({
apiBackend: { apiBackend: {
read: function (options) { read: function (options) {
console.log('Pretend to connect WebSocket'); console.log('Pretend to connect WebSocket');
options.success([]); callback();
}, },
send: function (options) { send: function (options) {
console.log('Pretend to send danmaku via WebSocket', options.data); console.log('Pretend to send danmaku via WebSocket', options.data);
options.success(); callback();
}, },
}, },
video: { video: {
@ -829,4 +786,4 @@ For full browser support it should look like this:
### Why can't player autoplay in some mobile browsers? ### Why can't player autoplay in some mobile browsers?
Most mobile browsers forbid video autoplay, you wont be able to achieve it without hacks. Most mobile browsers forbid video autoplay, you wont be able to achieve it without hacks.

View File

@ -1,15 +0,0 @@
{
"name": "docs",
"scripts": {
"docs:dev": "vuepress dev",
"docs:build": "vuepress build"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@vuepress/plugin-back-to-top": "^1.9.10",
"@vuepress/plugin-google-analytics": "^1.9.10",
"vuepress": "^1.9.10",
"vuepress-plugin-umami": "^0.0.4"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,30 +10,44 @@ If you run a business and are using DPlayer in a revenue-generating product, it
If you are an individual user and have enjoyed the productivity of using DPlayer, consider donating as a sign of appreciation - like buying me coffee once in a while :) If you are an individual user and have enjoyed the productivity of using DPlayer, consider donating as a sign of appreciation - like buying me coffee once in a while :)
You can support DPlayer via donations. You can support DPlayer development via the following methods:
### Recurring Donation ## One-time Donations
Recurring donors will be rewarded via express issue response, or even have your name displayed on our GitHub page and website. We accept donations through these channels:
- Become a Sponser on [GitHub](https://github.com/sponsors/DIYgod)
- Become a Sponser on [Patreon](https://www.patreon.com/DIYgod)
- Become a Sponser on [爱发电](https://afdian.net/@diygod)
- Contact us directly: i@diygod.me
### One-time Donation
We accept donations via the following ways:
- [WeChat Pay](https://diygod.me/images/wx.jpg)
- [Alipay](https://diygod.me/images/zfb.jpg)
- [Paypal](https://www.paypal.me/DIYgod) - [Paypal](https://www.paypal.me/DIYgod)
- [WeChat Pay](https://i.imgur.com/aq6PtWa.png)
- [Alipay](https://i.imgur.com/wv1Pj2k.png)
## Sponsors ## Recurring Pledges
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank"> Recurring pledges come with exclusive perks, e.g. having your name or your company logo listed in the DPlayer GitHub repository and this website.
<img width="222px" src="hhttps://player.dogecloud.com/img/logo_with_product3.png">
- Become a Backer or a Sponser on [Patreon](https://www.patreon.com/DIYgod)
- E-mail us: i#diygod.me
## Current Premium Sponsors
### Special Sponsors
<div>
<a href="https://www.cdnbye.com" target="_blank">
<img height="60px" src="https://cdnbye.oss-cn-beijing.aliyuncs.com/pic/cdnbye-dp.jpeg">
</a> </a>
</div>
<div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank">
<img height="60px" src="https://i.imgur.com/C2NgugY.png">
</a>
</div>
### Sponsors
| [极酷社](https://www.acg.app) |
| :---------------------------: |
## DPlayer contributors ## DPlayer contributors

View File

@ -1,7 +1,7 @@
--- ---
home: true home: true
actionText: Get Started → actionText: Get Started →
actionLink: /zh/guide/ actionLink: /guide/
footer: MIT Licensed | Made with love by DIYgod footer: MIT Licensed | Made with love by DIYgod
--- ---
@ -9,4 +9,4 @@ footer: MIT Licensed | Made with love by DIYgod
<DPlayer :immediate="true"></DPlayer> <DPlayer :immediate="true"></DPlayer>
</div> </div>
<div class="hero custom"><p class="action"><router-link to="/zh/guide/" class="nav-link action-button">快速上手 →</router-link></p></div> <div class="hero custom"><p class="action"><router-link to="/guide/" class="nav-link action-button">快速上手 →</router-link></p></div>

View File

@ -8,6 +8,10 @@ sidebar: auto
## 帮助 ## 帮助
### 参与讨论
- [Telegram 群](https://t.me/adplayer)
### 提交 issue ### 提交 issue
- [MoePlayer/DPlayer/issues](https://github.com/MoePlayer/DPlayer/issues) - [MoePlayer/DPlayer/issues](https://github.com/MoePlayer/DPlayer/issues)
@ -36,7 +40,6 @@ sidebar: auto
- [Selection](https://github.com/GreatSatan79/Selection): WordPress - [Selection](https://github.com/GreatSatan79/Selection): WordPress
- [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue - [Vue-DPlayer](https://github.com/sinchang/vue-dplayer): Vue
- [react-dplayer](https://github.com/hnsylitao/react-dplayer): React - [react-dplayer](https://github.com/hnsylitao/react-dplayer): React
- [rc-dplayer](https://github.com/tianfeng98/rc-dplayer): React
### 其他 ### 其他
@ -54,10 +57,3 @@ sidebar: auto
- [纸飞机南航青年网络社区](http://my.nuaa.edu.cn/video-video.html): 南京航空航天大学门户网站 - [纸飞机南航青年网络社区](http://my.nuaa.edu.cn/video-video.html): 南京航空航天大学门户网站
- [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站 - [otomads](https://otomads.com/): 专注于音 MAD 的视频弹幕网站
- [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统 - [Cloudreve](https://github.com/HFO4/Cloudreve): 基于 ThinkPHP 构建的网盘系统
- [arozos](https://github.com/tobychui/arozos): General purposed Web Desktop Operating Platform / OS for Raspberry Pis
- [新东方云教室](https://roombox.xdf.cn/)
- [BBHouse](https://github.com/endcloud/bbhouse-tauri): A Bilibili Cross-Platform Desktop Client Powered By Tauri
- [Tampermonkey 阿里云盘](https://greasyfork.org/zh-CN/scripts/425955-%E9%98%BF%E9%87%8C%E4%BA%91%E7%9B%98)
- [arozos](https://github.com/tobychui/arozos)
- [GBCLStudio/fof-upload-qcloud](https://github.com/GBCLStudio/FoF-Upload-Qcloud)
- Feel free to submit yours in [`Let me know!`](https://github.com/MoePlayer/DPlayer/issues/31)

View File

@ -14,14 +14,26 @@ sidebar: auto
## Special Thanks ## Special Thanks
### Sponsors ### Special Sponsors
<div>
<a href="https://www.cdnbye.com" target="_blank">
<img height="60px" src="https://cdnbye.oss-cn-beijing.aliyuncs.com/pic/cdnbye-dp.jpeg">
</a>
</div>
<div> <div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank"> <a href="https://www.dogecloud.com/?ref=dplayer" target="_blank">
<img height="60px" src="https://player.dogecloud.com/img/logo_with_product3.png"> <img height="60px" src="https://i.imgur.com/C2NgugY.png">
</a> </a>
</div> </div>
### Sponsors
| [极酷社](https://www.acg.app) |
| :---------------------------: |
## 安装 ## 安装
使用 npm: 使用 npm:
@ -82,14 +94,12 @@ DPlayer 有丰富的参数可以自定义你的播放器实例
| lang | navigator.language.toLowerCase() | 可选值: 'en', 'zh-cn', 'zh-tw' | | lang | navigator.language.toLowerCase() | 可选值: 'en', 'zh-cn', 'zh-tw' |
| screenshot | false | 开启截图,如果开启,视频和视频封面需要允许跨域 | | screenshot | false | 开启截图,如果开启,视频和视频封面需要允许跨域 |
| hotkey | true | 开启热键,支持快进、快退、音量控制、播放暂停 | | hotkey | true | 开启热键,支持快进、快退、音量控制、播放暂停 |
| airplay | false | 在 Safari 中开启 AirPlay | | airplay | true | 在 Safari 中开启 AirPlay |
| chromecast | false | 启用 Chromecast |
| preload | 'auto' | 视频预加载,可选值: 'none', 'metadata', 'auto' | | preload | 'auto' | 视频预加载,可选值: 'none', 'metadata', 'auto' |
| volume | 0.7 | 默认音量,请注意播放器会记忆用户设置,用户手动设置音量后默认音量即失效 | | volume | 0.7 | 默认音量,请注意播放器会记忆用户设置,用户手动设置音量后默认音量即失效 |
| playbackSpeed | [0.5, 0.75, 1, 1.25, 1.5, 2] | 可选的播放速率,可以设置成自定义的数组 | | playbackSpeed | [0.5, 0.75, 1, 1.25, 1.5, 2] | 可选的播放速率,可以设置成自定义的数组 |
| logo | - | 在左上角展示一个 logo你可以通过 CSS 调整它的大小和位置 | | logo | - | 在左上角展示一个 logo你可以通过 CSS 调整它的大小和位置 |
| apiBackend | - | 自定义获取和发送弹幕行为,见[#直播](#直播) | | apiBackend | - | 自定义获取和发送弹幕行为,见[#直播](#直播) |
| preventClickToggle | false | 阻止点击播放器时候自动切换播放/暂停 |
| video | - | 视频信息 | | video | - | 视频信息 |
| video.quality | - | 见[#清晰度切换](#清晰度切换) | | video.quality | - | 见[#清晰度切换](#清晰度切换) |
| video.defaultQuality | - | 见[#清晰度切换](#清晰度切换) | | video.defaultQuality | - | 见[#清晰度切换](#清晰度切换) |
@ -113,7 +123,6 @@ DPlayer 有丰富的参数可以自定义你的播放器实例
| danmaku.user | 'DIYgod' | 弹幕用户名 | | danmaku.user | 'DIYgod' | 弹幕用户名 |
| danmaku.bottom | - | 弹幕距离播放器底部的距离,防止遮挡字幕,取值形如: '10px' '10%' | | danmaku.bottom | - | 弹幕距离播放器底部的距离,防止遮挡字幕,取值形如: '10px' '10%' |
| danmaku.unlimited | false | 海量弹幕模式,即使重叠也展示全部弹幕,请注意播放器会记忆用户设置,用户手动设置后即失效 | | danmaku.unlimited | false | 海量弹幕模式,即使重叠也展示全部弹幕,请注意播放器会记忆用户设置,用户手动设置后即失效 |
| danmaku.speedRate | 1 | 弹幕速度倍率,越大速度越快 |
| contextmenu | [] | 自定义右键菜单 | | contextmenu | [] | 自定义右键菜单 |
| highlight | [] | 自定义进度条提示点 | | highlight | [] | 自定义进度条提示点 |
| mutex | true | 互斥,阻止多个播放器同时播放,当前播放器播放时暂停其他播放器 | | mutex | true | 互斥,阻止多个播放器同时播放,当前播放器播放时暂停其他播放器 |
@ -153,7 +162,6 @@ const dp = new DPlayer({
user: 'DIYgod', user: 'DIYgod',
bottom: '15%', bottom: '15%',
unlimited: true, unlimited: true,
speedRate: 0.5,
}, },
contextmenu: [ contextmenu: [
{ {
@ -396,56 +404,15 @@ const dp = new DPlayer({
}); });
``` ```
在使用 `customType` [配合其他 MSE 库使用](#配合其他-mse-库使用)时,需要监听 `destroy``quality_end` 事件及时销毁 MSE 库实例,避免内存泄漏
```js
const dp = new DPlayer({
container: document.getElementById('dplayer'),
video: {
quality: [
{
name: 'HD',
url: 'demo_hd.m3u8',
type: 'Hls',
},
{
name: 'SD',
url: 'demo_sd.m3u8',
type: 'Hls',
},
],
defaultQuality: 0,
customType: {
Hls: (video, player) => {
const hls = new window.Hls();
hls.loadSource(video.src);
hls.attachMedia(video);
hls.sessionId = crypto.randomUUID();
player.sessionId = hls.sessionId;
player.events.on('destroy', () => {
hls.destroy();
});
player.events.on('quality_end', () => {
if (hls.sessionId !== player.sessionId) {
hls.destroy();
}
});
},
},
},
});
```
## 弹幕 ## 弹幕
### 弹幕接口 ### 弹幕接口
`danmaku.api` `danmaku.api`
<!-- **现成的接口** **现成的接口**
链接: https://api.prprpr.me/dplayer/ --> 链接: https://api.prprpr.me/dplayer/
每日备份: [DPlayer-data](https://github.com/DIYgod/DPlayer-data) 每日备份: [DPlayer-data](https://github.com/DIYgod/DPlayer-data)
@ -716,36 +683,26 @@ const dp = new DPlayer({
### 配合其他 MSE 库使用 ### 配合其他 MSE 库使用
DPlayer 可以通过 `customType` 参数与任何 MSE 库一起使用,例如支持 P2P 插件: DPlayer 可以通过 `customType` 参数与任何 MSE 库一起使用
```html ```html
<div id="dplayer"></div> <div id="dplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/cdnbye@latest"></script> <script src="pearplayer.js"></script>
<script src="DPlayer.min.js"></script> <script src="DPlayer.min.js"></script>
``` ```
```js ```js
var type = 'normal';
if (Hls.isSupported() && Hls.WEBRTC_SUPPORT) {
type = 'customHls';
}
const dp = new DPlayer({ const dp = new DPlayer({
container: document.getElementById('dplayer'), container: document.getElementById('dplayer'),
video: { video: {
url: 'demo.m3u8', url: 'https://qq.webrtc.win/tv/Pear-Demo-Yosemite_National_Park.mp4',
type: type, type: 'pearplayer',
customType: { customType: {
customHls: function (video, player) { pearplayer: function (video, player) {
const hls = new Hls({ new PearPlayer(video, {
debug: false, src: video.src,
// Other hlsjsConfig options provided by hls.js autoplay: player.options.autoplay,
p2pConfig: {
live: false, // 如果是直播设为true
// Other p2pConfig options provided by CDNBye http://www.cdnbye.com/cn/
},
}); });
hls.loadSource(video.src);
hls.attachMedia(video);
}, },
}, },
}, },
@ -772,13 +729,13 @@ const dp = new DPlayer({
live: true, live: true,
danmaku: true, danmaku: true,
apiBackend: { apiBackend: {
read: function (options) { read: function (endpoint, callback) {
console.log('Pretend to connect WebSocket'); console.log('Pretend to connect WebSocket');
options.success([]); callback();
}, },
send: function (options) { send: function (endpoint, danmakuData, callback) {
console.log('Pretend to send danmaku via WebSocket', options.data); console.log('Pretend to send danmaku via WebSocket', danmakuData);
options.success(); callback();
}, },
}, },
video: { video: {
@ -813,4 +770,4 @@ dp.danmaku.draw(danmaku);
### 为什么播放器不能在手机上自动播放? ### 为什么播放器不能在手机上自动播放?
大多数移动端浏览器禁止了视频自动播放。 大多数移动端浏览器禁止了视频自动播放。

View File

@ -10,31 +10,45 @@ DPlayer 是采用 MIT 许可的开源项目,使用完全免费。 但是随着
如果你是个人开发者并且享受 DPlayer 带来的高开发效率,可以用捐助来表示你的谢意 —— 比如偶尔给我买杯咖啡 :) 如果你是个人开发者并且享受 DPlayer 带来的高开发效率,可以用捐助来表示你的谢意 —— 比如偶尔给我买杯咖啡 :)
你可以通过下列的方法来赞助 DPlayer 的开发. 你可以通过下列的方法来赞助 DPlayer 的开发。
## 周期性赞助
- 通过 [GitHub](https://github.com/sponsors/DIYgod) 赞助
- 通过 [Patreon](https://www.patreon.com/DIYgod) 赞助
- 通过 [爱发电](https://afdian.net/@diygod) 赞助
- 给我们发邮件联系赞助事宜: <i@diygod.me>
## 一次性赞助 ## 一次性赞助
我们通过以下方式接受赞助: 我们通过以下方式接受赞助:
- [微信支付](https://diygod.me/images/wx.jpg) - [微信支付](https://i.imgur.com/aq6PtWa.png)
- [支付宝](https://diygod.me/images/zfb.jpg) - [支付宝](https://i.imgur.com/wv1Pj2k.png)
- [Paypal](https://www.paypal.me/DIYgod) - [Paypal](https://www.paypal.me/DIYgod)
## 赞助商 ## 周期性赞助
周期性赞助可以获得额外的回报,比如你的名字或你的公司 logo 会出现在 DPlayer 的 GitHub 仓库和现在我们的官网中。
- 通过 [Patreon](https://www.patreon.com/DIYgod) 赞助
- 给我们发邮件联系赞助事宜: i#diygod.me
## 当前的顶级赞助商
### Special Sponsors
<div>
<a href="https://www.cdnbye.com" target="_blank">
<img height="60px" src="https://cdnbye.oss-cn-beijing.aliyuncs.com/pic/cdnbye-dp.jpeg">
</a>
</div>
<div> <div>
<a href="https://www.dogecloud.com/?ref=dplayer" target="_blank"> <a href="https://www.dogecloud.com/?ref=dplayer" target="_blank">
<img height="60px" src="https://player.dogecloud.com/img/logo_with_product3.png"> <img height="60px" src="https://i.imgur.com/C2NgugY.png">
</a> </a>
</div> </div>
### Sponsors
| [极酷社](https://www.acg.app) |
| :---------------------------: |
## DPlayer 贡献者 ## DPlayer 贡献者
感谢所有贡献者。 感谢所有贡献者。

View File

@ -1,20 +1,18 @@
{ {
"name": "dplayer", "name": "dplayer",
"version": "1.27.1", "version": "1.26.0",
"description": "Wow, such a lovely HTML5 danmaku video player", "description": "Wow, such a lovely HTML5 danmaku video player",
"main": "dist/DPlayer.min.js", "main": "dist/DPlayer.min.js",
"scripts": { "scripts": {
"start": "npm run dev", "start": "npm run dev",
"build": "cross-env NODE_ENV=production webpack --config webpack/prod.config.js --progress", "build": "cross-env NODE_ENV=production webpack --config webpack/prod.config.js --progress --display-error-details --colors",
"dev": "cross-env NODE_ENV=development webpack serve --config webpack/dev.config.js", "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack/dev.config.js --watch --colors",
"test": "", "test": "eslint src webpack",
"format": "prettier \"**/*.{js,json,md}\" --write", "format": "eslint \"**/*.js\" --fix && prettier \"**/*.{js,json,md}\" --write",
"format:staged": "eslint \"**/*.js\" --fix && pretty-quick --staged --verbose --pattern \"**/*.{js,json,md}\"",
"format:check": "eslint \"**/*.js\" && prettier-check \"**/*.{js,json,md}\"",
"docs:dev": "vuepress dev docs", "docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs", "docs:build": "vuepress build docs"
"prepare": "husky install"
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}, },
"files": [ "files": [
"dist" "dist"
@ -29,6 +27,9 @@
"video", "video",
"html5" "html5"
], ],
"gitHooks": {
"pre-commit": "npm run format:staged"
},
"author": "DIYgod", "author": "DIYgod",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
@ -36,43 +37,46 @@
}, },
"homepage": "https://github.com/DIYgod/DPlayer#readme", "homepage": "https://github.com/DIYgod/DPlayer#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.22.10", "@babel/core": "^7.6.0",
"@babel/preset-env": "^7.22.10", "@babel/preset-env": "^7.4.5",
"@vuepress/plugin-pwa": "1.9.9", "@vuepress/plugin-back-to-top": "1.5.1",
"@vuepress/plugin-google-analytics": "1.5.1",
"@vuepress/plugin-pwa": "1.5.1",
"art-template": "4.13.2", "art-template": "4.13.2",
"art-template-loader": "1.4.3", "art-template-loader": "1.4.3",
"autoprefixer": "^10.4.15", "autoprefixer": "^9.6.1",
"babel-loader": "^9.1.3", "babel-loader": "^8.0.6",
"cross-env": "^7.0.0", "cross-env": "^7.0.0",
"css-loader": "^6.8.1", "css-loader": "^3.2.0",
"cssnano": "^6.0.1", "cssnano": "^4.1.10",
"exports-loader": "^4.0.0", "eslint": "^7.0.0",
"eslint-config-prettier": "^6.3.0",
"eslint-loader": "^4.0.0",
"eslint-plugin-prettier": "^3.1.1",
"exports-loader": "^0.7.0",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"git-revision-webpack-plugin": "^5.0.0", "git-revision-webpack-plugin": "^3.0.3",
"husky": "^8.0.3", "mini-css-extract-plugin": "0.9.0",
"less": "^4.2.0", "node-sass": "^4.12.0",
"less-loader": "^11.1.3", "postcss-loader": "^3.0.0",
"lint-staged": "^13.3.0", "prettier": "^2.0.4",
"mini-css-extract-plugin": "2.7.6",
"postcss": "^8.4.27",
"postcss-loader": "^7.3.3",
"postcss-preset-env": "^9.1.1",
"prettier": "^3.0.1",
"prettier-check": "^2.0.0", "prettier-check": "^2.0.0",
"pretty-quick": "^3.0.0", "pretty-quick": "^2.0.0",
"sass-loader": "^8.0.0",
"strip-loader": "^0.1.2", "strip-loader": "^0.1.2",
"style-loader": "^3.3.3", "style-loader": "^1.0.0",
"svg-inline-loader": "0.8.2", "svg-inline-loader": "0.8.2",
"template-string-optimize-loader": "^3.0.0", "template-string-optimize-loader": "^3.0.0",
"url-loader": "^4.1.0", "url-loader": "^4.1.0",
"webpack": "^5.88.2", "vuepress": "1.5.1",
"webpack-cli": "5.1.4", "webpack": "^4.40.2",
"webpack-dev-server": "^4.15.1", "webpack-cli": "3.3.11",
"webpack-dev-server": "^3.8.1",
"yorkie": "^2.0.0" "yorkie": "^2.0.0"
}, },
"dependencies": { "dependencies": {
"axios": "1.4.0", "axios": "0.19.2",
"balloon-css": "^1.0.3", "balloon-css": "^1.0.3",
"promise-polyfill": "8.3.0" "promise-polyfill": "8.1.3"
} }
} }

File diff suppressed because it is too large Load Diff

View File

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

Before

Width:  |  Height:  |  Size: 642 B

View File

@ -60,11 +60,11 @@
animation: diplayer-loading-dot-fade .8s ease infinite; animation: diplayer-loading-dot-fade .8s ease infinite;
opacity: 0; opacity: 0;
transform-origin: 4px 4px; transform-origin: 4px 4px;
each(range(7), { @for $i from 7 through 1 {
&.diplayer-loading-dot-@{value} { &.diplayer-loading-dot-#{$i} {
animation-delay: (@value * 0.1s); animation-delay: .1s * $i;
} }
}); }
} }
@keyframes diplayer-loading-dot-fade { @keyframes diplayer-loading-dot-fade {
0% { 0% {

View File

@ -408,41 +408,6 @@
display: inline-block; display: inline-block;
height: 100%; height: 100%;
} }
.dplayer-subtitles {
display: inline-block;
height: 100%;
.dplayer-subtitles-box {
position: absolute;
right: 0;
bottom: 50px;
transform: scale(0);
width: fit-content;
max-width: 240px;
min-width: 120px;
border-radius: 2px;
background: rgba(28, 28, 28, 0.9);
padding: 7px 0;
transition: all .3s ease-in-out;
overflow: auto;
z-index: 2;
&.dplayer-subtitles-panel {
display: block;
}
&.dplayer-subtitles-box-open {
transform: scale(1);
}
}
.dplayer-subtitles-item {
height: 30px;
padding: 5px 10px;
box-sizing: border-box;
cursor: pointer;
position: relative;
&:hover {
background-color: rgba(255, 255, 255, .1);
}
}
}
.dplayer-setting { .dplayer-setting {
display: inline-block; display: inline-block;
height: 100%; height: 100%;

View File

@ -12,7 +12,7 @@
user-select: none; user-select: none;
cursor: default; cursor: default;
white-space: nowrap; white-space: nowrap;
text-shadow: 0.5px 0.5px 0.5px rgba(0, 0, 0, 0.5); text-shadow: .5px .5px .5px rgba(0, 0, 0, .5);
&--demo { &--demo {
position: absolute; position: absolute;
visibility: hidden; visibility: hidden;
@ -24,8 +24,7 @@
transform: translateX(100%); transform: translateX(100%);
&.dplayer-danmaku-move { &.dplayer-danmaku-move {
will-change: transform; will-change: transform;
animation-name: danmaku; animation: danmaku 5s linear;
animation-timing-function: linear;
animation-play-state: paused; animation-play-state: paused;
} }
} }
@ -42,8 +41,7 @@
visibility: hidden; visibility: hidden;
&.dplayer-danmaku-move { &.dplayer-danmaku-move {
will-change: visibility; will-change: visibility;
animation-name: danmaku-center; animation: danmaku-center 4s linear;
animation-timing-function: linear;
animation-play-state: paused; animation-play-state: paused;
} }
} }
@ -55,4 +53,4 @@
visibility: visible; visibility: visible;
} }
} }
} }

View File

@ -1,53 +0,0 @@
.dplayer-notice-list{
position: absolute;
bottom: 60px;
left: 20px;
.dplayer-notice {
border-radius: 2px;
background: rgba(28, 28, 28, 0.9);
transition: all .3s ease-in-out;
overflow: hidden;
color: #fff;
display: table;
pointer-events: none;
animation: showNotice .3s ease 1 forwards;
}
.remove-notice{
animation: removeNotice .3s ease 1 forwards;
}
}
@keyframes showNotice {
from {
padding: 0;
font-size: 0;
margin-top: 0;
}
to {
padding: 7px 20px;
font-size: 14px;
margin-top: 5px;
}
}
@keyframes removeNotice {
0%{
padding: 7px 20px;
font-size: 14px;
margin-top: 5px;
}
20%{
font-size: 12px;
}
21%{
font-size: 0;
padding: 7px 10px;
}
100%{
padding: 0;
margin-top: 0;
font-size: 0;
}
}

14
src/css/notice.scss Normal file
View File

@ -0,0 +1,14 @@
.dplayer-notice {
opacity: 0;
position: absolute;
bottom: 60px;
left: 20px;
font-size: 14px;
border-radius: 2px;
background: rgba(28, 28, 28, 0.9);
padding: 7px 20px;
transition: all .3s ease-in-out;
overflow: hidden;
color: #fff;
pointer-events: none;
}

View File

@ -29,7 +29,23 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
transform: translate(0, 0); transform: translate(0, 0);
.dplayer-danmaku {
.dplayer-danmaku-top,
.dplayer-danmaku-bottom {
&.dplayer-danmaku-move {
animation: danmaku-center 6s linear;
animation-play-state: inherit;
}
}
.dplayer-danmaku-right {
&.dplayer-danmaku-move {
animation: danmaku 8s linear;
animation-play-state: inherit;
}
}
}
} }
&.dplayer-no-danmaku { &.dplayer-no-danmaku {
@ -151,7 +167,6 @@
.dplayer-volume, .dplayer-volume,
.dplayer-camera-icon, .dplayer-camera-icon,
.dplayer-airplay-icon, .dplayer-airplay-icon,
.dplayer-chromecast-icon,
.dplayer-play-icon { .dplayer-play-icon {
display: none; display: none;
} }

View File

@ -76,7 +76,7 @@ class Comment {
// text can't be empty // text can't be empty
if (!this.player.template.commentInput.value.replace(/^\s+|\s+$/g, '')) { if (!this.player.template.commentInput.value.replace(/^\s+|\s+$/g, '')) {
this.player.notice(this.player.tran('please-input-danmaku')); this.player.notice(this.player.tran('Please input danmaku content!'));
return; return;
} }

View File

@ -12,7 +12,7 @@ class ContextMenu {
} }
}); });
this.contextmenuHandler = (e) => { this.player.container.addEventListener('contextmenu', (e) => {
if (this.shown) { if (this.shown) {
this.hide(); this.hide();
return; return;
@ -27,8 +27,7 @@ class ContextMenu {
this.player.template.mask.addEventListener('click', () => { this.player.template.mask.addEventListener('click', () => {
this.hide(); this.hide();
}); });
}; });
this.player.container.addEventListener('contextmenu', this.contextmenuHandler);
} }
show(x, y) { show(x, y) {
@ -63,10 +62,6 @@ class ContextMenu {
this.shown = false; this.shown = false;
this.player.events.trigger('contextmenu_hide'); this.player.events.trigger('contextmenu_hide');
} }
destroy() {
this.player.container.removeEventListener('contextmenu', this.contextmenuHandler);
}
} }
export default ContextMenu; export default ContextMenu;

View File

@ -2,21 +2,24 @@ import utils from './utils';
import Thumbnails from './thumbnails'; import Thumbnails from './thumbnails';
import Icons from './icons'; import Icons from './icons';
let cast;
let runOnce = true;
let isCasting = false;
class Controller { class Controller {
constructor(player) { constructor(player) {
this.player = player; this.player = player;
this.autoHideTimer = 0; this.autoHideTimer = 0;
if (!utils.isMobile) { if (!utils.isMobile) {
this.setAutoHideHandler = this.setAutoHide.bind(this); this.player.container.addEventListener('mousemove', () => {
this.player.container.addEventListener('mousemove', this.setAutoHideHandler); this.setAutoHide();
this.player.container.addEventListener('click', this.setAutoHideHandler); });
this.player.on('play', this.setAutoHideHandler); this.player.container.addEventListener('click', () => {
this.player.on('pause', this.setAutoHideHandler); this.setAutoHide();
});
this.player.on('play', () => {
this.setAutoHide();
});
this.player.on('pause', () => {
this.setAutoHide();
});
} }
this.initPlayButton(); this.initPlayButton();
@ -25,15 +28,11 @@ class Controller {
this.initFullButton(); this.initFullButton();
this.initQualityButton(); this.initQualityButton();
this.initScreenshotButton(); this.initScreenshotButton();
// if subtitle url not array, not init old single subtitle button this.initSubtitleButton();
if (this.player.options.subtitle) {
if (typeof this.player.options.subtitle.url === 'string') {
this.initSubtitleButton();
}
}
this.initHighlights(); this.initHighlights();
this.initAirplayButton(); if (utils.isSafari) {
this.initChromecastButton(); this.initAirplayButton();
}
if (!utils.isMobile) { if (!utils.isMobile) {
this.initVolumeButton(); this.initVolumeButton();
} }
@ -49,14 +48,12 @@ class Controller {
}); });
if (!utils.isMobile) { if (!utils.isMobile) {
if (!this.player.options.preventClickToggle) { this.player.template.videoWrap.addEventListener('click', () => {
this.player.template.videoWrap.addEventListener('click', () => { this.player.toggle();
this.player.toggle(); });
}); this.player.template.controllerMask.addEventListener('click', () => {
this.player.template.controllerMask.addEventListener('click', () => { this.player.toggle();
this.player.toggle(); });
});
}
} else { } else {
this.player.template.videoWrap.addEventListener('click', () => { this.player.template.videoWrap.addEventListener('click', () => {
this.toggle(); this.toggle();
@ -71,7 +68,7 @@ class Controller {
this.player.on('durationchange', () => { this.player.on('durationchange', () => {
if (this.player.video.duration !== 1 && this.player.video.duration !== Infinity) { if (this.player.video.duration !== 1 && this.player.video.duration !== Infinity) {
if (this.player.options.highlight) { if (this.player.options.highlight) {
const highlights = this.player.template.playedBarWrap.querySelectorAll('.dplayer-highlight'); const highlights = document.querySelectorAll('.dplayer-highlight');
[].slice.call(highlights, 0).forEach((item) => { [].slice.call(highlights, 0).forEach((item) => {
this.player.template.playedBarWrap.removeChild(item); this.player.template.playedBarWrap.removeChild(item);
}); });
@ -122,11 +119,11 @@ class Controller {
percentage = Math.min(percentage, 1); percentage = Math.min(percentage, 1);
this.player.bar.set('played', percentage, 'width'); this.player.bar.set('played', percentage, 'width');
this.player.seek(this.player.bar.get('played') * this.player.video.duration); this.player.seek(this.player.bar.get('played') * this.player.video.duration);
this.player.moveBar = false; this.player.timer.enable('progress');
}; };
this.player.template.playedBarWrap.addEventListener(utils.nameMap.dragStart, () => { this.player.template.playedBarWrap.addEventListener(utils.nameMap.dragStart, () => {
this.player.moveBar = true; this.player.timer.disable('progress');
document.addEventListener(utils.nameMap.dragMove, thumbMove); document.addEventListener(utils.nameMap.dragMove, thumbMove);
document.addEventListener(utils.nameMap.dragEnd, thumbUp); document.addEventListener(utils.nameMap.dragEnd, thumbUp);
}); });
@ -248,8 +245,9 @@ class Controller {
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
URL.revokeObjectURL(dataURL); URL.revokeObjectURL(dataURL);
this.player.events.trigger('screenshot', dataURL);
}); });
this.player.events.trigger('screenshot', dataURL);
}); });
} }
} }
@ -283,103 +281,25 @@ class Controller {
} }
} }
initChromecast() { initSubtitleButton() {
const script = window.document.createElement('script'); if (this.player.options.subtitle) {
script.setAttribute('type', 'text/javascript'); this.player.events.on('subtitle_show', () => {
script.setAttribute('src', 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1'); this.player.template.subtitleButton.dataset.balloon = this.player.tran('Hide subtitle');
window.document.body.appendChild(script); this.player.template.subtitleButtonInner.style.opacity = '';
this.player.user.set('subtitle', 1);
});
this.player.events.on('subtitle_hide', () => {
this.player.template.subtitleButton.dataset.balloon = this.player.tran('Show subtitle');
this.player.template.subtitleButtonInner.style.opacity = '0.4';
this.player.user.set('subtitle', 0);
});
window.__onGCastApiAvailable = (isAvailable) => { this.player.template.subtitleButton.addEventListener('click', () => {
if (isAvailable) { this.player.subtitle.toggle();
cast = window.chrome.cast;
const sessionRequest = new cast.SessionRequest(cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
const apiConfig = new cast.ApiConfig(
sessionRequest,
() => {},
(status) => {
if (status === cast.ReceiverAvailability.AVAILABLE) {
console.log('chromecast: ', status);
}
}
);
cast.initialize(apiConfig, () => {});
}
};
}
initChromecastButton() {
if (this.player.options.chromecast) {
if (runOnce) {
runOnce = false;
this.initChromecast();
}
const discoverDevices = () => {
cast.requestSession(
(s) => {
this.session = s;
launchMedia(this.player.options.video.url);
},
(err) => {
if (err.code === 'cancel') {
this.session = undefined;
} else {
console.error('Error selecting a cast device', err);
}
}
);
};
const launchMedia = (media) => {
const mediaInfo = new cast.media.MediaInfo(media);
const request = new cast.media.LoadRequest(mediaInfo);
if (!this.session) {
window.open(media);
return false;
}
this.session.loadMedia(request, onMediaDiscovered.bind(this, 'loadMedia'), onMediaError).play();
return true;
};
const onMediaDiscovered = (how, media) => {
this.currentMedia = media;
};
const onMediaError = (err) => {
console.error('Error launching media', err);
};
this.player.template.chromecastButton.addEventListener('click', () => {
if (isCasting) {
isCasting = false;
this.currentMedia.stop();
this.session.stop();
this.initChromecast();
} else {
isCasting = true;
discoverDevices();
}
}); });
} }
} }
initSubtitleButton() {
this.player.events.on('subtitle_show', () => {
this.player.template.subtitleButton.dataset.balloon = this.player.tran('hide-subs');
this.player.template.subtitleButtonInner.style.opacity = '';
this.player.user.set('subtitle', 1);
});
this.player.events.on('subtitle_hide', () => {
this.player.template.subtitleButton.dataset.balloon = this.player.tran('show-subs');
this.player.template.subtitleButtonInner.style.opacity = '0.4';
this.player.user.set('subtitle', 0);
});
this.player.template.subtitleButton.addEventListener('click', () => {
this.player.subtitle.toggle();
});
}
setAutoHide() { setAutoHide() {
this.show(); this.show();
clearTimeout(this.autoHideTimer); clearTimeout(this.autoHideTimer);
@ -413,10 +333,6 @@ class Controller {
} }
destroy() { destroy() {
if (!utils.isMobile) {
this.player.container.removeEventListener('mousemove', this.setAutoHideHandler);
this.player.container.removeEventListener('click', this.setAutoHideHandler);
}
clearTimeout(this.autoHideTimer); clearTimeout(this.autoHideTimer);
} }
} }

View File

@ -3,7 +3,6 @@ import utils from './utils';
class Danmaku { class Danmaku {
constructor(options) { constructor(options) {
this.options = options; this.options = options;
this.player = this.options.player;
this.container = this.options.container; this.container = this.options.container;
this.danTunnel = { this.danTunnel = {
right: {}, right: {},
@ -70,7 +69,7 @@ class Danmaku {
} }
}, },
error: (msg) => { error: (msg) => {
this.options.error(msg || this.options.tran('danmaku-failed')); this.options.error(msg || this.options.tran('Danmaku load failed'));
results[i] = []; results[i] = [];
++readCount; ++readCount;
@ -97,7 +96,7 @@ class Danmaku {
data: danmakuData, data: danmakuData,
success: callback, success: callback,
error: (msg) => { error: (msg) => {
this.options.error(msg || this.options.tran('danmaku-failed')); this.options.error(msg || this.options.tran('Danmaku send failed'));
}, },
}); });
@ -255,7 +254,6 @@ class Danmaku {
if (tunnel >= 0) { if (tunnel >= 0) {
// move // move
item.classList.add('dplayer-danmaku-move'); item.classList.add('dplayer-danmaku-move');
item.style.animationDuration = this._danAnimation(dan[i].type);
// insert // insert
docFragment.appendChild(item); docFragment.appendChild(item);
@ -309,7 +307,13 @@ class Danmaku {
} }
htmlEncode(str) { htmlEncode(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2f;'); return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.replace(/\//g, '&#x2f;');
} }
resize() { resize() {
@ -339,21 +343,6 @@ class Danmaku {
unlimit(boolean) { unlimit(boolean) {
this.unlimited = boolean; this.unlimited = boolean;
} }
speed(rate) {
this.options.api.speedRate = rate;
}
_danAnimation(position) {
const rate = this.options.api.speedRate || 1;
const isFullScreen = !!this.player.fullScreen.isFullScreen();
const animations = {
top: `${(isFullScreen ? 6 : 4) / rate}s`,
right: `${(isFullScreen ? 8 : 5) / rate}s`,
bottom: `${(isFullScreen ? 6 : 4) / rate}s`,
};
return animations[position];
}
} }
export default Danmaku; export default Danmaku;

View File

@ -12,7 +12,7 @@ class FullScreen {
utils.setScrollPosition(this.lastScrollPosition); utils.setScrollPosition(this.lastScrollPosition);
}); });
this.fullscreenchange = () => { const fullscreenchange = () => {
this.player.resize(); this.player.resize();
if (this.isFullScreen('browser')) { if (this.isFullScreen('browser')) {
this.player.events.trigger('fullscreen'); this.player.events.trigger('fullscreen');
@ -21,7 +21,7 @@ class FullScreen {
this.player.events.trigger('fullscreen_cancel'); this.player.events.trigger('fullscreen_cancel');
} }
}; };
this.docfullscreenchange = () => { const docfullscreenchange = () => {
const fullEle = document.fullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; const fullEle = document.fullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
if (fullEle && fullEle !== this.player.container) { if (fullEle && fullEle !== this.player.container) {
return; return;
@ -35,13 +35,13 @@ class FullScreen {
} }
}; };
if (/Firefox/.test(navigator.userAgent)) { if (/Firefox/.test(navigator.userAgent)) {
document.addEventListener('mozfullscreenchange', this.docfullscreenchange); document.addEventListener('mozfullscreenchange', docfullscreenchange);
document.addEventListener('fullscreenchange', this.docfullscreenchange); document.addEventListener('fullscreenchange', docfullscreenchange);
} else { } else {
this.player.container.addEventListener('fullscreenchange', this.fullscreenchange); this.player.container.addEventListener('fullscreenchange', fullscreenchange);
this.player.container.addEventListener('webkitfullscreenchange', this.fullscreenchange); this.player.container.addEventListener('webkitfullscreenchange', fullscreenchange);
document.addEventListener('msfullscreenchange', this.docfullscreenchange); document.addEventListener('msfullscreenchange', docfullscreenchange);
document.addEventListener('MSFullscreenChange', this.docfullscreenchange); document.addEventListener('MSFullscreenChange', docfullscreenchange);
} }
} }
@ -122,18 +122,6 @@ class FullScreen {
this.request(type); this.request(type);
} }
} }
destroy() {
if (/Firefox/.test(navigator.userAgent)) {
document.removeEventListener('mozfullscreenchange', this.docfullscreenchange);
document.removeEventListener('fullscreenchange', this.docfullscreenchange);
} else {
this.player.container.removeEventListener('fullscreenchange', this.fullscreenchange);
this.player.container.removeEventListener('webkitfullscreenchange', this.fullscreenchange);
document.removeEventListener('msfullscreenchange', this.docfullscreenchange);
document.removeEventListener('MSFullscreenChange', this.docfullscreenchange);
}
}
} }
export default FullScreen; export default FullScreen;

View File

@ -1,74 +1,60 @@
class HotKey { class HotKey {
constructor(player) { constructor(player) {
this.player = player; if (player.options.hotkey) {
this.doHotKeyHandler = this.doHotKey.bind(this); document.addEventListener('keydown', (e) => {
this.cancelFullScreenHandler = this.cancelFullScreen.bind(this); if (player.focus) {
if (this.player.options.hotkey) { const tag = document.activeElement.tagName.toUpperCase();
document.addEventListener('keydown', this.doHotKeyHandler); const editable = document.activeElement.getAttribute('contenteditable');
if (tag !== 'INPUT' && tag !== 'TEXTAREA' && editable !== '' && editable !== 'true') {
const event = e || window.event;
let percentage;
switch (event.keyCode) {
case 32:
event.preventDefault();
player.toggle();
break;
case 37:
event.preventDefault();
if (player.options.live) {
break;
}
player.seek(player.video.currentTime - 5);
player.controller.setAutoHide();
break;
case 39:
event.preventDefault();
if (player.options.live) {
break;
}
player.seek(player.video.currentTime + 5);
player.controller.setAutoHide();
break;
case 38:
event.preventDefault();
percentage = player.volume() + 0.1;
player.volume(percentage);
break;
case 40:
event.preventDefault();
percentage = player.volume() - 0.1;
player.volume(percentage);
break;
}
}
}
});
} }
document.addEventListener('keydown', this.cancelFullScreenHandler); document.addEventListener('keydown', (e) => {
} const event = e || window.event;
switch (event.keyCode) {
doHotKey(e) { case 27:
if (this.player.focus) { if (player.fullScreen.isFullScreen('web')) {
const tag = document.activeElement.tagName.toUpperCase(); player.fullScreen.cancel('web');
const editable = document.activeElement.getAttribute('contenteditable'); }
if (tag !== 'INPUT' && tag !== 'TEXTAREA' && editable !== '' && editable !== 'true') { break;
const event = e || window.event;
let percentage;
switch (event.keyCode) {
case 32:
event.preventDefault();
this.player.toggle();
break;
case 37:
event.preventDefault();
if (this.player.options.live) {
break;
}
this.player.seek(this.player.video.currentTime - 5);
this.player.controller.setAutoHide();
break;
case 39:
event.preventDefault();
if (this.player.options.live) {
break;
}
this.player.seek(this.player.video.currentTime + 5);
this.player.controller.setAutoHide();
break;
case 38:
event.preventDefault();
percentage = this.player.volume() + 0.1;
this.player.volume(percentage);
break;
case 40:
event.preventDefault();
percentage = this.player.volume() - 0.1;
this.player.volume(percentage);
break;
}
} }
} });
}
cancelFullScreen(e) {
const event = e || window.event;
switch (event.keyCode) {
case 27:
if (this.player.fullScreen.isFullScreen('web')) {
this.player.fullScreen.cancel('web');
}
break;
}
}
destroy() {
if (this.player.options.hotkey) {
document.removeEventListener('keydown', this.doHotKeyHandler);
}
document.removeEventListener('keydown', this.cancelFullScreenHandler);
} }
} }

View File

@ -9,358 +9,97 @@ Use this as shown below..... */
function i18n(lang) { function i18n(lang) {
this.lang = lang; this.lang = lang;
// in case someone says en-us, and en is present! this.tran = (text) => {
this.fallbackLang = this.lang.includes('-') ? this.lang.split('-')[0] : this.lang; if (tranTxt[this.lang] && tranTxt[this.lang][text]) {
this.tran = (key) => { return tranTxt[this.lang][text];
key = key.toLowerCase();
if (tranTxt[this.lang] && tranTxt[this.lang][key]) {
return tranTxt[this.lang][key];
} else if (tranTxt[this.fallbackLang] && tranTxt[this.fallbackLang][key]) {
return tranTxt[this.fallbackLang][key];
} else if (standard[key]) {
return standard[key];
} else { } else {
return key; return text;
} }
}; };
} }
// abstract model for recognizing if valid translations are present
// const model = {
// 'danmaku-loading': [],
// top: [],
// bottom: [],
// rolling: [],
// 'input-danmaku-enter': [],
// 'about-author': [],
// 'dplayer-feedback': [],
// 'about-dplayer': [],
// loop: [],
// speed: [],
// 'opacity-danmaku': [],
// normal: [],
// 'please-input-danmaku': [],
// 'set-danmaku-color': [],
// 'set-danmaku-type': [],
// 'show-danmaku': [],
// 'video-failed': [],
// 'danmaku-failed': [],
// 'danmaku-send-failed': [],
// 'switching-quality': [{ symbol: '%q', name: 'Quality', example: '720p' }],
// 'switched-quality': [{ symbol: '%q', name: 'Quality', example: '720p' }],
// ff: [{ symbol: '%s', name: 'Seconds', example: '5' }],
// rew: [{ symbol: '%s', name: 'Seconds', example: '5' }],
// 'unlimited-danmaku': [],
// 'send-danmaku': [],
// setting: [],
// fullscreen: [],
// 'web-fullscreen': [],
// send: [],
// screenshot: [],
// airplay: [],
// chromecast: [],
// 'show-subs': [],
// 'hide-subs': [],
// volume: [],
// live: [],
// 'video-info': [],
// };
// Standard english translations
const standard = {
'danmaku-loading': 'Danmaku is loading',
top: 'Top',
bottom: 'Bottom',
rolling: 'Rolling',
'input-danmaku-enter': 'Input danmaku, hit Enter',
'about-author': 'About author',
'dplayer-feedback': 'DPlayer feedback',
'about-dplayer': 'About DPlayer',
loop: 'Loop',
speed: 'Speed',
'opacity-danmaku': 'Opacity for danmaku',
normal: 'Normal',
'please-input-danmaku': 'Please input danmaku content!',
'set-danmaku-color': 'Set danmaku color',
'set-danmaku-type': 'Set danmaku type',
'show-danmaku': 'Show danmaku',
'video-failed': 'Video load failed',
'danmaku-failed': 'Danmaku load failed',
'danmaku-send-failed': 'Danmaku send failed',
'switching-quality': 'Switching to %q quality',
'switched-quality': 'Switched to %q quality',
ff: 'FF %s s',
rew: 'REW %s s',
'unlimited-danmaku': 'Unlimited danmaku',
'send-danmaku': 'Send danmaku',
setting: 'Setting',
fullscreen: 'Full screen',
'web-fullscreen': 'Web full screen',
send: 'Send',
screenshot: 'Screenshot',
airplay: 'AirPlay',
chromecast: 'ChromeCast',
subtitle: 'Subtitle',
off: 'Off',
'show-subs': 'Show subtitle',
'hide-subs': 'Hide subtitle',
volume: 'Volume',
live: 'Live',
'video-info': 'Video info',
};
// add translation text here // add translation text here
const tranTxt = { const tranTxt = {
en: standard,
'zh-cn': { 'zh-cn': {
'danmaku-loading': '弹幕加载中', 'Danmaku is loading': '弹幕加载中',
top: '顶部', Top: '顶部',
bottom: '底部', Bottom: '底部',
rolling: '滚动', Rolling: '滚动',
'input-danmaku-enter': '输入弹幕,回车发送', 'Input danmaku, hit Enter': '输入弹幕,回车发送',
'about-author': '关于作者', 'About author': '关于作者',
'dplayer-feedback': '播放器意见反馈', 'DPlayer feedback': '播放器意见反馈',
'about-dplayer': '关于 DPlayer 播放器', 'About DPlayer': '关于 DPlayer 播放器',
loop: '洗脑循环', Loop: '洗脑循环',
speed: '速度', Speed: '速度',
'opacity-danmaku': '弹幕透明度', 'Opacity for danmaku': '弹幕透明度',
normal: '正常', Normal: '正常',
'please-input-danmaku': '要输入弹幕内容啊喂!', 'Please input danmaku content!': '要输入弹幕内容啊喂!',
'set-danmaku-color': '设置弹幕颜色', 'Set danmaku color': '设置弹幕颜色',
'set-danmaku-type': '设置弹幕类型', 'Set danmaku type': '设置弹幕类型',
'show-danmaku': '显示弹幕', 'Show danmaku': '显示弹幕',
'video-failed': '视频加载失败', 'Video load failed': '视频加载失败',
'danmaku-failed': '弹幕加载失败', 'Danmaku load failed': '弹幕加载失败',
'danmaku-send-failed': '弹幕发送失败', 'Danmaku send failed': '弹幕发送失败',
'switching-quality': '正在切换至 %q 画质', 'Switching to': '正在切换至',
'switched-quality': '已经切换至 %q 画质', 'Switched to': '已经切换至',
ff: '快进 %s 秒', quality: '画质',
rew: '快退 %s 秒', FF: '快进',
'unlimited-danmaku': '海量弹幕', REW: '快退',
'send-danmaku': '发送弹幕', 'Unlimited danmaku': '海量弹幕',
setting: '设置', 'Send danmaku': '发送弹幕',
fullscreen: '全屏', Setting: '设置',
'web-fullscreen': '页面全屏', 'Full screen': '全屏',
send: '发送', 'Web full screen': '页面全屏',
screenshot: '截图', Send: '发送',
airplay: '无线投屏', Screenshot: '截图',
chromecast: 'ChromeCast', AirPlay: '无线投屏',
subtitle: '字幕', s: '秒',
off: '关闭', 'Show subtitle': '显示字幕',
'show-subs': '显示字幕', 'Hide subtitle': '隐藏字幕',
'hide-subs': '隐藏字幕', Volume: '音量',
volume: '音量', Live: '直播',
live: '直播', 'Video info': '视频统计信息',
'video-info': '视频统计信息',
}, },
'zh-tw': { 'zh-tw': {
'danmaku-loading': '彈幕載入中', 'Danmaku is loading': '彈幕載入中',
top: '頂部', Top: '頂部',
bottom: '底部', Bottom: '底部',
rolling: '滾動', Rolling: '滾動',
'input-danmaku-enter': '輸入彈幕Enter 發送', 'Input danmaku, hit Enter': '輸入彈幕Enter 發送',
'about-author': '關於作者', 'About author': '關於作者',
'dplayer-feedback': '播放器意見回饋', 'DPlayer feedback': '播放器意見回饋',
'about-dplayer': '關於 DPlayer 播放器', 'About DPlayer': '關於 DPlayer 播放器',
loop: '循環播放', Loop: '循環播放',
speed: '速度', Speed: '速度',
'opacity-danmaku': '彈幕透明度', 'Opacity for danmaku': '彈幕透明度',
normal: '正常', Normal: '正常',
'please-input-danmaku': '請輸入彈幕內容啊!', 'Please input danmaku content!': '請輸入彈幕內容啊!',
'set-danmaku-color': '設定彈幕顏色', 'Set danmaku color': '設定彈幕顏色',
'set-danmaku-type': '設定彈幕類型', 'Set danmaku type': '設定彈幕類型',
'show-danmaku': '顯示彈幕', 'Show danmaku': '顯示彈幕',
'video-failed': '影片載入失敗', 'Video load failed': '影片載入失敗',
'danmaku-failed': '彈幕載入失敗', 'Danmaku load failed': '彈幕載入失敗',
'danmaku-send-failed': '彈幕發送失敗', 'Danmaku send failed': '彈幕發送失敗',
'switching-quality': '正在切換至 %q 畫質', 'Switching to': '正在切換至',
'switched-quality': '已經切換至 %q 畫質', 'Switched to': '已經切換至',
ff: '快進 %s 秒', quality: '畫質',
rew: '快退 %s 秒', FF: '快進',
'unlimited-danmaku': '巨量彈幕', REW: '快退',
'send-danmaku': '發送彈幕', 'Unlimited danmaku': '巨量彈幕',
setting: '設定', 'Send danmaku': '發送彈幕',
fullscreen: '全螢幕', Setting: '設定',
'web-fullscreen': '頁面全螢幕', 'Full screen': '全螢幕',
send: '發送', 'Web full screen': '頁面全螢幕',
screenshot: '截圖', Send: '發送',
airplay: '無線投屏', Screenshot: '截圖',
chromecast: 'ChromeCast', AirPlay: '無線投屏',
subtitle: '字幕', s: '秒',
off: '關閉', 'Show subtitle': '顯示字幕',
'show-subs': '顯示字幕', 'Hide subtitle': '隱藏字幕',
'hide-subs': '隱藏字幕', Volume: '音量',
volume: '音量', Live: '直播',
live: '直播', 'Video info': '影片統計訊息',
'video-info': '影片統計訊息',
},
'ko-kr': {
'danmaku-loading': 'Danmaku를 불러오는 중입니다.',
top: 'Top',
bottom: 'Bottom',
rolling: 'Rolling',
'input-danmaku-enter': 'Danmaku를 입력하고 Enter를 누르세요.',
'about-author': '만든이',
'dplayer-feedback': '피드백 보내기',
'about-dplayer': 'DPlayer 정보',
loop: '반복',
speed: '배속',
'opacity-danmaku': 'Danmaku 불투명도',
normal: '표준',
'please-input-danmaku': 'Danmaku를 입력하세요!',
'set-danmaku-color': 'Danmaku 색상',
'set-danmaku-type': 'Danmaku 설정',
'show-danmaku': 'Danmaku 표시',
'video-failed': '비디오를 불러오지 못했습니다.',
'danmaku-failed': 'Danmaku를 불러오지 못했습니다.',
'danmaku-send-failed': 'Danmaku 전송에 실패했습니다.',
'Switching to': '',
'Switched to': '',
'switching-quality': '전환 %q 화질',
'switched-quality': '전환 됨 %q 화질',
ff: '앞으로 %s 초',
rew: '뒤로 %s 초',
'unlimited-danmaku': '끝없는 Danmaku',
'send-danmaku': 'Danmaku 보내기',
setting: '설정',
fullscreen: '전체 화면',
'web-fullscreen': '웹 내 전체화면',
send: '보내기',
screenshot: '화면 캡쳐',
airplay: '에어플레이',
chromecast: 'ChromeCast',
subtitle: '부제',
off: '끄다',
'show-subs': '자막 보이기',
'hide-subs': '자막 숨기기',
Volume: '볼륨',
live: '생방송',
'video-info': '비디오 정보',
},
de: {
'danmaku-loading': 'Danmaku lädt...',
top: 'Oben',
bottom: 'Unten',
rolling: 'Rollend',
'input-danmaku-enter': 'Drücke Enter nach dem Einfügen vom Danmaku',
'about-author': 'Über den Autor',
'dplayer-feedback': 'DPlayer Feedback',
'about-dplayer': 'Über DPlayer',
loop: 'Wiederholen',
speed: 'Geschwindigkeit',
'opacity-danmaku': 'Transparenz für Danmaku',
normal: 'Normal',
'please-input-danmaku': 'Bitte Danmaku Inhalt eingeben!',
'set-danmaku-color': 'Danmaku Farbe festlegen',
'set-danmaku-type': 'Danmaku Typ festlegen',
'show-danmaku': 'Zeige Danmaku',
'video-failed': 'Das Video konnte nicht geladen werden',
'danmaku-failed': 'Danmaku konnte nicht geladen werden',
'danmaku-send-failed': 'Das senden von Danmaku ist fehgeschlagen',
'switching-quality': 'Wechsle zur %q Qualität',
'switched-quality': 'Zur %q Qualität gewechselt',
ff: '%s s Vorwärts',
rew: '%s s Zurück',
'unlimited-danmaku': 'Unlimitiertes Danmaku',
'send-danmaku': 'Sende Danmaku',
setting: 'Einstellungen',
fullscreen: 'Vollbild',
'web-fullscreen': 'Browser Vollbild',
send: 'Senden',
screenshot: 'Screenshot',
airplay: 'AirPlay',
'show-subs': 'Zeige Untertitel',
chromecast: 'ChromeCast',
subtitle: 'Untertitel',
off: 'Schließung',
'hide-subs': 'Verstecke Untertitel',
volume: 'Lautstärke',
live: 'Live',
'video-info': 'Video Info',
},
"ja":{
'danmaku-loading':'コメントを読み込んでいます',
top:'トップ',
bottom:'ボトム',
rolling:'スクロール',
'input-danmaku-enter':'コメントを入力しEnter で送信',
'about-author':'作者について',
'dplayer-feedback':'DPlayer についての意見・要望',
'about-dplayer':'DPlayer について',
loop:'くり返し',
speed:'再生速度',
'opacity-danmaku':'コメントの透明度',
normal:'通常',
'please-input-danmaku':'コメントを入力してください!',
'set-danmaku-color':'コメントの色',
'set-danmaku-type':'コメントの種類',
'show-danmaku':'コメント表示',
'video-failed':'ビデオの読み込みに失敗',
'danmaku-failed':'コメントの読み込みに失敗',
'danmaku-send-failed':'コメントの送信に失敗',
'switching-quality':'%qへ切り替え中',
'switched-quality':'%qへ切り替え完了',
quality:'へ',
ff:'%s 秒早送り',
rew:'%s 秒早戻し',
'unlimited-danmaku':'コメント無制限',
'send-danmaku':'コメント送信',
setting:'設定',
fullscreen:'全画面表示',
'web-fullscreen':'ブラウザ全体表示',
send:'送信',
screenshot:'画面コピー',
airplay:'AirPlay',
'show-subs':'字幕表示',
chromecast: 'ChromeCast',
subtitle: '字幕',
off: 'OFF',
'hide-subs':'字幕非表示',
volume:'音量',
live:'ライブ',
'video-info':'動画情報'
},
'ru': {
'danmaku-loading': 'Загрузка комментариев',
top: 'Вверху',
bottom: 'Внизу',
rolling: 'Лента',
'input-danmaku-enter': 'Введите комментарий, нажмите Enter',
'about-author': 'Об авторе',
'dplayer-feedback': 'Обратная связь',
'about-dplayer': 'О плеере',
loop: 'Закольцевать',
speed: 'Скорость',
'opacity-danmaku': 'Прозрачность',
normal: 'Обычный',
'please-input-danmaku': 'Пожалуйста, введите комментарий!',
'set-danmaku-color': 'Цвет комментария',
'set-danmaku-type': 'Тип комментария',
'show-danmaku': 'Показать комментарии',
'video-failed': 'Ошибка загрузки видео',
'danmaku-failed': 'Ошибка загрузки комментариев',
'danmaku-send-failed': 'Ошибка отправки комментария',
'switching-quality': 'Переключение качества на: %q',
'switched-quality': 'Установлено качество: %q',
ff: 'Вперёд: %s с',
rew: 'Назад: %s с',
'unlimited-danmaku': 'Без ограничений',
'send-danmaku': 'Отправить комментарий',
setting: 'Настройки',
fullscreen: 'На весь экран',
'web-fullscreen': 'На весь Web экран',
send: 'Отправить',
screenshot: 'Скриншот',
airplay: 'AirPlay',
chromecast: 'ChromeCast',
subtitle: 'Комментарий',
off: 'Выкл',
'show-subs': 'Показать комментарии',
'hide-subs': 'Скрыть комментарии',
volume: 'Громкость',
live: 'Живые комментарии',
'video-info': 'Информация о видео',
}, },
}; };
export { i18n }; export default i18n;

View File

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

View File

@ -1,7 +1,7 @@
import '../css/index.less'; import '../css/index.scss';
import DPlayer from './player'; import DPlayer from './player';
/* global DPLAYER_VERSION GIT_HASH */ /* global DPLAYER_VERSION GIT_HASH */
console.log(`${'\n'} %c DPlayer v${DPLAYER_VERSION} ${GIT_HASH} %c https://dplayer.diygod.dev ${'\n'}${'\n'}`, 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;'); console.log(`${'\n'} %c DPlayer v${DPLAYER_VERSION} ${GIT_HASH} %c http://dplayer.js.org ${'\n'}${'\n'}`, 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
export default DPlayer; export default DPlayer;

View File

@ -12,7 +12,6 @@ export default (options) => {
lang: (navigator.language || navigator.browserLanguage).toLowerCase(), lang: (navigator.language || navigator.browserLanguage).toLowerCase(),
screenshot: false, screenshot: false,
airplay: true, airplay: true,
chromecast: false,
hotkey: true, hotkey: true,
preload: 'metadata', preload: 'metadata',
volume: 0.7, volume: 0.7,
@ -22,7 +21,6 @@ export default (options) => {
contextmenu: [], contextmenu: [],
mutex: true, mutex: true,
pluginOptions: { hls: {}, flv: {}, dash: {}, webtorrent: {} }, pluginOptions: { hls: {}, flv: {}, dash: {}, webtorrent: {} },
preventClickToggle: false,
}; };
for (const defaultKey in defaultOption) { for (const defaultKey in defaultOption) {
if (defaultOption.hasOwnProperty(defaultKey) && !options.hasOwnProperty(defaultKey)) { if (defaultOption.hasOwnProperty(defaultKey) && !options.hasOwnProperty(defaultKey)) {
@ -52,13 +50,13 @@ export default (options) => {
options.contextmenu = options.contextmenu.concat([ options.contextmenu = options.contextmenu.concat([
{ {
key: 'video-info', text: 'Video info',
click: (player) => { click: (player) => {
player.infoPanel.triggle(); player.infoPanel.triggle();
}, },
}, },
{ {
key: 'about-author', text: 'About author',
link: 'https://diygod.me', link: 'https://diygod.me',
}, },
{ {

View File

@ -2,7 +2,7 @@ import Promise from 'promise-polyfill';
import utils from './utils'; import utils from './utils';
import handleOption from './options'; import handleOption from './options';
import { i18n } from './i18n'; import i18n from './i18n';
import Template from './template'; import Template from './template';
import Icons from './icons'; import Icons from './icons';
import Danmaku from './danmaku'; import Danmaku from './danmaku';
@ -10,7 +10,6 @@ import Events from './events';
import FullScreen from './fullscreen'; import FullScreen from './fullscreen';
import User from './user'; import User from './user';
import Subtitle from './subtitle'; import Subtitle from './subtitle';
import Subtitles from './subtitles';
import Bar from './bar'; import Bar from './bar';
import Timer from './timer'; import Timer from './timer';
import Bezel from './bezel'; import Bezel from './bezel';
@ -43,7 +42,6 @@ class DPlayer {
this.events = new Events(); this.events = new Events();
this.user = new User(this); this.user = new User(this);
this.container = this.options.container; this.container = this.options.container;
this.noticeList = {};
this.container.classList.add('dplayer'); this.container.classList.add('dplayer');
if (!this.options.danmaku) { if (!this.options.danmaku) {
@ -51,8 +49,6 @@ class DPlayer {
} }
if (this.options.live) { if (this.options.live) {
this.container.classList.add('dplayer-live'); this.container.classList.add('dplayer-live');
} else {
this.container.classList.remove('dplayer-live');
} }
if (utils.isMobile) { if (utils.isMobile) {
this.container.classList.add('dplayer-mobile'); this.container.classList.add('dplayer-mobile');
@ -62,43 +58,6 @@ class DPlayer {
this.container.classList.add('dplayer-arrow'); this.container.classList.add('dplayer-arrow');
} }
// multi subtitles defaultSubtitle add index, off option
if (this.options.subtitle) {
if (Array.isArray(this.options.subtitle.url)) {
const offSubtitle = {
subtitle: '',
lang: 'off',
};
this.options.subtitle.url.push(offSubtitle);
if (this.options.subtitle.defaultSubtitle) {
if (typeof this.options.subtitle.defaultSubtitle === 'string') {
// defaultSubtitle is string, match in lang then name.
this.options.subtitle.index = this.options.subtitle.url.findIndex((sub) =>
/* if (sub.lang === this.options.subtitle.defaultSubtitle) {
return true;
} else if (sub.name === this.options.subtitle.defaultSubtitle) {
return true;
} else {
return false;
} */
sub.lang === this.options.subtitle.defaultSubtitle ? true : sub.name === this.options.subtitle.defaultSubtitle ? true : false
);
} else if (typeof this.options.subtitle.defaultSubtitle === 'number') {
// defaultSubtitle is int, directly use for index
this.options.subtitle.index = this.options.subtitle.defaultSubtitle;
}
}
// defaultSubtitle not match or not exist or index bound(when defaultSubtitle is int), try browser language.
if (this.options.subtitle.index === -1 || !this.options.subtitle.index || this.options.subtitle.index > this.options.subtitle.url.length - 1) {
this.options.subtitle.index = this.options.subtitle.url.findIndex((sub) => sub.lang === this.options.lang);
}
// browser language not match, default off title
if (this.options.subtitle.index === -1) {
this.options.subtitle.index = this.options.subtitle.url.length - 1;
}
}
}
this.template = new Template({ this.template = new Template({
container: this.container, container: this.container,
options: this.options, options: this.options,
@ -118,7 +77,6 @@ class DPlayer {
if (this.options.danmaku) { if (this.options.danmaku) {
this.danmaku = new Danmaku({ this.danmaku = new Danmaku({
player: this,
container: this.template.danmaku, container: this.template.danmaku,
opacity: this.user.get('opacity'), opacity: this.user.get('opacity'),
callback: () => { callback: () => {
@ -146,7 +104,6 @@ class DPlayer {
maximum: this.options.danmaku.maximum, maximum: this.options.danmaku.maximum,
addition: this.options.danmaku.addition, addition: this.options.danmaku.addition,
user: this.options.danmaku.user, user: this.options.danmaku.user,
speedRate: this.options.danmaku.speedRate,
}, },
events: this.events, events: this.events,
tran: (msg) => this.tran(msg), tran: (msg) => this.tran(msg),
@ -157,14 +114,21 @@ class DPlayer {
this.setting = new Setting(this); this.setting = new Setting(this);
this.plugins = {}; this.plugins = {};
this.docClickFun = () => {
this.focus = false; document.addEventListener(
}; 'click',
this.containerClickFun = () => { () => {
this.focus = true; this.focus = false;
}; },
document.addEventListener('click', this.docClickFun, true); true
this.container.addEventListener('click', this.containerClickFun, true); );
this.container.addEventListener(
'click',
() => {
this.focus = true;
},
true
);
this.paused = true; this.paused = true;
@ -182,8 +146,6 @@ class DPlayer {
this.play(); this.play();
} }
this.moveBar = false;
index++; index++;
instances.push(this); instances.push(this);
} }
@ -197,9 +159,9 @@ class DPlayer {
time = Math.min(time, this.video.duration); time = Math.min(time, this.video.duration);
} }
if (this.video.currentTime < time) { if (this.video.currentTime < time) {
this.notice(`${this.tran('ff').replace('%s', (time - this.video.currentTime).toFixed(0))}`); this.notice(`${this.tran('FF')} ${(time - this.video.currentTime).toFixed(0)} ${this.tran('s')}`);
} else if (this.video.currentTime > time) { } else if (this.video.currentTime > time) {
this.notice(`${this.tran('rew').replace('%s', (this.video.currentTime - time).toFixed(0))}`); this.notice(`${this.tran('REW')} ${(this.video.currentTime - time).toFixed(0)} ${this.tran('s')}`);
} }
this.video.currentTime = time; this.video.currentTime = time;
@ -296,7 +258,7 @@ class DPlayer {
this.user.set('volume', percentage); this.user.set('volume', percentage);
} }
if (!nonotice) { if (!nonotice) {
this.notice(`${this.tran('volume')} ${(percentage * 100).toFixed(0)}%`, undefined, undefined, 'volume'); this.notice(`${this.tran('Volume')} ${(percentage * 100).toFixed(0)}%`);
} }
this.video.volume = percentage; this.video.volume = percentage;
@ -382,7 +344,6 @@ class DPlayer {
this.type = 'normal'; this.type = 'normal';
} }
const src = this.quality.url; // 真实的视频 url, 用于切换清晰度时销毁实例
switch (this.type) { switch (this.type) {
// https://github.com/video-dev/hls.js // https://github.com/video-dev/hls.js
case 'hls': case 'hls':
@ -397,12 +358,6 @@ class DPlayer {
hls.destroy(); hls.destroy();
delete this.plugins.hls; delete this.plugins.hls;
}); });
// 切换清晰度时销毁实例
this.events.on('quality_end', () => {
if (src !== this.quality.url) {
hls.destroy();
}
});
} else { } else {
this.notice('Error: Hls is not supported.'); this.notice('Error: Hls is not supported.');
} }
@ -431,14 +386,6 @@ class DPlayer {
flvPlayer.destroy(); flvPlayer.destroy();
delete this.plugins.flvjs; delete this.plugins.flvjs;
}); });
// 切换清晰度时销毁实例
this.events.on('quality_end', () => {
if (src !== this.quality.url) {
flvPlayer.unload();
flvPlayer.detachMediaElement();
flvPlayer.destroy();
}
});
} else { } else {
this.notice('Error: flvjs is not supported.'); this.notice('Error: flvjs is not supported.');
} }
@ -450,8 +397,7 @@ class DPlayer {
// https://github.com/Dash-Industry-Forum/dash.js // https://github.com/Dash-Industry-Forum/dash.js
case 'dash': case 'dash':
if (window.dashjs) { if (window.dashjs) {
const dashjsPlayer = window.dashjs.MediaPlayer().create(); const dashjsPlayer = window.dashjs.MediaPlayer().create().initialize(video, video.src, false);
dashjsPlayer.initialize(video, video.src, false, 0);
const options = this.options.pluginOptions.dash; const options = this.options.pluginOptions.dash;
dashjsPlayer.updateSettings(options); dashjsPlayer.updateSettings(options);
this.plugins.dash = dashjsPlayer; this.plugins.dash = dashjsPlayer;
@ -459,12 +405,6 @@ class DPlayer {
window.dashjs.MediaPlayer().reset(); window.dashjs.MediaPlayer().reset();
delete this.plugins.dash; delete this.plugins.dash;
}); });
// 切换清晰度时销毁实例
this.events.on('quality_end', () => {
if (src !== this.quality.url) {
window.dashjs.MediaPlayer().reset();
}
});
} else { } else {
this.notice("Error: Can't find dashjs."); this.notice("Error: Can't find dashjs.");
} }
@ -494,13 +434,6 @@ class DPlayer {
client.destroy(); client.destroy();
delete this.plugins.webtorrent; delete this.plugins.webtorrent;
}); });
// 切换清晰度时销毁实例
this.events.on('quality_end', () => {
if (src !== this.quality.url) {
client.remove(torrentId);
client.destroy();
}
});
} else { } else {
this.notice('Error: Webtorrent is not supported.'); this.notice('Error: Webtorrent is not supported.');
} }
@ -538,7 +471,7 @@ class DPlayer {
// Not a video load error, may be poster load failed, see #307 // Not a video load error, may be poster load failed, see #307
return; return;
} }
this.tran && this.notice && this.type !== 'webtorrent' && this.notice(this.tran('video-failed')); this.tran && this.notice && this.type !== 'webtorrent' && this.notice(this.tran('Video load failed'), -1);
}); });
// video end // video end
@ -568,9 +501,7 @@ class DPlayer {
}); });
this.on('timeupdate', () => { this.on('timeupdate', () => {
if (!this.moveBar) { this.bar.set('played', this.video.currentTime / this.video.duration, 'width');
this.bar.set('played', this.video.currentTime / this.video.duration, 'width');
}
const currentTime = utils.secondToTime(this.video.currentTime); const currentTime = utils.secondToTime(this.video.currentTime);
if (this.template.ptime.innerHTML !== currentTime) { if (this.template.ptime.innerHTML !== currentTime) {
this.template.ptime.innerHTML = currentTime; this.template.ptime.innerHTML = currentTime;
@ -578,20 +509,15 @@ class DPlayer {
}); });
for (let i = 0; i < this.events.videoEvents.length; i++) { for (let i = 0; i < this.events.videoEvents.length; i++) {
video.addEventListener(this.events.videoEvents[i], (e) => { video.addEventListener(this.events.videoEvents[i], () => {
this.events.trigger(this.events.videoEvents[i], e); this.events.trigger(this.events.videoEvents[i]);
}); });
} }
this.volume(this.user.get('volume'), true, true); this.volume(this.user.get('volume'), true, true);
if (this.options.subtitle) { if (this.options.subtitle) {
// init old single subtitle function(sub show and style)
this.subtitle = new Subtitle(this.template.subtitle, this.video, this.options.subtitle, this.events); this.subtitle = new Subtitle(this.template.subtitle, this.video, this.options.subtitle, this.events);
// init multi subtitles function(sub update)
if (Array.isArray(this.options.subtitle.url)) {
this.subtitles = new Subtitles(this);
}
if (!this.user.get('subtitle')) { if (!this.user.get('subtitle')) {
this.subtitle.hide(); this.subtitle.hide();
} }
@ -603,7 +529,6 @@ class DPlayer {
if (this.qualityIndex === index || this.switchingQuality) { if (this.qualityIndex === index || this.switchingQuality) {
return; return;
} else { } else {
this.prevIndex = this.qualityIndex;
this.qualityIndex = index; this.qualityIndex = index;
} }
this.switchingQuality = true; this.switchingQuality = true;
@ -626,7 +551,7 @@ class DPlayer {
this.video = videoEle; this.video = videoEle;
this.initVideo(this.video, this.quality.type || this.options.video.type); this.initVideo(this.video, this.quality.type || this.options.video.type);
this.seek(this.prevVideo.currentTime); this.seek(this.prevVideo.currentTime);
this.notice(`${this.tran('switching-quality').replace('%q', this.quality.name)}`, -1, undefined, 'switch-quality'); this.notice(`${this.tran('Switching to')} ${this.quality.name} ${this.tran('quality')}`, -1);
this.events.trigger('quality_start', this.quality); this.events.trigger('quality_start', this.quality);
this.on('canplay', () => { this.on('canplay', () => {
@ -641,69 +566,26 @@ class DPlayer {
this.video.play(); this.video.play();
} }
this.prevVideo = null; this.prevVideo = null;
this.notice(`${this.tran('switched-quality').replace('%q', this.quality.name)}`, undefined, undefined, 'switch-quality'); this.notice(`${this.tran('Switched to')} ${this.quality.name} ${this.tran('quality')}`);
this.switchingQuality = false; this.switchingQuality = false;
this.events.trigger('quality_end'); this.events.trigger('quality_end');
} }
}); });
this.on('error', () => {
if (!this.video.error) {
return;
}
if (this.prevVideo) {
this.template.videoWrap.removeChild(this.video);
this.video = this.prevVideo;
if (!paused) {
this.video.play();
}
this.qualityIndex = this.prevIndex;
this.quality = this.options.video.quality[this.qualityIndex];
this.noticeTime = setTimeout(() => {
this.template.notice.style.opacity = 0;
this.events.trigger('notice_hide');
}, 3000);
this.prevVideo = null;
this.switchingQuality = false;
}
});
} }
notice(text, time = 2000, opacity = 0.8, id) { notice(text, time = 2000, opacity = 0.8) {
let oldNoticeEle; this.template.notice.innerHTML = text;
if (id) { this.template.notice.style.opacity = opacity;
oldNoticeEle = document.getElementById(`dplayer-notice-${id}`); if (this.noticeTime) {
if (oldNoticeEle) { clearTimeout(this.noticeTime);
oldNoticeEle.innerHTML = text;
}
if (this.noticeList[id]) {
clearTimeout(this.noticeList[id]);
this.noticeList[id] = null;
}
} }
if (!oldNoticeEle) { this.events.trigger('notice_show', text);
const notice = Template.NewNotice(text, opacity, id);
this.template.noticeList.appendChild(notice);
oldNoticeEle = notice;
}
this.events.trigger('notice_show', oldNoticeEle);
if (time > 0) { if (time > 0) {
this.noticeList[id] = setTimeout( this.noticeTime = setTimeout(() => {
(function (e, dp) { this.template.notice.style.opacity = 0;
return () => { this.events.trigger('notice_hide');
e.addEventListener('animationend', () => { }, time);
dp.template.noticeList.removeChild(e);
});
e.classList.add('remove-notice');
dp.events.trigger('notice_hide');
dp.noticeList[id] = null;
};
})(oldNoticeEle, this),
time
);
} }
} }
@ -724,11 +606,6 @@ class DPlayer {
destroy() { destroy() {
instances.splice(instances.indexOf(this), 1); instances.splice(instances.indexOf(this), 1);
this.pause(); this.pause();
document.removeEventListener('click', this.docClickFun, true);
this.container.removeEventListener('click', this.containerClickFun, true);
this.fullScreen.destroy();
this.hotkey.destroy();
this.contextmenu.destroy();
this.controller.destroy(); this.controller.destroy();
this.timer.destroy(); this.timer.destroy();
this.video.src = ''; this.video.src = '';

View File

@ -17,7 +17,7 @@ class Subtitle {
const track = this.video.textTracks[0]; const track = this.video.textTracks[0];
track.oncuechange = () => { track.oncuechange = () => {
const cue = track.activeCues[track.activeCues.length - 1]; const cue = track.activeCues[0];
this.container.innerHTML = ''; this.container.innerHTML = '';
if (cue) { if (cue) {
const template = document.createElement('div'); const template = document.createElement('div');

View File

@ -1,79 +0,0 @@
class Subtitles {
constructor(player) {
this.player = player;
this.player.template.mask.addEventListener('click', () => {
this.hide();
});
this.player.template.subtitlesButton.addEventListener('click', () => {
this.adaptiveHeight();
this.show();
});
const lastItemIndex = this.player.template.subtitlesItem.length - 1;
for (let i = 0; i < lastItemIndex; i++) {
this.player.template.subtitlesItem[i].addEventListener('click', () => {
this.hide();
if (this.player.options.subtitle.index !== i) {
// clear subtitle show for new subtitle don't have now duration time. If don't, will display last subtitle.
this.player.template.subtitle.innerHTML = `<p></p>`;
// update video track src
this.player.template.subtrack.src = this.player.template.subtitlesItem[i].dataset.subtitle;
// update options current subindex for reload (such as changeQuality)
this.player.options.subtitle.index = i;
if (this.player.template.subtitle.classList.contains('dplayer-subtitle-hide')) {
this.subContainerShow();
}
}
});
}
this.player.template.subtitlesItem[lastItemIndex].addEventListener('click', () => {
this.hide();
if (this.player.options.subtitle.index !== lastItemIndex) {
// clear subtitle show for new subtitle don't have now duration time. If don't, will display last subtitle.
this.player.template.subtitle.innerHTML = `<p></p>`;
// update video track src
this.player.template.subtrack.src = '';
// update options current subindex for reload (such as changeQuality)
this.player.options.subtitle.index = lastItemIndex;
this.subContainerHide();
}
});
}
subContainerShow() {
this.player.template.subtitle.classList.remove('dplayer-subtitle-hide');
this.player.events.trigger('subtitle_show');
}
subContainerHide() {
this.player.template.subtitle.classList.add('dplayer-subtitle-hide');
this.player.events.trigger('subtitle_hide');
}
hide() {
this.player.template.subtitlesBox.classList.remove('dplayer-subtitles-box-open');
this.player.template.mask.classList.remove('dplayer-mask-show');
this.player.controller.disableAutoHide = false;
}
show() {
this.player.template.subtitlesBox.classList.add('dplayer-subtitles-box-open');
this.player.template.mask.classList.add('dplayer-mask-show');
this.player.controller.disableAutoHide = true;
}
adaptiveHeight() {
const curBoxHeight = this.player.template.subtitlesItem.length * 30 + 14;
const stdMaxHeight = this.player.template.videoWrap.offsetHeight * 0.8;
if (curBoxHeight >= stdMaxHeight - 50) {
this.player.template.subtitlesBox.style.bottom = '8px';
this.player.template.subtitlesBox.style['max-height'] = stdMaxHeight - 8 + 'px';
} else {
this.player.template.subtitlesBox.style.bottom = '50px';
this.player.template.subtitlesBox.style['max-height'] = stdMaxHeight - 50 + 'px';
}
}
}
export default Subtitles;

View File

@ -22,8 +22,7 @@ class Template {
current: true, current: true,
pic: this.options.video.pic, pic: this.options.video.pic,
screenshot: this.options.screenshot, screenshot: this.options.screenshot,
airplay: utils.isSafari && !utils.isChrome ? this.options.airplay : false, airplay: this.options.airplay,
chromecast: this.options.chromecast,
preload: this.options.preload, preload: this.options.preload,
url: this.options.video.url, url: this.options.video.url,
subtitle: this.options.subtitle, subtitle: this.options.subtitle,
@ -81,18 +80,13 @@ class Template {
this.qualityList = this.container.querySelector('.dplayer-quality-list'); this.qualityList = this.container.querySelector('.dplayer-quality-list');
this.camareButton = this.container.querySelector('.dplayer-camera-icon'); this.camareButton = this.container.querySelector('.dplayer-camera-icon');
this.airplayButton = this.container.querySelector('.dplayer-airplay-icon'); this.airplayButton = this.container.querySelector('.dplayer-airplay-icon');
this.chromecastButton = this.container.querySelector('.dplayer-chromecast-icon');
this.subtitleButton = this.container.querySelector('.dplayer-subtitle-icon'); this.subtitleButton = this.container.querySelector('.dplayer-subtitle-icon');
this.subtitleButtonInner = this.container.querySelector('.dplayer-subtitle-icon .dplayer-icon-content'); this.subtitleButtonInner = this.container.querySelector('.dplayer-subtitle-icon .dplayer-icon-content');
this.subtitlesButton = this.container.querySelector('.dplayer-subtitles-icon');
this.subtitlesBox = this.container.querySelector('.dplayer-subtitles-box');
this.subtitlesItem = this.container.querySelectorAll('.dplayer-subtitles-item');
this.subtitle = this.container.querySelector('.dplayer-subtitle'); this.subtitle = this.container.querySelector('.dplayer-subtitle');
this.subtrack = this.container.querySelector('.dplayer-subtrack');
this.qualityButton = this.container.querySelector('.dplayer-quality-icon'); this.qualityButton = this.container.querySelector('.dplayer-quality-icon');
this.barPreview = this.container.querySelector('.dplayer-bar-preview'); this.barPreview = this.container.querySelector('.dplayer-bar-preview');
this.barWrap = this.container.querySelector('.dplayer-bar-wrap'); this.barWrap = this.container.querySelector('.dplayer-bar-wrap');
this.noticeList = this.container.querySelector('.dplayer-notice-list'); this.notice = this.container.querySelector('.dplayer-notice');
this.infoPanel = this.container.querySelector('.dplayer-info-panel'); this.infoPanel = this.container.querySelector('.dplayer-info-panel');
this.infoPanelClose = this.container.querySelector('.dplayer-info-panel-close'); this.infoPanelClose = this.container.querySelector('.dplayer-info-panel-close');
this.infoVersion = this.container.querySelector('.dplayer-info-panel-item-version .dplayer-info-panel-item-data'); this.infoVersion = this.container.querySelector('.dplayer-info-panel-item-version .dplayer-info-panel-item-data');
@ -105,17 +99,6 @@ class Template {
this.infoDanmakuApi = this.container.querySelector('.dplayer-info-panel-item-danmaku-api .dplayer-info-panel-item-data'); this.infoDanmakuApi = this.container.querySelector('.dplayer-info-panel-item-danmaku-api .dplayer-info-panel-item-data');
this.infoDanmakuAmount = this.container.querySelector('.dplayer-info-panel-item-danmaku-amount .dplayer-info-panel-item-data'); this.infoDanmakuAmount = this.container.querySelector('.dplayer-info-panel-item-danmaku-amount .dplayer-info-panel-item-data');
} }
static NewNotice(text, opacity, id) {
const notice = document.createElement('div');
notice.classList.add('dplayer-notice');
notice.style.opacity = opacity;
notice.innerText = text;
if (id) {
notice.id = `dplayer-notice-${id}`;
}
return notice;
}
} }
export default Template; export default Template;

View File

@ -8,7 +8,7 @@ class Timer {
window.mozRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.oRequestAnimationFrame ||
window.msRequestAnimationFrame || window.msRequestAnimationFrame ||
function (callback) { function(callback) {
window.setTimeout(callback, 1000 / 60); window.setTimeout(callback, 1000 / 60);
})(); })();

View File

@ -1,4 +1,6 @@
const isMobile = /mobile|android|iphone|ipod|phone|ipad/i.test(window.navigator.userAgent); const isMobile = /mobile/i.test(window.navigator.userAgent);
const isSafari = /Safari/i.test(window.navigator.userAgent);
const utils = { const utils = {
/** /**
@ -89,12 +91,12 @@ const utils = {
isMobile: isMobile, isMobile: isMobile,
isSafari: isSafari,
isFirefox: /firefox/i.test(window.navigator.userAgent), isFirefox: /firefox/i.test(window.navigator.userAgent),
isChrome: /chrome/i.test(window.navigator.userAgent), isChrome: /chrome/i.test(window.navigator.userAgent),
isSafari: /safari/i.test(window.navigator.userAgent),
storage: { storage: {
set: (key, value) => { set: (key, value) => {
localStorage.setItem(key, value); localStorage.setItem(key, value);

View File

@ -6,14 +6,14 @@
<img src="{{ options.logo }}"> <img src="{{ options.logo }}">
</div> </div>
{{ /if }} {{ /if }}
<div class="dplayer-danmaku"{{ if options.danmaku && options.danmaku.bottom }} style="margin-bottom:{{ options.danmaku.bottom }}"{{ /if }}> <div class="dplayer-danmaku"{{ if options.danmaku && options.danmaku.bottm }} style="margin-bottom:{{ options.danmaku.bottm }}"{{ /if }}>
<div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div> <div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>
</div> </div>
<div class="dplayer-subtitle"></div> <div class="dplayer-subtitle"></div>
<div class="dplayer-bezel"> <div class="dplayer-bezel">
<span class="dplayer-bezel-icon"></span> <span class="dplayer-bezel-icon"></span>
{{ if options.danmaku }} {{ if options.danmaku }}
<span class="dplayer-danloading">{{ tran('danmaku-loading') }}</span> <span class="dplayer-danloading">{{ tran('Danmaku is loading') }}</span>
{{ /if }} {{ /if }}
<span class="diplayer-loading-icon">{{@ icons.loading }}</span> <span class="diplayer-loading-icon">{{@ icons.loading }}</span>
</div> </div>
@ -21,12 +21,12 @@
<div class="dplayer-controller-mask"></div> <div class="dplayer-controller-mask"></div>
<div class="dplayer-controller"> <div class="dplayer-controller">
<div class="dplayer-icons dplayer-comment-box"> <div class="dplayer-icons dplayer-comment-box">
<button class="dplayer-icon dplayer-comment-setting-icon" data-balloon="{{ tran('setting') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-comment-setting-icon" data-balloon="{{ tran('Setting') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.pallette }}</span> <span class="dplayer-icon-content">{{@ icons.pallette }}</span>
</button> </button>
<div class="dplayer-comment-setting-box"> <div class="dplayer-comment-setting-box">
<div class="dplayer-comment-setting-color"> <div class="dplayer-comment-setting-color">
<div class="dplayer-comment-setting-title">{{ tran('set-danmaku-color') }}</div> <div class="dplayer-comment-setting-title">{{ tran('Set danmaku color') }}</div>
<label> <label>
<input type="radio" name="dplayer-danmaku-color-{{ index }}" value="#fff" checked> <input type="radio" name="dplayer-danmaku-color-{{ index }}" value="#fff" checked>
<span style="background: #fff;"></span> <span style="background: #fff;"></span>
@ -53,23 +53,23 @@
</label> </label>
</div> </div>
<div class="dplayer-comment-setting-type"> <div class="dplayer-comment-setting-type">
<div class="dplayer-comment-setting-title">{{ tran('set-danmaku-type') }}</div> <div class="dplayer-comment-setting-title">{{ tran('Set danmaku type') }}</div>
<label> <label>
<input type="radio" name="dplayer-danmaku-type-{{ index }}" value="1"> <input type="radio" name="dplayer-danmaku-type-{{ index }}" value="1">
<span>{{ tran('top') }}</span> <span>{{ tran('Top') }}</span>
</label> </label>
<label> <label>
<input type="radio" name="dplayer-danmaku-type-{{ index }}" value="0" checked> <input type="radio" name="dplayer-danmaku-type-{{ index }}" value="0" checked>
<span>{{ tran('rolling') }}</span> <span>{{ tran('Rolling') }}</span>
</label> </label>
<label> <label>
<input type="radio" name="dplayer-danmaku-type-{{ index }}" value="2"> <input type="radio" name="dplayer-danmaku-type-{{ index }}" value="2">
<span>{{ tran('bottom') }}</span> <span>{{ tran('Bottom') }}</span>
</label> </label>
</div> </div>
</div> </div>
<input class="dplayer-comment-input" type="text" placeholder="{{ tran('input-danmaku-enter') }}" maxlength="30"> <input class="dplayer-comment-input" type="text" placeholder="{{ tran('Input danmaku, hit Enter') }}" maxlength="30">
<button class="dplayer-icon dplayer-send-icon" data-balloon="{{ tran('send') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-send-icon" data-balloon="{{ tran('Send') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.send }}</span> <span class="dplayer-icon-content">{{@ icons.send }}</span>
</button> </button>
</div> </div>
@ -94,7 +94,7 @@
<span class="dplayer-dtime">0:00</span> <span class="dplayer-dtime">0:00</span>
</span> </span>
{{ if options.live }} {{ if options.live }}
<span class="dplayer-live-badge"><span class="dplayer-live-dot" style="background: {{ options.theme }};"></span>{{ tran('live') }}</span> <span class="dplayer-live-badge"><span class="dplayer-live-dot" style="background: {{ options.theme }};"></span>{{ tran('Live') }}</span>
{{ /if }} {{ /if }}
</div> </div>
<div class="dplayer-icons dplayer-icons-right"> <div class="dplayer-icons dplayer-icons-right">
@ -111,83 +111,60 @@
</div> </div>
{{ /if }} {{ /if }}
{{ if options.screenshot }} {{ if options.screenshot }}
<div class="dplayer-icon dplayer-camera-icon" data-balloon="{{ tran('screenshot') }}" data-balloon-pos="up"> <div class="dplayer-icon dplayer-camera-icon" data-balloon="{{ tran('Screenshot') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.camera }}</span> <span class="dplayer-icon-content">{{@ icons.camera }}</span>
</div> </div>
{{ /if }} {{ /if }}
{{ if options.airplay }} {{ if options.airplay }}
<div class="dplayer-icon dplayer-airplay-icon" data-balloon="{{ tran('airplay') }}" data-balloon-pos="up"> <div class="dplayer-icon dplayer-airplay-icon" data-balloon="{{ tran('AirPlay') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.airplay }}</span> <span class="dplayer-icon-content">{{@ icons.airplay }}</span>
</div> </div>
{{ /if }} {{ /if }}
{{ if options.chromecast }}
<div class="dplayer-icon dplayer-chromecast-icon" data-balloon="{{ tran('chromecast') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.chromecast }}</span>
</div>
{{ /if }}
<div class="dplayer-comment"> <div class="dplayer-comment">
<button class="dplayer-icon dplayer-comment-icon" data-balloon="{{ tran('send-danmaku') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-comment-icon" data-balloon="{{ tran('Send danmaku') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.comment }}</span> <span class="dplayer-icon-content">{{@ icons.comment }}</span>
</button> </button>
</div> </div>
{{ if options.subtitle }} {{ if options.subtitle }}
{{ if (typeof options.subtitle.url === 'string') }}
<div class="dplayer-subtitle-btn"> <div class="dplayer-subtitle-btn">
<button class="dplayer-icon dplayer-subtitle-icon" data-balloon="{{ tran('hide-subs') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-subtitle-icon" data-balloon="{{ tran('Hide subtitle') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.subtitle }}</span> <span class="dplayer-icon-content">{{@ icons.subtitle }}</span>
</button> </button>
</div> </div>
{{ else }}
<div class="dplayer-subtitles">
<button class="dplayer-icon dplayer-subtitles-icon" data-balloon="{{ tran('subtitle') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.subtitle }}</span>
</button>
<div class="dplayer-subtitles-box">
<div class="dplayer-subtitles-panel">
{{ each options.subtitle.url }}
<div class="dplayer-subtitles-item" data-subtitle="{{ $value.url }}">
<!-- if lang, show tran(lang). if lang and name, show name + (tran(lang)). if name, show name. off option use lang for translation. -->
<span class="dplayer-label">{{ $value.lang ? $value.name ? $value.name+' ('+tran($value.lang)+')' : tran($value.lang) : $value.name }}</span>
</div>
{{ /each }}
</div>
</div>
</div>
{{ /if }}
{{ /if }} {{ /if }}
<div class="dplayer-setting"> <div class="dplayer-setting">
<button class="dplayer-icon dplayer-setting-icon" data-balloon="{{ tran('setting') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-setting-icon" data-balloon="{{ tran('Setting') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.setting }}</span> <span class="dplayer-icon-content">{{@ icons.setting }}</span>
</button> </button>
<div class="dplayer-setting-box"> <div class="dplayer-setting-box">
<div class="dplayer-setting-origin-panel"> <div class="dplayer-setting-origin-panel">
<div class="dplayer-setting-item dplayer-setting-speed"> <div class="dplayer-setting-item dplayer-setting-speed">
<span class="dplayer-label">{{ tran('speed') }}</span> <span class="dplayer-label">{{ tran('Speed') }}</span>
<div class="dplayer-toggle">{{@ icons.right }}</div> <div class="dplayer-toggle">{{@ icons.right }}</div>
</div> </div>
<div class="dplayer-setting-item dplayer-setting-loop"> <div class="dplayer-setting-item dplayer-setting-loop">
<span class="dplayer-label">{{ tran('loop') }}</span> <span class="dplayer-label">{{ tran('Loop') }}</span>
<div class="dplayer-toggle"> <div class="dplayer-toggle">
<input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"> <input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle">
<label for="dplayer-toggle"></label> <label for="dplayer-toggle"></label>
</div> </div>
</div> </div>
<div class="dplayer-setting-item dplayer-setting-showdan"> <div class="dplayer-setting-item dplayer-setting-showdan">
<span class="dplayer-label">{{ tran('show-danmaku') }}</span> <span class="dplayer-label">{{ tran('Show danmaku') }}</span>
<div class="dplayer-toggle"> <div class="dplayer-toggle">
<input class="dplayer-showdan-setting-input" type="checkbox" name="dplayer-toggle-dan"> <input class="dplayer-showdan-setting-input" type="checkbox" name="dplayer-toggle-dan">
<label for="dplayer-toggle-dan"></label> <label for="dplayer-toggle-dan"></label>
</div> </div>
</div> </div>
<div class="dplayer-setting-item dplayer-setting-danunlimit"> <div class="dplayer-setting-item dplayer-setting-danunlimit">
<span class="dplayer-label">{{ tran('unlimited-danmaku') }}</span> <span class="dplayer-label">{{ tran('Unlimited danmaku') }}</span>
<div class="dplayer-toggle"> <div class="dplayer-toggle">
<input class="dplayer-danunlimit-setting-input" type="checkbox" name="dplayer-toggle-danunlimit"> <input class="dplayer-danunlimit-setting-input" type="checkbox" name="dplayer-toggle-danunlimit">
<label for="dplayer-toggle-danunlimit"></label> <label for="dplayer-toggle-danunlimit"></label>
</div> </div>
</div> </div>
<div class="dplayer-setting-item dplayer-setting-danmaku"> <div class="dplayer-setting-item dplayer-setting-danmaku">
<span class="dplayer-label">{{ tran('opacity-danmaku') }}</span> <span class="dplayer-label">{{ tran('Opacity for danmaku') }}</span>
<div class="dplayer-danmaku-bar-wrap"> <div class="dplayer-danmaku-bar-wrap">
<div class="dplayer-danmaku-bar"> <div class="dplayer-danmaku-bar">
<div class="dplayer-danmaku-bar-inner"> <div class="dplayer-danmaku-bar-inner">
@ -200,17 +177,17 @@
<div class="dplayer-setting-speed-panel"> <div class="dplayer-setting-speed-panel">
{{ each options.playbackSpeed }} {{ each options.playbackSpeed }}
<div class="dplayer-setting-speed-item" data-speed="{{ $value }}"> <div class="dplayer-setting-speed-item" data-speed="{{ $value }}">
<span class="dplayer-label">{{ $value === 1 ? tran('normal') : $value }}</span> <span class="dplayer-label">{{ $value === 1 ? tran('Normal') : $value }}</span>
</div> </div>
{{ /each }} {{ /each }}
</div> </div>
</div> </div>
</div> </div>
<div class="dplayer-full"> <div class="dplayer-full">
<button class="dplayer-icon dplayer-full-in-icon" data-balloon="{{ tran('web-fullscreen') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-full-in-icon" data-balloon="{{ tran('Web full screen') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.fullWeb }}</span> <span class="dplayer-icon-content">{{@ icons.fullWeb }}</span>
</button> </button>
<button class="dplayer-icon dplayer-full-icon" data-balloon="{{ tran('fullscreen') }}" data-balloon-pos="up"> <button class="dplayer-icon dplayer-full-icon" data-balloon="{{ tran('Full screen') }}" data-balloon-pos="up">
<span class="dplayer-icon-content">{{@ icons.full }}</span> <span class="dplayer-icon-content">{{@ icons.full }}</span>
</button> </button>
</div> </div>
@ -270,11 +247,11 @@
<div class="dplayer-menu"> <div class="dplayer-menu">
{{ each options.contextmenu }} {{ each options.contextmenu }}
<div class="dplayer-menu-item"> <div class="dplayer-menu-item">
<a{{ if $value.link }} target="_blank"{{ /if }} href="{{ $value.link || 'javascript:void(0);' }}">{{ if $value.key }} {{ tran($value.key) }}{{ /if }}{{ if $value.text }} {{$value.text}}{{ /if }}</a> <a{{ if $value.link }} target="_blank"{{ /if }} href="{{ $value.link || 'javascript:void(0);' }}">{{ tran($value.text) }}</a>
</div> </div>
{{ /each }} {{ /each }}
</div> </div>
<div class="dplayer-notice-list"></div> <div class="dplayer-notice"></div>
<button class="dplayer-mobile-play"> <button class="dplayer-mobile-play">
{{@ icons.play }} {{@ icons.play }}
</button> </button>

View File

@ -7,16 +7,9 @@
{{ if pic }}poster="{{ pic }}"{{ /if }} {{ if pic }}poster="{{ pic }}"{{ /if }}
{{ if screenshot || enableSubtitle }}crossorigin="anonymous"{{ /if }} {{ if screenshot || enableSubtitle }}crossorigin="anonymous"{{ /if }}
{{ if preload }}preload="{{ preload }}"{{ /if }} {{ if preload }}preload="{{ preload }}"{{ /if }}
{{ if airplay }} {{ if url }}src="{{ url }}"{{ /if }}
nosrc
{{ else if url }}
src="{{ url }}"
{{ /if }}
> >
{{ if airplay }}
<source src="{{ url }}">
{{ /if}}
{{ if enableSubtitle }} {{ if enableSubtitle }}
<track class="dplayer-subtrack" kind="metadata" default src="{{ typeof subtitle.url === 'string' ? subtitle.url : subtitle.url[subtitle.index].url }}"></track> <track kind="metadata" default src="{{ subtitle.url }}"></track>
{{ /if }} {{ /if }}
</video> </video>

View File

@ -1,6 +0,0 @@
# https://tea.xyz/what-is-this-file
---
version: 1.0.0
codeOwners:
- '0x185bfcef7b37010e2511309048a130f477f54fBf'
quorum: 1

View File

@ -1,7 +1,9 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const { GitRevisionPlugin } = require('git-revision-webpack-plugin'); const GitRevisionPlugin = require('git-revision-webpack-plugin');
const gitRevisionPlugin = new GitRevisionPlugin(); const gitRevisionPlugin = new GitRevisionPlugin();
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
module.exports = { module.exports = {
mode: 'development', mode: 'development',
@ -24,13 +26,7 @@ module.exports = {
resolve: { resolve: {
modules: ['node_modules'], modules: ['node_modules'],
extensions: ['.js', '.less'], extensions: ['.js', '.scss'],
fallback: {
dgram: false,
fs: false,
net: false,
tls: false,
},
}, },
module: { module: {
@ -49,7 +45,7 @@ module.exports = {
], ],
}, },
{ {
test: /\.less$/, test: /\.scss$/,
use: [ use: [
'style-loader', 'style-loader',
{ {
@ -61,12 +57,10 @@ module.exports = {
{ {
loader: 'postcss-loader', loader: 'postcss-loader',
options: { options: {
postcssOptions: { plugins: [autoprefixer, cssnano],
plugins: ['postcss-preset-env'],
},
}, },
}, },
'less-loader', 'sass-loader',
], ],
}, },
{ {
@ -88,11 +82,17 @@ module.exports = {
}, },
devServer: { devServer: {
static: {
directory: path.join(__dirname, '..', 'demo'),
},
compress: true, compress: true,
contentBase: path.resolve(__dirname, '..', 'demo'),
clientLogLevel: 'none',
quiet: false,
open: true, open: true,
historyApiFallback: {
disableDotRule: true,
},
watchOptions: {
ignored: /node_modules/,
},
}, },
plugins: [ plugins: [
@ -102,6 +102,13 @@ module.exports = {
}), }),
], ],
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
},
performance: { performance: {
hints: false, hints: false,
}, },

View File

@ -1,7 +1,9 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const { GitRevisionPlugin } = require('git-revision-webpack-plugin'); const GitRevisionPlugin = require('git-revision-webpack-plugin');
const gitRevisionPlugin = new GitRevisionPlugin(); const gitRevisionPlugin = new GitRevisionPlugin();
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
module.exports = { module.exports = {
mode: 'production', mode: 'production',
@ -26,13 +28,7 @@ module.exports = {
resolve: { resolve: {
modules: ['node_modules'], modules: ['node_modules'],
extensions: ['.js', '.less'], extensions: ['.js', '.scss'],
fallback: {
dgram: false,
fs: false,
net: false,
tls: false,
},
}, },
module: { module: {
@ -52,7 +48,7 @@ module.exports = {
], ],
}, },
{ {
test: /\.less$/, test: /\.scss$/,
use: [ use: [
'style-loader', 'style-loader',
{ {
@ -64,12 +60,10 @@ module.exports = {
{ {
loader: 'postcss-loader', loader: 'postcss-loader',
options: { options: {
postcssOptions: { plugins: [autoprefixer, cssnano],
plugins: ['postcss-preset-env'],
},
}, },
}, },
'less-loader', 'sass-loader',
], ],
}, },
{ {
@ -96,4 +90,11 @@ module.exports = {
GIT_HASH: JSON.stringify(gitRevisionPlugin.version()), GIT_HASH: JSON.stringify(gitRevisionPlugin.version()),
}), }),
], ],
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
},
}; };

9625
yarn.lock Normal file

File diff suppressed because it is too large Load Diff