mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-16 02:18:16 -07:00
Merge branch 'master' into migrate-to-ES6-57
This commit is contained in:
commit
4fa691d80d
24
package.json
24
package.json
@ -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": [
|
||||
@ -127,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",
|
||||
@ -158,19 +160,28 @@
|
||||
"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/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 +211,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 +232,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",
|
||||
@ -278,6 +294,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",
|
||||
@ -301,6 +318,7 @@
|
||||
"src/scripts/autoThemes.js",
|
||||
"src/scripts/themeManager.js",
|
||||
"src/scripts/keyboardNavigation.js",
|
||||
"src/scripts/libraryMenu.js",
|
||||
"src/scripts/libraryBrowser.js",
|
||||
"src/scripts/mouseManager.js",
|
||||
"src/scripts/multiDownload.js",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,131 +1,130 @@
|
||||
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);
|
||||
|
||||
// TODO: Check user data change reason?
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
function getEventsToMonitor(instance) {
|
||||
var options = instance.options;
|
||||
var monitor = options ? options.monitorEvents : null;
|
||||
if (monitor) {
|
||||
return monitor.split(',');
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function onTimerCreated(e, apiClient, data) {
|
||||
var instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCreated(e, apiClient, data) {
|
||||
var instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onTimerCancelled(e, apiClient, data) {
|
||||
var instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCancelled(e, apiClient, data) {
|
||||
var instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onLibraryChanged(e, apiClient, data) {
|
||||
var instance = this;
|
||||
var 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 || [];
|
||||
if (!itemsAdded.length && !itemsRemoved.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var options = instance.options || {};
|
||||
var parentId = options.parentId;
|
||||
if (parentId) {
|
||||
var foldersAddedTo = data.FoldersAddedTo || [];
|
||||
var foldersRemovedFrom = data.FoldersRemovedFrom || [];
|
||||
var collectionFolders = data.CollectionFolders || [];
|
||||
|
||||
if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const eventsToMonitor = getEventsToMonitor(instance);
|
||||
|
||||
// TODO: Check user data change reason?
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
function onPlaybackStopped(e, stopInfo) {
|
||||
var instance = this;
|
||||
function getEventsToMonitor(instance) {
|
||||
const options = instance.options;
|
||||
const monitor = options ? options.monitorEvents : null;
|
||||
if (monitor) {
|
||||
return monitor.split(',');
|
||||
}
|
||||
|
||||
var state = stopInfo.state;
|
||||
return [];
|
||||
}
|
||||
|
||||
var eventsToMonitor = getEventsToMonitor(instance);
|
||||
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
|
||||
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
|
||||
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
function onTimerCreated(e, apiClient, data) {
|
||||
const instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCreated(e, apiClient, data) {
|
||||
const instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onTimerCancelled(e, apiClient, data) {
|
||||
const instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCancelled(e, apiClient, data) {
|
||||
const instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const itemsAdded = data.ItemsAdded || [];
|
||||
const itemsRemoved = data.ItemsRemoved || [];
|
||||
if (!itemsAdded.length && !itemsRemoved.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = instance.options || {};
|
||||
const parentId = options.parentId;
|
||||
if (parentId) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function addNotificationEvent(instance, name, handler, owner) {
|
||||
var localHandler = handler.bind(instance);
|
||||
instance.notifyRefreshNeeded();
|
||||
}
|
||||
|
||||
function onPlaybackStopped(e, stopInfo) {
|
||||
const instance = this;
|
||||
|
||||
const state = stopInfo.state;
|
||||
|
||||
const eventsToMonitor = getEventsToMonitor(instance);
|
||||
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
|
||||
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
|
||||
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const handler = instance['event_' + name];
|
||||
if (handler) {
|
||||
owner = owner || serverNotifications;
|
||||
events.on(owner, name, localHandler);
|
||||
instance['event_' + name] = localHandler;
|
||||
events.off(owner, name, handler);
|
||||
instance['event_' + name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
function removeNotificationEvent(instance, name, owner) {
|
||||
var 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;
|
||||
|
@ -1,23 +1,19 @@
|
||||
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) {
|
||||
if (layout === selectedLayout) {
|
||||
instance[layout] = true;
|
||||
document.documentElement.classList.add('layout-' + layout);
|
||||
} else {
|
||||
instance[layout] = false;
|
||||
document.documentElement.classList.remove('layout-' + layout);
|
||||
}
|
||||
function setLayout(instance, layout, selectedLayout) {
|
||||
if (layout === selectedLayout) {
|
||||
instance[layout] = true;
|
||||
document.documentElement.classList.add('layout-' + layout);
|
||||
} else {
|
||||
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();
|
||||
|
@ -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({
|
||||
|
@ -1,6 +1,7 @@
|
||||
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';
|
||||
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
|
||||
var currentDialog;
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
function onSubmit(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
function initEditor(context, settings) {
|
||||
context.querySelector('form').addEventListener('submit', onSubmit);
|
||||
|
||||
function initEditor(context, settings) {
|
||||
context.querySelector('form').addEventListener('submit', onSubmit);
|
||||
context.querySelector('.selectSortOrder').value = settings.sortOrder;
|
||||
context.querySelector('.selectSortBy').value = settings.sortBy;
|
||||
}
|
||||
|
||||
context.querySelector('.selectSortOrder').value = settings.sortOrder;
|
||||
context.querySelector('.selectSortBy').value = settings.sortBy;
|
||||
}
|
||||
function centerFocus(elem, horiz, on) {
|
||||
import('scrollHelper').then(({default: scrollHelper}) => {
|
||||
const fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
var fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
function fillSortBy(context, options) {
|
||||
const selectSortBy = context.querySelector('.selectSortBy');
|
||||
|
||||
function fillSortBy(context, options) {
|
||||
var selectSortBy = context.querySelector('.selectSortBy');
|
||||
selectSortBy.innerHTML = options.map(function (o) {
|
||||
return '<option value="' + o.value + '">' + o.name + '</option>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
selectSortBy.innerHTML = options.map(function (o) {
|
||||
return '<option value="' + o.value + '">' + o.name + '</option>';
|
||||
}).join('');
|
||||
}
|
||||
function saveValues(context, settingsKey) {
|
||||
userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value);
|
||||
userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value);
|
||||
}
|
||||
|
||||
function saveValues(context, settings, 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;
|
||||
|
@ -1,428 +1,437 @@
|
||||
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;
|
||||
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
|
||||
apiClient.ajax({
|
||||
|
||||
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
|
||||
apiClient.ajax({
|
||||
type: 'POST',
|
||||
url: apiClient.getUrl(url)
|
||||
|
||||
type: 'POST',
|
||||
url: apiClient.getUrl(url)
|
||||
}).then(function () {
|
||||
hasChanges = true;
|
||||
|
||||
import('toast').then(({default: toast}) => {
|
||||
toast(globalize.translate('MessageDownloadQueued'));
|
||||
});
|
||||
|
||||
focusManager.autoFocus(context);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteLocalSubtitle(context, index) {
|
||||
let msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
|
||||
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm({
|
||||
|
||||
title: globalize.translate('ConfirmDeletion'),
|
||||
text: msg,
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
|
||||
}).then(function () {
|
||||
hasChanges = true;
|
||||
loading.show();
|
||||
|
||||
require(['toast'], function (toast) {
|
||||
toast(globalize.translate('MessageDownloadQueued'));
|
||||
});
|
||||
let itemId = currentItem.Id;
|
||||
let url = 'Videos/' + itemId + '/Subtitles/' + index;
|
||||
|
||||
focusManager.autoFocus(context);
|
||||
});
|
||||
}
|
||||
let apiClient = connectionManager.getApiClient(currentItem.ServerId);
|
||||
|
||||
function deleteLocalSubtitle(context, index) {
|
||||
var msg = globalize.translate('MessageAreYouSureDeleteSubtitles');
|
||||
apiClient.ajax({
|
||||
|
||||
require(['confirm'], function (confirm) {
|
||||
confirm.default({
|
||||
|
||||
title: globalize.translate('ConfirmDeletion'),
|
||||
text: msg,
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
type: 'DELETE',
|
||||
url: apiClient.getUrl(url)
|
||||
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
|
||||
var itemId = currentItem.Id;
|
||||
var url = 'Videos/' + itemId + '/Subtitles/' + index;
|
||||
|
||||
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
|
||||
|
||||
apiClient.ajax({
|
||||
|
||||
type: 'DELETE',
|
||||
url: apiClient.getUrl(url)
|
||||
|
||||
}).then(function () {
|
||||
hasChanges = true;
|
||||
reload(context, apiClient, itemId);
|
||||
});
|
||||
hasChanges = true;
|
||||
reload(context, apiClient, itemId);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fillSubtitleList(context, item) {
|
||||
var streams = item.MediaStreams || [];
|
||||
function fillSubtitleList(context, item) {
|
||||
let streams = item.MediaStreams || [];
|
||||
|
||||
var subs = streams.filter(function (s) {
|
||||
return s.Type === 'Subtitle';
|
||||
});
|
||||
let subs = streams.filter(function (s) {
|
||||
return s.Type === 'Subtitle';
|
||||
});
|
||||
|
||||
var html = '';
|
||||
let html = '';
|
||||
|
||||
if (subs.length) {
|
||||
html += '<h2>' + globalize.translate('MySubtitles') + '</h2>';
|
||||
if (subs.length) {
|
||||
html += '<h2>' + globalize.translate('MySubtitles') + '</h2>';
|
||||
|
||||
html += '<div>';
|
||||
html += '<div>';
|
||||
|
||||
html += subs.map(function (s) {
|
||||
var itemHtml = '';
|
||||
html += subs.map(function (s) {
|
||||
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';
|
||||
}
|
||||
|
||||
className += ' listItem-noborder';
|
||||
|
||||
itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">';
|
||||
|
||||
itemHtml += '<span class="listItemIcon material-icons closed_caption"></span>';
|
||||
|
||||
itemHtml += '<div class="listItemBody two-line">';
|
||||
|
||||
itemHtml += '<div>';
|
||||
itemHtml += s.DisplayTitle || '';
|
||||
itemHtml += '</div>';
|
||||
|
||||
if (s.Path) {
|
||||
itemHtml += '<div class="secondary listItemBodyText">' + (s.Path) + '</div>';
|
||||
}
|
||||
|
||||
itemHtml += '</a>';
|
||||
itemHtml += '</div>';
|
||||
|
||||
if (!layoutManager.tv) {
|
||||
if (s.Path) {
|
||||
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete"></span></button>';
|
||||
}
|
||||
}
|
||||
|
||||
itemHtml += '</' + tagName + '>';
|
||||
|
||||
return itemHtml;
|
||||
}).join('');
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
var elem = context.querySelector('.subtitleList');
|
||||
|
||||
if (subs.length) {
|
||||
elem.classList.remove('hide');
|
||||
} else {
|
||||
elem.classList.add('hide');
|
||||
}
|
||||
elem.innerHTML = html;
|
||||
}
|
||||
|
||||
function fillLanguages(context, apiClient, languages) {
|
||||
var selectLanguage = context.querySelector('#selectLanguage');
|
||||
|
||||
selectLanguage.innerHTML = languages.map(function (l) {
|
||||
return '<option value="' + l.ThreeLetterISOLanguageName + '">' + l.DisplayName + '</option>';
|
||||
});
|
||||
|
||||
var lastLanguage = userSettings.get('subtitleeditor-language');
|
||||
if (lastLanguage) {
|
||||
selectLanguage.value = lastLanguage;
|
||||
} else {
|
||||
apiClient.getCurrentUser().then(function (user) {
|
||||
var lang = user.Configuration.SubtitleLanguagePreference;
|
||||
|
||||
if (lang) {
|
||||
selectLanguage.value = lang;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderSearchResults(context, results) {
|
||||
var lastProvider = '';
|
||||
var html = '';
|
||||
|
||||
if (!results.length) {
|
||||
context.querySelector('.noSearchResults').classList.remove('hide');
|
||||
context.querySelector('.subtitleResults').innerHTML = '';
|
||||
loading.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
context.querySelector('.noSearchResults').classList.add('hide');
|
||||
|
||||
for (var i = 0, length = results.length; i < length; i++) {
|
||||
var result = results[i];
|
||||
|
||||
var provider = result.ProviderName;
|
||||
|
||||
if (provider !== lastProvider) {
|
||||
if (i > 0) {
|
||||
html += '</div>';
|
||||
}
|
||||
html += '<h2>' + provider + '</h2>';
|
||||
html += '<div>';
|
||||
lastProvider = provider;
|
||||
}
|
||||
|
||||
var tagName = layoutManager.tv ? 'button' : 'div';
|
||||
var className = layoutManager.tv ? 'listItem listItem-border btnOptions' : 'listItem listItem-border';
|
||||
if (layoutManager.tv) {
|
||||
className += ' listItem-focusscale listItem-button';
|
||||
}
|
||||
|
||||
html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">';
|
||||
className += ' listItem-noborder';
|
||||
|
||||
html += '<span class="listItemIcon material-icons closed_caption"></span>';
|
||||
itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">';
|
||||
|
||||
var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
|
||||
itemHtml += '<span class="listItemIcon material-icons closed_caption"></span>';
|
||||
|
||||
html += '<div class="listItemBody ' + bodyClass + '">';
|
||||
itemHtml += '<div class="listItemBody two-line">';
|
||||
|
||||
html += '<div>' + (result.Name) + '</div>';
|
||||
html += '<div class="secondary listItemBodyText">';
|
||||
itemHtml += '<div>';
|
||||
itemHtml += s.DisplayTitle || '';
|
||||
itemHtml += '</div>';
|
||||
|
||||
if (result.Format) {
|
||||
html += '<span style="margin-right:1em;">' + globalize.translate('FormatValue', result.Format) + '</span>';
|
||||
if (s.Path) {
|
||||
itemHtml += '<div class="secondary listItemBodyText">' + (s.Path) + '</div>';
|
||||
}
|
||||
|
||||
if (result.DownloadCount != null) {
|
||||
html += '<span>' + globalize.translate('DownloadsValue', result.DownloadCount) + '</span>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
if (result.Comment) {
|
||||
html += '<div class="secondary listItemBodyText">' + (result.Comment) + '</div>';
|
||||
}
|
||||
|
||||
if (result.IsHashMatch) {
|
||||
html += '<div class="secondary listItemBodyText"><div class="inline-flex align-items-center justify-content-center" style="background:#3388cc;color:#fff;padding: .3em 1em;border-radius:1000em;">' + globalize.translate('PerfectMatch') + '</div></div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
itemHtml += '</a>';
|
||||
itemHtml += '</div>';
|
||||
|
||||
if (!layoutManager.tv) {
|
||||
html += '<button type="button" is="paper-icon-button-light" data-subid="' + result.Id + '" class="btnDownload listItemButton"><span class="material-icons file_download"></span></button>';
|
||||
if (s.Path) {
|
||||
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete"></span></button>';
|
||||
}
|
||||
}
|
||||
|
||||
html += '</' + tagName + '>';
|
||||
itemHtml += '</' + tagName + '>';
|
||||
|
||||
return itemHtml;
|
||||
}).join('');
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
let elem = context.querySelector('.subtitleList');
|
||||
|
||||
if (subs.length) {
|
||||
elem.classList.remove('hide');
|
||||
} else {
|
||||
elem.classList.add('hide');
|
||||
}
|
||||
elem.innerHTML = html;
|
||||
}
|
||||
|
||||
function fillLanguages(context, apiClient, languages) {
|
||||
let selectLanguage = context.querySelector('#selectLanguage');
|
||||
|
||||
selectLanguage.innerHTML = languages.map(function (l) {
|
||||
return '<option value="' + l.ThreeLetterISOLanguageName + '">' + l.DisplayName + '</option>';
|
||||
});
|
||||
|
||||
let lastLanguage = userSettings.get('subtitleeditor-language');
|
||||
if (lastLanguage) {
|
||||
selectLanguage.value = lastLanguage;
|
||||
} else {
|
||||
apiClient.getCurrentUser().then(function (user) {
|
||||
let lang = user.Configuration.SubtitleLanguagePreference;
|
||||
|
||||
if (lang) {
|
||||
selectLanguage.value = lang;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderSearchResults(context, results) {
|
||||
let lastProvider = '';
|
||||
let html = '';
|
||||
|
||||
if (!results.length) {
|
||||
context.querySelector('.noSearchResults').classList.remove('hide');
|
||||
context.querySelector('.subtitleResults').innerHTML = '';
|
||||
loading.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
context.querySelector('.noSearchResults').classList.add('hide');
|
||||
|
||||
for (let i = 0, length = results.length; i < length; i++) {
|
||||
let result = results[i];
|
||||
|
||||
let provider = result.ProviderName;
|
||||
|
||||
if (provider !== lastProvider) {
|
||||
if (i > 0) {
|
||||
html += '</div>';
|
||||
}
|
||||
html += '<h2>' + provider + '</h2>';
|
||||
html += '<div>';
|
||||
lastProvider = provider;
|
||||
}
|
||||
|
||||
if (results.length) {
|
||||
html += '</div>';
|
||||
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';
|
||||
}
|
||||
|
||||
var elem = context.querySelector('.subtitleResults');
|
||||
elem.innerHTML = html;
|
||||
html += '<' + tagName + ' class="' + className + '" data-subid="' + result.Id + '">';
|
||||
|
||||
html += '<span class="listItemIcon material-icons closed_caption"></span>';
|
||||
|
||||
let bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line';
|
||||
|
||||
html += '<div class="listItemBody ' + bodyClass + '">';
|
||||
|
||||
html += '<div>' + (result.Name) + '</div>';
|
||||
html += '<div class="secondary listItemBodyText">';
|
||||
|
||||
if (result.Format) {
|
||||
html += '<span style="margin-right:1em;">' + globalize.translate('FormatValue', result.Format) + '</span>';
|
||||
}
|
||||
|
||||
if (result.DownloadCount != null) {
|
||||
html += '<span>' + globalize.translate('DownloadsValue', result.DownloadCount) + '</span>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
if (result.Comment) {
|
||||
html += '<div class="secondary listItemBodyText">' + (result.Comment) + '</div>';
|
||||
}
|
||||
|
||||
if (result.IsHashMatch) {
|
||||
html += '<div class="secondary listItemBodyText"><div class="inline-flex align-items-center justify-content-center" style="background:#3388cc;color:#fff;padding: .3em 1em;border-radius:1000em;">' + globalize.translate('PerfectMatch') + '</div></div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (!layoutManager.tv) {
|
||||
html += '<button type="button" is="paper-icon-button-light" data-subid="' + result.Id + '" class="btnDownload listItemButton"><span class="material-icons file_download"></span></button>';
|
||||
}
|
||||
|
||||
html += '</' + tagName + '>';
|
||||
}
|
||||
|
||||
if (results.length) {
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
let elem = context.querySelector('.subtitleResults');
|
||||
elem.innerHTML = html;
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function searchForSubtitles(context, language) {
|
||||
userSettings.set('subtitleeditor-language', language);
|
||||
|
||||
loading.show();
|
||||
|
||||
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) {
|
||||
context.querySelector('.noSearchResults').classList.add('hide');
|
||||
|
||||
function onGetItem(item) {
|
||||
currentItem = item;
|
||||
|
||||
fillSubtitleList(context, item);
|
||||
let file = item.Path || '';
|
||||
let index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
|
||||
if (index > -1) {
|
||||
file = file.substring(index + 1);
|
||||
}
|
||||
|
||||
if (file) {
|
||||
context.querySelector('.pathValue').innerHTML = file;
|
||||
context.querySelector('.originalFile').classList.remove('hide');
|
||||
} else {
|
||||
context.querySelector('.pathValue').innerHTML = '';
|
||||
context.querySelector('.originalFile').classList.add('hide');
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function searchForSubtitles(context, language) {
|
||||
userSettings.set('subtitleeditor-language', language);
|
||||
if (typeof itemId === 'string') {
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem);
|
||||
} else {
|
||||
onGetItem(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
loading.show();
|
||||
function onSearchSubmit(e) {
|
||||
let form = this;
|
||||
|
||||
var apiClient = connectionManager.getApiClient(currentItem.ServerId);
|
||||
var url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language);
|
||||
let lang = form.querySelector('#selectLanguage', form).value;
|
||||
|
||||
apiClient.getJSON(url).then(function (results) {
|
||||
renderSearchResults(context, results);
|
||||
});
|
||||
searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang);
|
||||
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
function onSubtitleListClick(e) {
|
||||
let btnDelete = dom.parentWithClass(e.target, 'btnDelete');
|
||||
if (btnDelete) {
|
||||
let index = btnDelete.getAttribute('data-index');
|
||||
let context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
|
||||
deleteLocalSubtitle(context, index);
|
||||
}
|
||||
}
|
||||
|
||||
function onSubtitleResultsClick(e) {
|
||||
let subtitleId;
|
||||
let context;
|
||||
|
||||
let btnOptions = dom.parentWithClass(e.target, 'btnOptions');
|
||||
if (btnOptions) {
|
||||
subtitleId = btnOptions.getAttribute('data-subid');
|
||||
context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog');
|
||||
showDownloadOptions(btnOptions, context, subtitleId);
|
||||
}
|
||||
|
||||
function reload(context, apiClient, itemId) {
|
||||
context.querySelector('.noSearchResults').classList.add('hide');
|
||||
let btnDownload = dom.parentWithClass(e.target, 'btnDownload');
|
||||
if (btnDownload) {
|
||||
subtitleId = btnDownload.getAttribute('data-subid');
|
||||
context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog');
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
}
|
||||
}
|
||||
|
||||
function onGetItem(item) {
|
||||
currentItem = item;
|
||||
function showDownloadOptions(button, context, subtitleId) {
|
||||
let items = [];
|
||||
|
||||
fillSubtitleList(context, item);
|
||||
var file = item.Path || '';
|
||||
var index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
|
||||
if (index > -1) {
|
||||
file = file.substring(index + 1);
|
||||
items.push({
|
||||
name: globalize.translate('Download'),
|
||||
id: 'download'
|
||||
});
|
||||
|
||||
import('actionsheet').then(({default: actionsheet}) => {
|
||||
actionsheet.show({
|
||||
items: items,
|
||||
positionTo: button
|
||||
|
||||
}).then(function (id) {
|
||||
switch (id) {
|
||||
case 'download':
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (file) {
|
||||
context.querySelector('.pathValue').innerHTML = file;
|
||||
context.querySelector('.originalFile').classList.remove('hide');
|
||||
} else {
|
||||
context.querySelector('.pathValue').innerHTML = '';
|
||||
context.querySelector('.originalFile').classList.add('hide');
|
||||
}
|
||||
function centerFocus(elem, horiz, on) {
|
||||
import('scrollHelper').then(({default: scrollHelper}) => {
|
||||
let fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
function showEditorInternal(itemId, serverId, template) {
|
||||
hasChanges = false;
|
||||
|
||||
if (typeof itemId === 'string') {
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem);
|
||||
let apiClient = connectionManager.getApiClient(serverId);
|
||||
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
|
||||
let dialogOptions = {
|
||||
removeOnClose: true,
|
||||
scrollY: false
|
||||
};
|
||||
|
||||
if (layoutManager.tv) {
|
||||
dialogOptions.size = 'fullscreen';
|
||||
} else {
|
||||
onGetItem(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
function onSearchSubmit(e) {
|
||||
var form = this;
|
||||
|
||||
var 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');
|
||||
if (btnDelete) {
|
||||
var index = btnDelete.getAttribute('data-index');
|
||||
var context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog');
|
||||
deleteLocalSubtitle(context, index);
|
||||
}
|
||||
}
|
||||
|
||||
function onSubtitleResultsClick(e) {
|
||||
var subtitleId;
|
||||
var context;
|
||||
|
||||
var btnOptions = dom.parentWithClass(e.target, 'btnOptions');
|
||||
if (btnOptions) {
|
||||
subtitleId = btnOptions.getAttribute('data-subid');
|
||||
context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog');
|
||||
showDownloadOptions(btnOptions, context, subtitleId);
|
||||
dialogOptions.size = 'small';
|
||||
}
|
||||
|
||||
var btnDownload = dom.parentWithClass(e.target, 'btnDownload');
|
||||
if (btnDownload) {
|
||||
subtitleId = btnDownload.getAttribute('data-subid');
|
||||
context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog');
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
let dlg = dialogHelper.createDialog(dialogOptions);
|
||||
|
||||
dlg.classList.add('formDialog');
|
||||
dlg.classList.add('subtitleEditorDialog');
|
||||
|
||||
dlg.innerHTML = globalize.translateHtml(template, 'core');
|
||||
|
||||
dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File');
|
||||
|
||||
dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit);
|
||||
|
||||
let btnSubmit = dlg.querySelector('.btnSubmit');
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
|
||||
dlg.querySelector('.btnSearchSubtitles').classList.add('hide');
|
||||
} else {
|
||||
btnSubmit.classList.add('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function showDownloadOptions(button, context, subtitleId) {
|
||||
var items = [];
|
||||
let editorContent = dlg.querySelector('.formDialogContent');
|
||||
|
||||
items.push({
|
||||
name: globalize.translate('Download'),
|
||||
id: 'download'
|
||||
dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick);
|
||||
dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick);
|
||||
|
||||
apiClient.getCultures().then(function (languages) {
|
||||
fillLanguages(editorContent, apiClient, languages);
|
||||
});
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
actionsheet.show({
|
||||
items: items,
|
||||
positionTo: button
|
||||
|
||||
}).then(function (id) {
|
||||
switch (id) {
|
||||
case 'download':
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
var fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
|
||||
function showEditorInternal(itemId, serverId, template) {
|
||||
hasChanges = false;
|
||||
|
||||
var apiClient = connectionManager.getApiClient(serverId);
|
||||
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
|
||||
var dialogOptions = {
|
||||
removeOnClose: true,
|
||||
scrollY: false
|
||||
};
|
||||
|
||||
if (layoutManager.tv) {
|
||||
dialogOptions.size = 'fullscreen';
|
||||
} else {
|
||||
dialogOptions.size = 'small';
|
||||
}
|
||||
|
||||
var dlg = dialogHelper.createDialog(dialogOptions);
|
||||
|
||||
dlg.classList.add('formDialog');
|
||||
dlg.classList.add('subtitleEditorDialog');
|
||||
|
||||
dlg.innerHTML = globalize.translateHtml(template, 'core');
|
||||
|
||||
dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File');
|
||||
|
||||
dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit);
|
||||
|
||||
var btnSubmit = dlg.querySelector('.btnSubmit');
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.formDialogContent'), false, true);
|
||||
dlg.querySelector('.btnSearchSubtitles').classList.add('hide');
|
||||
} else {
|
||||
btnSubmit.classList.add('hide');
|
||||
}
|
||||
|
||||
var editorContent = dlg.querySelector('.formDialogContent');
|
||||
|
||||
dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick);
|
||||
dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick);
|
||||
|
||||
apiClient.getCultures().then(function (languages) {
|
||||
fillLanguages(editorContent, apiClient, languages);
|
||||
});
|
||||
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
dlg.addEventListener('close', function () {
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
dialogHelper.open(dlg);
|
||||
|
||||
reload(editorContent, apiClient, item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showEditor(itemId, serverId) {
|
||||
loading.show();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
require(['text!./subtitleeditor.template.html'], function (template) {
|
||||
showEditorInternal(itemId, serverId, template).then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
dlg.addEventListener('close', function () {
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
|
||||
}
|
||||
|
||||
return {
|
||||
show: showEditor
|
||||
};
|
||||
});
|
||||
if (hasChanges) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
dialogHelper.open(dlg);
|
||||
|
||||
reload(editorContent, apiClient, item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showEditor(itemId, serverId) {
|
||||
loading.show();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
import('text!./subtitleeditor.template.html').then(({default: template}) => {
|
||||
showEditorInternal(itemId, serverId, template).then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
show: showEditor
|
||||
};
|
||||
|
@ -1,147 +1,148 @@
|
||||
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) {
|
||||
const parent = document.createElement('div');
|
||||
document.body.appendChild(parent);
|
||||
parent.innerHTML = template;
|
||||
|
||||
function init(instance) {
|
||||
var parent = document.createElement('div');
|
||||
document.body.appendChild(parent);
|
||||
parent.innerHTML = template;
|
||||
subtitleSyncSlider = parent.querySelector('.subtitleSyncSlider');
|
||||
subtitleSyncTextField = parent.querySelector('.subtitleSyncTextField');
|
||||
subtitleSyncCloseButton = parent.querySelector('.subtitleSync-closeButton');
|
||||
subtitleSyncContainer = parent.querySelector('.subtitleSyncContainer');
|
||||
|
||||
subtitleSyncSlider = parent.querySelector('.subtitleSyncSlider');
|
||||
subtitleSyncTextField = parent.querySelector('.subtitleSyncTextField');
|
||||
subtitleSyncCloseButton = parent.querySelector('.subtitleSync-closeButton');
|
||||
subtitleSyncContainer = parent.querySelector('.subtitleSyncContainer');
|
||||
if (layoutManager.tv) {
|
||||
subtitleSyncSlider.classList.add('focusable');
|
||||
// HACK: Delay to give time for registered element attach (Firefox)
|
||||
setTimeout(function () {
|
||||
subtitleSyncSlider.enableKeyboardDragging();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
subtitleSyncSlider.classList.add('focusable');
|
||||
// HACK: Delay to give time for registered element attach (Firefox)
|
||||
setTimeout(function () {
|
||||
subtitleSyncSlider.enableKeyboardDragging();
|
||||
}, 0);
|
||||
}
|
||||
subtitleSyncContainer.classList.add('hide');
|
||||
|
||||
subtitleSyncContainer.classList.add('hide');
|
||||
subtitleSyncTextField.updateOffset = function (offset) {
|
||||
this.textContent = offset + 's';
|
||||
};
|
||||
|
||||
subtitleSyncTextField.updateOffset = function(offset) {
|
||||
this.textContent = offset + 's';
|
||||
};
|
||||
subtitleSyncTextField.addEventListener('click', function () {
|
||||
// keep focus to prevent fade with osd
|
||||
this.hasFocus = true;
|
||||
});
|
||||
|
||||
subtitleSyncTextField.addEventListener('click', function () {
|
||||
subtitleSyncTextField.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Enter') {
|
||||
// if input key is enter search for float pattern
|
||||
let inputOffset = /[-+]?\d+\.?\d*/g.exec(this.textContent);
|
||||
if (inputOffset) {
|
||||
inputOffset = inputOffset[0];
|
||||
|
||||
// replace current text by considered offset
|
||||
this.textContent = inputOffset + 's';
|
||||
|
||||
inputOffset = parseFloat(inputOffset);
|
||||
// set new offset
|
||||
playbackManager.setSubtitleOffset(inputOffset, player);
|
||||
// synchronize with slider value
|
||||
subtitleSyncSlider.updateOffset(
|
||||
getPercentageFromOffset(inputOffset));
|
||||
} else {
|
||||
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
|
||||
}
|
||||
this.hasFocus = false;
|
||||
event.preventDefault();
|
||||
} else {
|
||||
// keep focus to prevent fade with osd
|
||||
this.hasFocus = true;
|
||||
});
|
||||
|
||||
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);
|
||||
if (inputOffset) {
|
||||
inputOffset = inputOffset[0];
|
||||
|
||||
// replace current text by considered offset
|
||||
this.textContent = inputOffset + 's';
|
||||
|
||||
inputOffset = parseFloat(inputOffset);
|
||||
// set new offset
|
||||
playbackManager.setSubtitleOffset(inputOffset, player);
|
||||
// synchronize with slider value
|
||||
subtitleSyncSlider.updateOffset(
|
||||
getPercentageFromOffset(inputOffset));
|
||||
} else {
|
||||
this.textContent = (playbackManager.getPlayerSubtitleOffset(player) || 0) + 's';
|
||||
}
|
||||
this.hasFocus = false;
|
||||
if (event.key.match(/[+-\d.s]/) === null) {
|
||||
event.preventDefault();
|
||||
} else {
|
||||
// keep focus to prevent fade with osd
|
||||
this.hasFocus = true;
|
||||
if (event.key.match(/[+-\d.s]/) === null) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: TV layout will require special handling for navigation keys. But now field is not focusable
|
||||
event.stopPropagation();
|
||||
});
|
||||
// FIXME: TV layout will require special handling for navigation keys. But now field is not focusable
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
subtitleSyncTextField.blur = function() {
|
||||
// prevent textfield to blur while element has focus
|
||||
if (!this.hasFocus && this.prototype) {
|
||||
this.prototype.blur();
|
||||
}
|
||||
};
|
||||
subtitleSyncTextField.blur = function () {
|
||||
// prevent textfield to blur while element has focus
|
||||
if (!this.hasFocus && this.prototype) {
|
||||
this.prototype.blur();
|
||||
}
|
||||
};
|
||||
|
||||
subtitleSyncSlider.updateOffset = function(percent) {
|
||||
// default value is 0s = 50%
|
||||
this.value = percent === undefined ? 50 : percent;
|
||||
};
|
||||
subtitleSyncSlider.updateOffset = function (percent) {
|
||||
// default value is 0s = 50%
|
||||
this.value = percent === undefined ? 50 : percent;
|
||||
};
|
||||
|
||||
subtitleSyncSlider.addEventListener('change', function () {
|
||||
// set new offset
|
||||
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
|
||||
// synchronize with textField value
|
||||
subtitleSyncTextField.updateOffset(
|
||||
getOffsetFromPercentage(this.value));
|
||||
});
|
||||
subtitleSyncSlider.addEventListener('change', function () {
|
||||
// set new offset
|
||||
playbackManager.setSubtitleOffset(getOffsetFromPercentage(this.value), player);
|
||||
// synchronize with textField value
|
||||
subtitleSyncTextField.updateOffset(
|
||||
getOffsetFromPercentage(this.value));
|
||||
});
|
||||
|
||||
subtitleSyncSlider.getBubbleHtml = function (value) {
|
||||
var newOffset = getOffsetFromPercentage(value);
|
||||
return '<h1 class="sliderBubbleText">' +
|
||||
subtitleSyncSlider.getBubbleHtml = function (value) {
|
||||
const newOffset = getOffsetFromPercentage(value);
|
||||
return '<h1 class="sliderBubbleText">' +
|
||||
(newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's' +
|
||||
'</h1>';
|
||||
};
|
||||
};
|
||||
|
||||
subtitleSyncCloseButton.addEventListener('click', function() {
|
||||
playbackManager.disableShowingSubtitleOffset(player);
|
||||
SubtitleSync.prototype.toggle('forceToHide');
|
||||
});
|
||||
subtitleSyncCloseButton.addEventListener('click', function () {
|
||||
playbackManager.disableShowingSubtitleOffset(player);
|
||||
SubtitleSync.prototype.toggle('forceToHide');
|
||||
});
|
||||
|
||||
instance.element = parent;
|
||||
}
|
||||
instance.element = parent;
|
||||
}
|
||||
|
||||
function getOffsetFromPercentage(value) {
|
||||
// convert percent to fraction
|
||||
var offset = (value - 50) / 50;
|
||||
// multiply by offset min/max range value (-x to +x) :
|
||||
offset *= 30;
|
||||
return offset.toFixed(1);
|
||||
}
|
||||
function getOffsetFromPercentage(value) {
|
||||
// convert percent to fraction
|
||||
let offset = (value - 50) / 50;
|
||||
// multiply by offset min/max range value (-x to +x) :
|
||||
offset *= 30;
|
||||
return offset.toFixed(1);
|
||||
}
|
||||
|
||||
function getPercentageFromOffset(value) {
|
||||
// divide by offset min/max range value (-x to +x) :
|
||||
var percentValue = value / 30;
|
||||
// convert fraction to percent
|
||||
percentValue *= 50;
|
||||
percentValue += 50;
|
||||
return Math.min(100, Math.max(0, percentValue.toFixed()));
|
||||
}
|
||||
function getPercentageFromOffset(value) {
|
||||
// divide by offset min/max range value (-x to +x) :
|
||||
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;
|
||||
|
@ -1,30 +1,33 @@
|
||||
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) {
|
||||
var tabControllers = this.tabControllers;
|
||||
function onViewDestroy(e) {
|
||||
var tabControllers = this.tabControllers;
|
||||
|
||||
if (tabControllers) {
|
||||
tabControllers.forEach(function (t) {
|
||||
if (t.destroy) {
|
||||
t.destroy();
|
||||
}
|
||||
});
|
||||
if (tabControllers) {
|
||||
tabControllers.forEach(function (t) {
|
||||
if (t.destroy) {
|
||||
t.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
this.tabControllers = null;
|
||||
}
|
||||
|
||||
this.view = null;
|
||||
this.params = null;
|
||||
this.currentTabController = null;
|
||||
this.initialTabIndex = null;
|
||||
this.tabControllers = null;
|
||||
}
|
||||
|
||||
function onBeforeTabChange() {
|
||||
this.view = null;
|
||||
this.params = null;
|
||||
this.currentTabController = null;
|
||||
this.initialTabIndex = null;
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1,143 +1,146 @@
|
||||
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];
|
||||
html += '<h2 style="margin:.5em 0;">' + version.version + '</h2>';
|
||||
html += '<div style="margin-bottom:1.5em;">' + version.changelog + '</div>';
|
||||
}
|
||||
|
||||
$('#revisionHistory', page).html(html);
|
||||
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>';
|
||||
}
|
||||
|
||||
function populateVersions(packageInfo, page, installedPlugin) {
|
||||
var html = '';
|
||||
$('#revisionHistory', page).html(html);
|
||||
}
|
||||
|
||||
for (var i = 0; i < packageInfo.versions.length; i++) {
|
||||
var version = packageInfo.versions[i];
|
||||
html += '<option value="' + version.version + '">' + version.version + '</option>';
|
||||
}
|
||||
function populateVersions(packageInfo, page, installedPlugin) {
|
||||
let html = '';
|
||||
|
||||
var selectmenu = $('#selectVersion', page).html(html);
|
||||
|
||||
if (!installedPlugin) {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
var packageVersion = packageInfo.versions[0];
|
||||
if (packageVersion) {
|
||||
selectmenu.val(packageVersion.version);
|
||||
}
|
||||
for (let i = 0; i < packageInfo.versions.length; i++) {
|
||||
const version = packageInfo.versions[i];
|
||||
html += '<option value="' + version.version + '">' + version.version + '</option>';
|
||||
}
|
||||
|
||||
function renderPackage(pkg, installedPlugins, page) {
|
||||
var installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Name == pkg.name;
|
||||
})[0];
|
||||
const selectmenu = $('#selectVersion', page).html(html);
|
||||
|
||||
populateVersions(pkg, page, installedPlugin);
|
||||
populateHistory(pkg, page);
|
||||
|
||||
$('.pluginName', page).html(pkg.name);
|
||||
$('#btnInstallDiv', page).removeClass('hide');
|
||||
$('#pSelectVersion', page).removeClass('hide');
|
||||
|
||||
if (pkg.overview) {
|
||||
$('#overview', page).show().html(pkg.overview);
|
||||
} else {
|
||||
$('#overview', page).hide();
|
||||
}
|
||||
|
||||
$('#description', page).html(pkg.description);
|
||||
$('#developer', page).html(pkg.owner);
|
||||
|
||||
if (installedPlugin) {
|
||||
var currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
if (!installedPlugin) {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
function alertText(options) {
|
||||
require(['alert'], function ({default: alert}) {
|
||||
alert(options);
|
||||
});
|
||||
const packageVersion = packageInfo.versions[0];
|
||||
if (packageVersion) {
|
||||
selectmenu.val(packageVersion.version);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPackage(pkg, installedPlugins, page) {
|
||||
const installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Name == pkg.name;
|
||||
})[0];
|
||||
|
||||
populateVersions(pkg, page, installedPlugin);
|
||||
populateHistory(pkg, page);
|
||||
|
||||
$('.pluginName', page).html(pkg.name);
|
||||
$('#btnInstallDiv', page).removeClass('hide');
|
||||
$('#pSelectVersion', page).removeClass('hide');
|
||||
|
||||
if (pkg.overview) {
|
||||
$('#overview', page).show().html(pkg.overview);
|
||||
} else {
|
||||
$('#overview', page).hide();
|
||||
}
|
||||
|
||||
function performInstallation(page, name, guid, version) {
|
||||
var developer = $('#developer', page).html().toLowerCase();
|
||||
$('#description', page).html(pkg.description);
|
||||
$('#developer', page).html(pkg.owner);
|
||||
|
||||
var alertCallback = function () {
|
||||
loading.show();
|
||||
page.querySelector('#btnInstall').disabled = true;
|
||||
ApiClient.installPlugin(name, guid, version).then(function () {
|
||||
loading.hide();
|
||||
alertText(globalize.translate('MessagePluginInstalled'));
|
||||
});
|
||||
};
|
||||
if (installedPlugin) {
|
||||
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().html('');
|
||||
}
|
||||
|
||||
if (developer !== 'jellyfin') {
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function alertText(options) {
|
||||
import('alert').then(({default: alert}) => {
|
||||
alert(options);
|
||||
});
|
||||
}
|
||||
|
||||
function performInstallation(page, name, guid, version) {
|
||||
const developer = $('#developer', page).html().toLowerCase();
|
||||
|
||||
const alertCallback = function () {
|
||||
loading.show();
|
||||
page.querySelector('#btnInstall').disabled = true;
|
||||
ApiClient.installPlugin(name, guid, version).then(() => {
|
||||
loading.hide();
|
||||
var msg = globalize.translate('MessagePluginInstallDisclaimer');
|
||||
msg += '<br/>';
|
||||
msg += '<br/>';
|
||||
msg += globalize.translate('PleaseConfirmPluginInstallation');
|
||||
|
||||
require(['confirm'], function (confirm) {
|
||||
confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
|
||||
alertCallback();
|
||||
}, function () {
|
||||
console.debug('plugin not installed');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alertCallback();
|
||||
}
|
||||
}
|
||||
|
||||
return function (view, params) {
|
||||
$('.addPluginForm', view).on('submit', function () {
|
||||
loading.show();
|
||||
var page = $(this).parents('#addPluginPage')[0];
|
||||
var name = params.name;
|
||||
var guid = params.guid;
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
var installedPlugin = plugins.filter(function (plugin) {
|
||||
return plugin.Name == name;
|
||||
})[0];
|
||||
|
||||
var version = $('#selectVersion', page).val();
|
||||
if (installedPlugin && installedPlugin.Version === version) {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageAlreadyInstalled'),
|
||||
title: globalize.translate('HeaderPluginInstallation')
|
||||
});
|
||||
} else {
|
||||
performInstallation(page, name, guid, version);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
view.addEventListener('viewshow', function () {
|
||||
var page = this;
|
||||
loading.show();
|
||||
var name = params.name;
|
||||
var guid = params.guid;
|
||||
var promise1 = ApiClient.getPackageInfo(name, guid);
|
||||
var promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
renderPackage(responses[0], responses[1], page);
|
||||
});
|
||||
alertText(globalize.translate('MessagePluginInstalled'));
|
||||
}).catch(() => {
|
||||
alertText(globalize.translate('MessagePluginInstallError'));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
if (developer !== 'jellyfin') {
|
||||
loading.hide();
|
||||
let msg = globalize.translate('MessagePluginInstallDisclaimer');
|
||||
msg += '<br/>';
|
||||
msg += '<br/>';
|
||||
msg += globalize.translate('PleaseConfirmPluginInstallation');
|
||||
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () {
|
||||
alertCallback();
|
||||
}).catch(() => {
|
||||
console.debug('plugin not installed');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alertCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export default function(view, params) {
|
||||
$('.addPluginForm', view).on('submit', function () {
|
||||
loading.show();
|
||||
const page = $(this).parents('#addPluginPage')[0];
|
||||
const name = params.name;
|
||||
const guid = params.guid;
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
const installedPlugin = plugins.filter(function (plugin) {
|
||||
return plugin.Name == name;
|
||||
})[0];
|
||||
|
||||
const version = $('#selectVersion', page).val();
|
||||
if (installedPlugin && installedPlugin.Version === version) {
|
||||
loading.hide();
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageAlreadyInstalled'),
|
||||
title: globalize.translate('HeaderPluginInstallation')
|
||||
});
|
||||
} else {
|
||||
performInstallation(page, name, guid, version);
|
||||
}
|
||||
}).catch(() => {
|
||||
alertText(globalize.translate('MessageGetInstalledPluginsError'));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
view.addEventListener('viewshow', function () {
|
||||
const page = this;
|
||||
loading.show();
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,143 +1,142 @@
|
||||
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) {
|
||||
loading.show();
|
||||
var promise1 = ApiClient.getAvailablePlugins();
|
||||
var promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
populateList({
|
||||
catalogElement: page.querySelector('#pluginTiles'),
|
||||
noItemsElement: page.querySelector('#noPlugins'),
|
||||
availablePlugins: responses[0],
|
||||
installedPlugins: responses[1]
|
||||
});
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
const promise1 = ApiClient.getAvailablePlugins();
|
||||
const promise2 = ApiClient.getInstalledPlugins();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
populateList({
|
||||
catalogElement: page.querySelector('#pluginTiles'),
|
||||
noItemsElement: page.querySelector('#noPlugins'),
|
||||
availablePlugins: responses[0],
|
||||
installedPlugins: responses[1]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getHeaderText(category) {
|
||||
category = category.replace(' ', '');
|
||||
// TODO: Replace with switch
|
||||
if (category === 'Channel') {
|
||||
category = 'Channels';
|
||||
} else if (category === 'Theme') {
|
||||
category = 'Themes';
|
||||
} else if (category === 'LiveTV') {
|
||||
category = 'HeaderLiveTV';
|
||||
} else if (category === 'ScreenSaver') {
|
||||
category = 'HeaderScreenSavers';
|
||||
}
|
||||
|
||||
function getHeaderText(category) {
|
||||
category = category.replace(' ', '');
|
||||
if (category === 'Channel') {
|
||||
category = 'Channels';
|
||||
} else if (category === 'Theme') {
|
||||
category = 'Themes';
|
||||
} else if (category === 'LiveTV') {
|
||||
category = 'HeaderLiveTV';
|
||||
} else if (category === 'ScreenSaver') {
|
||||
category = 'HeaderScreenSavers';
|
||||
return globalize.translate(category);
|
||||
}
|
||||
|
||||
function populateList(options) {
|
||||
const availablePlugins = options.availablePlugins;
|
||||
const installedPlugins = options.installedPlugins;
|
||||
|
||||
availablePlugins.forEach(function (plugin, index, array) {
|
||||
plugin.category = plugin.category || 'General';
|
||||
plugin.categoryDisplayName = getHeaderText(plugin.category);
|
||||
array[index] = plugin;
|
||||
});
|
||||
|
||||
availablePlugins.sort(function (a, b) {
|
||||
if (a.category > b.category) {
|
||||
return 1;
|
||||
} else if (b.category > a.category) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
} else if (b.name > a.name) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return globalize.translate(category);
|
||||
}
|
||||
let currentCategory = null;
|
||||
let html = '';
|
||||
|
||||
function populateList(options) {
|
||||
var availablePlugins = options.availablePlugins;
|
||||
var installedPlugins = options.installedPlugins;
|
||||
|
||||
availablePlugins.forEach(function (plugin, index, array) {
|
||||
plugin.category = plugin.category || 'General';
|
||||
plugin.categoryDisplayName = getHeaderText(plugin.category);
|
||||
array[index] = plugin;
|
||||
});
|
||||
|
||||
availablePlugins.sort(function (a, b) {
|
||||
if (a.category > b.category) {
|
||||
return 1;
|
||||
} else if (b.category > a.category) {
|
||||
return -1;
|
||||
for (let i = 0; i < availablePlugins.length; i++) {
|
||||
const plugin = availablePlugins[i];
|
||||
const category = plugin.categoryDisplayName;
|
||||
if (category != currentCategory) {
|
||||
if (currentCategory) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
} else if (b.name > a.name) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var currentCategory = null;
|
||||
var html = '';
|
||||
|
||||
for (var i = 0; i < availablePlugins.length; i++) {
|
||||
var plugin = availablePlugins[i];
|
||||
var category = plugin.categoryDisplayName;
|
||||
if (category != currentCategory) {
|
||||
if (currentCategory) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards">' + category + '</h2>';
|
||||
html += '<div class="itemsContainer vertical-wrap">';
|
||||
currentCategory = category;
|
||||
}
|
||||
html += getPluginHtml(plugin, options, installedPlugins);
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards">' + category + '</h2>';
|
||||
html += '<div class="itemsContainer vertical-wrap">';
|
||||
currentCategory = category;
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += getPluginHtml(plugin, options, installedPlugins);
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
if (!availablePlugins.length && options.noItemsElement) {
|
||||
options.noItemsElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
options.catalogElement.innerHTML = html;
|
||||
loading.hide();
|
||||
if (!availablePlugins.length && options.noItemsElement) {
|
||||
options.noItemsElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
function getPluginHtml(plugin, options, installedPlugins) {
|
||||
var html = '';
|
||||
var href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
options.catalogElement.innerHTML = html;
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
if (options.context) {
|
||||
href += '&context=' + options.context;
|
||||
}
|
||||
function getPluginHtml(plugin, options, installedPlugins) {
|
||||
let html = '';
|
||||
let href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid;
|
||||
|
||||
var target = plugin.externalUrl ? ' target="_blank"' : '';
|
||||
html += "<div class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable visualCardBox-cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + href + '"' + target + '>';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
html += "<div class='cardText'>";
|
||||
html += plugin.name;
|
||||
html += '</div>';
|
||||
var installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Id == plugin.guid;
|
||||
})[0];
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' ';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
if (options.context) {
|
||||
href += '&context=' + options.context;
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
const target = plugin.externalUrl ? ' target="_blank"' : '';
|
||||
html += "<div class='card backdropCard'>";
|
||||
html += '<div class="cardBox visualCardBox">';
|
||||
html += '<div class="cardScalable visualCardBox-cardScalable">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + href + '"' + target + '>';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
html += "<div class='cardText'>";
|
||||
html += plugin.name;
|
||||
html += '</div>';
|
||||
const installedPlugin = installedPlugins.filter(function (ip) {
|
||||
return ip.Id == plugin.guid;
|
||||
})[0];
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' ';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
}
|
||||
|
||||
window.PluginCatalog = {
|
||||
renderCatalog: populateList
|
||||
};
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
return function (view, params) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
libraryMenu.setTabs('plugins', 1, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
};
|
||||
});
|
||||
export default function (view) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
libraryMenu.setTabs('plugins', 1, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
}
|
||||
|
@ -1,192 +1,193 @@
|
||||
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) {
|
||||
confirm.default({
|
||||
title: globalize.translate('HeaderUninstallPlugin'),
|
||||
text: msg,
|
||||
primary: 'delete',
|
||||
confirmText: globalize.translate('HeaderUninstallPlugin')
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
ApiClient.uninstallPlugin(uniqueid).then(function () {
|
||||
reloadList(page);
|
||||
});
|
||||
import('confirm').then(({default: confirm}) => {
|
||||
confirm.default({
|
||||
title: globalize.translate('HeaderUninstallPlugin'),
|
||||
text: msg,
|
||||
primary: 'delete',
|
||||
confirmText: globalize.translate('HeaderUninstallPlugin')
|
||||
}).then(function () {
|
||||
loading.show();
|
||||
ApiClient.uninstallPlugin(uniqueid).then(function () {
|
||||
reloadList(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showNoConfigurationMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageNoPluginConfiguration')
|
||||
});
|
||||
}
|
||||
function showNoConfigurationMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessageNoPluginConfiguration')
|
||||
});
|
||||
}
|
||||
|
||||
function showConnectMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess')
|
||||
});
|
||||
}
|
||||
function showConnectMessage() {
|
||||
Dashboard.alert({
|
||||
message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess')
|
||||
});
|
||||
}
|
||||
|
||||
function getPluginCardHtml(plugin, pluginConfigurationPages) {
|
||||
var configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
|
||||
return pluginConfigurationPage.PluginId == plugin.Id;
|
||||
})[0];
|
||||
var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null;
|
||||
var 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">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer emby-button">';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += configPageUrl ? '</a>' : '</div>';
|
||||
function getPluginCardHtml(plugin, pluginConfigurationPages) {
|
||||
const configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) {
|
||||
return pluginConfigurationPage.PluginId == plugin.Id;
|
||||
})[0];
|
||||
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">';
|
||||
html += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
html += configPageUrl ? '<a class="cardContent cardImageContainer" is="emby-linkbutton" href="' + configPageUrl + '">' : '<div class="cardContent noConfigPluginCard noHoverEffect cardImageContainer emby-button">';
|
||||
html += '<span class="cardImageIcon material-icons folder"></span>';
|
||||
html += configPageUrl ? '</a>' : '</div>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
|
||||
if (configPage || plugin.CanUninstall) {
|
||||
html += '<div style="text-align:right; float:right;padding-top:5px;">';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
|
||||
html += '</div>';
|
||||
html += '<div class="cardFooter">';
|
||||
}
|
||||
|
||||
if (configPage || plugin.CanUninstall) {
|
||||
html += '<div style="text-align:right; float:right;padding-top:5px;">';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnCardMenu autoSize"><span class="material-icons more_vert"></span></button>';
|
||||
html += '</div>';
|
||||
html += "<div class='cardText'>";
|
||||
html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name;
|
||||
html += '</div>';
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += plugin.Version;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPlugins(page, plugins) {
|
||||
ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) {
|
||||
populateList(page, plugins, configPages);
|
||||
});
|
||||
}
|
||||
|
||||
function populateList(page, plugins, pluginConfigurationPages) {
|
||||
plugins = plugins.sort(function (plugin1, plugin2) {
|
||||
if (plugin1.Name > plugin2.Name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
html += "<div class='cardText'>";
|
||||
html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name;
|
||||
html += '</div>';
|
||||
html += "<div class='cardText cardText-secondary'>";
|
||||
html += plugin.Version;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPlugins(page, plugins) {
|
||||
ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) {
|
||||
populateList(page, plugins, configPages);
|
||||
});
|
||||
}
|
||||
|
||||
function populateList(page, plugins, pluginConfigurationPages) {
|
||||
plugins = plugins.sort(function (plugin1, plugin2) {
|
||||
if (plugin1.Name > plugin2.Name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
});
|
||||
|
||||
var html = plugins.map(function (p) {
|
||||
return getPluginCardHtml(p, pluginConfigurationPages);
|
||||
}).join('');
|
||||
|
||||
var installedPluginsElement = page.querySelector('.installedPlugins');
|
||||
installedPluginsElement.removeEventListener('click', onInstalledPluginsClick);
|
||||
installedPluginsElement.addEventListener('click', onInstalledPluginsClick);
|
||||
|
||||
if (plugins.length) {
|
||||
installedPluginsElement.classList.add('itemsContainer');
|
||||
installedPluginsElement.classList.add('vertical-wrap');
|
||||
} else {
|
||||
html += '<div class="centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNoPluginsInstalled') + '</h1>';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="availableplugins.html">';
|
||||
html += globalize.translate('MessageBrowsePluginCatalog');
|
||||
html += '</a></p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
||||
if (configHref) {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonSettings'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (removable === 'true') {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonUninstall'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: elem,
|
||||
callback: function (resultId) {
|
||||
switch (resultId) {
|
||||
case 'open':
|
||||
Dashboard.navigate(configHref);
|
||||
break;
|
||||
case 'delete':
|
||||
deletePlugin(page, id, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
renderPlugins(page, plugins);
|
||||
});
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
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');
|
||||
if (btnCardMenu) {
|
||||
showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageIdOn('pageshow', 'pluginsPage', function () {
|
||||
libraryMenu.setTabs('plugins', 0, getTabs);
|
||||
reloadList(this);
|
||||
return -1;
|
||||
});
|
||||
|
||||
window.PluginsPage = {
|
||||
renderPlugins: renderPlugins
|
||||
};
|
||||
let html = plugins.map(function (p) {
|
||||
return getPluginCardHtml(p, pluginConfigurationPages);
|
||||
}).join('');
|
||||
|
||||
const installedPluginsElement = page.querySelector('.installedPlugins');
|
||||
installedPluginsElement.removeEventListener('click', onInstalledPluginsClick);
|
||||
installedPluginsElement.addEventListener('click', onInstalledPluginsClick);
|
||||
|
||||
if (plugins.length) {
|
||||
installedPluginsElement.classList.add('itemsContainer');
|
||||
installedPluginsElement.classList.add('vertical-wrap');
|
||||
} else {
|
||||
html += '<div class="centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNoPluginsInstalled') + '</h1>';
|
||||
html += '<p><a is="emby-linkbutton" class="button-link" href="availableplugins.html">';
|
||||
html += globalize.translate('MessageBrowsePluginCatalog');
|
||||
html += '</a></p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
installedPluginsElement.innerHTML = html;
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
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({
|
||||
name: globalize.translate('ButtonSettings'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (removable === 'true') {
|
||||
menuItems.push({
|
||||
name: globalize.translate('ButtonUninstall'),
|
||||
id: 'delete',
|
||||
icon: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
import('actionsheet').then(({default: actionsheet}) => {
|
||||
actionsheet.show({
|
||||
items: menuItems,
|
||||
positionTo: elem,
|
||||
callback: function (resultId) {
|
||||
switch (resultId) {
|
||||
case 'open':
|
||||
Dashboard.navigate(configHref);
|
||||
break;
|
||||
case 'delete':
|
||||
deletePlugin(page, id, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reloadList(page) {
|
||||
loading.show();
|
||||
ApiClient.getInstalledPlugins().then(function (plugins) {
|
||||
renderPlugins(page, plugins);
|
||||
});
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: 'installedplugins.html',
|
||||
name: globalize.translate('TabMyPlugins')
|
||||
}, {
|
||||
href: 'availableplugins.html',
|
||||
name: globalize.translate('TabCatalog')
|
||||
}, {
|
||||
href: 'repositories.html',
|
||||
name: globalize.translate('TabRepositories')
|
||||
}];
|
||||
}
|
||||
|
||||
function onInstalledPluginsClick(e) {
|
||||
if (dom.parentWithClass(e.target, 'noConfigPluginCard')) {
|
||||
showNoConfigurationMessage();
|
||||
} else if (dom.parentWithClass(e.target, 'connectModePluginCard')) {
|
||||
showConnectMessage();
|
||||
} else {
|
||||
const btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu');
|
||||
if (btnCardMenu) {
|
||||
showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageIdOn('pageshow', 'pluginsPage', function () {
|
||||
libraryMenu.setTabs('plugins', 0, getTabs);
|
||||
reloadList(this);
|
||||
});
|
||||
|
||||
window.PluginsPage = {
|
||||
renderPlugins: renderPlugins
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,134 +1,128 @@
|
||||
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() {
|
||||
// Returns the minimum amount of idle time required before the screen saver can be displayed
|
||||
//time units used Millisecond
|
||||
return 180000;
|
||||
}
|
||||
|
||||
function getMinIdleTime() {
|
||||
// Returns the minimum amount of idle time required before the screen saver can be displayed
|
||||
//time units used Millisecond
|
||||
return 180000;
|
||||
let lastFunctionalEvent = 0;
|
||||
|
||||
function getFunctionalEventIdleTime() {
|
||||
return new Date().getTime() - lastFunctionalEvent;
|
||||
}
|
||||
|
||||
events.on(playbackManager, 'playbackstop', function (e, stopInfo) {
|
||||
const state = stopInfo.state;
|
||||
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
|
||||
lastFunctionalEvent = new Date().getTime();
|
||||
}
|
||||
|
||||
var lastFunctionalEvent = 0;
|
||||
|
||||
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") {
|
||||
lastFunctionalEvent = new Date().getTime();
|
||||
}
|
||||
});
|
||||
|
||||
function getScreensaverPlugin(isLoggedIn) {
|
||||
|
||||
var option;
|
||||
try {
|
||||
option = userSettings.get("screensaver", false);
|
||||
} catch (err) {
|
||||
option = isLoggedIn ? "backdropscreensaver" : "logoscreensaver";
|
||||
}
|
||||
|
||||
var plugins = pluginManager.ofType("screensaver");
|
||||
|
||||
for (var i = 0, length = plugins.length; i < length; i++) {
|
||||
var plugin = plugins[i];
|
||||
|
||||
if (plugin.id === option) {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function ScreenSaverManager() {
|
||||
|
||||
var self = this;
|
||||
var activeScreenSaver;
|
||||
|
||||
function showScreenSaver(screensaver) {
|
||||
|
||||
if (activeScreenSaver) {
|
||||
throw new Error("An existing screensaver is already active.");
|
||||
}
|
||||
|
||||
console.debug("Showing screensaver " + screensaver.name);
|
||||
|
||||
screensaver.show();
|
||||
activeScreenSaver = screensaver;
|
||||
|
||||
if (screensaver.hideOnClick !== false) {
|
||||
window.addEventListener("click", hide, true);
|
||||
}
|
||||
if (screensaver.hideOnMouse !== false) {
|
||||
window.addEventListener("mousemove", hide, true);
|
||||
}
|
||||
if (screensaver.hideOnKey !== false) {
|
||||
window.addEventListener("keydown", hide, true);
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (activeScreenSaver) {
|
||||
console.debug("Hiding screensaver");
|
||||
activeScreenSaver.hide();
|
||||
activeScreenSaver = null;
|
||||
}
|
||||
|
||||
window.removeEventListener("click", hide, true);
|
||||
window.removeEventListener("mousemove", hide, true);
|
||||
window.removeEventListener("keydown", hide, true);
|
||||
}
|
||||
|
||||
self.isShowing = function () {
|
||||
return activeScreenSaver != null;
|
||||
};
|
||||
|
||||
self.show = function () {
|
||||
var isLoggedIn;
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
|
||||
if (apiClient && apiClient.isLoggedIn()) {
|
||||
isLoggedIn = true;
|
||||
}
|
||||
|
||||
var screensaver = getScreensaverPlugin(isLoggedIn);
|
||||
|
||||
if (screensaver) {
|
||||
showScreenSaver(screensaver);
|
||||
}
|
||||
};
|
||||
|
||||
self.hide = function () {
|
||||
hide();
|
||||
};
|
||||
|
||||
function onInterval() {
|
||||
|
||||
if (self.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputManager.idleTime() < getMinIdleTime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getFunctionalEventIdleTime < getMinIdleTime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (playbackManager.isPlayingVideo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.show();
|
||||
}
|
||||
|
||||
setInterval(onInterval, 10000);
|
||||
}
|
||||
|
||||
return new ScreenSaverManager();
|
||||
});
|
||||
|
||||
function getScreensaverPlugin(isLoggedIn) {
|
||||
let option;
|
||||
try {
|
||||
option = userSettings.get('screensaver', false);
|
||||
} catch (err) {
|
||||
option = isLoggedIn ? 'backdropscreensaver' : 'logoscreensaver';
|
||||
}
|
||||
|
||||
const plugins = pluginManager.ofType('screensaver');
|
||||
|
||||
for (const plugin of plugins) {
|
||||
if (plugin.id === option) {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function ScreenSaverManager() {
|
||||
let activeScreenSaver;
|
||||
|
||||
function showScreenSaver(screensaver) {
|
||||
if (activeScreenSaver) {
|
||||
throw new Error('An existing screensaver is already active.');
|
||||
}
|
||||
|
||||
console.debug('Showing screensaver ' + screensaver.name);
|
||||
|
||||
screensaver.show();
|
||||
activeScreenSaver = screensaver;
|
||||
|
||||
if (screensaver.hideOnClick !== false) {
|
||||
window.addEventListener('click', hide, true);
|
||||
}
|
||||
if (screensaver.hideOnMouse !== false) {
|
||||
window.addEventListener('mousemove', hide, true);
|
||||
}
|
||||
if (screensaver.hideOnKey !== false) {
|
||||
window.addEventListener('keydown', hide, true);
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (activeScreenSaver) {
|
||||
console.debug('Hiding screensaver');
|
||||
activeScreenSaver.hide();
|
||||
activeScreenSaver = null;
|
||||
}
|
||||
|
||||
window.removeEventListener('click', hide, true);
|
||||
window.removeEventListener('mousemove', hide, true);
|
||||
window.removeEventListener('keydown', hide, true);
|
||||
}
|
||||
|
||||
this.isShowing = () => {
|
||||
return activeScreenSaver != null;
|
||||
};
|
||||
|
||||
this.show = function () {
|
||||
let isLoggedIn;
|
||||
const apiClient = connectionManager.currentApiClient();
|
||||
|
||||
if (apiClient && apiClient.isLoggedIn()) {
|
||||
isLoggedIn = true;
|
||||
}
|
||||
|
||||
const screensaver = getScreensaverPlugin(isLoggedIn);
|
||||
|
||||
if (screensaver) {
|
||||
showScreenSaver(screensaver);
|
||||
}
|
||||
};
|
||||
|
||||
this.hide = function () {
|
||||
hide();
|
||||
};
|
||||
|
||||
const onInterval = () => {
|
||||
if (this.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputManager.idleTime() < getMinIdleTime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getFunctionalEventIdleTime < getMinIdleTime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (playbackManager.isPlayingVideo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.show();
|
||||
};
|
||||
|
||||
setInterval(onInterval, 10000);
|
||||
}
|
||||
|
||||
export default new ScreenSaverManager;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,165 +1,165 @@
|
||||
define(['pluginManager'], function (pluginManager) {
|
||||
return function () {
|
||||
var self = this;
|
||||
import pluginManager from 'pluginManager';
|
||||
|
||||
self.name = 'Logo ScreenSaver';
|
||||
self.type = 'screensaver';
|
||||
self.id = 'logoscreensaver';
|
||||
self.supportsAnonymous = true;
|
||||
export default function () {
|
||||
const self = this;
|
||||
|
||||
var interval;
|
||||
self.name = 'Logo ScreenSaver';
|
||||
self.type = 'screensaver';
|
||||
self.id = 'logoscreensaver';
|
||||
self.supportsAnonymous = true;
|
||||
|
||||
function animate() {
|
||||
var animations = [
|
||||
let interval;
|
||||
|
||||
bounceInLeft,
|
||||
bounceInRight,
|
||||
swing,
|
||||
tada,
|
||||
wobble,
|
||||
rotateIn,
|
||||
rotateOut
|
||||
];
|
||||
function animate() {
|
||||
const animations = [
|
||||
|
||||
var elem = document.querySelector('.logoScreenSaverImage');
|
||||
bounceInLeft,
|
||||
bounceInRight,
|
||||
swing,
|
||||
tada,
|
||||
wobble,
|
||||
rotateIn,
|
||||
rotateOut
|
||||
];
|
||||
|
||||
if (elem && elem.animate) {
|
||||
var random = getRandomInt(0, animations.length - 1);
|
||||
const elem = document.querySelector('.logoScreenSaverImage');
|
||||
|
||||
animations[random](elem, 1);
|
||||
if (elem && elem.animate) {
|
||||
const random = getRandomInt(0, animations.length - 1);
|
||||
|
||||
animations[random](elem, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function bounceInLeft(elem, iterations) {
|
||||
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 }];
|
||||
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) {
|
||||
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 }];
|
||||
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) {
|
||||
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 }];
|
||||
const timing = { duration: 900, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function tada(elem, iterations) {
|
||||
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 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 },
|
||||
{ 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 }];
|
||||
const timing = { duration: 900, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function wobble(elem, iterations) {
|
||||
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 }];
|
||||
const timing = { duration: 900, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function rotateIn(elem, iterations) {
|
||||
const keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 },
|
||||
{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }];
|
||||
const timing = { duration: 900, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function rotateOut(elem, iterations) {
|
||||
const keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 },
|
||||
{ transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }];
|
||||
const timing = { duration: 900, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function fadeOut(elem, iterations) {
|
||||
const keyframes = [
|
||||
{ opacity: '1', offset: 0 },
|
||||
{ opacity: '0', offset: 1 }];
|
||||
const timing = { duration: 400, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function stopInterval() {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
}
|
||||
}
|
||||
|
||||
self.show = function () {
|
||||
import('css!' + pluginManager.mapPath(self, 'style.css')).then(() => {
|
||||
let elem = document.querySelector('.logoScreenSaver');
|
||||
|
||||
if (!elem) {
|
||||
elem = document.createElement('div');
|
||||
elem.classList.add('logoScreenSaver');
|
||||
document.body.appendChild(elem);
|
||||
|
||||
elem.innerHTML = '<img class="logoScreenSaverImage" src="assets/img/banner-light.png" />';
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function bounceInLeft(elem, iterations) {
|
||||
var 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)' };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function bounceInRight(elem, iterations) {
|
||||
var 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)' };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function swing(elem, iterations) {
|
||||
var 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 };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function tada(elem, iterations) {
|
||||
var 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 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 },
|
||||
{ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 },
|
||||
{ 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 };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function wobble(elem, iterations) {
|
||||
var 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 };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function rotateIn(elem, iterations) {
|
||||
var 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 };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function rotateOut(elem, iterations) {
|
||||
var 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 };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function fadeOut(elem, iterations) {
|
||||
var keyframes = [
|
||||
{ opacity: '1', offset: 0 },
|
||||
{ opacity: '0', offset: 1 }];
|
||||
var timing = { duration: 400, iterations: iterations };
|
||||
return elem.animate(keyframes, timing);
|
||||
}
|
||||
|
||||
function stopInterval() {
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
}
|
||||
}
|
||||
|
||||
self.show = function () {
|
||||
require(['css!' + pluginManager.mapPath(self, 'style.css')], function () {
|
||||
var elem = document.querySelector('.logoScreenSaver');
|
||||
|
||||
if (!elem) {
|
||||
elem = document.createElement('div');
|
||||
elem.classList.add('logoScreenSaver');
|
||||
document.body.appendChild(elem);
|
||||
|
||||
elem.innerHTML = '<img class="logoScreenSaverImage" src="assets/img/banner-light.png" />';
|
||||
}
|
||||
|
||||
stopInterval();
|
||||
interval = setInterval(animate, 3000);
|
||||
});
|
||||
};
|
||||
|
||||
self.hide = function () {
|
||||
stopInterval();
|
||||
|
||||
var elem = document.querySelector('.logoScreenSaver');
|
||||
|
||||
if (elem) {
|
||||
var onAnimationFinish = function () {
|
||||
elem.parentNode.removeChild(elem);
|
||||
};
|
||||
|
||||
if (elem.animate) {
|
||||
var animation = fadeOut(elem, 1);
|
||||
animation.onfinish = onAnimationFinish;
|
||||
} else {
|
||||
onAnimationFinish();
|
||||
}
|
||||
}
|
||||
};
|
||||
interval = setInterval(animate, 3000);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
self.hide = function () {
|
||||
stopInterval();
|
||||
|
||||
const elem = document.querySelector('.logoScreenSaver');
|
||||
|
||||
if (elem) {
|
||||
const onAnimationFinish = function () {
|
||||
elem.parentNode.removeChild(elem);
|
||||
};
|
||||
|
||||
if (elem.animate) {
|
||||
const animation = fadeOut(elem, 1);
|
||||
animation.onfinish = onAnimationFinish;
|
||||
} else {
|
||||
onAnimationFinish();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,98 +830,98 @@ 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) {
|
||||
if (type) {
|
||||
mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () {
|
||||
return [];
|
||||
});
|
||||
} else {
|
||||
mainTabsManager.setTabs(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
setDefaultTitle: function () {
|
||||
if (!pageTitleElement) {
|
||||
pageTitleElement = document.querySelector('.pageTitle');
|
||||
}
|
||||
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;
|
||||
|
||||
if (pageTitleElement) {
|
||||
pageTitleElement.classList.add('pageTitleWithLogo');
|
||||
pageTitleElement.classList.add('pageTitleWithDefaultLogo');
|
||||
pageTitleElement.style.backgroundImage = null;
|
||||
pageTitleElement.innerHTML = '';
|
||||
}
|
||||
|
||||
document.title = 'Jellyfin';
|
||||
},
|
||||
setTitle: function (title) {
|
||||
if (title == null) {
|
||||
return void LibraryMenu.setDefaultTitle();
|
||||
}
|
||||
|
||||
if (title === '-') {
|
||||
title = '';
|
||||
}
|
||||
|
||||
var html = title;
|
||||
|
||||
if (!pageTitleElement) {
|
||||
pageTitleElement = document.querySelector('.pageTitle');
|
||||
}
|
||||
|
||||
if (pageTitleElement) {
|
||||
pageTitleElement.classList.remove('pageTitleWithLogo');
|
||||
pageTitleElement.classList.remove('pageTitleWithDefaultLogo');
|
||||
pageTitleElement.style.backgroundImage = null;
|
||||
pageTitleElement.innerHTML = html || '';
|
||||
}
|
||||
|
||||
document.title = title || 'Jellyfin';
|
||||
},
|
||||
setTransparentMenu: function (transparent) {
|
||||
if (transparent) {
|
||||
skinHeader.classList.add('semiTransparent');
|
||||
function setTabs (type, selectedIndex, builder) {
|
||||
import('mainTabsManager').then(({default: mainTabsManager}) => {
|
||||
if (type) {
|
||||
mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () {
|
||||
return [];
|
||||
});
|
||||
} else {
|
||||
skinHeader.classList.remove('semiTransparent');
|
||||
mainTabsManager.setTabs(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setDefaultTitle () {
|
||||
if (!pageTitleElement) {
|
||||
pageTitleElement = document.querySelector('.pageTitle');
|
||||
}
|
||||
};
|
||||
var currentPageType;
|
||||
|
||||
if (pageTitleElement) {
|
||||
pageTitleElement.classList.add('pageTitleWithLogo');
|
||||
pageTitleElement.classList.add('pageTitleWithDefaultLogo');
|
||||
pageTitleElement.style.backgroundImage = null;
|
||||
pageTitleElement.innerHTML = '';
|
||||
}
|
||||
|
||||
document.title = 'Jellyfin';
|
||||
}
|
||||
|
||||
function setTitle (title) {
|
||||
if (title == null) {
|
||||
return void LibraryMenu.setDefaultTitle();
|
||||
}
|
||||
|
||||
if (title === '-') {
|
||||
title = '';
|
||||
}
|
||||
|
||||
const html = title;
|
||||
|
||||
if (!pageTitleElement) {
|
||||
pageTitleElement = document.querySelector('.pageTitle');
|
||||
}
|
||||
|
||||
if (pageTitleElement) {
|
||||
pageTitleElement.classList.remove('pageTitleWithLogo');
|
||||
pageTitleElement.classList.remove('pageTitleWithDefaultLogo');
|
||||
pageTitleElement.style.backgroundImage = null;
|
||||
pageTitleElement.innerHTML = html || '';
|
||||
}
|
||||
|
||||
document.title = title || 'Jellyfin';
|
||||
}
|
||||
|
||||
function setTransparentMenu (transparent) {
|
||||
if (transparent) {
|
||||
skinHeader.classList.add('semiTransparent');
|
||||
} else {
|
||||
skinHeader.classList.remove('semiTransparent');
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -1,6 +1,8 @@
|
||||
define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layoutManager, datetime, cardBuilder, appHost) {
|
||||
'use strict';
|
||||
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
}
|
||||
|
@ -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({
|
||||
|
@ -350,6 +350,7 @@ function initClient() {
|
||||
}
|
||||
|
||||
function getLayoutManager(layoutManager, appHost) {
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
if (appHost.getDefaultLayout) {
|
||||
layoutManager.defaultLayout = appHost.getDefaultLayout();
|
||||
}
|
||||
|
@ -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."
|
||||
}
|
||||
|
@ -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.",
|
||||
|
@ -1542,5 +1542,8 @@
|
||||
"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 :"
|
||||
}
|
||||
|
@ -161,7 +161,7 @@
|
||||
"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.",
|
||||
"DirectStreamHelp2": "Direct streamen van een bestand gebruikt weinig processorkracht zonder verlies van beeldkwaliteit.",
|
||||
"DirectStreaming": "Direct streamen",
|
||||
"Director": "Regiseur",
|
||||
"Directors": "Regisseurs",
|
||||
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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><baseurl></b></code>。",
|
||||
"LabelBaseUrlHelp": "为服务器 URL添加自定义子目录,例如:<code>http://example.com/<b><baseurl></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": "不稳定"
|
||||
}
|
||||
|
16
yarn.lock
16
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user