1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge branch 'master' into migrate-to-ES6-58

This commit is contained in:
Cameron 2020-08-07 09:29:15 +01:00 committed by GitHub
commit b44ca55f0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 3305 additions and 3026 deletions

View file

@ -2,4 +2,3 @@ node_modules
dist
.idea
.vscode
src/libraries

View file

@ -17,7 +17,7 @@
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.12",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^4.2.0",
"css-loader": "^4.2.1",
"cssnano": "^4.1.10",
"del": "^5.1.0",
"eslint": "^7.6.0",
@ -155,6 +155,8 @@
"src/components/playlisteditor/playlisteditor.js",
"src/components/playmenu.js",
"src/components/prompt/prompt.js",
"src/components/recordingcreator/seriesrecordingeditor.js",
"src/components/recordingcreator/recordinghelper.js",
"src/components/refreshdialog/refreshdialog.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
@ -169,6 +171,9 @@
"src/components/syncPlay/playbackPermissionManager.js",
"src/components/syncPlay/syncPlayManager.js",
"src/components/syncPlay/timeSyncManager.js",
"src/components/viewManager/viewManager.js",
"src/components/tvproviders/schedulesdirect.js",
"src/components/tvproviders/xmltv.js",
"src/components/toast/toast.js",
"src/components/upnextdialog/upnextdialog.js",
"src/components/viewContainer.js",
@ -216,6 +221,11 @@
"src/controllers/edititemmetadata.js",
"src/controllers/favorites.js",
"src/controllers/hometab.js",
"src/controllers/movies/moviecollections.js",
"src/controllers/movies/moviegenres.js",
"src/controllers/movies/movies.js",
"src/controllers/movies/moviesrecommended.js",
"src/controllers/movies/movietrailers.js",
"src/controllers/playback/nowplaying.js",
"src/controllers/playback/videoosd.js",
"src/controllers/itemDetails/index.js",
@ -268,6 +278,8 @@
"src/elements/emby-tabs/emby-tabs.js",
"src/elements/emby-textarea/emby-textarea.js",
"src/elements/emby-toggle/emby-toggle.js",
"src/libraries/navdrawer/navdrawer.js",
"src/libraries/scroller.js",
"src/plugins/backdropScreensaver/plugin.js",
"src/plugins/bookPlayer/plugin.js",
"src/plugins/bookPlayer/tableOfContents.js",
@ -293,10 +305,13 @@
"src/scripts/mouseManager.js",
"src/scripts/multiDownload.js",
"src/scripts/playlists.js",
"src/scripts/scrollHelper.js",
"src/scripts/serverNotifications.js",
"src/scripts/routes.js",
"src/scripts/settings/appSettings.js",
"src/scripts/settings/userSettings.js",
"src/scripts/settings/webSettings.js",
"src/scripts/shell.js",
"src/scripts/taskbutton.js",
"src/scripts/themeLoader.js",
"src/scripts/touchHelper.js"

View file

@ -236,12 +236,6 @@
text-align: center;
}
.layout-desktop .searchTabButton,
.layout-mobile .searchTabButton,
.layout-tv .headerSearchButton {
display: none !important;
}
.mainDrawer-scrollContainer {
padding-bottom: 10vh;
}

View file

@ -1,6 +1,7 @@
define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
'use strict';
viewManager = viewManager.default || viewManager;
browser = browser.default || browser;
loading = loading.default || loading;

View file

