Merge branch 'multisubtitle' of github.com:jiliangsoft/DPlayer into jiliangsoft-multisubtitle

This commit is contained in:
DIYgod 2022-08-15 22:11:03 +01:00
commit 2b6b46e5f6
9 changed files with 217 additions and 19 deletions

View File

@ -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',

View File

@ -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%;

View File

@ -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() {

View File

@ -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();
}

View File

@ -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');

79
src/js/subtitles.js Normal file
View File

@ -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;

View File

@ -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');

View File

@ -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">

View File

@ -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>