Implemented support for multiple subtitle tracks

This commit is contained in:
Erik Thuning 2022-11-04 10:54:41 +01:00
parent 110376146f
commit 0a56ad07b4
2 changed files with 132 additions and 36 deletions

@ -498,25 +498,13 @@
'title': 'Kopiera länk till den här tidpunkten',
'data-title_en': 'Copy URL at current time'},
[createIcon(['timelink'])]);
this._subtitlesButton = parent.createChild(
'button',
{'id': 'subtitles-button',
'title': 'Undertexter är av',
'class': 'hidden',
'data-title_alt': 'Undertexter är på',
'data-title_en': 'Subtitles are off',
'data-title_alt_en': 'Subtitles are on'},
[createIcon(['subtitles-off',
'subtitles-on'])]);
this._subtitlesButton.addEventListener('click', (event) => {
const track = this._mainSource.textTracks[0];
if(track.mode === 'disabled') {
track.mode = 'showing';
} else {
track.mode = 'disabled';
}
this.toggleButtonState(this._subtitlesButton);
});
// setSubtitles() requires this._subtitlesSelect to
// be in the correct position in the DOM already,
// so creating a dummy
this._subtitlesSelect = parent.createChild('button',
{'class': 'hidden'},
['placeholder']);
// setResolutions() requires this._resolutionSelect to
// be in the correct position in the DOM already,
@ -567,13 +555,10 @@
const defaultRes = 0;
const token = presentation.token;
this.setResolutions(resolutions, defaultRes);
if(presentation.subtitles
&& this._subtitlesButton.classList.contains('hidden')) {
this._subtitlesButton.classList.remove('hidden');
}
if(!presentation.subtitles
&& !this._subtitlesButton.classList.contains('hidden')) {
this._subtitlesButton.classList.add('hidden');
if(presentation.subtitles) {
this.setSubtitles(Object.keys(presentation.subtitles));
} else {
this._subtitlesSelect.classList.add('hidden');
}
// create streams
@ -601,10 +586,14 @@
// subs, if present
if(presentation.subtitles) {
video.createChild(
'track',
{'kind': 'subtitles',
'src': `${presentation.subtitles}?token=${token}`});
for(const key in presentation.subtitles) {
const file = presentation.subtitles[key];
video.createChild(
'track',
{'kind': 'subtitles',
'label': key,
'src': `${file}?token=${token}`});
}
}
//misc details
@ -830,6 +819,100 @@
return select;
}
/*
* Populate the list of subtitle choices.
*
* The available subtitles are passed in the 'subtitles' arg.
*
* If there is only one subtitle track, the menu is eliminated
* and it becomes a button toggle instead.
*/
setSubtitles(subtitles) {
let select = null;
if(subtitles.length === 1) {
select = createElement(
'button',
{'id': 'subtitles-select',
'title': 'Undertexter är av',
'data-title_alt': 'Undertexter är på',
'data-title_en': 'Subtitles are off',
'data-title_alt_en': 'Subtitles are on'},
[createIcon(['subtitles-off',
'subtitles-on'])]);
select.addEventListener('click', (event) => {
const track = this._mainSource.textTracks[0];
if(track.mode === 'disabled') {
track.mode = 'showing';
} else {
track.mode = 'disabled';
}
this.toggleButtonState(select);
});
} else {
select = createElement('div', {'id': 'subtitles-select',
'class': 'select'});
const current = select.createChild(
'button',
{'id': 'subtitles-current',
'title': 'Undertexter är av',
'data-title-off': 'Undertexter är av',
'data-title-off_en': 'Subtitles are off',
'data-subtitles-state': 'off'
},
[createIcon(['subtitles-off',
'subtitles-on'])]);
const onIcon = current.querySelector(
'svg use[href="#subtitles-on-icon"]');
const offIcon = current.querySelector(
'svg use[href="#subtitles-off-icon"]');
const list = select.createChild('ul', {'id': 'subtitles-list',
'class': 'list'});
for(let i=0; i<subtitles.length; ++i) {
list.createChild('li')
.createChild('button',
{'data-choice': subtitles[i]},
[subtitles[i]]);
}
list.createChild('li').createChild(
'button',
{'data-text-content_en': 'Off',
'data-choice': 'off'},
['Av']);
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
button.addEventListener('click', (event) => {
const choice = event.currentTarget.dataset.choice;
if(choice === current.dataset.subtitlesState) {
return;
}
current.dataset.subtitlesState = choice;
const length = this._mainSource.textTracks.length
for(let i=0; i<length; i++) {
const track = this._mainSource.textTracks[i]
if(choice !== 'off' && track.label === choice) {
track.mode = 'showing';
} else {
track.mode = 'disabled';
}
}
if(choice === 'off') {
current.title = current.dataset.titleOff;
onIcon.classList.add('hidden');
offIcon.classList.remove('hidden');
} else {
current.title = choice;
offIcon.classList.add('hidden');
onIcon.classList.remove('hidden');
}
});
});
}
this._subtitlesSelect.replaceWith(select);
this._subtitlesSelect = select;
return select;
}
/*
* Return a consistently formatted time string.
*
@ -956,12 +1039,24 @@
newMainParent.classList.add('main');
switchIcons(newMainParent);
const oldTrack = this._mainSource.textTracks[0];
const newTrack = newMain.textTracks[0];
const oldState = oldTrack.mode;
oldTrack.mode = newTrack.mode;
newTrack.mode = oldState;
let oldTrackLabel = null;
for(let i=0; i<this._mainSource.textTracks.length; i++) {
const track = this._mainSource.textTracks[i];
if(track.mode === "showing") {
track.mode = "disabled";
oldTrackLabel = track.label;
break;
}
}
if(oldTrackLabel !== null) {
for(let i=0; i<newMain.textTracks.length; i++) {
const track = newMain.textTracks[i];
if(track.label === oldTrackLabel) {
track.mode = "showing";
break;
}
}
}
this._mainSource = newMain;
this.syncSources();
}

@ -252,6 +252,7 @@ button {
margin-bottom: var(--controls-height);
padding-bottom: var(--gap);
list-style: none;
width: max-content;
}
.list li {
background-color: var(--fade);