mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #220 from redSpoutnik/synchronize-subtitles
Add subtitle synchronization on HTML video player
This commit is contained in:
commit
d7a0a9b5bc
10 changed files with 399 additions and 1 deletions
|
@ -21,6 +21,7 @@
|
||||||
- [RazeLighter777](https://github.com/RazeLighter777)
|
- [RazeLighter777](https://github.com/RazeLighter777)
|
||||||
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
|
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
|
||||||
- [thornbill](https://github.com/thornbill)
|
- [thornbill](https://github.com/thornbill)
|
||||||
|
- [redSpoutnik](https://github.com/redSpoutnik)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
var currentAssRenderer;
|
var currentAssRenderer;
|
||||||
var customTrackIndex = -1;
|
var customTrackIndex = -1;
|
||||||
|
|
||||||
|
var showTrackOffset = false;
|
||||||
|
var currentTrackOffset;
|
||||||
|
|
||||||
var videoSubtitlesElem;
|
var videoSubtitlesElem;
|
||||||
var currentTrackEvents;
|
var currentTrackEvents;
|
||||||
|
|
||||||
|
@ -543,6 +546,81 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
setCurrentTrackElement(index);
|
setCurrentTrackElement(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.enableShowingSubtitleOffset = function() {
|
||||||
|
showTrackOffset = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disableShowingSubtitleOffset = function() {
|
||||||
|
showTrackOffset = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowingSubtitleOffsetEnabled = function() {
|
||||||
|
return showTrackOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubtitleOffset = function(offset) {
|
||||||
|
|
||||||
|
var offsetValue = parseFloat(offset);
|
||||||
|
var videoElement = self._mediaElement;
|
||||||
|
var mediaStreamTextTracks = getMediaStreamTextTracks(self._currentPlayOptions.mediaSource);
|
||||||
|
|
||||||
|
Array.from(videoElement.textTracks)
|
||||||
|
.filter(function(trackElement) {
|
||||||
|
if (customTrackIndex === -1 ) {
|
||||||
|
// get showing .vtt textTacks
|
||||||
|
return trackElement.mode === 'showing';
|
||||||
|
} else {
|
||||||
|
// get current .ass textTrack
|
||||||
|
return ("textTrack" + customTrackIndex) === trackElement.id;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.forEach(function(trackElement) {
|
||||||
|
|
||||||
|
var track = mediaStreamTextTracks.filter(function(stream) {
|
||||||
|
return ("textTrack" + stream.Index) === trackElement.id;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if(track) {
|
||||||
|
offsetValue = updateCurrentTrackOffset(offsetValue);
|
||||||
|
var format = (track.Codec || '').toLowerCase();
|
||||||
|
if (format !== 'ass' && format !== 'ssa') {
|
||||||
|
setVttSubtitleOffset(trackElement, offsetValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("No available track, cannot apply offset : " + offsetValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateCurrentTrackOffset(offsetValue) {
|
||||||
|
|
||||||
|
var relativeOffset = offsetValue;
|
||||||
|
var newTrackOffset = offsetValue;
|
||||||
|
if(currentTrackOffset){
|
||||||
|
relativeOffset -= currentTrackOffset;
|
||||||
|
}
|
||||||
|
currentTrackOffset = newTrackOffset;
|
||||||
|
// relative to currentTrackOffset
|
||||||
|
return relativeOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVttSubtitleOffset(currentTrack, offsetValue) {
|
||||||
|
|
||||||
|
if(currentTrack.cues) {
|
||||||
|
Array.from(currentTrack.cues)
|
||||||
|
.forEach(function(cue) {
|
||||||
|
cue.startTime -= offsetValue;
|
||||||
|
cue.endTime -= offsetValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getSubtitleOffset = function() {
|
||||||
|
return currentTrackOffset;
|
||||||
|
}
|
||||||
|
|
||||||
function isAudioStreamSupported(stream, deviceProfile) {
|
function isAudioStreamSupported(stream, deviceProfile) {
|
||||||
|
|
||||||
var codec = (stream.Codec || '').toLowerCase();
|
var codec = (stream.Codec || '').toLowerCase();
|
||||||
|
@ -1167,6 +1245,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
|
|
||||||
function updateSubtitleText(timeMs) {
|
function updateSubtitleText(timeMs) {
|
||||||
|
|
||||||
|
// handle offset for ass tracks
|
||||||
|
if(currentTrackOffset) {
|
||||||
|
timeMs += (currentTrackOffset * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
var clock = currentClock;
|
var clock = currentClock;
|
||||||
if (clock) {
|
if (clock) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1635,6 +1635,46 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
getPlayerData(player).subtitleStreamIndex = index;
|
getPlayerData(player).subtitleStreamIndex = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.supportSubtitleOffset = function(player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
return player && 'setSubtitleOffset' in player;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.enableShowingSubtitleOffset = function(player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
player.enableShowingSubtitleOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disableShowingSubtitleOffset = function(player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
player.disableShowingSubtitleOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowingSubtitleOffsetEnabled = function(player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
return player.isShowingSubtitleOffsetEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isSubtitleStreamExternal = function(index, player) {
|
||||||
|
var stream = getSubtitleStream(player, index);
|
||||||
|
return stream ? getDeliveryMethod(stream) === 'External' : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubtitleOffset = function (value, player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
player.setSubtitleOffset(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.getPlayerSubtitleOffset = function(player) {
|
||||||
|
player = player || self._currentPlayer;
|
||||||
|
return player.getSubtitleOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.canHandleOffsetOnCurrentSubtitle = function(player) {
|
||||||
|
var index = self.getSubtitleStreamIndex(player);
|
||||||
|
return index !== -1 && self.isSubtitleStreamExternal(index, player);
|
||||||
|
}
|
||||||
|
|
||||||
self.seek = function (ticks, player) {
|
self.seek = function (ticks, player) {
|
||||||
|
|
||||||
ticks = Math.max(0, ticks);
|
ticks = Math.max(0, ticks);
|
||||||
|
|
|
@ -213,6 +213,15 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.suboffset) {
|
||||||
|
|
||||||
|
menuItems.push({
|
||||||
|
name: globalize.translate('SubtitleOffset'),
|
||||||
|
id: 'suboffset',
|
||||||
|
asideText: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return actionsheet.show({
|
return actionsheet.show({
|
||||||
items: menuItems,
|
items: menuItems,
|
||||||
positionTo: options.positionTo
|
positionTo: options.positionTo
|
||||||
|
@ -248,6 +257,11 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
|
||||||
options.onOption('stats');
|
options.onOption('stats');
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
case 'suboffset':
|
||||||
|
if (options.onOption) {
|
||||||
|
options.onOption('suboffset');
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
48
src/components/subtitlesync/subtitlesync.css
Normal file
48
src/components/subtitlesync/subtitlesync.css
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
.subtitleSyncContainer {
|
||||||
|
width: 40%;
|
||||||
|
margin-left: 30%;
|
||||||
|
margin-right: 30%;
|
||||||
|
height: 4.2em;
|
||||||
|
background: rgba(28,28,28,0.8);
|
||||||
|
border-radius: .3em;
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleSync-closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
color: #ccc;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleSyncTextField {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 40%;
|
||||||
|
margin-left: 30%;
|
||||||
|
margin-right: 30%;
|
||||||
|
top: 0.1em;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20px;
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleSyncSliderContainer {
|
||||||
|
width: 98%;
|
||||||
|
margin-left: 1%;
|
||||||
|
margin-right: 1%;
|
||||||
|
top: 2.5em;
|
||||||
|
height: 1.4em;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-webkit-flex-grow: 1;
|
||||||
|
flex-grow: 1;
|
||||||
|
border-radius: .3em;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
163
src/components/subtitlesync/subtitlesync.js
Normal file
163
src/components/subtitlesync/subtitlesync.js
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, template, css) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var player;
|
||||||
|
var subtitleSyncSlider;
|
||||||
|
var subtitleSyncTextField;
|
||||||
|
var subtitleSyncCloseButton;
|
||||||
|
var subtitleSyncContainer;
|
||||||
|
|
||||||
|
function init(instance) {
|
||||||
|
|
||||||
|
var parent = document.createElement('div');
|
||||||
|
parent.innerHTML = template;
|
||||||
|
|
||||||
|
subtitleSyncSlider = parent.querySelector(".subtitleSyncSlider");
|
||||||
|
subtitleSyncTextField = parent.querySelector(".subtitleSyncTextField");
|
||||||
|
subtitleSyncCloseButton = parent.querySelector(".subtitleSync-closeButton");
|
||||||
|
subtitleSyncContainer = parent.querySelector(".subtitleSyncContainer");
|
||||||
|
|
||||||
|
subtitleSyncContainer.classList.add("hide");
|
||||||
|
|
||||||
|
subtitleSyncTextField.updateOffset = function(offset) {
|
||||||
|
this.textContent = offset + "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
subtitleSyncTextField.addEventListener("keypress", function(event) {
|
||||||
|
|
||||||
|
if(event.key === "Enter"){
|
||||||
|
// if input key is enter search for float pattern
|
||||||
|
var inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
|
||||||
|
if(inputOffset) {
|
||||||
|
inputOffset = inputOffset[0];
|
||||||
|
|
||||||
|
// replace current text by considered offset
|
||||||
|
this.textContent = inputOffset + "s";
|
||||||
|
|
||||||
|
inputOffset = parseFloat(inputOffset);
|
||||||
|
// set new offset
|
||||||
|
playbackManager.setSubtitleOffset(inputOffset, player);
|
||||||
|
// synchronize with slider value
|
||||||
|
subtitleSyncSlider.updateOffset(
|
||||||
|
getPercentageFromOffset(inputOffset));
|
||||||
|
} else {
|
||||||
|
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + "s";
|
||||||
|
}
|
||||||
|
this.hasFocus = false;
|
||||||
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
// keep focus to prevent fade with bottom layout
|
||||||
|
this.hasFocus = true;
|
||||||
|
if(event.key.match(/[+-\d.s]/) === null) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
subtitleSyncSlider.updateOffset = function(percent) {
|
||||||
|
// default value is 0s = 50%
|
||||||
|
this.value = percent === undefined ? 50 : percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
subtitleSyncSlider.addEventListener("change", function () {
|
||||||
|
// set new offset
|
||||||
|
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
|
||||||
|
// synchronize with textField value
|
||||||
|
subtitleSyncTextField.updateOffset(
|
||||||
|
getOffsetFromPercentage(this.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
subtitleSyncSlider.addEventListener("touchmove", function () {
|
||||||
|
// set new offset
|
||||||
|
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
|
||||||
|
// synchronize with textField value
|
||||||
|
subtitleSyncTextField.updateOffset(
|
||||||
|
getOffsetFromPercentage(this.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
subtitleSyncSlider.getBubbleHtml = function (value) {
|
||||||
|
var newOffset = getOffsetFromPercentage(value);
|
||||||
|
return '<h1 class="sliderBubbleText">' +
|
||||||
|
(newOffset > 0 ? "+" : "") + parseFloat(newOffset) + "s" +
|
||||||
|
"</h1>";
|
||||||
|
};
|
||||||
|
|
||||||
|
subtitleSyncCloseButton.addEventListener("click", function() {
|
||||||
|
playbackManager.disableShowingSubtitleOffset(player);
|
||||||
|
SubtitleSync.prototype.toggle("forceToHide");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(parent);
|
||||||
|
|
||||||
|
instance.element = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getOffsetFromPercentage(value) {
|
||||||
|
// convert percent to fraction
|
||||||
|
var offset = (value - 50) / 50;
|
||||||
|
// multiply by offset min/max range value (-x to +x) :
|
||||||
|
offset *= 30;
|
||||||
|
return offset.toFixed(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getPercentageFromOffset(value) {
|
||||||
|
// divide by offset min/max range value (-x to +x) :
|
||||||
|
var percentValue = value / 30;
|
||||||
|
// convert fraction to percent
|
||||||
|
percentValue *= 50;
|
||||||
|
percentValue += 50;
|
||||||
|
return Math.min(100, Math.max(0, percentValue.toFixed()));
|
||||||
|
};
|
||||||
|
|
||||||
|
function SubtitleSync(currentPlayer) {
|
||||||
|
player = currentPlayer;
|
||||||
|
init(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtitleSync.prototype.destroy = function(){
|
||||||
|
SubtitleSync.prototype.toggle("forceToHide");
|
||||||
|
if(player){
|
||||||
|
playbackManager.disableShowingSubtitleOffset(player);
|
||||||
|
playbackManager.setSubtitleOffset(0, player);
|
||||||
|
}
|
||||||
|
var elem = this.element;
|
||||||
|
if (elem) {
|
||||||
|
elem.parentNode.removeChild(elem);
|
||||||
|
this.element = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtitleSync.prototype.toggle = function(action) {
|
||||||
|
|
||||||
|
if(player && playbackManager.supportSubtitleOffset(player)){
|
||||||
|
|
||||||
|
switch(action) {
|
||||||
|
case undefined:
|
||||||
|
// if showing subtitle sync is enabled
|
||||||
|
if(playbackManager.isShowingSubtitleOffsetEnabled(player) &&
|
||||||
|
// if there is an external subtitle stream enabled
|
||||||
|
playbackManager.canHandleOffsetOnCurrentSubtitle(player)){
|
||||||
|
// if no subtitle offset is defined
|
||||||
|
if(!playbackManager.getPlayerSubtitleOffset(player)) {
|
||||||
|
// set default offset to '0' = 50%
|
||||||
|
subtitleSyncSlider.value = "50";
|
||||||
|
subtitleSyncTextField.textContent = "0s";
|
||||||
|
playbackManager.setSubtitleOffset(0, player);
|
||||||
|
}
|
||||||
|
// show subtitle sync
|
||||||
|
subtitleSyncContainer.classList.remove("hide");
|
||||||
|
break; // stop here
|
||||||
|
} // else continue and hide
|
||||||
|
case "hide":
|
||||||
|
if(subtitleSyncTextField.hasFocus){break;} // else continue and hide
|
||||||
|
case "forceToHide":
|
||||||
|
subtitleSyncContainer.classList.add("hide");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SubtitleSync;
|
||||||
|
});
|
7
src/components/subtitlesync/subtitlesync.template.html
Normal file
7
src/components/subtitlesync/subtitlesync.template.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="subtitleSyncContainer">
|
||||||
|
<button type="button" is="paper-icon-button-light" class="subtitleSync-closeButton"><i class="md-icon">close</i></button>
|
||||||
|
<div class="subtitleSyncTextField" contenteditable="true" spellcheck="false">0s</div>
|
||||||
|
<div class="sliderContainer subtitleSyncSliderContainer">
|
||||||
|
<input is="emby-slider" type="range" step="1" min="0" max="100" value="50" class="subtitleSyncSlider" />
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -295,8 +295,10 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
|
|
||||||
if (playbackManager.subtitleTracks(player).length) {
|
if (playbackManager.subtitleTracks(player).length) {
|
||||||
view.querySelector(".btnSubtitles").classList.remove("hide");
|
view.querySelector(".btnSubtitles").classList.remove("hide");
|
||||||
|
toggleSubtitleSync();
|
||||||
} else {
|
} else {
|
||||||
view.querySelector(".btnSubtitles").classList.add("hide");
|
view.querySelector(".btnSubtitles").classList.add("hide");
|
||||||
|
toggleSubtitleSync("forceToHide");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playbackManager.audioTracks(player).length > 1) {
|
if (playbackManager.audioTracks(player).length > 1) {
|
||||||
|
@ -419,6 +421,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
focusManager.focus(elem.querySelector(".btnPause"));
|
focusManager.focus(elem.querySelector(".btnPause"));
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
toggleSubtitleSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,6 +434,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
once: true
|
once: true
|
||||||
});
|
});
|
||||||
currentVisibleMenu = null;
|
currentVisibleMenu = null;
|
||||||
|
toggleSubtitleSync("hide");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,6 +626,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
|
|
||||||
function releaseCurrentPlayer() {
|
function releaseCurrentPlayer() {
|
||||||
destroyStats();
|
destroyStats();
|
||||||
|
destroySubtitleSync();
|
||||||
resetUpNextDialog();
|
resetUpNextDialog();
|
||||||
var player = currentPlayer;
|
var player = currentPlayer;
|
||||||
|
|
||||||
|
@ -897,11 +902,17 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
var player = currentPlayer;
|
var player = currentPlayer;
|
||||||
|
|
||||||
if (player) {
|
if (player) {
|
||||||
|
|
||||||
|
// show subtitle offset feature only if player and media support it
|
||||||
|
var showSubOffset = playbackManager.supportSubtitleOffset(player) &&
|
||||||
|
playbackManager.canHandleOffsetOnCurrentSubtitle(player);
|
||||||
|
|
||||||
playerSettingsMenu.show({
|
playerSettingsMenu.show({
|
||||||
mediaType: "Video",
|
mediaType: "Video",
|
||||||
player: player,
|
player: player,
|
||||||
positionTo: btn,
|
positionTo: btn,
|
||||||
stats: true,
|
stats: true,
|
||||||
|
suboffset: showSubOffset,
|
||||||
onOption: onSettingsOption
|
onOption: onSettingsOption
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -911,6 +922,12 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
function onSettingsOption(selectedOption) {
|
function onSettingsOption(selectedOption) {
|
||||||
if ("stats" === selectedOption) {
|
if ("stats" === selectedOption) {
|
||||||
toggleStats();
|
toggleStats();
|
||||||
|
} else if ("suboffset" === selectedOption) {
|
||||||
|
var player = currentPlayer;
|
||||||
|
if (player) {
|
||||||
|
playbackManager.enableShowingSubtitleOffset(player);
|
||||||
|
toggleSubtitleSync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1008,10 +1025,30 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
if (index !== currentIndex) {
|
if (index !== currentIndex) {
|
||||||
playbackManager.setSubtitleStreamIndex(index, player);
|
playbackManager.setSubtitleStreamIndex(index, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleSubtitleSync();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSubtitleSync(action) {
|
||||||
|
require(["subtitleSync"], function (SubtitleSync) {
|
||||||
|
var player = currentPlayer;
|
||||||
|
if (subtitleSyncOverlay) {
|
||||||
|
subtitleSyncOverlay.toggle(action);
|
||||||
|
} else if(player){
|
||||||
|
subtitleSyncOverlay = new SubtitleSync(player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroySubtitleSync() {
|
||||||
|
if (subtitleSyncOverlay) {
|
||||||
|
subtitleSyncOverlay.destroy();
|
||||||
|
subtitleSyncOverlay = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onWindowKeyDown(e) {
|
function onWindowKeyDown(e) {
|
||||||
if (!currentVisibleMenu && (32 === e.keyCode || 13 === e.keyCode)) {
|
if (!currentVisibleMenu && (32 === e.keyCode || 13 === e.keyCode)) {
|
||||||
playbackManager.playPause(currentPlayer);
|
playbackManager.playPause(currentPlayer);
|
||||||
|
@ -1146,6 +1183,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
var programStartDateMs = 0;
|
var programStartDateMs = 0;
|
||||||
var programEndDateMs = 0;
|
var programEndDateMs = 0;
|
||||||
var playbackStartTimeTicks = 0;
|
var playbackStartTimeTicks = 0;
|
||||||
|
var subtitleSyncOverlay;
|
||||||
var nowPlayingVolumeSlider = view.querySelector(".osdVolumeSlider");
|
var nowPlayingVolumeSlider = view.querySelector(".osdVolumeSlider");
|
||||||
var nowPlayingVolumeSliderContainer = view.querySelector(".osdVolumeSliderContainer");
|
var nowPlayingVolumeSliderContainer = view.querySelector(".osdVolumeSliderContainer");
|
||||||
var nowPlayingPositionSlider = view.querySelector(".osdPositionSlider");
|
var nowPlayingPositionSlider = view.querySelector(".osdPositionSlider");
|
||||||
|
@ -1215,6 +1253,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyStats();
|
destroyStats();
|
||||||
|
destroySubtitleSync();
|
||||||
});
|
});
|
||||||
var lastPointerDown = 0;
|
var lastPointerDown = 0;
|
||||||
dom.addEventListener(view, window.PointerEvent ? "pointerdown" : "click", function (e) {
|
dom.addEventListener(view, window.PointerEvent ? "pointerdown" : "click", function (e) {
|
||||||
|
@ -1269,6 +1308,7 @@ define(["playbackManager", "dom", "inputmanager", "datetime", "itemHelper", "med
|
||||||
nowPlayingVolumeSlider.addEventListener("touchmove", function () {
|
nowPlayingVolumeSlider.addEventListener("touchmove", function () {
|
||||||
playbackManager.setVolume(this.value, currentPlayer);
|
playbackManager.setVolume(this.value, currentPlayer);
|
||||||
});
|
});
|
||||||
|
|
||||||
nowPlayingPositionSlider.addEventListener("change", function () {
|
nowPlayingPositionSlider.addEventListener("change", function () {
|
||||||
var player = currentPlayer;
|
var player = currentPlayer;
|
||||||
|
|
||||||
|
|
|
@ -758,6 +758,7 @@ var AppInfo = {};
|
||||||
define("recordingButton", [componentsPath + "/recordingcreator/recordingbutton"], returnFirstDependency);
|
define("recordingButton", [componentsPath + "/recordingcreator/recordingbutton"], returnFirstDependency);
|
||||||
define("recordingHelper", [componentsPath + "/recordingcreator/recordinghelper"], returnFirstDependency);
|
define("recordingHelper", [componentsPath + "/recordingcreator/recordinghelper"], returnFirstDependency);
|
||||||
define("subtitleEditor", [componentsPath + "/subtitleeditor/subtitleeditor"], returnFirstDependency);
|
define("subtitleEditor", [componentsPath + "/subtitleeditor/subtitleeditor"], returnFirstDependency);
|
||||||
|
define("subtitleSync", [componentsPath + "/subtitlesync/subtitlesync"], returnFirstDependency);
|
||||||
define("itemIdentifier", [componentsPath + "/itemidentifier/itemidentifier"], returnFirstDependency);
|
define("itemIdentifier", [componentsPath + "/itemidentifier/itemidentifier"], returnFirstDependency);
|
||||||
define("mediaInfo", [componentsPath + "/mediainfo/mediainfo"], returnFirstDependency);
|
define("mediaInfo", [componentsPath + "/mediainfo/mediainfo"], returnFirstDependency);
|
||||||
define("itemContextMenu", [componentsPath + "/itemcontextmenu"], returnFirstDependency);
|
define("itemContextMenu", [componentsPath + "/itemcontextmenu"], returnFirstDependency);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
|
<div id="videoOsdPage" data-role="page" class="page libraryPage" data-backbutton="true">
|
||||||
<div class="pageContainer flex">
|
<div class="pageContainer flex">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,6 +59,7 @@
|
||||||
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
|
<button is="paper-icon-button-light" class="btnSubtitles hide autoSize" title="${Subtitles}">
|
||||||
<i class="xlargePaperIconButton md-icon"></i>
|
<i class="xlargePaperIconButton md-icon"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
|
<button is="paper-icon-button-light" class="btnVideoOsdSettings hide autoSize" title="${Settings}">
|
||||||
<i class="largePaperIconButton md-icon"></i>
|
<i class="largePaperIconButton md-icon"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue