Merge branch 'multisubtitle' of github.com:jiliangsoft/DPlayer into jiliangsoft-multisubtitle
This commit is contained in:
commit
2b6b46e5f6
18
demo/demo.js
18
demo/demo.js
|
|
@ -65,7 +65,23 @@ function initPlayers() {
|
|||
thumbnails: 'https://i.loli.net/2019/06/06/5cf8c5d9cec8510758.jpg'
|
||||
},
|
||||
subtitle: {
|
||||
url: 'https://s-sh-17-dplayercdn.oss.dogecdn.com/hikarunara.vtt'
|
||||
url: [
|
||||
{
|
||||
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: {
|
||||
id: '9E2E3368B56CDBB4',
|
||||
|
|
|
|||
|
|
@ -408,6 +408,41 @@
|
|||
display: inline-block;
|
||||
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 {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,12 @@ class Controller {
|
|||
this.initFullButton();
|
||||
this.initQualityButton();
|
||||
this.initScreenshotButton();
|
||||
this.initSubtitleButton();
|
||||
// if subtitle url not array, not init old single subtitle button
|
||||
if (this.player.options.subtitle) {
|
||||
if (typeof this.player.options.subtitle.url === 'string') {
|
||||
this.initSubtitleButton();
|
||||
}
|
||||
}
|
||||
this.initHighlights();
|
||||
this.initAirplayButton();
|
||||
this.initChromecastButton();
|
||||
|
|
@ -364,22 +369,20 @@ class Controller {
|
|||
}
|
||||
|
||||
initSubtitleButton() {
|
||||
if (this.player.options.subtitle) {
|
||||
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.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();
|
||||
});
|
||||
}
|
||||
this.player.template.subtitleButton.addEventListener('click', () => {
|
||||
this.player.subtitle.toggle();
|
||||
});
|
||||
}
|
||||
|
||||
setAutoHide() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Events from './events';
|
|||
import FullScreen from './fullscreen';
|
||||
import User from './user';
|
||||
import Subtitle from './subtitle';
|
||||
import Subtitles from './subtitles';
|
||||
import Bar from './bar';
|
||||
import Timer from './timer';
|
||||
import Bezel from './bezel';
|
||||
|
|
@ -60,6 +61,43 @@ class DPlayer {
|
|||
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({
|
||||
container: this.container,
|
||||
options: this.options,
|
||||
|
|
@ -514,7 +552,12 @@ class DPlayer {
|
|||
this.volume(this.user.get('volume'), true, true);
|
||||
|
||||
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);
|
||||
// init multi subtitles function(sub update)
|
||||
if (Array.isArray(this.options.subtitle.url)) {
|
||||
this.subtitles = new Subtitles(this);
|
||||
}
|
||||
if (!this.user.get('subtitle')) {
|
||||
this.subtitle.hide();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Subtitle {
|
|||
const track = this.video.textTracks[0];
|
||||
|
||||
track.oncuechange = () => {
|
||||
const cue = track.activeCues[0];
|
||||
const cue = track.activeCues[track.activeCues.length - 1];
|
||||
this.container.innerHTML = '';
|
||||
if (cue) {
|
||||
const template = document.createElement('div');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
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;
|
||||
|
|
@ -84,7 +84,11 @@ class Template {
|
|||
this.chromecastButton = this.container.querySelector('.dplayer-chromecast-icon');
|
||||
this.subtitleButton = this.container.querySelector('.dplayer-subtitle-icon');
|
||||
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.subtrack = this.container.querySelector('.dplayer-subtrack');
|
||||
this.qualityButton = this.container.querySelector('.dplayer-quality-icon');
|
||||
this.barPreview = this.container.querySelector('.dplayer-bar-preview');
|
||||
this.barWrap = this.container.querySelector('.dplayer-bar-wrap');
|
||||
|
|
|
|||
|
|
@ -131,11 +131,29 @@
|
|||
</button>
|
||||
</div>
|
||||
{{ if options.subtitle }}
|
||||
{{ if (typeof options.subtitle.url === 'string') }}
|
||||
<div class="dplayer-subtitle-btn">
|
||||
<button class="dplayer-icon dplayer-subtitle-icon" data-balloon="{{ tran('hide-subs') }}" data-balloon-pos="up">
|
||||
<span class="dplayer-icon-content">{{@ icons.subtitle }}</span>
|
||||
</button>
|
||||
</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 }}
|
||||
<div class="dplayer-setting">
|
||||
<button class="dplayer-icon dplayer-setting-icon" data-balloon="{{ tran('setting') }}" data-balloon-pos="up">
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@
|
|||
<source src="{{ url }}">
|
||||
{{ /if}}
|
||||
{{ if enableSubtitle }}
|
||||
<track kind="metadata" default src="{{ subtitle.url }}"></track>
|
||||
<track class="dplayer-subtrack" kind="metadata" default src="{{ typeof subtitle.url === 'string' ? subtitle.url : subtitle.url[subtitle.index].url }}"></track>
|
||||
{{ /if }}
|
||||
</video>
|
||||
Loading…
Reference in New Issue