@ -200,7 +200,7 @@ import 'flexStyles';
}
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(scrollHelper => {
import('scrollHelper').then((scrollHelper) => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -354,7 +354,7 @@ import 'scrollStyles';
}
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(scrollHelper => {
import('scrollHelper').then((scrollHelper) => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -153,6 +153,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost',
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -2,6 +2,7 @@ define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectio
'use strict';
layoutManager = layoutManager.default || layoutManager;
scrollHelper = scrollHelper.default || scrollHelper;
function saveCategories(context, options) {
var categories = [];

View file

@ -6,6 +6,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;
serverNotifications = serverNotifications.default || serverNotifications;
function showViewSettings(instance) {
require(['guide-settings-dialog'], function (guideSettingsDialog) {

View file

@ -2,6 +2,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
'use strict';
playbackManager = playbackManager.default || playbackManager;
serverNotifications = serverNotifications.default || serverNotifications;
function onOneDocumentClick() {
document.removeEventListener('click', onOneDocumentClick);

View file

@ -210,7 +210,7 @@ import 'emby-button';
}
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(scrollHelper => {
import('scrollHelper').then((scrollHelper) => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -1,6 +1,8 @@
define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields'], function (globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events) {
'use strict';
recordingHelper = recordingHelper.default || recordingHelper;
function onRecordingButtonClick(e) {
var item = this.item;

View file

@ -2,6 +2,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
'use strict';
layoutManager = layoutManager.default || layoutManager;
scrollHelper = scrollHelper.default || scrollHelper;
var currentDialog;
var closeAction;

View file

@ -1,6 +1,7 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'scrollStyles', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader) {
'use strict';
scrollHelper = scrollHelper.default || scrollHelper;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
@ -13,6 +14,8 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
function deleteTimer(apiClient, timerId) {
return new Promise(function (resolve, reject) {
require(['recordingHelper'], function (recordingHelper) {
recordingHelper = recordingHelper.default || recordingHelper;
recordingHelper.cancelTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject);
});
});

View file

@ -1,6 +1,8 @@
define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields', 'flexStyles'], function (globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events) {
'use strict';
serverNotifications = serverNotifications.default || serverNotifications;
recordingHelper = recordingHelper.default || recordingHelper;
loading = loading.default || loading;
function loadData(parent, program, apiClient) {

View file

@ -1,9 +1,10 @@
define(['globalize', 'loading', 'connectionManager'], function (globalize, loading, connectionManager) {
'use strict';
import globalize from 'globalize';
import loading from 'loading';
import connectionManager from 'connectionManager';
loading = loading.default || loading;
/*eslint prefer-const: "error"*/
function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) {
function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) {
loading.show();
return apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function (item) {
@ -24,11 +25,11 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
return cancelTimer(apiClient.serverId(), timerId, true);
}
});
}
}
function cancelTimerWithConfirmation(timerId, serverId) {
function cancelTimerWithConfirmation(timerId, serverId) {
return new Promise(function (resolve, reject) {
require(['confirm'], function (confirm) {
import('confirm').then(({ default: confirm }) => {
confirm.default({
text: globalize.translate('MessageConfirmRecordingCancellation'),
@ -39,16 +40,16 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
}).then(function () {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
cancelTimer(apiClient, timerId, true).then(resolve, reject);
}, reject);
});
});
}
}
function cancelSeriesTimerWithConfirmation(timerId, serverId) {
function cancelSeriesTimerWithConfirmation(timerId, serverId) {
return new Promise(function (resolve, reject) {
require(['confirm'], function (confirm) {
import('confirm').then(({ default: confirm }) => {
confirm.default({
text: globalize.translate('MessageConfirmRecordingCancellation'),
@ -59,9 +60,9 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
}).then(function () {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
apiClient.cancelLiveTvSeriesTimer(timerId).then(function () {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('SeriesCancelled'));
});
@ -71,9 +72,9 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
}, reject);
});
});
}
}
function cancelTimer(apiClient, timerId, hideLoading) {
function cancelTimer(apiClient, timerId, hideLoading) {
loading.show();
return apiClient.cancelLiveTvTimer(timerId).then(function () {
if (hideLoading !== false) {
@ -81,12 +82,12 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
sendToast(globalize.translate('RecordingCancelled'));
}
});
}
}
function createRecording(apiClient, programId, isSeries) {
function createRecording(apiClient, programId, isSeries) {
loading.show();
return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (item) {
var promise = isSeries ?
const promise = isSeries ?
apiClient.createLiveTvSeriesTimer(item) :
apiClient.createLiveTvTimer(item);
@ -95,18 +96,18 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
sendToast(globalize.translate('RecordingScheduled'));
});
});
}
}
function sendToast(msg) {
require(['toast'], function (toast) {
function sendToast(msg) {
import('toast').then(({ default: toast }) => {
toast(msg);
});
}
}
function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) {
function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) {
return new Promise(function (resolve, reject) {
require(['dialog'], function (dialog) {
var items = [];
import('dialog').then(({ default: dialog }) => {
const items = [];
items.push({
name: globalize.translate('HeaderKeepRecording'),
@ -140,7 +141,7 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
buttons: items
}).then(function (result) {
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
if (result === 'canceltimer') {
loading.show();
@ -150,7 +151,7 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
loading.show();
apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function () {
require(['toast'], function (toast) {
import('toast').then(({ default: toast }) => {
toast(globalize.translate('SeriesCancelled'));
});
@ -163,11 +164,11 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
}, reject);
});
});
}
}
function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) {
var apiClient = connectionManager.getApiClient(serverId);
var hasTimer = timerId && timerStatus !== 'Cancelled';
function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) {
const apiClient = connectionManager.getApiClient(serverId);
const hasTimer = timerId && timerStatus !== 'Cancelled';
if (seriesTimerId && hasTimer) {
// cancel
return showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId);
@ -181,14 +182,14 @@ define(['globalize', 'loading', 'connectionManager'], function (globalize, loadi
} else {
return Promise.reject();
}
}
}
return {
export default {
cancelTimer: cancelTimer,
createRecording: createRecording,
changeRecordingToSeries: changeRecordingToSeries,
toggleRecording: toggleRecording,
cancelTimerWithConfirmation: cancelTimerWithConfirmation,
cancelSeriesTimerWithConfirmation: cancelSeriesTimerWithConfirmation
};
});
};

View file

@ -1,24 +1,38 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'datetime', 'scrollStyles', 'emby-button', 'emby-checkbox', 'emby-input', 'emby-select', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader, datetime) {
'use strict';
import dialogHelper from 'dialogHelper';
import globalize from 'globalize';
import layoutManager from 'layoutManager';
import connectionManager from 'connectionManager';
import loading from 'loading';
import scrollHelper from 'scrollHelper';
import datetime from 'datetime';
import 'scrollStyles';
import 'emby-button';
import 'emby-checkbox';
import 'emby-input';
import 'emby-select';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'css!./recordingcreator';
import 'material-icons';
import 'flexStyles';
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
/*eslint prefer-const: "error"*/
var currentDialog;
var recordingUpdated = false;
var recordingDeleted = false;
var currentItemId;
var currentServerId;
let currentDialog;
let recordingUpdated = false;
let recordingDeleted = false;
let currentItemId;
let currentServerId;
function deleteTimer(apiClient, timerId) {
function deleteTimer(apiClient, timerId) {
return new Promise(function (resolve, reject) {
require(['recordingHelper'], function (recordingHelper) {
import('recordingHelper').then(({ default: recordingHelper }) => {
recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject);
});
});
}
}
function renderTimer(context, item, apiClient) {
function renderTimer(context, item) {
context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60;
context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60;
@ -38,19 +52,19 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
context.querySelector('.optionAroundTime').innerHTML = globalize.translate('AroundTime', datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate)));
loading.hide();
}
}
function closeDialog(isDeleted) {
function closeDialog(isDeleted) {
recordingUpdated = true;
recordingDeleted = isDeleted;
dialogHelper.close(currentDialog);
}
}
function onSubmit(e) {
var form = this;
function onSubmit(e) {
const form = this;
var apiClient = connectionManager.getApiClient(currentServerId);
const apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getLiveTvSeriesTimer(currentItemId).then(function (item) {
item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60;
@ -68,9 +82,9 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
// Disable default form submission
return false;
}
}
function init(context) {
function init(context) {
fillKeepUpTo(context);
context.querySelector('.btnCancel').addEventListener('click', function () {
@ -78,39 +92,39 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
});
context.querySelector('.btnCancelRecording').addEventListener('click', function () {
var apiClient = connectionManager.getApiClient(currentServerId);
const apiClient = connectionManager.getApiClient(currentServerId);
deleteTimer(apiClient, currentItemId).then(function () {
closeDialog(true);
});
});
context.querySelector('form').addEventListener('submit', onSubmit);
}
}
function reload(context, id) {
var apiClient = connectionManager.getApiClient(currentServerId);
function reload(context, id) {
const apiClient = connectionManager.getApiClient(currentServerId);
loading.show();
if (typeof id === 'string') {
currentItemId = id;
apiClient.getLiveTvSeriesTimer(id).then(function (result) {
renderTimer(context, result, apiClient);
renderTimer(context, result);
loading.hide();
});
} else if (id) {
currentItemId = id.Id;
renderTimer(context, id, apiClient);
renderTimer(context, id);
loading.hide();
}
}
}
function fillKeepUpTo(context) {
var html = '';
function fillKeepUpTo(context) {
let html = '';
for (var i = 0; i <= 50; i++) {
var text;
for (let i = 0; i <= 50; i++) {
let text;
if (i === 0) {
text = globalize.translate('AsManyAsPossible');
@ -124,21 +138,21 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
}
context.querySelector('.selectKeepUpTo').innerHTML = html;
}
}
function onFieldChange(e) {
function onFieldChange() {
this.querySelector('.btnSubmit').click();
}
}
function embed(itemId, serverId, options) {
function embed(itemId, serverId, options) {
recordingUpdated = false;
recordingDeleted = false;
currentServerId = serverId;
loading.show();
options = options || {};
require(['text!./seriesrecordingeditor.template.html'], function (template) {
var dialogOptions = {
import('text!./seriesrecordingeditor.template.html').then(({ default: template }) => {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -149,7 +163,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
dialogOptions.size = 'small';
}
var dlg = options.context;
const dlg = options.context;
dlg.classList.add('hide');
dlg.innerHTML = globalize.translateHtml(template, 'core');
@ -169,9 +183,9 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
reload(dlg, itemId);
});
}
}
function showEditor(itemId, serverId, options) {
function showEditor(itemId, serverId, options) {
return new Promise(function (resolve, reject) {
recordingUpdated = false;
recordingDeleted = false;
@ -179,8 +193,8 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
loading.show();
options = options || {};
require(['text!./seriesrecordingeditor.template.html'], function (template) {
var dialogOptions = {
import('text!./seriesrecordingeditor.template.html').then(({ default: template }) => {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -191,7 +205,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('recordingDialog');
@ -200,7 +214,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
dlg.style['min-width'] = '20%';
}
var html = '';
let html = '';
html += globalize.translateHtml(template, 'core');
@ -240,10 +254,9 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
dialogHelper.open(dlg);
});
});
}
}
return {
export default {
show: showEditor,
embed: embed
};
});
};

View file

@ -18,6 +18,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -338,6 +338,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -3,31 +3,9 @@
* @module components/subtitleSettings/subtitleAppearanceHelper
*/
function getTextStyles(settings, isCue) {
function getTextStyles(settings, preview) {
let list = [];
if (isCue) {
switch (settings.textSize || '') {
case 'smaller':
list.push({ name: 'font-size', value: '.5em' });
break;
case 'small':
list.push({ name: 'font-size', value: '.7em' });
break;
case 'large':
list.push({ name: 'font-size', value: '1.3em' });
break;
case 'larger':
list.push({ name: 'font-size', value: '1.72em' });
break;
case 'extralarge':
list.push({ name: 'font-size', value: '2em' });
break;
default:
case 'medium':
break;
}
} else {
switch (settings.textSize || '') {
case 'smaller':
list.push({ name: 'font-size', value: '.8em' });
@ -49,7 +27,6 @@ function getTextStyles(settings, isCue) {
list.push({ name: 'font-size', value: '1.36em' });
break;
}
}
switch (settings.dropShadow || '') {
case 'raised':
@ -111,13 +88,43 @@ function getTextStyles(settings, isCue) {
break;
}
if (!preview) {
const pos = parseInt(settings.verticalPosition, 10);
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;
}
export function getStyles(settings, isCue) {
function getWindowStyles(settings, preview) {
const list = [];
if (!preview) {
const pos = parseInt(settings.verticalPosition, 10);
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;
}
export function getStyles(settings, preview) {
return {
text: getTextStyles(settings, isCue),
window: []
text: getTextStyles(settings, preview),
window: getWindowStyles(settings, preview)
};
}
@ -130,7 +137,7 @@ function applyStyleList(styles, elem) {
}
export function applyStyles(elements, appearanceSettings) {
let styles = getStyles(appearanceSettings);
let styles = getStyles(appearanceSettings, !!elements.preview);
if (elements.text) {
applyStyleList(styles.text, elements.text);

View file

@ -0,0 +1,26 @@
.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%;
}

View file

@ -2,6 +2,7 @@ import globalize from 'globalize';
import appHost from 'apphost';
import appSettings from 'appSettings';
import focusManager from 'focusManager';
import layoutManager from 'layoutManager';
import loading from 'loading';
import connectionManager from 'connectionManager';
import subtitleAppearanceHelper from 'subtitleAppearanceHelper';
@ -10,9 +11,11 @@ import dom from 'dom';
import events from 'events';
import 'listViewStyle';
import 'emby-select';
import 'emby-slider';
import 'emby-input';
import 'emby-checkbox';
import 'flexStyles';
import 'css!./subtitlesettings';
/**
* Subtitle settings.
@ -27,6 +30,7 @@ function getSubtitleAppearanceObject(context) {
appearanceSettings.font = context.querySelector('#selectFont').value;
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
appearanceSettings.verticalPosition = context.querySelector('#sliderVerticalPosition').value;
return appearanceSettings;
}
@ -51,6 +55,7 @@ function loadForm(context, user, userSettings, appearanceSettings, apiClient) {
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
context.querySelector('#selectFont').value = appearanceSettings.font || '';
context.querySelector('#sliderVerticalPosition').value = appearanceSettings.verticalPosition;
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
@ -112,10 +117,45 @@ function onAppearanceFieldChange(e) {
let elements = {
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({
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) {
@ -138,6 +178,36 @@ function embed(options, self) {
if (appHost.supports('subtitleappearancesettings')) {
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();

View file

@ -38,6 +38,16 @@
${HeaderSubtitleAppearance}
</h2>
<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 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;">
@ -89,6 +99,20 @@
<option value="">${DropShadow}</option>
</select>
</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>
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">

View file

@ -5,6 +5,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'connectionManager', 'globalize'
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;
var enableFocusTransform = !browser.slow && !browser.edge;

View file

@ -1,13 +1,19 @@
define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emby-input', 'emby-select', 'emby-button', 'flexStyles'], function ($, loading, globalize) {
'use strict';
import $ from 'jQuery';
import loading from 'loading';
import globalize from 'globalize';
import 'emby-checkbox';
import 'emby-input';
import 'listViewStyle';
import 'paper-icon-button-light';
import 'emby-select';
import 'emby-button';
import 'flexStyles';
loading = loading.default || loading;
return function (page, providerId, options) {
export default function (page, providerId, options) {
function reload() {
loading.show();
ApiClient.getNamedConfiguration('livetv').then(function (config) {
var info = config.ListingProviders.filter(function (i) {
const info = config.ListingProviders.filter(function (i) {
return i.Id === providerId;
})[0] || {};
listingsId = info.ListingsId;
@ -37,12 +43,12 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
function setCountry(info) {
ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/SchedulesDirect/Countries')).then(function (result) {
var i;
var length;
var countryList = [];
let i;
let length;
const countryList = [];
for (var region in result) {
var countries = result[region];
for (const region in result) {
const countries = result[region];
if (countries.length && region !== 'ZZZ') {
for (i = 0, length = countries.length; i < length; i++) {
@ -82,20 +88,20 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
return Promise.resolve('');
}
var buffer = new TextEncoder('utf-8').encode(str);
const buffer = new TextEncoder('utf-8').encode(str);
return crypto.subtle.digest('SHA-256', buffer).then(function (hash) {
return hex(hash);
});
}
function hex(buffer) {
var hexCodes = [];
var view = new DataView(buffer);
const hexCodes = [];
const view = new DataView(buffer);
for (var i = 0; i < view.byteLength; i += 4) {
var value = view.getUint32(i);
var stringValue = value.toString(16);
var paddedValue = ('00000000' + stringValue).slice(-'00000000'.length);
for (let i = 0; i < view.byteLength; i += 4) {
const value = view.getUint32(i);
const stringValue = value.toString(16);
const paddedValue = ('00000000' + stringValue).slice(-'00000000'.length);
hexCodes.push(paddedValue);
}
@ -105,14 +111,14 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
function submitLoginForm() {
loading.show();
sha256(page.querySelector('.txtPass').value).then(function (passwordHash) {
var info = {
const info = {
Type: 'SchedulesDirect',
Username: page.querySelector('.txtUser').value,
EnableAllTuners: true,
Password: passwordHash,
Pw: page.querySelector('.txtPass').value
};
var id = providerId;
const id = providerId;
if (id) {
info.Id = id;
@ -139,7 +145,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
}
function submitListingsForm() {
var selectedListingsId = $('#selectListing', page).val();
const selectedListingsId = $('#selectListing', page).val();
if (!selectedListingsId) {
return void Dashboard.alert({
@ -148,9 +154,9 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
}
loading.show();
var id = providerId;
const id = providerId;
ApiClient.getNamedConfiguration('livetv').then(function (config) {
var info = config.ListingProviders.filter(function (i) {
const info = config.ListingProviders.filter(function (i) {
return i.Id === id;
})[0];
info.ZipCode = page.querySelector('.txtZipCode').value;
@ -233,14 +239,14 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
}
function refreshTunerDevices(page, providerInfo, devices) {
var html = '';
let html = '';
for (var i = 0, length = devices.length; i < length; i++) {
var device = devices[i];
for (let i = 0, length = devices.length; i < length; i++) {
const device = devices[i];
html += '<div class="listItem">';
var enabledTuners = providerInfo.EnabledTuners || [];
var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1;
var checkedAttribute = isChecked ? ' checked' : '';
const enabledTuners = providerInfo.EnabledTuners || [];
const isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1;
const checkedAttribute = isChecked ? ' checked' : '';
html += '<label class="checkboxContainer listItemCheckboxContainer"><input type="checkbox" is="emby-checkbox" data-id="' + device.Id + '" class="chkTuner" ' + checkedAttribute + '/><span></span></label>';
html += '<div class="listItemBody two-line">';
html += '<div class="listItemBodyText">';
@ -256,8 +262,8 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
page.querySelector('.tunerList').innerHTML = html;
}
var listingsId;
var self = this;
let listingsId;
const self = this;
self.submit = function () {
page.querySelector('.btnSubmitListingsContainer').click();
@ -268,10 +274,10 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
// Only hide the buttons if explicitly set to false; default to showing if undefined or null
// FIXME: rename this option to clarify logic
var hideCancelButton = options.showCancelButton === false;
const hideCancelButton = options.showCancelButton === false;
page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton);
var hideSubmitButton = options.showSubmitButton === false;
const hideSubmitButton = options.showSubmitButton === false;
page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton);
$('.formLogin', page).on('submit', function () {
@ -295,5 +301,4 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emb
$('.createAccountHelp', page).html(globalize.translate('MessageCreateAccountAt', '<a is="emby-linkbutton" class="button-link" href="http://www.schedulesdirect.org" target="_blank">http://www.schedulesdirect.org</a>'));
reload();
};
};
});
}

View file

@ -1,12 +1,15 @@
define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listViewStyle', 'paper-icon-button-light'], function ($, loading, globalize) {
'use strict';
import $ from 'jQuery';
import loading from 'loading';
import globalize from 'globalize';
import 'emby-checkbox';
import 'emby-input';
import 'listViewStyle';
import 'paper-icon-button-light';
loading = loading.default || loading;
return function (page, providerId, options) {
export default function (page, providerId, options) {
function getListingProvider(config, id) {
if (config && id) {
var result = config.ListingProviders.filter(function (provider) {
const result = config.ListingProviders.filter(function (provider) {
return provider.Id === id;
})[0];
@ -46,7 +49,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
}
function getCategories(txtInput) {
var value = txtInput.value;
const value = txtInput.value;
if (value) {
return value.split('|');
@ -57,9 +60,9 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
function submitListingsForm() {
loading.show();
var id = providerId;
const id = providerId;
ApiClient.getNamedConfiguration('livetv').then(function (config) {
var info = config.ListingProviders.filter(function (provider) {
const info = config.ListingProviders.filter(function (provider) {
return provider.Id === id;
})[0] || {};
info.Type = 'xmltv';
@ -114,14 +117,14 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
}
function refreshTunerDevices(page, providerInfo, devices) {
var html = '';
let html = '';
for (var i = 0, length = devices.length; i < length; i++) {
var device = devices[i];
for (let i = 0, length = devices.length; i < length; i++) {
const device = devices[i];
html += '<div class="listItem">';
var enabledTuners = providerInfo.EnabledTuners || [];
var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1;
var checkedAttribute = isChecked ? ' checked' : '';
const enabledTuners = providerInfo.EnabledTuners || [];
const isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1;
const checkedAttribute = isChecked ? ' checked' : '';
html += '<label class="listItemCheckboxContainer"><input type="checkbox" is="emby-checkbox" class="chkTuner" data-id="' + device.Id + '" ' + checkedAttribute + '><span></span></label>';
html += '<div class="listItemBody two-line">';
html += '<div class="listItemBodyText">';
@ -138,15 +141,15 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
}
function onSelectPathClick(e) {
var page = $(e.target).parents('.xmltvForm')[0];
const page = $(e.target).parents('.xmltvForm')[0];
require(['directorybrowser'], function (directoryBrowser) {
var picker = new directoryBrowser.default();
import('directorybrowser').then(({default: directoryBrowser}) => {
const picker = new directoryBrowser();
picker.show({
includeFiles: true,
callback: function (path) {
if (path) {
var txtPath = page.querySelector('.txtPath');
const txtPath = page.querySelector('.txtPath');
txtPath.value = path;
txtPath.focus();
}
@ -156,7 +159,7 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
});
}
var self = this;
const self = this;
self.submit = function () {
page.querySelector('.btnSubmitListings').click();
@ -167,10 +170,10 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
// Only hide the buttons if explicitly set to false; default to showing if undefined or null
// FIXME: rename this option to clarify logic
var hideCancelButton = options.showCancelButton === false;
const hideCancelButton = options.showCancelButton === false;
page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton);
var hideSubmitButton = options.showSubmitButton === false;
const hideSubmitButton = options.showSubmitButton === false;
page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton);
$('form', page).on('submit', function () {
@ -187,5 +190,4 @@ define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listVi
});
reload();
};
};
});
}

View file

@ -1,23 +1,22 @@
define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], function (viewContainer, focusManager, queryString, layoutManager) {
'use strict';
import viewContainer from 'viewContainer';
import focusManager from 'focusManager';
import queryString from 'queryString';
import layoutManager from 'layoutManager';
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
let currentView;
let dispatchPageEvents;
var currentView;
var dispatchPageEvents;
viewContainer.setOnBeforeChange(function (newView, isRestored, options) {
var lastView = currentView;
viewContainer.setOnBeforeChange(function (newView, isRestored, options) {
const lastView = currentView;
if (lastView) {
var beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true);
const beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true);
if (!beforeHideResult) {
// todo: cancel
}
}
var eventDetail = getViewEventDetail(newView, options, isRestored);
const eventDetail = getViewEventDetail(newView, options, isRestored);
if (!newView.initComplete) {
newView.initComplete = true;
@ -34,17 +33,17 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
}
dispatchViewEvent(newView, eventDetail, 'viewbeforeshow');
});
});
function onViewChange(view, options, isRestore) {
var lastView = currentView;
function onViewChange(view, options, isRestore) {
const lastView = currentView;
if (lastView) {
dispatchViewEvent(lastView, null, 'viewhide');
}
currentView = view;
var eventDetail = getViewEventDetail(view, options, isRestore);
const eventDetail = getViewEventDetail(view, options, isRestore);
if (!isRestore) {
if (options.autoFocus !== false) {
@ -63,19 +62,19 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
if (dispatchPageEvents) {
view.dispatchEvent(new CustomEvent('pageshow', eventDetail));
}
}
}
function getProperties(view) {
var props = view.getAttribute('data-properties');
function getProperties(view) {
const props = view.getAttribute('data-properties');
if (props) {
return props.split(',');
}
return [];
}
}
function dispatchViewEvent(view, eventInfo, eventName, isCancellable) {
function dispatchViewEvent(view, eventInfo, eventName, isCancellable) {
if (!eventInfo) {
eventInfo = {
detail: {
@ -89,7 +88,7 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
eventInfo.cancelable = isCancellable || false;
var eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo));
const eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo));
if (dispatchPageEvents) {
eventInfo.cancelable = false;
@ -97,12 +96,12 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
}
return eventResult;
}
}
function getViewEventDetail(view, options, isRestore) {
var url = options.url;
var index = url.indexOf('?');
var params = index === -1 ? {} : queryString.parse(url.substring(index + 1));
function getViewEventDetail(view, options, isRestore) {
const url = options.url;
const index = url.indexOf('?');
const params = index === -1 ? {} : queryString.parse(url.substring(index + 1));
return {
detail: {
@ -118,20 +117,18 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
bubbles: true,
cancelable: false
};
}
}
function resetCachedViews() {
function resetCachedViews() {
// Reset all cached views whenever the skin changes
viewContainer.reset();
}
}
document.addEventListener('skinunload', resetCachedViews);
document.addEventListener('skinunload', resetCachedViews);
function ViewManager() {
}
ViewManager.prototype.loadView = function (options) {
var lastView = currentView;
class ViewManager {
loadView(options) {
const lastView = currentView;
// Record the element that has focus
if (lastView) {
@ -145,9 +142,9 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
viewContainer.loadView(options).then(function (view) {
onViewChange(view, options);
});
};
}
ViewManager.prototype.tryRestoreView = function (options, onViewChanging) {
tryRestoreView(options, onViewChanging) {
if (options.cancel) {
return Promise.reject({ cancelled: true });
}
@ -161,15 +158,15 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
onViewChanging();
onViewChange(view, options, true);
});
};
}
ViewManager.prototype.currentView = function () {
currentView() {
return currentView;
};
}
ViewManager.prototype.dispatchPageEvents = function (value) {
dispatchPageEvents(value) {
dispatchPageEvents = value;
};
}
}
return new ViewManager();
});
export default new ViewManager();

View file

@ -31,6 +31,7 @@ define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'conne
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});

View file

@ -1,4 +1,4 @@
define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, AlphaPicker) {
define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, playbackManager, AlphaPicker) {
'use strict';
playbackManager = playbackManager.default || playbackManager;

View file

@ -170,9 +170,6 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
name: globalize.translate('HeaderSchedule')
}, {
name: globalize.translate('TabSeries')
}, {
name: globalize.translate('ButtonSearch'),
cssClass: 'searchTabButton'
}];
}
@ -256,9 +253,6 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
case 5:
depends.push('controllers/livetv/livetvseriestimers');
break;
case 6:
depends.push('scripts/searchtab');
}
require(depends, function (controllerFactory) {

View file

@ -1,13 +1,18 @@
define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings, globalize) {
'use strict';
import loading from 'loading';
import libraryBrowser from 'libraryBrowser';
import imageLoader from 'imageLoader';
import listView from 'listView';
import cardBuilder from 'cardBuilder';
import * as userSettings from 'userSettings';
import globalize from 'globalize';
import 'emby-itemscontainer';
loading = loading.default || loading;
libraryBrowser = libraryBrowser.default || libraryBrowser;
/* eslint-disable indent */
return function (view, params, tabContent) {
export default function (view, params, tabContent) {
function getPageData(context) {
var key = getSavedQueryKey(context);
var pageData = data[key];
const key = getSavedQueryKey(context);
let pageData = data[key];
if (!pageData) {
pageData = data[key] = {
@ -47,9 +52,9 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
return context.savedQueryKey;
}
function onViewStyleChange() {
var viewStyle = self.getCurrentViewStyle();
var itemsContainer = tabContent.querySelector('.itemsContainer');
const onViewStyleChange = () => {
const viewStyle = this.getCurrentViewStyle();
const itemsContainer = tabContent.querySelector('.itemsContainer');
if (viewStyle == 'List') {
itemsContainer.classList.add('vertical-list');
@ -60,13 +65,13 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
}
itemsContainer.innerHTML = '';
}
};
function reloadItems(page) {
const reloadItems = (page) => {
loading.show();
isLoading = true;
var query = getQuery(page);
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) {
const query = getQuery(page);
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => {
function onNextPageClick() {
if (isLoading) {
return;
@ -90,8 +95,8 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
}
window.scrollTo(0, 0);
var html;
var pagingHtml = libraryBrowser.getQueryPagingHtml({
let html;
const pagingHtml = libraryBrowser.getQueryPagingHtml({
startIndex: query.StartIndex,
limit: query.Limit,
totalRecordCount: result.TotalRecordCount,
@ -101,7 +106,7 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
sortButton: false,
filterButton: false
});
var viewStyle = self.getCurrentViewStyle();
const viewStyle = this.getCurrentViewStyle();
if (viewStyle == 'Thumb') {
html = cardBuilder.getCardsHtml({
items: result.Items,
@ -155,22 +160,21 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
showTitle: true
});
}
var i;
var length;
var elems = tabContent.querySelectorAll('.paging');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
let elems = tabContent.querySelectorAll('.paging');
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
if (!result.Items.length) {
@ -182,28 +186,27 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
html += '</div>';
}
var itemsContainer = tabContent.querySelector('.itemsContainer');
const itemsContainer = tabContent.querySelector('.itemsContainer');
itemsContainer.innerHTML = html;
imageLoader.lazyChildren(itemsContainer);
libraryBrowser.saveQueryValues(getSavedQueryKey(page), query);
loading.hide();
isLoading = false;
require(['autoFocuser'], function (autoFocuser) {
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(page);
});
});
}
};
var self = this;
var data = {};
var isLoading = false;
const data = {};
let isLoading = false;
self.getCurrentViewStyle = function () {
this.getCurrentViewStyle = function () {
return getPageData(tabContent).view;
};
function initPage(tabContent) {
const initPage = (tabContent) => {
tabContent.querySelector('.btnSort').addEventListener('click', function (e) {
libraryBrowser.showSortMenu({
items: [{
@ -230,36 +233,37 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
button: e.target
});
});
var btnSelectView = tabContent.querySelector('.btnSelectView');
const btnSelectView = tabContent.querySelector('.btnSelectView');
btnSelectView.addEventListener('click', function (e) {
libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
});
btnSelectView.addEventListener('layoutchange', function (e) {
var viewStyle = e.detail.viewStyle;
const viewStyle = e.detail.viewStyle;
getPageData(tabContent).view = viewStyle;
libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle);
getQuery(tabContent).StartIndex = 0;
onViewStyleChange();
reloadItems(tabContent);
});
tabContent.querySelector('.btnNewCollection').addEventListener('click', function () {
require(['collectionEditor'], function (collectionEditor) {
var serverId = ApiClient.serverInfo().Id;
tabContent.querySelector('.btnNewCollection').addEventListener('click', () => {
import('collectionEditor').then(({default: collectionEditor}) => {
const serverId = ApiClient.serverInfo().Id;
new collectionEditor.showEditor({
items: [],
serverId: serverId
});
});
});
}
};
initPage(tabContent);
onViewStyleChange();
self.renderTab = function () {
this.renderTab = function () {
reloadItems(tabContent);
};
self.destroy = function () {};
};
});
this.destroy = function () {};
}
/* eslint-enable indent */

View file

@ -1,14 +1,18 @@
define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost', 'globalize', 'appRouter', 'dom', 'emby-button'], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) {
'use strict';
import layoutManager from 'layoutManager';
import loading from 'loading';
import libraryBrowser from 'libraryBrowser';
import cardBuilder from 'cardBuilder';
import lazyLoader from 'lazyLoader';
import globalize from 'globalize';
import appRouter from 'appRouter';
import 'emby-button';
loading = loading.default || loading;
libraryBrowser = libraryBrowser.default || libraryBrowser;
layoutManager = layoutManager.default || layoutManager;
/* eslint-disable indent */
return function (view, params, tabContent) {
export default function (view, params, tabContent) {
function getPageData() {
var key = getSavedQueryKey();
var pageData = data[key];
const key = getSavedQueryKey();
let pageData = data[key];
if (!pageData) {
pageData = data[key] = {
@ -38,7 +42,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
function getPromise() {
loading.show();
var query = getQuery();
const query = getQuery();
return ApiClient.getGenres(ApiClient.getCurrentUserId(), query);
}
@ -54,18 +58,18 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
return enableScrollX() ? 'overflowPortrait' : 'portrait';
}
function fillItemsContainer(entry) {
var elem = entry.target;
var id = elem.getAttribute('data-id');
var viewStyle = self.getCurrentViewStyle();
var limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9;
const fillItemsContainer = (entry) => {
const elem = entry.target;
const id = elem.getAttribute('data-id');
const viewStyle = this.getCurrentViewStyle();
let limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9;
if (enableScrollX()) {
limit = 10;
}
var enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary';
var query = {
const enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary';
const query = {
SortBy: 'SortName',
SortOrder: 'Ascending',
IncludeItemTypes: 'Movie',
@ -127,17 +131,17 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
tabContent.querySelector('.btnMoreFromGenre' + id + ' .material-icons').classList.remove('hide');
}
});
}
};
function reloadItems(context, promise) {
var query = getQuery();
const query = getQuery();
promise.then(function (result) {
var elem = context.querySelector('#items');
var html = '';
var items = result.Items;
const elem = context.querySelector('#items');
let html = '';
const items = result.Items;
for (var i = 0, length = items.length; i < length; i++) {
var item = items[i];
for (let i = 0, length = items.length; i < length; i++) {
const item = items[i];
html += '<div class="verticalSection">';
html += '<div class="sectionTitleContainer sectionTitleContainer-cards padded-left">';
@ -152,7 +156,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
html += '</a>';
html += '</div>';
if (enableScrollX()) {
var scrollXClass = 'scrollX hiddenScrollX';
let scrollXClass = 'scrollX hiddenScrollX';
if (layoutManager.tv) {
scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale';
@ -183,37 +187,37 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
});
}
function fullyReload() {
self.preRender();
self.renderTab();
}
const fullyReload = () => {
this.preRender();
this.renderTab();
};
var self = this;
var data = {};
const data = {};
self.getViewStyles = function () {
this.getViewStyles = function () {
return 'Poster,PosterCard,Thumb,ThumbCard'.split(',');
};
self.getCurrentViewStyle = function () {
this.getCurrentViewStyle = function () {
return getPageData().view;
};
self.setCurrentViewStyle = function (viewStyle) {
this.setCurrentViewStyle = function (viewStyle) {
getPageData().view = viewStyle;
libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle);
fullyReload();
};
self.enableViewSelection = true;
var promise;
this.enableViewSelection = true;
let promise;
self.preRender = function () {
this.preRender = function () {
promise = getPromise();
};
self.renderTab = function () {
this.renderTab = function () {
reloadItems(tabContent, promise);
};
};
});
}
/* eslint-enable indent */

View file

@ -1,13 +1,18 @@
define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, AlphaPicker, listView, cardBuilder, globalize) {
'use strict';
import loading from 'loading';
import * as userSettings from 'userSettings';
import events from 'events';
import libraryBrowser from 'libraryBrowser';
import AlphaPicker from 'alphaPicker';
import listView from 'listView';
import cardBuilder from 'cardBuilder';
import globalize from 'globalize';
import 'emby-itemscontainer';
loading = loading.default || loading;
libraryBrowser = libraryBrowser.default || libraryBrowser;
layoutManager = layoutManager.default || layoutManager;
/* eslint-disable indent */
return function (view, params, tabContent, options) {
function onViewStyleChange() {
if (self.getCurrentViewStyle() == 'List') {
export default function (view, params, tabContent, options) {
const onViewStyleChange = () => {
if (this.getCurrentViewStyle() == 'List') {
itemsContainer.classList.add('vertical-list');
itemsContainer.classList.remove('vertical-wrap');
} else {
@ -16,13 +21,13 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
}
itemsContainer.innerHTML = '';
}
};
function updateFilterControls() {
if (self.alphaPicker) {
self.alphaPicker.value(query.NameStartsWithOrGreater);
}
const updateFilterControls = () => {
if (this.alphaPicker) {
this.alphaPicker.value(query.NameStartsWithOrGreater);
}
};
function fetchData() {
isLoading = true;
@ -55,7 +60,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
window.scrollTo(0, 0);
updateFilterControls();
var pagingHtml = libraryBrowser.getQueryPagingHtml({
const pagingHtml = libraryBrowser.getQueryPagingHtml({
startIndex: query.StartIndex,
limit: query.Limit,
totalRecordCount: result.TotalRecordCount,
@ -65,35 +70,30 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
sortButton: false,
filterButton: false
});
var i;
var length;
var elems = tabContent.querySelectorAll('.paging');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
for (const elem of tabContent.querySelectorAll('.paging')) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
for (const elem of tabContent.querySelectorAll('.btnNextPage')) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
for (const elem of tabContent.querySelectorAll('.btnPreviousPage')) {
elem.addEventListener('click', onPreviousPageClick);
}
isLoading = false;
loading.hide();
require(['autoFocuser'], function (autoFocuser) {
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(tabContent);
});
}
function getItemsHtml(items) {
var html;
var viewStyle = self.getCurrentViewStyle();
const getItemsHtml = (items) => {
let html;
const viewStyle = this.getCurrentViewStyle();
if (viewStyle == 'Thumb') {
html = cardBuilder.getCardsHtml({
@ -157,22 +157,22 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
}
return html;
}
};
function initPage(tabContent) {
const initPage = (tabContent) => {
itemsContainer.fetchData = fetchData;
itemsContainer.getItemsHtml = getItemsHtml;
itemsContainer.afterRefresh = afterRefresh;
var alphaPickerElement = tabContent.querySelector('.alphaPicker');
let alphaPickerElement = tabContent.querySelector('.alphaPicker');
if (alphaPickerElement) {
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
var newValue = e.detail.value;
let newValue = e.detail.value;
query.NameStartsWithOrGreater = newValue;
query.StartIndex = 0;
itemsContainer.refreshItems();
});
self.alphaPicker = new AlphaPicker.default({
this.alphaPicker = new AlphaPicker({
element: alphaPickerElement,
valueChangeEvent: 'click'
});
@ -182,14 +182,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
itemsContainer.classList.add('padded-right-withalphapicker');
}
var btnFilter = tabContent.querySelector('.btnFilter');
const btnFilter = tabContent.querySelector('.btnFilter');
if (btnFilter) {
btnFilter.addEventListener('click', function () {
self.showFilterMenu();
btnFilter.addEventListener('click', () => {
this.showFilterMenu();
});
}
var btnSort = tabContent.querySelector('.btnSort');
const btnSort = tabContent.querySelector('.btnSort');
if (btnSort) {
btnSort.addEventListener('click', function (e) {
@ -232,24 +232,23 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
});
});
}
var btnSelectView = tabContent.querySelector('.btnSelectView');
const btnSelectView = tabContent.querySelector('.btnSelectView');
btnSelectView.addEventListener('click', function (e) {
libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle, 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
});
btnSelectView.addEventListener('layoutchange', function (e) {
var viewStyle = e.detail.viewStyle;
let viewStyle = e.detail.viewStyle;
userSettings.set(savedViewKey, viewStyle);
query.StartIndex = 0;
onViewStyleChange();
itemsContainer.refreshItems();
});
}
};
var self = this;
var itemsContainer = tabContent.querySelector('.itemsContainer');
var savedQueryKey = params.topParentId + '-' + options.mode;
var savedViewKey = savedQueryKey + '-view';
var query = {
let itemsContainer = tabContent.querySelector('.itemsContainer');
const savedQueryKey = params.topParentId + '-' + options.mode;
const savedViewKey = savedQueryKey + '-view';
let query = {
SortBy: 'SortName,ProductionYear',
SortOrder: 'Ascending',
IncludeItemTypes: 'Movie',
@ -265,7 +264,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
query['Limit'] = userSettings.libraryPageSize();
}
var isLoading = false;
let isLoading = false;
if (options.mode === 'favorites') {
query.IsFavorite = true;
@ -273,14 +272,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
query = userSettings.loadQuerySettings(savedQueryKey, query);
self.showFilterMenu = function () {
require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) {
var filterDialog = new filterDialogFactory({
this.showFilterMenu = function () {
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
let filterDialog = new filterDialogFactory({
query: query,
mode: 'movies',
serverId: ApiClient.serverId()
});
events.on(filterDialog, 'filterchange', function () {
events.on(filterDialog, 'filterchange', () => {
query.StartIndex = 0;
itemsContainer.refreshItems();
});
@ -288,22 +287,23 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
});
};
self.getCurrentViewStyle = function () {
this.getCurrentViewStyle = function () {
return userSettings.get(savedViewKey) || 'Poster';
};
self.initTab = function () {
this.initTab = function () {
initPage(tabContent);
onViewStyleChange();
};
self.renderTab = function () {
this.renderTab = function () {
itemsContainer.refreshItems();
updateFilterControls();
};
self.destroy = function () {
this.destroy = function () {
itemsContainer = null;
};
};
});
}
/* eslint-enable indent */

View file

@ -1,8 +1,20 @@
define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu', 'mainTabsManager', 'cardBuilder', 'dom', 'imageLoader', 'playbackManager', 'globalize', 'emby-scroller', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager, globalize) {
'use strict';
import events from 'events';
import layoutManager from 'layoutManager';
import inputManager from 'inputManager';
import * as userSettings from 'userSettings';
import libraryMenu from 'libraryMenu';
import * as mainTabsManager from 'mainTabsManager';
import cardBuilder from 'cardBuilder';
import dom from 'dom';
import imageLoader from 'imageLoader';
import playbackManager from 'playbackManager';
import globalize from 'globalize';
import 'emby-scroller';
import 'emby-itemscontainer';
import 'emby-tabs';
import 'emby-button';
playbackManager = playbackManager.default || playbackManager;
layoutManager = layoutManager.default || layoutManager;
/* eslint-disable indent */
function enableScrollX() {
return !layoutManager.desktop;
@ -17,7 +29,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function loadLatest(page, userId, parentId) {
var options = {
const options = {
IncludeItemTypes: 'Movie',
Limit: 18,
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
@ -27,8 +39,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
EnableTotalRecordCount: false
};
ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) {
var allowBottomPadding = !enableScrollX();
var container = page.querySelector('#recentlyAddedItems');
const allowBottomPadding = !enableScrollX();
const container = page.querySelector('#recentlyAddedItems');
cardBuilder.buildCards(items, {
itemsContainer: container,
shape: getPortraitShape(),
@ -46,8 +58,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function loadResume(page, userId, parentId) {
var screenWidth = dom.getWindowSize().innerWidth;
var options = {
let screenWidth = dom.getWindowSize().innerWidth;
const options = {
SortBy: 'DatePlayed',
SortOrder: 'Descending',
IncludeItemTypes: 'Movie',
@ -68,8 +80,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
page.querySelector('#resumableSection').classList.add('hide');
}
var allowBottomPadding = !enableScrollX();
var container = page.querySelector('#resumableItems');
const allowBottomPadding = !enableScrollX();
const container = page.querySelector('#resumableItems');
cardBuilder.buildCards(result.Items, {
itemsContainer: container,
preferThumb: true,
@ -89,8 +101,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function getRecommendationHtml(recommendation) {
var html = '';
var title = '';
let html = '';
let title = '';
switch (recommendation.RecommendationType) {
case 'SimilarToRecentlyPlayed':
@ -114,7 +126,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
html += '<div class="verticalSection">';
html += '<h2 class="sectionTitle sectionTitle-cards padded-left">' + title + '</h2>';
var allowBottomPadding = true;
const allowBottomPadding = true;
if (enableScrollX()) {
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-mousewheel="false" data-centerfocus="true">';
@ -142,8 +154,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function loadSuggestions(page, userId, parentId) {
var screenWidth = dom.getWindowSize().innerWidth;
var url = ApiClient.getUrl('Movies/Recommendations', {
let screenWidth = dom.getWindowSize().innerWidth;
let url = ApiClient.getUrl('Movies/Recommendations', {
userId: userId,
categoryLimit: 6,
ItemLimit: screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 6 : 5,
@ -158,9 +170,9 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
return;
}
var html = recommendations.map(getRecommendationHtml).join('');
const html = recommendations.map(getRecommendationHtml).join('');
page.querySelector('.noItemsMessage').classList.add('hide');
var recs = page.querySelector('.recommendations');
let recs = page.querySelector('.recommendations');
recs.innerHTML = html;
imageLoader.lazyChildren(recs);
@ -170,7 +182,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function autoFocus(page) {
require(['autoFocuser'], function (autoFocuser) {
import('autoFocuser').then(({default: autoFocuser}) => {
autoFocuser.autoFocus(page);
});
}
@ -196,17 +208,16 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
function initSuggestedTab(page, tabContent) {
var containers = tabContent.querySelectorAll('.itemsContainer');
const containers = tabContent.querySelectorAll('.itemsContainer');
for (var i = 0, length = containers.length; i < length; i++) {
setScrollClasses(containers[i], enableScrollX());
for (const container of containers) {
setScrollClasses(container, enableScrollX());
}
}
function loadSuggestionsTab(view, params, tabContent) {
var parentId = params.topParentId;
var userId = ApiClient.getCurrentUserId();
console.debug('loadSuggestionsTab');
const parentId = params.topParentId;
const userId = ApiClient.getCurrentUserId();
loadResume(tabContent, userId, parentId);
loadLatest(tabContent, userId, parentId);
loadSuggestions(tabContent, userId, parentId);
@ -225,9 +236,6 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
name: globalize.translate('TabCollections')
}, {
name: globalize.translate('TabGenres')
}, {
name: globalize.translate('ButtonSearch'),
cssClass: 'searchTabButton'
}];
}
@ -250,13 +258,13 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
}
return function (view, params) {
export default function (view, params) {
function onBeforeTabChange(e) {
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
}
function onTabChange(e) {
var newIndex = parseInt(e.detail.selectedTabIndex);
const newIndex = parseInt(e.detail.selectedTabIndex);
loadTab(view, newIndex);
}
@ -268,52 +276,50 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
mainTabsManager.setTabs(view, currentTabIndex, getTabs, getTabContainers, onBeforeTabChange, onTabChange);
}
function getTabController(page, index, callback) {
var depends = [];
const getTabController = (page, index, callback) => {
let depends = '';
switch (index) {
case 0:
depends.push('controllers/movies/movies');
depends = 'controllers/movies/movies';
break;
case 1:
depends = 'controllers/movies/moviesrecommended.js';
break;
case 2:
depends.push('controllers/movies/movietrailers');
depends = 'controllers/movies/movietrailers';
break;
case 3:
depends.push('controllers/movies/movies');
depends = 'controllers/movies/movies';
break;
case 4:
depends.push('controllers/movies/moviecollections');
depends = 'controllers/movies/moviecollections';
break;
case 5:
depends.push('controllers/movies/moviegenres');
depends = 'controllers/movies/moviegenres';
break;
case 6:
depends.push('scripts/searchtab');
}
require(depends, function (controllerFactory) {
var tabContent;
import(depends).then(({default: controllerFactory}) => {
let tabContent;
if (index === suggestionsTabIndex) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
self.tabContent = tabContent;
this.tabContent = tabContent;
}
var controller = tabControllers[index];
let controller = tabControllers[index];
if (!controller) {
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
if (index === suggestionsTabIndex) {
controller = self;
controller = this;
} else if (index === 6) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'movies',
@ -336,7 +342,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
callback(controller);
});
}
};
function preLoadTab(page, index) {
getTabController(page, index, function (controller) {
@ -348,12 +354,12 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
function loadTab(page, index) {
currentTabIndex = index;
getTabController(page, index, function (controller) {
getTabController(page, index, ((controller) => {
if (renderedTabs.indexOf(index) == -1) {
renderedTabs.push(index);
controller.renderTab();
}
});
}));
}
function onPlaybackStop(e, state) {
@ -371,22 +377,21 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
}
}
var self = this;
var currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
var suggestionsTabIndex = 1;
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
const suggestionsTabIndex = 1;
self.initTab = function () {
var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
this.initTab = function () {
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
initSuggestedTab(view, tabContent);
};
self.renderTab = function () {
var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
this.renderTab = function () {
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
loadSuggestionsTab(view, params, tabContent);
};
var tabControllers = [];
var renderedTabs = [];
let tabControllers = [];
let renderedTabs = [];
view.addEventListener('viewshow', function (e) {
initTabs();
if (!view.getAttribute('data-title')) {
@ -406,15 +411,14 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
events.on(playbackManager, 'playbackstop', onPlaybackStop);
inputManager.on(window, onInputCommand);
});
view.addEventListener('viewbeforehide', function (e) {
view.addEventListener('viewbeforehide', function () {
inputManager.off(window, onInputCommand);
});
view.addEventListener('viewdestroy', function (e) {
tabControllers.forEach(function (t) {
if (t.destroy) {
t.destroy();
for (const tabController of tabControllers) {
if (tabController.destroy) {
tabController.destroy();
}
});
});
};
});
}
}
/* eslint-enable indent */

View file

@ -1,14 +1,20 @@
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, userSettings, globalize) {
'use strict';
import loading from 'loading';
import events from 'events';
import libraryBrowser from 'libraryBrowser';
import imageLoader from 'imageLoader';
import AlphaPicker from 'alphaPicker';
import listView from 'listView';
import cardBuilder from 'cardBuilder';
import * as userSettings from 'userSettings';
import globalize from 'globalize';
import 'emby-itemscontainer';
loading = loading.default || loading;
libraryBrowser = libraryBrowser.default || libraryBrowser;
layoutManager = layoutManager.default || layoutManager;
/* eslint-disable indent */
return function (view, params, tabContent) {
export default function (view, params, tabContent) {
function getPageData(context) {
var key = getSavedQueryKey(context);
var pageData = data[key];
const key = getSavedQueryKey(context);
let pageData = data[key];
if (!pageData) {
pageData = data[key] = {
@ -47,11 +53,11 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
return context.savedQueryKey;
}
function reloadItems() {
const reloadItems = () => {
loading.show();
isLoading = true;
var query = getQuery(tabContent);
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) {
const query = getQuery(tabContent);
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => {
function onNextPageClick() {
if (isLoading) {
return;
@ -76,7 +82,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
window.scrollTo(0, 0);
updateFilterControls(tabContent);
var pagingHtml = libraryBrowser.getQueryPagingHtml({
const pagingHtml = libraryBrowser.getQueryPagingHtml({
startIndex: query.StartIndex,
limit: query.Limit,
totalRecordCount: result.TotalRecordCount,
@ -86,8 +92,8 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
sortButton: false,
filterButton: false
});
var html;
var viewStyle = self.getCurrentViewStyle();
let html;
const viewStyle = this.getCurrentViewStyle();
if (viewStyle == 'Thumb') {
html = cardBuilder.getCardsHtml({
@ -143,22 +149,20 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
});
}
var i;
var length;
var elems = tabContent.querySelectorAll('.paging');
let elems = tabContent.querySelectorAll('.paging');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
if (!result.Items.length) {
@ -170,27 +174,26 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
html += '</div>';
}
var itemsContainer = tabContent.querySelector('.itemsContainer');
const itemsContainer = tabContent.querySelector('.itemsContainer');
itemsContainer.innerHTML = html;
imageLoader.lazyChildren(itemsContainer);
libraryBrowser.saveQueryValues(getSavedQueryKey(tabContent), query);
loading.hide();
isLoading = false;
});
}
};
function updateFilterControls(tabContent) {
var query = getQuery(tabContent);
self.alphaPicker.value(query.NameStartsWithOrGreater);
}
const updateFilterControls = (tabContent) => {
const query = getQuery(tabContent);
this.alphaPicker.value(query.NameStartsWithOrGreater);
};
var self = this;
var data = {};
var isLoading = false;
const data = {};
let isLoading = false;
self.showFilterMenu = function () {
require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) {
var filterDialog = new filterDialogFactory({
this.showFilterMenu = function () {
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
const filterDialog = new filterDialogFactory({
query: getQuery(tabContent),
mode: 'movies',
serverId: ApiClient.serverId()
@ -203,21 +206,21 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
});
};
self.getCurrentViewStyle = function () {
this.getCurrentViewStyle = function () {
return getPageData(tabContent).view;
};
function initPage(tabContent) {
var alphaPickerElement = tabContent.querySelector('.alphaPicker');
var itemsContainer = tabContent.querySelector('.itemsContainer');
const initPage = (tabContent) => {
const alphaPickerElement = tabContent.querySelector('.alphaPicker');
const itemsContainer = tabContent.querySelector('.itemsContainer');
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
var newValue = e.detail.value;
var query = getQuery(tabContent);
const newValue = e.detail.value;
const query = getQuery(tabContent);
query.NameStartsWithOrGreater = newValue;
query.StartIndex = 0;
reloadItems();
});
self.alphaPicker = new AlphaPicker.default({
this.alphaPicker = new AlphaPicker({
element: alphaPickerElement,
valueChangeEvent: 'click'
});
@ -227,7 +230,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
itemsContainer.classList.add('padded-right-withalphapicker');
tabContent.querySelector('.btnFilter').addEventListener('click', function () {
self.showFilterMenu();
this.showFilterMenu();
});
tabContent.querySelector('.btnSort').addEventListener('click', function (e) {
libraryBrowser.showSortMenu({
@ -261,15 +264,16 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
button: e.target
});
});
}
};
initPage(tabContent);
self.renderTab = function () {
this.renderTab = function () {
reloadItems();
updateFilterControls(tabContent);
};
self.destroy = function () {};
};
});
this.destroy = function () {};
}
/* eslint-enable indent */

View file

@ -191,9 +191,6 @@ import 'flexStyles';
name: globalize.translate('TabSongs')
}, {
name: globalize.translate('TabGenres')
}, {
name: globalize.translate('ButtonSearch'),
cssClass: 'searchTabButton'
}];
}
@ -295,10 +292,6 @@ import 'flexStyles';
case 6:
depends = 'controllers/music/musicgenres';
break;
case 7:
depends = 'scripts/searchtab';
break;
}
import(depends).then(({default: controllerFactory}) => {

View file

@ -30,9 +30,6 @@ import 'emby-button';
name: globalize.translate('TabNetworks')
}, {
name: globalize.translate('TabEpisodes')
}, {
name: globalize.translate('ButtonSearch'),
cssClass: 'searchTabButton'
}];
}
@ -217,10 +214,6 @@ import 'emby-button';
case 6:
depends = 'controllers/shows/episodes';
break;
case 7:
depends = 'scripts/searchtab';
break;
}
import(depends).then(({default: controllerFactory}) => {

View file

@ -230,3 +230,18 @@
margin: 0;
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;
}

View file

@ -150,6 +150,16 @@ import 'emby-input';
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);
}
const containerElement = this.parentNode;
containerElement.classList.add('mdl-slider-container');

View file

@ -1,15 +1,19 @@
define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser, dom) {
"use strict";
/* Cleaning this file properly is not neecessary, since it's an outdated library
* and will be replaced soon by a Vue component.
*/
browser = browser.default || browser;
import browser from 'browser';
import dom from 'dom';
import 'css!./navdrawer';
import 'scrollStyles';
return function (options) {
export default function (options) {
function getTouches(e) {
return e.changedTouches || e.targetTouches || e.touches;
}
function onMenuTouchStart(e) {
options.target.classList.remove("transition");
options.target.classList.remove('transition');
var touches = getTouches(e);
var touch = touches[0] || {};
menuTouchStartX = touch.clientX;
@ -32,27 +36,27 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
var deltaY = endY - (menuTouchStartY || 0);
setVelocity(deltaX);
if (isOpen && 1 !== dragMode && deltaX > 0) {
if (isOpen && dragMode !== 1 && deltaX > 0) {
dragMode = 2;
}
if (0 === dragMode && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) {
if (dragMode === 0 && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) {
dragMode = 1;
scrollContainer.addEventListener("scroll", disableEvent);
scrollContainer.addEventListener('scroll', disableEvent);
self.showMask();
} else if (0 === dragMode && Math.abs(deltaY) >= 5) {
} else if (dragMode === 0 && Math.abs(deltaY) >= 5) {
dragMode = 2;
}
if (1 === dragMode) {
if (dragMode === 1) {
newPos = currentPos + deltaX;
self.changeMenuPos();
}
}
function onMenuTouchEnd(e) {
options.target.classList.add("transition");
scrollContainer.removeEventListener("scroll", disableEvent);
options.target.classList.add('transition');
scrollContainer.removeEventListener('scroll', disableEvent);
dragMode = 0;
var touches = getTouches(e);
var touch = touches[0] || {};
@ -71,9 +75,9 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) {
isPeeking = true;
if (e.type === "touchstart") {
dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
dom.addEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
if (e.type === 'touchstart') {
dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
dom.addEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
}
onMenuTouchStart(e);
@ -90,7 +94,7 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
function onEdgeTouchEnd(e) {
if (isPeeking) {
isPeeking = false;
dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
onMenuTouchEnd(e);
}
}
@ -144,8 +148,8 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
function onMaskTransitionEnd() {
var classList = mask.classList;
if (!classList.contains("backdrop")) {
classList.add("hide");
if (!classList.contains('backdrop')) {
classList.add('hide');
}
}
@ -157,10 +161,10 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
var startPoint = 0;
var countStart = 0;
var velocity = 0;
options.target.classList.add("transition");
options.target.classList.add('transition');
var dragMode = 0;
var scrollContainer = options.target.querySelector(".mainDrawer-scrollContainer");
scrollContainer.classList.add("scrollY");
var scrollContainer = options.target.querySelector('.mainDrawer-scrollContainer');
scrollContainer.classList.add('scrollY');
var TouchMenuLA = function () {
self = this;
@ -175,13 +179,13 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
};
TouchMenuLA.prototype.initElements = function () {
options.target.classList.add("touch-menu-la");
options.target.style.width = options.width + "px";
options.target.style.left = -options.width + "px";
options.target.classList.add('touch-menu-la');
options.target.style.width = options.width + 'px';
options.target.style.left = -options.width + 'px';
if (!options.disableMask) {
mask = document.createElement("div");
mask.className = "tmla-mask hide";
mask = document.createElement('div');
mask.className = 'tmla-mask hide';
document.body.appendChild(mask);
dom.addEventListener(mask, dom.whichTransitionEvent(), onMaskTransitionEnd, {
passive: true
@ -192,12 +196,12 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
var menuTouchStartX;
var menuTouchStartY;
var menuTouchStartTime;
var edgeContainer = document.querySelector(".mainDrawerHandle");
var edgeContainer = document.querySelector('.mainDrawerHandle');
var isPeeking = false;
TouchMenuLA.prototype.animateToPosition = function (pos) {
requestAnimationFrame(function () {
options.target.style.transform = pos ? "translateX(" + pos + "px)" : "none";
options.target.style.transform = pos ? 'translateX(' + pos + 'px)' : 'none';
});
};
@ -208,7 +212,7 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
};
TouchMenuLA.prototype.clickMaskClose = function () {
mask.addEventListener("click", function () {
mask.addEventListener('click', function () {
self.close();
});
};
@ -235,7 +239,7 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
this.animateToPosition(options.width);
currentPos = options.width;
this.isVisible = true;
options.target.classList.add("drawer-open");
options.target.classList.add('drawer-open');
self.showMask();
self.invoke(options.onChange);
};
@ -244,7 +248,7 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
this.animateToPosition(0);
currentPos = 0;
self.isVisible = false;
options.target.classList.remove("drawer-open");
options.target.classList.remove('drawer-open');
self.hideMask();
self.invoke(options.onChange);
};
@ -261,13 +265,13 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
var backgroundTouchStartTime;
TouchMenuLA.prototype.showMask = function () {
mask.classList.remove("hide");
mask.classList.add("backdrop");
mask.classList.remove('hide');
mask.classList.add('backdrop');
};
TouchMenuLA.prototype.hideMask = function () {
mask.classList.add("hide");
mask.classList.remove("backdrop");
mask.classList.add('hide');
mask.classList.remove('backdrop');
};
TouchMenuLA.prototype.invoke = function (fn) {
@ -284,26 +288,26 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
if (enabled) {
if (!_edgeSwipeEnabled) {
_edgeSwipeEnabled = true;
dom.addEventListener(edgeContainer, "touchstart", onEdgeTouchStart, {
dom.addEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, {
passive: true
});
dom.addEventListener(edgeContainer, "touchend", onEdgeTouchEnd, {
dom.addEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, {
passive: true
});
dom.addEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, {
dom.addEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, {
passive: true
});
}
} else {
if (_edgeSwipeEnabled) {
_edgeSwipeEnabled = false;
dom.removeEventListener(edgeContainer, "touchstart", onEdgeTouchStart, {
dom.removeEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, {
passive: true
});
dom.removeEventListener(edgeContainer, "touchend", onEdgeTouchEnd, {
dom.removeEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, {
passive: true
});
dom.removeEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, {
dom.removeEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, {
passive: true
});
}
@ -322,26 +326,26 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
self.initElements();
if (browser.touch) {
dom.addEventListener(options.target, "touchstart", onMenuTouchStart, {
dom.addEventListener(options.target, 'touchstart', onMenuTouchStart, {
passive: true
});
dom.addEventListener(options.target, "touchmove", onMenuTouchMove, {
dom.addEventListener(options.target, 'touchmove', onMenuTouchMove, {
passive: true
});
dom.addEventListener(options.target, "touchend", onMenuTouchEnd, {
dom.addEventListener(options.target, 'touchend', onMenuTouchEnd, {
passive: true
});
dom.addEventListener(options.target, "touchcancel", onMenuTouchEnd, {
dom.addEventListener(options.target, 'touchcancel', onMenuTouchEnd, {
passive: true
});
dom.addEventListener(mask, "touchstart", onBackgroundTouchStart, {
dom.addEventListener(mask, 'touchstart', onBackgroundTouchStart, {
passive: true
});
dom.addEventListener(mask, "touchmove", onBackgroundTouchMove, {});
dom.addEventListener(mask, "touchend", onBackgroundTouchEnd, {
dom.addEventListener(mask, 'touchmove', onBackgroundTouchMove, {});
dom.addEventListener(mask, 'touchend', onBackgroundTouchEnd, {
passive: true
});
dom.addEventListener(mask, "touchcancel", onBackgroundTouchEnd, {
dom.addEventListener(mask, 'touchcancel', onBackgroundTouchEnd, {
passive: true
});
}
@ -350,5 +354,4 @@ define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser,
};
return new TouchMenuLA();
};
});
}

View file

@ -1,18 +1,22 @@
define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'scrollStyles'], function (browser, layoutManager, dom, focusManager, ResizeObserver) {
'use strict';
/* Cleaning this file properly is not neecessary, since it's an outdated library
* and will be replaced soon by a Vue component.
*/
browser = browser.default || browser;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
import browser from 'browser';
import layoutManager from 'layoutManager';
import dom from 'dom';
import focusManager from 'focusManager';
import ResizeObserver from 'ResizeObserver';
import 'scrollStyles';
/**
/**
* Return type of the value.
*
* @param {Mixed} value
*
* @return {String}
*/
function type(value) {
function type(value) {
if (value == null) {
return String(value);
}
@ -22,23 +26,23 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
return typeof value;
}
}
/**
/**
* Disables an event it was triggered on and unbinds itself.
*
* @param {Event} event
*
* @return {Void}
*/
function disableOneEvent(event) {
function disableOneEvent(event) {
/*jshint validthis:true */
event.preventDefault();
event.stopPropagation();
this.removeEventListener(event.type, disableOneEvent);
}
}
/**
/**
* Make sure that number is within the limits.
*
* @param {Number} number
@ -47,28 +51,17 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Number}
*/
function within(number, min, max) {
function within(number, min, max) {
return number < min ? min : number > max ? max : number;
}
}
// Other global values
var dragMouseEvents = ['mousemove', 'mouseup'];
var dragTouchEvents = ['touchmove', 'touchend'];
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA'];
var tmpArray = [];
var time;
// Math shorthands
var abs = Math.abs;
var sqrt = Math.sqrt;
var pow = Math.pow;
var round = Math.round;
var max = Math.max;
var min = Math.min;
var scrollerFactory = function (frame, options) {
// Other global values
var dragMouseEvents = ['mousemove', 'mouseup'];
var dragTouchEvents = ['touchmove', 'touchend'];
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA'];
var scrollerFactory = function (frame, options) {
// Extend options
var o = Object.assign({}, {
slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE.
@ -101,11 +94,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
// native smooth scroll
options.enableNativeScroll = true;
} else if (options.requireAnimation && (browser.animate || browser.supportsCssAnimation())) {
// transform is the only way to guarantee animation
options.enableNativeScroll = false;
} else if (!layoutManager.tv || !browser.animate) {
options.enableNativeScroll = true;
}
@ -166,9 +157,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
var frameSize = 0;
var slideeSize = 0;
function ensureSizeInfo() {
if (requiresReflow) {
requiresReflow = false;
// Reset global variables
@ -177,7 +166,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']);
// Set position limits & relativess
self._pos.end = max(slideeSize - frameSize, 0);
self._pos.end = Math.max(slideeSize - frameSize, 0);
}
}
@ -190,11 +179,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Void}
*/
function load(isInit) {
requiresReflow = true;
if (!isInit) {
ensureSizeInfo();
// Fix possible overflowing
@ -204,7 +191,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
function initFrameResizeObserver() {
var observerOptions = {};
self.frameResizeObserver = new ResizeObserver(onResize, observerOptions);
@ -229,16 +215,13 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
};
function nativeScrollTo(container, pos, immediate) {
if (container.scroll) {
if (o.horizontal) {
container.scroll({
left: pos,
behavior: immediate ? 'instant' : 'smooth'
});
} else {
container.scroll({
top: pos,
behavior: immediate ? 'instant' : 'smooth'
@ -270,14 +253,12 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Void}
*/
self.slideTo = function (newPos, immediate, fullItemPos) {
ensureSizeInfo();
var pos = self._pos;
newPos = within(newPos, pos.start, pos.end);
if (!transform) {
nativeScrollTo(nativeScrollElement, newPos, immediate);
return;
}
@ -295,7 +276,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
if (!immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible) {
return;
}
@ -307,7 +287,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
};
function setStyleProperty(elem, name, value, speed, resetTransition) {
var style = elem.style;
if (resetTransition || browser.edge) {
@ -329,7 +308,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
function renderAnimateWithTransform(fromPosition, toPosition, immediate) {
var speed = o.speed;
if (immediate) {
@ -337,9 +315,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
if (o.horizontal) {
setStyleProperty(slideeElement, 'transform', 'translateX(' + (-round(toPosition)) + 'px)', speed);
setStyleProperty(slideeElement, 'transform', 'translateX(' + (-Math.round(toPosition)) + 'px)', speed);
} else {
setStyleProperty(slideeElement, 'transform', 'translateY(' + (-round(toPosition)) + 'px)', speed);
setStyleProperty(slideeElement, 'transform', 'translateY(' + (-Math.round(toPosition)) + 'px)', speed);
}
self._pos.cur = toPosition;
@ -347,7 +325,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
function getBoundingClientRect(elem) {
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
@ -365,14 +342,10 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Object}
*/
self.getPos = function (item) {
var scrollElement = transform ? slideeElement : nativeScrollElement;
var slideeOffset = getBoundingClientRect(scrollElement);
var itemOffset = getBoundingClientRect(item);
var slideeStartPos = o.horizontal ? slideeOffset.left : slideeOffset.top;
var slideeEndPos = o.horizontal ? slideeOffset.right : slideeOffset.bottom;
var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
var size = o.horizontal ? itemOffset.width : itemOffset.height;
@ -409,7 +382,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
};
self.getCenterPosition = function (item) {
ensureSizeInfo();
var pos = self.getPos(item);
@ -454,7 +426,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
// Bind dragging events
if (transform) {
if (isTouch) {
dragTouchEvents.forEach(function (eventName) {
dom.addEventListener(document, eventName, dragHandler, {
@ -483,7 +454,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
var pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event;
dragging.pathX = pointer.pageX - dragging.initX;
dragging.pathY = pointer.pageY - dragging.initY;
dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2));
dragging.path = Math.sqrt(Math.pow(dragging.pathX, 2) + Math.pow(dragging.pathY, 2));
dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY;
if (!dragging.released && dragging.path < 1) {
@ -500,7 +471,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
} else {
// If dragging path is sufficiently long we can confidently start a drag
// if drag is in different direction than scroll, ignore it
if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) {
if (o.horizontal ? Math.abs(dragging.pathX) > Math.abs(dragging.pathY) : Math.abs(dragging.pathX) < Math.abs(dragging.pathY)) {
dragging.init = 1;
} else {
return dragEnd();
@ -521,7 +492,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
dragEnd();
}
self.slideTo(round(dragging.initPos - dragging.delta));
self.slideTo(Math.round(dragging.initPos - dragging.delta));
}
/**
@ -558,9 +529,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Boolean}
*/
function isInteractive(element) {
while (element) {
if (interactiveElements.indexOf(element.tagName) !== -1) {
return true;
}
@ -596,7 +565,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Void}
*/
function scrollHandler(event) {
ensureSizeInfo();
var pos = self._pos;
// Ignore if there is no scrolling to be done
@ -613,7 +581,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
self.slideBy(o.scrollBy * delta);
} else {
if (isSmoothScrollSupported) {
delta *= 12;
}
@ -632,7 +599,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
* @return {Void}
*/
self.destroy = function () {
if (self.frameResizeObserver) {
self.frameResizeObserver.disconnect();
self.frameResizeObserver = null;
@ -668,11 +634,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
var contentRect = {};
function onResize(entries) {
var entry = entries[0];
if (entry) {
var newRect = entry.contentRect;
// handle element being hidden
@ -681,7 +645,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
if (newRect.width !== contentRect.width || newRect.height !== contentRect.height) {
contentRect = newRect;
load(false);
@ -707,7 +670,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
}
self.getScrollPosition = function () {
if (transform) {
return self._pos.cur;
}
@ -720,7 +682,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
};
self.getScrollSize = function () {
if (transform) {
return slideeSize;
}
@ -796,7 +757,6 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
initFrameResizeObserver();
if (transform) {
dom.addEventListener(dragSourceElement, 'touchstart', dragInitSlidee, {
passive: true
});
@ -813,9 +773,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
passive: true
});
}
} else if (o.horizontal) {
// Don't bind to mouse events with vertical scroll since the mouse wheel can handle this natively
if (o.mouseWheel) {
@ -840,9 +798,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
// Return instance
return self;
};
};
};
/**
/**
* Slide SLIDEE by amount of pixels.
*
* @param {Int} delta Pixels/Items. Positive means forward, negative means backward.
@ -850,14 +808,14 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Void}
*/
scrollerFactory.prototype.slideBy = function (delta, immediate) {
scrollerFactory.prototype.slideBy = function (delta, immediate) {
if (!delta) {
return;
}
this.slideTo(this._pos.dest + delta, immediate);
};
};
/**
/**
* Core method for handling `toLocation` methods.
*
* @param {String} location
@ -866,7 +824,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Void}
*/
scrollerFactory.prototype.to = function (location, item, immediate) {
scrollerFactory.prototype.to = function (location, item, immediate) {
// Optional arguments logic
if (type(item) === 'boolean') {
immediate = item;
@ -882,9 +840,9 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
this.slideTo(itemPos[location], immediate, itemPos);
}
}
};
};
/**
/**
* Animate element or the whole SLIDEE to the start of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
@ -892,11 +850,11 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Void}
*/
scrollerFactory.prototype.toStart = function (item, immediate) {
scrollerFactory.prototype.toStart = function (item, immediate) {
this.to('start', item, immediate);
};
};
/**
/**
* Animate element or the whole SLIDEE to the end of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
@ -904,11 +862,11 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Void}
*/
scrollerFactory.prototype.toEnd = function (item, immediate) {
scrollerFactory.prototype.toEnd = function (item, immediate) {
this.to('end', item, immediate);
};
};
/**
/**
* Animate element or the whole SLIDEE to the center of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
@ -916,14 +874,13 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc
*
* @return {Void}
*/
scrollerFactory.prototype.toCenter = function (item, immediate) {
scrollerFactory.prototype.toCenter = function (item, immediate) {
this.to('center', item, immediate);
};
};
scrollerFactory.create = function (frame, options) {
scrollerFactory.create = function (frame, options) {
var instance = new scrollerFactory(frame, options);
return Promise.resolve(instance);
};
};
return scrollerFactory;
});
export default scrollerFactory;

View file

@ -577,7 +577,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
this.isLocalPlayer = false;
this.lastPlayerData = {};
new castSenderApiLoader().load().then(initializeChromecast.bind(this));
new castSenderApiLoader.default().load().then(initializeChromecast.bind(this));
}
ChromecastPlayer.prototype.tryPair = function (target) {

View file

@ -1132,7 +1132,7 @@ function tryRemoveElement(elem) {
*/
getCueCss(appearance, selector) {
return `${selector}::cue {
${appearance.text.map((s) => `${s.name}:${s.value}!important;`).join('')}
${appearance.text.map((s) => s.value !== undefined && s.value !== '' ? `${s.name}:${s.value}!important;` : '').join('')}
}`;
}
@ -1150,7 +1150,7 @@ function tryRemoveElement(elem) {
document.getElementsByTagName('head')[0].appendChild(styleElem);
}
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings(), true), '.htmlvideoplayer');
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings()), '.htmlvideoplayer');
});
}
@ -1195,18 +1195,29 @@ function tryRemoveElement(elem) {
// download the track json
this.fetchSubtitles(track, item).then(function (data) {
import('userSettings').then((userSettings) => {
// show in ui
console.debug(`downloaded ${data.TrackEvents.length} track events`);
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings();
const cueLine = parseInt(subtitleAppearance.verticalPosition, 10);
// add some cues to show the text
// in safari, the cues need to be added before setting the track mode to showing
for (const trackEvent of data.TrackEvents) {
const trackCueObject = window.VTTCue || window.TextTrackCue;
const cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
if (cue.line === 'auto') {
cue.line = cueLine;
}
trackElement.addCue(cue);
}
trackElement.mode = 'showing';
});
});
}
/**

View file

@ -33,16 +33,22 @@ video::-webkit-media-controls {
text-shadow: 0.14em 0.14em 0.14em rgba(0, 0, 0, 1);
-webkit-font-smoothing: antialiased;
font-family: inherit;
line-height: normal; /* Restore value. See -webkit-media-text-track-container 'line-height' */
}
.htmlvideoplayer-moveupsubtitles::-webkit-media-text-track-display {
/* style the text itself */
margin-top: -2em;
.htmlvideoplayer::-webkit-media-text-track-container {
font-size: 170% !important; /* Override element inline style */
line-height: 50%; /* Child element cannot set line height smaller than its parent has. This allow smaller values for children */
}
.htmlvideoplayer::-webkit-media-text-track-display {
max-width: 70%;
margin-left: 15%;
}
.videoSubtitles {
position: fixed;
bottom: 10%;
bottom: 0;
text-align: center;
left: 0;
right: 0;
@ -53,7 +59,6 @@ video::-webkit-media-controls {
.videoSubtitlesInner {
max-width: 70%;
background-color: rgba(0, 0, 0, 0.8);
padding: 0.25em;
margin: auto;
display: inline-block;
}

View file

@ -1,6 +1,7 @@
define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'], function (playbackManager, events, serverNotifications, connectionManager) {
'use strict';
serverNotifications = serverNotifications.default || serverNotifications;
playbackManager = playbackManager.default || playbackManager;
function getActivePlayerId() {

View file

@ -1,6 +1,7 @@
define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'syncPlayManager', 'groupSelectionMenu', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, syncPlayManager, groupSelectionMenu, browser, globalize, imageHelper) {
'use strict';
viewManager = viewManager.default || viewManager;
playbackManager = playbackManager.default || playbackManager;
browser = browser.default || browser;
layoutManager = layoutManager.default || layoutManager;
@ -807,6 +808,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
navDrawerScrollContainer.addEventListener('click', onMainDrawerClick);
return new Promise(function (resolve, reject) {
require(['navdrawer'], function (navdrawer) {
navdrawer = navdrawer.default || navdrawer;
navDrawerInstance = new navdrawer(getNavDrawerOptions());
if (!layoutManager.tv) {

View file

@ -1,9 +1,8 @@
define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
'use strict';
import focusManager from 'focusManager';
import dom from 'dom';
import 'scrollStyles';
focusManager = focusManager.default || focusManager;
function getBoundingClientRect(elem) {
function getBoundingClientRect(elem) {
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
@ -11,27 +10,27 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
} else {
return { top: 0, left: 0 };
}
}
}
function getPosition(scrollContainer, item, horizontal) {
var slideeOffset = getBoundingClientRect(scrollContainer);
var itemOffset = getBoundingClientRect(item);
export function getPosition(scrollContainer, item, horizontal) {
const slideeOffset = getBoundingClientRect(scrollContainer);
const itemOffset = getBoundingClientRect(item);
var offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
var size = horizontal ? itemOffset.width : itemOffset.height;
let offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
let size = horizontal ? itemOffset.width : itemOffset.height;
if (!size && size !== 0) {
size = item[horizontal ? 'offsetWidth' : 'offsetHeight'];
}
var currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop;
const currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop;
offset += currentStart;
var frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight;
const frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight;
var currentEnd = currentStart + frameSize;
const currentEnd = currentStart + frameSize;
var isVisible = offset >= currentStart && (offset + size) <= currentEnd;
const isVisible = offset >= currentStart && (offset + size) <= currentEnd;
return {
start: offset,
@ -40,10 +39,10 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
size: size,
isVisible: isVisible
};
}
}
function toCenter(container, elem, horizontal, skipWhenVisible) {
var pos = getPosition(container, elem, horizontal);
export function toCenter(container, elem, horizontal, skipWhenVisible) {
const pos = getPosition(container, elem, horizontal);
if (skipWhenVisible && pos.isVisible) {
return;
@ -62,10 +61,10 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
container.scrollTop = Math.round(pos.center);
}
}
}
}
function toStart(container, elem, horizontal, skipWhenVisible) {
var pos = getPosition(container, elem, horizontal);
export function toStart(container, elem, horizontal, skipWhenVisible) {
const pos = getPosition(container, elem, horizontal);
if (skipWhenVisible && pos.isVisible) {
return;
@ -84,26 +83,25 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
container.scrollTop = Math.round(pos.start);
}
}
}
}
function centerOnFocus(e, scrollSlider, horizontal) {
var focused = focusManager.focusableParent(e.target);
function centerOnFocus(e, scrollSlider, horizontal) {
const focused = focusManager.focusableParent(e.target);
if (focused) {
toCenter(scrollSlider, focused, horizontal);
}
}
}
function centerOnFocusHorizontal(e) {
function centerOnFocusHorizontal(e) {
centerOnFocus(e, this, true);
}
function centerOnFocusVertical(e) {
centerOnFocus(e, this, false);
}
}
return {
getPosition: getPosition,
centerFocus: {
function centerOnFocusVertical(e) {
centerOnFocus(e, this, false);
}
export const centerFocus = {
on: function (element, horizontal) {
if (horizontal) {
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
@ -130,8 +128,11 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
});
}
}
},
};
export default {
getPosition: getPosition,
centerFocus: centerFocus,
toCenter: toCenter,
toStart: toStart
};
});
};

View file

@ -1,57 +0,0 @@
define(['searchFields', 'searchResults', 'events'], function (SearchFields, SearchResults, events) {
'use strict';
SearchFields = SearchFields.default || SearchFields;
SearchResults = SearchResults.default || SearchResults;
function init(instance, tabContent, options) {
tabContent.innerHTML = '<div class="padded-left padded-right searchFields"></div><div class="searchResults padded-top" style="padding-top:1.5em;"></div>';
instance.searchFields = new SearchFields({
element: tabContent.querySelector('.searchFields')
});
instance.searchResults = new SearchResults({
element: tabContent.querySelector('.searchResults'),
serverId: ApiClient.serverId(),
parentId: options.parentId,
collectionType: options.collectionType
});
events.on(instance.searchFields, 'search', function (e, value) {
instance.searchResults.search(value);
});
}
function SearchTab(view, tabContent, options) {
var self = this;
options = options || {};
init(this, tabContent, options);
self.preRender = function () {};
self.renderTab = function () {
var searchFields = this.searchFields;
if (searchFields) {
searchFields.focus();
}
};
}
SearchTab.prototype.destroy = function () {
var searchFields = this.searchFields;
if (searchFields) {
searchFields.destroy();
}
this.searchFields = null;
var searchResults = this.searchResults;
if (searchResults) {
searchResults.destroy();
}
this.searchResults = null;
};
return SearchTab;
});

View file

@ -1,41 +1,43 @@
define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, syncPlayManager, events, inputManager, focusManager, appRouter) {
'use strict';
import connectionManager from 'connectionManager';
import playbackManager from 'playbackManager';
import syncPlayManager from 'syncPlayManager';
import events from 'events';
import inputManager from 'inputManager';
import focusManager from 'focusManager';
import appRouter from 'appRouter';
playbackManager = playbackManager.default || playbackManager;
focusManager = focusManager.default || focusManager;
const serverNotifications = {};
var serverNotifications = {};
function notifyApp() {
function notifyApp() {
inputManager.notify();
}
}
function displayMessage(cmd) {
var args = cmd.Arguments;
function displayMessage(cmd) {
const args = cmd.Arguments;
if (args.TimeoutMs) {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast({ title: args.Header, text: args.Text });
});
} else {
require(['alert'], function (alert) {
alert.default({ title: args.Header, text: args.Text });
import('alert').then(({default: alert}) => {
alert({ title: args.Header, text: args.Text });
});
}
}
}
function displayContent(cmd, apiClient) {
function displayContent(cmd, apiClient) {
if (!playbackManager.isPlayingLocally(['Video', 'Book'])) {
appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId());
}
}
}
function playTrailers(apiClient, itemId) {
function playTrailers(apiClient, itemId) {
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
playbackManager.playTrailers(item);
});
}
}
function processGeneralCommand(cmd, apiClient) {
function processGeneralCommand(cmd, apiClient) {
console.debug('Received command: ' + cmd.Name);
switch (cmd.Name) {
case 'Select':
@ -144,13 +146,13 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in
}
notifyApp();
}
}
function onMessageReceived(e, msg) {
var apiClient = this;
function onMessageReceived(e, msg) {
const apiClient = this;
if (msg.MessageType === 'Play') {
notifyApp();
var serverId = apiClient.serverInfo().Id;
const serverId = apiClient.serverInfo().Id;
if (msg.Data.PlayCommand === 'PlayNext') {
playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId });
} else if (msg.Data.PlayCommand === 'PlayLast') {
@ -185,11 +187,11 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in
notifyApp();
}
} else if (msg.MessageType === 'GeneralCommand') {
var cmd = msg.Data;
const cmd = msg.Data;
processGeneralCommand(cmd, apiClient);
} else if (msg.MessageType === 'UserDataChanged') {
if (msg.Data.UserId === apiClient.getCurrentUserId()) {
for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) {
for (let i = 0, length = msg.Data.UserDataList.length; i < length; i++) {
events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]);
}
}
@ -200,15 +202,15 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in
} else {
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
}
}
function bindEvents(apiClient) {
}
function bindEvents(apiClient) {
events.off(apiClient, 'message', onMessageReceived);
events.on(apiClient, 'message', onMessageReceived);
}
}
connectionManager.getApiClients().forEach(bindEvents);
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
connectionManager.getApiClients().forEach(bindEvents);
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
bindEvents(newApiClient);
});
return serverNotifications;
});
export default serverNotifications;

View file

@ -15,6 +15,10 @@ function saveServerPreferences(instance) {
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
}
const defaultSubtitleAppearanceSettings = {
verticalPosition: -3
};
export class UserSettings {
constructor() {
}
@ -412,7 +416,7 @@ export class UserSettings {
*/
getSubtitleAppearanceSettings(key) {
key = key || 'localplayersubtitleappearance3';
return JSON.parse(this.get(key, false) || '{}');
return Object.assign(defaultSubtitleAppearanceSettings, JSON.parse(this.get(key, false) || '{}'));
}
/**

View file

@ -1,7 +1,5 @@
define([], function () {
'use strict';
return {
// TODO: This seems like a good candidate for deprecation
export default {
openUrl: function (url, target) {
if (window.NativeShell) {
window.NativeShell.openUrl(url, target);
@ -19,5 +17,4 @@ define([], function () {
window.NativeShell.disableFullscreen();
}
}
};
});
};

View file

@ -814,8 +814,8 @@ function initClient() {
define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency);
define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency);
define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) {
window.ViewManager = viewManager;
viewManager.dispatchPageEvents(true);
window.ViewManager = viewManager.default;
viewManager.default.dispatchPageEvents(true);
return viewManager;
});
define('slideshow', [componentsPath + '/slideshow/slideshow'], returnFirstDependency);

View file

@ -99,9 +99,9 @@
"DeleteUserConfirmation": "هل انت متاكد من انك تريد حذف هذا المستخدم ؟",
"DeviceAccessHelp": "هذه الميزة تنطبق حصرياً على الأجهزة التي يمكن التعرف عليها فردياً ولن تمنع المتصفح من الدخول عليها. ترشيح الوصول لأجهزة المستخدم ستمنع المستخدمين من استعمال الأجهزة الجديدة إلى أن يتم اعتمادهم من هنا.",
"DrmChannelsNotImported": "القنوات المجهزة بإدارة الحقوق الرقمية DRM لن تورّد.",
"EasyPasswordHelp": "الرمز الشخصي الميسرالخاص بك يمكنك من الاتصال إلى خادم مكتبتك، عبر تطبيقات أمبي على الأجهزة أو الدخول على حسابك في الشبكة الداخلية.",
"EasyPasswordHelp": "الرمز pin الميسر الخاص بك يستخدم للوصول بدون اتصل عبر التطبيقات المدعومة أو الدخول على حسابك في الشبكة الداخلية.",
"EnablePhotos": "عرض الصور",
"EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى",
"EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى.",
"ErrorAddingListingsToSchedulesDirect": "كان هناك خطأ في إضافة الاصطفاف لخدمة \"Schedules Direct\" الخاصة بك. خدمة \"Schedules Direct\" لا تسمح إلا بعدد محدود من الاصطفافات لكل حساب. قد تحتاج إلى تسجيل الدخول إلى موقع \"Schedules Direct\" لإزالة الاصطفافات الأخرى من حسابك قبل المتابعة.",
"ErrorAddingMediaPathToVirtualFolder": "كان هناك خطأ في إضافة مسار الوسائط. الرجاء التأكد من صحة المسار وأن خادم أمبي لديه صلاحية الوصول إلى الموقع.",
"ErrorAddingTunerDevice": "كان هناك خطأ في إضافة جهاز المولف. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.",
@ -111,7 +111,7 @@
"ErrorPleaseSelectLineup": "الرجاء اختيار اصطفاف ثم المحاولة مرة أخرى. إن لم تتوفر أية اصطفافات، فالرجاء التأكد من اسم المستخدم وكلمة المرور الخاصة بك، وتأكد من صحة رمزك البريدي.",
"ErrorSavingTvProvider": "كان هناك خطأ في حفظ مزود التلفزة. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.",
"ExitFullscreen": "الخروج من الشاشة الكاملة",
"ExtractChapterImagesHelp": "استخلاص صور الأبواب سيسمح لتطبيقات أمبي أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل قدرة المعالج بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.",
"ExtractChapterImagesHelp": "استخلاص صور الفصول سيسمح للتطبيقات أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل موارد الجهاز بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.",
"FFmpegSavePathNotFound": "لم نستطع تحديد موقع ffmpeg باستخدام المسار الذي أدخلته. سوف نحتاج تطبيق FFprobe أيضاً ويجب أن يتواجد في نفس المكان. إن هذه الأجزاء تكون بالعادة محزومة معاً في نفس ملف الإنزال. الرجاء التأكد من المسار المدخل والمحاولة مرة أخرى.",
"FastForward": "التقديم السريع",
"FileNotFound": "الملف غير موجود.",
@ -129,7 +129,7 @@
"GuideProviderSelectListings": "إختر المبوبات",
"H264CrfHelp": "معامل المعدل الثابت CRF هو الجودة الافتراضية لإعدادات مشفر x264. بإمكانك إعطاء قيمة تتراوح بين 0 و 51، وكلما قلت القيمة فسينتج عن ذلك جودة أفضل (على حساب حجم تخزين أعلى). القيم المعقول تتراوح بين 18 و 28. الافتراضي لـ x264 هي 23، لذا فبإمكانك استخدام هذه القيمة كنقطة بداية.",
"EncoderPresetHelp": "اختر قيمة أعلى لتحسين السرة والأداء وقيمة أقل لتحسين الجودة.",
"HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية آلي.",
"HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية بلا None.",
"HeaderAccessSchedule": "جدول الدخولات",
"HeaderAccessScheduleHelp": "إنشئ جدول دخولات لكي تتمكن من تحديد ساعات للدخول.",
"HeaderActiveDevices": "الأجهزة المفعّلة",
@ -145,7 +145,7 @@
"HeaderAllowMediaDeletionFrom": "السماح بحذف الوسائط من قبل",
"HeaderApiKey": "مفتاح API",
"HeaderApiKeys": "مفاتيح API",
"HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بخادم أمبي. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بحساب أمبي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.",
"HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بالخادم. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بمستخدم عادي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.",
"HeaderApp": "التطبيق",
"HeaderAudioSettings": "إعدادات الصوت",
"HeaderBooks": "الكتب",
@ -232,7 +232,7 @@
"HeaderPreferredMetadataLanguage": "اللغة المفضلة لواصفات البيانات",
"HeaderProfile": "الحساب",
"HeaderProfileInformation": "معلومات العريضة",
"HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل خادم أمبي في الجهاز",
"HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل الخادم في للعملاء.",
"HeaderRecentlyPlayed": "تم تشغيله مؤخراً",
"HeaderRecordingPostProcessing": "تطبيق ما-بعد-المعالجة للتسجيل",
"HeaderRemoteControl": "التحكم عن بعد",
@ -254,7 +254,7 @@
"HeaderSelectServerCachePath": "إختر مسار كاشة الخادم",
"HeaderSelectServerCachePathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم كاشة لملفات الخادم. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
"HeaderSelectTranscodingPath": "إختر المسار المؤقت للتشفير البيني",
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم للملفات المؤقتة للتشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
"HeaderSendMessage": "أرسل رسالة",
"HeaderSeries": "المسلسلات",
"HeaderServerSettings": "إعدادات الخادم",
@ -291,17 +291,17 @@
"HeaderXmlSettings": "إعدادات xml",
"HeaderYears": "السنوات",
"HeadersFolders": "مجلدات",
"ImportFavoriteChannelsHelp": "عند التفعيل، فقط القنوات التي علّمت في المفضلة على هذا المولف ستورد إلى النظام.",
"ImportMissingEpisodesHelp": "عند التمكين، المعلومات الناقصة للحلقات ستورّد إلى قاعدة بيانات أمبي وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكنبات.",
"ImportFavoriteChannelsHelp": "فقط القنوات التي علّمت في المفضلة على جهاز المولف ستورد.",
"ImportMissingEpisodesHelp": "المعلومات الناقصة للحلقات ستورّد إلى قاعدة بياناتك وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكتبات.",
"LabelAbortedByServerShutdown": "(تم إهماله بسبب عملية إغلاق الخادم)",
"LabelAccessDay": "يوم الأسبوع:",
"LabelAccessEnd": "تاريخ النهاية",
"LabelAccessStart": "تاريخ البداية",
"LabelAccessEnd": "وقت النهاية:",
"LabelAccessStart": "وقت البداية:",
"LabelAirDays": "أيام البث:",
"LabelAirTime": "وقت البث:",
"LabelAlbum": "الألبوم",
"LabelAlbum": "الألبوم:",
"LabelAlbumArtHelp": "PN المستخدمة في رسومات الألبوم، داخل سمة dlna:profileID في upnp:albumArtURI. بعض الأجهزة تحتاج قيمة محددة، مهما كان حجم الصورة.",
"LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم",
"LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم:",
"LabelAlbumArtMaxHeightHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.",
"LabelAlbumArtMaxWidth": "العرض الأقصى لرسوم الألبوم:",
"LabelAlbumArtMaxWidthHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.",
@ -310,42 +310,42 @@
"LabelAll": "الجميع",
"LabelAllowHWTranscoding": "السماح بالتشفير البيني بعتاد الحاسب",
"LabelAppName": "اسم التطبيق",
"LabelAppNameExample": "مثال: Sickbeard، NzbDrone",
"LabelAppNameExample": "مثال: Sickbeard، Sonarr",
"LabelArtists": "الفنانون:",
"LabelArtistsHelp": "فصل الاستعمالات المتعددة ;",
"LabelArtistsHelp": "افصل بين الفنانين ب ; فاصلة منقوطة.",
"LabelAudioLanguagePreference": "اللغة المفضلة للصوت:",
"LabelBindToLocalNetworkAddress": "إربطه إلى عنوان شبكة محلي:",
"LabelBindToLocalNetworkAddressHelp": "هذا خياري. امتطي عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم أمبي.",
"LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل (بالثواني)",
"LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين يث رسائل قيد التشغيل",
"LabelCache": "ذاكرة الكاشة",
"LabelCachePath": "مسار ذاكرة الكاشة:",
"LabelCachePathHelp": "حدد موقع مخصص لملفات كاشة الخادم، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة التلقائية.",
"LabelBindToLocalNetworkAddressHelp": "تجاوز عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم جيلليفن.",
"LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل",
"LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين بث رسائل قيد التشغيل.",
"LabelCache": "مَخبأ (كاش):",
"LabelCachePath": "مسار ذاكرة الكاش:",
"LabelCachePathHelp": "حدد موقع مخصص لملفات الخادم المؤقتة، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة الافتراضية.",
"LabelCancelled": "تم الإلغاء",
"LabelCollection": "المجموعة",
"LabelCollection": "المجموعة:",
"LabelCommunityRating": "تقييم المجتمع:",
"LabelContentType": "نوع المحتوى",
"LabelContentType": "نوع المحتوى:",
"LabelCountry": "البلد:",
"LabelCurrentPassword": "كلمة السر الحالية:",
"LabelCustomCertificatePath": "مسار شهادة ssl مخصص:",
"LabelCustomCertificatePath": "مسار شهادة SSL المخصص:",
"LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.",
"LabelCustomCss": "تنيسق CSS مخصوص:",
"LabelCustomCssHelp": "طبق تنسيق css مخصوصة لواجهة الويب.",
"LabelCustomCss": "تنيسق CSS مخصص:",
"LabelCustomCssHelp": "طبق تنسيقك css المخصص لواجهة الويب.",
"LabelCustomDeviceDisplayName": "اسم العرض:",
"LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام",
"LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام الاسم المبلغ من الجهاز.",
"LabelDateAddedBehavior": "كيف يتصرف المحتوى الجديد نحو \"تاريخ الإضافة\" الخاص به:",
"LabelDateAddedBehaviorHelp": "إذا استعرضت قيمة واصفات البيانا فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.",
"LabelDateAddedBehaviorHelp": "إذا اخذت واصفات البيانات قيمة، فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.",
"LabelDay": "اليوم:",
"LabelDeathDate": "تاريخ الوفاة:",
"LabelDefaultUser": "المستخدم الافتراضي",
"LabelDefaultUser": "المستخدم الافتراضي:",
"LabelDefaultUserHelp": "لتحديد مكتبة المستخدم التي تظهر على الأجهزة المتصلة. بإمكان الامتطاء على هذه القيمة لكل جهاز عن طريق عرائض الأجهزة.",
"LabelDeviceDescription": "وصف الجهاز",
"LabelDidlMode": "طور didl:",
"LabelDidlMode": "طور DIDL:",
"LabelDisplayMissingEpisodesWithinSeasons": "أظهر الحلقات المفقودة في مجلدات المواسم",
"LabelDisplayName": "الاسم المعروض:",
"LabelDisplaySpecialsWithinSeasons": "أظهر الحلقات الخاصة في المواسم التي بثت فيها",
"LabelDownMixAudioScale": "تعزيز الصوت عند تقليل توزيع قنوات الصوت:",
"LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة بـ 1 للمحافظة على القيمة الأصلية للصوت.",
"LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة ب 1 للمحافظة على القيمة الأصلية للصوت.",
"LabelDownloadLanguages": "إنزال اللغة:",
"LabelDynamicExternalId": "معرفة {0}:",
"LabelEasyPinCode": "الرمز الشخصي الميسر:",
@ -354,8 +354,8 @@
"LabelEnableAutomaticPortMap": "فعل الخاصية الآلية في التوفيق بين المنافذ",
"LabelEnableAutomaticPortMapHelp": "حاول التوفيق بين المنفذ العالمي والمنفذ المحلي آلياً باستخدام آلية UPnP. هذه الخاصية قد لا تعمل مع بعض أنواع الراوترات.",
"LabelEnableBlastAliveMessages": "بث رسائل قيد التشغيل",
"LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك",
"LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء (بالثواني)",
"LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك.",
"LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء",
"LabelEnableDlnaClientDiscoveryIntervalHelp": "يحدد الفترة بالثواني بين عمليات بحث SSDP التي يقوم بها أمبي.",
"LabelEnableDlnaDebugLogging": "تفعيل خاصية كشوفات أخطاء DLNA",
"LabelEnableDlnaDebugLoggingHelp": "هذه ستنشئ سجلات كشفية ضخمة ولا ينبغي تفعيلها إلا عند الحاجة إليها بغرض استكشاف الأخطاء وحصرها.",
@ -930,7 +930,7 @@
"Backdrops": "خلفيات متغيرة للصفحة",
"Backdrop": "خلفية متغيرة للصفحة",
"Auto": "تلقائي",
"AuthProviderHelp": "حدد مقدم المصادقات ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.",
"AuthProviderHelp": "اختار مقدم المصادقة ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.",
"AroundTime": "حول",
"AttributeNew": "جديد",
"AspectRatio": "نسبة العرض الى الارتفاع",
@ -1038,7 +1038,7 @@
"Director": "المخرج",
"DirectPlaying": "بث بدون تحويل الصيغة",
"DirectStreaming": "البث المباشر",
"DirectStreamHelp2": "البث المباشر للملف يستخدم طاقة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.",
"DirectStreamHelp2": "البث المباشر للملف يستخدم قوة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.",
"DirectStreamHelp1": "الوسائط متوافقة مع الجهاز فيما يتعلق بالدقة ونوع الوسائط (H.264 ، AC3 ، إلخ) ، ولكنها في حاوية ملفات غير متوافقة (mkv ، avi ، wmv ، إلخ). سيتم إعادة حزم الفيديو في الوقت الحقيقي قبل بثه إلى الجهاز.",
"DetectingDevices": "يتم الكشف عن الأجهزة",
"Desktop": "سطح المكتب",
@ -1139,5 +1139,51 @@
"Dislike": "لم يعجبنى",
"ButtonSyncPlay": "SyncPlay",
"ExtraLarge": "كبير جدا",
"EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل."
"EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل.",
"LabelDroppedFrames": "الاطارات الساقطة:",
"LabelDropImageHere": "اسقط صورة هنا، او ضغط تصفح.",
"LabelDisplayOrder": "ترتيب المعروض:",
"LabelDisplayMode": "وضع المعروض:",
"LabelDisplayLanguageHelp": "ترجمة جيلليفين هو مشروع مستمر.",
"LabelDisplayLanguage": "لغة العرض:",
"LabelDiscNumber": "رقم القرص:",
"LabelDeinterlaceMethod": "طريقة تقليل التشابك:",
"LabelDefaultScreen": "الشاشة الافتراضية:",
"LabelDateTimeLocale": "وقت و تاريخ محلي:",
"LabelDateAdded": "تاريخ الاضافة:",
"LabelCustomRating": "تقييم مخصص:",
"LabelCriticRating": "تقييم النقاد:",
"LabelCorruptedFrames": "الإطارات التالفة:",
"LabelChannels": "القنوات:",
"LabelCertificatePasswordHelp": "اذا تطلبت شهادتك الامنية كلمة مرور، من فضلك ادخلها هنا.",
"LabelCertificatePassword": "كلمة مرور الشهادة الامنية:",
"LabelBurnSubtitles": "الترجمات المحروقة:",
"LabelBlockContentWithTags": "احجب العناصر بالعلامات:",
"LabelBitrate": "معدل البت:",
"LabelBirthYear": "عام الميلاد:",
"LabelBirthDate": "تاريخ الميلاد:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "حدث وصف البيانات تلقائيا من الانترنت:",
"LabelAuthProvider": "مقدم التصديق:",
"LabelAudioSampleRate": "سرعة معينة الصوت:",
"LabelAudioCodec": "ترميز الصوت:",
"LabelAudioChannels": "قنوات الصوت:",
"LabelAudioBitrate": "معدل بث الصوت:",
"LabelAudioBitDepth": "عمق بث الصوت:",
"LabelAudio": "الصوت",
"LabelAllowedRemoteAddressesMode": "وضع مرشح عنوان المضيف IP البعيد:",
"LabelAllowedRemoteAddresses": "مرشح عنوان المضيف IP البعيد:",
"LabelAirsBeforeSeason": "عروض بث قبل الموسم:",
"LabelAirsBeforeEpisode": "عروض بث قبل الحلقة:",
"LabelAirsAfterSeason": "عروض بث بعد الموسم:",
"Label3DFormat": "صيغة ثلاثية الابعاد:",
"Kids": "اطفال",
"Items": "عناصر",
"ItemCount": "{0} عنصر",
"InstantMix": "خلط فوري",
"HeaderSyncPlayEnabled": "تزامن اللعب ممكَّن",
"HeaderSyncPlaySelectGroup": "انضم لمجموعة",
"EnableDetailsBannerHelp": "اظهر صوره اللافته اعلى عنصر تفاصيل الصفحة.",
"EnableDetailsBanner": "لافتة التفاصيل",
"EnableDecodingColorDepth10Vp9": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل VP9",
"EnableDecodingColorDepth10Hevc": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل HEVC"
}

View file

@ -1542,5 +1542,8 @@
"ButtonCast": "Cast",
"ButtonPlayer": "Player",
"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"
}

View file

@ -10,7 +10,7 @@
"AllEpisodes": "כל הפרקים",
"AllLibraries": "כל הספריות",
"Anytime": "בכל עת",
"AroundTime": "בסביבות {0}",
"AroundTime": "בסביבות",
"AsManyAsPossible": "כמה שיותר",
"AttributeNew": "חדש",
"Backdrops": "תמונות רקע",
@ -32,13 +32,13 @@
"ButtonRefreshGuideData": "רענן את מדריך השידור",
"ButtonRemove": "הסר",
"ButtonResetPassword": "איפוס סיסמא",
"ButtonRestart": "איתחול",
"ButtonRestart": "הפעל מחדש",
"ButtonSave": "שמור",
"ButtonSearch": "חיפוש",
"ButtonSelectDirectory": "בחר תיקיות",
"ButtonShutdown": "כבה",
"ButtonSignIn": "היכנס",
"ButtonSignOut": "Sign out",
"ButtonSignOut": "התנתק",
"ButtonSort": "מיין",
"CancelRecording": "ביטול הקלטה",
"CancelSeries": "בטל סדרה",
@ -114,11 +114,11 @@
"HeaderLatestRecordings": "הקלטות אחרונות",
"HeaderLiveTV": "שידורים חיים",
"HeaderMediaFolders": "ספריות מדיה",
"HeaderMetadataSettings": "הגדרות מטא נתונים",
"HeaderMetadataSettings": "הגדרות מטא-דאטה",
"HeaderMovies": "סרטים",
"HeaderMusicVideos": "קליפים",
"HeaderMyMedia": ספרייה שלי",
"HeaderNextUp": "הבא",
"HeaderMyMedia": מדיה שלי",
"HeaderNextUp": "הבא בתור",
"HeaderPaths": "נתיבים",
"HeaderPlayAll": "נגן הכל",
"HeaderPleaseSignIn": "אנא היכנס",
@ -138,7 +138,7 @@
"Help": "עזרה",
"Identify": "לזהות",
"Images": "תמונות",
"InstallingPackage": "מתקין {0}",
"InstallingPackage": "מתקין {0} (גירסה {1})",
"InstantMix": "מיקס מיידי",
"ItemCount": "פריטים {0}",
"Kids": "ילדים",
@ -151,13 +151,13 @@
"LabelAlbum": "אלבום:",
"LabelAlbumArtists": "אלבום אומנים:",
"LabelArtists": "אומנים:",
"LabelArtistsHelp": "הפרד מרובים באמצעות;",
"LabelArtistsHelp": "הפרד אמנים מרובים באמצעות נקודה-פסיק (;).",
"LabelAudioLanguagePreference": "שפת קול מועדפת:",
"LabelBirthDate": "תאריך לידה:",
"LabelBirthYear": "שנת לידה:",
"LabelBlastMessageInterval": "אינטרוול הודעות דחיפה (בשניות)",
"LabelBlastMessageInterval": "תדירות הודעות דחיפה",
"LabelBlastMessageIntervalHelp": "מגדיר את משך הזמן בשניות בין הודעות דחיפה של השרת.",
"LabelCachePath": "נתיב cache:",
"LabelCachePath": "נתיב מטמון:",
"LabelChannels": "ערוצים:",
"LabelCollection": "אוספים:",
"LabelCommunityRating": "דירוג הקהילה:",
@ -165,7 +165,7 @@
"LabelCountry": "מדינה:",
"LabelCriticRating": "דירוג ביקורת:",
"LabelCurrentPassword": "סיסמא נוכחית:",
"LabelCustomCss": "CSS מותאם אישית",
"LabelCustomCss": "CSS מותאם אישית:",
"LabelCustomRating": "דירוג מותאם אישית:",
"LabelDateAdded": "תאריך הוסף:",
"LabelDay": "יום:",
@ -175,15 +175,15 @@
"LabelDiscNumber": "מספר דיסק:",
"LabelDisplayMissingEpisodesWithinSeasons": "הצג פרקים חסרים בתוך העונות",
"LabelDisplayOrder": "סדר תצוגה:",
"LabelDownMixAudioScaleHelp": "הגבר אודיו כאשר הוא ממוזג. הגדר ל-1 לשמור על ערך הווליום המקורי",
"LabelDownMixAudioScaleHelp": "הגבר את עוצמת השמע כאשר הוא ממוזג. ערך השווה ל-1 יישמר את העוצמה המקורית.",
"LabelDynamicExternalId": "{0} תעודת זהות:",
"LabelEnableBlastAliveMessages": "הודעות דחיפה",
"LabelEnableBlastAliveMessagesHelp": "אפשר זאת אם השרת לא מזוהה כאמין על ידי מכשירי UPnP אחרים ברשת שלך.",
"LabelEnableDlnaClientDiscoveryInterval": "זמן גילוי קליינטים (בשניות)",
"LabelEnableDlnaClientDiscoveryInterval": "זמן גילוי קליינטים",
"LabelEnableDlnaDebugLogging": "אפשר ניהול רישום באגים בDLNA",
"LabelEnableDlnaDebugLoggingHelp": "אפשרות זו תיצור קבצי לוג גדולים יותר ועליך להשתמש בה רק על מנת לפתור תקלות.",
"LabelEnableDlnaPlayTo": "מאפשר ניגון DLNA ל",
"LabelEnableDlnaServer": "אפשר שרת Dina",
"LabelEnableDlnaServer": "אפשר שרת DLNA",
"LabelEnableRealtimeMonitor": "אפשר מעקב בזמן אמת",
"LabelEnableRealtimeMonitorHelp": "שינויים יעשו באופן מיידית על מערכות קבצים נתמכות.",
"LabelEndDate": "תאריך סיום:",
@ -195,21 +195,21 @@
"LabelServerNameHelp": "השם יתן לזיהוי השרת. אם מושאר ריק, שם השרת יהיה שם המחשב.",
"LabelKeepUpTo": "שמור עד ל:",
"LabelLanguage": "שפה:",
"LabelLocalHttpServerPortNumber": "מספר פורט HTTP מקומי",
"LabelLocalHttpServerPortNumber": "מספר פורט HTTP מקומי:",
"LabelLockItemToPreventChanges": "נעל פריט זה כדי למנוע שינויים עתידיים",
"LabelMaxBackdropsPerItem": "מספר תמונות רקע מקסימאלי לפריט:",
"LabelMaxParentalRating": "דירוג הורים מקסימאלי:",
"LabelMaxResumePercentage": "אחוזי המשכה מקסימאלים",
"LabelMaxResumePercentageHelp": "קובץ מוגדר כנוגן במלואו אם נעצר אחרי הזמן הזה",
"LabelMaxResumePercentage": "אחוזי המשכה מקסימאלים:",
"LabelMaxResumePercentageHelp": "קובץ מוגדר כנוגן במלואו אם נעצר אחרי הזמן הזה.",
"LabelMaxScreenshotsPerItem": "מספר תמונות מסך מקסימאלי לפריט:",
"LabelMessageTitle": "כותרת הודעה:",
"LabelMetadataDownloadLanguage": "שפת הורדה מועדפת:",
"LabelMetadataPath": "נתיב Metadata:",
"LabelMetadataPath": "נתיב מטא-דאטה:",
"LabelMinBackdropDownloadWidth": "רוחב תמונת רקע מינימאלי להורדה:",
"LabelMinResumeDuration": "משך המשכה מינימאלי (בשניות):",
"LabelMinResumeDuration": "משך המשכה מינימאלי:",
"LabelMinResumeDurationHelp": "קובץ קצר מזה לא יהיה ניתן להמשך ניגון מנקודת העצירה",
"LabelMinResumePercentage": "אחוזי המשכה מינימאלים:",
"LabelMinResumePercentageHelp": "כותרים יוצגו כלא נוגנו אם נצרו לפני הזמן הזה",
"LabelMinResumePercentageHelp": "כותרים יוצגו כלא נוגנו אם נצרו לפני הזמן הזה.",
"LabelMinScreenshotDownloadWidth": "רחוב תמונת מסך מינימאלית להורדה:",
"LabelMonitorUsers": "עקוב אחר פעילות מ:",
"LabelName": "שם:",
@ -237,12 +237,12 @@
"LabelProfileAudioCodecs": "מקודדי שמע:",
"LabelProfileCodecs": "מקודדים:",
"LabelProfileVideoCodecs": "‮מקודדי וידאו:",
"LabelPublicHttpPort": "מספר פורט HTTP פומבי",
"LabelPublicHttpPort": "מספר פורט HTTP פומבי:",
"LabelReadHowYouCanContribute": "למד איך אתה יכול לתרום.",
"LabelRecord": "הקלטה:",
"LabelRefreshMode": "מצב רענון:",
"LabelReleaseDate": "תאריך הוצאה:",
"LabelRuntimeMinutes": "זמן ריצה (דקות):",
"LabelRuntimeMinutes": "זמן ריצה:",
"LabelSaveLocalMetadata": "שמור תמונות רקע בתוך ספריות המדיה",
"LabelSaveLocalMetadataHelp": "שמירת תמונות רקע בתוך ספריות המדיה תשים אותם במקום שבו יהיה קל לערוך אותם.",
"LabelSeasonNumber": "מספר עונה:",
@ -264,7 +264,7 @@
"LabelUserLibrary": "ספריית משתמש:",
"LabelYear": "שנה:",
"LabelYoureDone": "סיימת!",
"LibraryAccessHelp": "בחר את ספריות המדיה אשר ישותפו עם המשתמש. מנהלים יוכלו לערות את כל התיקיות באמצעות עורך המידע.",
"LibraryAccessHelp": "בחר את הספריות אשר ישותפו עם המשתמש. מנהלים יוכלו לערות את כל התיקיות באמצעות עורך המידע.",
"Like": "אוהב",
"Live": "שידור חי",
"LiveBroadcasts": "שידורים חיים",
@ -302,7 +302,7 @@
"OptionAlbumArtist": "אמן אלבום",
"OptionAllUsers": "כל המשתמשים",
"OptionAllowLinkSharing": "אפשר שיתוף ברשתות חברתיות",
"OptionAllowMediaPlayback": "הרשה נגינת מדיה",
"OptionAllowMediaPlayback": "אפשר ניגון מדיה",
"OptionAllowUserToManageServer": "אפשר למשתמש זה לנהל את השרת",
"OptionArtist": "אמן",
"OptionAscending": "סדר עולה",
@ -314,12 +314,12 @@
"OptionContinuing": "ממשיך",
"OptionCriticRating": "ציון מבקרים",
"OptionCustomUsers": "מותאם אישית",
"OptionDaily": "יומי",
"OptionDaily": "כל יום",
"OptionDateAdded": "תאריך הוספה",
"OptionDatePlayed": "תאריך ניגון",
"OptionDescending": "סדר יורד",
"OptionDisableUser": "בטל משתמש זה",
"OptionDisableUserHelp": "אם מבוטל, השרת שלא יאפשר חיבורים ממשתמש זה. חיבורים פעילים יבוטלו מייד.",
"OptionDisableUserHelp": "השרת לא יאפשר חיבורים ממשתמש זה. חיבורים פעילים יבוטלו מייד.",
"OptionDislikes": "לא אוהב",
"OptionDownloadArtImage": "עטיפה",
"OptionDownloadBackImage": "גב",
@ -337,42 +337,42 @@
"OptionHasSubtitles": "כתוביות",
"OptionHasThemeSong": "שיר נושא",
"OptionHasThemeVideo": "סרט נושא",
"OptionHasTrailer": "טריילר",
"OptionHasTrailer": "קדימון",
"OptionHideUser": "הסתר משתמש זה בחלון ההתחברות",
"OptionImdbRating": "דירוג IMDb",
"OptionLikes": "נבחרים",
"OptionMissingEpisode": "פרקים חסרים",
"OptionMonday": "שני",
"OptionNameSort": "שם",
"OptionNew": "חדש...",
"OptionNew": "חדש",
"OptionOnAppStartup": "בהפעלת התוכנה",
"OptionOnInterval": "כל פרק זמן",
"OptionParentalRating": "דירוג בקרת הורים",
"OptionPlayCount": "מספר השמעות",
"OptionPlayCount": "כמות ניגונים",
"OptionPlayed": "נוגן",
"OptionPremiereDate": "תאריך שידור ראשון",
"OptionPremiereDate": "תאריך בכורה",
"OptionProfileAudio": "צליל",
"OptionProfilePhoto": "תמונה",
"OptionProfileVideo": "וידאו",
"OptionProfileVideoAudio": "צליל וידאו",
"OptionResumable": "ניתן להמשיך",
"OptionRuntime": "משך",
"OptionRuntime": "זמן ריצה",
"OptionSaturday": "שבת",
"OptionSpecialEpisode": "ספיישלים",
"OptionSunday": "ראשון",
"OptionThursday": "חמישי",
"OptionTrackName": "שם השיר",
"OptionTrackName": "שם הרצועה",
"OptionTuesday": "שלישי",
"OptionTvdbRating": "דירוג Tvdb",
"OptionTvdbRating": "דירוג TVDB",
"OptionUnairedEpisode": "פרקים שלא שודרו",
"OptionUnplayed": "לא נוגן",
"OptionWakeFromSleep": "הער ממצב שינה",
"OptionWednesday": "רביעי",
"OptionWeekly": "שבועי",
"OptionWeekly": "כל שבוע",
"OriginalAirDateValue": "תאריך אוויר מקורי: {0}",
"Overview": "סקירה כללית",
"PackageInstallCancelled": "{0} ההתקנה בוטלה.",
"PackageInstallFailed": "ההתקנה {0} נכשלה.",
"PackageInstallCancelled": "ההתקנה של {0} (גירסה {1}) בוטלה.",
"PackageInstallFailed": "ההתקנה של {0} (גירסה {1}) נכשלה.",
"ParentalRating": "דירוג ההורים",
"PasswordMatchError": "הסיסמא ואימות הסיסמא צריכות להיות זהות.",
"PasswordResetComplete": "הסיסמא אופסה.",
@ -395,7 +395,7 @@
"RecordingCancelled": "הקלטה בוטלה.",
"RecordingScheduled": "ההקלטה מתוזמנת.",
"Refresh": "רענון",
"RefreshDialogHelp": "המטא נתונים מתרעננים על סמך הגדרות ושירותי אינטרנט שמופעלים בלוח המחוונים של מרכז אמבי.",
"RefreshDialogHelp": "המטא-דאטה מתרעננת על סמך הגדרות ושירותי אינטרנט שמופעלים בלוח הבקרה.",
"RefreshQueued": "רענן תור.",
"ReleaseDate": "תאריך שיחרור",
"RemoveFromCollection": "הסר מאוספים",
@ -419,7 +419,7 @@
"SeriesSettings": "הגדרות סדרה",
"SeriesYearToPresent": "{0} - היום",
"ServerNameIsRestarting": "שרת Jellyfin - {0} מופעל מחדש.",
"ServerNameIsShuttingDown": "שרת Jellyfin - {0} נכבה.",
"ServerNameIsShuttingDown": "שרת Jellyfin - {0} בתהליך כיבוי.",
"ServerUpdateNeeded": "שרת אמבי זה צריך להיות מעודכן. כדי להוריד את הגרסה העדכנית ביותר, בקר בכתובת {0}",
"Settings": "הגדרות",
"SettingsSaved": "ההגדרות נשמרו.",
@ -471,7 +471,7 @@
"TabTrailers": "טריילרים",
"TabTranscoding": "קידוד",
"TabUpcoming": "בקרוב",
"Tags": "תגים",
"Tags": "מילות מפתח",
"TellUsAboutYourself": "ספר לנו על עצמך",
"ThisWizardWillGuideYou": "אשף זה יעזור לך בהתליך ההתקנה.",
"Thursday": "חמישי",
@ -514,8 +514,8 @@
"AllLanguages": "כל השפות",
"Alerts": "התראות",
"Box": "מארז",
"BirthPlaceValue": יקום לידה: {0}",
"BirthDateValue": "תאריך לידה: {0}",
"BirthPlaceValue": קום לידה: {0}",
"BirthDateValue": "נולד: {0}",
"Backdrop": "רקע",
"AuthProviderHelp": "בחר ספק אימות שישמש לאימות הסיסמה של משתמש זה.",
"Audio": "שמע",
@ -530,10 +530,10 @@
"Yesterday": "אתמול",
"HeaderAlbumArtists": "אמני האלבום",
"Favorites": "מועדפים",
"HeaderFavoriteAlbums": "אלבומים שאהבתי",
"HeaderFavoriteAlbums": "אלבומים מועדפים",
"HeaderFavoriteArtists": "אמנים מועדפים",
"Folders": "תיקיות",
"HeaderFavoriteShows": "סדרות מועדפות",
"HeaderFavoriteShows": "תוכניות מועדפות",
"HeaderFavoriteEpisodes": "פרקים מועדפים",
"HeaderFavoriteSongs": "שירים מועדפים",
"Collections": "אוספים",
@ -541,7 +541,7 @@
"HeaderContinueWatching": "המשך לצפות",
"AllowOnTheFlySubtitleExtraction": "אפשר חילוץ כתוביות בזמן אמת",
"AllowHWTranscodingHelp": "אפשר למלקט לקודד הזרמות בזמן אמת. זה עשוי לעזור בהפחתת הקידוד שנעשה ע\"י השרת.",
"AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX)",
"AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX, …)",
"Songs": "שירים",
"Shows": "סדרות",
"DownloadsValue": "{0} הורדות",
@ -588,7 +588,7 @@
"MessageConfirmRestart": "‫האם אתה בטוח שברצונך לאתחל את שרת ה-Jellyfin?",
"HeaderThisUserIsCurrentlyDisabled": "משתמש זה אינו פעיל כרגע",
"HeaderTaskTriggers": "טריגרים של המשימה",
"HeaderTags": "תגיות",
"HeaderTags": "מילות מפתח",
"HeaderStopRecording": "עצור הקלטה",
"HeaderSortOrder": "סדר מיון",
"HeaderSortBy": "מיין לפי",
@ -601,7 +601,7 @@
"HeaderRestart": "הפעלה מחדש",
"HeaderProfileInformation": "מידע פרופיל",
"HeaderProfile": "פרופיל",
"HeaderPreferredMetadataLanguage": "שפת מטא-נתונים מועדפת",
"HeaderPreferredMetadataLanguage": "שפת מטא-דאטה מועדפת",
"HeaderPluginInstallation": "התקנת תוסף",
"HeaderPlayOn": "נגן על",
"HeaderPinCodeReset": "איפוס קוד סיכה",
@ -677,7 +677,7 @@
"EnableExternalVideoPlayers": "נגני וידאו חיצוניים",
"EnableCinemaMode": "מצב קולנוע",
"EnableBackdrops": "תמונות רקע",
"EditMetadata": "ערוך מטא-נתונים",
"EditMetadata": "ערוך מטא-דאטה",
"DrmChannelsNotImported": "‫ערוצים בעלי ניהול זכויות דיגיטלי (DRM) לא ייובאו.",
"Down": "למטה",
"Display": "תצוגה",
@ -726,7 +726,7 @@
"ButtonAddScheduledTaskTrigger": "הוסף טריגר",
"Browse": "עיין",
"BoxRear": "מארז (מאחור)",
"BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0}במדריך מתן שמות לספרים{1}.",
"BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0} במדריך מתן שמות לספרים {1}.",
"Desktop": "שולחן עבודה",
"MessageDeleteTaskTrigger": "האם אתה בטוח שברצונך למחוק את מפעיל המשימה הזה?",
"LastSeen": "נראה לאחרונה ב-{0}",
@ -738,11 +738,11 @@
"DeleteDeviceConfirmation": "האם אתה בטוח שברצונך למחוק את המכשיר? הוא יופיע שוב בפעם הבאה שמשתמש ייכנס באמצעותו.",
"ColorSpace": "מרחב צבע",
"CinemaModeConfigurationHelp": "מצב קולנוע מביא את חוויית הקולנוע היישר אל הסלון עם האפשרות להפעיל טריילרים וקדימונים מותאמים אישית לפני הסרט.",
"ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת \"מנהל המטא-דאטה\".",
"ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת מנהל המטא-דאטה.",
"ButtonResetEasyPassword": "אתחל קוד פין פשוט",
"ButtonOff": "כיבוי",
"ButtonLibraryAccess": "הרשאות גישה לספרייה",
"BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VOBSUB, PGS, SUB, IDX) וכתוביות ASS או SSA מסויימות.",
"BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VOBSUB, PGS, SUB, IDX, …) וכתוביות ASS או SSA מסויימות.",
"Artist": "אמן",
"AllowedRemoteAddressesHelp": "רשימת IP \\ מיסוך רשת המופרדת בפסיקים עבור רשתות שיורשו להתחבר מרחוק. במידה ותישאר ריקה, כל הכתובות יורשו להתחבר.",
"Album": "אלבום",
@ -764,5 +764,110 @@
"DashboardServerName": "שרת: {0}",
"DashboardVersionNumber": "גירסה: {0}",
"DashboardArchitecture": "ארכיטקטורה: {0}",
"DashboardOperatingSystem": "מערכת הפעלה: {0}"
"DashboardOperatingSystem": "מערכת הפעלה: {0}",
"HeaderMyMediaSmall": "המדיה שלי (קטן)",
"HeaderMusicQuality": "איכות מוזיקה",
"HeaderMediaInfo": "מידע על המדיה",
"HeaderMoreLikeThis": "עוד כמו זה",
"HeaderMedia": "מדיה",
"LabelMetadata": "מטא-דאטה:",
"HeaderSelectMetadataPath": "בחר נתיב מטא-דאטה",
"NextUp": "הבא בתור",
"LabelBaseUrl": "כתובת בסיס:",
"LabelEnableHttpsHelp": "האזן על גבי פורט ה-HTTPS המוגדר. חובה לספק תעודה תקינה על מנת שהגדרה זו תכנס לתוקף.",
"LabelEnableHttps": "הפעל HTTPS",
"LabelEnableHardwareDecodingFor": "הפעל פענוח חומרה עבור:",
"LabelEnableDlnaServerHelp": "אפשר למכשירי UPnP ברשת שלך לעיין בתוכן ולנגן אותו.",
"LabelEnableAutomaticPortMap": "הפעל מיפוי פורט אוטומטי",
"LabelDropImageHere": "גרור תמונה לכאן, או לחץ כדי לעיין.",
"LabelDownloadLanguages": "הורד שפות:",
"LabelDownMixAudioScale": "הגברת עוצמת שמע כאשר הוא ממוזג:",
"LabelDisplaySpecialsWithinSeasons": "הצג פרקים מיוחדים בתוך העונות שבמהלכן הם שודרו",
"LabelDisplayName": "שם תצוגה:",
"LabelDisplayMode": "מצב תצוגה:",
"LabelDisplayLanguageHelp": "תרגום Jellyfin הוא פרויקט מתמשך.",
"LabelDisplayLanguage": "שפת תצוגה:",
"LabelDidlMode": "מצב DIDL:",
"LabelDeviceDescription": "תיאור מכשיר",
"LabelDefaultScreen": "מסך ברירת-מחדל:",
"LabelCustomDeviceDisplayName": "שם תצוגה:",
"LabelImageType": "סוג תמונה:",
"LabelHttpsPortHelp": "מספר פורט ה-TCP עבור שרת ה-HTTPS.",
"LabelHttpsPort": "מספר פורט HTTPS מקומי:",
"LabelGroupMoviesIntoCollections": "אגד סרטים לתוך אוספים",
"LabelFriendlyName": "שם ידידותי:",
"LabelFormat": "תבנית:",
"LabelForgotPasswordUsernameHelp": "הכנס/י את שם המשתמש שלך, אם את/ה זוכר/ת אותו.",
"LabelFont": "גופן:",
"LabelFolder": "תיקייה:",
"LabelFileOrUrl": "קובץ או כתובת אינטרנט:",
"Season": "עונה",
"OptionEnableAccessFromAllDevices": "אפשר גישה מכל המכשירים",
"Primary": "ראשי",
"Menu": "תפריט",
"LiveTV": "שידורים חיים",
"ManageLibrary": "נהל ספרייה",
"Logo": "לוגו",
"OptionDateAddedImportTime": "השתמש בתאריך הסריקה לתוך הספרייה",
"OptionDateAddedFileTime": "השתמש בתאריך יצירת הקובץ",
"OptionBlockTrailers": "קדימונים",
"OptionBlockMusic": "מוזיקה",
"OptionBlockLiveTvChannels": "ערוצי שידורים חיים",
"OptionBlockBooks": "ספרים",
"OptionAllowRemoteSharedDevices": "אפשר שליטה מרחוק על מכשירים משותפים",
"OptionAllowRemoteControlOthers": "אפשר שליטה מרחוק על משתמשים אחרים",
"SelectAdminUsername": "נא לבחור שם משתמש עבור חשבון המנהל.",
"OptionHideUserFromLoginHelp": "שימושי עבור חשבונות פרטיים או חשבונות מנהל מוסתרים. המשתמש יצטרך להזין את שם המשתמש והסיסמה ידנית על מנת להתחבר.",
"MessagePlayAccessRestricted": "התוכן הזה לא ניתן לניגון כרגע. למידע נוסף, נא ליצור קשר עם מנהל המערכת שלך.",
"MessageContactAdminToResetPassword": "נא ליצור קשר עם מנהל המערכת שלך על מנת לאפס את הסיסמה שלך.",
"HeaderAdmin": "מנהל",
"TabDisplay": "תצוגה",
"HeaderDisplay": "תצוגה",
"Suggestions": "המלצות",
"MessageSyncPlayNoGroupsAvailable": "אין קבוצות זמינות. התחל לנגן משהו קודם.",
"OptionHomeVideos": "תמונות",
"Home": "בית",
"LabelServerName": "שם השרת:",
"TabPlugins": "תוספים",
"MessageNoPluginsInstalled": "אין לך תוספים מותקנים.",
"MessageNoAvailablePlugins": "אין תוספים זמינים.",
"TabLogs": "יומני רישום",
"LabelLogs": "יומני רישום:",
"TabNetworking": "תקשורת",
"TabDVR": "ממיר-מקליט",
"HeaderDVR": "ממיר-מקליט",
"LabelScheduledTaskLastRan": "רץ לאחרונה {0}, במשך {1}.",
"LabelTheme": "ערכת נושא:",
"LabelTextSize": "גודל טקסט:",
"LabelTextColor": "צבע טקסט:",
"LabelSyncPlayAccessNone": "מבוטל עבור משתמש זה",
"LabelSyncPlayAccessJoinGroups": "אפשר למשתמש להצטרף לקבוצות",
"LabelSyncPlayAccessCreateAndJoinGroups": "אפשר למשתמש ליצור קבוצות ולהצטרף אליהן",
"LabelSyncPlayLeaveGroup": "עזוב קבוצה",
"LabelSyncPlayNewGroupDescription": "צור קבוצה חדשה",
"LabelSyncPlayNewGroup": "קבוצה חדשה",
"MoreFromValue": "עוד מ{0}",
"Writers": "תסריטאים",
"DailyAt": "כל יום ב-{0}",
"OptionWeekends": "סופי שבוע",
"OptionWeekdays": "ימי חול",
"Unplayed": "לא נוגן",
"OptionSubstring": "מחרוזת משנה",
"OptionReleaseDate": "תאריך שחרור",
"OptionRegex": "ביטוי-רגולרי",
"OptionRandom": "אקראי",
"OptionPoster": "פוסטר",
"OptionNone": "כלום",
"OptionMax": "מקסימום",
"List": "רשימה",
"OptionList": "רשימה",
"OptionIsSD": "הבחנה רגילה (SD)",
"OptionIsHD": "הבחנה גבוהה (HD)",
"OptionExternallyDownloaded": "הורדה חיצונית",
"OptionEveryday": "כל יום",
"OptionEnableExternalContentInSuggestions": "הפעל תוכן חיצוני בהמלצות",
"OptionEnableAccessToAllLibraries": "אפשר גישה לכל הספריות",
"OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים",
"HeaderSyncPlaySelectGroup": "הצטרף לקבוצה",
"TabUsers": "משתמשים"
}

View file

@ -175,8 +175,8 @@
"DetectingDevices": "Обнаруживются устройства",
"DeviceAccessHelp": "Это относится только к устройствам, которые могут быть однозначно распознаны и не препятствует доступу через браузер. Фильтрация доступа пользовательского устройства запретит использование новых устройств до тех пор, пока они не будут одобрены.",
"DirectPlaying": "Воспроизводится напрямую",
"DirectStreamHelp1": "Медиаданные совместимы с устройством в отношении разрешения и типа медиаданных (H.264, AC3, и т.д.), но в несовместимом файловом контейнере (mkv, avi, wmv и т.д.). Видео будет повторно упаковано динамически перед его трансляцией на устройство.",
"DirectStreamHelp2": "При прямой трансляции файла расходуется очень мало вычислительной мощности без потери качества видео.",
"DirectStreamHelp1": "Медиаданные совместимы с устройством в отношении разрешения и типа медиаданных (H.264, AC3, и т.д.), но в несовместимом файловом контейнере (mkv, avi, wmv и т.д.). Видео будет повторно упаковано динамически перед его отправлением на устройство.",
"DirectStreamHelp2": "Прямая трансляции расходует очень мало вычислительной мощности с минимальной потерей качества видео.",
"DirectStreaming": "Транслируется напрямую",
"Director": "Режиссёр",
"Directors": "Режиссёры",
@ -287,7 +287,7 @@
"HeaderAllowMediaDeletionFrom": "Разрешить удаление медиаданных из",
"HeaderApiKey": "API-ключ",
"HeaderApiKeys": "API-ключи",
"HeaderApiKeysHelp": "Внешним приложениям требуется API-ключ для того, чтобы подключиться к Jellyfin Server. Ключи выдаются при входе с учётной записью Jellyfin или ключ предоставляется приложению вручную.",
"HeaderApiKeysHelp": "Внешним приложениям требуется API-ключ для того, чтобы подключиться к серверу. Ключи выдаются при входе с учётной записью обычного пользователя или ключ предоставляется приложению вручную.",
"HeaderApp": "Приложение",
"HeaderAppearsOn": "Фигурирует в",
"HeaderAudioBooks": "Аудиокниги",
@ -359,7 +359,7 @@
"HeaderItems": "Элементы",
"HeaderKeepRecording": "Хранение записи",
"HeaderKeepSeries": "Хранение сериала",
"HeaderKodiMetadataHelp": "Для включения или отключения NFO-метаданных, начните править медиатеку в области настройки медиатек и найдите раздел хранителей метаданных.",
"HeaderKodiMetadataHelp": "Для включения или отключения NFO-метаданных, правьте медиатеку и найдите раздел хранителей метаданных.",
"HeaderLatestEpisodes": "Новейшие эпизоды",
"HeaderLatestMedia": "Новейшие медиаданные",
"HeaderLatestMovies": "Новейшие фильмы",
@ -408,7 +408,7 @@
"HeaderPreferredMetadataLanguage": "Выбор языка метаданных",
"HeaderProfile": "Профиль",
"HeaderProfileInformation": "О профиле",
"HeaderProfileServerSettingsHelp": "Данные значения управляют тем, как Jellyfin Server будет представляться устройству.",
"HeaderProfileServerSettingsHelp": "Данные значения управляют тем, как сервер будет представлять себя клиентам.",
"HeaderRecentlyPlayed": "Воспроизведённые недавно",
"HeaderRecordingOptions": "Опции записи",
"HeaderRecordingPostProcessing": "Постобработка записи",
@ -426,13 +426,13 @@
"HeaderSecondsValue": "{0} с",
"HeaderSelectCertificatePath": "Выбор пути к сертификату",
"HeaderSelectMetadataPath": "Выбор пути для метаданных",
"HeaderSelectMetadataPathHelp": "Найдите или введите путь, в пределах которого хотите хранить метаданные. Папка должна быть доступна для записи.",
"HeaderSelectMetadataPathHelp": "Найдите или введите путь, который хотите использовать для метаданныхе. Папка должна быть доступна для записи.",
"HeaderSelectPath": "Выбор пути",
"HeaderSelectServer": "Выбор сервера",
"HeaderSelectServerCachePath": "Выбор пути для серверного кэша",
"HeaderSelectServerCachePathHelp": "Найдите или введите путь, чтобы использовать для файлов серверного кэша. Папка должна быть доступна для записи.",
"HeaderSelectTranscodingPath": "Выбор пути для временных файлов перекодировки",
"HeaderSelectTranscodingPathHelp": "Найдите или введите путь, чтобы использовать для временных файлов перекодировки. Папка должна быть доступна для записи.",
"HeaderSelectTranscodingPathHelp": "Найдите или введите путь, чтобы использовать для файлов перекодировки. Папка должна быть доступна для записи.",
"HeaderSendMessage": "Передача сообщения",
"HeaderSeries": "Сериалы",
"HeaderSeriesOptions": "Опции сериала",
@ -485,8 +485,8 @@
"HttpsRequiresCert": "Чтобы включить HTTPS для внешних подключений, вам нужно будет предоставить доверенный SSL-cертификат, например, Let's Encrypt. Предоставьте сертификат или отключите защищенные соединения.",
"Identify": "Распознать",
"Images": "Изображения",
"ImportFavoriteChannelsHelp": "При включении, будут импортированы только каналы, которые обозначены как избранное на тюнерном устройстве.",
"ImportMissingEpisodesHelp": "При включении, информация об отсутствующих эпизодах будет импортирована в вашу базу данных Jellyfin и отображаться в пределах сезонов и сериалов. Это может увеличить время сканирования медиатеки.",
"ImportFavoriteChannelsHelp": "Будут импортированы только каналы, которые обозначены как избранное на тюнерном устройстве.",
"ImportMissingEpisodesHelp": "Информация об отсутствующих эпизодах будет импортирована в вашу базу данных и отображена в пределах сезонов и сериалов. Это может заметно увеличить время сканирования медиатеки.",
"InstallingPackage": "Устанавливается {0} (версия {1})",
"InstantMix": "Автомикс",
"ItemCount": "{0} элемент(а/ов)",
@ -517,15 +517,15 @@
"LabelAppName": "Название приложения",
"LabelAppNameExample": "Пример: Sickbeard, Sonarr",
"LabelArtists": "Исполнители:",
"LabelArtistsHelp": "Для разделения используйте точку с запятой ;",
"LabelArtistsHelp": "Для разделения исполнителей используйте точку с запятой ;",
"LabelAudio": "Аудио",
"LabelAudioLanguagePreference": "Выбор языка аудио:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "Автоматически обновлять метаданные из Интернета:",
"LabelBindToLocalNetworkAddress": "Привязка к адресу в локальной сети:",
"LabelBindToLocalNetworkAddressHelp": "Необязательно. Переопределяется локальный IP-адрес для привязки HTTP-сервера. Если поле пусто, то привязка сервера будет ко всем доступным адресам. При изменении данного значения потребуется перезапуск Jellyfin Server.",
"LabelBindToLocalNetworkAddressHelp": "Переопределяется локальный IP-адрес для HTTP-сервера. Если поле пусто, то привязка сервера будет ко всем доступным адресам. При изменении данного значения потребуется перезапуск Jellyfin Server.",
"LabelBirthDate": "Дата рождения:",
"LabelBirthYear": "Год рождения:",
"LabelBlastMessageInterval": "Интервал сообщений проверки активности, с",
"LabelBlastMessageInterval": "Интервал сообщений проверки активности",
"LabelBlastMessageIntervalHelp": "Определяет длительность в секундах между бомбардированием сообщениями проверки активности.",
"LabelBlockContentWithTags": "Блокирование элементов с тегами:",
"LabelBurnSubtitles": "Внедрение субтитров:",
@ -545,7 +545,7 @@
"LabelCustomCertificatePath": "Путь к пользовательскому SSL-сертификату:",
"LabelCustomCertificatePathHelp": "Путь к файлу PKCS #12, содержащему сертификат и \tзакрытый ключ для включения поддержки TLS на произвольном домене.",
"LabelCustomCss": "Настраиваемые CSS:",
"LabelCustomCssHelp": "Применяйте свою собственную настраиваемую стилизацию к веб-интерфейсу.",
"LabelCustomCssHelp": "Применяйте свои собственные настраиваемые стили в веб-интерфейсе.",
"LabelCustomDeviceDisplayName": "Отображаемое название:",
"LabelCustomDeviceDisplayNameHelp": "Приведите произвольное имя для отображения или не заполняйте, чтобы использовать имя, выданное устройством.",
"LabelCustomRating": "Произвольная возрастная категория:",
@ -585,7 +585,7 @@
"LabelEnableDlnaDebugLogging": "Включить журналирование отладки DLNA",
"LabelEnableDlnaDebugLoggingHelp": "Создаются большие файлы Журнала, рекомендуется использовать только для поиска неполадок.",
"LabelEnableDlnaPlayTo": "Включить DLNA-функцию Воспроизвести На",
"LabelEnableDlnaPlayToHelp": "Обнаруживаются устройства внутри своей сети, а также предоставляется возможность удалённо управлять ими.",
"LabelEnableDlnaPlayToHelp": "Обнаруживаются устройства внутри своей сети, а также предоставляется возможность управлять ими удалённо.",
"LabelEnableDlnaServer": "Включить DLNA-сервер",
"LabelEnableDlnaServerHelp": "Для UPnP-устройств домашней сети возможна навигация по содержанию и его воспроизведение.",
"LabelEnableHardwareDecodingFor": "Включить аппаратное декодирование для:",
@ -606,9 +606,9 @@
"LabelForgotPasswordUsernameHelp": "Введите имя пользователя, если помните его.",
"LabelFormat": "Формат:",
"LabelFriendlyName": "Понятное имя:",
"LabelServerNameHelp": "Это имя используется для распознавания сервера и будет по умолчанию именем компьютера.",
"LabelServerNameHelp": "Это имя используется для распознавания сервера и будет по умолчанию именем хоста сервера.",
"LabelGroupMoviesIntoCollections": "Группировать фильмы внутрь коллекций",
"LabelGroupMoviesIntoCollectionsHelp": "При отображении списка фильмов, элементы, принадлежащие к одной коллекции будут отображаться как единый сгруппированный элемент.",
"LabelGroupMoviesIntoCollectionsHelp": "При отображении списков фильмов, фильмы из коллекции будут отображаться как единый группированный элемент.",
"LabelH264Crf": "Значение CRF H264-кодирования:",
"LabelEncoderPreset": "Предустановка H264-кодирования:",
"LabelHardwareAccelerationType": "Аппаратное ускорение:",
@ -616,7 +616,7 @@
"LabelHomeNetworkQuality": "Качество в домашней сети:",
"LabelHomeScreenSectionValue": "Главная страница - раздел {0}:",
"LabelHttpsPort": "Номер локального HTTPS-порта:",
"LabelHttpsPortHelp": "TCP-порт, ко которому следует создать привязку HTTPS-сервера Jellyfin.",
"LabelHttpsPortHelp": "Номер TCP-порта для HTTPS-сервера.",
"LabelIconMaxHeight": "Макс. высота значка:",
"LabelIconMaxHeightHelp": "Максимальное разрешение значков представляемых с помощью upnp:icon.",
"LabelIconMaxWidth": "Макс. ширина значка:",
@ -644,7 +644,7 @@
"LabelLanguage": "Язык:",
"LabelLineup": "Список сопоставления:",
"LabelLocalHttpServerPortNumber": "Номер локального HTTP-порта:",
"LabelLocalHttpServerPortNumberHelp": "TCP-порт, ко которому следует создать привязку HTTP-сервера Jellyfin.",
"LabelLocalHttpServerPortNumberHelp": "Номер TCP-порта для HTTP-сервера.",
"LabelLockItemToPreventChanges": "Зафиксировать данный элемент, чтобы запретить будущие правки",
"LabelLoginDisclaimer": "Предупреждение при входе:",
"LabelLoginDisclaimerHelp": "Сообщение будет отображаться в нижней части страницы входа в систему.",
@ -670,7 +670,7 @@
"LabelMetadataReaders": "Считыватели метаданных:",
"LabelMetadataReadersHelp": "Ранжируйте предпочитаемые локальные источники метаданных в порядке приоритета. Будет считан первый же найденный файл.",
"LabelMetadataSavers": "Хранители метаданных:",
"LabelMetadataSaversHelp": "Выберите форматы файлов, куда будут сохраняться метаданные.",
"LabelMetadataSaversHelp": "Выберите форматы файлов, которые будут использоваться при сохранении метаданных.",
"LabelMethod": "Метод:",
"LabelMinBackdropDownloadWidth": "Минимальная ширина загружаемого фона:",
"LabelMinResumeDuration": "Минимальная длительность для возобновления:",
@ -686,7 +686,7 @@
"LabelMovieCategories": "Фильмовые категории:",
"LabelMoviePrefix": "Префикс фильма:",
"LabelMoviePrefixHelp": "При применении к названиям фильмов префикса, введите его здесь, чтобы он правильно обрабатывался на сервере.",
"LabelMovieRecordingPath": "Путь к записываемым фильмам (необязательно):",
"LabelMovieRecordingPath": "Путь к записываемым фильмам:",
"LabelMusicStreamingTranscodingBitrate": "Битрейт перекодировки музыки:",
"LabelMusicStreamingTranscodingBitrateHelp": "Укажите максимальный битрейт при трансляции музыки.",
"LabelName": "Имя:",
@ -699,7 +699,7 @@
"LabelNumber": "Номер:",
"LabelNumberOfGuideDays": "Число дней для загрузки данных телегида:",
"LabelNumberOfGuideDaysHelp": "Больше дней загрузки данных телегида обеспечивает возможность заблаговременно назначать расписание и просматривать больше перечней, однако это займёт больше времени для загрузки. При значении «Авто» выбор определяется числом каналов.",
"LabelOptionalNetworkPath": "(Необязательно) Общедоступная сетевая папка:",
"LabelOptionalNetworkPath": "Общедоступная сетевая папка:",
"LabelOptionalNetworkPathHelp": "Если данная папка является общей в сети, указание пути к сетевой папке может позволить Jellyfin-приложениям на других устройствах иметь прямой доступ к медиафайлам. Например, {0} или {1}.",
"LabelOriginalAspectRatio": "Исходное соотношение сторон:",
"LabelOriginalTitle": "Оригинальное название:",
@ -744,7 +744,7 @@
"LabelReleaseDate": "Дата выпуска:",
"LabelRemoteClientBitrateLimit": "Ограничение битрейта интернет-трансляции, Мбит/с:",
"LabelRemoteClientBitrateLimitHelp": "Необязательное ограничение битрейта для каждого из сетевых устройств. Может потребоваться, чтобы не допускать использования устройствами большего битрейта, чем способно пропустить интернет-соединение. Может привести к росту загрузки процессора на вашем сервере, так как потребуется динамическое перекодирование видео для снижения битрейта.",
"LabelRuntimeMinutes": "Длительность, мин:",
"LabelRuntimeMinutes": "Длительность:",
"LabelSaveLocalMetadata": "Сохранять иллюстрации внутри медиапапок",
"LabelSaveLocalMetadataHelp": "При сохранении иллюстраций внутри медиапапок, те помещаются в месте, где их можно легко править.",
"LabelScheduledTaskLastRan": "Последний запуск был {0}, занял {1}.",
@ -756,7 +756,7 @@
"LabelSelectVersionToInstall": "Выбрать версию для установки:",
"LabelSendNotificationToUsers": "Передача уведомления для:",
"LabelSerialNumber": "Серийный номер",
"LabelSeriesRecordingPath": "Путь к записываемым сериалам (необязательно):",
"LabelSeriesRecordingPath": "Путь к записываемым сериалам:",
"LabelServerHost": "Узел:",
"LabelServerHostHelp": "192.168.1.100:8096 или https://myserver.com",
"LabelSimultaneousConnectionLimit": "Лимит одновременных потоков:",
@ -917,7 +917,7 @@
"MessageYouHaveVersionInstalled": "В настоящее время установлена версия {0}.",
"Metadata": "Метаданные",
"MetadataManager": "Дисп. метаданных",
"MetadataSettingChangeHelp": "Изменение параметров метаданных повлияет на новое содержание, которое будет добавляться в дальнейшем. Чтобы обновить существующие содержание, откройте экран с подробностями и нажмите кнопку Обновить, или выполните массовое обновление, с помощью Диспетчера метаданных.",
"MetadataSettingChangeHelp": "Изменение параметров метаданных повлияет на новое содержание, добавляемое в будущем.. Чтобы обновить существующие содержание, откройте экран с подробностями и нажмите кнопку Обновить, или выполните массовое обновление, с помощью Диспетчера метаданных.",
"MinutesAfter": "минут(у/ы) после",
"MinutesBefore": "минут(у/ы) до",
"Mobile": "Мобильный",
@ -966,7 +966,7 @@
"OptionAllowLinkSharingHelp": "Общедоступны только веб-страницы содержащие сведения о медиаданных. Медиафайлы никогда не предоставляются для общего просмотра. Совместно используемые ресурсы ограничены во времени, а срок действия истекает через {0} дн(я/ей).",
"OptionAllowManageLiveTv": "Разрешить управление эфирными записями",
"OptionAllowMediaPlayback": "Разрешить воспроизведение медиаданных",
"OptionAllowMediaPlaybackTranscodingHelp": "Ограничение доступа к перекодировке может привести к сбоям воспроизведения в Jellyfin-приложениях из-за неподдерживаемых форматов носителей.",
"OptionAllowMediaPlaybackTranscodingHelp": "Ограничение доступа к перекодировке может привести к сбоям воспроизведения в клиентах из-за неподдерживаемых форматов носителей.",
"OptionAllowRemoteControlOthers": "Разрешить удалённое управление другими пользователями",
"OptionAllowRemoteSharedDevices": "Разрешить удалённое управление используемыми совместно устройствами",
"OptionAllowRemoteSharedDevicesHelp": "DLNA-устройства считаются используемыми совместно, пока какой-либо пользователь не начнёт управлять ими.",
@ -979,7 +979,7 @@
"OptionAuto": "Авто",
"OptionAutomatic": "Авто",
"OptionAutomaticallyGroupSeries": "Автоматически сливать вместе сериалы, которые разбросаны по нескольким папкам",
"OptionAutomaticallyGroupSeriesHelp": "При включении, сериалы, которые разбросаны по нескольким папкам данной медиатеки, будут автоматически слиты в единый сериал.",
"OptionAutomaticallyGroupSeriesHelp": "Части сериала, которые разбросаны по нескольким папкам данной медиатеки, будут автоматически слиты в единый сериал.",
"OptionBlockBooks": "Книги",
"OptionBlockChannelContent": "Содержание интернет-канала",
"OptionBlockLiveTvChannels": "Эфирные каналы",
@ -999,7 +999,7 @@
"OptionDatePlayed": "Дата воспроизведения",
"OptionDescending": "По убыванию",
"OptionDisableUser": "Заблокировать пользователя",
"OptionDisableUserHelp": "При блокировании, этому пользователю не разрешаются любые подключения к серверу. Имеющиеся соединения будут разорваны.",
"OptionDisableUserHelp": "Этому пользователю не разрешаются любые подключения к серверу. Имеющиеся соединения будут разорваны.",
"OptionDislikes": "Не нравящиеся",
"OptionDisplayFolderView": "Отображать аспект Папки для просмотра обычных медиапапок",
"OptionDisplayFolderViewHelp": "Отображение аспекта \"Папки\" рядом с другими вашими медиатеками. Это может быть полезно, если вы хотите вид обычных папок.",
@ -1009,7 +1009,7 @@
"OptionDownloadBoxImage": "DVD-бокс",
"OptionDownloadDiscImage": "Диск",
"OptionDownloadImagesInAdvance": "Загружать изображения заблаговременно",
"OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство изображений загружаются только при запросе от Jellyfin-приложения. Включите данную опцию, чтобы загружать все изображения заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.",
"OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство изображений загружаются только при запросе от клиента. Включите данную опцию, чтобы загружать все изображения заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.",
"OptionDownloadLogoImage": "Логотип",
"OptionDownloadMenuImage": "Меню",
"OptionDownloadPrimaryImage": "Основной",
@ -1042,7 +1042,7 @@
"OptionHlsSegmentedSubtitles": "Сегмент. субтитры HLS",
"OptionHomeVideos": "Фотографии",
"OptionIgnoreTranscodeByteRangeRequests": "Игнорировать запросы диапазона байтов перекодировки",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "При включении, эти запросы будут учтены, но заголовок диапазона байтов будет проигнорирован.",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Эти запросы будут учтены, но заголовок диапазона байтов будет проигнорирован.",
"OptionImdbRating": "Оценка IMDb",
"OptionLikes": "Нравящиеся",
"OptionMax": "Макс.",
@ -1055,9 +1055,9 @@
"OptionOnInterval": "В интервале",
"OptionParentalRating": "Возрастная категория",
"OptionPlainStorageFolders": "Отображать все папки, как обычные папки хранения",
"OptionPlainStorageFoldersHelp": "При включении, все папки описываются в DIDL как «object.container.storageFolder», вместо более конкретного типа, например, «object.container.person.musicArtist».",
"OptionPlainStorageFoldersHelp": "Все папки описываются в DIDL как «object.container.storageFolder», вместо более специфичного типа, например, «object.container.person.musicArtist».",
"OptionPlainVideoItems": "Отображать все видео, как обычные видео элементы",
"OptionPlainVideoItemsHelp": "При включении, все видео описываются в DIDL как «object.item.videoItem», вместо более конкретного типа, например, «object.item.videoItem.movie».",
"OptionPlainVideoItemsHelp": "Все видео описываются в DIDL как «object.item.videoItem», вместо более специфичного типа, например, «object.item.videoItem.movie».",
"OptionPlayCount": "Кол. воспроизведений",
"OptionPlayed": "Воспроизведённые",
"OptionPremiereDate": "Дата премьеры",
@ -1148,7 +1148,7 @@
"RecordingScheduled": "Запись назначена.",
"Recordings": "Записи",
"Refresh": "Обновить",
"RefreshDialogHelp": "Обновление метаданных определяются параметрами и интернет-услугами, которые включены в Панели Jellyfin Server.",
"RefreshDialogHelp": "Обновление метаданных определяются параметрами и интернет-услугами, которые включены в Панели.",
"RefreshMetadata": "Обновить метаданные",
"RefreshQueued": "Обновление в очереди.",
"ReleaseDate": "Дата выпуска",
@ -1395,11 +1395,11 @@
"OptionPosterCard": "Постер-карта",
"OptionThumb": "Эскиз",
"OptionThumbCard": "Эскиз-карта",
"PasswordResetProviderHelp": "Выберите поставщика сброса пароля, который будет использоваться, когда этот пользователь запрашивает сброс пароля",
"PasswordResetProviderHelp": "Выберите поставщика сброса пароля, который использовуется при запросе пользователем сброса пароля",
"PlaybackData": "Данные воспроизведения",
"SubtitleOffset": "Сдвиг субтитров",
"TabNetworking": "Работа в сети",
"LabelBaseUrlHelp": "Добавляется пользовательский подкаталог к URL сервера. Например: <code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelBaseUrlHelp": "Добавляет пользовательский подкаталог к URL сервера. Например: <code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelPlayer": "Проигрыватель:",
"MoreMediaInfo": "О медиаданных",
"LabelVideoCodec": "Видео кодек:",
@ -1472,7 +1472,7 @@
"UnsupportedPlayback": "Jellyfin не может расшифровать содержимое, защищенное DRM, но в любом случае будет предпринята попытка расшифровки всего содержимого, включая защищенные заголовки. Некоторые файлы могут выглядеть полностью черными из-за шифрования или других неподдерживаемых функций, таких как интерактивные заголовки.",
"HeaderFavoritePlaylists": "Избранные плей-листы",
"LabelRequireHttpsHelp": "Если этот флажок установлен, сервер будет автоматически перенаправлять все запросы через HTTP на HTTPS. Это не имеет никакого эффекта, если сервер не слушает HTTPS.",
"LabelEnableHttpsHelp": озволяет серверу слушать HTTPS-порт. Для работы необходим действующий сертификат.",
"LabelEnableHttpsHelp": рослушивается указанный HTTPS-порт. Чтобы это вступило в силу, также необходимо предоставить действительный сертификат.",
"ApiKeysCaption": "Список действующих текущих API-ключей",
"TabDVR": "DVR",
"SaveChanges": "Сохранить изменения",
@ -1521,7 +1521,7 @@
"EnableBlurHashHelp": "Рисунки, которые всё ещё загружаются, будут отображаться с размытым заполнением",
"EnableBlurHash": "Включить размытые заполнители для изображений",
"ButtonSyncPlay": "SyncPlay",
"ButtonCast": "В ролях",
"ButtonCast": "Транслировать",
"TabRepositories": "Репозитории",
"MessageNoGenresAvailable": "Разрешить поставщикам метаданных получать жанры из интернета.",
"MessageAddRepository": "Если вы хотите добавить репозиторий, нажмите кнопку рядом с заголовком и заполните необходимую информацию.",
@ -1539,5 +1539,11 @@
"Writers": "Сценаристы",
"ViewAlbumArtist": "Посмотреть альбом исполнителя",
"ClearQueue": "Очистить очередь",
"ButtonPlayer": "Проигрыватель"
"ButtonPlayer": "Проигрыватель",
"PreviousTrack": "Перейти к предыдущему",
"NextTrack": "Перейти к следующему",
"LabelUnstable": "Нестабильная",
"LabelSubtitleVerticalPosition": "Вертикальная позиция:",
"SubtitleVerticalPositionHelp": "Номер строки, где появляется текст. Положительные числа означают сверху вниз. Отрицательные числа означают снизу вверх.",
"Preview": "Предварительный просмотр"
}

View file

@ -3020,10 +3020,10 @@ css-has-pseudo@^0.10.0:
postcss "^7.0.6"
postcss-selector-parser "^5.0.0-rc.4"
css-loader@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.0.tgz#b57efb92ac8f0cd85bf92d89df9634ef1f51b8bf"
integrity sha512-ko7a9b0iFpWtk9eSI/C8IICvZeGtYnjxYjw45rJprokXj/+kBd/siX4vAIBq9Uij8Jubc4jL1EvSnTjCEwaHSw==
css-loader@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.1.tgz#9f48fd7eae1219d629a3f085ba9a9102ca1141a7"
integrity sha512-MoqmF1if7Z0pZIEXA4ZF9PgtCXxWbfzfJM+3p+OYfhcrwcqhaCRb74DSnfzRl7e024xEiCRn5hCvfUbTf2sgFA==
dependencies:
camelcase "^6.0.0"
cssesc "^3.0.0"