Quality switching; New option: logo, contextmenu

This commit is contained in:
DIYgod 2017-06-18 18:39:37 +08:00
parent c6973854d0
commit 83d795d983
No known key found for this signature in database
GPG Key ID: EC0B76A252D3EF67
10 changed files with 383 additions and 118 deletions

View File

@ -54,14 +54,16 @@
<h3>Normal</h3>
<div class="dplayer" id="dplayer1"></div>
<button onclick="switchDPlayer()">Switch Video</button>
<h3>Bilibili video and danmaku support</h3>
<div class="dplayer" id="dplayer2"></div>
<h3>Quality switching</h3>
<div class="dplayer" id="dplayer6"></div>
<h3>Live Video (HTTP Live Streaming, M3U8 format) support</h3>
<div class="dplayer" id="dplayer3"></div>
<h3>FLV format support</h3>
<div class="dplayer" id="dplayer4"></div>
<h3>Segmented videos</h3>
<div class="dplayer" id="dplayer5"></div>
<h3>Bilibili video and danmaku support</h3>
<div class="dplayer" id="dplayer2"></div>
<!--<h3>Segmented videos</h3>
<div class="dplayer" id="dplayer5"></div>-->
</div>
<script src="https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js"></script>
<script src="../plugin/flv.min.js"></script>
@ -76,6 +78,7 @@
loop: true,
screenshot: true,
hotkey: true,
logo: 'http://devtest.qiniudn.com/DPlayer.png',
video: {
url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
@ -87,7 +90,25 @@
token: 'tokendemo',
maximum: 3000,
user: 'DIYgod的女粉'
}
},
contextmenu: [
{
text: '关于作者',
link: 'http://diygod.me'
},
{
text: '播放器意见反馈',
link: 'https://github.com/DIYgod/DPlayer/issues'
},
{
text: '关于 DPlayer 播放器',
link: 'https://github.com/DIYgod/DPlayer'
},
{
text: '自定义右键菜单demo',
link: 'https://github.com/DIYgod/DPlayer'
},
]
});
function switchDPlayer() {
if (dp1.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') {
@ -125,6 +146,7 @@
theme: '#FADFA3',
loop: true,
screenshot: false,
logo: 'http://devtest.qiniudn.com/DPlayer.png',
video: {
url: 'https://api.prprpr.me/dplayer/video/bilibili?aid=4045652',
pic: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.jpg',
@ -147,6 +169,7 @@
loop: true,
screenshot: true,
hotkey: true,
logo: 'http://devtest.qiniudn.com/DPlayer.png',
video: {
url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
@ -168,6 +191,7 @@
loop: true,
screenshot: true,
hotkey: true,
logo: 'http://devtest.qiniudn.com/DPlayer.png',
video: {
url: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.flv',
pic: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.jpg',
@ -182,24 +206,54 @@
});
// Segmented videos
var dp5 = new DPlayer({
element: document.getElementById('dplayer5'),
// var dp5 = new DPlayer({
// element: document.getElementById('dplayer5'),
// autoplay: false,
// theme: '#FADFA3',
// loop: true,
// screenshot: true,
// hotkey: true,
// preload: 'metadata',
// video: {
// url: ['http://devtest.qiniudn.com/若能绽放光芒0.mp4', 'http://devtest.qiniudn.com/若能绽放光芒1.mp4', 'http://devtest.qiniudn.com/若能绽放光芒2.mp4', 'http://devtest.qiniudn.com/若能绽放光芒3.mp4', 'http://devtest.qiniudn.com/若能绽放光芒4.mp4', 'http://devtest.qiniudn.com/若能绽放光芒5.mp4', 'http://devtest.qiniudn.com/若能绽放光芒6.mp4', 'http://devtest.qiniudn.com/若能绽放光芒7.mp4', 'http://devtest.qiniudn.com/若能绽放光芒8.mp4'],
// pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
// type: 'normal',
// },
// danmaku: {
// id: '9E2E3368B56CDBB40',
// api: 'https://api.prprpr.me/dplayer/',
// token: 'tokendemo',
// maximum: 3000
// }
// });
// Quality switching
var dp6 = new DPlayer({
element: document.getElementById('dplayer6'),
autoplay: false,
theme: '#FADFA3',
loop: true,
screenshot: true,
hotkey: true,
preload: 'metadata',
logo: 'http://devtest.qiniudn.com/DPlayer.png',
video: {
url: ['http://devtest.qiniudn.com/若能绽放光芒0.mp4', 'http://devtest.qiniudn.com/若能绽放光芒1.mp4', 'http://devtest.qiniudn.com/若能绽放光芒2.mp4', 'http://devtest.qiniudn.com/若能绽放光芒3.mp4', 'http://devtest.qiniudn.com/若能绽放光芒4.mp4', 'http://devtest.qiniudn.com/若能绽放光芒5.mp4', 'http://devtest.qiniudn.com/若能绽放光芒6.mp4', 'http://devtest.qiniudn.com/若能绽放光芒7.mp4', 'http://devtest.qiniudn.com/若能绽放光芒8.mp4'],
quality: [{
name: '高清',
url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?1'
}, {
name: '超清',
url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?2'
}],
defaultQuality: 0,
pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
type: 'normal',
type: 'auto',
},
danmaku: {
id: '9E2E3368B56CDBB40',
id: '9E2E3368B56CDBB4',
api: 'https://api.prprpr.me/dplayer/',
token: 'tokendemo',
maximum: 3000
maximum: 3000,
user: 'DIYgod的女粉'
}
});

File diff suppressed because one or more lines are too long

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

@ -22,7 +22,12 @@ class DPlayer {
constructor (option) {
this.option = handleOption(option);
const tran = new i18n(this.option.lang).tran;
if (this.option.video.quality) {
this.qualityIndex = this.option.video.defaultQuality;
this.quality = this.option.video.quality[this.option.video.defaultQuality];
}
this.tran = new i18n(this.option.lang).tran;
/**
* Update progress bar, including loading progress bar and play progress bar
@ -57,7 +62,7 @@ class DPlayer {
this.element.classList.add('dplayer-mobile');
}
this.element.innerHTML = html.main(option, index, tran);
this.element.innerHTML = html.main(option, index, this.tran);
// arrow style
this.arrow = this.element.offsetWidth <= 500;
@ -68,7 +73,7 @@ class DPlayer {
}
// get this video manager
this.video = new Video(this.element.getElementsByClassName('dplayer-video'));
this.video = new Video(this.element.getElementsByClassName('dplayer-video-current'));
// Support HTTP Live Streaming
let enablehls;
@ -88,7 +93,7 @@ class DPlayer {
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
hls.loadSource(this.option.video.url);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log("manifest loaded, found " + data.levels.length + " quality level");
this.notice("manifest loaded, found " + data.levels.length + " quality level");
});
});
}
@ -155,18 +160,6 @@ class DPlayer {
const pbarTimeTips = this.element.getElementsByClassName('dplayer-bar-time')[0];
let barWidth;
if (this.option.danmaku) {
this.video.on('all', 'seeking', () => {
for (let i = 0; i < this.dan.length; i++) {
if (this.dan[i].time >= this.video.currentTime()) {
this.danIndex = i;
return;
}
this.danIndex = this.dan.length;
}
});
}
let lastPlayPos = 0;
let currentPlayPos = 0;
let bufferingDetected = false;
@ -337,7 +330,7 @@ class DPlayer {
* setting
*/
this.danOpacity = localStorage.getItem('DPlayer-opacity') || 0.7;
const settingHTML = html.setting(tran);
const settingHTML = html.setting(this.tran);
// toggle setting box
const settingIcon = this.element.getElementsByClassName('dplayer-setting-icon')[0];
@ -368,7 +361,7 @@ class DPlayer {
openSetting();
});
let loop = this.option.loop;
this.loop = this.option.loop;
const danContainer = this.element.getElementsByClassName('dplayer-danmaku')[0];
let showdan = true;
const settingEvent = () => {
@ -376,15 +369,15 @@ class DPlayer {
const loopEle = this.element.getElementsByClassName('dplayer-setting-loop')[0];
const loopToggle = loopEle.getElementsByClassName('dplayer-toggle-setting-input')[0];
loopToggle.checked = loop;
loopToggle.checked = this.loop;
loopEle.addEventListener('click', () => {
loopToggle.checked = !loopToggle.checked;
if (loopToggle.checked) {
loop = true;
this.loop = true;
}
else {
loop = false;
this.loop = false;
}
closeSetting();
});
@ -497,66 +490,7 @@ class DPlayer {
};
settingEvent();
/**
* video events
*/
// show video time: the metadata has loaded or changed
this.video.on('all', 'durationchange', (i, video) => {
if (video.duration !== 1) { // compatibility: Android browsers will output 1 at first
this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = utils.secondToTime(this.video.duration);
}
});
// show video loaded bar: to inform interested parties of progress downloading the media
this.video.on('current', 'progress', (i, video) => {
const percentage = video.buffered.length ? video.buffered.end(video.buffered.length - 1) / video.duration : 0;
this.updateBar('loaded', percentage, 'width');
});
// video download error: an error occurs
this.video.on('all', 'error', () => {
this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = `Error happens ╥﹏╥`;
this.trigger('pause');
});
// video can play: enough data is available that the media can be played
this.video.on('current', 'canplay', () => {
this.trigger('canplay');
});
// music end
this.ended = false;
this.video.on('all', 'ended', (i) => {
if (i === this.video.videos.length - 1) {
this.updateBar('played', 1, 'width');
console.log(loop);
if (!loop) {
this.ended = true;
this.pause();
this.trigger('ended');
}
else {
this.video.switch(0);
this.video.play();
}
}
});
this.video.on('current', 'play', () => {
if (this.paused) {
this.play();
}
});
this.video.on('current', 'pause', () => {
if (!this.paused) {
this.pause();
}
});
// control volume
this.video.attr('volume', parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100);
this.initVideo();
// set duration time
if (this.video.duration !== 1) { // compatibility: Android browsers will output 1 at first
@ -609,7 +543,7 @@ class DPlayer {
// text can't be empty
if (!commentInput.value.replace(/^\s+|\s+$/g, '')) {
alert(tran('Please input danmaku!'));
this.notice(this.tran('Please input danmaku content!'));
return;
}
@ -856,6 +790,18 @@ class DPlayer {
});
});
/**
* Switch quality
*/
if (this.option.video.quality) {
this.element.getElementsByClassName('dplayer-quality-list')[0].addEventListener('click', (e) => {
if (e.target.classList.contains('dplayer-quality-item')) {
this.switchQuality(e.target.dataset.index);
this.element.getElementsByClassName('dplayer-quality-icon')[0].innerHTML = this.option.video.quality[this.qualityIndex].name;
}
});
}
/**
* Screenshot
*/
@ -964,10 +910,10 @@ class DPlayer {
++readCount;
if (err) {
if (err.response) {
console.log(err.response.msg);
this.notice(err.response.msg);
}
else {
console.log('Request was unsuccessful: ' + err.status);
this.notice('Request was unsuccessful: ' + err.status);
}
results[i] = [];
}
@ -1128,7 +1074,7 @@ class DPlayer {
this.updateBar('loaded', 0, 'width');
this.element.getElementsByClassName('dplayer-ptime')[0].innerHTML = '00:00';
this.element.getElementsByClassName('dplayer-danmaku')[0].innerHTML = `<div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>`;
this.danTunnel = {
this.danTuel = {
right: {},
top: {},
bottom: {}
@ -1139,6 +1085,111 @@ class DPlayer {
}
}
initVideo () {
if (this.option.danmaku) {
this.video.on('all', 'seeking', () => {
for (let i = 0; i < this.dan.length; i++) {
if (this.dan[i].time >= this.video.currentTime()) {
this.danIndex = i;
return;
}
this.danIndex = this.dan.length;
}
});
}
/**
* video events
*/
// show video time: the metadata has loaded or changed
this.video.on('all', 'durationchange', (i, video) => {
if (video.duration !== 1) { // compatibility: Android browsers will output 1 at first
this.element.getElementsByClassName('dplayer-dtime')[0].innerHTML = utils.secondToTime(this.video.duration);
}
});
// show video loaded bar: to inform interested parties of progress downloading the media
this.video.on('current', 'progress', (i, video) => {
const percentage = video.buffered.length ? video.buffered.end(video.buffered.length - 1) / video.duration : 0;
this.updateBar('loaded', percentage, 'width');
});
// video download error: an error occurs
this.video.on('all', 'error', () => {
this.notice(this.tran('This video fails to load'), -1);
this.trigger('pause');
});
// video can play: enough data is available that the media can be played
this.video.on('current', 'canplay', () => {
this.trigger('canplay');
});
// music end
this.ended = false;
this.video.on('all', 'ended', (i) => {
if (i === this.video.videos.length - 1) {
this.updateBar('played', 1, 'width');
if (!this.loop) {
this.ended = true;
this.pause();
this.trigger('ended');
}
else {
this.video.switch(0);
this.video.play();
}
}
});
this.video.on('current', 'play', () => {
if (this.paused) {
this.play();
}
});
this.video.on('current', 'pause', () => {
if (!this.paused) {
this.pause();
}
});
// control volume
this.video.attr('volume', parseInt(this.element.getElementsByClassName('dplayer-volume-bar-inner')[0].style.width) / 100);
}
switchQuality (index) {
if (this.qualityIndex === index || this.switchingQuality) {
return;
}
else {
this.qualityIndex = index;
}
this.switchingQuality = true;
this.video.pause();
this.quality = this.option.video.quality[index];
const videoHTML = html.video(false, null, this.option.screenshot, 'auto', this.quality.url);
const videoEle = new DOMParser().parseFromString(videoHTML, 'text/html').body.firstChild;
const parent = this.element.getElementsByClassName('dplayer-video-wrap')[0];
parent.prepend(videoEle);
this.prevVideo = this.video;
this.video = new Video([videoEle], this.prevVideo.duration);
this.initVideo();
this.video.seek(this.prevVideo.currentTime());
this.notice(`${this.tran('Switching to')} ${this.quality.name} ${this.tran('quality')}`, -1);
this.video.on('current', 'canplay', () => {
if (this.prevVideo) {
parent.removeChild(this.prevVideo.current);
this.prevVideo = null;
this.video.current.classList.add('dplayer-video-current');
this.video.play();
this.notice(`${this.tran('Switched to')} ${this.quality.name} ${this.tran('quality')}`);
this.switchingQuality = false;
}
});
}
timeTipsHandler (pbar, timeTips) {
// http://stackoverflow.com/questions/1480133/how-can-i-get-an-objects-absolute-position-on-the-page-in-javascript
const cumulativeOffset = (element) => {
@ -1185,6 +1236,21 @@ class DPlayer {
}
};
}
notice (text, time) {
const noticeEle = this.element.getElementsByClassName('dplayer-notice')[0];
noticeEle.innerHTML = text;
noticeEle.style.opacity = 1;
if (this.noticeTime) {
clearTimeout(this.noticeTime);
}
if (time && time < 0) {
return;
}
this.noticeTime = setTimeout(() => {
noticeEle.style.opacity = 0;
}, time || 2000);
}
}
module.exports = DPlayer;

View File

@ -167,6 +167,10 @@
.dplayer-video-current {
display: block;
}
.dplayer-video-prepare {
display: none;
}
}
.dplayer-danmaku {
@ -381,7 +385,7 @@
font-size: 12px;
text-align: center;
opacity: 1;
transition: left opacity .1s ease-in-out;
transition: opacity .1s ease-in-out;
}
.dplayer-bar {
@ -460,7 +464,7 @@
}
.dplayer-icon {
width: 46px;
width: 40px;
height: 100%;
border: none;
background-color: transparent;
@ -476,6 +480,13 @@
opacity: 1;
}
&.dplayer-quality-icon {
color: #fff;
width: auto;
line-height: 22px;
font-size: 14px;
}
&.dplayer-comment-icon {
padding: 10px 9px 9px;
}
@ -684,6 +695,55 @@
}
}
.dplayer-quality {
position: relative;
display: inline-block;
height: 100%;
z-index: 2;
&:hover {
.dplayer-quality-list {
display: block;
}
.dplayer-quality-mask {
display: block;
}
}
.dplayer-quality-mask {
display: none;
position: absolute;
bottom: 38px;
left: -18px;
width: 80px;
padding-bottom: 12px;
}
.dplayer-quality-list {
display: none;
font-size: 12px;
width: 80px;
border-radius: 2px;
background: rgba(28, 28, 28, 0.9);
padding: 5px 0;
transition: all .3s ease-in-out;
overflow: hidden;
color: #fff;
text-align: center;
}
.dplayer-quality-item {
height: 25px;
box-sizing: border-box;
cursor: pointer;
line-height: 25px;
&:hover {
background-color: rgba(255,255,255,.1);
}
}
}
.dplayer-comment {
display: inline-block;
height: 100%;
@ -974,6 +1034,35 @@
}
}
}
.dplayer-logo {
pointer-events: none;
position: absolute;
left: 20px;
top: 20px;
max-width: 50px;
max-height: 50px;
img {
max-width: 100%;
max-height: 100%;
}
}
.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;
}
}
@keyframes my-face {

View File

@ -1,15 +1,18 @@
const svg = require('./svg.js');
module.exports = {
const html = {
main: (option, index, tran) => {
let videos = ``;
for (let i = 0; i < option.video.url.length; i++) {
videos += `<video class="dplayer-video ${i === 0 ? `dplayer-video-current"` : ``}" ${option.video.pic ? `poster="${option.video.pic}"` : ``} webkit-playsinline playsinline ${option.screenshot ? `crossorigin="anonymous"` : ``} preload="${option.video.url.length ? 'metadata' : option.preload}" src="${option.video.url[i]}"></video>`;
videos += html.video(i === 0, option.video.pic, option.screenshot, option.video.url.length ? 'metadata' : option.preload, option.video.url[i]);
}
return `
<div class="dplayer-mask"></div>
<div class="dplayer-video-wrap">
${videos}
${option.logo ? `
<div class="dplayer-logo"><img src="${option.logo}"></div>
` : ``}
<div class="dplayer-danmaku">
<div class="dplayer-danmaku-item dplayer-danmaku-item--demo"></div>
</div>
@ -67,8 +70,16 @@ module.exports = {
<span class="dplayer-time"><span class="dplayer-ptime">0:00</span> / <span class="dplayer-dtime">0:00</span></span>
</div>
<div class="dplayer-icons dplayer-icons-right">
${option.video.quality ? `
<div class="dplayer-quality">
<button class="dplayer-icon dplayer-quality-icon">${option.video.quality[option.video.defaultQuality].name}</button>
<div class="dplayer-quality-mask">
${html.qualityList(option.video.quality)}
</div>
</div>
` : ``}
${option.screenshot ? `
<a href="#" class="dplayer-icon dplayer-camera-icon"}dplayer-volume>
<a href="#" class="dplayer-icon dplayer-camera-icon">
${svg('camera')}
</a>
` : ``}
@ -155,13 +166,32 @@ module.exports = {
</div>
</div>
</div>
<div class="dplayer-menu">
<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="http://diygod.me/">${tran('About author')}</a></span></div>
<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer/issues">${tran('DPlayer feedback')}</a></span></div>
<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="https://github.com/DIYgod/DPlayer">${tran('About DPlayer')}</a></span></div>
</div>`;
${html.contextmenuList(option.contextmenu)}
<div class="dplayer-notice"></div>`;
},
contextmenuList: (contextmenu) => {
let result = '<div class="dplayer-menu">';
for (let i = 0; i < contextmenu.length; i++) {
result += `<div class="dplayer-menu-item"><span class="dplayer-menu-label"><a target="_blank" href="${contextmenu[i].link}">${contextmenu[i].text}</a></span></div>`;
}
result += '</div>';
return result;
},
qualityList: (quality) => {
let result = '<div class="dplayer-quality-list">';
for (let i = 0; i < quality.length; i++) {
result += `<div class="dplayer-quality-item" data-index="${i}">${quality[i].name}</div>`;
}
result += '</div>';
return result;
},
video: (current, pic, screenshot, preload, url) => `<video class="dplayer-video ${current ? `dplayer-video-current"` : ``}" ${pic ? `poster="${pic}"` : ``} webkit-playsinline playsinline ${screenshot ? `crossorigin="anonymous"` : ``} ${preload ? `preload="${preload}"` : ``} src="${url}"></video>`,
setting: (tran) => ({
'original': `
<div class="dplayer-setting-item dplayer-setting-speed">
@ -214,4 +244,6 @@ module.exports = {
<span class="dplayer-label">2</span>
</div>`
})
};
};
module.exports = html;

View File

@ -11,10 +11,14 @@ const tranZH = {
'Speed': '速度',
'Opacity for danmaku': '弹幕透明度',
'Normal': '正常',
'Please input danmaku!': '要输入弹幕内容啊喂!',
'Please input danmaku content!': '要输入弹幕内容啊喂!',
'Set danmaku color': '设置弹幕颜色',
'Set danmaku type': '设置弹幕类型',
'Danmaku': '弹幕'
'Danmaku': '弹幕',
'This video fails to load': '视频加载失败',
'Switching to': '正在切换至',
'Switched to': '已经切换至',
'quality': '画质',
};
module.exports = function (lang) {

View File

@ -17,7 +17,21 @@ module.exports = (option) => {
screenshot: false,
hotkey: true,
preload: 'auto',
apiBackend: defaultApiBackend
apiBackend: defaultApiBackend,
contextmenu: [
{
text: '关于作者',
link: 'http://diygod.me'
},
{
text: '播放器意见反馈',
link: 'https://github.com/DIYgod/DPlayer/issues'
},
{
text: '关于 DPlayer 播放器',
link: 'https://github.com/DIYgod/DPlayer'
}
]
};
for (const defaultKey in defaultOption) {
if (defaultOption.hasOwnProperty(defaultKey) && !option.hasOwnProperty(defaultKey)) {
@ -34,5 +48,9 @@ module.exports = (option) => {
option.danmaku.user = 'DIYgod';
}
if (option.video.quality) {
option.video.url = [option.video.quality[option.video.defaultQuality].url];
}
return option;
};

View File

@ -1,19 +1,21 @@
class Video {
constructor (videos) {
constructor (videos, duration) {
this.videos = videos;
this.multi = this.videos.length > 1;
this.index = 0;
this.current = this.videos[this.index];
this.duration = 0;
this.duration = duration || 0;
this.durationArr = [];
this.eventAll = [];
this.eventCurrent = [];
this.on('all', 'durationchange', (i, video) => {
if (video.duration !== 1) { // some Android browsers will output 1 at first
this.durationArr[i] = video.duration;
this.duration = this.durationArr.reduce((sum, cur) => sum + cur);
if (!duration) {
this.duration = this.durationArr.reduce((sum, cur) => sum + cur);
}
}
});
this.on('current', 'end', () => {