mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Add subtitle position
This commit is contained in:
parent
9e92bfaae7
commit
597b4258d9
10 changed files with 219 additions and 33 deletions
|
@ -1,7 +1,7 @@
|
||||||
define([], function () {
|
define([], function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function getTextStyles(settings) {
|
function getTextStyles(settings, preview) {
|
||||||
|
|
||||||
var list = [];
|
var list = [];
|
||||||
|
|
||||||
|
@ -89,19 +89,44 @@ define([], function () {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!preview) {
|
||||||
|
const pos = parseInt(settings.verticalPosition);
|
||||||
|
const lineHeight = 1.35; // FIXME: It is better to read this value from element
|
||||||
|
const line = Math.abs(pos * lineHeight);
|
||||||
|
if (pos < 0) {
|
||||||
|
list.push({ name: 'min-height', value: `${line}em` });
|
||||||
|
list.push({ name: 'margin-top', value: '' });
|
||||||
|
} else {
|
||||||
|
list.push({ name: 'min-height', value: '' });
|
||||||
|
list.push({ name: 'margin-top', value: `${line}em` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWindowStyles(settings) {
|
function getWindowStyles(settings, preview) {
|
||||||
|
const list = [];
|
||||||
|
|
||||||
return [];
|
if (!preview) {
|
||||||
|
const pos = parseInt(settings.verticalPosition);
|
||||||
|
if (pos < 0) {
|
||||||
|
list.push({ name: 'top', value: '' });
|
||||||
|
list.push({ name: 'bottom', value: '0' });
|
||||||
|
} else {
|
||||||
|
list.push({ name: 'top', value: '0' });
|
||||||
|
list.push({ name: 'bottom', value: '' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStyles(settings) {
|
function getStyles(settings, preview) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: getTextStyles(settings),
|
text: getTextStyles(settings, preview),
|
||||||
window: getWindowStyles(settings)
|
window: getWindowStyles(settings, preview)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +142,7 @@ define([], function () {
|
||||||
|
|
||||||
function applyStyles(elements, appearanceSettings) {
|
function applyStyles(elements, appearanceSettings) {
|
||||||
|
|
||||||
var styles = getStyles(appearanceSettings);
|
var styles = getStyles(appearanceSettings, !!elements.preview);
|
||||||
|
|
||||||
if (elements.text) {
|
if (elements.text) {
|
||||||
applyStyleList(styles.text, elements.text);
|
applyStyleList(styles.text, elements.text);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) {
|
define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'layoutManager', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'emby-slider', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events, layoutManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function populateLanguages(select, languages) {
|
function populateLanguages(select, languages) {
|
||||||
|
@ -21,6 +21,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
appearanceSettings.font = context.querySelector('#selectFont').value;
|
appearanceSettings.font = context.querySelector('#selectFont').value;
|
||||||
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
|
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
|
||||||
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
|
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
|
||||||
|
appearanceSettings.verticalPosition = context.querySelector('#sliderVerticalPosition').value;
|
||||||
|
|
||||||
return appearanceSettings;
|
return appearanceSettings;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +48,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
|
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
|
||||||
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
|
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
|
||||||
context.querySelector('#selectFont').value = appearanceSettings.font || '';
|
context.querySelector('#selectFont').value = appearanceSettings.font || '';
|
||||||
|
context.querySelector('#sliderVerticalPosition').value = appearanceSettings.verticalPosition;
|
||||||
|
|
||||||
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
|
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
|
||||||
|
|
||||||
|
@ -134,10 +136,45 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
|
|
||||||
var elements = {
|
var elements = {
|
||||||
window: view.querySelector('.subtitleappearance-preview-window'),
|
window: view.querySelector('.subtitleappearance-preview-window'),
|
||||||
text: view.querySelector('.subtitleappearance-preview-text')
|
text: view.querySelector('.subtitleappearance-preview-text'),
|
||||||
|
preview: true
|
||||||
};
|
};
|
||||||
|
|
||||||
subtitleAppearanceHelper.applyStyles(elements, appearanceSettings);
|
subtitleAppearanceHelper.applyStyles(elements, appearanceSettings);
|
||||||
|
|
||||||
|
subtitleAppearanceHelper.applyStyles({
|
||||||
|
window: view.querySelector('.subtitleappearance-fullpreview-window'),
|
||||||
|
text: view.querySelector('.subtitleappearance-fullpreview-text')
|
||||||
|
}, appearanceSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtitlePreviewDelay = 1000;
|
||||||
|
let subtitlePreviewTimer;
|
||||||
|
|
||||||
|
function showSubtitlePreview(persistent) {
|
||||||
|
clearTimeout(subtitlePreviewTimer);
|
||||||
|
|
||||||
|
this._fullPreview.classList.remove('subtitleappearance-fullpreview-hide');
|
||||||
|
|
||||||
|
if (persistent) {
|
||||||
|
this._refFullPreview++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._refFullPreview === 0) {
|
||||||
|
subtitlePreviewTimer = setTimeout(hideSubtitlePreview.bind(this), subtitlePreviewDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSubtitlePreview(persistent) {
|
||||||
|
clearTimeout(subtitlePreviewTimer);
|
||||||
|
|
||||||
|
if (persistent) {
|
||||||
|
this._refFullPreview--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._refFullPreview === 0) {
|
||||||
|
this._fullPreview.classList.add('subtitleappearance-fullpreview-hide');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function embed(options, self) {
|
function embed(options, self) {
|
||||||
|
@ -162,6 +199,36 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
|
|
||||||
if (appHost.supports('subtitleappearancesettings')) {
|
if (appHost.supports('subtitleappearancesettings')) {
|
||||||
options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide');
|
options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide');
|
||||||
|
|
||||||
|
self._fullPreview = options.element.querySelector('.subtitleappearance-fullpreview');
|
||||||
|
self._refFullPreview = 0;
|
||||||
|
|
||||||
|
const sliderVerticalPosition = options.element.querySelector('#sliderVerticalPosition');
|
||||||
|
sliderVerticalPosition.addEventListener('input', onAppearanceFieldChange);
|
||||||
|
sliderVerticalPosition.addEventListener('input', () => showSubtitlePreview.call(self));
|
||||||
|
|
||||||
|
const eventPrefix = window.PointerEvent ? 'pointer' : 'mouse';
|
||||||
|
sliderVerticalPosition.addEventListener(`${eventPrefix}enter`, () => showSubtitlePreview.call(self, true));
|
||||||
|
sliderVerticalPosition.addEventListener(`${eventPrefix}leave`, () => hideSubtitlePreview.call(self, true));
|
||||||
|
|
||||||
|
if (layoutManager.tv) {
|
||||||
|
sliderVerticalPosition.addEventListener('focus', () => showSubtitlePreview.call(self, true));
|
||||||
|
sliderVerticalPosition.addEventListener('blur', () => hideSubtitlePreview.call(self, true));
|
||||||
|
|
||||||
|
// Give CustomElements time to attach
|
||||||
|
setTimeout(() => {
|
||||||
|
sliderVerticalPosition.classList.add('focusable');
|
||||||
|
sliderVerticalPosition.enableKeyboardDragging();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.element.querySelector('.chkPreview').addEventListener('change', (e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
showSubtitlePreview.call(self, true);
|
||||||
|
} else {
|
||||||
|
hideSubtitlePreview.call(self, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loadData();
|
self.loadData();
|
||||||
|
|
|
@ -38,6 +38,45 @@
|
||||||
${HeaderSubtitleAppearance}
|
${HeaderSubtitleAppearance}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.subtitleappearance-fullpreview {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: 0.2s
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleappearance-fullpreview-hide {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleappearance-fullpreview-window {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 170%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitleappearance-fullpreview-text {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="subtitleappearance-fullpreview subtitleappearance-fullpreview-hide">
|
||||||
|
<div class="subtitleappearance-fullpreview-window">
|
||||||
|
<div class="subtitleappearance-fullpreview-text">
|
||||||
|
${HeaderSubtitleAppearance}
|
||||||
|
<br>
|
||||||
|
${TheseSettingsAffectSubtitlesOnThisDevice}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="margin: 2em 0 2em;">
|
<div style="margin: 2em 0 2em;">
|
||||||
<div class="subtitleappearance-preview flex align-items-center justify-content-center" style="margin:2em 0;padding:1.6em;color:black;background:linear-gradient(140deg,#aa5cc3,#00a4dc);">
|
<div class="subtitleappearance-preview flex align-items-center justify-content-center" style="margin:2em 0;padding:1.6em;color:black;background:linear-gradient(140deg,#aa5cc3,#00a4dc);">
|
||||||
<div class="subtitleappearance-preview-window flex align-items-center justify-content-center" style="width: 90%; padding: .25em;">
|
<div class="subtitleappearance-preview-window flex align-items-center justify-content-center" style="width: 90%; padding: .25em;">
|
||||||
|
@ -89,6 +128,20 @@
|
||||||
<option value="">${DropShadow}</option>
|
<option value="">${DropShadow}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sliderContainer-settings">
|
||||||
|
<div class="sliderContainer">
|
||||||
|
<input is="emby-slider" id="sliderVerticalPosition" label="${LabelSubtitleVerticalPosition}" type="range" min="-16" max="16" />
|
||||||
|
</div>
|
||||||
|
<div class="fieldDescription">${SubtitleVerticalPositionHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="checkboxContainer">
|
||||||
|
<label>
|
||||||
|
<input is="emby-checkbox" type="checkbox" class="chkPreview" />
|
||||||
|
<span>${Preview}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
|
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
|
||||||
|
|
|
@ -230,3 +230,18 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5em 0.75em;
|
padding: 0.5em 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: 'sliderContainer' is used to wrap slider's pieces */
|
||||||
|
.sliderContainer-settings {
|
||||||
|
margin-bottom: 1.8em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderContainer-settings .mdl-slider-container {
|
||||||
|
height: 2.83em; /* similar to emby-input with its 110% font-size */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderLabel {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
|
@ -158,6 +158,16 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
|
||||||
this.classList.add('show-focus');
|
this.classList.add('show-focus');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const topContainer = dom.parentWithClass(this, 'sliderContainer-settings');
|
||||||
|
|
||||||
|
if (topContainer && this.getAttribute('label')) {
|
||||||
|
const label = this.ownerDocument.createElement('label');
|
||||||
|
label.innerHTML = this.getAttribute('label');
|
||||||
|
label.classList.add('sliderLabel');
|
||||||
|
label.htmlFor = this.id;
|
||||||
|
topContainer.insertBefore(label, topContainer.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
var containerElement = this.parentNode;
|
var containerElement = this.parentNode;
|
||||||
containerElement.classList.add('mdl-slider-container');
|
containerElement.classList.add('mdl-slider-container');
|
||||||
|
|
||||||
|
|
|
@ -1154,9 +1154,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
var html = selector + '::cue {';
|
var html = selector + '::cue {';
|
||||||
|
|
||||||
html += appearance.text.map(function (s) {
|
html += appearance.text.map(function (s) {
|
||||||
|
if (s.value !== undefined && s.value !== '') {
|
||||||
return s.name + ':' + s.value + '!important;';
|
return s.name + ':' + s.value + '!important;';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
html += '}';
|
html += '}';
|
||||||
|
@ -1221,19 +1223,28 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
|
|
||||||
// download the track json
|
// download the track json
|
||||||
fetchSubtitles(track, item).then(function (data) {
|
fetchSubtitles(track, item).then(function (data) {
|
||||||
|
require(['userSettings'], (userSettings) => {
|
||||||
|
// show in ui
|
||||||
|
console.debug('downloaded ' + data.TrackEvents.length + ' track events');
|
||||||
|
|
||||||
// show in ui
|
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings();
|
||||||
console.debug('downloaded ' + data.TrackEvents.length + ' track events');
|
const cueLine = parseInt(subtitleAppearance.verticalPosition);
|
||||||
// add some cues to show the text
|
|
||||||
// in safari, the cues need to be added before setting the track mode to showing
|
|
||||||
data.TrackEvents.forEach(function (trackEvent) {
|
|
||||||
|
|
||||||
var trackCueObject = window.VTTCue || window.TextTrackCue;
|
// add some cues to show the text
|
||||||
var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
|
// in safari, the cues need to be added before setting the track mode to showing
|
||||||
|
data.TrackEvents.forEach(function (trackEvent) {
|
||||||
|
var trackCueObject = window.VTTCue || window.TextTrackCue;
|
||||||
|
var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
|
||||||
|
|
||||||
trackElement.addCue(cue);
|
if (cue.line === 'auto') {
|
||||||
|
cue.line = cueLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackElement.addCue(cue);
|
||||||
|
});
|
||||||
|
|
||||||
|
trackElement.mode = 'showing';
|
||||||
});
|
});
|
||||||
trackElement.mode = 'showing';
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,10 +1331,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
var html = '';
|
var html = '';
|
||||||
var cssClass = 'htmlvideoplayer';
|
var cssClass = 'htmlvideoplayer';
|
||||||
|
|
||||||
if (!browser.chromecast) {
|
|
||||||
cssClass += ' htmlvideoplayer-moveupsubtitles';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't autoplay in these browsers so we need to use the full controls, at least until playback starts
|
// Can't autoplay in these browsers so we need to use the full controls, at least until playback starts
|
||||||
if (!appHost.supports('htmlvideoautoplay')) {
|
if (!appHost.supports('htmlvideoautoplay')) {
|
||||||
html += '<video class="' + cssClass + '" preload="metadata" autoplay="autoplay" controls="controls" webkit-playsinline playsinline>';
|
html += '<video class="' + cssClass + '" preload="metadata" autoplay="autoplay" controls="controls" webkit-playsinline playsinline>';
|
||||||
|
|
|
@ -41,14 +41,14 @@ video::-webkit-media-controls {
|
||||||
line-height: 50%; /* Child element cannot set line height smaller than its parent has. This allow smaller values for children */
|
line-height: 50%; /* Child element cannot set line height smaller than its parent has. This allow smaller values for children */
|
||||||
}
|
}
|
||||||
|
|
||||||
.htmlvideoplayer-moveupsubtitles::-webkit-media-text-track-display {
|
.htmlvideoplayer::-webkit-media-text-track-display {
|
||||||
/* style the text itself */
|
max-width: 70%;
|
||||||
margin-top: -2em;
|
margin-left: 15%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.videoSubtitles {
|
.videoSubtitles {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 10%;
|
bottom: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -59,7 +59,6 @@ video::-webkit-media-controls {
|
||||||
.videoSubtitlesInner {
|
.videoSubtitlesInner {
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
padding: 0.25em;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ function saveServerPreferences(instance) {
|
||||||
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
|
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultSubtitleAppearanceSettings = {
|
||||||
|
verticalPosition: -3
|
||||||
|
};
|
||||||
|
|
||||||
export class UserSettings {
|
export class UserSettings {
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
@ -412,7 +416,7 @@ export class UserSettings {
|
||||||
*/
|
*/
|
||||||
getSubtitleAppearanceSettings(key) {
|
getSubtitleAppearanceSettings(key) {
|
||||||
key = key || 'localplayersubtitleappearance3';
|
key = key || 'localplayersubtitleappearance3';
|
||||||
return JSON.parse(this.get(key, false) || '{}');
|
return Object.assign(defaultSubtitleAppearanceSettings, JSON.parse(this.get(key, false) || '{}'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1565,5 +1565,8 @@
|
||||||
"ButtonCast": "Cast",
|
"ButtonCast": "Cast",
|
||||||
"ButtonPlayer": "Player",
|
"ButtonPlayer": "Player",
|
||||||
"StopPlayback": "Stop playback",
|
"StopPlayback": "Stop playback",
|
||||||
"ClearQueue": "Clear queue"
|
"ClearQueue": "Clear queue",
|
||||||
|
"LabelSubtitleVerticalPosition": "Vertical position:",
|
||||||
|
"SubtitleVerticalPositionHelp": "Line number where text appears. Positive numbers indicate top down. Negative numbers indicate bottom up.",
|
||||||
|
"Preview": "Preview"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1572,5 +1572,8 @@
|
||||||
"Writers": "Сценаристы",
|
"Writers": "Сценаристы",
|
||||||
"ViewAlbumArtist": "Посмотреть альбом исполнителя",
|
"ViewAlbumArtist": "Посмотреть альбом исполнителя",
|
||||||
"ClearQueue": "Очистить очередь",
|
"ClearQueue": "Очистить очередь",
|
||||||
"ButtonPlayer": "Проигрыватель"
|
"ButtonPlayer": "Проигрыватель",
|
||||||
|
"LabelSubtitleVerticalPosition": "Вертикальная позиция:",
|
||||||
|
"SubtitleVerticalPositionHelp": "Номер строки, где появляется текст. Положительные числа означают сверху вниз. Отрицательные числа означают снизу вверх.",
|
||||||
|
"Preview": "Предварительный просмотр"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue