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

This commit is contained in:
Cameron 2020-08-10 22:17:58 +01:00 committed by GitHub
commit a0a1fafc9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 6248 additions and 6033 deletions

View file

@ -6,8 +6,8 @@
"license": "GPL-2.0-or-later",
"devDependencies": {
"@babel/core": "^7.11.1",
"@babel/eslint-parser": "^7.11.0",
"@babel/eslint-plugin": "^7.11.0",
"@babel/eslint-parser": "^7.11.3",
"@babel/eslint-plugin": "^7.11.3",
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-proposal-private-methods": "^7.10.1",
"@babel/plugin-transform-modules-amd": "^7.10.5",
@ -63,7 +63,7 @@
"fast-text-encoding": "^1.0.3",
"flv.js": "^1.5.0",
"headroom.js": "^0.11.0",
"hls.js": "^0.14.7",
"hls.js": "^0.14.8",
"howler": "^2.2.0",
"intersection-observer": "^0.11.0",
"jellyfin-apiclient": "^1.4.1",
@ -80,7 +80,7 @@
"sortablejs": "^1.10.2",
"swiper": "^5.4.5",
"webcomponents.js": "^0.7.24",
"whatwg-fetch": "^3.3.1"
"whatwg-fetch": "^3.4.0"
},
"babel": {
"presets": [
@ -112,6 +112,8 @@
"src/components/filterdialog/filterdialog.js",
"src/components/focusManager.js",
"src/components/groupedcards.js",
"src/components/guide/guide.js",
"src/components/guide/guide-settings.js",
"src/components/homeScreenSettings/homeScreenSettings.js",
"src/components/homesections/homesections.js",
"src/components/htmlMediaHelper.js",
@ -125,6 +127,8 @@
"src/components/itemHelper.js",
"src/components/itemidentifier/itemidentifier.js",
"src/components/itemMediaInfo/itemMediaInfo.js",
"src/components/itemsrefresher.js",
"src/components/layoutManager.js",
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
"src/components/libraryoptionseditor/libraryoptionseditor.js",
"src/components/listview/listview.js",
@ -153,24 +157,37 @@
"src/components/playlisteditor/playlisteditor.js",
"src/components/playmenu.js",
"src/components/prompt/prompt.js",
"src/components/recordingcreator/recordingbutton.js",
"src/components/recordingcreator/recordingcreator.js",
"src/components/recordingcreator/seriesrecordingeditor.js",
"src/components/recordingcreator/recordinghelper.js",
"src/components/refreshdialog/refreshdialog.js",
"src/components/remotecontrol/remotecontrol.js",
"src/components/sanatizefilename.js",
"src/components/scrollManager.js",
"src/plugins/experimentalWarnings/plugin.js",
"src/plugins/htmlVideoPlayer/plugin.js",
"src/plugins/sessionPlayer/plugin.js",
"src/plugins/htmlAudioPlayer/plugin.js",
"src/plugins/chromecastPlayer/plugin.js",
"src/components/slideshow/slideshow.js",
"src/components/sortmenu/sortmenu.js",
"src/plugins/htmlVideoPlayer/plugin.js",
"src/plugins/logoScreensaver/plugin.js",
"src/plugins/playAccessValidation/plugin.js",
"src/components/search/searchfields.js",
"src/components/search/searchresults.js",
"src/components/settingshelper.js",
"src/components/shortcuts.js",
"src/components/subtitleeditor/subtitleeditor.js",
"src/components/subtitlesync/subtitlesync.js",
"src/components/subtitlesettings/subtitleappearancehelper.js",
"src/components/subtitlesettings/subtitlesettings.js",
"src/components/syncPlay/groupSelectionMenu.js",
"src/components/syncPlay/playbackPermissionManager.js",
"src/components/syncPlay/syncPlayManager.js",
"src/components/syncPlay/timeSyncManager.js",
"src/components/tabbedview/tabbedview.js",
"src/components/viewManager/viewManager.js",
"src/components/tvproviders/schedulesdirect.js",
"src/components/tvproviders/xmltv.js",
@ -200,13 +217,16 @@
"src/controllers/music/musicplaylists.js",
"src/controllers/music/musicrecommended.js",
"src/controllers/music/songs.js",
"src/controllers/dashboard/mediaLibrary.js",
"src/controllers/dashboard/library.js",
"src/controllers/dashboard/metadataImages.js",
"src/controllers/dashboard/metadatanfo.js",
"src/controllers/dashboard/networking.js",
"src/controllers/dashboard/notifications/notification.js",
"src/controllers/dashboard/notifications/notifications.js",
"src/controllers/dashboard/playback.js",
"src/controllers/dashboard/plugins/add/index.js",
"src/controllers/dashboard/plugins/installed/index.js",
"src/controllers/dashboard/plugins/available/index.js",
"src/controllers/dashboard/plugins/repositories/index.js",
"src/controllers/dashboard/scheduledtasks/scheduledtask.js",
"src/controllers/dashboard/scheduledtasks/scheduledtasks.js",
@ -218,6 +238,8 @@
"src/controllers/dashboard/users/userparentalcontrol.js",
"src/controllers/dashboard/users/userpasswordpage.js",
"src/controllers/dashboard/users/userprofilespage.js",
"src/controllers/home.js",
"src/controllers/list.js",
"src/controllers/edititemmetadata.js",
"src/controllers/favorites.js",
"src/controllers/hometab.js",
@ -232,6 +254,7 @@
"src/controllers/playback/queue/index.js",
"src/controllers/playback/video/index.js",
"src/controllers/searchpage.js",
"src/controllers/livetv/livetvguide.js",
"src/controllers/livetvtuner.js",
"src/controllers/livetvstatus.js",
"src/controllers/livetvguideprovider.js",
@ -278,6 +301,7 @@
"src/elements/emby-tabs/emby-tabs.js",
"src/elements/emby-textarea/emby-textarea.js",
"src/elements/emby-toggle/emby-toggle.js",
"src/libraries/screensavermanager.js",
"src/libraries/navdrawer/navdrawer.js",
"src/libraries/scroller.js",
"src/plugins/backdropScreensaver/plugin.js",
@ -297,11 +321,14 @@
"src/scripts/filesystem.js",
"src/scripts/globalize.js",
"src/scripts/imagehelper.js",
"src/scripts/itembynamedetailpage.js",
"src/scripts/inputManager.js",
"src/scripts/autoThemes.js",
"src/scripts/themeManager.js",
"src/scripts/keyboardNavigation.js",
"src/scripts/libraryMenu.js",
"src/scripts/libraryBrowser.js",
"src/scripts/livetvcomponents.js",
"src/scripts/mouseManager.js",
"src/scripts/multiDownload.js",
"src/scripts/playlists.js",

View file

@ -2,6 +2,8 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost',
'use strict';
focusManager = focusManager.default || focusManager;
layoutManager = layoutManager.default || layoutManager;
function onSubmit(e) {
e.preventDefault();
return false;

View file

@ -1,16 +1,21 @@
define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-radio', 'css!./../formdialog', 'material-icons'], function (dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) {
'use strict';
import dialogHelper from 'dialogHelper';
import globalize from 'globalize';
import * as userSettings from 'userSettings';
import layoutManager from 'layoutManager';
import scrollHelper from 'scrollHelper';
import 'emby-checkbox';
import 'emby-radio';
import 'css!./../formdialog';
import 'material-icons';
scrollHelper = scrollHelper.default || scrollHelper;
function saveCategories(context, options) {
const categories = [];
function saveCategories(context, options) {
var categories = [];
const chkCategorys = context.querySelectorAll('.chkCategory');
for (const chkCategory of chkCategorys) {
const type = chkCategory.getAttribute('data-type');
var chkCategorys = context.querySelectorAll('.chkCategory');
for (var i = 0, length = chkCategorys.length; i < length; i++) {
var type = chkCategorys[i].getAttribute('data-type');
if (chkCategorys[i].checked) {
if (chkCategory.checked) {
categories.push(type);
}
}
@ -22,73 +27,69 @@ define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectio
// differentiate between none and all
categories.push('all');
options.categories = categories;
}
function loadCategories(context, options) {
const selectedCategories = options.categories || [];
const chkCategorys = context.querySelectorAll('.chkCategory');
for (const chkCategory of chkCategorys) {
const type = chkCategory.getAttribute('data-type');
chkCategory.checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1;
}
}
function loadCategories(context, options) {
var selectedCategories = options.categories || [];
function save(context) {
const chkIndicators = context.querySelectorAll('.chkIndicator');
var chkCategorys = context.querySelectorAll('.chkCategory');
for (var i = 0, length = chkCategorys.length; i < length; i++) {
var type = chkCategorys[i].getAttribute('data-type');
chkCategorys[i].checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1;
}
}
function save(context) {
var i;
var length;
var chkIndicators = context.querySelectorAll('.chkIndicator');
for (i = 0, length = chkIndicators.length; i < length; i++) {
var type = chkIndicators[i].getAttribute('data-type');
userSettings.set('guide-indicator-' + type, chkIndicators[i].checked);
for (const chkIndicator of chkIndicators) {
const type = chkIndicator.getAttribute('data-type');
userSettings.set('guide-indicator-' + type, chkIndicator.checked);
}
userSettings.set('guide-colorcodedbackgrounds', context.querySelector('.chkColorCodedBackgrounds').checked);
userSettings.set('livetv-favoritechannelsattop', context.querySelector('.chkFavoriteChannelsAtTop').checked);
var sortBys = context.querySelectorAll('.chkSortOrder');
for (i = 0, length = sortBys.length; i < length; i++) {
if (sortBys[i].checked) {
userSettings.set('livetv-channelorder', sortBys[i].value);
const sortBys = context.querySelectorAll('.chkSortOrder');
for (const sortBy of sortBys) {
if (sortBy.checked) {
userSettings.set('livetv-channelorder', sortBy.value);
break;
}
}
}
}
function load(context) {
var i;
var length;
function load(context) {
const chkIndicators = context.querySelectorAll('.chkIndicator');
var chkIndicators = context.querySelectorAll('.chkIndicator');
for (i = 0, length = chkIndicators.length; i < length; i++) {
var type = chkIndicators[i].getAttribute('data-type');
for (const chkIndicator of chkIndicators) {
const type = chkIndicator.getAttribute('data-type');
if (chkIndicators[i].getAttribute('data-default') === 'true') {
chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) !== 'false';
if (chkIndicator.getAttribute('data-default') === 'true') {
chkIndicator.checked = userSettings.get('guide-indicator-' + type) !== 'false';
} else {
chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) === 'true';
chkIndicator.checked = userSettings.get('guide-indicator-' + type) === 'true';
}
}
context.querySelector('.chkColorCodedBackgrounds').checked = userSettings.get('guide-colorcodedbackgrounds') === 'true';
context.querySelector('.chkFavoriteChannelsAtTop').checked = userSettings.get('livetv-favoritechannelsattop') !== 'false';
var sortByValue = userSettings.get('livetv-channelorder') || 'Number';
const sortByValue = userSettings.get('livetv-channelorder') || 'Number';
var sortBys = context.querySelectorAll('.chkSortOrder');
for (i = 0, length = sortBys.length; i < length; i++) {
sortBys[i].checked = sortBys[i].value === sortByValue;
}
const sortBys = context.querySelectorAll('.chkSortOrder');
for (const sortBy of sortBys) {
sortBy.checked = sortBy.value === sortByValue;
}
}
function showEditor(options) {
function showEditor(options) {
return new Promise(function (resolve, reject) {
var settingsChanged = false;
let settingsChanged = false;
require(['text!./guide-settings.template.html'], function (template) {
var dialogOptions = {
import('text!./guide-settings.template.html').then(({ default: template }) => {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -99,11 +100,11 @@ define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectio
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
let html = '';
html += globalize.translateHtml(template, 'core');
@ -141,9 +142,8 @@ define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectio
dialogHelper.open(dlg);
});
});
}
}
return {
export default {
show: showEditor
};
});
};

View file

@ -1,46 +1,64 @@
define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', 'scrollHelper', 'serverNotifications', 'loading', 'datetime', 'focusManager', 'playbackManager', 'userSettings', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'dom', 'css!./guide.css', 'programStyles', 'material-icons', 'scrollStyles', 'emby-programcell', 'emby-button', 'paper-icon-button-light', 'emby-tabs', 'emby-scroller', 'flexStyles', 'webcomponents'], function (require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) {
'use strict';
import inputManager from 'inputManager';
import browser from 'browser';
import globalize from 'globalize';
import connectionManager from 'connectionManager';
import scrollHelper from 'scrollHelper';
import serverNotifications from 'serverNotifications';
import loading from 'loading';
import datetime from 'datetime';
import focusManager from 'focusManager';
import playbackManager from 'playbackManager';
import * as userSettings from 'userSettings';
import imageLoader from 'imageLoader';
import events from 'events';
import layoutManager from 'layoutManager';
import itemShortcuts from 'itemShortcuts';
import dom from 'dom';
import 'css!./guide.css';
import 'programStyles';
import 'material-icons';
import 'scrollStyles';
import 'emby-programcell';
import 'emby-button';
import 'paper-icon-button-light';
import 'emby-tabs';
import 'emby-scroller';
import 'flexStyles';
import 'webcomponents';
playbackManager = playbackManager.default || playbackManager;
browser = browser.default || browser;
loading = loading.default || loading;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;
serverNotifications = serverNotifications.default || serverNotifications;
function showViewSettings(instance) {
require(['guide-settings-dialog'], function (guideSettingsDialog) {
function showViewSettings(instance) {
import('guide-settings-dialog').then(({default: guideSettingsDialog}) => {
guideSettingsDialog.show(instance.categoryOptions).then(function () {
instance.refresh();
});
});
}
}
function updateProgramCellOnScroll(cell, scrollPct) {
var left = cell.posLeft;
function updateProgramCellOnScroll(cell, scrollPct) {
let left = cell.posLeft;
if (!left) {
left = parseFloat(cell.style.left.replace('%', ''));
cell.posLeft = left;
}
var width = cell.posWidth;
let width = cell.posWidth;
if (!width) {
width = parseFloat(cell.style.width.replace('%', ''));
cell.posWidth = width;
}
var right = left + width;
var newPct = Math.max(Math.min(scrollPct, right), left);
const right = left + width;
const newPct = Math.max(Math.min(scrollPct, right), left);
var offset = newPct - left;
var pctOfWidth = (offset / width) * 100;
const offset = newPct - left;
const pctOfWidth = (offset / width) * 100;
var guideProgramName = cell.guideProgramName;
let guideProgramName = cell.guideProgramName;
if (!guideProgramName) {
guideProgramName = cell.querySelector('.guideProgramName');
cell.guideProgramName = guideProgramName;
}
var caret = cell.caret;
let caret = cell.caret;
if (!caret) {
caret = cell.querySelector('.guide-programNameCaret');
cell.caret = caret;
@ -55,10 +73,10 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
caret.classList.add('hide');
}
}
}
}
var isUpdatingProgramCellScroll = false;
function updateProgramCellsOnScroll(programGrid, programCells) {
let isUpdatingProgramCellScroll = false;
function updateProgramCellsOnScroll(programGrid, programCells) {
if (isUpdatingProgramCellScroll) {
return;
}
@ -66,34 +84,34 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
isUpdatingProgramCellScroll = true;
requestAnimationFrame(function () {
var scrollLeft = programGrid.scrollLeft;
const scrollLeft = programGrid.scrollLeft;
var scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0;
const scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0;
for (var i = 0, length = programCells.length; i < length; i++) {
updateProgramCellOnScroll(programCells[i], scrollPct);
for (const programCell of programCells) {
updateProgramCellOnScroll(programCell, scrollPct);
}
isUpdatingProgramCellScroll = false;
});
}
}
function onProgramGridClick(e) {
function onProgramGridClick(e) {
if (!layoutManager.tv) {
return;
}
var programCell = dom.parentWithClass(e.target, 'programCell');
const programCell = dom.parentWithClass(e.target, 'programCell');
if (programCell) {
var startDate = programCell.getAttribute('data-startdate');
var endDate = programCell.getAttribute('data-enddate');
let startDate = programCell.getAttribute('data-startdate');
let endDate = programCell.getAttribute('data-enddate');
startDate = datetime.parseISO8601Date(startDate, { toLocal: true }).getTime();
endDate = datetime.parseISO8601Date(endDate, { toLocal: true }).getTime();
var now = new Date().getTime();
const now = new Date().getTime();
if (now >= startDate && now < endDate) {
var channelId = programCell.getAttribute('data-channelid');
var serverId = programCell.getAttribute('data-serverid');
const channelId = programCell.getAttribute('data-channelid');
const serverId = programCell.getAttribute('data-serverid');
e.preventDefault();
e.stopPropagation();
@ -104,27 +122,27 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
});
}
}
}
}
function Guide(options) {
var self = this;
var items = {};
function Guide(options) {
const self = this;
let items = {};
self.options = options;
self.categoryOptions = { categories: [] };
// 30 mins
var cellCurationMinutes = 30;
var cellDurationMs = cellCurationMinutes * 60 * 1000;
var msPerDay = 86400000;
const cellCurationMinutes = 30;
const cellDurationMs = cellCurationMinutes * 60 * 1000;
const msPerDay = 86400000;
var currentDate;
var currentStartIndex = 0;
var currentChannelLimit = 0;
var autoRefreshInterval;
var programCells;
var lastFocusDirection;
var programGrid;
let currentDate;
let currentStartIndex = 0;
let currentChannelLimit = 0;
let autoRefreshInterval;
let programCells;
let lastFocusDirection;
let programGrid;
self.refresh = function () {
currentDate = null;
@ -160,7 +178,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
function restartAutoRefresh() {
stopAutoRefresh();
var intervalMs = 60000 * 15; // (minutes)
const intervalMs = 60000 * 15; // (minutes)
autoRefreshInterval = setInterval(function () {
self.refresh();
@ -175,7 +193,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function normalizeDateToTimeslot(date) {
var minutesOffset = date.getMinutes() - cellCurationMinutes;
const minutesOffset = date.getMinutes() - cellCurationMinutes;
if (minutesOffset >= 0) {
date.setHours(date.getHours(), cellCurationMinutes, 0, 0);
@ -195,9 +213,9 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) {
var apiClient = connectionManager.getApiClient(options.serverId);
const apiClient = connectionManager.getApiClient(options.serverId);
var channelQuery = {
const channelQuery = {
StartIndex: 0,
EnableFavoriteSorting: userSettings.get('livetv-favoritechannelsattop') !== 'false'
@ -205,7 +223,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
channelQuery.UserId = apiClient.getCurrentUserId();
var channelLimit = 500;
const channelLimit = 500;
currentChannelLimit = channelLimit;
showLoading();
@ -216,12 +234,12 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
channelQuery.EnableUserData = false;
channelQuery.EnableImageTypes = 'Primary';
var categories = self.categoryOptions.categories || [];
var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1;
var displayNewsContent = !categories.length || categories.indexOf('news') !== -1;
var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1;
var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1;
const categories = self.categoryOptions.categories || [];
const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
const displaySportsContent = !categories.length || categories.indexOf('sports') !== -1;
const displayNewsContent = !categories.length || categories.indexOf('news') !== -1;
const displayKidsContent = !categories.length || categories.indexOf('kids') !== -1;
const displaySeriesContent = !categories.length || categories.indexOf('series') !== -1;
if (displayMovieContent && displaySportsContent && displayNewsContent && displayKidsContent) {
channelQuery.IsMovie = null;
@ -255,19 +273,19 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
channelQuery.SortOrder = null;
}
var date = newStartDate;
let date = newStartDate;
// Add one second to avoid getting programs that are just ending
date = new Date(date.getTime() + 1000);
// Subtract to avoid getting programs that are starting when the grid ends
var nextDay = new Date(date.getTime() + msPerDay - 2000);
const nextDay = new Date(date.getTime() + msPerDay - 2000);
// Normally we'd want to just let responsive css handle this,
// but since mobile browsers are often underpowered,
// it can help performance to get them out of the markup
var allowIndicators = dom.getWindowSize().innerWidth >= 600;
const allowIndicators = dom.getWindowSize().innerWidth >= 600;
var renderOptions = {
const renderOptions = {
showHdIcon: allowIndicators && userSettings.get('guide-indicator-hd') === 'true',
showLiveIndicator: allowIndicators && userSettings.get('guide-indicator-live') !== 'false',
showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false',
@ -277,8 +295,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
};
apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) {
var btnPreviousPage = context.querySelector('.btnPreviousPage');
var btnNextPage = context.querySelector('.btnNextPage');
const btnPreviousPage = context.querySelector('.btnPreviousPage');
const btnNextPage = context.querySelector('.btnNextPage');
if (channelsResult.TotalRecordCount > channelLimit) {
context.querySelector('.guideOptions').classList.remove('hide');
@ -301,9 +319,9 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
context.querySelector('.guideOptions').classList.add('hide');
}
var programFields = [];
const programFields = [];
var programQuery = {
const programQuery = {
UserId: apiClient.getCurrentUserId(),
MaxStartDate: nextDay.toISOString(),
MinEndDate: date.toISOString(),
@ -347,7 +365,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function getTimeslotHeadersHtml(startDate, endDateTime) {
var html = '';
let html = '';
// clone
startDate = new Date(startDate.getTime());
@ -388,7 +406,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function getTimerIndicator(item) {
var status;
let status;
if (item.Type === 'SeriesTimer') {
return '<span class="material-icons programIcon seriesTimerIcon fiber_smart_record"></span>';
@ -412,30 +430,30 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function getChannelProgramsHtml(context, date, channel, programs, options, listInfo) {
var html = '';
let html = '';
var startMs = date.getTime();
var endMs = startMs + msPerDay - 1;
const startMs = date.getTime();
const endMs = startMs + msPerDay - 1;
var outerCssClass = layoutManager.tv ? 'channelPrograms channelPrograms-tv' : 'channelPrograms';
const outerCssClass = layoutManager.tv ? 'channelPrograms channelPrograms-tv' : 'channelPrograms';
html += '<div class="' + outerCssClass + '" data-channelid="' + channel.Id + '">';
var clickAction = layoutManager.tv ? 'link' : 'programdialog';
const clickAction = layoutManager.tv ? 'link' : 'programdialog';
var categories = self.categoryOptions.categories || [];
var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1;
var displayNewsContent = !categories.length || categories.indexOf('news') !== -1;
var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1;
var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1;
var enableColorCodedBackgrounds = userSettings.get('guide-colorcodedbackgrounds') === 'true';
const categories = self.categoryOptions.categories || [];
const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1;
const displaySportsContent = !categories.length || categories.indexOf('sports') !== -1;
const displayNewsContent = !categories.length || categories.indexOf('news') !== -1;
const displayKidsContent = !categories.length || categories.indexOf('kids') !== -1;
const displaySeriesContent = !categories.length || categories.indexOf('series') !== -1;
const enableColorCodedBackgrounds = userSettings.get('guide-colorcodedbackgrounds') === 'true';
var programsFound;
var now = new Date().getTime();
let programsFound;
const now = new Date().getTime();
for (var i = listInfo.startIndex, length = programs.length; i < length; i++) {
var program = programs[i];
for (let i = listInfo.startIndex, length = programs.length; i < length; i++) {
const program = programs[i];
if (program.ChannelId !== channel.Id) {
if (programsFound) {
@ -450,8 +468,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
parseDates(program);
var startDateLocalMs = program.StartDateLocal.getTime();
var endDateLocalMs = program.EndDateLocal.getTime();
const startDateLocalMs = program.StartDateLocal.getTime();
const endDateLocalMs = program.EndDateLocal.getTime();
if (endDateLocalMs < startMs) {
continue;
@ -463,18 +481,18 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
items[program.Id] = program;
var renderStartMs = Math.max(startDateLocalMs, startMs);
var startPercent = (startDateLocalMs - startMs) / msPerDay;
const renderStartMs = Math.max(startDateLocalMs, startMs);
let startPercent = (startDateLocalMs - startMs) / msPerDay;
startPercent *= 100;
startPercent = Math.max(startPercent, 0);
var renderEndMs = Math.min(endDateLocalMs, endMs);
var endPercent = (renderEndMs - renderStartMs) / msPerDay;
const renderEndMs = Math.min(endDateLocalMs, endMs);
let endPercent = (renderEndMs - renderStartMs) / msPerDay;
endPercent *= 100;
var cssClass = 'programCell itemAction';
var accentCssClass = null;
var displayInnerContent = true;
let cssClass = 'programCell itemAction';
let accentCssClass = null;
let displayInnerContent = true;
if (program.IsKids) {
displayInnerContent = displayKidsContent;
@ -502,7 +520,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
cssClass += ' programCell-active';
}
var timerAttributes = '';
let timerAttributes = '';
if (program.TimerId) {
timerAttributes += ' data-timerid="' + program.TimerId + '"';
}
@ -510,12 +528,12 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
timerAttributes += ' data-seriestimerid="' + program.SeriesTimerId + '"';
}
var isAttribute = endPercent >= 2 ? ' is="emby-programcell"' : '';
const isAttribute = endPercent >= 2 ? ' is="emby-programcell"' : '';
html += '<button' + isAttribute + ' data-action="' + clickAction + '"' + timerAttributes + ' data-channelid="' + program.ChannelId + '" data-id="' + program.Id + '" data-serverid="' + program.ServerId + '" data-startdate="' + program.StartDate + '" data-enddate="' + program.EndDate + '" data-type="' + program.Type + '" class="' + cssClass + '" style="left:' + startPercent + '%;width:' + endPercent + '%;">';
if (displayInnerContent) {
var guideProgramNameClass = 'guideProgramName';
const guideProgramNameClass = 'guideProgramName';
html += '<div class="' + guideProgramNameClass + '">';
@ -523,7 +541,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
html += '<div class="guideProgramNameText">' + program.Name;
var indicatorHtml = null;
let indicatorHtml = null;
if (program.IsLive && options.showLiveIndicator) {
indicatorHtml = '<span class="liveTvProgram guideProgramIndicator">' + globalize.translate('Live') + '</span>';
} else if (program.IsPremiere && options.showPremiereIndicator) {
@ -568,19 +586,18 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function renderChannelHeaders(context, channels, apiClient) {
var html = '';
let html = '';
for (var i = 0, length = channels.length; i < length; i++) {
var channel = channels[i];
var hasChannelImage = channel.ImageTags.Primary;
for (const channel of channels) {
const hasChannelImage = channel.ImageTags.Primary;
var cssClass = 'guide-channelHeaderCell itemAction';
let cssClass = 'guide-channelHeaderCell itemAction';
if (layoutManager.tv) {
cssClass += ' guide-channelHeaderCell-tv';
}
var title = [];
const title = [];
if (channel.ChannelNumber) {
title.push(channel.ChannelNumber);
}
@ -591,7 +608,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
html += '<button title="' + title.join(' ') + '" type="button" class="' + cssClass + '"' + ' data-action="link" data-isfolder="' + channel.IsFolder + '" data-id="' + channel.Id + '" data-serverid="' + channel.ServerId + '" data-type="' + channel.Type + '">';
if (hasChannelImage) {
var url = apiClient.getScaledImageUrl(channel.Id, {
const url = apiClient.getScaledImageUrl(channel.Id, {
maxHeight: 220,
tag: channel.ImageTags.Primary,
type: 'Primary'
@ -611,20 +628,20 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
html += '</button>';
}
var channelList = context.querySelector('.channelsContainer');
const channelList = context.querySelector('.channelsContainer');
channelList.innerHTML = html;
imageLoader.lazyChildren(channelList);
}
function renderPrograms(context, date, channels, programs, options) {
var listInfo = {
const listInfo = {
startIndex: 0
};
var html = [];
const html = [];
for (var i = 0, length = channels.length; i < length; i++) {
html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo));
for (const channel of channels) {
html.push(getChannelProgramsHtml(context, date, channel, programs, options, listInfo));
}
programGrid.innerHTML = html.join('');
@ -635,17 +652,17 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function getProgramSortOrder(program, channels) {
var channelId = program.ChannelId;
var channelIndex = -1;
const channelId = program.ChannelId;
let channelIndex = -1;
for (var i = 0, length = channels.length; i < length; i++) {
for (let i = 0, length = channels.length; i < length; i++) {
if (channelId === channels[i].Id) {
channelIndex = i;
break;
}
}
var start = datetime.parseISO8601Date(program.StartDate, { toLocal: true });
const start = datetime.parseISO8601Date(program.StartDate, { toLocal: true });
return (channelIndex * 10000000) + (start.getTime() / 60000);
}
@ -655,9 +672,9 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels);
});
var activeElement = document.activeElement;
var itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null;
var channelRowId = null;
const activeElement = document.activeElement;
const itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null;
let channelRowId = null;
if (activeElement) {
channelRowId = dom.parentWithClass(activeElement, 'channelPrograms');
@ -666,8 +683,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
renderChannelHeaders(context, channels, apiClient);
var startDate = date;
var endDate = new Date(startDate.getTime() + msPerDay);
const startDate = date;
const endDate = new Date(startDate.getTime() + msPerDay);
context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate);
items = {};
renderPrograms(context, date, channels, programs, renderOptions);
@ -682,17 +699,17 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) {
scrollToTimeMs -= startTimeOfDayMs;
var pct = scrollToTimeMs / msPerDay;
const pct = scrollToTimeMs / msPerDay;
programGrid.scrollTop = 0;
var scrollPos = pct * programGrid.scrollWidth;
const scrollPos = pct * programGrid.scrollWidth;
nativeScrollTo(programGrid, scrollPos, true);
}
function focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs) {
var focusElem;
let focusElem;
if (itemId) {
focusElem = context.querySelector('[data-id="' + itemId + '"]');
}
@ -700,7 +717,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
if (focusElem) {
focusManager.focus(focusElem);
} else {
var autoFocusParent;
let autoFocusParent;
if (channelRowId) {
autoFocusParent = context.querySelector('[data-channelid="' + channelRowId + '"]');
@ -712,14 +729,14 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
focusToTimeMs -= startTimeOfDayMs;
var pct = (focusToTimeMs / msPerDay) * 100;
const pct = (focusToTimeMs / msPerDay) * 100;
var programCell = autoFocusParent.querySelector('.programCell');
let programCell = autoFocusParent.querySelector('.programCell');
while (programCell) {
var left = (programCell.style.left || '').replace('%', '');
let left = (programCell.style.left || '').replace('%', '');
left = left ? parseFloat(left) : 0;
var width = (programCell.style.width || '').replace('%', '');
let width = (programCell.style.width || '').replace('%', '');
width = width ? parseFloat(width) : 0;
if (left >= pct || (left + width) >= pct) {
@ -752,14 +769,14 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
}
var lastGridScroll = 0;
var lastHeaderScroll = 0;
var scrollXPct = 0;
let lastGridScroll = 0;
let lastHeaderScroll = 0;
let scrollXPct = 0;
function onProgramGridScroll(context, elem, timeslotHeaders) {
if ((new Date().getTime() - lastHeaderScroll) >= 1000) {
lastGridScroll = new Date().getTime();
var scrollLeft = elem.scrollLeft;
const scrollLeft = elem.scrollLeft;
scrollXPct = (scrollLeft * 100) / elem.scrollWidth;
nativeScrollTo(timeslotHeaders, scrollLeft, true);
}
@ -775,17 +792,17 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) {
var newStartDate = normalizeDateToTimeslot(date);
const newStartDate = normalizeDateToTimeslot(date);
currentDate = newStartDate;
reloadGuide(page, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender);
}
function getDateTabText(date, isActive, tabIndex) {
var cssClass = isActive ? 'emby-tab-button guide-date-tab-button emby-tab-button-active' : 'emby-tab-button guide-date-tab-button';
const cssClass = isActive ? 'emby-tab-button guide-date-tab-button emby-tab-button-active' : 'emby-tab-button guide-date-tab-button';
var html = '<button is="emby-button" class="' + cssClass + '" data-index="' + tabIndex + '" data-date="' + date.getTime() + '">';
var tabText = datetime.toLocaleDateString(date, { weekday: 'short' });
let html = '<button is="emby-button" class="' + cssClass + '" data-index="' + tabIndex + '" data-date="' + date.getTime() + '">';
let tabText = datetime.toLocaleDateString(date, { weekday: 'short' });
tabText += '<br/>';
tabText += date.getDate();
@ -796,12 +813,12 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function setDateRange(page, guideInfo) {
var today = new Date();
var nowHours = today.getHours();
const today = new Date();
const nowHours = today.getHours();
today.setHours(nowHours, 0, 0, 0);
var start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true });
var end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true });
let start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true });
const end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true });
start.setHours(nowHours, 0, 0, 0);
end.setHours(0, 0, 0, 0);
@ -812,11 +829,11 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
start = new Date(Math.max(today, start));
var dateTabsHtml = '';
var tabIndex = 0;
let dateTabsHtml = '';
let tabIndex = 0;
// TODO: Use date-fns
var date = new Date();
const date = new Date();
if (currentDate) {
date.setTime(currentDate.getTime());
@ -824,11 +841,11 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
date.setHours(nowHours, 0, 0, 0);
var startTimeOfDayMs = (start.getHours() * 60 * 60 * 1000);
let startTimeOfDayMs = (start.getHours() * 60 * 60 * 1000);
startTimeOfDayMs += start.getMinutes() * 60 * 1000;
while (start <= end) {
var isActive = date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear();
const isActive = date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear();
dateTabsHtml += getDateTabText(start, isActive, tabIndex);
@ -840,23 +857,23 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
page.querySelector('.emby-tabs-slider').innerHTML = dateTabsHtml;
page.querySelector('.guideDateTabs').refresh();
var newDate = new Date();
var newDateHours = newDate.getHours();
var scrollToTimeMs = newDateHours * 60 * 60 * 1000;
const newDate = new Date();
const newDateHours = newDate.getHours();
let scrollToTimeMs = newDateHours * 60 * 60 * 1000;
var minutes = newDate.getMinutes();
const minutes = newDate.getMinutes();
if (minutes >= 30) {
scrollToTimeMs += 30 * 60 * 1000;
}
var focusToTimeMs = ((newDateHours * 60) + minutes) * 60 * 1000;
const focusToTimeMs = ((newDateHours * 60) + minutes) * 60 * 1000;
changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, layoutManager.tv);
}
function reloadPage(page) {
showLoading();
var apiClient = connectionManager.getApiClient(options.serverId);
const apiClient = connectionManager.getApiClient(options.serverId);
apiClient.getLiveTvGuideInfo().then(function (guideInfo) {
setDateRange(page, guideInfo);
@ -864,18 +881,17 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function getChannelProgramsFocusableElements(container) {
var elements = container.querySelectorAll('.programCell');
const elements = container.querySelectorAll('.programCell');
var list = [];
const list = [];
// add 1 to avoid programs that are out of view to the left
var currentScrollXPct = scrollXPct + 1;
const currentScrollXPct = scrollXPct + 1;
for (var i = 0, length = elements.length; i < length; i++) {
var elem = elements[i];
var left = (elem.style.left || '').replace('%', '');
for (const elem of elements) {
let left = (elem.style.left || '').replace('%', '');
left = left ? parseFloat(left) : 0;
var width = (elem.style.width || '').replace('%', '');
let width = (elem.style.width || '').replace('%', '');
width = width ? parseFloat(width) : 0;
if ((left + width) >= currentScrollXPct) {
@ -887,12 +903,12 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function onInputCommand(e) {
var target = e.target;
var programCell = dom.parentWithClass(target, 'programCell');
var container;
var channelPrograms;
var focusableElements;
var newRow;
const target = e.target;
const programCell = dom.parentWithClass(target, 'programCell');
let container;
let channelPrograms;
let focusableElements;
let newRow;
switch (e.detail.command) {
case 'up':
@ -976,14 +992,14 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function onScrollerFocus(e) {
var target = e.target;
var programCell = dom.parentWithClass(target, 'programCell');
const target = e.target;
const programCell = dom.parentWithClass(target, 'programCell');
if (programCell) {
var focused = target;
const focused = target;
var id = focused.getAttribute('data-id');
var item = items[id];
const id = focused.getAttribute('data-id');
const item = items[id];
if (item) {
events.trigger(self, 'focus', [
@ -1002,9 +1018,9 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
scrollHelper.toCenter(programGrid, programCell, true, true);
}
} else if (lastFocusDirection === 'up' || lastFocusDirection === 'down') {
var verticalScroller = dom.parentWithClass(target, 'guideVerticalScroller');
const verticalScroller = dom.parentWithClass(target, 'guideVerticalScroller');
if (verticalScroller) {
var focusedElement = programCell || dom.parentWithTag(target, 'BUTTON');
const focusedElement = programCell || dom.parentWithTag(target, 'BUTTON');
verticalScroller.toCenter(focusedElement, true);
}
}
@ -1012,7 +1028,7 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
function setScrollEvents(view, enabled) {
if (layoutManager.tv) {
var guideVerticalScroller = view.querySelector('.guideVerticalScroller');
const guideVerticalScroller = view.querySelector('.guideVerticalScroller');
if (enabled) {
inputManager.on(guideVerticalScroller, onInputCommand);
@ -1023,16 +1039,14 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function onTimerCreated(e, apiClient, data) {
var programId = data.ProgramId;
const programId = data.ProgramId;
// This could be null, not supported by all tv providers
var newTimerId = data.Id;
const newTimerId = data.Id;
// find guide cells by program id, ensure timer icon
var cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]');
for (var i = 0, length = cells.length; i < length; i++) {
var cell = cells[i];
var icon = cell.querySelector('.timerIcon');
const cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]');
for (const cell of cells) {
const icon = cell.querySelector('.timerIcon');
if (!icon) {
cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', '<span class="timerIcon material-icons programIcon fiber_manual_record"></span>');
}
@ -1047,42 +1061,46 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
function onTimerCancelled(e, apiClient, data) {
var id = data.Id;
const id = data.Id;
// find guide cells by timer id, remove timer icon
var cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]');
for (var i = 0, length = cells.length; i < length; i++) {
var cell = cells[i];
var icon = cell.querySelector('.timerIcon');
const cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]');
for (const cell of cells) {
const icon = cell.querySelector('.timerIcon');
if (icon) {
icon.parentNode.removeChild(icon);
}
cell.removeAttribute('data-timerid');
}
}
function onSeriesTimerCancelled(e, apiClient, data) {
var id = data.Id;
const id = data.Id;
// find guide cells by timer id, remove timer icon
var cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]');
for (var i = 0, length = cells.length; i < length; i++) {
var cell = cells[i];
var icon = cell.querySelector('.seriesTimerIcon');
const cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]');
for (const cell of cells) {
const icon = cell.querySelector('.seriesTimerIcon');
if (icon) {
icon.parentNode.removeChild(icon);
}
cell.removeAttribute('data-seriestimerid');
}
}
require(['text!./tvguide.template.html'], function (template) {
var context = options.element;
import('text!./tvguide.template.html').then(({default: template}) => {
const context = options.element;
context.classList.add('tvguide');
context.innerHTML = globalize.translateHtml(template, 'core');
programGrid = context.querySelector('.programGrid');
var timeslotHeaders = context.querySelector('.timeslotHeaders');
const timeslotHeaders = context.querySelector('.timeslotHeaders');
if (layoutManager.tv) {
dom.addEventListener(context.querySelector('.guideVerticalScroller'), 'focus', onScrollerFocus, {
@ -1131,17 +1149,17 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
});
context.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) {
var allTabButtons = e.target.querySelectorAll('.guide-date-tab-button');
const allTabButtons = e.target.querySelectorAll('.guide-date-tab-button');
var tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)];
const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)];
if (tabButton) {
var previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)];
const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)];
var date = new Date();
const date = new Date();
date.setTime(parseInt(tabButton.getAttribute('data-date')));
var scrollWidth = programGrid.scrollWidth;
var scrollToTimeMs;
const scrollWidth = programGrid.scrollWidth;
let scrollToTimeMs;
if (scrollWidth) {
scrollToTimeMs = (programGrid.scrollLeft / scrollWidth) * msPerDay;
} else {
@ -1149,14 +1167,14 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
}
if (previousButton) {
var previousDate = new Date();
const previousDate = new Date();
previousDate.setTime(parseInt(previousButton.getAttribute('data-date')));
scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000);
scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000);
}
var startTimeOfDayMs = (date.getHours() * 60 * 60 * 1000);
let startTimeOfDayMs = (date.getHours() * 60 * 60 * 1000);
startTimeOfDayMs += (date.getMinutes() * 60 * 1000);
changeDate(context, date, scrollToTimeMs, scrollToTimeMs, startTimeOfDayMs, false);
@ -1175,7 +1193,6 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
self.refresh();
});
}
}
return Guide;
});
export default Guide;

View file

@ -8,7 +8,6 @@ import browser from 'browser';
import layoutManager from 'layoutManager';
import scrollHelper from 'scrollHelper';
import globalize from 'globalize';
import require from 'require';
import 'emby-checkbox';
import 'paper-icon-button-light';
import 'emby-button';
@ -317,7 +316,7 @@ import 'cardStyle';
function showEditor(itemId, serverId, itemType) {
loading.show();
require(['text!./imageDownloader.template.html'], function (template) {
import('text!./imageDownloader.template.html').then(({default: template}) => {
const apiClient = connectionManager.getApiClient(serverId);
currentItemId = itemId;

View file

@ -1,13 +1,11 @@
define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) {
'use strict';
import playbackManager from 'playbackManager';
import serverNotifications from 'serverNotifications';
import events from 'events';
serverNotifications = serverNotifications.default || serverNotifications;
playbackManager = playbackManager.default || playbackManager;
function onUserDataChanged(e, apiClient, userData) {
const instance = this;
function onUserDataChanged(e, apiClient, userData) {
var instance = this;
var eventsToMonitor = getEventsToMonitor(instance);
const eventsToMonitor = getEventsToMonitor(instance);
// TODO: Check user data change reason?
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
@ -15,72 +13,72 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
instance.notifyRefreshNeeded();
}
}
}
function getEventsToMonitor(instance) {
var options = instance.options;
var monitor = options ? options.monitorEvents : null;
function getEventsToMonitor(instance) {
const options = instance.options;
const monitor = options ? options.monitorEvents : null;
if (monitor) {
return monitor.split(',');
}
return [];
}
}
function onTimerCreated(e, apiClient, data) {
var instance = this;
function onTimerCreated(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
}
function onSeriesTimerCreated(e, apiClient, data) {
var instance = this;
function onSeriesTimerCreated(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
}
function onTimerCancelled(e, apiClient, data) {
var instance = this;
function onTimerCancelled(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
}
function onSeriesTimerCancelled(e, apiClient, data) {
var instance = this;
function onSeriesTimerCancelled(e, apiClient, data) {
const instance = this;
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
instance.notifyRefreshNeeded();
return;
}
}
}
function onLibraryChanged(e, apiClient, data) {
var instance = this;
var eventsToMonitor = getEventsToMonitor(instance);
function onLibraryChanged(e, apiClient, data) {
const instance = this;
const eventsToMonitor = getEventsToMonitor(instance);
if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) {
// yes this is an assumption
return;
}
var itemsAdded = data.ItemsAdded || [];
var itemsRemoved = data.ItemsRemoved || [];
const itemsAdded = data.ItemsAdded || [];
const itemsRemoved = data.ItemsRemoved || [];
if (!itemsAdded.length && !itemsRemoved.length) {
return;
}
var options = instance.options || {};
var parentId = options.parentId;
const options = instance.options || {};
const parentId = options.parentId;
if (parentId) {
var foldersAddedTo = data.FoldersAddedTo || [];
var foldersRemovedFrom = data.FoldersRemovedFrom || [];
var collectionFolders = data.CollectionFolders || [];
const foldersAddedTo = data.FoldersAddedTo || [];
const foldersRemovedFrom = data.FoldersRemovedFrom || [];
const collectionFolders = data.CollectionFolders || [];
if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
return;
@ -88,14 +86,14 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
}
instance.notifyRefreshNeeded();
}
}
function onPlaybackStopped(e, stopInfo) {
var instance = this;
function onPlaybackStopped(e, stopInfo) {
const instance = this;
var state = stopInfo.state;
const state = stopInfo.state;
var eventsToMonitor = getEventsToMonitor(instance);
const eventsToMonitor = getEventsToMonitor(instance);
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
instance.notifyRefreshNeeded(true);
@ -107,25 +105,26 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
return;
}
}
}
}
function addNotificationEvent(instance, name, handler, owner) {
var localHandler = handler.bind(instance);
function addNotificationEvent(instance, name, handler, owner) {
const localHandler = handler.bind(instance);
owner = owner || serverNotifications;
events.on(owner, name, localHandler);
instance['event_' + name] = localHandler;
}
}
function removeNotificationEvent(instance, name, owner) {
var handler = instance['event_' + name];
function removeNotificationEvent(instance, name, owner) {
const handler = instance['event_' + name];
if (handler) {
owner = owner || serverNotifications;
events.off(owner, name, handler);
instance['event_' + name] = null;
}
}
}
function ItemsRefresher(options) {
class ItemsRefresher {
constructor(options) {
this.options = options || {};
addNotificationEvent(this, 'UserDataChanged', onUserDataChanged);
@ -137,18 +136,18 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager);
}
ItemsRefresher.prototype.pause = function () {
pause() {
clearRefreshInterval(this, true);
this.paused = true;
};
}
ItemsRefresher.prototype.resume = function (options) {
resume(options) {
this.paused = false;
var refreshIntervalEndTime = this.refreshIntervalEndTime;
const refreshIntervalEndTime = this.refreshIntervalEndTime;
if (refreshIntervalEndTime) {
var remainingMs = refreshIntervalEndTime - new Date().getTime();
const remainingMs = refreshIntervalEndTime - new Date().getTime();
if (remainingMs > 0 && !this.needsRefresh) {
resetRefreshInterval(this, remainingMs);
} else {
@ -162,9 +161,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
}
return Promise.resolve();
};
}
ItemsRefresher.prototype.refreshItems = function () {
refreshItems() {
if (!this.fetchData) {
return Promise.resolve();
}
@ -177,15 +176,15 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
this.needsRefresh = false;
return this.fetchData().then(onDataFetched.bind(this));
};
}
ItemsRefresher.prototype.notifyRefreshNeeded = function (isInForeground) {
notifyRefreshNeeded(isInForeground) {
if (this.paused) {
this.needsRefresh = true;
return;
}
var timeout = this.refreshTimeout;
const timeout = this.refreshTimeout;
if (timeout) {
clearTimeout(timeout);
}
@ -195,44 +194,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
} else {
this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000);
}
};
function clearRefreshInterval(instance, isPausing) {
if (instance.refreshInterval) {
clearInterval(instance.refreshInterval);
instance.refreshInterval = null;
if (!isPausing) {
instance.refreshIntervalEndTime = null;
}
}
}
function resetRefreshInterval(instance, intervalMs) {
clearRefreshInterval(instance);
if (!intervalMs) {
var options = instance.options;
if (options) {
intervalMs = options.refreshIntervalMs;
}
}
if (intervalMs) {
instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs);
instance.refreshIntervalEndTime = new Date().getTime() + intervalMs;
}
}
function onDataFetched(result) {
resetRefreshInterval(this);
if (this.afterRefresh) {
this.afterRefresh(result);
}
}
ItemsRefresher.prototype.destroy = function () {
destroy() {
clearRefreshInterval(this);
removeNotificationEvent(this, 'UserDataChanged');
@ -245,7 +209,42 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
this.fetchData = null;
this.options = null;
};
}
}
return ItemsRefresher;
});
function clearRefreshInterval(instance, isPausing) {
if (instance.refreshInterval) {
clearInterval(instance.refreshInterval);
instance.refreshInterval = null;
if (!isPausing) {
instance.refreshIntervalEndTime = null;
}
}
}
function resetRefreshInterval(instance, intervalMs) {
clearRefreshInterval(instance);
if (!intervalMs) {
const options = instance.options;
if (options) {
intervalMs = options.refreshIntervalMs;
}
}
if (intervalMs) {
instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs);
instance.refreshIntervalEndTime = new Date().getTime() + intervalMs;
}
}
function onDataFetched(result) {
resetRefreshInterval(this);
if (this.afterRefresh) {
this.afterRefresh(result);
}
}
export default ItemsRefresher;

View file

@ -1,9 +1,8 @@
define(['browser', 'appSettings', 'events'], function (browser, appSettings, events) {
'use strict';
import browser from 'browser';
import appSettings from 'appSettings';
import events from 'events';
browser = browser.default || browser;
function setLayout(instance, layout, selectedLayout) {
function setLayout(instance, layout, selectedLayout) {
if (layout === selectedLayout) {
instance[layout] = true;
document.documentElement.classList.add('layout-' + layout);
@ -11,13 +10,10 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve
instance[layout] = false;
document.documentElement.classList.remove('layout-' + layout);
}
}
}
function LayoutManager() {
}
LayoutManager.prototype.setLayout = function (layout, save) {
class LayoutManager {
setLayout(layout, save) {
if (!layout || layout === 'auto') {
this.autoLayout();
@ -35,13 +31,13 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve
}
events.trigger(this, 'modechange');
};
}
LayoutManager.prototype.getSavedLayout = function (layout) {
getSavedLayout(layout) {
return appSettings.get('layout');
};
}
LayoutManager.prototype.autoLayout = function () {
autoLayout() {
// Take a guess at initial layout. The consuming app can override
if (browser.mobile) {
this.setLayout('mobile', false);
@ -50,16 +46,16 @@ define(['browser', 'appSettings', 'events'], function (browser, appSettings, eve
} else {
this.setLayout(this.defaultLayout || 'tv', false);
}
};
}
LayoutManager.prototype.init = function () {
var saved = this.getSavedLayout();
init() {
const saved = this.getSavedLayout();
if (saved) {
this.setLayout(saved, false);
} else {
this.autoLayout();
}
};
}
}
return new LayoutManager();
});
export default new LayoutManager();

View file

@ -6,7 +6,6 @@ import loading from 'loading';
import focusManager from 'focusManager';
import connectionManager from 'connectionManager';
import globalize from 'globalize';
import require from 'require';
import shell from 'shell';
import 'emby-checkbox';
import 'emby-input';
@ -37,7 +36,7 @@ import 'flexStyles';
function submitUpdatedItem(form, item) {
function afterContentTypeUpdated() {
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageItemSaved'));
});
@ -227,7 +226,7 @@ import 'flexStyles';
}
function editPerson(context, person, index) {
require(['personEditor'], function (personEditor) {
import('personEditor').then(({default: personEditor}) => {
personEditor.show(person).then(function (updatedPerson) {
const isNew = index === -1;
@ -246,14 +245,14 @@ import 'flexStyles';
if (parentId) {
reload(context, parentId, item.ServerId);
} else {
require(['appRouter'], function (appRouter) {
import('appRouter').then(({default: appRouter}) => {
appRouter.goHome();
});
}
}
function showMoreMenu(context, button, user) {
require(['itemContextMenu'], function (itemContextMenu) {
import('itemContextMenu').then(({default: itemContextMenu}) => {
var item = currentItem;
itemContextMenu.show({

View file

@ -1,37 +1,40 @@
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';
import connectionManager from 'connectionManager';
import dom from 'dom';
import recordingHelper from 'recordingHelper';
import 'paper-icon-button-light';
import 'emby-button';
import 'css!./recordingfields';
recordingHelper = recordingHelper.default || recordingHelper;
function onRecordingButtonClick(e) {
var item = this.item;
function onRecordingButtonClick(e) {
const item = this.item;
if (item) {
var serverId = item.ServerId;
var programId = item.Id;
var timerId = item.TimerId;
var timerStatus = item.Status;
var seriesTimerId = item.SeriesTimerId;
const serverId = item.ServerId;
const programId = item.Id;
const timerId = item.TimerId;
const timerStatus = item.Status;
const seriesTimerId = item.SeriesTimerId;
var instance = this;
const instance = this;
recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function () {
instance.refresh(serverId, programId);
});
}
}
}
function setButtonIcon(button, icon) {
var inner = button.querySelector('.material-icons');
function setButtonIcon(button, icon) {
const inner = button.querySelector('.material-icons');
inner.classList.remove('fiber_smart_record');
inner.classList.remove('fiber_manual_record');
inner.classList.add(icon);
}
}
function RecordingButton(options) {
class RecordingButton {
constructor(options) {
this.options = options;
var button = options.button;
const button = options.button;
setButtonIcon(button, 'fiber_manual_record');
@ -41,7 +44,7 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom'
this.refresh(options.itemId, options.serverId);
}
var clickFn = onRecordingButtonClick.bind(this);
const clickFn = onRecordingButtonClick.bind(this);
this.clickFn = clickFn;
dom.addEventListener(button, 'click', clickFn, {
@ -49,8 +52,49 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom'
});
}
function getIndicatorIcon(item) {
var status;
refresh(serverId, itemId) {
const apiClient = connectionManager.getApiClient(serverId);
const self = this;
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
self.refreshItem(item);
});
}
refreshItem(item) {
const options = this.options;
const button = options.button;
this.item = item;
setButtonIcon(button, getIndicatorIcon(item));
if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') {
button.classList.add('recordingIcon-active');
} else {
button.classList.remove('recordingIcon-active');
}
}
destroy() {
const options = this.options;
if (options) {
const button = options.button;
const clickFn = this.clickFn;
if (clickFn) {
dom.removeEventListener(button, 'click', clickFn, {
passive: true
});
}
}
this.options = null;
this.item = null;
}
}
function getIndicatorIcon(item) {
let status;
if (item.Type === 'SeriesTimer') {
return 'fiber_smart_record';
@ -69,47 +113,6 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom'
}
return 'fiber_manual_record';
}
}
RecordingButton.prototype.refresh = function (serverId, itemId) {
var apiClient = connectionManager.getApiClient(serverId);
var self = this;
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
self.refreshItem(item);
});
};
RecordingButton.prototype.refreshItem = function (item) {
var options = this.options;
var button = options.button;
this.item = item;
setButtonIcon(button, getIndicatorIcon(item));
if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') {
button.classList.add('recordingIcon-active');
} else {
button.classList.remove('recordingIcon-active');
}
};
RecordingButton.prototype.destroy = function () {
var options = this.options;
if (options) {
var button = options.button;
var clickFn = this.clickFn;
if (clickFn) {
dom.removeEventListener(button, 'click', clickFn, {
passive: true
});
}
}
this.options = null;
this.item = null;
};
return RecordingButton;
});
export default RecordingButton;

View file

@ -1,17 +1,33 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) {
'use strict';
import dialogHelper from 'dialogHelper';
import globalize from 'globalize';
import layoutManager from 'layoutManager';
import mediaInfo from 'mediaInfo';
import connectionManager from 'connectionManager';
import require from 'require';
import loading from 'loading';
import scrollHelper from 'scrollHelper';
import datetime from 'datetime';
import imageLoader from 'imageLoader';
import recordingFields from 'recordingFields';
import events from 'events';
import 'emby-checkbox';
import 'emby-button';
import 'emby-collapse';
import 'emby-input';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'css!./recordingcreator';
import 'material-icons';
scrollHelper = scrollHelper.default || scrollHelper;
let currentDialog;
let closeAction;
let currentRecordingFields;
var currentDialog;
var closeAction;
var currentRecordingFields;
function closeDialog() {
function closeDialog() {
dialogHelper.close(currentDialog);
}
}
function init(context) {
function init(context) {
context.querySelector('.btnPlay').addEventListener('click', function () {
closeAction = 'play';
closeDialog();
@ -21,10 +37,10 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
closeAction = null;
closeDialog();
});
}
}
function getImageUrl(item, apiClient, imageHeight) {
var imageTags = item.ImageTags || {};
function getImageUrl(item, apiClient, imageHeight) {
const imageTags = item.ImageTags || {};
if (item.PrimaryImageTag) {
imageTags.Primary = item.PrimaryImageTag;
@ -45,12 +61,12 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
}
return null;
}
}
function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) {
function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) {
if (!refreshRecordingStateOnly) {
var imgUrl = getImageUrl(program, apiClient, 200);
var imageContainer = context.querySelector('.recordingDialog-imageContainer');
const imgUrl = getImageUrl(program, apiClient, 200);
const imageContainer = context.querySelector('.recordingDialog-imageContainer');
if (imgUrl) {
imageContainer.innerHTML = '<img src="' + require.toUrl('.').split('?')[0] + '/empty.png" data-src="' + imgUrl + '" class="recordingDialog-img lazy" />';
@ -67,8 +83,8 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
context.querySelector('.itemGenres').innerHTML = (program.Genres || []).join(' / ');
context.querySelector('.itemOverview').innerHTML = program.Overview || '';
var formDialogFooter = context.querySelector('.formDialogFooter');
var now = new Date();
const formDialogFooter = context.querySelector('.formDialogFooter');
const now = new Date();
if (now >= datetime.parseISO8601Date(program.StartDate, true) && now < datetime.parseISO8601Date(program.EndDate, true)) {
formDialogFooter.classList.remove('hide');
} else {
@ -82,31 +98,31 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
});
loading.hide();
}
}
function reload(context, programId, serverId, refreshRecordingStateOnly) {
function reload(context, programId, serverId, refreshRecordingStateOnly) {
loading.show();
var apiClient = connectionManager.getApiClient(serverId);
const apiClient = connectionManager.getApiClient(serverId);
var promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId });
var promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId());
const promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId });
const promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId());
Promise.all([promise1, promise2]).then(function (responses) {
var defaults = responses[0];
var program = responses[1];
const defaults = responses[0];
const program = responses[1];
renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly);
});
}
}
function executeCloseAction(action, programId, serverId) {
function executeCloseAction(action, programId, serverId) {
if (action === 'play') {
require(['playbackManager'], function (playbackManager) {
var apiClient = connectionManager.getApiClient(serverId);
import('playbackManager').then(({default: playbackManager}) => {
const apiClient = connectionManager.getApiClient(serverId);
apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) {
playbackManager.default.play({
playbackManager.play({
ids: [item.ChannelId],
serverId: serverId
});
@ -114,16 +130,16 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
});
return;
}
}
}
function showEditor(itemId, serverId) {
function showEditor(itemId, serverId) {
return new Promise(function (resolve, reject) {
closeAction = null;
loading.show();
require(['text!./recordingcreator.template.html'], function (template) {
var dialogOptions = {
import('text!./recordingcreator.template.html').then(({default: template}) => {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -134,12 +150,12 @@ 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');
var html = '';
let html = '';
html += globalize.translateHtml(template, 'core');
@ -181,9 +197,8 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
dialogHelper.open(dlg);
});
});
}
}
return {
export default {
show: showEditor
};
});
};

View file

@ -3,6 +3,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c
scrollHelper = scrollHelper.default || scrollHelper;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
var currentDialog;
var recordingDeleted = false;

View file

@ -1,16 +1,32 @@
define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'itemContextMenu', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder, itemContextMenu) {
'use strict';
import datetime from 'datetime';
import backdrop from 'backdrop';
import listView from 'listView';
import imageLoader from 'imageLoader';
import playbackManager from 'playbackManager';
import nowPlayingHelper from 'nowPlayingHelper';
import events from 'events';
import connectionManager from 'connectionManager';
import appHost from 'apphost';
import globalize from 'globalize';
import layoutManager from 'layoutManager';
import * as userSettings from 'userSettings';
import cardBuilder from 'cardBuilder';
import itemContextMenu from 'itemContextMenu';
import 'cardStyle';
import 'emby-itemscontainer';
import 'css!./remotecontrol.css';
import 'emby-ratingbutton';
playbackManager = playbackManager.default || playbackManager;
/*eslint prefer-const: "error"*/
var showMuteButton = true;
var showVolumeSlider = true;
let showMuteButton = true;
let showVolumeSlider = true;
function showAudioMenu(context, player, button, item) {
var currentIndex = playbackManager.getAudioStreamIndex(player);
var streams = playbackManager.audioTracks(player);
var menuItems = streams.map(function (s) {
var menuItem = {
function showAudioMenu(context, player, button, item) {
const currentIndex = playbackManager.getAudioStreamIndex(player);
const streams = playbackManager.audioTracks(player);
const menuItems = streams.map(function (s) {
const menuItem = {
name: s.DisplayTitle,
id: s.Index
};
@ -22,7 +38,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
return menuItem;
});
require(['actionsheet'], function (actionsheet) {
import('actionsheet').then(({ default: actionsheet }) => {
actionsheet.show({
items: menuItems,
positionTo: button,
@ -31,13 +47,13 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
});
});
}
}
function showSubtitleMenu(context, player, button, item) {
var currentIndex = playbackManager.getSubtitleStreamIndex(player);
var streams = playbackManager.subtitleTracks(player);
var menuItems = streams.map(function (s) {
var menuItem = {
function showSubtitleMenu(context, player, button, item) {
const currentIndex = playbackManager.getSubtitleStreamIndex(player);
const streams = playbackManager.subtitleTracks(player);
const menuItems = streams.map(function (s) {
const menuItem = {
name: s.DisplayTitle,
id: s.Index
};
@ -54,7 +70,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
selected: currentIndex == null
});
require(['actionsheet'], function (actionsheet) {
import('actionsheet').then(({ default: actionsheet }) => {
actionsheet.show({
items: menuItems,
positionTo: button,
@ -63,15 +79,15 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
});
});
}
}
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) {
return nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo).map(function (i) {
return i.text;
}).join('<br/>');
}
}
function seriesImageUrl(item, options) {
function seriesImageUrl(item, options) {
if (item.Type !== 'Episode') {
return null;
}
@ -96,9 +112,9 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
return null;
}
}
function imageUrl(item, options) {
function imageUrl(item, options) {
options = options || {};
options.type = options.type || 'Primary';
@ -113,22 +129,22 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
return null;
}
}
function updateNowPlayingInfo(context, state, serverId) {
var item = state.NowPlayingItem;
var displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
function updateNowPlayingInfo(context, state, serverId) {
const item = state.NowPlayingItem;
const displayName = item ? getNowPlayingNameHtml(item).replace('<br/>', ' - ') : '';
if (typeof item !== 'undefined') {
var nowPlayingServerId = (item.ServerId || serverId);
const nowPlayingServerId = (item.ServerId || serverId);
if (item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') {
var songName = item.Name;
var artistsSeries = '';
var albumName = '';
const songName = item.Name;
let artistsSeries = '';
let albumName = '';
if (item.Artists != null) {
if (item.ArtistItems != null) {
for (const artist of item.ArtistItems) {
let artistName = artist.Name;
let artistId = artist.Id;
const artistName = artist.Name;
const artistId = artist.Id;
artistsSeries += `<a class="button-link emby-button" is="emby-linkbutton" href="details?id=${artistId}&serverId=${nowPlayingServerId}">${artistName}</a>`;
if (artist !== item.ArtistItems.slice(-1)[0]) {
artistsSeries += ', ';
@ -154,11 +170,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.nowPlayingSongName').innerHTML = songName;
} else if (item.Type == 'Episode') {
if (item.SeasonName != null) {
var seasonName = item.SeasonName;
const seasonName = item.SeasonName;
context.querySelector('.nowPlayingSeason').innerHTML = '<a class="button-link emby-button" is="emby-linkbutton" href="details?id=' + item.SeasonId + `&serverId=${nowPlayingServerId}">${seasonName}</a>`;
}
if (item.SeriesName != null) {
var seriesName = item.SeriesName;
const seriesName = item.SeriesName;
if (item.SeriesId != null) {
context.querySelector('.nowPlayingSerie').innerHTML = '<a class="button-link emby-button" is="emby-linkbutton" href="details?id=' + item.SeriesId + `&serverId=${nowPlayingServerId}">${seriesName}</a>`;
} else {
@ -176,7 +192,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.nowPlayingPageTitle').classList.add('hide');
}
var url = item ? seriesImageUrl(item, {
const url = item ? seriesImageUrl(item, {
maxHeight: 300
}) || imageUrl(item, {
maxHeight: 300
@ -185,14 +201,14 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
let contextButton = context.querySelector('.btnToggleContextMenu');
// We remove the previous event listener by replacing the item in each update event
const autoFocusContextButton = document.activeElement === contextButton;
let contextButtonClone = contextButton.cloneNode(true);
const contextButtonClone = contextButton.cloneNode(true);
contextButton.parentNode.replaceChild(contextButtonClone, contextButton);
contextButton = context.querySelector('.btnToggleContextMenu');
if (autoFocusContextButton) {
contextButton.focus();
}
const stopPlayback = !!layoutManager.mobile;
var options = {
const options = {
play: false,
queue: false,
stopPlayback: stopPlayback,
@ -200,7 +216,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
openAlbum: false,
positionTo: contextButton
};
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) {
apiClient.getCurrentUser().then(function (user) {
contextButton.addEventListener('click', function () {
@ -215,8 +231,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
if (item) {
backdrop.setBackdrops([item]);
apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) {
var userData = fullItem.UserData || {};
var likes = userData.Likes == null ? '' : userData.Likes;
const userData = fullItem.UserData || {};
const likes = userData.Likes == null ? '' : userData.Likes;
context.querySelector('.nowPlayingPageUserDataButtonsTitle').innerHTML = '<button is="emby-ratingbutton" type="button" class="listItemButton paper-icon-button-light" data-id="' + fullItem.Id + '" data-serverid="' + fullItem.ServerId + '" data-itemtype="' + fullItem.Type + '" data-likes="' + likes + '" data-isfavorite="' + userData.IsFavorite + '"><span class="material-icons favorite"></span></button>';
context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = '<button is="emby-ratingbutton" type="button" class="listItemButton paper-icon-button-light" data-id="' + fullItem.Id + '" data-serverid="' + fullItem.ServerId + '" data-itemtype="' + fullItem.Type + '" data-likes="' + likes + '" data-isfavorite="' + userData.IsFavorite + '"><span class="material-icons favorite"></span></button>';
});
@ -225,11 +241,11 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = '';
}
}
}
}
function setImageUrl(context, state, url) {
var item = state.NowPlayingItem;
var imgContainer = context.querySelector('.nowPlayingPageImageContainer');
function setImageUrl(context, state, url) {
const item = state.NowPlayingItem;
const imgContainer = context.querySelector('.nowPlayingPageImageContainer');
if (url) {
imgContainer.innerHTML = '<img class="nowPlayingPageImage" src="' + url + '" />';
@ -243,26 +259,26 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
} else {
imgContainer.innerHTML = '<div class="nowPlayingPageImageContainerNoAlbum"><button data-action="link" class="cardImageContainer coveredImage ' + cardBuilder.getDefaultBackgroundClass(item.Name) + ' cardContent cardContent-shadow itemAction"><span class="cardImageIcon material-icons album"></span></button></div>';
}
}
}
function buttonVisible(btn, enabled) {
function buttonVisible(btn, enabled) {
if (enabled) {
btn.classList.remove('hide');
} else {
btn.classList.add('hide');
}
}
}
function updateSupportedCommands(context, commands) {
var all = context.querySelectorAll('.btnCommand');
function updateSupportedCommands(context, commands) {
const all = context.querySelectorAll('.btnCommand');
for (var i = 0, length = all.length; i < length; i++) {
var enableButton = commands.indexOf(all[i].getAttribute('data-command')) !== -1;
for (let i = 0, length = all.length; i < length; i++) {
const enableButton = commands.indexOf(all[i].getAttribute('data-command')) !== -1;
all[i].disabled = !enableButton;
}
}
}
return function () {
export default function () {
function toggleRepeat() {
switch (playbackManager.getRepeatMode()) {
case 'RepeatAll':
@ -278,12 +294,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
function updatePlayerState(player, context, state) {
lastPlayerState = state;
var item = state.NowPlayingItem;
var playerInfo = playbackManager.getPlayerInfo();
var supportedCommands = playerInfo.supportedCommands;
const item = state.NowPlayingItem;
const playerInfo = playbackManager.getPlayerInfo();
const supportedCommands = playerInfo.supportedCommands;
currentPlayerSupportedCommands = supportedCommands;
var playState = state.PlayState || {};
var isSupportedCommands = supportedCommands.includes('DisplayMessage') || supportedCommands.includes('SendString') || supportedCommands.includes('Select');
const playState = state.PlayState || {};
const isSupportedCommands = supportedCommands.includes('DisplayMessage') || supportedCommands.includes('SendString') || supportedCommands.includes('Select');
buttonVisible(context.querySelector('.btnToggleFullscreen'), item && item.MediaType == 'Video' && supportedCommands.includes('ToggleFullscreen'));
updateAudioTracksDisplay(player, context);
updateSubtitleTracksDisplay(player, context);
@ -322,7 +338,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
buttonVisible(context.querySelector('.btnRewind'), item != null);
buttonVisible(context.querySelector('.btnFastForward'), item != null);
}
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
const positionSlider = context.querySelector('.nowPlayingPositionSlider');
if (positionSlider && item && item.RunTimeTicks) {
positionSlider.setKeyboardSteps(userSettings.skipBackLength() * 1000000 / item.RunTimeTicks,
@ -331,7 +347,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
if (positionSlider && !positionSlider.dragging) {
positionSlider.disabled = !playState.CanSeek;
var isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null;
const isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null;
positionSlider.setIsClear(isProgressClear);
}
@ -351,18 +367,18 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function updateAudioTracksDisplay(player, context) {
var supportedCommands = currentPlayerSupportedCommands;
const supportedCommands = currentPlayerSupportedCommands;
buttonVisible(context.querySelector('.btnAudioTracks'), playbackManager.audioTracks(player).length > 1 && supportedCommands.indexOf('SetAudioStreamIndex') != -1);
}
function updateSubtitleTracksDisplay(player, context) {
var supportedCommands = currentPlayerSupportedCommands;
const supportedCommands = currentPlayerSupportedCommands;
buttonVisible(context.querySelector('.btnSubtitles'), playbackManager.subtitleTracks(player).length && supportedCommands.indexOf('SetSubtitleStreamIndex') != -1);
}
function updateRepeatModeDisplay(repeatMode) {
var context = dlg;
let toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton');
const context = dlg;
const toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton');
const cssClass = 'buttonActive';
let innHtml = '<span class="material-icons repeat"></span>';
let repeatOn = true;
@ -386,8 +402,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function updatePlayerVolumeState(context, isMuted, volumeLevel) {
var view = context;
var supportedCommands = currentPlayerSupportedCommands;
const view = context;
const supportedCommands = currentPlayerSupportedCommands;
if (supportedCommands.indexOf('Mute') === -1) {
showMuteButton = false;
@ -420,8 +436,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
} else {
buttonMute.classList.toggle('hide', !showMuteButton);
var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider');
var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer');
const nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider');
const nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer');
if (nowPlayingVolumeSlider) {
nowPlayingVolumeSliderContainer.classList.toggle('hide', !showVolumeSlider);
@ -434,8 +450,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function updatePlayPauseState(isPaused, isActive) {
var context = dlg;
var btnPlayPause = context.querySelector('.btnPlayPause');
const context = dlg;
const btnPlayPause = context.querySelector('.btnPlayPause');
const btnPlayPauseIcon = btnPlayPause.querySelector('.material-icons');
btnPlayPauseIcon.classList.remove('play_circle_filled', 'pause_circle_filled');
@ -445,12 +461,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function updateTimeDisplay(positionTicks, runtimeTicks) {
var context = dlg;
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
const context = dlg;
const positionSlider = context.querySelector('.nowPlayingPositionSlider');
if (positionSlider && !positionSlider.dragging) {
if (runtimeTicks) {
var pct = positionTicks / runtimeTicks;
let pct = positionTicks / runtimeTicks;
pct *= 100;
positionSlider.value = pct;
} else {
@ -468,7 +484,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
function loadPlaylist(context, player) {
getPlaylistItems(player).then(function (items) {
var html = '';
let html = '';
let favoritesEnabled = true;
if (layoutManager.mobile) {
if (items.length > 0) {
@ -492,7 +508,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
dragHandle: true
});
var itemsContainer = context.querySelector('.playlist');
const itemsContainer = context.querySelector('.playlist');
let focusedItemPlaylistId = itemsContainer.querySelector('button:focus');
itemsContainer.innerHTML = html;
if (focusedItemPlaylistId !== null) {
@ -503,10 +519,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
}
var playlistItemId = playbackManager.getCurrentPlaylistItemId(player);
const playlistItemId = playbackManager.getCurrentPlaylistItemId(player);
if (playlistItemId) {
var img = itemsContainer.querySelector(`.listItem[data-playlistItemId="${playlistItemId}"] .listItemImage`);
const img = itemsContainer.querySelector(`.listItem[data-playlistItemId="${playlistItemId}"] .listItemImage`);
if (img) {
img.classList.remove('lazy');
@ -520,7 +536,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
function onPlaybackStart(e, state) {
console.debug('remotecontrol event: ' + e.type);
var player = this;
const player = this;
onStateChanged.call(player, e, state);
}
@ -529,12 +545,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function onShuffleQueueModeChange(updateView = true) {
let shuffleMode = playbackManager.getQueueShuffleMode(this);
let context = dlg;
const shuffleMode = playbackManager.getQueueShuffleMode(this);
const context = dlg;
const cssClass = 'buttonActive';
let shuffleButtons = context.querySelectorAll('.btnShuffleQueue');
const shuffleButtons = context.querySelectorAll('.btnShuffleQueue');
for (let shuffleButton of shuffleButtons) {
for (const shuffleButton of shuffleButtons) {
switch (shuffleMode) {
case 'Shuffle':
shuffleButton.classList.add(cssClass);
@ -556,12 +572,12 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function onPlaylistItemRemoved(e, info) {
var context = dlg;
const context = dlg;
if (info !== undefined) {
var playlistItemIds = info.playlistItemIds;
const playlistItemIds = info.playlistItemIds;
for (var i = 0, length = playlistItemIds.length; i < length; i++) {
var listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]');
for (let i = 0, length = playlistItemIds.length; i < length; i++) {
const listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]');
if (listItem) {
listItem.parentNode.removeChild(listItem);
@ -574,7 +590,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
function onPlaybackStopped(e, state) {
console.debug('remotecontrol event: ' + e.type);
var player = this;
const player = this;
if (!state.NextMediaType) {
updatePlayerState(player, dlg, {});
@ -587,29 +603,29 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function onStateChanged(event, state) {
var player = this;
const player = this;
updatePlayerState(player, dlg, state);
onPlaylistUpdate();
}
function onTimeUpdate(e) {
var now = new Date().getTime();
const now = new Date().getTime();
if (!(now - lastUpdateTime < 700)) {
lastUpdateTime = now;
var player = this;
const player = this;
currentRuntimeTicks = playbackManager.duration(player);
updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks);
}
}
function onVolumeChanged(e) {
var player = this;
const player = this;
updatePlayerVolumeState(dlg, player.isMuted(), player.getVolume());
}
function releaseCurrentPlayer() {
var player = currentPlayer;
const player = currentPlayer;
if (player) {
events.off(player, 'playbackstart', onPlaybackStart);
@ -630,7 +646,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
function bindToPlayer(context, player) {
if (releaseCurrentPlayer(), currentPlayer = player, player) {
var state = playbackManager.getPlayerState(player);
const state = playbackManager.getPlayerState(player);
onStateChanged.call(player, {
type: 'init'
}, state);
@ -646,8 +662,8 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
events.on(player, 'pause', onPlayPauseStateChanged);
events.on(player, 'unpause', onPlayPauseStateChanged);
events.on(player, 'timeupdate', onTimeUpdate);
var playerInfo = playbackManager.getPlayerInfo();
var supportedCommands = playerInfo.supportedCommands;
const playerInfo = playbackManager.getPlayerInfo();
const supportedCommands = playerInfo.supportedCommands;
currentPlayerSupportedCommands = supportedCommands;
updateSupportedCommands(context, supportedCommands);
}
@ -674,10 +690,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function savePlaylist() {
require(['playlistEditor'], function (playlistEditor) {
import('playlistEditor').then(({ default: playlistEditor }) => {
getSaveablePlaylistItems().then(function (items) {
var serverId = items.length ? items[0].ServerId : ApiClient.serverId();
new playlistEditor.showEditor({
const serverId = items.length ? items[0].ServerId : ApiClient.serverId();
new playlistEditor({
items: items.map(function (i) {
return i.Id;
}),
@ -690,10 +706,10 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function bindEvents(context) {
var btnCommand = context.querySelectorAll('.btnCommand');
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
const btnCommand = context.querySelectorAll('.btnCommand');
const positionSlider = context.querySelector('.nowPlayingPositionSlider');
for (var i = 0, length = btnCommand.length; i < length; i++) {
for (let i = 0, length = btnCommand.length; i < length; i++) {
btnCommand[i].addEventListener('click', onBtnCommandClick);
}
@ -770,22 +786,22 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
});
positionSlider.addEventListener('change', function () {
var value = this.value;
const value = this.value;
if (currentPlayer) {
var newPercent = parseFloat(value);
const newPercent = parseFloat(value);
playbackManager.seekPercent(newPercent, currentPlayer);
}
});
positionSlider.getBubbleText = function (value) {
var state = lastPlayerState;
const state = lastPlayerState;
if (!state || !state.NowPlayingItem || !currentRuntimeTicks) {
return '--:--';
}
var ticks = currentRuntimeTicks;
let ticks = currentRuntimeTicks;
ticks /= 100;
ticks *= value;
return datetime.getDisplayRunningTime(ticks);
@ -798,13 +814,13 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
context.querySelector('.buttonMute').addEventListener('click', function () {
playbackManager.toggleMute(currentPlayer);
});
var playlistContainer = context.querySelector('.playlist');
const playlistContainer = context.querySelector('.playlist');
playlistContainer.addEventListener('action-remove', function (e) {
playbackManager.removeFromPlaylist([e.detail.playlistItemId], currentPlayer);
});
playlistContainer.addEventListener('itemdrop', function (e) {
var newIndex = e.detail.newIndex;
var playlistItemId = e.detail.playlistItemId;
const newIndex = e.detail.newIndex;
const playlistItemId = e.detail.playlistItemId;
playbackManager.movePlaylistItem(playlistItemId, newIndex, currentPlayer);
});
context.querySelector('.btnSavePlaylist').addEventListener('click', savePlaylist);
@ -834,7 +850,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function onMessageSubmit(e) {
var form = e.target;
const form = e.target;
playbackManager.sendCommand({
Name: 'DisplayMessage',
Arguments: {
@ -844,7 +860,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}, currentPlayer);
form.querySelector('input').value = '';
require(['toast'], function (toast) {
import('toast').then(({ default: toast }) => {
toast('Message sent.');
});
@ -854,7 +870,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}
function onSendStringSubmit(e) {
var form = e.target;
const form = e.target;
playbackManager.sendCommand({
Name: 'SendString',
Arguments: {
@ -863,7 +879,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
}, currentPlayer);
form.querySelector('input').value = '';
require(['toast'], function (toast) {
import('toast').then(({ default: toast }) => {
toast('Text sent.');
});
@ -877,7 +893,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
volumecontrolHtml += `<button is="paper-icon-button-light" class="buttonMute autoSize" title=${globalize.translate('Mute')}><span class="xlargePaperIconButton material-icons volume_up"></span></button>`;
volumecontrolHtml += '<div class="sliderContainer nowPlayingVolumeSliderContainer"><input is="emby-slider" type="range" step="1" min="0" max="100" value="0" class="nowPlayingVolumeSlider"/></div>';
volumecontrolHtml += '</div>';
let optionsSection = context.querySelector('.playlistSectionButton');
const optionsSection = context.querySelector('.playlistSectionButton');
if (!layoutManager.mobile) {
context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', volumecontrolHtml);
optionsSection.classList.remove('align-items-center', 'justify-content-center');
@ -899,7 +915,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
events.on(playbackManager, 'playerchange', onPlayerChange);
if (layoutManager.tv) {
var positionSlider = context.querySelector('.nowPlayingPositionSlider');
const positionSlider = context.querySelector('.nowPlayingPositionSlider');
positionSlider.classList.add('focusable');
positionSlider.enableKeyboardDragging();
}
@ -915,13 +931,13 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
bindToPlayer(context, playbackManager.getCurrentPlayer());
}
var dlg;
var currentPlayer;
var lastPlayerState;
var currentPlayerSupportedCommands = [];
var lastUpdateTime = 0;
var currentRuntimeTicks = 0;
var self = this;
let dlg;
let currentPlayer;
let lastPlayerState;
let currentPlayerSupportedCommands = [];
let lastUpdateTime = 0;
let currentRuntimeTicks = 0;
const self = this;
self.init = function (ownerView, context) {
dlg = context;
@ -935,5 +951,4 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
self.destroy = function () {
onDialogClosed();
};
};
});
}

View file

@ -2,31 +2,37 @@
* Image viewer component
* @module components/slideshow/slideshow
*/
define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'dom', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost, dom) {
'use strict';
import dialogHelper from 'dialogHelper';
import inputManager from 'inputManager';
import connectionManager from 'connectionManager';
import layoutManager from 'layoutManager';
import focusManager from 'focusManager';
import browser from 'browser';
import appHost from 'apphost';
import dom from 'dom';
import 'css!./style';
import 'material-icons';
import 'paper-icon-button-light';
browser = browser.default || browser;
focusManager = focusManager.default || focusManager;
/**
/**
* Name of transition event.
*/
const transitionEndEventName = dom.whichTransitionEvent();
const transitionEndEventName = dom.whichTransitionEvent();
/**
/**
* Flag to use fake image to fix blurry zoomed image.
* At least WebKit doesn't restore quality for zoomed images.
*/
const useFakeZoomImage = browser.safari;
const useFakeZoomImage = browser.safari;
/**
/**
* Retrieves an item's image URL from the API.
* @param {object|string} item - Item used to generate the image URL.
* @param {object} options - Options of the image.
* @param {object} apiClient - API client instance used to retrieve the image.
* @returns {null|string} URL of the item's image.
*/
function getImageUrl(item, options, apiClient) {
function getImageUrl(item, options, apiClient) {
options = options || {};
options.type = options.type || 'Primary';
@ -47,16 +53,16 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
}
return null;
}
}
/**
/**
* Retrieves a backdrop's image URL from the API.
* @param {object} item - Item used to generate the image URL.
* @param {object} options - Options of the image.
* @param {object} apiClient - API client instance used to retrieve the image.
* @returns {null|string} URL of the item's backdrop.
*/
function getBackdropImageUrl(item, options, apiClient) {
function getBackdropImageUrl(item, options, apiClient) {
options = options || {};
options.type = options.type || 'Backdrop';
@ -71,16 +77,16 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
}
return null;
}
}
/**
/**
* Dispatches a request for an item's image to its respective handler.
* @param {object} item - Item used to generate the image URL.
* @returns {string} URL of the item's image.
*/
function getImgUrl(item, user) {
var apiClient = connectionManager.getApiClient(item.ServerId);
var imageOptions = {};
function getImgUrl(item, user) {
const apiClient = connectionManager.getApiClient(item.ServerId);
const imageOptions = {};
if (item.BackdropImageTags && item.BackdropImageTags.length) {
return getBackdropImageUrl(item, imageOptions, apiClient);
@ -91,9 +97,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
imageOptions.type = 'Primary';
return getImageUrl(item, imageOptions, apiClient);
}
}
}
/**
/**
* Generates a button using the specified icon, classes and properties.
* @param {string} icon - Name of the material icon on the button
* @param {string} cssClass - CSS classes to assign to the button
@ -101,36 +107,36 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* @param {boolean} autoFocus - Flag to set the autofocus attribute on the button.
* @returns {string} The HTML markup of the button.
*/
function getIcon(icon, cssClass, canFocus, autoFocus) {
var tabIndex = canFocus ? '' : ' tabindex="-1"';
function getIcon(icon, cssClass, canFocus, autoFocus) {
const tabIndex = canFocus ? '' : ' tabindex="-1"';
autoFocus = autoFocus ? ' autofocus' : '';
return '<button is="paper-icon-button-light" class="autoSize ' + cssClass + '"' + tabIndex + autoFocus + '><span class="material-icons slideshowButtonIcon ' + icon + '"></span></button>';
}
}
/**
/**
* Sets the viewport meta tag to enable or disable scaling by the user.
* @param {boolean} scalable - Flag to set the scalability of the viewport.
*/
function setUserScalable(scalable) {
function setUserScalable(scalable) {
try {
appHost.setUserScalable(scalable);
} catch (err) {
console.error('error in appHost.setUserScalable: ' + err);
}
}
}
return function (options) {
var self = this;
export default function (options) {
const self = this;
/** Initialized instance of Swiper. */
var swiperInstance;
let swiperInstance;
/** Initialized instance of the dialog containing the Swiper instance. */
var dialog;
let dialog;
/** Options of the slideshow components */
var currentOptions;
let currentOptions;
/** ID of the timeout used to hide the OSD. */
var hideTimeout;
let hideTimeout;
/** Last coordinates of the mouse pointer. */
var lastMouseMoveData;
let lastMouseMoveData;
/**
* Creates the HTML markup for the dialog and the OSD.
@ -150,12 +156,12 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
dialog.classList.add('slideshowDialog');
var html = '';
let html = '';
html += '<div class="slideshowSwiperContainer"><div class="swiper-wrapper"></div></div>';
if (options.interactive && !layoutManager.tv) {
var actionButtonsOnTop = layoutManager.mobile;
const actionButtonsOnTop = layoutManager.mobile;
html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false);
html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false);
@ -196,17 +202,17 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
dialogHelper.close(dialog);
});
var btnPause = dialog.querySelector('.btnSlideshowPause');
const btnPause = dialog.querySelector('.btnSlideshowPause');
if (btnPause) {
btnPause.addEventListener('click', playPause);
}
var btnDownload = dialog.querySelector('.btnDownload');
const btnDownload = dialog.querySelector('.btnDownload');
if (btnDownload) {
btnDownload.addEventListener('click', download);
}
var btnShare = dialog.querySelector('.btnShare');
const btnShare = dialog.querySelector('.btnShare');
if (btnShare) {
btnShare.addEventListener('click', share);
}
@ -231,7 +237,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Handles OSD changes when the autoplay is started.
*/
function onAutoplayStart() {
var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons');
const btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons');
if (btnSlideshowPause) {
btnSlideshowPause.classList.replace('play_arrow', 'pause');
}
@ -241,7 +247,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Handles OSD changes when the autoplay is stopped.
*/
function onAutoplayStop() {
var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons');
const btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons');
if (btnSlideshowPause) {
btnSlideshowPause.classList.replace('pause', 'play_arrow');
}
@ -288,14 +294,14 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* @param {Object} options - Options used to initialize the Swiper instance.
*/
function loadSwiper(dialog, options) {
var slides;
let slides;
if (currentOptions.slides) {
slides = currentOptions.slides;
} else {
slides = currentOptions.items;
}
require(['swiper'], function (Swiper) {
import('swiper').then(({default: Swiper}) => {
swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), {
direction: 'horizontal',
// Loop is disabled due to the virtual slides option not supporting it.
@ -369,7 +375,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* @returns {string} The HTML markup of the slide.
*/
function getSwiperSlideHtmlFromSlide(item) {
var html = '';
let html = '';
html += '<div class="swiper-slide" data-original="' + item.originalImage + '" data-itemid="' + item.Id + '" data-serverid="' + item.ServerId + '">';
html += '<div class="swiper-zoom-container">';
if (useFakeZoomImage) {
@ -404,7 +410,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
*/
function getCurrentImageInfo() {
if (swiperInstance) {
var slide = document.querySelector('.swiper-slide-active');
const slide = document.querySelector('.swiper-slide-active');
if (slide) {
return {
@ -424,9 +430,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Starts a download for the currently displayed slide.
*/
function download() {
var imageInfo = getCurrentImageInfo();
const imageInfo = getCurrentImageInfo();
require(['fileDownloader'], function (fileDownloader) {
import('fileDownloader').then(({default: fileDownloader}) => {
fileDownloader.download([imageInfo]);
});
}
@ -435,7 +441,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Shares the currently displayed slide using the browser's built-in sharing feature.
*/
function share() {
var imageInfo = getCurrentImageInfo();
const imageInfo = getCurrentImageInfo();
navigator.share({
url: imageInfo.shareUrl
@ -464,7 +470,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Toggles the autoplay feature of the Swiper instance.
*/
function playPause() {
var paused = !dialog.querySelector('.btnSlideshowPause .material-icons').classList.contains('pause');
const paused = !dialog.querySelector('.btnSlideshowPause .material-icons').classList.contains('pause');
if (paused) {
play();
} else {
@ -476,7 +482,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Closes the dialog and destroys the Swiper instance.
*/
function onDialogClosed() {
var swiper = swiperInstance;
const swiper = swiperInstance;
if (swiper) {
swiper.destroy(true, true);
swiperInstance = null;
@ -494,7 +500,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Shows the OSD.
*/
function showOsd() {
var bottom = dialog.querySelector('.slideshowBottomBar');
const bottom = dialog.querySelector('.slideshowBottomBar');
if (bottom) {
slideUpToShow(bottom);
startHideTimer();
@ -505,7 +511,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* Hides the OSD.
*/
function hideOsd() {
var bottom = dialog.querySelector('.slideshowBottomBar');
const bottom = dialog.querySelector('.slideshowBottomBar');
if (bottom) {
slideDownToHide(bottom);
}
@ -540,7 +546,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
element.classList.remove('hide');
var onFinish = function () {
const onFinish = function () {
focusManager.focus(element.querySelector('.btnSlideshowPause'));
};
@ -550,11 +556,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
}
requestAnimationFrame(function () {
var keyframes = [
const keyframes = [
{ transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 },
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 }
];
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
const timing = { duration: 300, iterations: 1, easing: 'ease-out' };
element.animate(keyframes, timing).onfinish = onFinish;
});
}
@ -568,7 +574,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
return;
}
var onFinish = function () {
const onFinish = function () {
element.classList.add('hide');
};
@ -578,11 +584,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
}
requestAnimationFrame(function () {
var keyframes = [
const keyframes = [
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 },
{ transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 }
];
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
const timing = { duration: 300, iterations: 1, easing: 'ease-out' };
element.animate(keyframes, timing).onfinish = onFinish;
});
}
@ -592,13 +598,13 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
* @param {Event} event - Pointer movement event.
*/
function onPointerMove(event) {
var pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse');
const pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse');
if (pointerType === 'mouse') {
var eventX = event.screenX || 0;
var eventY = event.screenY || 0;
const eventX = event.screenX || 0;
const eventY = event.screenY || 0;
var obj = lastMouseMoveData;
const obj = lastMouseMoveData;
if (!obj) {
lastMouseMoveData = {
x: eventX,
@ -664,5 +670,4 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
dialogHelper.close(dialog);
}
};
};
});
}

View file

@ -1,49 +1,51 @@
define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'globalize', 'userSettings', 'emby-select', 'paper-icon-button-light', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, layoutManager, connectionManager, globalize, userSettings) {
'use strict';
import dialogHelper from 'dialogHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import * as userSettings from 'userSettings';
import 'emby-select';
import 'paper-icon-button-light';
import 'material-icons';
import 'css!./../formdialog';
import 'emby-button';
import 'flexStyles';
focusManager = focusManager.default || focusManager;
function onSubmit(e) {
function onSubmit(e) {
e.preventDefault();
return false;
}
}
function initEditor(context, settings) {
function initEditor(context, settings) {
context.querySelector('form').addEventListener('submit', onSubmit);
context.querySelector('.selectSortOrder').value = settings.sortOrder;
context.querySelector('.selectSortBy').value = settings.sortBy;
}
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(({default: scrollHelper}) => {
const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
}
function fillSortBy(context, options) {
var selectSortBy = context.querySelector('.selectSortBy');
function fillSortBy(context, options) {
const selectSortBy = context.querySelector('.selectSortBy');
selectSortBy.innerHTML = options.map(function (o) {
return '<option value="' + o.value + '">' + o.name + '</option>';
}).join('');
}
}
function saveValues(context, settings, settingsKey) {
function saveValues(context, settingsKey) {
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
}
}
function SortMenu() {
}
SortMenu.prototype.show = function (options) {
class SortMenu {
show(options) {
return new Promise(function (resolve, reject) {
require(['text!./sortmenu.template.html'], function (template) {
var dialogOptions = {
import('text!./sortmenu.template.html').then(({default: template}) => {
const dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -54,11 +56,11 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
const dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
var html = '';
let html = '';
html += '<div class="formDialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel hide-mouse-idle-tv" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
@ -81,7 +83,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
}
var submitted;
let submitted;
dlg.querySelector('form').addEventListener('change', function () {
submitted = true;
@ -93,7 +95,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
}
if (submitted) {
saveValues(dlg, options.settings, options.settingsKey);
saveValues(dlg, options.settingsKey);
resolve();
return;
}
@ -102,7 +104,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
});
});
});
};
}
}
return SortMenu;
});
export default SortMenu;

View file

@ -1,16 +1,27 @@
define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'css!./subtitleeditor', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) {
'use strict';
import dialogHelper from 'dialogHelper';
import layoutManager from 'layoutManager';
import globalize from 'globalize';
import * as userSettings from 'userSettings';
import connectionManager from 'connectionManager';
import loading from 'loading';
import focusManager from 'focusManager';
import dom from 'dom';
import 'emby-select';
import 'listViewStyle';
import 'paper-icon-button-light';
import 'css!./../formdialog';
import 'material-icons';
import 'css!./subtitleeditor';
import 'emby-button';
import 'flexStyles';
loading = loading.default || loading;
focusManager = focusManager.default || focusManager;
let currentItem;
let hasChanges;
var currentItem;
var hasChanges;
function downloadRemoteSubtitles(context, id) {
let url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id;
function downloadRemoteSubtitles(context, id) {
var url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id;
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
apiClient.ajax({
type: 'POST',
@ -19,19 +30,19 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
}).then(function () {
hasChanges = true;
require(['toast'], function (toast) {
import('toast').then(({default: toast}) => {
toast(globalize.translate('MessageDownloadQueued'));
});
focusManager.autoFocus(context);
});
}
}
function deleteLocalSubtitle(context, index) {
var msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
function deleteLocalSubtitle(context, index) {
let msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
require(['confirm'], function (confirm) {
confirm.default({
import('confirm').then(({default: confirm}) => {
confirm({
title: globalize.translate('ConfirmDeletion'),
text: msg,
@ -41,10 +52,10 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
}).then(function () {
loading.show();
var itemId = currentItem.Id;
var url = 'Videos/' + itemId + '/Subtitles/' + index;
let itemId = currentItem.Id;
let url = 'Videos/' + itemId + '/Subtitles/' + index;
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
apiClient.ajax({
@ -57,16 +68,16 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
});
});
});
}
}
function fillSubtitleList(context, item) {
var streams = item.MediaStreams || [];
function fillSubtitleList(context, item) {
let streams = item.MediaStreams || [];
var subs = streams.filter(function (s) {
let subs = streams.filter(function (s) {
return s.Type === 'Subtitle';
});
var html = '';
let html = '';
if (subs.length) {
html += '<h2>' + globalize.translate('MySubtitles') + '</h2>';
@ -74,10 +85,10 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
html += '<div>';
html += subs.map(function (s) {
var itemHtml = '';
let itemHtml = '';
var tagName = layoutManager.tv ? 'button' : 'div';
var className = layoutManager.tv && s.Path ? 'listItem listItem-border btnDelete' : 'listItem listItem-border';
let tagName = layoutManager.tv ? 'button' : 'div';
let className = layoutManager.tv && s.Path ? 'listItem listItem-border btnDelete' : 'listItem listItem-border';
if (layoutManager.tv) {
className += ' listItem-focusscale listItem-button';
@ -116,7 +127,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
html += '</div>';
}
var elem = context.querySelector('.subtitleList');
let elem = context.querySelector('.subtitleList');
if (subs.length) {
elem.classList.remove('hide');
@ -124,32 +135,32 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
elem.classList.add('hide');
}
elem.innerHTML = html;
}
}
function fillLanguages(context, apiClient, languages) {
var selectLanguage = context.querySelector('#selectLanguage');
function fillLanguages(context, apiClient, languages) {
let selectLanguage = context.querySelector('#selectLanguage');
selectLanguage.innerHTML = languages.map(function (l) {
return '<option value="' + l.ThreeLetterISOLanguageName + '">' + l.DisplayName + '</option>';
});
var lastLanguage = userSettings.get('subtitleeditor-language');
let lastLanguage = userSettings.get('subtitleeditor-language');
if (lastLanguage) {
selectLanguage.value = lastLanguage;
} else {
apiClient.getCurrentUser().then(function (user) {
var lang = user.Configuration.SubtitleLanguagePreference;
let lang = user.Configuration.SubtitleLanguagePreference;
if (lang) {
selectLanguage.value = lang;
}
});
}
}
}
function renderSearchResults(context, results) {
var lastProvider = '';
var html = '';
function renderSearchResults(context, results) {
let lastProvider = '';
let html = '';
if (!results.length) {
context.querySelector('.noSearchResults').classList.remove('hide');
@ -160,10 +171,10 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
context.querySelector('.noSearchResults').classList.add('hide');
for (var i = 0, length = results.length; i < length; i++) {
var result = results[i];
for (let i = 0, length = results.length; i < length; i++) {
let result = results[i];
var provider = result.ProviderName;
let provider = result.ProviderName;
if (provider !== lastProvider) {
if (i > 0) {
@ -174,8 +185,8 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
lastProvider = provider;
}
var tagName = layoutManager.tv ? 'button' : 'div';
var className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border';
let tagName = layoutManager.tv ? 'button' : 'div';
let className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border';
if (layoutManager.tv) {
className += ' listItem-focusscale listItem-button';
}
@ -184,7 +195,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
html += '<span class="listItemIcon material-icons closed_caption"></span>';
var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
let bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
html += '<div class="listItemBody ' + bodyClass + '">';
@ -221,34 +232,34 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
html += '</div>';
}
var elem = context.querySelector('.subtitleResults');
let elem = context.querySelector('.subtitleResults');
elem.innerHTML = html;
loading.hide();
}
}
function searchForSubtitles(context, language) {
function searchForSubtitles(context, language) {
userSettings.set('subtitleeditor-language', language);
loading.show();
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
var url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language);
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
let url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language);
apiClient.getJSON(url).then(function (results) {
renderSearchResults(context, results);
});
}
}
function reload(context, apiClient, itemId) {
function reload(context, apiClient, itemId) {
context.querySelector('.noSearchResults').classList.add('hide');
function onGetItem(item) {
currentItem = item;
fillSubtitleList(context, item);
var file = item.Path || '';
var index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
let file = item.Path || '';
let index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
if (index > -1) {
file = file.substring(index + 1);
}
@ -269,56 +280,56 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
} else {
onGetItem(itemId);
}
}
}
function onSearchSubmit(e) {
var form = this;
function onSearchSubmit(e) {
let form = this;
var lang = form.querySelector('#selectLanguage', form).value;
let lang = form.querySelector('#selectLanguage', form).value;
searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang);
e.preventDefault();
return false;
}
}
function onSubtitleListClick(e) {
var btnDelete = dom.parentWithClass(e.target, 'btnDelete');
function onSubtitleListClick(e) {
let btnDelete = dom.parentWithClass(e.target, 'btnDelete');
if (btnDelete) {
var index = btnDelete.getAttribute('data-index');
var context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
let index = btnDelete.getAttribute('data-index');
let context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
deleteLocalSubtitle(context, index);
}
}
}
function onSubtitleResultsClick(e) {
var subtitleId;
var context;
function onSubtitleResultsClick(e) {
let subtitleId;
let context;
var btnOptions = dom.parentWithClass(e.target, 'btnOptions');
let btnOptions = dom.parentWithClass(e.target, 'btnOptions');
if (btnOptions) {
subtitleId = btnOptions.getAttribute('data-subid');
context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog');
showDownloadOptions(btnOptions, context, subtitleId);
}
var btnDownload = dom.parentWithClass(e.target, 'btnDownload');
let btnDownload = dom.parentWithClass(e.target, 'btnDownload');
if (btnDownload) {
subtitleId = btnDownload.getAttribute('data-subid');
context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog');
downloadRemoteSubtitles(context, subtitleId);
}
}
}
function showDownloadOptions(button, context, subtitleId) {
var items = [];
function showDownloadOptions(button, context, subtitleId) {
let items = [];
items.push({
name: globalize.translate('Download'),
id: 'download'
});
require(['actionsheet'], function (actionsheet) {
import('actionsheet').then(({default: actionsheet}) => {
actionsheet.show({
items: items,
positionTo: button
@ -333,22 +344,21 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
}
});
});
}
}
function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) {
scrollHelper = scrollHelper.default || scrollHelper;
var fn = on ? 'on' : 'off';
function centerFocus(elem, horiz, on) {
import('scrollHelper').then(({default: scrollHelper}) => {
let fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz);
});
}
}
function showEditorInternal(itemId, serverId, template) {
function showEditorInternal(itemId, serverId, template) {
hasChanges = false;
var apiClient = connectionManager.getApiClient(serverId);
let apiClient = connectionManager.getApiClient(serverId);
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
var dialogOptions = {
let dialogOptions = {
removeOnClose: true,
scrollY: false
};
@ -359,7 +369,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
dialogOptions.size = 'small';
}
var dlg = dialogHelper.createDialog(dialogOptions);
let dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add('formDialog');
dlg.classList.add('subtitleEditorDialog');
@ -370,7 +380,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit);
var btnSubmit = dlg.querySelector('.btnSubmit');
let btnSubmit = dlg.querySelector('.btnSubmit');
if (layoutManager.tv) {
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
@ -379,7 +389,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
btnSubmit.classList.add('hide');
}
var editorContent = dlg.querySelector('.formDialogContent');
let editorContent = dlg.querySelector('.formDialogContent');
dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick);
dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick);
@ -410,19 +420,18 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings',
reload(editorContent, apiClient, item);
});
});
}
}
function showEditor(itemId, serverId) {
function showEditor(itemId, serverId) {
loading.show();
return new Promise(function (resolve, reject) {
require(['text!./subtitleeditor.template.html'], function (template) {
import('text!./subtitleeditor.template.html').then(({default: template}) => {
showEditorInternal(itemId, serverId, template).then(resolve, reject);
});
});
}
}
return {
export default {
show: showEditor
};
});
};

View file

@ -1,16 +1,16 @@
define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, layoutManager, template, css) {
'use strict';
import playbackManager from 'playbackManager';
import layoutManager from 'layoutManager';
import template from 'text!./subtitlesync.template.html';
import 'css!./subtitlesync';
playbackManager = playbackManager.default || playbackManager;
let player;
let subtitleSyncSlider;
let subtitleSyncTextField;
let subtitleSyncCloseButton;
let subtitleSyncContainer;
var player;
var subtitleSyncSlider;
var subtitleSyncTextField;
var subtitleSyncCloseButton;
var subtitleSyncContainer;
function init(instance) {
var parent = document.createElement('div');
function init(instance) {
const parent = document.createElement('div');
document.body.appendChild(parent);
parent.innerHTML = template;
@ -29,7 +29,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
subtitleSyncContainer.classList.add('hide');
subtitleSyncTextField.updateOffset = function(offset) {
subtitleSyncTextField.updateOffset = function (offset) {
this.textContent = offset + 's';
};
@ -38,10 +38,10 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
this.hasFocus = true;
});
subtitleSyncTextField.addEventListener('keydown', function(event) {
subtitleSyncTextField.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
// if input key is enter search for float pattern
var inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
let inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
if (inputOffset) {
inputOffset = inputOffset[0];
@ -71,14 +71,14 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
event.stopPropagation();
});
subtitleSyncTextField.blur = function() {
subtitleSyncTextField.blur = function () {
// prevent textfield to blur while element has focus
if (!this.hasFocus && this.prototype) {
this.prototype.blur();
}
};
subtitleSyncSlider.updateOffset = function(percent) {
subtitleSyncSlider.updateOffset = function (percent) {
// default value is 0s = 50%
this.value = percent === undefined ? 50 : percent;
};
@ -92,56 +92,57 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
});
subtitleSyncSlider.getBubbleHtml = function (value) {
var newOffset = getOffsetFromPercentage(value);
const newOffset = getOffsetFromPercentage(value);
return '<h1 class="sliderBubbleText">' +
(newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's' +
'</h1>';
};
subtitleSyncCloseButton.addEventListener('click', function() {
subtitleSyncCloseButton.addEventListener('click', function () {
playbackManager.disableShowingSubtitleOffset(player);
SubtitleSync.prototype.toggle('forceToHide');
});
instance.element = parent;
}
}
function getOffsetFromPercentage(value) {
function getOffsetFromPercentage(value) {
// convert percent to fraction
var offset = (value - 50) / 50;
let offset = (value - 50) / 50;
// multiply by offset min/max range value (-x to +x) :
offset *= 30;
return offset.toFixed(1);
}
}
function getPercentageFromOffset(value) {
function getPercentageFromOffset(value) {
// divide by offset min/max range value (-x to +x) :
var percentValue = value / 30;
let percentValue = value / 30;
// convert fraction to percent
percentValue *= 50;
percentValue += 50;
return Math.min(100, Math.max(0, percentValue.toFixed()));
}
}
function SubtitleSync(currentPlayer) {
class SubtitleSync {
constructor(currentPlayer) {
player = currentPlayer;
init(this);
}
SubtitleSync.prototype.destroy = function() {
destroy() {
SubtitleSync.prototype.toggle('forceToHide');
if (player) {
playbackManager.disableShowingSubtitleOffset(player);
playbackManager.setSubtitleOffset(0, player);
}
var elem = this.element;
const elem = this.element;
if (elem) {
elem.parentNode.removeChild(elem);
this.element = null;
}
};
}
SubtitleSync.prototype.toggle = function(action) {
toggle(action) {
if (player && playbackManager.supportSubtitleOffset(player)) {
/* eslint-disable no-fallthrough */
switch (action) {
@ -170,7 +171,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html',
}
/* eslint-enable no-fallthrough */
}
};
}
}
return SubtitleSync;
});
export default SubtitleSync;

View file

@ -1,7 +1,9 @@
define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (backdrop, mainTabsManager, layoutManager) {
'use strict';
import backdrop from 'backdrop';
import * as mainTabsManager from 'mainTabsManager';
import layoutManager from 'layoutManager';
import 'emby-tabs';
function onViewDestroy(e) {
function onViewDestroy(e) {
var tabControllers = this.tabControllers;
if (tabControllers) {
@ -18,13 +20,14 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (
this.params = null;
this.currentTabController = null;
this.initialTabIndex = null;
}
}
function onBeforeTabChange() {
function onBeforeTabChange() {
}
}
function TabbedView(view, params) {
class TabbedView {
constructor(view, params) {
this.tabControllers = [];
this.view = view;
this.params = params;
@ -85,7 +88,7 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (
view.addEventListener('viewdestroy', onViewDestroy.bind(this));
}
TabbedView.prototype.onResume = function (options) {
onResume(options) {
this.setTitle();
backdrop.clearBackdrop();
@ -96,19 +99,18 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (
} else if (currentTabController && currentTabController.onResume) {
currentTabController.onResume({});
}
};
}
TabbedView.prototype.onPause = function () {
onPause() {
var currentTabController = this.currentTabController;
if (currentTabController && currentTabController.onPause) {
currentTabController.onPause();
}
};
TabbedView.prototype.setTitle = function () {
}
setTitle() {
Emby.Page.setTitle('');
};
}
}
return TabbedView;
});
export default TabbedView;

View file

@ -3,6 +3,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'connectionManager', 'globalize'
browser = browser.default || browser;
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
focusManager = focusManager.default || focusManager;
scrollHelper = scrollHelper.default || scrollHelper;

View file

@ -1,6 +1,8 @@
define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize, userSettings) {
'use strict';
layoutManager = layoutManager.default || layoutManager;
function onSubmit(e) {
e.preventDefault();
return false;

View file

@ -1,43 +1,43 @@
define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'emby-button'], function ($, loading, libraryMenu, globalize, connectionManager) {
'use strict';
import $ from 'jQuery';
import loading from 'loading';
import globalize from 'globalize';
import 'emby-button';
loading = loading.default || loading;
function populateHistory(packageInfo, page) {
let html = '';
const length = Math.min(packageInfo.versions.length, 10);
function populateHistory(packageInfo, page) {
var html = '';
var length = Math.min(packageInfo.versions.length, 10);
for (var i = 0; i < length; i++) {
var version = packageInfo.versions[i];
for (let i = 0; i < length; i++) {
let version = packageInfo.versions[i];
html += '<h2 style="margin:.5em 0;">' + version.version + '</h2>';
html += '<div style="margin-bottom:1.5em;">' + version.changelog + '</div>';
}
$('#revisionHistory', page).html(html);
}
}
function populateVersions(packageInfo, page, installedPlugin) {
var html = '';
function populateVersions(packageInfo, page, installedPlugin) {
let html = '';
for (var i = 0; i < packageInfo.versions.length; i++) {
var version = packageInfo.versions[i];
for (let i = 0; i < packageInfo.versions.length; i++) {
const version = packageInfo.versions[i];
html += '<option value="' + version.version + '">' + version.version + '</option>';
}
var selectmenu = $('#selectVersion', page).html(html);
const selectmenu = $('#selectVersion', page).html(html);
if (!installedPlugin) {
$('#pCurrentVersion', page).hide().html('');
}
var packageVersion = packageInfo.versions[0];
const packageVersion = packageInfo.versions[0];
if (packageVersion) {
selectmenu.val(packageVersion.version);
}
}
}
function renderPackage(pkg, installedPlugins, page) {
var installedPlugin = installedPlugins.filter(function (ip) {
function renderPackage(pkg, installedPlugins, page) {
const installedPlugin = installedPlugins.filter(function (ip) {
return ip.Name == pkg.name;
})[0];
@ -58,64 +58,66 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'e
$('#developer', page).html(pkg.owner);
if (installedPlugin) {
var currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
$('#pCurrentVersion', page).show().html(currentVersionText);
} else {
$('#pCurrentVersion', page).hide().html('');
}
loading.hide();
}
}
function alertText(options) {
require(['alert'], function ({default: alert}) {
function alertText(options) {
import('alert').then(({default: alert}) => {
alert(options);
});
}
}
function performInstallation(page, name, guid, version) {
var developer = $('#developer', page).html().toLowerCase();
function performInstallation(page, name, guid, version) {
const developer = $('#developer', page).html().toLowerCase();
var alertCallback = function () {
const alertCallback = function () {
loading.show();
page.querySelector('#btnInstall').disabled = true;
ApiClient.installPlugin(name, guid, version).then(function () {
ApiClient.installPlugin(name, guid, version).then(() => {
loading.hide();
alertText(globalize.translate('MessagePluginInstalled'));
}).catch(() => {
alertText(globalize.translate('MessagePluginInstallError'));
});
};
if (developer !== 'jellyfin') {
loading.hide();
var msg = globalize.translate('MessagePluginInstallDisclaimer');
let msg = globalize.translate('MessagePluginInstallDisclaimer');
msg += '<br/>';
msg += '<br/>';
msg += globalize.translate('PleaseConfirmPluginInstallation');
require(['confirm'], function (confirm) {
confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
import('confirm').then(({default: confirm}) => {
confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
alertCallback();
}, function () {
}).catch(() => {
console.debug('plugin not installed');
});
});
} else {
alertCallback();
}
}
}
return function (view, params) {
export default function(view, params) {
$('.addPluginForm', view).on('submit', function () {
loading.show();
var page = $(this).parents('#addPluginPage')[0];
var name = params.name;
var guid = params.guid;
const page = $(this).parents('#addPluginPage')[0];
const name = params.name;
const guid = params.guid;
ApiClient.getInstalledPlugins().then(function (plugins) {
var installedPlugin = plugins.filter(function (plugin) {
const installedPlugin = plugins.filter(function (plugin) {
return plugin.Name == name;
})[0];
var version = $('#selectVersion', page).val();
const version = $('#selectVersion', page).val();
if (installedPlugin && installedPlugin.Version === version) {
loading.hide();
Dashboard.alert({
@ -125,19 +127,20 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'e
} else {
performInstallation(page, name, guid, version);
}
}).catch(() => {
alertText(globalize.translate('MessageGetInstalledPluginsError'));
});
return false;
});
view.addEventListener('viewshow', function () {
var page = this;
const page = this;
loading.show();
var name = params.name;
var guid = params.guid;
var promise1 = ApiClient.getPackageInfo(name, guid);
var promise2 = ApiClient.getInstalledPlugins();
const name = params.name;
const guid = params.guid;
const promise1 = ApiClient.getPackageInfo(name, guid);
const promise2 = ApiClient.getInstalledPlugins();
Promise.all([promise1, promise2]).then(function (responses) {
renderPackage(responses[0], responses[1], page);
});
});
};
});
}

View file

@ -1,12 +1,15 @@
define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) {
'use strict';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import globalize from 'globalize';
import 'cardStyle';
import 'emby-button';
import 'emby-checkbox';
import 'emby-select';
loading = loading.default || loading;
function reloadList(page) {
function reloadList(page) {
loading.show();
var promise1 = ApiClient.getAvailablePlugins();
var promise2 = ApiClient.getInstalledPlugins();
const promise1 = ApiClient.getAvailablePlugins();
const promise2 = ApiClient.getInstalledPlugins();
Promise.all([promise1, promise2]).then(function (responses) {
populateList({
catalogElement: page.querySelector('#pluginTiles'),
@ -15,10 +18,11 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
installedPlugins: responses[1]
});
});
}
}
function getHeaderText(category) {
function getHeaderText(category) {
category = category.replace(' ', '');
// TODO: Replace with switch
if (category === 'Channel') {
category = 'Channels';
} else if (category === 'Theme') {
@ -30,11 +34,11 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
}
return globalize.translate(category);
}
}
function populateList(options) {
var availablePlugins = options.availablePlugins;
var installedPlugins = options.installedPlugins;
function populateList(options) {
const availablePlugins = options.availablePlugins;
const installedPlugins = options.installedPlugins;
availablePlugins.forEach(function (plugin, index, array) {
plugin.category = plugin.category || 'General';
@ -56,12 +60,12 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
return 0;
});
var currentCategory = null;
var html = '';
let currentCategory = null;
let html = '';
for (var i = 0; i < availablePlugins.length; i++) {
var plugin = availablePlugins[i];
var category = plugin.categoryDisplayName;
for (let i = 0; i < availablePlugins.length; i++) {
const plugin = availablePlugins[i];
const category = plugin.categoryDisplayName;
if (category != currentCategory) {
if (currentCategory) {
html += '</div>';
@ -83,17 +87,17 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
options.catalogElement.innerHTML = html;
loading.hide();
}
}
function getPluginHtml(plugin, options, installedPlugins) {
var html = '';
var href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
function getPluginHtml(plugin, options, installedPlugins) {
let html = '';
let href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
if (options.context) {
href += '&context=' + options.context;
}
var target = plugin.externalUrl ? ' target="_blank"' : '';
const target = plugin.externalUrl ? ' target="_blank"' : '';
html += "<div class='card backdropCard'>";
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable visualCardBox-cardScalable">';
@ -106,7 +110,7 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
html += "<div class='cardText'>";
html += plugin.name;
html += '</div>';
var installedPlugin = installedPlugins.filter(function (ip) {
const installedPlugin = installedPlugins.filter(function (ip) {
return ip.Id == plugin.guid;
})[0];
html += "<div class='cardText cardText-secondary'>";
@ -115,9 +119,9 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
html += '</div>';
html += '</div>';
return html += '</div>';
}
}
function getTabs() {
function getTabs() {
return [{
href: 'installedplugins.html',
name: globalize.translate('TabMyPlugins')
@ -128,16 +132,11 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby
href: 'repositories.html',
name: globalize.translate('TabRepositories')
}];
}
}
window.PluginCatalog = {
renderCatalog: populateList
};
return function (view, params) {
export default function (view) {
view.addEventListener('viewshow', function () {
libraryMenu.setTabs('plugins', 1, getTabs);
reloadList(this);
});
};
});
}

View file

@ -1,12 +1,14 @@
define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'], function (loading, libraryMenu, dom, globalize) {
'use strict';
import loading from 'loading';
import libraryMenu from 'libraryMenu';
import dom from 'dom';
import globalize from 'globalize';
import 'cardStyle';
import 'emby-button';
loading = loading.default || loading;
function deletePlugin(page, uniqueid, name) {
const msg = globalize.translate('UninstallPluginConfirmation', name);
function deletePlugin(page, uniqueid, name) {
var msg = globalize.translate('UninstallPluginConfirmation', name);
require(['confirm'], function (confirm) {
import('confirm').then(({default: confirm}) => {
confirm.default({
title: globalize.translate('HeaderUninstallPlugin'),
text: msg,
@ -19,26 +21,26 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
});
});
});
}
}
function showNoConfigurationMessage() {
function showNoConfigurationMessage() {
Dashboard.alert({
message: globalize.translate('MessageNoPluginConfiguration')
});
}
}
function showConnectMessage() {
function showConnectMessage() {
Dashboard.alert({
message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess')
});
}
}
function getPluginCardHtml(plugin, pluginConfigurationPages) {
var configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
function getPluginCardHtml(plugin, pluginConfigurationPages) {
const configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
return pluginConfigurationPage.PluginId == plugin.Id;
})[0];
var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null;
var html = '';
const configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null;
let html = '';
html += "<div data-id='" + plugin.Id + "' data-name='" + plugin.Name + "' data-removable='" + plugin.CanUninstall + "' class='card backdropCard'>";
html += '<div class="cardBox visualCardBox">';
html += '<div class="cardScalable">';
@ -65,15 +67,15 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
html += '</div>';
html += '</div>';
return html;
}
}
function renderPlugins(page, plugins) {
function renderPlugins(page, plugins) {
ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) {
populateList(page, plugins, configPages);
});
}
}
function populateList(page, plugins, pluginConfigurationPages) {
function populateList(page, plugins, pluginConfigurationPages) {
plugins = plugins.sort(function (plugin1, plugin2) {
if (plugin1.Name > plugin2.Name) {
return 1;
@ -82,11 +84,11 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
return -1;
});
var html = plugins.map(function (p) {
let html = plugins.map(function (p) {
return getPluginCardHtml(p, pluginConfigurationPages);
}).join('');
var installedPluginsElement = page.querySelector('.installedPlugins');
const installedPluginsElement = page.querySelector('.installedPlugins');
installedPluginsElement.removeEventListener('click', onInstalledPluginsClick);
installedPluginsElement.addEventListener('click', onInstalledPluginsClick);
@ -104,15 +106,15 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
installedPluginsElement.innerHTML = html;
loading.hide();
}
}
function showPluginMenu(page, elem) {
var card = dom.parentWithClass(elem, 'card');
var id = card.getAttribute('data-id');
var name = card.getAttribute('data-name');
var removable = card.getAttribute('data-removable');
var configHref = card.querySelector('.cardContent').getAttribute('href');
var menuItems = [];
function showPluginMenu(page, elem) {
const card = dom.parentWithClass(elem, 'card');
const id = card.getAttribute('data-id');
const name = card.getAttribute('data-name');
const removable = card.getAttribute('data-removable');
const configHref = card.querySelector('.cardContent').getAttribute('href');
const menuItems = [];
if (configHref) {
menuItems.push({
@ -130,7 +132,7 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
});
}
require(['actionsheet'], function (actionsheet) {
import('actionsheet').then(({default: actionsheet}) => {
actionsheet.show({
items: menuItems,
positionTo: elem,
@ -146,16 +148,16 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
}
});
});
}
}
function reloadList(page) {
function reloadList(page) {
loading.show();
ApiClient.getInstalledPlugins().then(function (plugins) {
renderPlugins(page, plugins);
});
}
}
function getTabs() {
function getTabs() {
return [{
href: 'installedplugins.html',
name: globalize.translate('TabMyPlugins')
@ -166,27 +168,26 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'
href: 'repositories.html',
name: globalize.translate('TabRepositories')
}];
}
}
function onInstalledPluginsClick(e) {
function onInstalledPluginsClick(e) {
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
showNoConfigurationMessage();
} else if (dom.parentWithClass(e.target, 'connectModePluginCard')) {
showConnectMessage();
} else {
var btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu');
const btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu');
if (btnCardMenu) {
showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu);
}
}
}
}
pageIdOn('pageshow', 'pluginsPage', function () {
pageIdOn('pageshow', 'pluginsPage', function () {
libraryMenu.setTabs('plugins', 0, getTabs);
reloadList(this);
});
window.PluginsPage = {
renderPlugins: renderPlugins
};
});
window.PluginsPage = {
renderPlugins: renderPlugins
};

View file

@ -1,7 +1,33 @@
define(['tabbedView', 'globalize', 'require', 'emby-tabs', 'emby-button', 'emby-scroller'], function (TabbedView, globalize, require) {
'use strict';
import TabbedView from 'tabbedView';
import globalize from 'globalize';
import 'emby-tabs';
import 'emby-button';
import 'emby-scroller';
function getTabs() {
class HomeView extends TabbedView {
constructor(view, params) {
super(view, params);
}
setTitle() {
Emby.Page.setTitle(null);
}
onPause() {
super.onPause(this);
document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader');
}
onResume(options) {
super.onResume(this, options);
document.querySelector('.skinHeader').classList.add('noHomeButtonHeader');
}
getDefaultTabIndex() {
return 0;
}
getTabs() {
return [{
name: globalize.translate('Home')
}, {
@ -9,67 +35,34 @@ define(['tabbedView', 'globalize', 'require', 'emby-tabs', 'emby-button', 'emby-
}];
}
function getDefaultTabIndex() {
return 0;
}
function getRequirePromise(deps) {
return new Promise(function (resolve, reject) {
require(deps, resolve);
});
}
function getTabController(index) {
getTabController(index) {
if (index == null) {
throw new Error('index cannot be null');
}
var depends = [];
let depends = '';
switch (index) {
case 0:
depends.push('controllers/hometab');
depends = 'controllers/hometab';
break;
case 1:
depends.push('controllers/favorites');
depends = 'controllers/favorites';
}
var instance = this;
return getRequirePromise(depends).then(function (controllerFactory) {
var controller = instance.tabControllers[index];
const instance = this;
return import(depends).then(({ default: controllerFactory }) => {
let controller = instance.tabControllers[index];
if (!controller) {
controller = new controllerFactory.default(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params);
controller = new controllerFactory(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params);
instance.tabControllers[index] = controller;
}
return controller;
});
}
}
function HomeView(view, params) {
TabbedView.call(this, view, params);
}
Object.assign(HomeView.prototype, TabbedView.prototype);
HomeView.prototype.getTabs = getTabs;
HomeView.prototype.getDefaultTabIndex = getDefaultTabIndex;
HomeView.prototype.getTabController = getTabController;
HomeView.prototype.setTitle = function () {
Emby.Page.setTitle(null);
};
HomeView.prototype.onPause = function () {
TabbedView.prototype.onPause.call(this);
document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader');
};
HomeView.prototype.onResume = function (options) {
TabbedView.prototype.onResume.call(this, options);
document.querySelector('.skinHeader').classList.add('noHomeButtonHeader');
};
return HomeView;
});
export default HomeView;

View file

@ -1,3 +1,4 @@
import appHost from 'apphost';
import loading from 'loading';
import appRouter from 'appRouter';
import layoutManager from 'layoutManager';
@ -657,7 +658,7 @@ import 'emby-select';
setPeopleHeader(page, item);
loading.hide();
if (item.Type === 'Book') {
if (item.Type === 'Book' && item.CanDownload && appHost.supports('filedownload')) {
hideAll(page, 'btnDownload', true);
}

View file

@ -1,12 +1,21 @@
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';
import globalize from 'globalize';
import listView from 'listView';
import layoutManager from 'layoutManager';
import * as userSettings from 'userSettings';
import focusManager from 'focusManager';
import cardBuilder from 'cardBuilder';
import loading from 'loading';
import connectionManager from 'connectionManager';
import AlphaNumericShortcuts from 'alphaNumericShortcuts';
import playbackManager from 'playbackManager';
import AlphaPicker from 'alphaPicker';
import 'emby-itemscontainer';
import 'emby-scroller';
playbackManager = playbackManager.default || playbackManager;
loading = loading.default || loading;
focusManager = focusManager.default || focusManager;
/* eslint-disable indent */
function getInitialLiveTvQuery(instance, params) {
var query = {
const query = {
UserId: connectionManager.getApiClient(params.serverId).getCurrentUserId(),
StartIndex: 0,
Fields: 'ChannelInfo,PrimaryImageAspectRatio',
@ -63,7 +72,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function modifyQueryWithFilters(instance, query) {
var sortValues = instance.getSortValues();
const sortValues = instance.getSortValues();
if (!query.SortBy) {
query.SortBy = sortValues.sortBy;
@ -72,9 +81,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
query.Fields = query.Fields ? query.Fields + ',PrimaryImageAspectRatio' : 'PrimaryImageAspectRatio';
query.ImageTypeLimit = 1;
var hasFilters;
var queryFilters = [];
var filters = instance.getFilters();
let hasFilters;
const queryFilters = [];
const filters = instance.getFilters();
if (filters.IsPlayed) {
queryFilters.push('IsPlayed');
@ -168,21 +177,21 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function updateSortText(instance) {
var btnSortText = instance.btnSortText;
const btnSortText = instance.btnSortText;
if (btnSortText) {
var options = instance.getSortMenuOptions();
var values = instance.getSortValues();
var sortBy = values.sortBy;
const options = instance.getSortMenuOptions();
const values = instance.getSortValues();
const sortBy = values.sortBy;
for (var i = 0, length = options.length; i < length; i++) {
if (sortBy === options[i].value) {
btnSortText.innerHTML = globalize.translate('SortByValue', options[i].name);
for (const option of options) {
if (sortBy === option.value) {
btnSortText.innerHTML = globalize.translate('SortByValue', option.name);
break;
}
}
var btnSortIcon = instance.btnSortIcon;
const btnSortIcon = instance.btnSortIcon;
if (btnSortIcon) {
setSortButtonIcon(btnSortIcon, values.sortOrder === 'Descending' ? 'arrow_downward' : 'arrow_upward');
@ -202,10 +211,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
function updateAlphaPickerState(instance, numItems) {
if (instance.alphaPicker) {
var alphaPicker = instance.alphaPickerElement;
const alphaPicker = instance.alphaPickerElement;
if (alphaPicker) {
var values = instance.getSortValues();
const values = instance.getSortValues();
if (numItems == null) {
numItems = 100;
@ -223,7 +232,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function getItems(instance, params, item, sortBy, startIndex, limit) {
var apiClient = connectionManager.getApiClient(params.serverId);
const apiClient = connectionManager.getApiClient(params.serverId);
instance.queryRecursive = false;
if (params.type === 'Recordings') {
@ -252,7 +261,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
if (!item) {
instance.queryRecursive = true;
var method = 'getItems';
let method = 'getItems';
if (params.type === 'MusicArtist') {
method = 'getArtists';
@ -275,7 +284,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
if (item.Type === 'Genre' || item.Type === 'MusicGenre' || item.Type === 'Studio' || item.Type === 'Person') {
instance.queryRecursive = true;
var query = {
const query = {
StartIndex: startIndex,
Limit: limit,
Fields: 'PrimaryImageAspectRatio,SortName',
@ -324,8 +333,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
return Promise.resolve(null);
}
var apiClient = connectionManager.getApiClient(params.serverId);
var itemId = params.genreId || params.musicGenreId || params.studioId || params.personId || params.parentId;
const apiClient = connectionManager.getApiClient(params.serverId);
const itemId = params.genreId || params.musicGenreId || params.studioId || params.personId || params.parentId;
if (itemId) {
return apiClient.getItem(apiClient.getCurrentUserId(), itemId);
@ -335,9 +344,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function showViewSettingsMenu() {
var instance = this;
const instance = this;
require(['viewSettings'], function (ViewSettings) {
import('viewSettings').then(({default: ViewSettings}) => {
new ViewSettings().show({
settingsKey: instance.getSettingsKey(),
settings: instance.getViewSettings(),
@ -350,9 +359,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function showFilterMenu() {
var instance = this;
const instance = this;
require(['filterMenu'], function (FilterMenu) {
import('filterMenu').then(({default: FilterMenu}) => {
new FilterMenu().show({
settingsKey: instance.getSettingsKey(),
settings: instance.getFilters(),
@ -369,9 +378,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function showSortMenu() {
var instance = this;
const instance = this;
require(['sortMenu'], function (SortMenu) {
import('sortMenu').then(({default: SortMenu}) => {
new SortMenu().show({
settingsKey: instance.getSettingsKey(),
settings: instance.getSortValues(),
@ -387,10 +396,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function onNewItemClick() {
var instance = this;
const instance = this;
require(['playlistEditor'], function (playlistEditor) {
new playlistEditor.showEditor({
import('playlistEditor').then(({default: playlistEditor}) => {
new playlistEditor({
items: [],
serverId: instance.params.serverId
});
@ -398,22 +407,23 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function hideOrShowAll(elems, hide) {
for (var i = 0, length = elems.length; i < length; i++) {
for (const elem of elems) {
if (hide) {
elems[i].classList.add('hide');
elem.classList.add('hide');
} else {
elems[i].classList.remove('hide');
elem.classList.remove('hide');
}
}
}
function bindAll(elems, eventName, fn) {
for (var i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener(eventName, fn);
for (const elem of elems) {
elem.addEventListener(eventName, fn);
}
}
function ItemsView(view, params) {
class ItemsView {
constructor(view, params) {
function fetchData() {
return getItems(self, params, self.currentItem).then(function (result) {
if (self.totalItemCount == null) {
@ -426,7 +436,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function getItemsHtml(items) {
var settings = self.getViewSettings();
const settings = self.getViewSettings();
if (settings.imageType === 'list') {
return listView.getListViewHtml({
@ -434,13 +444,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
});
}
var shape;
var preferThumb;
var preferDisc;
var preferLogo;
var defaultShape;
var item = self.currentItem;
var lines = settings.showTitle ? 2 : 0;
let shape;
let preferThumb;
let preferDisc;
let preferLogo;
let defaultShape;
const item = self.currentItem;
let lines = settings.showTitle ? 2 : 0;
if (settings.imageType === 'banner') {
shape = 'banner';
@ -464,7 +474,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
shape = 'autoVertical';
}
var posterOptions = {
let posterOptions = {
shape: shape,
showTitle: settings.showTitle,
showYear: settings.showTitle,
@ -497,19 +507,19 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
lines = 1;
} else if (params.type === 'Programs') {
lines = settings.showTitle ? 1 : 0;
var showParentTitle = settings.showTitle && params.IsMovie !== 'true';
const showParentTitle = settings.showTitle && params.IsMovie !== 'true';
if (showParentTitle) {
lines++;
}
var showAirTime = settings.showTitle && params.type !== 'Recordings';
const showAirTime = settings.showTitle && params.type !== 'Recordings';
if (showAirTime) {
lines++;
}
var showYear = settings.showTitle && params.IsMovie === 'true' && params.type === 'Recordings';
const showYear = settings.showTitle && params.IsMovie === 'true' && params.type === 'Recordings';
if (showYear) {
lines++;
@ -542,13 +552,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
function initAlphaPicker() {
self.scroller = view.querySelector('.scrollFrameY');
var alphaPickerElement = self.alphaPickerElement;
const alphaPickerElement = self.alphaPickerElement;
alphaPickerElement.classList.add('alphaPicker-fixed-right');
alphaPickerElement.classList.add('focuscontainer-right');
self.itemsContainer.parentNode.classList.add('padded-right-withalphapicker');
self.alphaPicker = new AlphaPicker.default({
self.alphaPicker = new AlphaPicker({
element: alphaPickerElement,
itemsContainer: layoutManager.tv ? self.itemsContainer : null,
itemClass: 'card',
@ -653,7 +663,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function play() {
var currentItem = self.currentItem;
const currentItem = self.currentItem;
if (currentItem && !self.hasFilters) {
playbackManager.play({
@ -669,7 +679,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function queue() {
var currentItem = self.currentItem;
const currentItem = self.currentItem;
if (currentItem && !self.hasFilters) {
playbackManager.queue({
@ -685,7 +695,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
function shuffle() {
var currentItem = self.currentItem;
const currentItem = self.currentItem;
if (currentItem && !self.hasFilters) {
playbackManager.shuffle(currentItem);
@ -698,7 +708,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
}
var self = this;
const self = this;
self.params = params;
this.itemsContainer = view.querySelector('.itemsContainer');
@ -712,20 +722,17 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
this.itemsContainer.setAttribute('data-refreshinterval', '300000');
}
var i;
var length;
var btnViewSettings = view.querySelectorAll('.btnViewSettings');
const btnViewSettings = view.querySelectorAll('.btnViewSettings');
for (i = 0, length = btnViewSettings.length; i < length; i++) {
btnViewSettings[i].addEventListener('click', showViewSettingsMenu.bind(this));
for (const btnViewSetting of btnViewSettings) {
btnViewSetting.addEventListener('click', showViewSettingsMenu.bind(this));
}
var filterButtons = view.querySelectorAll('.btnFilter');
const filterButtons = view.querySelectorAll('.btnFilter');
this.filterButtons = filterButtons;
var hasVisibleFilters = this.getVisibleFilters().length;
const hasVisibleFilters = this.getVisibleFilters().length;
for (i = 0, length = filterButtons.length; i < length; i++) {
var btnFilter = filterButtons[i];
for (const btnFilter of filterButtons) {
btnFilter.addEventListener('click', showFilterMenu.bind(this));
if (hasVisibleFilters) {
@ -735,10 +742,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
}
var sortButtons = view.querySelectorAll('.btnSort');
const sortButtons = view.querySelectorAll('.btnSort');
for (this.sortButtons = sortButtons, i = 0, length = sortButtons.length; i < length; i++) {
var sortButton = sortButtons[i];
this.sortButtons = sortButtons;
for (const sortButton of sortButtons) {
sortButton.addEventListener('click', showSortMenu.bind(this));
if (params.type !== 'nextup') {
@ -753,7 +760,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
self.itemsContainer.fetchData = fetchData;
self.itemsContainer.getItemsHtml = getItemsHtml;
view.addEventListener('viewshow', function (e) {
var isRestored = e.detail.isRestored;
const isRestored = e.detail.isRestored;
if (!isRestored) {
loading.show();
@ -765,7 +772,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
getItem(params).then(function (item) {
setTitle(item);
self.currentItem = item;
var refresh = !isRestored;
const refresh = !isRestored;
self.itemsContainer.resume({
refresh: refresh
}).then(function () {
@ -780,7 +787,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
initAlphaPicker();
}
var itemType = item ? item.Type : null;
const itemType = item ? item.Type : null;
if (itemType === 'MusicGenre' || params.type !== 'Programs' && itemType !== 'Channel') {
hideOrShowAll(view.querySelectorAll('.btnPlay'), false);
@ -807,18 +814,18 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
bindAll(view.querySelectorAll('.btnShuffle'), 'click', shuffle);
}
self.alphaNumericShortcuts = new AlphaNumericShortcuts.default({
self.alphaNumericShortcuts = new AlphaNumericShortcuts({
itemsContainer: self.itemsContainer
});
});
view.addEventListener('viewhide', function (e) {
var itemsContainer = self.itemsContainer;
const itemsContainer = self.itemsContainer;
if (itemsContainer) {
itemsContainer.pause();
}
var alphaNumericShortcuts = self.alphaNumericShortcuts;
const alphaNumericShortcuts = self.alphaNumericShortcuts;
if (alphaNumericShortcuts) {
alphaNumericShortcuts.destroy();
@ -846,8 +853,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
});
}
ItemsView.prototype.getFilters = function () {
var basekey = this.getSettingsKey();
getFilters() {
const basekey = this.getSettingsKey();
return {
IsPlayed: userSettings.getFilter(basekey + '-filter-IsPlayed') === 'true',
IsUnplayed: userSettings.getFilter(basekey + '-filter-IsUnplayed') === 'true',
@ -866,39 +873,37 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
HasThemeVideo: userSettings.getFilter(basekey + '-filter-HasThemeVideo'),
GenreIds: userSettings.getFilter(basekey + '-filter-GenreIds')
};
};
}
ItemsView.prototype.getSortValues = function () {
var basekey = this.getSettingsKey();
getSortValues() {
const basekey = this.getSettingsKey();
return {
sortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(),
sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending'
};
};
}
ItemsView.prototype.getDefaultSortBy = function () {
var params = this.params;
var sortNameOption = this.getNameSortOption(params);
getDefaultSortBy() {
const sortNameOption = this.getNameSortOption(this.params);
if (params.type) {
if (this.params.type) {
return sortNameOption.value;
}
return 'IsFolder,' + sortNameOption.value;
};
}
ItemsView.prototype.getSortMenuOptions = function () {
var sortBy = [];
var params = this.params;
getSortMenuOptions() {
const sortBy = [];
if (params.type === 'Programs') {
if (this.params.type === 'Programs') {
sortBy.push({
name: globalize.translate('AirDate'),
value: 'StartDate,SortName'
});
}
var option = this.getNameSortOption(params);
let option = this.getNameSortOption(this.params);
if (option) {
sortBy.push(option);
@ -916,7 +921,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
sortBy.push(option);
}
if (params.type !== 'Programs') {
if (this.params.type !== 'Programs') {
sortBy.push({
name: globalize.translate('DateAdded'),
value: 'DateCreated,SortName'
@ -929,8 +934,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
sortBy.push(option);
}
if (!params.type) {
option = this.getNameSortOption(params);
if (!this.params.type) {
option = this.getNameSortOption(this.params);
sortBy.push({
name: globalize.translate('Folders'),
value: 'IsFolder,' + option.value
@ -956,9 +961,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
value: 'Runtime,SortName'
});
return sortBy;
};
}
ItemsView.prototype.getNameSortOption = function (params) {
getNameSortOption(params) {
if (params.type === 'Episode') {
return {
name: globalize.translate('Name'),
@ -970,9 +975,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
name: globalize.translate('Name'),
value: 'SortName'
};
};
}
ItemsView.prototype.getPlayCountSortOption = function () {
getPlayCountSortOption() {
if (this.params.type === 'Programs') {
return null;
}
@ -981,9 +986,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
name: globalize.translate('PlayCount'),
value: 'PlayCount,SortName'
};
};
}
ItemsView.prototype.getDatePlayedSortOption = function () {
getDatePlayedSortOption() {
if (this.params.type === 'Programs') {
return null;
}
@ -992,9 +997,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
name: globalize.translate('DatePlayed'),
value: 'DatePlayed,SortName'
};
};
}
ItemsView.prototype.getCriticRatingSortOption = function () {
getCriticRatingSortOption() {
if (this.params.type === 'Programs') {
return null;
}
@ -1003,18 +1008,18 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
name: globalize.translate('CriticRating'),
value: 'CriticRating,SortName'
};
};
}
ItemsView.prototype.getCommunityRatingSortOption = function () {
getCommunityRatingSortOption() {
return {
name: globalize.translate('CommunityRating'),
value: 'CommunityRating,SortName'
};
};
}
ItemsView.prototype.getVisibleFilters = function () {
var filters = [];
var params = this.params;
getVisibleFilters() {
const filters = [];
const params = this.params;
if (!(params.type === 'nextup')) {
if (params.type === 'Programs') {
@ -1038,16 +1043,15 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
return filters;
};
}
ItemsView.prototype.setFilterStatus = function (hasFilters) {
setFilterStatus(hasFilters) {
this.hasFilters = hasFilters;
var filterButtons = this.filterButtons;
const filterButtons = this.filterButtons;
if (filterButtons.length) {
for (var i = 0, length = filterButtons.length; i < length; i++) {
var btnFilter = filterButtons[i];
var bubble = btnFilter.querySelector('.filterButtonBubble');
for (const btnFilter of filterButtons) {
let bubble = btnFilter.querySelector('.filterButtonBubble');
if (!bubble) {
if (!hasFilters) {
@ -1066,10 +1070,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
}
}
};
}
ItemsView.prototype.getFilterMenuOptions = function () {
var params = this.params;
getFilterMenuOptions() {
const params = this.params;
return {
IsAiring: params.IsAiring,
IsMovie: params.IsMovie,
@ -1079,11 +1083,11 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
IsSeries: params.IsSeries,
Recursive: this.queryRecursive
};
};
}
ItemsView.prototype.getVisibleViewSettings = function () {
var item = (this.params, this.currentItem);
var fields = ['showTitle'];
getVisibleViewSettings() {
const item = (this.params, this.currentItem);
const fields = ['showTitle'];
if (!item || item.Type !== 'PhotoAlbum' && item.Type !== 'ChannelFolderItem') {
fields.push('imageType');
@ -1091,13 +1095,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
fields.push('viewType');
return fields;
};
}
ItemsView.prototype.getViewSettings = function () {
var basekey = this.getSettingsKey();
var params = this.params;
var item = this.currentItem;
var showTitle = userSettings.get(basekey + '-showTitle');
getViewSettings() {
const basekey = this.getSettingsKey();
const params = this.params;
const item = this.currentItem;
let showTitle = userSettings.get(basekey + '-showTitle');
if (showTitle === 'true') {
showTitle = true;
@ -1109,7 +1113,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
showTitle = true;
}
var imageType = userSettings.get(basekey + '-imageType');
let imageType = userSettings.get(basekey + '-imageType');
if (!imageType && params.type === 'nextup') {
imageType = 'thumb';
@ -1121,10 +1125,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
imageType: imageType || 'primary',
viewType: userSettings.get(basekey + '-viewType') || 'images'
};
};
}
ItemsView.prototype.getItemTypes = function () {
var params = this.params;
getItemTypes() {
const params = this.params;
if (params.type === 'nextup') {
return ['Episode'];
@ -1135,12 +1139,12 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
return [];
};
}
ItemsView.prototype.getSettingsKey = function () {
var values = [];
getSettingsKey() {
const values = [];
values.push('items');
var params = this.params;
const params = this.params;
if (params.type) {
values.push(params.type);
@ -1197,7 +1201,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager'
}
return values.join('-');
};
}
}
return ItemsView;
});
export default ItemsView;
/* eslint-enable indent */

View file

@ -1,9 +1,8 @@
define(['tvguide'], function (tvguide) {
'use strict';
import tvguide from 'tvguide';
return function (view, params, tabContent) {
var guideInstance;
var self = this;
export default function (view, params, tabContent) {
let guideInstance;
const self = this;
self.renderTab = function () {
if (!guideInstance) {
@ -25,5 +24,4 @@ define(['tvguide'], function (tvguide) {
guideInstance.pause();
}
};
};
});
}

View file

@ -2,6 +2,7 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
'use strict';
loading = loading.default || loading;
layoutManager = layoutManager.default || layoutManager;
function enableScrollX() {
return !layoutManager.desktop;
@ -255,6 +256,8 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
}
require(depends, function (controllerFactory) {
controllerFactory = controllerFactory.default || controllerFactory;
var tabContent;
if (index == 0) {

View file

@ -137,8 +137,7 @@
}
@media screen
and (min-device-width: 992px)
and (-webkit-min-device-pixel-ratio: 1) {
and (min-device-width: 992px) {
.splashLogo {
background-image: url(assets/img/banner-light.png);
}

View file

@ -1,114 +1,109 @@
define(["events", "playbackManager", "pluginManager", "inputManager", "connectionManager", "userSettings"], function (events, playbackManager, pluginManager, inputManager, connectionManager, userSettings) {
"use strict";
import events from 'events';
import playbackManager from 'playbackManager';
import pluginManager from 'pluginManager';
import inputManager from 'inputManager';
import connectionManager from 'connectionManager';
import * as userSettings from 'userSettings';
playbackManager = playbackManager.default || playbackManager;
function getMinIdleTime() {
function getMinIdleTime() {
// Returns the minimum amount of idle time required before the screen saver can be displayed
//time units used Millisecond
return 180000;
}
}
var lastFunctionalEvent = 0;
let lastFunctionalEvent = 0;
function getFunctionalEventIdleTime() {
function getFunctionalEventIdleTime() {
return new Date().getTime() - lastFunctionalEvent;
}
}
events.on(playbackManager, "playbackstop", function (e, stopInfo) {
var state = stopInfo.state;
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == "Video") {
events.on(playbackManager, 'playbackstop', function (e, stopInfo) {
const state = stopInfo.state;
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
lastFunctionalEvent = new Date().getTime();
}
});
});
function getScreensaverPlugin(isLoggedIn) {
var option;
function getScreensaverPlugin(isLoggedIn) {
let option;
try {
option = userSettings.get("screensaver", false);
option = userSettings.get('screensaver', false);
} catch (err) {
option = isLoggedIn ? "backdropscreensaver" : "logoscreensaver";
option = isLoggedIn ? 'backdropscreensaver' : 'logoscreensaver';
}
var plugins = pluginManager.ofType("screensaver");
for (var i = 0, length = plugins.length; i < length; i++) {
var plugin = plugins[i];
const plugins = pluginManager.ofType('screensaver');
for (const plugin of plugins) {
if (plugin.id === option) {
return plugin;
}
}
return null;
}
}
function ScreenSaverManager() {
var self = this;
var activeScreenSaver;
function ScreenSaverManager() {
let activeScreenSaver;
function showScreenSaver(screensaver) {
if (activeScreenSaver) {
throw new Error("An existing screensaver is already active.");
throw new Error('An existing screensaver is already active.');
}
console.debug("Showing screensaver " + screensaver.name);
console.debug('Showing screensaver ' + screensaver.name);
screensaver.show();
activeScreenSaver = screensaver;
if (screensaver.hideOnClick !== false) {
window.addEventListener("click", hide, true);
window.addEventListener('click', hide, true);
}
if (screensaver.hideOnMouse !== false) {
window.addEventListener("mousemove", hide, true);
window.addEventListener('mousemove', hide, true);
}
if (screensaver.hideOnKey !== false) {
window.addEventListener("keydown", hide, true);
window.addEventListener('keydown', hide, true);
}
}
function hide() {
if (activeScreenSaver) {
console.debug("Hiding screensaver");
console.debug('Hiding screensaver');
activeScreenSaver.hide();
activeScreenSaver = null;
}
window.removeEventListener("click", hide, true);
window.removeEventListener("mousemove", hide, true);
window.removeEventListener("keydown", hide, true);
window.removeEventListener('click', hide, true);
window.removeEventListener('mousemove', hide, true);
window.removeEventListener('keydown', hide, true);
}
self.isShowing = function () {
this.isShowing = () => {
return activeScreenSaver != null;
};
self.show = function () {
var isLoggedIn;
var apiClient = connectionManager.currentApiClient();
this.show = function () {
let isLoggedIn;
const apiClient = connectionManager.currentApiClient();
if (apiClient && apiClient.isLoggedIn()) {
isLoggedIn = true;
}
var screensaver = getScreensaverPlugin(isLoggedIn);
const screensaver = getScreensaverPlugin(isLoggedIn);
if (screensaver) {
showScreenSaver(screensaver);
}
};
self.hide = function () {
this.hide = function () {
hide();
};
function onInterval() {
if (self.isShowing()) {
const onInterval = () => {
if (this.isShowing()) {
return;
}
@ -124,11 +119,10 @@ define(["events", "playbackManager", "pluginManager", "inputManager", "connectio
return;
}
self.show();
}
this.show();
};
setInterval(onInterval, 10000);
}
}
return new ScreenSaverManager();
});
export default new ScreenSaverManager;

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,21 @@
define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelper'], function (events, browser, require, appHost, appSettings, htmlMediaHelper) {
'use strict';
import events from 'events';
import browser from 'browser';
import appHost from 'apphost';
import * as htmlMediaHelper from 'htmlMediaHelper';
function getDefaultProfile() {
return new Promise(function (resolve, reject) {
require(['browserdeviceprofile'], function (profileBuilder) {
resolve(profileBuilder({}));
function getDefaultProfile() {
return import('browserdeviceprofile').then(({ default: profileBuilder }) => {
return profileBuilder({});
});
});
}
}
var fadeTimeout;
function fade(instance, elem, startingVolume) {
let fadeTimeout;
function fade(instance, elem, startingVolume) {
instance._isFadingOut = true;
// Need to record the starting volume on each pass rather than querying elem.volume
// This is due to iOS safari not allowing volume changes and always returning the system volume value
var newVolume = Math.max(0, startingVolume - 0.15);
const newVolume = Math.max(0, startingVolume - 0.15);
console.debug('fading volume to ' + newVolume);
elem.volume = newVolume;
@ -30,17 +30,17 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
fade(instance, elem, newVolume).then(resolve, reject);
}, 100);
});
}
}
function cancelFadeTimeout() {
var timeout = fadeTimeout;
function cancelFadeTimeout() {
const timeout = fadeTimeout;
if (timeout) {
clearTimeout(timeout);
fadeTimeout = null;
}
}
}
function supportsFade() {
function supportsFade() {
if (browser.tv) {
// Not working on tizen.
// We could possibly enable on other tv's, but all smart tv browsers tend to be pretty primitive
@ -48,16 +48,16 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
}
return true;
}
}
function requireHlsPlayer(callback) {
require(['hlsjs'], function (hls) {
function requireHlsPlayer(callback) {
import('hlsjs').then(({ default: hls }) => {
window.Hls = hls;
callback();
});
}
}
function enableHlsPlayer(url, item, mediaSource, mediaType) {
function enableHlsPlayer(url, item, mediaSource, mediaType) {
if (!htmlMediaHelper.enableHlsJsPlayer(mediaSource.RunTimeTicks, mediaType)) {
return Promise.reject();
}
@ -68,12 +68,12 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
// issue head request to get content type
return new Promise(function (resolve, reject) {
require(['fetchHelper'], function (fetchHelper) {
import('fetchHelper').then((fetchHelper) => {
fetchHelper.ajax({
url: url,
type: 'HEAD'
}).then(function (response) {
var contentType = (response.headers.get('Content-Type') || '').toLowerCase();
const contentType = (response.headers.get('Content-Type') || '').toLowerCase();
if (contentType === 'application/x-mpegurl') {
resolve();
} else {
@ -82,10 +82,11 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
}, reject);
});
});
}
}
function HtmlAudioPlayer() {
var self = this;
class HtmlAudioPlayer {
constructor() {
const self = this;
self.name = 'Html Audio Player';
self.type = 'mediaplayer';
@ -99,7 +100,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
self._timeUpdated = false;
self._currentTime = null;
var elem = createMediaElement();
const elem = createMediaElement();
return setCurrentSrc(elem, options);
};
@ -109,11 +110,11 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
unBindEvents(elem);
bindEvents(elem);
var val = options.url;
let val = options.url;
console.debug('playing url: ' + val);
// Convert to seconds
var seconds = (options.playerStartPositionTicks || 0) / 10000000;
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
if (seconds) {
val += '#t=' + seconds;
}
@ -122,7 +123,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
self._currentPlayOptions = options;
var crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource);
const crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource);
if (crossOrigin) {
elem.crossOrigin = crossOrigin;
}
@ -130,9 +131,9 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
return enableHlsPlayer(val, options.item, options.mediaSource, 'Audio').then(function () {
return new Promise(function (resolve, reject) {
requireHlsPlayer(function () {
var hls = new Hls({
const hls = new Hls({
manifestLoadingTimeOut: 20000,
xhrSetup: function(xhr, url) {
xhrSetup: function (xhr, url) {
xhr.withCredentials = true;
}
});
@ -183,8 +184,8 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
self.stop = function (destroyPlayer) {
cancelFadeTimeout();
var elem = self._mediaElement;
var src = self._currentSrc;
const elem = self._mediaElement;
const src = self._currentSrc;
if (elem && src) {
if (!destroyPlayer || !supportsFade()) {
@ -198,7 +199,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
return Promise.resolve();
}
var originalVolume = elem.volume;
const originalVolume = elem.volume;
return fade(self, elem, elem.volume).then(function () {
elem.pause();
@ -219,7 +220,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
};
function createMediaElement() {
var elem = self._mediaElement;
let elem = self._mediaElement;
if (elem) {
return elem;
@ -248,7 +249,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
function onTimeUpdate() {
// Get the player position + the transcoding offset
var time = this.currentTime;
const time = this.currentTime;
// Don't trigger events after user stop
if (!self._isFadingOut) {
@ -287,11 +288,11 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
}
function onError() {
var errorCode = this.error ? (this.error.code || 0) : 0;
var errorMessage = this.error ? (this.error.message || '') : '';
const errorCode = this.error ? (this.error.code || 0) : 0;
const errorMessage = this.error ? (this.error.message || '') : '';
console.error('media element error: ' + errorCode.toString() + ' ' + errorMessage);
var type;
let type;
switch (errorCode) {
case 1:
@ -325,59 +326,59 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
}
}
HtmlAudioPlayer.prototype.currentSrc = function () {
currentSrc() {
return this._currentSrc;
};
}
HtmlAudioPlayer.prototype.canPlayMediaType = function (mediaType) {
canPlayMediaType(mediaType) {
return (mediaType || '').toLowerCase() === 'audio';
};
}
HtmlAudioPlayer.prototype.getDeviceProfile = function (item) {
getDeviceProfile(item) {
if (appHost.getDeviceProfile) {
return appHost.getDeviceProfile(item);
}
return getDefaultProfile();
};
}
// Save this for when playback stops, because querying the time at that point might return 0
HtmlAudioPlayer.prototype.currentTime = function (val) {
var mediaElement = this._mediaElement;
currentTime(val) {
const mediaElement = this._mediaElement;
if (mediaElement) {
if (val != null) {
mediaElement.currentTime = val / 1000;
return;
}
var currentTime = this._currentTime;
const currentTime = this._currentTime;
if (currentTime) {
return currentTime * 1000;
}
return (mediaElement.currentTime || 0) * 1000;
}
};
}
HtmlAudioPlayer.prototype.duration = function (val) {
var mediaElement = this._mediaElement;
duration(val) {
const mediaElement = this._mediaElement;
if (mediaElement) {
var duration = mediaElement.duration;
const duration = mediaElement.duration;
if (htmlMediaHelper.isValidDuration(duration)) {
return duration * 1000;
}
}
return null;
};
}
HtmlAudioPlayer.prototype.seekable = function () {
var mediaElement = this._mediaElement;
seekable() {
const mediaElement = this._mediaElement;
if (mediaElement) {
var seekable = mediaElement.seekable;
const seekable = mediaElement.seekable;
if (seekable && seekable.length) {
var start = seekable.start(0);
var end = seekable.end(0);
let start = seekable.start(0);
let end = seekable.end(0);
if (!htmlMediaHelper.isValidDuration(start)) {
start = 0;
@ -391,124 +392,120 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
return false;
}
};
}
HtmlAudioPlayer.prototype.getBufferedRanges = function () {
var mediaElement = this._mediaElement;
getBufferedRanges() {
const mediaElement = this._mediaElement;
if (mediaElement) {
return htmlMediaHelper.getBufferedRanges(this, mediaElement);
}
return [];
};
}
HtmlAudioPlayer.prototype.pause = function () {
var mediaElement = this._mediaElement;
pause() {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.pause();
}
};
}
// This is a retry after error
HtmlAudioPlayer.prototype.resume = function () {
var mediaElement = this._mediaElement;
resume() {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.play();
}
};
}
HtmlAudioPlayer.prototype.unpause = function () {
var mediaElement = this._mediaElement;
unpause() {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.play();
}
};
}
HtmlAudioPlayer.prototype.paused = function () {
var mediaElement = this._mediaElement;
paused() {
const mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.paused;
}
return false;
};
}
HtmlAudioPlayer.prototype.setPlaybackRate = function (value) {
var mediaElement = this._mediaElement;
setPlaybackRate(value) {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.playbackRate = value;
}
};
}
HtmlAudioPlayer.prototype.getPlaybackRate = function () {
var mediaElement = this._mediaElement;
getPlaybackRate() {
const mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.playbackRate;
}
return null;
};
}
HtmlAudioPlayer.prototype.setVolume = function (val) {
var mediaElement = this._mediaElement;
setVolume(val) {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.volume = val / 100;
}
};
}
HtmlAudioPlayer.prototype.getVolume = function () {
var mediaElement = this._mediaElement;
getVolume() {
const mediaElement = this._mediaElement;
if (mediaElement) {
return Math.min(Math.round(mediaElement.volume * 100), 100);
}
};
}
HtmlAudioPlayer.prototype.volumeUp = function () {
volumeUp() {
this.setVolume(Math.min(this.getVolume() + 2, 100));
};
}
HtmlAudioPlayer.prototype.volumeDown = function () {
volumeDown() {
this.setVolume(Math.max(this.getVolume() - 2, 0));
};
}
HtmlAudioPlayer.prototype.setMute = function (mute) {
var mediaElement = this._mediaElement;
setMute(mute) {
const mediaElement = this._mediaElement;
if (mediaElement) {
mediaElement.muted = mute;
}
};
}
HtmlAudioPlayer.prototype.isMuted = function () {
var mediaElement = this._mediaElement;
isMuted() {
const mediaElement = this._mediaElement;
if (mediaElement) {
return mediaElement.muted;
}
return false;
};
}
HtmlAudioPlayer.prototype.destroy = function () {
supports(feature) {
if (!supportedFeatures) {
supportedFeatures = getSupportedFeatures();
}
};
return supportedFeatures.indexOf(feature) !== -1;
}
}
var supportedFeatures;
let supportedFeatures;
function getSupportedFeatures() {
var list = [];
var audio = document.createElement('audio');
function getSupportedFeatures() {
const list = [];
const audio = document.createElement('audio');
if (typeof audio.playbackRate === 'number') {
list.push('PlaybackRate');
}
return list;
}
}
HtmlAudioPlayer.prototype.supports = function (feature) {
if (!supportedFeatures) {
supportedFeatures = getSupportedFeatures();
}
return supportedFeatures.indexOf(feature) !== -1;
};
return HtmlAudioPlayer;
});
export default HtmlAudioPlayer;

View file

@ -1,16 +1,17 @@
define(['pluginManager'], function (pluginManager) {
return function () {
var self = this;
import pluginManager from 'pluginManager';
export default function () {
const self = this;
self.name = 'Logo ScreenSaver';
self.type = 'screensaver';
self.id = 'logoscreensaver';
self.supportsAnonymous = true;
var interval;
let interval;
function animate() {
var animations = [
const animations = [
bounceInLeft,
bounceInRight,
@ -21,10 +22,10 @@ define(['pluginManager'], function (pluginManager) {
rotateOut
];
var elem = document.querySelector('.logoScreenSaverImage');
const elem = document.querySelector('.logoScreenSaverImage');
if (elem && elem.animate) {
var random = getRandomInt(0, animations.length - 1);
const random = getRandomInt(0, animations.length - 1);
animations[random](elem, 1);
}
@ -35,41 +36,41 @@ define(['pluginManager'], function (pluginManager) {
}
function bounceInLeft(elem, iterations) {
var keyframes = [
const keyframes = [
{ transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 },
{ transform: 'translate3d(25px, 0, 0)', opacity: '1', offset: 0.6 },
{ transform: 'translate3d(-100px, 0, 0)', offset: 0.75 },
{ transform: 'translate3d(5px, 0, 0)', offset: 0.9 },
{ transform: 'none', opacity: '1', offset: 1 }];
var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
const timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
return elem.animate(keyframes, timing);
}
function bounceInRight(elem, iterations) {
var keyframes = [
const keyframes = [
{ transform: 'translate3d(3000px, 0, 0)', opacity: '0', offset: 0 },
{ transform: 'translate3d(-25px, 0, 0)', opacity: '1', offset: 0.6 },
{ transform: 'translate3d(100px, 0, 0)', offset: 0.75 },
{ transform: 'translate3d(-5px, 0, 0)', offset: 0.9 },
{ transform: 'none', opacity: '1', offset: 1 }];
var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
const timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' };
return elem.animate(keyframes, timing);
}
function swing(elem, iterations) {
var keyframes = [
const keyframes = [
{ transform: 'translate(0%)', offset: 0 },
{ transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 },
{ transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 },
{ transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 },
{ transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 },
{ transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
const timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function tada(elem, iterations) {
var keyframes = [
const keyframes = [
{ transform: 'scale3d(1, 1, 1)', offset: 0 },
{ transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 },
{ transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 },
@ -81,41 +82,41 @@ define(['pluginManager'], function (pluginManager) {
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 },
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 },
{ transform: 'scale3d(1, 1, 1)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
const timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function wobble(elem, iterations) {
var keyframes = [
const keyframes = [
{ transform: 'translate(0%)', offset: 0 },
{ transform: 'translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)', offset: 0.15 },
{ transform: 'translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)', offset: 0.45 },
{ transform: 'translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)', offset: 0.6 },
{ transform: 'translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)', offset: 0.75 },
{ transform: 'translateX(0%)', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
const timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function rotateIn(elem, iterations) {
var keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 },
const keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 },
{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
const timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function rotateOut(elem, iterations) {
var keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 },
const keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 },
{ transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }];
var timing = { duration: 900, iterations: iterations };
const timing = { duration: 900, iterations: iterations };
return elem.animate(keyframes, timing);
}
function fadeOut(elem, iterations) {
var keyframes = [
const keyframes = [
{ opacity: '1', offset: 0 },
{ opacity: '0', offset: 1 }];
var timing = { duration: 400, iterations: iterations };
const timing = { duration: 400, iterations: iterations };
return elem.animate(keyframes, timing);
}
@ -127,8 +128,8 @@ define(['pluginManager'], function (pluginManager) {
}
self.show = function () {
require(['css!' + pluginManager.mapPath(self, 'style.css')], function () {
var elem = document.querySelector('.logoScreenSaver');
import('css!' + pluginManager.mapPath(self, 'style.css')).then(() => {
let elem = document.querySelector('.logoScreenSaver');
if (!elem) {
elem = document.createElement('div');
@ -146,20 +147,19 @@ define(['pluginManager'], function (pluginManager) {
self.hide = function () {
stopInterval();
var elem = document.querySelector('.logoScreenSaver');
const elem = document.querySelector('.logoScreenSaver');
if (elem) {
var onAnimationFinish = function () {
const onAnimationFinish = function () {
elem.parentNode.removeChild(elem);
};
if (elem.animate) {
var animation = fadeOut(elem, 1);
const animation = fadeOut(elem, 1);
animation.onfinish = onAnimationFinish;
} else {
onAnimationFinish();
}
}
};
};
});
}

View file

@ -1,33 +1,26 @@
define(['connectionManager', 'globalize'], function (connectionManager, globalize) {
'use strict';
import connectionManager from 'connectionManager';
import globalize from 'globalize';
function getRequirePromise(deps) {
return new Promise(function (resolve, reject) {
require(deps, resolve);
function showErrorMessage() {
return import('alert').then(({default: alert}) => {
return alert(globalize.translate('MessagePlayAccessRestricted'));
});
}
}
function showErrorMessage() {
return getRequirePromise(['alert']).then(function (alert) {
return alert(globalize.translate('MessagePlayAccessRestricted')).then(function () {
return Promise.reject();
});
});
}
function PlayAccessValidation() {
class PlayAccessValidation {
constructor() {
this.name = 'Playback validation';
this.type = 'preplayintercept';
this.id = 'playaccessvalidation';
this.order = -2;
}
PlayAccessValidation.prototype.intercept = function (options) {
var item = options.item;
intercept(options) {
const item = options.item;
if (!item) {
return Promise.resolve();
}
var serverId = item.ServerId;
const serverId = item.ServerId;
if (!serverId) {
return Promise.resolve();
}
@ -44,7 +37,7 @@ define(['connectionManager', 'globalize'], function (connectionManager, globaliz
return showErrorMessage();
});
};
}
}
return PlayAccessValidation;
});
export default PlayAccessValidation;

View file

@ -1,8 +1,13 @@
define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryBrowser', 'globalize', 'emby-itemscontainer', 'emby-button'], function (connectionManager, listView, cardBuilder, imageLoader, libraryBrowser, globalize) {
'use strict';
import connectionManager from 'connectionManager';
import listView from 'listView';
import cardBuilder from 'cardBuilder';
import imageLoader from 'imageLoader';
import globalize from 'globalize';
import 'emby-itemscontainer';
import 'emby-button';
function renderItems(page, item) {
var sections = [];
function renderItems(page, item) {
const sections = [];
if (item.ArtistCount) {
sections.push({
@ -11,7 +16,7 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
});
}
if (item.ProgramCount && item.Type == 'Person') {
if (item.ProgramCount && item.Type === 'Person') {
sections.push({
name: globalize.translate('HeaderUpcomingOnTV'),
type: 'Program'
@ -60,10 +65,10 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
});
}
var elem = page.querySelector('#childrenContent');
const elem = page.querySelector('#childrenContent');
elem.innerHTML = sections.map(function (section) {
var html = '';
var sectionClass = 'verticalSection';
let html = '';
let sectionClass = 'verticalSection';
if (section.type === 'Audio') {
sectionClass += ' verticalSection-extrabottompadding';
@ -78,16 +83,17 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
html += '</div>';
html += '<div is="emby-itemscontainer" class="itemsContainer padded-right">';
html += '</div>';
return html += '</div>';
html += '</div>';
return html;
}).join('');
var sectionElems = elem.querySelectorAll('.verticalSection');
const sectionElems = elem.querySelectorAll('.verticalSection');
for (var i = 0, length = sectionElems.length; i < length; i++) {
for (let i = 0, length = sectionElems.length; i < length; i++) {
renderSection(page, item, sectionElems[i], sectionElems[i].getAttribute('data-type'));
}
}
}
function renderSection(page, item, element, type) {
function renderSection(page, item, element, type) {
switch (type) {
case 'Program':
loadItems(element, item, type, {
@ -254,15 +260,15 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
artist: true
});
}
}
}
function loadItems(element, item, type, query, listOptions) {
function loadItems(element, item, type, query, listOptions) {
query = getQuery(query, item);
getItemsFunction(query, item)(query.StartIndex, query.Limit, query.Fields).then(function (result) {
var html = '';
let html = '';
if (query.Limit && result.TotalRecordCount > query.Limit) {
var link = element.querySelector('a');
const link = element.querySelector('a');
link.classList.remove('hide');
link.setAttribute('href', getMoreItemsHref(item, type));
} else {
@ -270,9 +276,9 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
}
listOptions.items = result.Items;
var itemsContainer = element.querySelector('.itemsContainer');
const itemsContainer = element.querySelector('.itemsContainer');
if (type == 'Audio') {
if (type === 'Audio') {
html = listView.getListViewHtml(listOptions);
itemsContainer.classList.remove('vertical-wrap');
itemsContainer.classList.add('vertical-list');
@ -285,50 +291,50 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
itemsContainer.innerHTML = html;
imageLoader.lazyChildren(itemsContainer);
});
}
}
function getMoreItemsHref(item, type) {
if (item.Type == 'Genre') {
function getMoreItemsHref(item, type) {
if (item.Type === 'Genre') {
return 'list.html?type=' + type + '&genreId=' + item.Id + '&serverId=' + item.ServerId;
}
if (item.Type == 'MusicGenre') {
if (item.Type === 'MusicGenre') {
return 'list.html?type=' + type + '&musicGenreId=' + item.Id + '&serverId=' + item.ServerId;
}
if (item.Type == 'Studio') {
if (item.Type === 'Studio') {
return 'list.html?type=' + type + '&studioId=' + item.Id + '&serverId=' + item.ServerId;
}
if (item.Type == 'MusicArtist') {
if (item.Type === 'MusicArtist') {
return 'list.html?type=' + type + '&artistId=' + item.Id + '&serverId=' + item.ServerId;
}
if (item.Type == 'Person') {
if (item.Type === 'Person') {
return 'list.html?type=' + type + '&personId=' + item.Id + '&serverId=' + item.ServerId;
}
return 'list.html?type=' + type + '&parentId=' + item.Id + '&serverId=' + item.ServerId;
}
}
function addCurrentItemToQuery(query, item) {
if (item.Type == 'Person') {
function addCurrentItemToQuery(query, item) {
if (item.Type === 'Person') {
query.PersonIds = item.Id;
} else if (item.Type == 'Genre') {
} else if (item.Type === 'Genre') {
query.Genres = item.Name;
} else if (item.Type == 'MusicGenre') {
} else if (item.Type === 'MusicGenre') {
query.Genres = item.Name;
} else if (item.Type == 'GameGenre') {
} else if (item.Type === 'GameGenre') {
query.Genres = item.Name;
} else if (item.Type == 'Studio') {
} else if (item.Type === 'Studio') {
query.StudioIds = item.Id;
} else if (item.Type == 'MusicArtist') {
} else if (item.Type === 'MusicArtist') {
query.AlbumArtistIds = item.Id;
}
}
}
function getQuery(options, item) {
var query = {
function getQuery(options, item) {
let query = {
SortOrder: 'Ascending',
IncludeItemTypes: '',
Recursive: true,
@ -340,10 +346,10 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
query = Object.assign(query, options || {});
addCurrentItemToQuery(query, item);
return query;
}
}
function getItemsFunction(options, item) {
var query = getQuery(options, item);
function getItemsFunction(options, item) {
const query = getQuery(options, item);
return function (index, limit, fields) {
query.StartIndex = index;
query.Limit = limit;
@ -352,7 +358,7 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
query.Fields += ',' + fields;
}
var apiClient = connectionManager.getApiClient(item.ServerId);
const apiClient = connectionManager.getApiClient(item.ServerId);
if (query.IncludeItemTypes === 'MusicArtist') {
query.IncludeItemTypes = null;
@ -361,9 +367,8 @@ define(['connectionManager', 'listView', 'cardBuilder', 'imageLoader', 'libraryB
return apiClient.getItems(apiClient.getCurrentUserId(), query);
};
}
}
window.ItemsByName = {
window.ItemsByName = {
renderItems: renderItems
};
});
};

View file

@ -119,7 +119,10 @@ export function getQueryPagingHtml (options) {
}
export function showSortMenu (options) {
require(['dialogHelper', 'emby-radio'], function (dialogHelper) {
Promise.all([
import('dialogHelper'),
import('emby-radio')
]).then(([{default: dialogHelper}]) => {
function onSortByChange() {
var newValue = this.value;

View file

@ -1,12 +1,26 @@
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';
import dom from 'dom';
import layoutManager from 'layoutManager';
import inputManager from 'inputManager';
import connectionManager from 'connectionManager';
import events from 'events';
import viewManager from 'viewManager';
import appRouter from 'appRouter';
import appHost from 'apphost';
import playbackManager from 'playbackManager';
import syncPlayManager from 'syncPlayManager';
import groupSelectionMenu from 'groupSelectionMenu';
import browser from 'browser';
import globalize from 'globalize';
import imageHelper from 'scripts/imagehelper';
import 'paper-icon-button-light';
import 'material-icons';
import 'scrollStyles';
import 'flexStyles';
viewManager = viewManager.default || viewManager;
playbackManager = playbackManager.default || playbackManager;
browser = browser.default || browser;
/* eslint-disable indent */
function renderHeader() {
var html = '';
let html = '';
html += '<div class="flex align-items-center flex-grow headerTop">';
html += '<div class="headerLeft">';
html += '<button type="button" is="paper-icon-button-light" class="headerButton headerButtonLeft headerBackButton hide"><span class="material-icons ' + (browser.safari ? 'chevron_left' : 'arrow_back') + '"></span></button>';
@ -50,7 +64,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function lazyLoadViewMenuBarImages() {
require(['imageLoader'], function (imageLoader) {
import('imageLoader').then(({default: imageLoader}) => {
imageLoader.lazyChildren(skinHeader);
});
}
@ -60,11 +74,11 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateUserInHeader(user) {
var hasImage;
let hasImage;
if (user && user.name) {
if (user.imageUrl) {
var url = user.imageUrl;
const url = user.imageUrl;
updateHeaderUserButton(url);
hasImage = true;
}
@ -91,9 +105,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
headerCastButton.classList.remove('hide');
}
var policy = user.Policy ? user.Policy : user.localUser.Policy;
const policy = user.Policy ? user.Policy : user.localUser.Policy;
var apiClient = getCurrentApiClient();
const apiClient = getCurrentApiClient();
if (headerSyncButton && policy && policy.SyncPlayAccess !== 'None' && apiClient.isMinServerVersion('10.6.0')) {
headerSyncButton.classList.remove('hide');
}
@ -143,7 +157,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
mainDrawerButton.addEventListener('click', toggleMainDrawer);
}
var headerBackButton = skinHeader.querySelector('.headerBackButton');
const headerBackButton = skinHeader.querySelector('.headerBackButton');
if (headerBackButton) {
headerBackButton.addEventListener('click', onBackClick);
@ -185,20 +199,20 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function onCastButtonClicked() {
var btn = this;
const btn = this;
require(['playerSelectionMenu'], function (playerSelectionMenu) {
import('playerSelectionMenu').then(({default: playerSelectionMenu}) => {
playerSelectionMenu.show(btn);
});
}
function onSyncButtonClicked() {
var btn = this;
const btn = this;
groupSelectionMenu.show(btn);
}
function onSyncPlayEnabled(event, enabled) {
var icon = headerSyncButton.querySelector('span');
const icon = headerSyncButton.querySelector('span');
icon.classList.remove('sync', 'sync_disabled', 'sync_problem');
if (enabled) {
icon.classList.add('sync');
@ -208,7 +222,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function onSyncPlaySyncing(event, is_syncing, syncMethod) {
var icon = headerSyncButton.querySelector('span');
const icon = headerSyncButton.querySelector('span');
icon.classList.remove('sync', 'sync_disabled', 'sync_problem');
if (is_syncing) {
icon.classList.add('sync_problem');
@ -254,7 +268,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function refreshLibraryInfoInDrawer(user, drawer) {
var html = '';
let html = '';
html += '<div style="height:.5em;"></div>';
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder" href="home.html"><span class="material-icons navMenuOptionIcon home"></span><span class="navMenuOptionText">' + globalize.translate('ButtonHome') + '</span></a>';
@ -290,12 +304,12 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
// add buttons to navigation drawer
navDrawerScrollContainer.innerHTML = html;
var btnSettings = navDrawerScrollContainer.querySelector('.btnSettings');
const btnSettings = navDrawerScrollContainer.querySelector('.btnSettings');
if (btnSettings) {
btnSettings.addEventListener('click', onSettingsClick);
}
var btnLogout = navDrawerScrollContainer.querySelector('.btnLogout');
const btnLogout = navDrawerScrollContainer.querySelector('.btnLogout');
if (btnLogout) {
btnLogout.addEventListener('click', onLogoutClick);
}
@ -317,20 +331,20 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateDashboardMenuSelectedItem() {
var links = navDrawerScrollContainer.querySelectorAll('.navMenuOption');
var currentViewId = viewManager.currentView().id;
const links = navDrawerScrollContainer.querySelectorAll('.navMenuOption');
const currentViewId = viewManager.currentView().id;
for (var i = 0, length = links.length; i < length; i++) {
var link = links[i];
var selected = false;
var pageIds = link.getAttribute('data-pageids');
for (let i = 0, length = links.length; i < length; i++) {
let link = links[i];
let selected = false;
let pageIds = link.getAttribute('data-pageids');
if (pageIds) {
pageIds = pageIds.split('|');
selected = pageIds.indexOf(currentViewId) != -1;
}
var pageUrls = link.getAttribute('data-pageurls');
let pageUrls = link.getAttribute('data-pageurls');
if (pageUrls) {
pageUrls = pageUrls.split('|');
@ -339,7 +353,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
if (selected) {
link.classList.add('navMenuOption-selected');
var title = '';
let title = '';
link = link.querySelector('.navMenuOptionText') || link;
title += (link.innerText || link.textContent).trim();
LibraryMenu.setTitle(title);
@ -350,7 +364,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function createToolsMenuList(pluginItems) {
var links = [{
const links = [{
name: globalize.translate('TabServer')
}, {
name: globalize.translate('TabDashboard'),
@ -462,8 +476,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function addPluginPagesToMainMenu(links, pluginItems, section) {
for (var i = 0, length = pluginItems.length; i < length; i++) {
var pluginItem = pluginItems[i];
for (let i = 0, length = pluginItems.length; i < length; i++) {
const pluginItem = pluginItems[i];
if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === section) {
links.push({
@ -483,10 +497,10 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function getToolsLinkHtml(item) {
var menuHtml = '';
var pageIds = item.pageIds ? item.pageIds.join('|') : '';
let menuHtml = '';
let pageIds = item.pageIds ? item.pageIds.join('|') : '';
pageIds = pageIds ? ' data-pageids="' + pageIds + '"' : '';
var pageUrls = item.pageUrls ? item.pageUrls.join('|') : '';
let pageUrls = item.pageUrls ? item.pageUrls.join('|') : '';
pageUrls = pageUrls ? ' data-pageurls="' + pageUrls + '"' : '';
menuHtml += '<a is="emby-linkbutton" class="navMenuOption" href="' + item.href + '"' + pageIds + pageUrls + '>';
@ -502,11 +516,11 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
function getToolsMenuHtml(apiClient) {
return getToolsMenuLinks(apiClient).then(function (items) {
var item;
var menuHtml = '';
let item;
let menuHtml = '';
menuHtml += '<div class="drawerContent">';
for (var i = 0; i < items.length; i++) {
for (let i = 0; i < items.length; i++) {
item = items[i];
if (item.href) {
@ -524,7 +538,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
function createDashboardMenu(apiClient) {
return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) {
var html = '';
let html = '';
html += '<a class="adminDrawerLogo clearLink" is="emby-linkbutton" href="home.html">';
html += '<img src="assets/img/icon-transparent.png" />';
html += '</a>';
@ -535,24 +549,24 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function onSidebarLinkClick() {
var section = this.getElementsByClassName('sectionName')[0];
var text = section ? section.innerHTML : this.innerHTML;
const section = this.getElementsByClassName('sectionName')[0];
const text = section ? section.innerHTML : this.innerHTML;
LibraryMenu.setTitle(text);
}
function getUserViews(apiClient, userId) {
return apiClient.getUserViews({}, userId).then(function (result) {
var items = result.Items;
var list = [];
const items = result.Items;
const list = [];
for (var i = 0, length = items.length; i < length; i++) {
var view = items[i];
for (let i = 0, length = items.length; i < length; i++) {
const view = items[i];
list.push(view);
if (view.CollectionType == 'livetv') {
view.ImageTags = {};
view.icon = 'live_tv';
var guideView = Object.assign({}, view);
const guideView = Object.assign({}, view);
guideView.Name = globalize.translate('ButtonGuide');
guideView.ImageTags = {};
guideView.icon = 'dvr';
@ -566,7 +580,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function showBySelector(selector, show) {
var elem = document.querySelector(selector);
const elem = document.querySelector(selector);
if (elem) {
if (show) {
@ -596,17 +610,17 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
showBySelector('.libraryMenuDownloads', false);
}
var userId = Dashboard.getCurrentUserId();
var apiClient = getCurrentApiClient();
var libraryMenuOptions = document.querySelector('.libraryMenuOptions');
const userId = Dashboard.getCurrentUserId();
const apiClient = getCurrentApiClient();
const libraryMenuOptions = document.querySelector('.libraryMenuOptions');
if (libraryMenuOptions) {
getUserViews(apiClient, userId).then(function (result) {
var items = result;
var html = `<h3 class="sidebarHeader">${globalize.translate('HeaderMedia')}</h3>`;
const items = result;
let html = `<h3 class="sidebarHeader">${globalize.translate('HeaderMedia')}</h3>`;
html += items.map(function (i) {
var icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType);
var itemId = i.Id;
const icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType);
const itemId = i.Id;
return `<a is="emby-linkbutton" data-itemid="${itemId}" class="lnkMediaFolder navMenuOption" href="${getItemHref(i, i.CollectionType)}">
<span class="material-icons navMenuOptionIcon ${icon}"></span>
@ -614,8 +628,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
</a>`;
}).join('');
libraryMenuOptions.innerHTML = html;
var elem = libraryMenuOptions;
var sidebarLinks = elem.querySelectorAll('.navMenuOption');
const elem = libraryMenuOptions;
const sidebarLinks = elem.querySelectorAll('.navMenuOption');
for (const sidebarLink of sidebarLinks) {
sidebarLink.removeEventListener('click', onSidebarLinkClick);
@ -644,9 +658,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateCastIcon() {
var context = document;
var info = playbackManager.getPlayerInfo();
var icon = headerCastButton.querySelector('.material-icons');
const context = document;
const info = playbackManager.getPlayerInfo();
const icon = headerCastButton.querySelector('.material-icons');
icon.classList.remove('cast_connected', 'cast');
@ -662,18 +676,16 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateLibraryNavLinks(page) {
var i;
var length;
var isLiveTvPage = page.classList.contains('liveTvPage');
var isChannelsPage = page.classList.contains('channelsPage');
var isEditorPage = page.classList.contains('metadataEditorPage');
var isMySyncPage = page.classList.contains('mySyncPage');
var id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains('allLibraryPage') ? '' : getTopParentId() || '';
var elems = document.getElementsByClassName('lnkMediaFolder');
const isLiveTvPage = page.classList.contains('liveTvPage');
const isChannelsPage = page.classList.contains('channelsPage');
const isEditorPage = page.classList.contains('metadataEditorPage');
const isMySyncPage = page.classList.contains('mySyncPage');
const id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains('allLibraryPage') ? '' : getTopParentId() || '';
const elems = document.getElementsByClassName('lnkMediaFolder');
for (var i = 0, length = elems.length; i < length; i++) {
var lnkMediaFolder = elems[i];
var itemId = lnkMediaFolder.getAttribute('data-itemid');
for (let i = 0, length = elems.length; i < length; i++) {
const lnkMediaFolder = elems[i];
const itemId = lnkMediaFolder.getAttribute('data-itemid');
if (isChannelsPage && itemId === 'channels') {
lnkMediaFolder.classList.add('navMenuOption-selected');
@ -694,7 +706,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateMenuForPageType(isDashboardPage, isLibraryPage) {
var newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3;
const newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3;
if (currentPageType !== newPageType) {
currentPageType = newPageType;
@ -705,7 +717,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
skinHeader.classList.remove('headroomDisabled');
}
var bodyClassList = document.body.classList;
const bodyClassList = document.body.classList;
if (isLibraryPage) {
bodyClassList.add('libraryDocument');
@ -742,7 +754,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function updateTitle(page) {
var title = page.getAttribute('data-title');
const title = page.getAttribute('data-title');
if (title) {
LibraryMenu.setTitle(title);
@ -766,8 +778,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function initHeadRoom(elem) {
require(['headroom'], function (Headroom) {
var headroom = new Headroom(elem);
import('headroom').then(({default: Headroom}) => {
const headroom = new Headroom(elem);
headroom.init();
});
}
@ -787,7 +799,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
function getNavDrawerOptions() {
var drawerWidth = screen.availWidth - 50;
let drawerWidth = screen.availWidth - 50;
drawerWidth = Math.max(drawerWidth, 240);
drawerWidth = Math.min(drawerWidth, 320);
return {
@ -806,9 +818,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
navDrawerScrollContainer = navDrawerElement.querySelector('.scrollContainer');
navDrawerScrollContainer.addEventListener('click', onMainDrawerClick);
return new Promise(function (resolve, reject) {
require(['navdrawer'], function (navdrawer) {
navdrawer = navdrawer.default || navdrawer;
import('navdrawer').then(({default: navdrawer}) => {
navDrawerInstance = new navdrawer(getNavDrawerOptions());
if (!layoutManager.tv) {
@ -820,31 +830,27 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
});
}
var navDrawerElement;
var navDrawerScrollContainer;
var navDrawerInstance;
var mainDrawerButton;
var headerHomeButton;
var currentDrawerType;
var pageTitleElement;
var headerBackButton;
var headerUserButton;
var currentUser;
var headerCastButton;
var headerSearchButton;
var headerAudioPlayerButton;
var headerSyncButton;
var enableLibraryNavDrawer = layoutManager.desktop;
var enableLibraryNavDrawerHome = !layoutManager.tv;
var skinHeader = document.querySelector('.skinHeader');
var requiresUserRefresh = true;
window.LibraryMenu = {
getTopParentId: getTopParentId,
onHardwareMenuButtonClick: function () {
toggleMainDrawer();
},
setTabs: function (type, selectedIndex, builder) {
require(['mainTabsManager'], function (mainTabsManager) {
let navDrawerElement;
let navDrawerScrollContainer;
let navDrawerInstance;
let mainDrawerButton;
let headerHomeButton;
let currentDrawerType;
let pageTitleElement;
let headerBackButton;
let headerUserButton;
let currentUser;
let headerCastButton;
let headerSearchButton;
let headerAudioPlayerButton;
let headerSyncButton;
const enableLibraryNavDrawer = layoutManager.desktop;
const enableLibraryNavDrawerHome = !layoutManager.tv;
const skinHeader = document.querySelector('.skinHeader');
let requiresUserRefresh = true;
function setTabs (type, selectedIndex, builder) {
import('mainTabsManager').then((mainTabsManager) => {
if (type) {
mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () {
return [];
@ -853,8 +859,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
mainTabsManager.setTabs(null);
}
});
},
setDefaultTitle: function () {
}
function setDefaultTitle () {
if (!pageTitleElement) {
pageTitleElement = document.querySelector('.pageTitle');
}
@ -867,8 +874,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
document.title = 'Jellyfin';
},
setTitle: function (title) {
}
function setTitle (title) {
if (title == null) {
return void LibraryMenu.setDefaultTitle();
}
@ -877,7 +885,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
title = '';
}
var html = title;
const html = title;
if (!pageTitleElement) {
pageTitleElement = document.querySelector('.pageTitle');
@ -891,27 +899,29 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
}
document.title = title || 'Jellyfin';
},
setTransparentMenu: function (transparent) {
}
function setTransparentMenu (transparent) {
if (transparent) {
skinHeader.classList.add('semiTransparent');
} else {
skinHeader.classList.remove('semiTransparent');
}
}
};
var currentPageType;
let currentPageType;
pageClassOn('pagebeforeshow', 'page', function (e) {
if (!this.classList.contains('withTabs')) {
LibraryMenu.setTabs(null);
}
});
pageClassOn('pageshow', 'page', function (e) {
var page = this;
var isDashboardPage = page.classList.contains('type-interior');
var isHomePage = page.classList.contains('homePage');
var isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage');
var apiClient = getCurrentApiClient();
const page = this;
const isDashboardPage = page.classList.contains('type-interior');
const isHomePage = page.classList.contains('homePage');
const isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage');
const apiClient = getCurrentApiClient();
if (isDashboardPage) {
if (mainDrawerButton) {
@ -948,7 +958,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
renderHeader();
events.on(connectionManager, 'localusersignedin', function (e, user) {
var currentApiClient = connectionManager.getApiClient(user.ServerId);
const currentApiClient = connectionManager.getApiClient(user.ServerId);
currentDrawerType = null;
currentUser = {
@ -962,15 +972,32 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
updateUserInHeader(user);
});
});
events.on(connectionManager, 'localusersignedout', function () {
currentUser = {};
updateUserInHeader();
});
events.on(playbackManager, 'playerchange', updateCastIcon);
events.on(syncPlayManager, 'enabled', onSyncPlayEnabled);
events.on(syncPlayManager, 'syncing', onSyncPlaySyncing);
loadNavDrawer();
return LibraryMenu;
});
const LibraryMenu = {
getTopParentId: getTopParentId,
onHardwareMenuButtonClick: function () {
toggleMainDrawer();
},
setTabs: setTabs,
setDefaultTitle: setDefaultTitle,
setTitle: setTitle,
setTransparentMenu: setTransparentMenu
};
window.LibraryMenu = LibraryMenu;
export default LibraryMenu;
/* eslint-enable indent */

View file

@ -1,33 +1,33 @@
define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layoutManager, datetime, cardBuilder, appHost) {
'use strict';
import layoutManager from 'layoutManager';
import datetime from 'datetime';
import cardBuilder from 'cardBuilder';
function enableScrollX() {
function enableScrollX() {
return !layoutManager.desktop;
}
}
function getBackdropShape() {
function getBackdropShape() {
return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
}
}
function getTimersHtml(timers, options) {
function getTimersHtml(timers, options) {
options = options || {};
var i;
var length;
var items = timers.map(function (t) {
const items = timers.map(function (t) {
t.Type = 'Timer';
return t;
});
var groups = [];
var currentGroupName = '';
var currentGroup = [];
for (i = 0, length = items.length; i < length; i++) {
var item = items[i];
var dateText = '';
const groups = [];
let currentGroupName = '';
let currentGroup = [];
for (const item of items) {
let dateText = '';
if (options.indexByDate !== false && item.StartDate) {
try {
var premiereDate = datetime.parseISO8601Date(item.StartDate, true);
const premiereDate = datetime.parseISO8601Date(item.StartDate, true);
dateText = datetime.toLocaleDateString(premiereDate, {
weekday: 'long',
month: 'short',
@ -59,23 +59,18 @@ define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layout
items: currentGroup
});
}
var html = '';
for (i = 0, length = groups.length; i < length; i++) {
var group = groups[i];
let html = '';
for (const group of groups) {
if (group.name) {
html += '<div class="verticalSection">';
html += '<h2 class="sectionTitle sectionTitle-cards padded-left">' + group.name + '</h2>';
}
if (enableScrollX()) {
var scrollXClass = 'scrollX hiddenScrollX';
if (enableScrollX()) {
let scrollXClass = 'scrollX hiddenScrollX';
if (layoutManager.tv) {
scrollXClass += ' smoothScrollX';
}
html += '<div is="emby-itemscontainer" class="itemsContainer ' + scrollXClass + ' padded-left padded-right">';
} else {
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right">';
@ -99,17 +94,16 @@ define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layout
overlayText: false,
showChannelLogo: true
});
html += '</div>';
if (group.name) {
html += '</div>';
}
}
return Promise.resolve(html);
}
}
window.LiveTvHelpers = {
window.LiveTvHelpers = {
getTimersHtml: getTimersHtml
};
});
};

View file

@ -112,54 +112,69 @@ import 'detailtablecss';
});
defineRoute({
path: '/dashboard.html',
alias: '/dashboard.html',
path: '/controllers/dashboard/dashboard.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dashboard'
});
defineRoute({
path: '/dashboardgeneral.html',
alias: '/dashboardgeneral.html',
path: '/controllers/dashboard/general.html',
controller: 'dashboard/general',
autoFocus: false,
roles: 'admin'
});
defineRoute({
path: '/networking.html',
alias: '/networking.html',
path: '/controllers/dashboard/networking.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/networking'
});
defineRoute({
path: '/devices.html',
alias: '/devices.html',
path: '/controllers/dashboard/devices/devices.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/devices/devices'
});
defineRoute({
path: '/device.html',
alias: '/device.html',
path: '/controllers/dashboard/devices/device.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/devices/device'
});
defineRoute({
path: '/dlnaprofile.html',
alias: '/dlnaprofile.html',
path: '/controllers/dashboard/dlna/profile.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dlna/profile'
});
defineRoute({
path: '/dlnaprofiles.html',
alias: '/dlnaprofiles.html',
path: '/controllers/dashboard/dlna/profiles.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dlna/profiles'
});
defineRoute({
alias: '/dlnasettings.html',
path: '/controllers/dashboard/dlna/settings.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dlna/settings'
});
defineRoute({
alias: '/addplugin.html',
path: '/controllers/dashboard/plugins/add/index.html',
@ -169,54 +184,54 @@ import 'detailtablecss';
});
defineRoute({
path: '/library.html',
alias: '/library.html',
path: '/controllers/dashboard/library.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/mediaLibrary'
controller: 'dashboard/library'
});
defineRoute({
path: '/librarydisplay.html',
alias: '/librarydisplay.html',
path: '/controllers/dashboard/librarydisplay.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/librarydisplay'
});
defineRoute({
path: '/dlnasettings.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/dlna/settings'
});
defineRoute({
path: '/edititemmetadata.html',
alias: '/edititemmetadata.html',
path: '/controllers/edititemmetadata.html',
controller: 'edititemmetadata',
autoFocus: false
});
defineRoute({
path: '/encodingsettings.html',
alias: '/encodingsettings.html',
path: '/controllers/dashboard/encodingsettings.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/encodingsettings'
});
defineRoute({
path: '/log.html',
alias: '/log.html',
path: '/controllers/dashboard/logs.html',
roles: 'admin',
controller: 'dashboard/logs'
});
defineRoute({
path: '/metadataimages.html',
alias: '/metadataimages.html',
path: '/controllers/dashboard/metadataimages.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/metadataImages'
});
defineRoute({
path: '/metadatanfo.html',
alias: '/metadatanfo.html',
path: '/controllers/dashboard/metadatanfo.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/metadatanfo'
@ -239,7 +254,8 @@ import 'detailtablecss';
});
defineRoute({
path: '/playbackconfiguration.html',
alias: '/playbackconfiguration.html',
path: '/controllers/dashboard/playback.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/playback'
@ -262,19 +278,22 @@ import 'detailtablecss';
});
defineRoute({
path: '/home.html',
alias: '/home.html',
path: '/controllers/home.html',
autoFocus: false,
controller: 'home',
type: 'home'
});
defineRoute({
path: '/search.html',
alias: '/search.html',
path: '/controllers/search.html',
controller: 'searchpage'
});
defineRoute({
path: '/list.html',
alias: '/list.html',
path: '/controllers/list.html',
autoFocus: false,
controller: 'list'
});
@ -287,46 +306,53 @@ import 'detailtablecss';
});
defineRoute({
path: '/livetv.html',
alias: '/livetv.html',
path: '/controllers/livetv.html',
controller: 'livetv/livetvsuggested',
autoFocus: false
});
defineRoute({
path: '/livetvguideprovider.html',
alias: '/livetvguideprovider.html',
path: '/controllers/livetvguideprovider.html',
autoFocus: false,
roles: 'admin',
controller: 'livetvguideprovider'
});
defineRoute({
path: '/livetvsettings.html',
alias: '/livetvsettings.html',
path: '/controllers/livetvsettings.html',
autoFocus: false,
controller: 'livetvsettings'
});
defineRoute({
path: '/livetvstatus.html',
alias: '/livetvstatus.html',
path: '/controllers/livetvstatus.html',
autoFocus: false,
roles: 'admin',
controller: 'livetvstatus'
});
defineRoute({
path: '/livetvtuner.html',
alias: '/livetvtuner.html',
path: '/controllers/livetvtuner.html',
autoFocus: false,
roles: 'admin',
controller: 'livetvtuner'
});
defineRoute({
path: '/movies.html',
alias: '/movies.html',
path: '/controllers/movies/movies.html',
autoFocus: false,
controller: 'movies/moviesrecommended'
});
defineRoute({
path: '/music.html',
alias: '/music.html',
path: '/controllers/music/music.html',
controller: 'music/musicrecommended',
autoFocus: false
});
@ -340,82 +366,94 @@ import 'detailtablecss';
});
defineRoute({
path: '/scheduledtask.html',
alias: '/scheduledtask.html',
path: '/controllers/dashboard/scheduledtasks/scheduledtask.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/scheduledtasks/scheduledtask'
});
defineRoute({
path: '/scheduledtasks.html',
alias: '/scheduledtasks.html',
path: '/controllers/dashboard/scheduledtasks/scheduledtasks.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/scheduledtasks/scheduledtasks'
});
defineRoute({
path: '/serveractivity.html',
alias: '/serveractivity.html',
path: '/controllers/dashboard/serveractivity.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/serveractivity'
});
defineRoute({
path: '/apikeys.html',
alias: '/apikeys.html',
path: '/controllers/dashboard/apikeys.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/apikeys'
});
defineRoute({
path: '/streamingsettings.html',
alias: '/streamingsettings.html',
path: '/controllers/dashboard/streaming.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/streaming'
});
defineRoute({
path: '/tv.html',
alias: '/tv.html',
path: '/controllers/shows/tvrecommended.html',
autoFocus: false,
controller: 'shows/tvrecommended'
});
defineRoute({
path: '/useredit.html',
alias: '/useredit.html',
path: '/controllers/dashboard/users/useredit.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/users/useredit'
});
defineRoute({
path: '/userlibraryaccess.html',
alias: '/userlibraryaccess.html',
path: '/controllers/dashboard/users/userlibraryaccess.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/users/userlibraryaccess'
});
defineRoute({
path: '/usernew.html',
alias: '/usernew.html',
path: '/controllers/dashboard/users/usernew.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/users/usernew'
});
defineRoute({
path: '/userparentalcontrol.html',
alias: '/userparentalcontrol.html',
path: '/controllers/dashboard/users/userparentalcontrol.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/users/userparentalcontrol'
});
defineRoute({
path: '/userpassword.html',
alias: '/userpassword.html',
path: '/controllers/dashboard/users/userpassword.html',
autoFocus: false,
controller: 'dashboard/users/userpasswordpage'
});
defineRoute({
path: '/userprofiles.html',
alias: '/userprofiles.html',
path: '/controllers/dashboard/users/userprofiles.html',
autoFocus: false,
roles: 'admin',
controller: 'dashboard/users/userprofilespage'
@ -438,10 +476,11 @@ import 'detailtablecss';
});
defineRoute({
path: '/wizardlibrary.html',
alias: '/wizardlibrary.html',
path: '/controllers/wizard/library.html',
autoFocus: false,
anonymous: true,
controller: 'dashboard/mediaLibrary'
controller: 'dashboard/library'
});
defineRoute({

View file

@ -350,6 +350,7 @@ function initClient() {
}
function getLayoutManager(layoutManager, appHost) {
layoutManager = layoutManager.default || layoutManager;
if (appHost.getDefaultLayout) {
layoutManager.defaultLayout = appHost.getDefaultLayout();
}

View file

@ -1542,5 +1542,10 @@
"ViewAlbumArtist": "Zobrazit interpreta alba",
"PreviousTrack": "Předchozí",
"NextTrack": "Další",
"LabelUnstable": "Nestabilní"
"LabelUnstable": "Nestabilní",
"Preview": "Náhled",
"SubtitleVerticalPositionHelp": "Číslo řádku, na kterém se zobrazí text. Kladná čísla znamenají směr shora dolů. Záporná čísla zdola nahoru.",
"LabelSubtitleVerticalPosition": "Svislé umístění:",
"MessageGetInstalledPluginsError": "Při načítání seznamu nainstalovaných zásuvných modulů došlo k chybě.",
"MessagePluginInstallError": "Při instalaci zásuvného modulu došlo k chybě."
}

View file

@ -1542,5 +1542,10 @@
"ViewAlbumArtist": "Zeige Albumkünstler",
"PreviousTrack": "Zum Vorherigen springen",
"NextTrack": "Zum Nächsten springen",
"LabelUnstable": "Instabil"
"LabelUnstable": "Instabil",
"SubtitleVerticalPositionHelp": "Zeilennummer, in der der Text angezeigt wird. Positive Zahlen geben die Zeile von oben an. Negative Zahlen geben die Zeile von unten an.",
"Preview": "Vorschau",
"LabelSubtitleVerticalPosition": "Vertikale Position:",
"MessageGetInstalledPluginsError": "Beim Abrufen der Liste der derzeit installierten Plugins ist ein Fehler aufgetreten.",
"MessagePluginInstallError": "Bei der Installation des Plugins ist ein Fehler aufgetreten."
}

View file

@ -1282,6 +1282,8 @@
"PleaseRestartServerName": "Please restart Jellyfin Server - {0}.",
"PleaseSelectTwoItems": "Please select at least two items.",
"MessagePluginInstalled": "The plugin has been successfully installed. Jellyfin Server will need to be restarted for changes to take effect.",
"MessagePluginInstallError": "An error occured while installing the plugin.",
"MessageGetInstalledPluginsError": "An error occured while getting the list of currently installed plugins.",
"PreferEmbeddedTitlesOverFileNames": "Prefer embedded titles over filenames",
"PreferEmbeddedTitlesOverFileNamesHelp": "This determines the default display title when no internet metadata or local metadata is available.",
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode information from the embedded metadata if available.",

View file

@ -231,7 +231,7 @@
"HeaderAllowMediaDeletionFrom": "Permitir borrar contenido desde",
"HeaderApiKey": "Clave API",
"HeaderApiKeys": "Claves API",
"HeaderApiKeysHelp": "Las aplicaciones externas requieren de una clave API para comunicarse con el servidor Jellyfin. Las claves se facilitan iniciando sesión con una cuenta de Jellyfin, u otorgando manualmente una clave a la aplicación.",
"HeaderApiKeysHelp": "Las aplicaciones externas requieren de una clave API para comunicarse con el servidor. Las claves se facilitan iniciando sesión con una cuenta de usuario en Jellyfin, u otorgando manualmente una clave a la aplicación.",
"HeaderAudioBooks": "Audiolibros",
"HeaderAudioSettings": "Ajustes de audio",
"HeaderBlockItemsWithNoRating": "Bloquear artículos sin valoraciones o si son desconocidas:",
@ -346,7 +346,7 @@
"HeaderPreferredMetadataLanguage": "Idioma preferido para las etiquetas",
"HeaderProfile": "Perfil",
"HeaderProfileInformation": "Información del perfil",
"HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presenta al dispositivo.",
"HeaderProfileServerSettingsHelp": "Estos valores controlan cómo el servidor será presentado a los clientes.",
"HeaderRecentlyPlayed": "Reproducido recientemente",
"HeaderRecordingOptions": "Ajustes de grabación",
"HeaderRecordingPostProcessing": "Grabación post procesamiento",
@ -370,7 +370,7 @@
"HeaderSelectServerCachePath": "Seleccione la ruta para el caché del servidor",
"HeaderSelectServerCachePathHelp": "Navega o introduce la ruta para alojar los archivos caché del servidor. Tienes que tener permisos de escritura en esa carpeta.",
"HeaderSelectTranscodingPath": "Ruta para los archivos temporales de las conversiones",
"HeaderSelectTranscodingPathHelp": "Busca o escribe la ruta que se utilizará para guardar los archivos temporales que se generarán mientras se convierten los archivos. Jellyfin debe tener permisos de escritura en la carpeta.",
"HeaderSelectTranscodingPathHelp": "Busca o escribe la ruta que se utilizará para guardar los archivos que se generarán mientras se convierten los archivos. Jellyfin debe tener permisos de escritura en la carpeta.",
"HeaderSendMessage": "Enviar mensaje",
"HeaderSeries": "Series",
"HeaderSeriesOptions": "Opciones de series",
@ -417,8 +417,8 @@
"HttpsRequiresCert": "Para activar la conexión segura, necesitas un certificado SSL de confianza, como Let's Encrypt. De lo contrario, desactive las conexiones seguras.",
"Identify": "Identificar",
"Images": "Imágenes",
"ImportFavoriteChannelsHelp": "Si está activado, sólo los canales guardados como favoritos en el sintonizador se importarán.",
"ImportMissingEpisodesHelp": "Si está activada, la información sobre los episodios que faltan se importará en su base de datos Jellyfin y se mostrará en temporadas y series. Esto puede causar exploraciones de bibliotecas significativamente más largas.",
"ImportFavoriteChannelsHelp": "Sólo los canales guardados como favoritos en el sintonizador se importarán.",
"ImportMissingEpisodesHelp": "La información sobre los episodios que faltan se importará en su base de datos y se mostrará en temporadas y series. Esto puede causar exploraciones de bibliotecas significativamente más largas.",
"InstallingPackage": "Instalando {0} (versión {1})",
"InstantMix": "Mix instantáneo",
"ItemCount": "Elementos {0}",
@ -449,11 +449,11 @@
"LabelAppName": "Nombre de la aplicación",
"LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr",
"LabelArtists": "Artistas:",
"LabelArtistsHelp": "Separar múltiples artistas usando ;",
"LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.",
"LabelAudioLanguagePreference": "Idioma de audio preferido:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar las etiquetas automáticamente desde Internet:",
"LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:",
"LabelBindToLocalNetworkAddressHelp": "Opcional. Anule la dirección IP local para enlazar el servidor HTTP. Si se deja vacío, el servidor se enlazará a todas las direcciones disponibles. Para cambiar este valor, debe reiniciar el servidor Jellyfin.",
"LabelBindToLocalNetworkAddressHelp": "Anule la dirección IP local para enlazar el servidor HTTP. Si se deja vacío, el servidor se enlazará a todas las direcciones disponibles. Para cambiar este valor, debe reiniciar el servidor Jellyfin.",
"LabelBirthDate": "Fecha de nacimiento:",
"LabelBirthYear": "Año de nacimiento:",
"LabelBlastMessageInterval": "Intervalo para mensajes en vivo (segundos)",
@ -1228,7 +1228,7 @@
"DatePlayed": "Reproducido el",
"Descending": "Descendiente",
"DirectStreamHelp1": "El tipo de archivo (H.264, AC3, etc.) y la resolución son compatibles con el dispositivo, pero no el contenedor (mkv, avi, wmv, etc.). El vídeo será re-empaquetado al vuelo antes de transmitirlo al dispositivo.",
"DirectStreamHelp2": "La transmisión directa del archivo usa muy poco procesamiento sin ninguna pérdida de calidad en el vídeo.",
"DirectStreamHelp2": "La transmisión directa del archivo usa muy poco procesamiento sin mínima pérdida de calidad en el vídeo.",
"Director": "Dirección de",
"Directors": "Directores",
"Display": "Mostrar",
@ -1539,5 +1539,11 @@
"MessageNoRepositories": "Sin repositorios.",
"Writers": "Escritores",
"StopPlayback": "Detener la reproducción",
"ClearQueue": "Borrar la cola"
"ClearQueue": "Borrar la cola",
"LabelSubtitleVerticalPosition": "Posición vertical:",
"PreviousTrack": "Saltar al anterior",
"MessageGetInstalledPluginsError": "Ha ocurrido un error al recuperar la lista de plugins instalados.",
"MessagePluginInstallError": "Ha ocurrido un error al instalar este plugin.",
"NextTrack": "Saltar al siguiente",
"LabelUnstable": "Inestable"
}

View file

@ -181,9 +181,9 @@
"OptionPoster": "Póster",
"OptionPlayed": "Reproducido",
"OptionPlayCount": "Contador de reproducciones",
"OptionPlainVideoItemsHelp": "Si se habilita, todos los videos serán representados en DIDL como «object.item.videoItem» en lugar de un tipo más específico, como «object.item.videoItem.movie».",
"OptionPlainVideoItemsHelp": "Todos los videos serán representados en DIDL como «object.item.videoItem» en vez de un tipo más específico, como «object.item.videoItem.movie».",
"OptionPlainVideoItems": "Mostrar todos los videos como elementos de video simples",
"OptionPlainStorageFoldersHelp": "Si se habilita, todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».",
"OptionPlainStorageFoldersHelp": "Todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».",
"OptionPlainStorageFolders": "Mostrar todas las carpetas como carpetas de almacenamiento simples",
"OptionParentalRating": "Clasificación parental",
"OptionOnInterval": "En un intervalo",
@ -201,7 +201,7 @@
"OptionIsSD": "SD",
"OptionIsHD": "HD",
"OptionImdbRating": "Calificación de IMDb",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes serán honradas pero se ignorará el encabezado de rango de bytes.",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Estas solicitudes serán consideradas pero se ignorará el encabezado de rango de bytes.",
"OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificación de rango de bytes",
"OptionHomeVideos": "Fotos",
"OptionHlsSegmentedSubtitles": "Subtítulos segmentados HLS",
@ -234,7 +234,7 @@
"OptionDownloadPrimaryImage": "Principal",
"OptionDownloadMenuImage": "Menú",
"OptionDownloadLogoImage": "Logo",
"OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes solo son descargadas cuando son solicitadas por una aplicación Jellyfin. Habilita esta opción para descargar todas las imágenes por adelantado, a medida que se agreguen nuevos medios. Esto podría causar escaneos de bibliotecas significativamente más largos.",
"OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes se descargan cuando son solicitadas por un cliente. Habilita esta opción para descargarlas todas por por adelantado a medida que se agreguen nuevos medios. Esto podría causar que los escaneos de bibliotecas sean significativamente más largos.",
"OptionDownloadImagesInAdvance": "Descargar las imágenes con antelación",
"OptionDownloadDiscImage": "Disco",
"OptionDownloadBoxImage": "Caja",
@ -244,7 +244,7 @@
"OptionDisplayFolderViewHelp": "Muestra las carpetas junto con sus otras bibliotecas de medios. Esto puede ser útil si deseas tener una vista simple de carpeta.",
"OptionDisplayFolderView": "Mostrar una vista de carpetas para mostrar las carpetas simples de los medios",
"OptionDislikes": "No me gusta",
"OptionDisableUserHelp": "Si se desactiva, el servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.",
"OptionDisableUserHelp": "El servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.",
"OptionDisableUser": "Desactivar este usuario",
"OptionDescending": "Descendente",
"OptionDatePlayed": "Fecha de reproducción",
@ -263,7 +263,7 @@
"OptionBlockChannelContent": "Contenido de canales de Internet",
"OptionBlockBooks": "Libros",
"OptionBanner": "Banner",
"OptionAutomaticallyGroupSeriesHelp": "Si se habilita, las series que se reparten a través de múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.",
"OptionAutomaticallyGroupSeriesHelp": "Series que estén repartidas en múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.",
"OptionAutomaticallyGroupSeries": "Fusionar automáticamente series esparcidas a través de múltiples carpetas",
"OptionAutomatic": "Automático",
"OptionAuto": "Automático",
@ -276,7 +276,7 @@
"OptionAllowRemoteSharedDevicesHelp": "Los dispositivos DLNA se considerarán compartidos hasta que un usuario comience a controlarlos.",
"OptionAllowRemoteSharedDevices": "Permitir control remoto de dispositivos compartidos",
"OptionAllowRemoteControlOthers": "Permitir control remoto de otros usuarios",
"OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en la reproducción en las aplicaciones Jellyfin debido a los formatos de medios no soportados.",
"OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en reproducción en las aplicaciones debido a formatos de medios no soportados.",
"OptionAllowMediaPlayback": "Permitir reproducción de medios",
"OptionAllowManageLiveTv": "Permitir gestión de grabación de TV en vivo",
"OptionAllowLinkSharingHelp": "Solo son compartidas páginas web que contienen información sobre los medios. Los archivos de medios nunca son compartidos públicamente. Los compartidos tienen un límite de tiempo y expirarán después de {0} días.",
@ -333,7 +333,7 @@
"Mobile": "Móvil",
"MinutesBefore": "minutos antes",
"MinutesAfter": "minutos después",
"MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o realiza actualizaciones masivas usando el administrador de metadatos.",
"MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o haz actualizaciones masivas usando el administrador de metadatos.",
"MetadataManager": "Administrador de metadatos",
"Metadata": "Metadatos",
"MessageSyncPlayErrorMedia": "¡Fallo al activar SyncPlay! Error en el archivo de medios.",
@ -392,7 +392,7 @@
"LatestFromLibrary": "Últimas - {0}",
"Large": "Grande",
"LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para las redes que se considerarán en la red local al aplicar las restricciones de ancho de banda. Si se establecen, todas las demás direcciones IP se considerarán como parte de la red externa y estarán sujetas a las restricciones de ancho de banda externa. Si se deja en blanco, solo se considera a la subred del servidor estar en la red local.",
"LabelffmpegPathHelp": "La ruta hacia el archivo de la aplicación ffmpeg, o la carpeta que contenga ffmpeg.",
"LabelffmpegPathHelp": "La ruta hacia el archivo ejecutable ffmpeg, o la carpeta que contenga ffmpeg.",
"LabelffmpegPath": "Ruta del FFmpeg:",
"LabelZipCode": "Código postal:",
"LabelYoureDone": "¡Has terminado!",
@ -563,7 +563,7 @@
"ReleaseDate": "Fecha de estreno",
"RefreshQueued": "Actualización puesta en la cola.",
"RefreshMetadata": "Actualizar metadatos",
"RefreshDialogHelp": "Los metadatos son actualizados basándose en las configuraciones y servicios de Internet que estén activados en el panel de control de tu servidor Jellyfin.",
"RefreshDialogHelp": "Los metadatos se actualizan según las configuraciones y servicios de internet que se habilitan en el panel de control.",
"Refresh": "Actualizar",
"Recordings": "Grabaciones",
"RecordingScheduled": "Grabación programada.",
@ -664,7 +664,7 @@
"LabelServerName": "Nombre del servidor:",
"LabelServerHostHelp": "192.168.1.100:8096 o https://miservidor.com",
"LabelServerHost": "Servidor:",
"LabelSeriesRecordingPath": "Ruta para las grabaciones de series (opcional):",
"LabelSeriesRecordingPath": "Ruta para las grabaciones de series:",
"LabelSerialNumber": "Número de serie",
"LabelSendNotificationToUsers": "Enviar la notificación a:",
"LabelSelectVersionToInstall": "Seleccionar versión a instalar:",
@ -676,7 +676,7 @@
"LabelScheduledTaskLastRan": "Última ejecución {0}, tomando {1}.",
"LabelSaveLocalMetadataHelp": "Guardar ilustraciones en las carpetas de los medios los colocará en un lugar donde se pueden editar fácilmente.",
"LabelSaveLocalMetadata": "Guardar las ilustraciones en las carpetas de los medios",
"LabelRuntimeMinutes": "Duración (minutos):",
"LabelRuntimeMinutes": "Duración:",
"LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes a través de HTTP a HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.",
"LabelRequireHttps": "Requerir HTTPS",
"LabelRemoteClientBitrateLimitHelp": "Un límite opcional de velocidad de bits por transmisión para todos los dispositivos fuera de la red. Esto es útil para evitar que los dispositivos soliciten una tasa de bits más alta de la que puede manejar tu conexión a Internet. Esto puede provocar un aumento de la carga de la CPU en el servidor para transcodificar los videos sobre la marcha a una velocidad de bits inferior.",
@ -728,7 +728,7 @@
"LabelOriginalTitle": "Título original:",
"LabelOriginalAspectRatio": "Relación de aspecto original:",
"LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente. Por ejemplo, {0} o {1}.",
"LabelOptionalNetworkPath": "(Opcional) Carpeta de red compartida:",
"LabelOptionalNetworkPath": "Carpeta de red compartida:",
"LabelNumberOfGuideDaysHelp": "Descargar más días de datos de programación permite programar con mayor anticipación y ver más listados, pero tomará más tiempo en descargar. Auto hará la selección basada en el número de canales.",
"LabelNumberOfGuideDays": "Número de días de datos de la programación a descargar:",
"LabelNumber": "Número:",
@ -741,9 +741,9 @@
"LabelStable": "Estable",
"LabelChromecastVersion": "Versión de Chromecast",
"LabelName": "Nombre:",
"LabelMusicStreamingTranscodingBitrateHelp": "Especifica la velocidad de bits máxima al transmitir música.",
"LabelMusicStreamingTranscodingBitrateHelp": "Especifica la máxima velocidad de bits al transmitir música.",
"LabelMusicStreamingTranscodingBitrate": "Velocidad de bits de transcodificación de música:",
"LabelMovieRecordingPath": "Ruta para las grabaciones de películas (opcional):",
"LabelMovieRecordingPath": "Ruta para las grabaciones de películas:",
"LabelMoviePrefixHelp": "Si un prefijo es aplicado al título de las películas, introdúcelo aquí para que el servidor pueda manejarlo correctamente.",
"LabelMoviePrefix": "Prefijo de la película:",
"LabelMovieCategories": "Categorías de películas:",
@ -941,11 +941,11 @@
"LabelBurnSubtitles": "Quemar subtítulos:",
"LabelBlockContentWithTags": "Bloquear elementos con las etiquetas:",
"LabelBlastMessageIntervalHelp": "Determina la duración en segundos del intervalo entre mensajes de vida.",
"LabelBlastMessageInterval": "Intervalo de mensajes de vida (segundos)",
"LabelBlastMessageInterval": "Intervalo de mensajes de vida",
"LabelBitrate": "Velocidad de bits:",
"LabelBirthYear": "Año de nacimiento:",
"LabelBirthDate": "Fecha de nacimiento:",
"LabelBindToLocalNetworkAddressHelp": "Opcional. Sobrescribe la dirección IP local a la que se vincula el servidor http. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Cambiar este valor requiere reiniciar el servidor Jellyfin.",
"LabelBindToLocalNetworkAddressHelp": "Sobrescribe la dirección IP local del servidor HTTP. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Para cambiar este valor se necesita reiniciar el servidor.",
"LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar automáticamente los metadatos desde Internet:",
"LabelAuthProvider": "Proveedor de autenticación:",
@ -956,7 +956,7 @@
"LabelAudioBitrate": "Velocidad de bits de audio:",
"LabelAudioBitDepth": "Profundidad de bits de audio:",
"LabelAudio": "Audio",
"LabelArtistsHelp": "Separar múltiples empleando ;",
"LabelArtistsHelp": "Separar múltiples artistas por punto y coma.",
"LabelArtists": "Artistas:",
"LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr",
"LabelAppName": "Nombre de la aplicación",
@ -987,8 +987,8 @@
"ItemCount": "{0} elementos",
"InstantMix": "Mix instantáneo",
"InstallingPackage": "Instalando {0} (versión {1})",
"ImportMissingEpisodesHelp": "Si se habilita, la información sobre los episodios faltantes se importará a la base de datos de Jellyfin y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.",
"ImportFavoriteChannelsHelp": "Si se habilita, solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.",
"ImportMissingEpisodesHelp": "La información sobre los episodios faltantes se importará a la base de datos y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.",
"ImportFavoriteChannelsHelp": "Solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.",
"Images": "Imágenes",
"Identify": "Identificar",
"HttpsRequiresCert": "Para habilitar las conexiones seguras, necesitarás proporcionar un certificado SSL de confianza, como el de Let's Encrypt. Por favor, proporciona un certificado o desactiva las conexiones seguras.",
@ -1041,7 +1041,7 @@
"HeaderSelectServerCachePath": "Seleccionar ruta para la caché del servidor",
"HeaderSelectServer": "Seleccionar servidor",
"HeaderSelectPath": "Seleccionar ruta",
"HeaderSelectMetadataPathHelp": "Explora o introduce la ruta donde deseas almacenar los metadatos. Se debe tener permisos de escritura en dicha carpeta.",
"HeaderSelectMetadataPathHelp": "Explora o escribe la ruta donde deseas guardar los metadatos. Se tienen que tener permisos de escritura en esa carpeta.",
"HeaderSelectMetadataPath": "Selecciona la ruta para los metadatos",
"HeaderSelectCertificatePath": "Selecciona la ruta del certificado",
"HeaderSecondsValue": "{0} segundos",
@ -1060,7 +1060,7 @@
"HeaderRecordingPostProcessing": "Post procesado de las grabaciones",
"HeaderRecordingOptions": "Opciones de grabación",
"HeaderRecentlyPlayed": "Reproducido recientemente",
"HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presentará al dispositivo.",
"HeaderProfileServerSettingsHelp": "Estos valores controlan cómo el servidor se presentará a los clientes.",
"HeaderProfileInformation": "Información del perfil",
"HeaderProfile": "Perfil",
"HeaderPreferredMetadataLanguage": "Idioma preferido para los metadatos",
@ -1108,7 +1108,7 @@
"HeaderLatestMovies": "Últimas películas",
"HeaderLatestMedia": "Últimos medios",
"HeaderLatestEpisodes": "Últimos episodios",
"HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edite una biblioteca en la configuración de bibliotecas de Jellyfin y ubica la sección grabadores de metadatos.",
"HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edita una biblioteca y ubica la sección de grabadores de metadatos.",
"HeaderKeepSeries": "Conservar serie",
"HeaderKeepRecording": "Conservar grabación",
"HeaderItems": "Elementos",
@ -1226,7 +1226,7 @@
"HeaderAppearsOn": "Aparece en",
"HeaderApp": "Aplicación",
"ApiKeysCaption": "Lista de claves API actualmente habilitadas",
"HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor Jellyfin. Las claves se emiten al iniciar sesión con una cuenta Jellyfin, o al otorgar manualmente una clave a la aplicación.",
"HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor. Las claves se emiten al iniciar sesión con una cuenta de usuario, o al otorgar manualmente una clave a la aplicación.",
"HeaderApiKeys": "Claves API",
"HeaderApiKey": "Clave API",
"HeaderAllowMediaDeletionFrom": "Permitir eliminación de medios de",
@ -1372,7 +1372,7 @@
"HeaderSeriesOptions": "Opciones de serie",
"HeaderSeries": "Series",
"HeaderSendMessage": "Enviar mensaje",
"HeaderSelectTranscodingPathHelp": "Explora o introduce la ruta a utilizar para los archivos temporales de transcodificación. Se debe tener permisos de escritura en dicha carpeta.",
"HeaderSelectTranscodingPathHelp": "Explora o escribe la ruta para los archivos de transcodificación. Se tienen que tener permisos de escritura en esa carpeta.",
"HeaderSelectTranscodingPath": "Selecciona la ruta para los archivos temporales de transcodificación",
"ConfirmDeleteItems": "Eliminar estos elementos los eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?",
"ConfirmDeleteItem": "Eliminar este elemento lo eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?",
@ -1485,7 +1485,7 @@
"Backdrops": "Imágenes de fondo",
"Backdrop": "Imagen de fondo",
"Auto": "Auto",
"AuthProviderHelp": "Selecciona un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.",
"AuthProviderHelp": "Selecciona el proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.",
"Audio": "Audio",
"AttributeNew": "Nuevo",
"AspectRatio": "Relación de aspecto",
@ -1539,5 +1539,13 @@
"ButtonCast": "Emitir",
"Writers": "Escritores",
"ViewAlbumArtist": "Ver Álbum de Artista",
"TabRepositories": "Repositorios"
"TabRepositories": "Repositorios",
"NextTrack": "Saltar al siguiente",
"LabelUnstable": "Inestable",
"Preview": "Vista previa",
"SubtitleVerticalPositionHelp": "Número de línea donde aparece el texto. Números positivos representan de arriba hacia abajo. Números negativos representan de abajo hacia arriba.",
"LabelSubtitleVerticalPosition": "Posición Vertical:",
"PreviousTrack": "Saltar al anterior",
"MessageGetInstalledPluginsError": "Ocurrió un error buscando la lista de plugins instalados.",
"MessagePluginInstallError": "Ocurrió un error instalando el plugin."
}

View file

@ -1542,5 +1542,10 @@
"ViewAlbumArtist": "Voir l'album de l'artiste",
"PreviousTrack": "Revenir au précédent",
"NextTrack": "Passer au prochain",
"LabelUnstable": "Instable"
"LabelUnstable": "Instable",
"Preview": "Aperçu",
"SubtitleVerticalPositionHelp": "Numéro de ligne où le texte apparaît. Un nombre positif compte les lignes de haut en bas. Un nombre négatif, de bas en haut.",
"LabelSubtitleVerticalPosition": "Position verticale :",
"MessageGetInstalledPluginsError": "Une erreur est survenue lors de la récupération de la liste des extensions installées.",
"MessagePluginInstallError": "Une erreur est survenue durant l'installation de l'extension."
}

View file

@ -160,8 +160,8 @@
"DetectingDevices": "Apparaten detecteren",
"DeviceAccessHelp": "Dit geldt alleen voor apparaten die uniek geïdentificeerd kunnen worden en voorkomen niet toegang via een webbrowser. Filteren van apparaat toegang voor gebruikers voorkomt dat zij nieuwe apparaten gebruiken totdat deze hier zijn goedgekeurd.",
"DirectPlaying": "Direct afspelen",
"DirectStreamHelp1": "De resolutie en codec (bijv. H.264, AC3, etc.) wordt ondersteund door het apparaat, maar het medium is in een niet-ondersteunde bestandscontainer (bijv. mkv, avi, wmv). De video zal tijdens het afspelen opnieuw verpakt worden naar een andere bestandscontainer.",
"DirectStreamHelp2": "Direct streamen van een bestand gebruikt weinig processor kracht zonder verlies van beeldkwaliteit.",
"DirectStreamHelp1": "De resolutie en codec (H.264, AC3, etc.) wordt ondersteund door het apparaat, maar het medium is in een niet-ondersteunde bestandscontainer (mkv, avi, wmv, etc.). De video zal tijdens het afspelen opnieuw verpakt worden naar een andere bestandscontainer.",
"DirectStreamHelp2": "Direct streamen van een bestand gebruikt weinig processorkracht zonder verlies van beeldkwaliteit.",
"DirectStreaming": "Direct streamen",
"Director": "Regiseur",
"Directors": "Regisseurs",
@ -267,7 +267,7 @@
"HeaderAllowMediaDeletionFrom": "Wissen van media toestaan van",
"HeaderApiKey": "API Sleutel",
"HeaderApiKeys": "API Sleutels",
"HeaderApiKeysHelp": "Externe applicaties zijn verplicht om een API sleutel te hebben om te communiceren met Jellyfin Server. Sleutels worden uitgegeven door in te loggen met een Jellyfin account, of door het handmatig verlenen van een sleutel voor de toepassing.",
"HeaderApiKeysHelp": "Externe applicaties zijn verplicht om een API sleutel te hebben om te communiceren met de server. Sleutels kunnen verkregen worden door in te loggen met een Jellyfin account, of door er een handmatig te verlenen.",
"HeaderApp": "Applicatie",
"HeaderAppearsOn": "Verschijnt op",
"HeaderAudioBooks": "Luisterboeken",
@ -378,7 +378,7 @@
"HeaderPreferredMetadataLanguage": "Gewenste metadata taal",
"HeaderProfile": "Profiel",
"HeaderProfileInformation": "Profiel Informatie",
"HeaderProfileServerSettingsHelp": "Deze waarden bepalen hoe Jellyfin Server zich zal presenteren aan het apparaat.",
"HeaderProfileServerSettingsHelp": "Deze waarden bepalen hoe de server zich zal presenteren aan het apparaat.",
"HeaderRecentlyPlayed": "Recent afgespeeld",
"HeaderRecordingOptions": "Opname instellingen",
"HeaderRecordingPostProcessing": "Opname nabewerking",
@ -396,13 +396,13 @@
"HeaderSecondsValue": "{0} Seconden",
"HeaderSelectCertificatePath": "Selecteer Certificaat Pad",
"HeaderSelectMetadataPath": "Selecteer Metadata Pad",
"HeaderSelectMetadataPathHelp": "Blader of voer het pad in dat u wilt gebruiken om metadata in op te slaan. De map moet beschrijfbaar zijn.",
"HeaderSelectMetadataPathHelp": "Blader of voer het pad in dat u wilt gebruiken om metadata in op te slaan. De map moet schrijfbaar zijn.",
"HeaderSelectPath": "Selecteer Pad",
"HeaderSelectServer": "Selecteer server",
"HeaderSelectServerCachePath": "Selecteer Server Cache Pad",
"HeaderSelectServerCachePathHelp": "Bladeren of voer het pad in om te gebruiken voor server cache-bestanden. De map moet beschrijfbaar zijn.",
"HeaderSelectTranscodingPath": "Selecteer Tijdelijke Transcodeer Pad",
"HeaderSelectTranscodingPathHelp": "Bladeren of voer het pad in om te gebruiken voor het transcoderen van tijdelijke bestanden. De map moet beschrijfbaar zijn.",
"HeaderSelectTranscodingPathHelp": "Blader of voer het pad in om te gebruiken voor het transcoderen van tijdelijke bestanden. De map moet schrijfbaar zijn.",
"HeaderSendMessage": "Stuur bericht",
"HeaderSeries": "Series",
"HeaderSeriesOptions": "Series Opties",
@ -1255,7 +1255,7 @@
"HeaderGenres": "Genres",
"HeaderHttpHeaders": "HTTP Headers",
"HeaderStatus": "Status",
"AuthProviderHelp": "Selecteer een Authenticatie Provider om het wachtwoord van deze gebruiker te verifiëren.",
"AuthProviderHelp": "Selecteer een authenticatie provider om het wachtwoord van deze gebruiker te verifiëren.",
"HeaderFavoriteMovies": "Favoriete Films",
"HeaderFavoriteShows": "Favoriete shows",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",

View file

@ -61,9 +61,9 @@
"HeaderTaskTriggers": "Declanșatori Sarcini",
"HeaderUsers": "Utilizatori",
"Help": "Ajutor",
"ImportMissingEpisodesHelp": "Dacă este activată, informația despre episoadele lipsă va fi importată in baza de date Jellyfin și va fi afișată în cadrul serialelor. Aceasta poate cauza un timp semnificativ mai îndelungat la scanarea bibliotecilor.",
"ImportMissingEpisodesHelp": "Informația despre episoadele lipsă va fi importată în baza de date și va fi afișată în cadrul serialelor. Aceasta poate cauza un timp semnificativ mai îndelungat la scanarea bibliotecilor.",
"LabelArtists": "Artisti:",
"LabelArtistsHelp": "Separare multiplă utilizând ;",
"LabelArtistsHelp": "Separară înșiruirea artiștilor utilizând ;",
"LabelAudioLanguagePreference": "Preferințe de limbă pentru audio:",
"LabelCachePath": "Cale pentru depozit:",
"LabelCachePathHelp": "Specificați o locație specială pentru fișierele de tip depozit, precum imagini etc. Lasați gol pentru a folosi setarea implicită.",
@ -82,7 +82,7 @@
"LabelMetadataPath": "Cale pentru metadata:",
"LabelMetadataPathHelp": "Specificați o locație specială pentru a descărca postere și metadata.",
"LabelMinBackdropDownloadWidth": "Lățimea maximă pentru fundalurile descărcate:",
"LabelMovieRecordingPath": "Calea pentru înregistrări filme (opțional):",
"LabelMovieRecordingPath": "Calea pentru înregistrări filme:",
"LabelName": "Nume:",
"LabelNewPassword": "Parola nouă:",
"LabelNewPasswordConfirm": "Confirmă parola nouă:",
@ -96,7 +96,7 @@
"LabelSaveLocalMetadata": "Salvează posterele si metadata în dosarele ce conțin fișierele media",
"LabelSaveLocalMetadataHelp": "Salvând posterele și metadata direct in dosarele media, acestea vor fi mai accesibile pentru a fi modificate.",
"LabelSelectUsers": "Selectare utilizatori:",
"LabelSeriesRecordingPath": "Calea pentru înregistrări seriale (opțional):",
"LabelSeriesRecordingPath": "Calea pentru înregistrări de seriale:",
"LabelStopWhenPossible": "Oprește când este posibil:",
"LabelTimeLimitHours": "Limită de timp(ore):",
"LabelTranscodingTempPathHelp": "Specificați o cale specială pentru fișierele transcodate trimise clienților. Lasați gol pentru a folosi pe cea implicită în directorul de lucru al serverului.",
@ -133,7 +133,7 @@
"OptionDatePlayed": "Dată Rulare",
"OptionDescending": "Descrescător",
"OptionDisableUser": "Dezactivați acest utilizator",
"OptionDisableUserHelp": "Dacă este dezactivat, serverul nu va permite nicio conexiune de la acest utilizator. Conexiunile existente vor fi terminate brusc.",
"OptionDisableUserHelp": "Serverul nu va permite nici o conexiune de la acest utilizator. Conexiunile existente vor fi terminate brusc.",
"OptionDislikes": "Dislike-uri",
"OptionDownloadArtImage": "Fundal",
"OptionDownloadBackImage": "Înapoi",
@ -419,7 +419,7 @@
"HeaderExternalIds": "ID-uri Externe:",
"HeaderFavoriteBooks": "Cărți Favorite",
"HeaderBranding": "Marca",
"HeaderApiKeysHelp": "Aplicațiile externe trebuie să aibă o cheie API pentru a comunica cu Jellyfin Server. Cheile sunt emise prin conectarea cu un cont Jellyfin sau prin acordarea manuală a unei chei aplicației.",
"HeaderApiKeysHelp": "Aplicațiile externe trebuie să aibă o cheie API pentru a comunica cu serverul. Cheile sunt emise prin conectarea cu un cont de utilizator sau prin acordarea manuală a unei chei aplicației.",
"Sync": "Sincronizare",
"ErrorAddingXmlTvFile": "A apărut o eroare la accesarea fișierului XMLTV. Vă rugăm să vă asigurați că fișierul există și încercați din nou.",
"HeaderApiKey": "Cheie API",
@ -459,7 +459,7 @@
"HeaderMyMediaSmall": "Fișierele mele Media ( micșorat )",
"HeaderNewApiKey": "Nouă cheie API",
"HeaderNewDevices": "Dispozitive noi",
"HeaderKodiMetadataHelp": "Pentru a activa sau dezactiva metadatele NFO, editați o bibliotecă, în configurarea bibliotecii Jellyfin, și localizați secțiunea de salvare a metadatelor.",
"HeaderKodiMetadataHelp": "Pentru a activa sau dezactiva metadatele NFO, editați o bibliotecă, și localizați secțiunea de salvare a metadatelor.",
"HeaderNextVideoPlayingInValue": "Următorul video se redă în {0}",
"HeaderOnNow": "Pornit Acum",
"HeaderOtherItems": "Alte Elemente",
@ -475,7 +475,7 @@
"HeaderPlaybackError": "Eroare la redare",
"HeaderPluginInstallation": "Instalare Plugin",
"HeaderProfileInformation": "Informații Profil",
"HeaderProfileServerSettingsHelp": "Aceste valori controlează modul în care Jellyfin Server va fi reprezentat in dispozitiv.",
"HeaderProfileServerSettingsHelp": "Aceste valori controlează modul în care serverul va fi reprezentat in dispozitivele clientilor.",
"HeaderRecordingOptions": "Opțiuni Înregistrare",
"HeaderRecordingPostProcessing": "Post procesarea înregistrării",
"HeaderRemoveMediaFolder": "Eliminați Dosarul Media",
@ -614,7 +614,7 @@
"HeaderSelectServer": "Selectați Serverul",
"HeaderSelectServerCachePath": "Selectați ruta pentru Server Cache",
"HeaderSelectTranscodingPath": "Selectați ruta temporară pentru transcodare",
"HeaderSelectTranscodingPathHelp": "Căutați sau introduceți ruta dosarului de utilizat pentru transcodarea fișierelor temporare. Dosarul trebuie permisiuni de scriere.",
"HeaderSelectTranscodingPathHelp": "Căutați sau introduceți ruta dosarului de utilizat pentru transcodarea fișierelor. Dosarul trebuie permisiuni de scriere.",
"HeaderSendMessage": "Trimite Mesaj",
"HeaderSeriesOptions": "Opțiuni Seriale",
"HeaderSeriesStatus": "Starea Serialelor",
@ -675,7 +675,7 @@
"LabelSeasonNumber": "Numărul sezonului:",
"LabelScreensaver": "Protector de ecran:",
"LabelScheduledTaskLastRan": "Ultima redare{0}, cu durata {1}.",
"LabelRuntimeMinutes": "Timp de redare (minute):",
"LabelRuntimeMinutes": "Timp de redare:",
"LabelRemoteClientBitrateLimitHelp": "O limită de biți per-stream opțională pentru toate dispozitivele din rețea. Acest lucru este util pentru a împiedica dispozitivele să solicite un bitrate mai mare decât poate gestiona conexiunea dvs. de internet. Acest lucru poate duce la creșterea încărcării procesorului pe serverul dvs. pentru a transcoda videoclipurile din zbor la un bitrate mai mic.",
"LabelRemoteClientBitrateLimit": "Limită de biți pentru streaming pe Internet (Mbps):",
"LabelReleaseDate": "Data lansării:",
@ -720,7 +720,7 @@
"LabelOriginalTitle": "Titlu original:",
"LabelOriginalAspectRatio": "Raport aspect original:",
"LabelOptionalNetworkPathHelp": "Dacă acest folder este partajat în rețeaua dvs., furnizarea căii de partajare a rețelei poate permite aplicațiilor Jellyfin de pe alte dispozitive să acceseze fișiere media direct.",
"LabelOptionalNetworkPath": "(Optional) Dosar partajat în rețea:",
"LabelOptionalNetworkPath": "Dosar partajat în rețea:",
"LabelNumber": "Număr:",
"LabelNotificationEnabled": "Activează această notificare",
"LabelNewsCategories": "Categoriile știrilor:",
@ -741,7 +741,7 @@
"LabelMinResumeDurationHelp": "Cea mai scurtă lungime video în secunde, care va salva locația de redare și vă va permite să reluați.",
"LabelMinResumeDuration": "Durata minimă a reluării:",
"LabelMethod": "Metoda:",
"LabelMetadataSaversHelp": "Alegeți formatele de fișiere pentru a vă salva metadatele.",
"LabelMetadataSaversHelp": "Alegeți formatele de fișiere pentru salvarea metadatele.",
"LabelMetadataSavers": "Salvări de metadate:",
"LabelMetadataReadersHelp": "Clasificați sursele preferate de metadate locale în ordinea priorității. Primul fișier găsit va fi citit.",
"LabelMetadataReaders": "Cititori de metadate:",
@ -761,7 +761,7 @@
"LabelLoginDisclaimerHelp": "Un mesaj care va fi afișat în partea de jos a paginii de conectare.",
"LabelLoginDisclaimer": "Act de renunțare la autentificare:",
"LabelLockItemToPreventChanges": "Blocați acest element pentru a preveni modificările viitoare",
"LabelLocalHttpServerPortNumberHelp": "Portul TCP pe care serverul HTTP Jellyfin ar trebui să îl utilizeze.",
"LabelLocalHttpServerPortNumberHelp": "Portul TCP pentru serverul HTTP.",
"LabelLocalHttpServerPortNumber": "Portul local HTTP:",
"LabelLineup": "Echipa:",
"LabelLanNetworks": "Rețele LAN:",
@ -788,7 +788,7 @@
"LabelIconMaxWidth": "Lățimea maximă a pictogramei:",
"LabelIconMaxHeightHelp": "Rezoluția maximă a pictogramelor expuse via upnp:icon.",
"LabelIconMaxHeight": "Înălțimea maximă a pictogramei:",
"LabelHttpsPortHelp": "Portul TCP pe care serverul HTTPS Jellyfin ar trebui sa îl utilizeze.",
"LabelHttpsPortHelp": "Portul TCP pentru serverul HTTPS.",
"LabelHttpsPort": "Portul local HTTPS:",
"LabelHomeScreenSectionValue": "Secțiunea ecranului de pornire {0}:",
"LabelHomeNetworkQuality": "Calitatea pe rețeaua de domiciliu:",
@ -816,7 +816,7 @@
"LabelEndDate": "Data de încheiere:",
"LabelEnableSingleImageInDidlLimitHelp": "Unele dispozitive nu vor reda corect dacă mai multe imagini sunt încorporate în Didl.",
"LabelEnableSingleImageInDidlLimit": "Limitați la o singură imagine încorporată",
"LabelEnableRealtimeMonitorHelp": "Modificările la fișiere vor fi procesate imediat, pe sistemele de fișiere acceptate.",
"LabelEnableRealtimeMonitorHelp": "Modificările la fișiere vor fi procesate imediat pe sistemele de fișiere acceptate.",
"LabelEnableRealtimeMonitor": "Activați monitorizarea în timp real",
"LabelEnableHardwareDecodingFor": "Activați decodarea hardware pentru:",
"LabelEnableDlnaServerHelp": "Permite dispozitivelor UPnP din rețeaua dvs. să răsfoiască și să redea conținut.",
@ -826,7 +826,7 @@
"LabelEnableDlnaDebugLoggingHelp": "Creați fișiere de jurnal mari și trebuie utilizate numai în funcție de necesități pentru rezolvarea problemelor.",
"LabelEnableDlnaDebugLogging": "Activați jurnalul de depanare DLNA",
"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determină durata în secunde între căutările SSDP efectuate de Jellyfin.",
"LabelEnableDlnaClientDiscoveryInterval": "Interval de descoperire a clientului (secunde)",
"LabelEnableDlnaClientDiscoveryInterval": "Interval de descoperire a clientului",
"LabelEnableBlastAliveMessagesHelp": "Activați acest lucru dacă serverul nu este detectat în mod fiabil de alte dispozitive UPnP din rețeaua dvs.",
"LabelEnableBlastAliveMessages": "Trimitere mesaje de disponibilitate",
"LabelEnableAutomaticPortMapHelp": "Încercați să mapați automat portul public către portul local prin UPnP. Este posibil să nu funcționeze cu unele modele de router. Modificările nu se vor aplica decât după repornirea serverului.",
@ -874,11 +874,11 @@
"LabelBurnSubtitles": "Imprimă subtitrările:",
"LabelBlockContentWithTags": "Blochează articolele cu etichetele:",
"LabelBlastMessageIntervalHelp": "Determină durata în secunde între transmiterea mesajele de viață.",
"LabelBlastMessageInterval": "Interval transmitere mesaj viu (secunde)",
"LabelBlastMessageInterval": "Interval transmitere mesaj viu",
"LabelBitrate": "Rată de biți:",
"LabelBirthYear": "Anul nașterii:",
"LabelBirthDate": "Data nașterii:",
"LabelBindToLocalNetworkAddressHelp": "Opțional. Rescrie adresa IP locală pentru a o utiliza serverul http. Dacă este lăsat gol, serverul se va lega la toate adresele disponibile. Modificarea acestei valori necesită repornirea Jellyfin Server.",
"LabelBindToLocalNetworkAddressHelp": "Rescrie adresa IP locală a serverului http. Dacă este lăsat gol, serverul se va lega la toate adresele disponibile. Modificarea acestei valori necesită repornirea Jellyfin Server.",
"LabelBindToLocalNetworkAddress": "Utilizează adresa de rețea locală:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizați automat metadatele de pe internet:",
"LabelAuthProvider": "Furnizor de autentificare:",
@ -917,7 +917,7 @@
"ItemCount": "{0} articole",
"InstantMix": "Mix instant",
"InstallingPackage": "Instalare {0} (versiune {1})",
"ImportFavoriteChannelsHelp": "Dacă este activat, vor fi importate numai canalele marcate ca preferate pe dispozitivul tuner.",
"ImportFavoriteChannelsHelp": "Vor fi importate numai canalele marcate ca preferate pe dispozitivul tuner.",
"Images": "Imagini",
"Identify": "Identifică",
"HttpsRequiresCert": "Pentru a activa conexiunile securizate, va trebui să furnizați un certificat SSL de încredere, cum ar fi Let's Encrypt. Vă rugăm să furnizați un certificat sau să dezactivați conexiunile securizate.",
@ -969,14 +969,14 @@
"OptionBlockChannelContent": "Conținut canal Internet",
"OptionBlockBooks": "Cărți",
"OptionBanner": "Steag",
"OptionAutomaticallyGroupSeriesHelp": "Dacă este activat, seriile distribuite pe mai multe foldere din această bibliotecă vor fi comasate automat într-o singură serie.",
"OptionAutomaticallyGroupSeriesHelp": "Seriile distribuite pe mai multe foldere din această bibliotecă vor fi comasate automat într-o singură serie.",
"OptionAutomaticallyGroupSeries": "Fuzionează automat seriile care sunt răspândite pe mai multe foldere",
"OptionAuto": "Auto",
"OptionArtist": "Artist",
"OptionAllowVideoPlaybackTranscoding": "Permiteți redarea video care necesită transcodare",
"OptionAllowVideoPlaybackRemuxing": "Permiteți redarea video care necesită conversie fără re-codificare",
"OptionAllowSyncTranscoding": "Permiteți descărcarea și sincronizarea media care necesită transcodare",
"OptionAllowMediaPlaybackTranscodingHelp": "Restrângerea accesului la transcodare poate provoca defecțiuni de redare în aplicațiile Jellyfin din cauza formatelor media neacceptate.",
"OptionAllowMediaPlaybackTranscodingHelp": "Restrângerea accesului la transcodare poate provoca defecțiuni de redare în aplicațiile client din cauza formatelor media neacceptate.",
"OptionAllowContentDownloading": "Permiteți descărcarea și sincronizarea media",
"OptionAllowAudioPlaybackTranscoding": "Permiteți redarea audio care necesită transcodare",
"OptionAllUsers": "Toți utilizatorii",
@ -1113,7 +1113,7 @@
"LatestFromLibrary": "Ultimele {0}",
"Large": "Mare",
"LanNetworksHelp": "Lista separată de virgule a adreselor IP sau a intrărilor de tip IP/mască de rețea pentru rețelele care vor fi luate în considerare în rețeaua locală atunci când se aplică restricțiile de lățime de bandă. Dacă este setat, toate celelalte adrese IP vor fi considerate a fi în rețeaua externă și vor fi supuse restricțiilor de lățime de bandă externe. Dacă este lăsat necompletat, numai subnetul serverului este considerat a fi în rețeaua locală.",
"LabelffmpegPathHelp": "Calea către executabilul ffmpeg, sau dosarul care conține ffmpeg.",
"LabelffmpegPathHelp": "Calea către executabilul ffmpeg sau dosarul care conține ffmpeg.",
"LabelffmpegPath": "Calea către FFmpeg:",
"LabelZipCode": "Cod poștal:",
"LabelYear": "Anul:",
@ -1217,7 +1217,7 @@
"ReleaseDate": "Data lansării",
"RefreshQueued": "Actualizare adăugată în coadă.",
"RefreshMetadata": "Actualizați metadatele",
"RefreshDialogHelp": "Metadatele sunt actualizate pe baza setărilor și a serviciilor de internet care sunt activate în tabloul de bord Jellyfin Server.",
"RefreshDialogHelp": "Metadatele sunt actualizate pe baza setărilor și a serviciilor de internet care sunt activate în tabloul de bord.",
"Refresh": "Reîmprospătează",
"Recordings": "Înregistrări",
"RecordingScheduled": "Înregistrare programată.",
@ -1263,7 +1263,7 @@
"PerfectMatch": "Potrivire perfectă",
"People": "Oameni",
"PasswordSaved": "Parolă salvată.",
"PasswordResetProviderHelp": "Alegeți un furnizor de resetare a parolei pentru a fi utilizat atunci când acest utilizator solicită o resetare a parolei",
"PasswordResetProviderHelp": "Alegeți un furnizor de resetare a parolei pentru a fi utilizat atunci când acest utilizator solicită o resetare a parolei.",
"HeaderResetPassword": "Resetează parola",
"PasswordResetConfirmation": "Sigur doriți să resetați parola?",
"PasswordResetComplete": "Parola a fost resetată.",
@ -1300,9 +1300,9 @@
"OptionProfileAudio": "Audio",
"OptionPosterCard": "Carte de afiș",
"OptionPoster": "Afiș",
"OptionPlainVideoItemsHelp": "Dacă este activat, toate videoclipurile sunt reprezentate în DIDL ca „object.item.videoItem” în loc de un tip mai specific, cum ar fi „object.item.videoItem.movie”.",
"OptionPlainVideoItemsHelp": "Toate videoclipurile sunt reprezentate în DIDL ca „object.item.videoItem” în loc de un tip mai specific, cum ar fi „object.item.videoItem.movie”.",
"OptionPlainVideoItems": "Afișați toate videoclipurile ca elemente video simple",
"OptionPlainStorageFoldersHelp": "Dacă este activat, toate folderele sunt reprezentate în DIDL ca „object.container.storageFolder” în loc de un tip mai specific, cum ar fi „object.container.person.musicArtist”.",
"OptionPlainStorageFoldersHelp": "Toate dosarele sunt reprezentate în DIDL ca „object.container.storageFolder” în loc de un tip mai specific, cum ar fi „object.container.person.musicArtist”.",
"OptionPlainStorageFolders": "Afișați toate dosarele ca dosare simple de stocare",
"OptionOnInterval": "La un interval",
"OptionOnAppStartup": "La pornirea aplicației",
@ -1315,7 +1315,7 @@
"OptionList": "Listă",
"OptionIsSD": "SD",
"OptionIsHD": "HD",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Dacă sunt activate, aceste solicitări vor fi respectate, dar vor ignora antetul intervalului de octeți.",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "Aceste solicitări vor fi respectate, dar vor ignora antetul intervalului de octeți.",
"OptionIgnoreTranscodeByteRangeRequests": "Ignorați solicitările pentru transcodarea intervalului de octeți",
"OptionHomeVideos": "Fotografii",
"OptionHlsSegmentedSubtitles": "Subtitrare segmentată HLS",
@ -1332,7 +1332,7 @@
"OptionEnableExternalContentInSuggestions": "Activați conținut extern în sugestii",
"OptionEmbedSubtitles": "Inclus în container",
"OptionDownloadLogoImage": "Siglă",
"OptionDownloadImagesInAdvanceHelp": "În mod implicit, majoritatea imaginilor sunt descărcate numai la cererea unei aplicații din Jellyfin. Activați această opțiune pentru a descărca în prealabil toate imaginile, pe măsură ce fișierele media sunt importate. Acest lucru poate provoca scanări ale bibliotecii semnificativ mai lungi.",
"OptionDownloadImagesInAdvanceHelp": "În mod implicit, majoritatea imaginilor sunt descărcate numai la cererea unei aplicații Jellyfin. Activați această opțiune pentru a descărca în avans toate imaginile, pe măsură ce fișiere media noi sunt importate. Acest lucru poate duce la mărirea semnificativă a timpilor de scanare a bibliotecii.",
"OptionDownloadImagesInAdvance": "Descărcați imaginile în avans",
"OptionDownloadDiscImage": "Disc",
"OptionDisplayFolderViewHelp": "Afișați dosarele alături de celelalte biblioteci media. Acest lucru poate fi util dacă doriți să aveți o vizualizare direct în dosar.",
@ -1476,7 +1476,7 @@
"LabelRequireHttps": "Trebuie HTTPS",
"LabelStable": "Stabilă",
"LabelChromecastVersion": "Versiunea de Chromecast",
"LabelEnableHttpsHelp": "Activează serverul să asculte pe portul HTTPS configurat. Un certificat valid trebuie de asemenea configurat pentru ca să funcţioneze.",
"LabelEnableHttpsHelp": "Ascultă pe portul HTTPS configurat. Un certificat valid trebuie de asemenea configurat pentru ca să funcţioneze.",
"LabelEnableHttps": "Activați HTTPS",
"HeaderServerAddressSettings": "Setările adresei serverului",
"HeaderRemoteAccessSettings": "Setări pentru aces remote",
@ -1539,5 +1539,11 @@
"LabelRepositoryNameHelp": "Un nume personalizat pentru a distinge acest repertoriu de altele adăugate la serverul dvs.",
"ClearQueue": "Golește lista de redare",
"StopPlayback": "Oprește redarea",
"ViewAlbumArtist": "Vezi artistul albumului"
"ViewAlbumArtist": "Vezi artistul albumului",
"NextTrack": "Sari la următorul",
"LabelUnstable": "Instabil",
"Preview": "Previzualizare",
"SubtitleVerticalPositionHelp": "Numărul de linie unde apare textul. Numerele pozitive indică de sus în jos. Numerele negative indică de jos în sus.",
"LabelSubtitleVerticalPosition": "Poziție verticală:",
"PreviousTrack": "Sari anterior"
}

View file

@ -1429,5 +1429,23 @@
"OptionEnableM2tsModeHelp": "Omogoči m2ts način pri kodiranju v mpegts.",
"OptionEnableM2tsMode": "Omogoči M2ts način",
"OptionDisplayFolderViewHelp": "Prikaže mape poleg ostalih knjižnic predstavnosti. Uporabno za preprost ogled map.",
"OptionDisplayFolderView": "Prikaži pogled mape za prikaz navadnih map predstavnosti"
"OptionDisplayFolderView": "Prikaži pogled mape za prikaz navadnih map predstavnosti",
"Yesterday": "Včeraj",
"Yes": "Da",
"RecommendationStarring": "Nastopa {0}",
"Recordings": "Posnetki",
"RemoveFromCollection": "Odstrani iz zbirke",
"ResumeAt": "Nadaljuj od {0}",
"SaveSubtitlesIntoMediaFolders": "Shrani podnapise v mape predstavnosti",
"ScanForNewAndUpdatedFiles": "Poišči nove in spremenjene datoteke",
"Screenshot": "Posnetek zaslona",
"Screenshots": "Posnetki zaslona",
"Search": "Iskanje",
"ShowAdvancedSettings": "Prikaži napredne nastavitve",
"New": "Novo",
"SubtitleOffset": "Zamik podnapisev",
"Subtitles": "Podnapisi",
"Sunday": "Nedelja",
"TabAdvanced": "Napredno",
"TabAlbums": "Albumi"
}

View file

@ -159,7 +159,7 @@
"DetectingDevices": "正在侦测设备",
"DeviceAccessHelp": "这仅适用于可以唯一标识的设备,而不会阻止浏览器访问。限制用户设备访问会阻止使用未在此被批准的新增设备。",
"DirectPlaying": "直接播放",
"DirectStreamHelp2": "直接串流只占用占用很少的CPU并且视频的品质不会有任何损失。",
"DirectStreamHelp2": "直接串流只占用占用很少的CPU并且视频的品质只会有极小程度的损失。",
"DirectStreaming": "直接串流",
"Director": "导演",
"Disabled": "已禁用",
@ -261,7 +261,7 @@
"HeaderAllowMediaDeletionFrom": "允许从中删除媒体",
"HeaderApiKey": "API 密钥",
"HeaderApiKeys": "API 密钥",
"HeaderApiKeysHelp": "外部应用程序需要 API 密钥才能与 Jellyfin Server 进行通信。使用 Jellyfin 账户进行登录时密钥将会自动生成,您也可以手动为某个应用程序分配一个密钥。",
"HeaderApiKeysHelp": "外部应用程序需要 API 密钥才能与服务器进行通信。密钥会在使用普通账户登录时自动生成,或是手动为应用分配。",
"HeaderAudioBooks": "有声读物",
"HeaderAudioSettings": "声音设置",
"HeaderBlockItemsWithNoRating": "通过没有评级和设置不允许的评级锁定内容:",
@ -327,7 +327,7 @@
"HeaderInstall": "安装",
"HeaderInstantMix": "速成合辑",
"HeaderItems": "项目",
"HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据, 请在 Jellyfin 库安装程序中编辑库, 然后找到“元数据储户”部分。",
"HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据, 请编辑库, 然后找到“元数据储户”部分。",
"HeaderLatestEpisodes": "最新剧集",
"HeaderLatestMedia": "最新媒体",
"HeaderLatestMovies": "最新电影",
@ -372,7 +372,7 @@
"HeaderPreferredMetadataLanguage": "首选元数据语言",
"HeaderProfile": "配置",
"HeaderProfileInformation": "配置信息",
"HeaderProfileServerSettingsHelp": "这些参数将控制 Jellyfin 媒体服务器如何呈现给设备。",
"HeaderProfileServerSettingsHelp": "这些参数将控制服务器如何将自己呈现给客户端。",
"HeaderRecentlyPlayed": "最近播放",
"HeaderRecordingOptions": "录制选项",
"HeaderRecordingPostProcessing": "记录后处理",
@ -396,7 +396,7 @@
"HeaderSelectServerCachePath": "选择服务器缓存路径",
"HeaderSelectServerCachePathHelp": "浏览或输入一个路径用于服务器缓存文件,此文件夹必须可写。",
"HeaderSelectTranscodingPath": "选择临时解码路径",
"HeaderSelectTranscodingPathHelp": "浏览或输入一个路径用于临时转码,此文件夹必须可写。",
"HeaderSelectTranscodingPathHelp": "浏览或输入一个路径用于转码文件,此文件夹必须可写。",
"HeaderSendMessage": "发送消息",
"HeaderSeries": "电视剧",
"HeaderSeriesOptions": "系列选项",
@ -445,8 +445,8 @@
"HttpsRequiresCert": "要启用安全连接, 您需要提供一个受信任的 SSL 证书, 例如 Let's Encrypt 。请提供证书或禁用安全连接。",
"Identify": "识别",
"Images": "图片",
"ImportFavoriteChannelsHelp": "如果启用,只有在协调器设备中被标记为我的最爱的频道才会被导入。",
"ImportMissingEpisodesHelp": "如果启用,会将缺少的剧集信息导入到你的 Jellyfin 数据库并分季分剧显示。可能会大大延长媒体库扫描时间。",
"ImportFavoriteChannelsHelp": "只有在协调器设备中被标记为我的最爱的频道才会被导入。",
"ImportMissingEpisodesHelp": "缺少的剧集信息将被导入到你的数据库并分季分剧显示。可能会大大延长媒体库扫描时间。",
"InstallingPackage": "正在安装 {0}(版本 {1}",
"InstantMix": "即时混音",
"ItemCount": "{0} 项",
@ -476,14 +476,14 @@
"LabelAppName": "APP名称",
"LabelAppNameExample": "例如Sickbeard, Sonarr",
"LabelArtists": "艺术家:",
"LabelArtistsHelp": "独立多功能 ;",
"LabelArtistsHelp": "将多个艺术家用分号分隔",
"LabelAudioLanguagePreference": "首选音频语言:",
"LabelAutomaticallyRefreshInternetMetadataEvery": "自动从互联网获取元数据并刷新:",
"LabelBindToLocalNetworkAddress": "监听的本地网络地址:",
"LabelBindToLocalNetworkAddressHelp": "(可选的)覆盖 HTTP 服务器绑定的本地 IP 地址。如果留空,服务器将会监听所有可用的地址。改变这个值需要重启 Jellyfin 服务器。",
"LabelBindToLocalNetworkAddressHelp": "覆盖 HTTP 服务器绑定的本地 IP 地址。如果留空,服务器将会监听所有可用的地址。改变这个值需要重启 Jellyfin 服务器。",
"LabelBirthDate": "出生日期:",
"LabelBirthYear": "出生年份:",
"LabelBlastMessageInterval": "活动信号的时间间隔(秒)",
"LabelBlastMessageInterval": "活动信号的时间间隔",
"LabelBlastMessageIntervalHelp": "确定爆炸活动消息之间的持续时间(以秒为单位)。",
"LabelBlockContentWithTags": "通过标签锁定内容:",
"LabelBurnSubtitles": "烧录字幕:",
@ -541,7 +541,7 @@
"LabelEnableAutomaticPortMapHelp": "通过UPnP将路由器端口自动转发到服务器端口。这可能不适用于某些型号的路由器和网络配置。需要服务器重新启动后才会应用更改。",
"LabelEnableBlastAliveMessages": "爆发活动信号",
"LabelEnableBlastAliveMessagesHelp": "如果该服务器不能被网络中的其他UPnP设备检测到请启用此选项。",
"LabelEnableDlnaClientDiscoveryInterval": "客户端搜寻时间间隔(秒)",
"LabelEnableDlnaClientDiscoveryInterval": "客户端搜寻时间间隔",
"LabelEnableDlnaClientDiscoveryIntervalHelp": "确定由 Jellyfin 执行的 SSDP 搜索之间的持续时间 (以秒为单位)。",
"LabelEnableDlnaDebugLogging": "启用 DLNA 调试日志",
"LabelEnableDlnaDebugLoggingHelp": "创建一个很大的日志文件,仅应在排除故障时使用。",
@ -567,9 +567,9 @@
"LabelForgotPasswordUsernameHelp": "输入你的用户名,如果你还记得。",
"LabelFormat": "格式:",
"LabelFriendlyName": "好记的名称:",
"LabelServerNameHelp": "此名称将用做服务器名,如果留空,将使用计算机名。",
"LabelServerNameHelp": "此名称将用做服务器名,默认使用服务器的主机名。",
"LabelGroupMoviesIntoCollections": "批量添加电影到收藏",
"LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,属于一个收藏的电影将显示为一个分组。",
"LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一收藏的电影将显示为一个分组。",
"LabelH264Crf": "H264 CRF 编码质量等级:",
"LabelEncoderPreset": "H264 和 H265 编码预设:",
"LabelHardwareAccelerationType": "硬件加速:",
@ -577,7 +577,7 @@
"LabelHomeNetworkQuality": "家庭网络质量:",
"LabelHomeScreenSectionValue": "主屏幕模块{0}",
"LabelHttpsPort": "本地 HTTPS 端口号:",
"LabelHttpsPortHelp": "Jellyfin HTTPS 服务器监听端口。",
"LabelHttpsPortHelp": "HTTPS 服务器监听的 TCP 端口。",
"LabelIconMaxHeight": "图标最大高度:",
"LabelIconMaxHeightHelp": "通过UPnP显示的图标最大分辨率。",
"LabelIconMaxWidth": "图标最大宽度:",
@ -604,7 +604,7 @@
"LabelLanguage": "语言:",
"LabelLineup": "排队:",
"LabelLocalHttpServerPortNumber": "本地 HTTP 端口号:",
"LabelLocalHttpServerPortNumberHelp": "Jellyfin HTTP 服务器监听的 TCP 端口。",
"LabelLocalHttpServerPortNumberHelp": "HTTP 服务器监听的 TCP 端口。",
"LabelLockItemToPreventChanges": "锁定此项目防止改动",
"LabelLoginDisclaimer": "登录声明:",
"LabelLoginDisclaimerHelp": "将在登录页面底部显示的信息。",
@ -646,9 +646,9 @@
"LabelMovieCategories": "电影分类:",
"LabelMoviePrefix": "电影前缀:",
"LabelMoviePrefixHelp": "如果将前缀应用于影片标题, 请在此处输入它, 以便服务器可以正确处理它。",
"LabelMovieRecordingPath": "电影录制路径 (可选的)",
"LabelMovieRecordingPath": "电影录制路径",
"LabelMusicStreamingTranscodingBitrate": "音乐转码的比特率:",
"LabelMusicStreamingTranscodingBitrateHelp": "请指定一个音乐媒体串流时的最大比特率。",
"LabelMusicStreamingTranscodingBitrateHelp": "请指定音乐媒体串流时的最大比特率。",
"LabelName": "名字:",
"LabelNewName": "新名字:",
"LabelNewPassword": "新密码:",
@ -659,7 +659,7 @@
"LabelNumber": "编号:",
"LabelNumberOfGuideDays": "下载几天的节目指南:",
"LabelNumberOfGuideDaysHelp": "下载更多天的节目指南可以帮你进一步查看节目列表并做出提前安排,但下载过程也将耗时更久。它将基于频道数量自动选择。",
"LabelOptionalNetworkPath": "(可选的)共享的网络文件夹:",
"LabelOptionalNetworkPath": "共享的网络文件夹:",
"LabelOptionalNetworkPathHelp": "如果这个文件夹在你的网络上是共享的,提供这个网络共享地址能够允许其他设备上的 Jellyfin 应用程序直接访问媒体文件,例如 {0} 或者 {1}。",
"LabelOriginalAspectRatio": "原始长宽比:",
"LabelOriginalTitle": "原标题:",
@ -704,7 +704,7 @@
"LabelReleaseDate": "发行日期:",
"LabelRemoteClientBitrateLimit": "互联网流媒体传输比特率限制Mbps",
"LabelRemoteClientBitrateLimitHelp": "所有网络设备都有一个可选的每流比特率限制。这对于防止设备请求比 internet 连接所能处理的更高的比特率非常有用。这可能会导致服务器上的 CPU 负载增加, 以便将视频转码到较低的比特率。",
"LabelRuntimeMinutes": "播放时长(分钟)",
"LabelRuntimeMinutes": "播放时长",
"LabelSaveLocalMetadata": "将媒体图像保存到媒体所在文件夹",
"LabelSaveLocalMetadataHelp": "直接将媒体图像保存到媒体所在文件夹以方便编辑。",
"LabelScheduledTaskLastRan": "最后运行 {0}, 花费时间 {1}.",
@ -714,7 +714,7 @@
"LabelSelectVersionToInstall": "选择安装版本:",
"LabelSendNotificationToUsers": "发送通知至:",
"LabelSerialNumber": "序列号",
"LabelSeriesRecordingPath": "电视剧录制路径 (可选的)",
"LabelSeriesRecordingPath": "电视剧录制路径",
"LabelServerHost": "主机:",
"LabelServerHostHelp": "192.168.1.100:8096 或 https://myserver.com",
"LabelSimultaneousConnectionLimit": "并发流限制:",
@ -786,7 +786,7 @@
"LabelYoureDone": "完成!",
"LabelZipCode": "邮编:",
"LabelffmpegPath": "FFmpeg 路径:",
"LabelffmpegPathHelp": "FFmpeg 应用程序的文件,或者包含了 FFmpeg 的文件夹的路径。",
"LabelffmpegPathHelp": "FFmpeg 应用文件或包含 FFmpeg 的文件夹的路径。",
"LanNetworksHelp": "在强制带宽限制时,认作本地网络上的 IP 地址或 IP/网络掩码条目的逗号分隔列表。如果设置此项,所有其它 IP 地址将被视为在外部网络上,并且将受到外部带宽限制。如果保留为空,则只将服务器的子网视为本地网络。",
"Large": "大",
"LatestFromLibrary": "最新的{0}",
@ -918,7 +918,7 @@
"OptionAllowLinkSharingHelp": "只有网页包含的媒体信息会被共享。媒体文件不会被公开共享。共享是有时间限制的并且会在 {0} 天后到期。",
"OptionAllowManageLiveTv": "允许电视直播录制管理",
"OptionAllowMediaPlayback": "允许播放媒体",
"OptionAllowMediaPlaybackTranscodingHelp": "由于不支持的媒体格式, 限制对代码转换的访问可能会导致 Jellyfin 应用程序中的播放失败。",
"OptionAllowMediaPlaybackTranscodingHelp": "限制对转码的访问可能会由于不支持的媒体格式导致客户端中播放失败。",
"OptionAllowRemoteControlOthers": "允许其他用户全程控制",
"OptionAllowRemoteSharedDevices": "允许远程控制共享的设备",
"OptionAllowRemoteSharedDevicesHelp": "DLNA 设备在用户对他们进行控制前都被视为是共享的。",
@ -931,7 +931,7 @@
"OptionAuto": "自动",
"OptionAutomatic": "自动",
"OptionAutomaticallyGroupSeries": "自动合并分布在不同文件夹的电视剧",
"OptionAutomaticallyGroupSeriesHelp": "如果启用,分布在这个媒体库的多个文件夹中的同一部电视剧将会自动整合成一部电视剧。",
"OptionAutomaticallyGroupSeriesHelp": "在这个媒体库的多个文件夹中的同一部电视剧将会自动整合成一部电视剧。",
"OptionBlockBooks": "书籍",
"OptionBlockChannelContent": "互联网频道内容",
"OptionBlockLiveTvChannels": "电视直播频道",
@ -952,7 +952,7 @@
"OptionDatePlayed": "播放日期",
"OptionDescending": "降序",
"OptionDisableUser": "禁用此用户",
"OptionDisableUserHelp": "如果禁用该用户,服务器将不允许该用户连接。现有的连接将被终止。",
"OptionDisableUserHelp": "服务器将不允许来自该用户的任何连接。现有的连接将立即被终止。",
"OptionDislikes": "不喜欢",
"OptionDisplayFolderView": "显示一个“文件夹”类别用于按文件夹分类浏览你的媒体文件夹",
"OptionDisplayFolderViewHelp": "在你的媒体库列表中显示文件夹。如果你有按文件夹分类进行浏览的需求,这会非常有用。",
@ -962,7 +962,7 @@
"OptionDownloadBoxImage": "包装",
"OptionDownloadDiscImage": "光盘",
"OptionDownloadImagesInAdvance": "提前下载图片",
"OptionDownloadImagesInAdvanceHelp": "默认下,大部分图片只有在 Jellyfin 应用程序请求时下载。开启此选项将随着媒体导入时下载所有图片。这可能需要更久媒体库扫描时间。",
"OptionDownloadImagesInAdvanceHelp": "默认大多数图片只在客户端请求时下载。开启此选项将在新媒体导入时预先下载所有图片。这可能大大延长媒体库扫描时间。",
"OptionDownloadMenuImage": "菜单",
"OptionDownloadPrimaryImage": "封面图",
"OptionDownloadThumbImage": "缩略图",
@ -994,7 +994,7 @@
"OptionHlsSegmentedSubtitles": "HLS分段字幕",
"OptionHomeVideos": "照片",
"OptionIgnoreTranscodeByteRangeRequests": "忽略转码字节范围请求",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "如果启用,这些请求会被兑现,但会忽略的字节范围标头。",
"OptionIgnoreTranscodeByteRangeRequestsHelp": "这些请求会被兑现,但会忽略的字节范围标头。",
"OptionImdbRating": "IMDb 评分",
"OptionIsHD": "HD高清",
"OptionIsSD": "SD标清",
@ -1009,9 +1009,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": "首映日期",
@ -1316,7 +1316,7 @@
"ErrorDeletingItem": "从 Jellyfin Server 删除项目时出错。请确认 Jellyfin Server 是否拥有对媒体目录的写权限,然后重试。",
"GroupBySeries": "按系列分组",
"HeaderApp": "应用程序",
"DirectStreamHelp1": "该媒体文件的分辨率和编码H.264、AC3 等)与您的设备兼容,但容器格式(.mkv、.avi、.wmv 等)不受支持。因此,视频在串流至您的设备之前将会被即时封装为另一种格式。",
"DirectStreamHelp1": "该媒体文件的分辨率和编码H.264、AC3 等)与您的设备兼容,但文件格式(.mkv、.avi、.wmv 等)不受支持。因此,视频在串流至您的设备之前将会被即时封装为另一种格式。",
"HeaderAppearsOn": "同时出现于",
"HeaderCancelSeries": "取消系列",
"HeaderFavoriteEpisodes": "最爱的剧集",
@ -1361,14 +1361,14 @@
"OptionDownloadLogoImage": "标志",
"OptionLoginAttemptsBeforeLockout": "确定在锁定之前可以进行多少次不正确的登录尝试。",
"OptionLoginAttemptsBeforeLockoutHelp": "如果值为0则表示将允许普通用户尝试三次、管理员尝试五次的默认值。将此设置为-1将禁用此功能。",
"PasswordResetProviderHelp": "选择一个密码重置提供者用于密码重置",
"PasswordResetProviderHelp": "选择一个密码重置提供者用于此用户申请重置密码",
"PlaceFavoriteChannelsAtBeginning": "将最喜爱的频道置顶",
"PlayNext": "播放下一个",
"PlayNextEpisodeAutomatically": "自动播放下一集",
"Premieres": "首映",
"Raised": "提高",
"Recordings": "录音",
"RefreshDialogHelp": "元数据根据设置和Jellyfin服务器中启用的网络服务进行刷新。",
"RefreshDialogHelp": "元数据根据设置和仪表盘中启用的网络服务进行刷新。",
"RepeatEpisodes": "重复剧集",
"Schedule": "日程",
"Screenshot": "屏幕截图",
@ -1421,7 +1421,7 @@
"ButtonAddImage": "添加图片",
"LabelPlayer": "播放器:",
"LabelBaseUrl": "基础 URL",
"LabelBaseUrlHelp": "为服务器 URL添加自定义子目录例如<code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"LabelBaseUrlHelp": "为服务器 URL添加自定义子目录例如<code>http://example.com/<b>&lt;baseurl&gt;</b></code>",
"MusicLibraryHelp": "重播 {0}音乐命名指南{1}。",
"HeaderFavoritePeople": "最喜欢的人物",
"OptionRandom": "随机",
@ -1480,7 +1480,7 @@
"LabelRequireHttpsHelp": "开启后服务器将自动将所有 HTTP 请求重定向到 HTTPS。如果服务器没有启用 HTTPS 则不生效。",
"LabelRequireHttps": "强制 HTTPS",
"LabelStable": "稳定版",
"LabelEnableHttpsHelp": "开启服务器对所配置 HTTPS 端口的监听。必须配置有效的证书才会生效。",
"LabelEnableHttpsHelp": "监听已配置的 HTTPS 端口。必须配置有效的证书才会生效。",
"LabelEnableHttps": "启用 HTTPS",
"LabelChromecastVersion": "Chromecast版本",
"HeaderDVR": "DVR",
@ -1539,5 +1539,13 @@
"ClearQueue": "清空队列",
"StopPlayback": "停止播放",
"Writers": "作者",
"ViewAlbumArtist": "查看专辑艺术家"
"ViewAlbumArtist": "查看专辑艺术家",
"Preview": "预览",
"SubtitleVerticalPositionHelp": "文字出现的行号。正数表示由上到下,负数表示由下到上。",
"LabelSubtitleVerticalPosition": "垂直位置:",
"PreviousTrack": "上一曲",
"MessageGetInstalledPluginsError": "获取已安装插件列表时出现错误。",
"MessagePluginInstallError": "安装插件时出现错误。",
"NextTrack": "下一曲",
"LabelUnstable": "不稳定"
}

View file

@ -40,19 +40,19 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/eslint-parser@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.0.tgz#b123924edd44508782c030066c926f1b807151cd"
integrity sha512-dJDM2Pc01D9TwKL3Mmz2xgVF9X953RBHq9H4gywbN1q8MrfvXmNHfsCt06vvByBVQqm+9WxMs+doEH/R09TwWQ==
"@babel/eslint-parser@^7.11.3":
version "7.11.3"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.3.tgz#ceb94cb6e2457c4a4d2d87db29925e6b48d20786"
integrity sha512-OdCt/CVXdR/eTNTYDEobf4e55m/AAc04ki+/Oe2/GE8ivh2FxX4yDab48lA6t7ysP4M7luap6Fxx3hUVNTwzFQ==
dependencies:
eslint-scope "5.1.0"
eslint-visitor-keys "^1.3.0"
semver "^6.3.0"
"@babel/eslint-plugin@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.11.0.tgz#55d5b6bd29859cabce152f16d01b3a8150d5b295"
integrity sha512-+gfPM0/T6d25jKBgmxWp38W0jqRs16Vt7DPBxGOcnN/7nS2A/6QoaXOYEaccvWS5a9UpWlMIAylivp6UtH8/sQ==
"@babel/eslint-plugin@^7.11.3":
version "7.11.3"
resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.11.3.tgz#66b531f90592f8f0621d072b59ea2c37c91e8d0d"
integrity sha512-gmi3lgaWlYpNb+h7qPfv5GVz2ZVwzCDyV+kAGj+3il+Mv5uan5Yccvdw7m14UAAY2tdTbB0VgRF6ZLjUbrUm0g==
dependencies:
eslint-rule-composer "^0.3.0"
@ -5465,10 +5465,10 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
hls.js@^0.14.7:
version "0.14.7"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.7.tgz#47fbd2662b13121ab17c07aea06b1c07828240cf"
integrity sha512-9JY0D9nwMrfQPRWc8/kEJTKK0TYfDTzIs6Xq+gdCvasRxdvQKQ2T76rdueTkS0AsFV6sQlJN0wxbnI44aRvvUA==
hls.js@^0.14.8:
version "0.14.8"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.8.tgz#c2c6ca7005524c81eece316c2a4a199258bd0590"
integrity sha512-4fh8k/sl1SmYXsT4Om8AY5fKa5tUUtAxup2sffrSMh5MNk4Kt4FOZxbjqTGL5VwkroY1oJ9twSciNQNFbPA/WQ==
dependencies:
eventemitter3 "^4.0.3"
url-toolkit "^2.1.6"
@ -11994,10 +11994,10 @@ webworkify@^1.5.0:
resolved "https://registry.yarnpkg.com/webworkify/-/webworkify-1.5.0.tgz#734ad87a774de6ebdd546e1d3e027da5b8f4a42c"
integrity sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==
whatwg-fetch@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.3.1.tgz#6c1acf37dec176b0fd6bc9a74b616bec2f612935"
integrity sha512-faXTmGDcLuEPBpJwb5LQfyxvubKiE+RlbmmweFGKjvIPFj4uHTTfdtTIkdTRhC6OSH9S9eyYbx8kZ0UEaQqYTA==
whatwg-fetch@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz#e11de14f4878f773fbebcde8871b2c0699af8b30"
integrity sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==
which-module@^1.0.0:
version "1.0.0"