mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 02:48:19 -07:00
Merge remote-tracking branch 'upstream/master' into apphost-es6
This commit is contained in:
commit
566a818ee6
@ -2,4 +2,3 @@ node_modules
|
||||
dist
|
||||
.idea
|
||||
.vscode
|
||||
src/libraries
|
||||
|
30
package.json
30
package.json
@ -17,7 +17,7 @@
|
||||
"babel-loader": "^8.0.6",
|
||||
"browser-sync": "^2.26.12",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^4.2.0",
|
||||
"css-loader": "^4.2.1",
|
||||
"cssnano": "^4.1.10",
|
||||
"del": "^5.1.0",
|
||||
"eslint": "^7.6.0",
|
||||
@ -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": [
|
||||
@ -126,6 +126,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",
|
||||
@ -160,17 +162,24 @@
|
||||
"src/components/qualityOptions.js",
|
||||
"src/components/sanatizefilename.js",
|
||||
"src/components/scrollManager.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/viewManager/viewManager.js",
|
||||
"src/components/tvproviders/schedulesdirect.js",
|
||||
"src/components/tvproviders/xmltv.js",
|
||||
"src/components/toast/toast.js",
|
||||
@ -206,6 +215,9 @@
|
||||
"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",
|
||||
@ -217,9 +229,15 @@
|
||||
"src/controllers/dashboard/users/userparentalcontrol.js",
|
||||
"src/controllers/dashboard/users/userpasswordpage.js",
|
||||
"src/controllers/dashboard/users/userprofilespage.js",
|
||||
"src/controllers/list.js",
|
||||
"src/controllers/edititemmetadata.js",
|
||||
"src/controllers/favorites.js",
|
||||
"src/controllers/hometab.js",
|
||||
"src/controllers/movies/moviecollections.js",
|
||||
"src/controllers/movies/moviegenres.js",
|
||||
"src/controllers/movies/movies.js",
|
||||
"src/controllers/movies/moviesrecommended.js",
|
||||
"src/controllers/movies/movietrailers.js",
|
||||
"src/controllers/playback/nowplaying.js",
|
||||
"src/controllers/playback/videoosd.js",
|
||||
"src/controllers/itemDetails/index.js",
|
||||
@ -272,6 +290,9 @@
|
||||
"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",
|
||||
"src/plugins/bookPlayer/plugin.js",
|
||||
"src/plugins/bookPlayer/tableOfContents.js",
|
||||
@ -297,10 +318,13 @@
|
||||
"src/scripts/mouseManager.js",
|
||||
"src/scripts/multiDownload.js",
|
||||
"src/scripts/playlists.js",
|
||||
"src/scripts/scrollHelper.js",
|
||||
"src/scripts/serverNotifications.js",
|
||||
"src/scripts/routes.js",
|
||||
"src/scripts/settings/appSettings.js",
|
||||
"src/scripts/settings/userSettings.js",
|
||||
"src/scripts/settings/webSettings.js",
|
||||
"src/scripts/shell.js",
|
||||
"src/scripts/taskbutton.js",
|
||||
"src/scripts/themeLoader.js",
|
||||
"src/scripts/touchHelper.js"
|
||||
|
@ -236,12 +236,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.layout-desktop .searchTabButton,
|
||||
.layout-mobile .searchTabButton,
|
||||
.layout-tv .headerSearchButton {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mainDrawer-scrollContainer {
|
||||
padding-bottom: 10vh;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
|
||||
'use strict';
|
||||
|
||||
viewManager = viewManager.default || viewManager;
|
||||
browser = browser.default || browser;
|
||||
loading = loading.default || loading;
|
||||
|
||||
|
@ -200,7 +200,7 @@ import 'flexStyles';
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
import('scrollHelper').then(scrollHelper => {
|
||||
import('scrollHelper').then((scrollHelper) => {
|
||||
const fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
|
@ -354,7 +354,7 @@ import 'scrollStyles';
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
import('scrollHelper').then(scrollHelper => {
|
||||
import('scrollHelper').then((scrollHelper) => {
|
||||
const fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
|
@ -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;
|
||||
@ -151,6 +153,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost',
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
var fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
|
@ -1,6 +1,9 @@
|
||||
define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-radio', 'css!./../formdialog', 'material-icons'], function (dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) {
|
||||
'use strict';
|
||||
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
|
||||
function saveCategories(context, options) {
|
||||
var categories = [];
|
||||
|
||||
|
@ -4,7 +4,10 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager',
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
browser = browser.default || browser;
|
||||
loading = loading.default || loading;
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
focusManager = focusManager.default || focusManager;
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
serverNotifications = serverNotifications.default || serverNotifications;
|
||||
|
||||
function showViewSettings(instance) {
|
||||
require(['guide-settings-dialog'], function (guideSettingsDialog) {
|
||||
|
@ -1,130 +1,130 @@
|
||||
define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) {
|
||||
'use strict';
|
||||
import playbackManager from 'playbackManager';
|
||||
import serverNotifications from 'serverNotifications';
|
||||
import events from 'events';
|
||||
|
||||
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);
|
||||
@ -136,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 {
|
||||
@ -161,9 +161,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
ItemsRefresher.prototype.refreshItems = function () {
|
||||
refreshItems() {
|
||||
if (!this.fetchData) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -176,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);
|
||||
}
|
||||
@ -194,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');
|
||||
@ -244,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();
|
||||
|
@ -2,6 +2,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
|
||||
'use strict';
|
||||
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
serverNotifications = serverNotifications.default || serverNotifications;
|
||||
|
||||
function onOneDocumentClick() {
|
||||
document.removeEventListener('click', onOneDocumentClick);
|
||||
|
@ -210,7 +210,7 @@ import 'emby-button';
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
import('scrollHelper').then(scrollHelper => {
|
||||
import('scrollHelper').then((scrollHelper) => {
|
||||
const fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
|
@ -1,6 +1,9 @@
|
||||
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;
|
||||
var closeAction;
|
||||
var currentRecordingFields;
|
||||
|
@ -1,7 +1,9 @@
|
||||
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'scrollStyles', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader) {
|
||||
'use strict';
|
||||
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
loading = loading.default || loading;
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
|
||||
var currentDialog;
|
||||
var recordingDeleted = false;
|
||||
|
@ -1,6 +1,7 @@
|
||||
define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields', 'flexStyles'], function (globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events) {
|
||||
'use strict';
|
||||
|
||||
serverNotifications = serverNotifications.default || serverNotifications;
|
||||
recordingHelper = recordingHelper.default || recordingHelper;
|
||||
loading = loading.default || loading;
|
||||
|
||||
|
@ -2,6 +2,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
|
||||
'use strict';
|
||||
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
|
||||
var showMuteButton = true;
|
||||
var showVolumeSlider = true;
|
||||
|
@ -1,184 +0,0 @@
|
||||
define(['apphost', 'userSettings', 'browser', 'events', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, backdrop, globalize, require, appSettings) {
|
||||
'use strict';
|
||||
|
||||
var themeStyleElement;
|
||||
var currentThemeId;
|
||||
|
||||
function unloadTheme() {
|
||||
var elem = themeStyleElement;
|
||||
if (elem) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
themeStyleElement = null;
|
||||
currentThemeId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function loadUserSkin(options) {
|
||||
options = options || {};
|
||||
if (options.start) {
|
||||
Emby.Page.invokeShortcut(options.start);
|
||||
} else {
|
||||
Emby.Page.goHome();
|
||||
}
|
||||
}
|
||||
|
||||
function getThemes() {
|
||||
return [{
|
||||
name: 'Apple TV',
|
||||
id: 'appletv'
|
||||
}, {
|
||||
name: 'Blue Radiance',
|
||||
id: 'blueradiance'
|
||||
}, {
|
||||
name: 'Dark',
|
||||
id: 'dark',
|
||||
isDefault: true,
|
||||
isDefaultServerDashboard: true
|
||||
}, {
|
||||
name: 'Light',
|
||||
id: 'light'
|
||||
}, {
|
||||
name: 'Purple Haze',
|
||||
id: 'purplehaze'
|
||||
}, {
|
||||
name: 'Windows Media Center',
|
||||
id: 'wmc'
|
||||
}];
|
||||
}
|
||||
|
||||
var skinManager = {
|
||||
getThemes: getThemes,
|
||||
loadUserSkin: loadUserSkin
|
||||
};
|
||||
|
||||
function getThemeStylesheetInfo(id, isDefaultProperty) {
|
||||
var themes = skinManager.getThemes();
|
||||
var defaultTheme;
|
||||
var selectedTheme;
|
||||
|
||||
for (var i = 0, length = themes.length; i < length; i++) {
|
||||
var theme = themes[i];
|
||||
if (theme[isDefaultProperty]) {
|
||||
defaultTheme = theme;
|
||||
}
|
||||
if (id === theme.id) {
|
||||
selectedTheme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
selectedTheme = selectedTheme || defaultTheme;
|
||||
return {
|
||||
stylesheetPath: require.toUrl('themes/' + selectedTheme.id + '/theme.css'),
|
||||
themeId: selectedTheme.id
|
||||
};
|
||||
}
|
||||
|
||||
var themeResources = {};
|
||||
var lastSound = 0;
|
||||
var currentSound;
|
||||
|
||||
function loadThemeResources(id) {
|
||||
lastSound = 0;
|
||||
if (currentSound) {
|
||||
currentSound.stop();
|
||||
currentSound = null;
|
||||
}
|
||||
|
||||
backdrop.clearBackdrop();
|
||||
}
|
||||
|
||||
function onThemeLoaded() {
|
||||
document.documentElement.classList.remove('preload');
|
||||
try {
|
||||
var color = getComputedStyle(document.querySelector('.skinHeader')).getPropertyValue('background-color');
|
||||
if (color) {
|
||||
appHost.default.setThemeColor(color);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('error setting theme color: ' + err);
|
||||
}
|
||||
}
|
||||
|
||||
skinManager.setTheme = function (id, context) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (currentThemeId && currentThemeId === id) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var isDefaultProperty = context === 'serverdashboard' ? 'isDefaultServerDashboard' : 'isDefault';
|
||||
var info = getThemeStylesheetInfo(id, isDefaultProperty);
|
||||
if (currentThemeId && currentThemeId === info.themeId) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var linkUrl = info.stylesheetPath;
|
||||
unloadTheme();
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.setAttribute('rel', 'stylesheet');
|
||||
link.setAttribute('type', 'text/css');
|
||||
link.onload = function () {
|
||||
onThemeLoaded();
|
||||
resolve();
|
||||
};
|
||||
|
||||
link.setAttribute('href', linkUrl);
|
||||
document.head.appendChild(link);
|
||||
themeStyleElement = link;
|
||||
currentThemeId = info.themeId;
|
||||
loadThemeResources(info.themeId);
|
||||
|
||||
onViewBeforeShow({});
|
||||
});
|
||||
};
|
||||
|
||||
function onViewBeforeShow(e) {
|
||||
if (e.detail && e.detail.type === 'video-osd') {
|
||||
// This removes the space that the scrollbar takes while playing a video
|
||||
document.body.classList.remove('force-scroll');
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeResources.backdrop) {
|
||||
backdrop.setBackdrop(themeResources.backdrop);
|
||||
}
|
||||
|
||||
if (!browser.mobile && userSettings.enableThemeSongs()) {
|
||||
if (lastSound === 0) {
|
||||
if (themeResources.themeSong) {
|
||||
playSound(themeResources.themeSong);
|
||||
}
|
||||
} else if ((new Date().getTime() - lastSound) > 30000) {
|
||||
if (themeResources.effect) {
|
||||
playSound(themeResources.effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This keeps the scrollbar always present in all pages, so we avoid clipping while switching between pages
|
||||
// that need the scrollbar and pages that don't.
|
||||
document.body.classList.add('force-scroll');
|
||||
}
|
||||
|
||||
document.addEventListener('viewshow', onViewBeforeShow);
|
||||
|
||||
function playSound(path, volume) {
|
||||
lastSound = new Date().getTime();
|
||||
require(['howler'], function (howler) {
|
||||
/* globals Howl */
|
||||
try {
|
||||
var sound = new Howl({
|
||||
src: [path],
|
||||
volume: volume || 0.1
|
||||
});
|
||||
sound.play();
|
||||
currentSound = sound;
|
||||
} catch (err) {
|
||||
console.error('error playing sound: ' + err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return skinManager;
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +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) {
|
||||
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
|
||||
};
|
||||
@ -53,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>';
|
||||
@ -80,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;
|
||||
@ -92,7 +95,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
|
||||
}
|
||||
|
||||
if (submitted) {
|
||||
saveValues(dlg, options.settings, options.settingsKey);
|
||||
saveValues(dlg, options.settingsKey);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
@ -101,7 +104,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return SortMenu;
|
||||
});
|
||||
export default SortMenu;
|
||||
|
@ -1,427 +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) {
|
||||
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
|
||||
};
|
||||
|
@ -3,52 +3,29 @@
|
||||
* @module components/subtitleSettings/subtitleAppearanceHelper
|
||||
*/
|
||||
|
||||
function getTextStyles(settings, isCue) {
|
||||
function getTextStyles(settings, preview) {
|
||||
const list = [];
|
||||
|
||||
if (isCue) {
|
||||
switch (settings.textSize || '') {
|
||||
case 'smaller':
|
||||
list.push({ name: 'font-size', value: '.5em' });
|
||||
break;
|
||||
case 'small':
|
||||
list.push({ name: 'font-size', value: '.7em' });
|
||||
break;
|
||||
case 'large':
|
||||
list.push({ name: 'font-size', value: '1.3em' });
|
||||
break;
|
||||
case 'larger':
|
||||
list.push({ name: 'font-size', value: '1.72em' });
|
||||
break;
|
||||
case 'extralarge':
|
||||
list.push({ name: 'font-size', value: '2em' });
|
||||
break;
|
||||
default:
|
||||
case 'medium':
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (settings.textSize || '') {
|
||||
case 'smaller':
|
||||
list.push({ name: 'font-size', value: '.8em' });
|
||||
break;
|
||||
case 'small':
|
||||
list.push({ name: 'font-size', value: 'inherit' });
|
||||
break;
|
||||
case 'larger':
|
||||
list.push({ name: 'font-size', value: '2em' });
|
||||
break;
|
||||
case 'extralarge':
|
||||
list.push({ name: 'font-size', value: '2.2em' });
|
||||
break;
|
||||
case 'large':
|
||||
list.push({ name: 'font-size', value: '1.72em' });
|
||||
break;
|
||||
default:
|
||||
case 'medium':
|
||||
list.push({ name: 'font-size', value: '1.36em' });
|
||||
break;
|
||||
}
|
||||
switch (settings.textSize || '') {
|
||||
case 'smaller':
|
||||
list.push({ name: 'font-size', value: '.8em' });
|
||||
break;
|
||||
case 'small':
|
||||
list.push({ name: 'font-size', value: 'inherit' });
|
||||
break;
|
||||
case 'larger':
|
||||
list.push({ name: 'font-size', value: '2em' });
|
||||
break;
|
||||
case 'extralarge':
|
||||
list.push({ name: 'font-size', value: '2.2em' });
|
||||
break;
|
||||
case 'large':
|
||||
list.push({ name: 'font-size', value: '1.72em' });
|
||||
break;
|
||||
default:
|
||||
case 'medium':
|
||||
list.push({ name: 'font-size', value: '1.36em' });
|
||||
break;
|
||||
}
|
||||
|
||||
switch (settings.dropShadow || '') {
|
||||
@ -111,13 +88,43 @@ function getTextStyles(settings, isCue) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preview) {
|
||||
const pos = parseInt(settings.verticalPosition, 10);
|
||||
const lineHeight = 1.35; // FIXME: It is better to read this value from element
|
||||
const line = Math.abs(pos * lineHeight);
|
||||
if (pos < 0) {
|
||||
list.push({ name: 'min-height', value: `${line}em` });
|
||||
list.push({ name: 'margin-top', value: '' });
|
||||
} else {
|
||||
list.push({ name: 'min-height', value: '' });
|
||||
list.push({ name: 'margin-top', value: `${line}em` });
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
export function getStyles(settings, isCue) {
|
||||
function getWindowStyles(settings, preview) {
|
||||
const list = [];
|
||||
|
||||
if (!preview) {
|
||||
const pos = parseInt(settings.verticalPosition, 10);
|
||||
if (pos < 0) {
|
||||
list.push({ name: 'top', value: '' });
|
||||
list.push({ name: 'bottom', value: '0' });
|
||||
} else {
|
||||
list.push({ name: 'top', value: '0' });
|
||||
list.push({ name: 'bottom', value: '' });
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
export function getStyles(settings, preview) {
|
||||
return {
|
||||
text: getTextStyles(settings, isCue),
|
||||
window: []
|
||||
text: getTextStyles(settings, preview),
|
||||
window: getWindowStyles(settings, preview)
|
||||
};
|
||||
}
|
||||
|
||||
@ -130,7 +137,7 @@ function applyStyleList(styles, elem) {
|
||||
}
|
||||
|
||||
export function applyStyles(elements, appearanceSettings) {
|
||||
const styles = getStyles(appearanceSettings);
|
||||
const styles = getStyles(appearanceSettings, !!elements.preview);
|
||||
|
||||
if (elements.text) {
|
||||
applyStyleList(styles.text, elements.text);
|
||||
|
26
src/components/subtitlesettings/subtitlesettings.css
Normal file
26
src/components/subtitlesettings/subtitlesettings.css
Normal file
@ -0,0 +1,26 @@
|
||||
.subtitleappearance-fullpreview {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.subtitleappearance-fullpreview-hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.subtitleappearance-fullpreview-window {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
font-size: 170%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subtitleappearance-fullpreview-text {
|
||||
display: inline-block;
|
||||
max-width: 70%;
|
||||
}
|
@ -2,6 +2,7 @@ import globalize from 'globalize';
|
||||
import appHost from 'apphost';
|
||||
import appSettings from 'appSettings';
|
||||
import focusManager from 'focusManager';
|
||||
import layoutManager from 'layoutManager';
|
||||
import loading from 'loading';
|
||||
import connectionManager from 'connectionManager';
|
||||
import subtitleAppearanceHelper from 'subtitleAppearanceHelper';
|
||||
@ -10,9 +11,11 @@ import dom from 'dom';
|
||||
import events from 'events';
|
||||
import 'listViewStyle';
|
||||
import 'emby-select';
|
||||
import 'emby-slider';
|
||||
import 'emby-input';
|
||||
import 'emby-checkbox';
|
||||
import 'flexStyles';
|
||||
import 'css!./subtitlesettings';
|
||||
|
||||
/**
|
||||
* Subtitle settings.
|
||||
@ -27,6 +30,7 @@ function getSubtitleAppearanceObject(context) {
|
||||
appearanceSettings.font = context.querySelector('#selectFont').value;
|
||||
appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value;
|
||||
appearanceSettings.textColor = context.querySelector('#inputTextColor').value;
|
||||
appearanceSettings.verticalPosition = context.querySelector('#sliderVerticalPosition').value;
|
||||
|
||||
return appearanceSettings;
|
||||
}
|
||||
@ -51,6 +55,7 @@ function loadForm(context, user, userSettings, appearanceSettings, apiClient) {
|
||||
context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent';
|
||||
context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff';
|
||||
context.querySelector('#selectFont').value = appearanceSettings.font || '';
|
||||
context.querySelector('#sliderVerticalPosition').value = appearanceSettings.verticalPosition;
|
||||
|
||||
context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || '';
|
||||
|
||||
@ -112,10 +117,45 @@ function onAppearanceFieldChange(e) {
|
||||
|
||||
const elements = {
|
||||
window: view.querySelector('.subtitleappearance-preview-window'),
|
||||
text: view.querySelector('.subtitleappearance-preview-text')
|
||||
text: view.querySelector('.subtitleappearance-preview-text'),
|
||||
preview: true
|
||||
};
|
||||
|
||||
subtitleAppearanceHelper.applyStyles(elements, appearanceSettings);
|
||||
|
||||
subtitleAppearanceHelper.applyStyles({
|
||||
window: view.querySelector('.subtitleappearance-fullpreview-window'),
|
||||
text: view.querySelector('.subtitleappearance-fullpreview-text')
|
||||
}, appearanceSettings);
|
||||
}
|
||||
|
||||
const subtitlePreviewDelay = 1000;
|
||||
let subtitlePreviewTimer;
|
||||
|
||||
function showSubtitlePreview(persistent) {
|
||||
clearTimeout(subtitlePreviewTimer);
|
||||
|
||||
this._fullPreview.classList.remove('subtitleappearance-fullpreview-hide');
|
||||
|
||||
if (persistent) {
|
||||
this._refFullPreview++;
|
||||
}
|
||||
|
||||
if (this._refFullPreview === 0) {
|
||||
subtitlePreviewTimer = setTimeout(hideSubtitlePreview.bind(this), subtitlePreviewDelay);
|
||||
}
|
||||
}
|
||||
|
||||
function hideSubtitlePreview(persistent) {
|
||||
clearTimeout(subtitlePreviewTimer);
|
||||
|
||||
if (persistent) {
|
||||
this._refFullPreview--;
|
||||
}
|
||||
|
||||
if (this._refFullPreview === 0) {
|
||||
this._fullPreview.classList.add('subtitleappearance-fullpreview-hide');
|
||||
}
|
||||
}
|
||||
|
||||
function embed(options, self) {
|
||||
@ -138,6 +178,36 @@ function embed(options, self) {
|
||||
|
||||
if (appHost.supports('subtitleappearancesettings')) {
|
||||
options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide');
|
||||
|
||||
self._fullPreview = options.element.querySelector('.subtitleappearance-fullpreview');
|
||||
self._refFullPreview = 0;
|
||||
|
||||
const sliderVerticalPosition = options.element.querySelector('#sliderVerticalPosition');
|
||||
sliderVerticalPosition.addEventListener('input', onAppearanceFieldChange);
|
||||
sliderVerticalPosition.addEventListener('input', () => showSubtitlePreview.call(self));
|
||||
|
||||
const eventPrefix = window.PointerEvent ? 'pointer' : 'mouse';
|
||||
sliderVerticalPosition.addEventListener(`${eventPrefix}enter`, () => showSubtitlePreview.call(self, true));
|
||||
sliderVerticalPosition.addEventListener(`${eventPrefix}leave`, () => hideSubtitlePreview.call(self, true));
|
||||
|
||||
if (layoutManager.tv) {
|
||||
sliderVerticalPosition.addEventListener('focus', () => showSubtitlePreview.call(self, true));
|
||||
sliderVerticalPosition.addEventListener('blur', () => hideSubtitlePreview.call(self, true));
|
||||
|
||||
// Give CustomElements time to attach
|
||||
setTimeout(() => {
|
||||
sliderVerticalPosition.classList.add('focusable');
|
||||
sliderVerticalPosition.enableKeyboardDragging();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
options.element.querySelector('.chkPreview').addEventListener('change', (e) => {
|
||||
if (e.target.checked) {
|
||||
showSubtitlePreview.call(self, true);
|
||||
} else {
|
||||
hideSubtitlePreview.call(self, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.loadData();
|
||||
|
@ -38,6 +38,16 @@
|
||||
${HeaderSubtitleAppearance}
|
||||
</h2>
|
||||
|
||||
<div class="subtitleappearance-fullpreview subtitleappearance-fullpreview-hide">
|
||||
<div class="subtitleappearance-fullpreview-window">
|
||||
<div class="subtitleappearance-fullpreview-text">
|
||||
${HeaderSubtitleAppearance}
|
||||
<br>
|
||||
${TheseSettingsAffectSubtitlesOnThisDevice}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin: 2em 0 2em;">
|
||||
<div class="subtitleappearance-preview flex align-items-center justify-content-center" style="margin:2em 0;padding:1.6em;color:black;background:linear-gradient(140deg,#aa5cc3,#00a4dc);">
|
||||
<div class="subtitleappearance-preview-window flex align-items-center justify-content-center" style="width: 90%; padding: .25em;">
|
||||
@ -89,6 +99,20 @@
|
||||
<option value="">${DropShadow}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="sliderContainer-settings">
|
||||
<div class="sliderContainer">
|
||||
<input is="emby-slider" id="sliderVerticalPosition" label="${LabelSubtitleVerticalPosition}" type="range" min="-16" max="16" />
|
||||
</div>
|
||||
<div class="fieldDescription">${SubtitleVerticalPositionHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" class="chkPreview" />
|
||||
<span>${Preview}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
|
||||
|
@ -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,6 +1,8 @@
|
||||
define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (backdrop, mainTabsManager, layoutManager) {
|
||||
'use strict';
|
||||
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
|
||||
function onViewDestroy(e) {
|
||||
var tabControllers = this.tabControllers;
|
||||
|
||||
|
@ -3,7 +3,9 @@ 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;
|
||||
|
||||
var enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
|
@ -1,136 +1,134 @@
|
||||
define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], function (viewContainer, focusManager, queryString, layoutManager) {
|
||||
'use strict';
|
||||
import viewContainer from 'viewContainer';
|
||||
import focusManager from 'focusManager';
|
||||
import queryString from 'queryString';
|
||||
import layoutManager from 'layoutManager';
|
||||
|
||||
focusManager = focusManager.default || focusManager;
|
||||
let currentView;
|
||||
let dispatchPageEvents;
|
||||
|
||||
var currentView;
|
||||
var dispatchPageEvents;
|
||||
viewContainer.setOnBeforeChange(function (newView, isRestored, options) {
|
||||
const lastView = currentView;
|
||||
if (lastView) {
|
||||
const beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true);
|
||||
|
||||
viewContainer.setOnBeforeChange(function (newView, isRestored, options) {
|
||||
var lastView = currentView;
|
||||
if (lastView) {
|
||||
var beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true);
|
||||
|
||||
if (!beforeHideResult) {
|
||||
// todo: cancel
|
||||
}
|
||||
}
|
||||
|
||||
var eventDetail = getViewEventDetail(newView, options, isRestored);
|
||||
|
||||
if (!newView.initComplete) {
|
||||
newView.initComplete = true;
|
||||
|
||||
if (typeof options.controllerFactory === 'function') {
|
||||
new options.controllerFactory(newView, eventDetail.detail.params);
|
||||
} else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') {
|
||||
new options.controllerFactory.default(newView, eventDetail.detail.params);
|
||||
}
|
||||
|
||||
if (!options.controllerFactory || dispatchPageEvents) {
|
||||
dispatchViewEvent(newView, eventDetail, 'viewinit');
|
||||
}
|
||||
}
|
||||
|
||||
dispatchViewEvent(newView, eventDetail, 'viewbeforeshow');
|
||||
});
|
||||
|
||||
function onViewChange(view, options, isRestore) {
|
||||
var lastView = currentView;
|
||||
if (lastView) {
|
||||
dispatchViewEvent(lastView, null, 'viewhide');
|
||||
}
|
||||
|
||||
currentView = view;
|
||||
|
||||
var eventDetail = getViewEventDetail(view, options, isRestore);
|
||||
|
||||
if (!isRestore) {
|
||||
if (options.autoFocus !== false) {
|
||||
focusManager.autoFocus(view);
|
||||
}
|
||||
} else if (!layoutManager.mobile) {
|
||||
if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) {
|
||||
focusManager.focus(view.activeElement);
|
||||
} else {
|
||||
focusManager.autoFocus(view);
|
||||
}
|
||||
}
|
||||
|
||||
view.dispatchEvent(new CustomEvent('viewshow', eventDetail));
|
||||
|
||||
if (dispatchPageEvents) {
|
||||
view.dispatchEvent(new CustomEvent('pageshow', eventDetail));
|
||||
if (!beforeHideResult) {
|
||||
// todo: cancel
|
||||
}
|
||||
}
|
||||
|
||||
function getProperties(view) {
|
||||
var props = view.getAttribute('data-properties');
|
||||
const eventDetail = getViewEventDetail(newView, options, isRestored);
|
||||
|
||||
if (props) {
|
||||
return props.split(',');
|
||||
if (!newView.initComplete) {
|
||||
newView.initComplete = true;
|
||||
|
||||
if (typeof options.controllerFactory === 'function') {
|
||||
new options.controllerFactory(newView, eventDetail.detail.params);
|
||||
} else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') {
|
||||
new options.controllerFactory.default(newView, eventDetail.detail.params);
|
||||
}
|
||||
|
||||
return [];
|
||||
if (!options.controllerFactory || dispatchPageEvents) {
|
||||
dispatchViewEvent(newView, eventDetail, 'viewinit');
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchViewEvent(view, eventInfo, eventName, isCancellable) {
|
||||
if (!eventInfo) {
|
||||
eventInfo = {
|
||||
detail: {
|
||||
type: view.getAttribute('data-type'),
|
||||
properties: getProperties(view)
|
||||
},
|
||||
bubbles: true,
|
||||
cancelable: isCancellable
|
||||
};
|
||||
}
|
||||
dispatchViewEvent(newView, eventDetail, 'viewbeforeshow');
|
||||
});
|
||||
|
||||
eventInfo.cancelable = isCancellable || false;
|
||||
|
||||
var eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo));
|
||||
|
||||
if (dispatchPageEvents) {
|
||||
eventInfo.cancelable = false;
|
||||
view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo));
|
||||
}
|
||||
|
||||
return eventResult;
|
||||
function onViewChange(view, options, isRestore) {
|
||||
const lastView = currentView;
|
||||
if (lastView) {
|
||||
dispatchViewEvent(lastView, null, 'viewhide');
|
||||
}
|
||||
|
||||
function getViewEventDetail(view, options, isRestore) {
|
||||
var url = options.url;
|
||||
var index = url.indexOf('?');
|
||||
var params = index === -1 ? {} : queryString.parse(url.substring(index + 1));
|
||||
currentView = view;
|
||||
|
||||
return {
|
||||
const eventDetail = getViewEventDetail(view, options, isRestore);
|
||||
|
||||
if (!isRestore) {
|
||||
if (options.autoFocus !== false) {
|
||||
focusManager.autoFocus(view);
|
||||
}
|
||||
} else if (!layoutManager.mobile) {
|
||||
if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) {
|
||||
focusManager.focus(view.activeElement);
|
||||
} else {
|
||||
focusManager.autoFocus(view);
|
||||
}
|
||||
}
|
||||
|
||||
view.dispatchEvent(new CustomEvent('viewshow', eventDetail));
|
||||
|
||||
if (dispatchPageEvents) {
|
||||
view.dispatchEvent(new CustomEvent('pageshow', eventDetail));
|
||||
}
|
||||
}
|
||||
|
||||
function getProperties(view) {
|
||||
const props = view.getAttribute('data-properties');
|
||||
|
||||
if (props) {
|
||||
return props.split(',');
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function dispatchViewEvent(view, eventInfo, eventName, isCancellable) {
|
||||
if (!eventInfo) {
|
||||
eventInfo = {
|
||||
detail: {
|
||||
type: view.getAttribute('data-type'),
|
||||
properties: getProperties(view),
|
||||
params: params,
|
||||
isRestored: isRestore,
|
||||
state: options.state,
|
||||
|
||||
// The route options
|
||||
options: options.options || {}
|
||||
properties: getProperties(view)
|
||||
},
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
cancelable: isCancellable
|
||||
};
|
||||
}
|
||||
|
||||
function resetCachedViews() {
|
||||
// Reset all cached views whenever the skin changes
|
||||
viewContainer.reset();
|
||||
eventInfo.cancelable = isCancellable || false;
|
||||
|
||||
const eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo));
|
||||
|
||||
if (dispatchPageEvents) {
|
||||
eventInfo.cancelable = false;
|
||||
view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo));
|
||||
}
|
||||
|
||||
document.addEventListener('skinunload', resetCachedViews);
|
||||
return eventResult;
|
||||
}
|
||||
|
||||
function ViewManager() {
|
||||
}
|
||||
function getViewEventDetail(view, options, isRestore) {
|
||||
const url = options.url;
|
||||
const index = url.indexOf('?');
|
||||
const params = index === -1 ? {} : queryString.parse(url.substring(index + 1));
|
||||
|
||||
ViewManager.prototype.loadView = function (options) {
|
||||
var lastView = currentView;
|
||||
return {
|
||||
detail: {
|
||||
type: view.getAttribute('data-type'),
|
||||
properties: getProperties(view),
|
||||
params: params,
|
||||
isRestored: isRestore,
|
||||
state: options.state,
|
||||
|
||||
// The route options
|
||||
options: options.options || {}
|
||||
},
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
};
|
||||
}
|
||||
|
||||
function resetCachedViews() {
|
||||
// Reset all cached views whenever the skin changes
|
||||
viewContainer.reset();
|
||||
}
|
||||
|
||||
document.addEventListener('skinunload', resetCachedViews);
|
||||
|
||||
class ViewManager {
|
||||
loadView(options) {
|
||||
const lastView = currentView;
|
||||
|
||||
// Record the element that has focus
|
||||
if (lastView) {
|
||||
@ -144,9 +142,9 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
|
||||
viewContainer.loadView(options).then(function (view) {
|
||||
onViewChange(view, options);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
ViewManager.prototype.tryRestoreView = function (options, onViewChanging) {
|
||||
tryRestoreView(options, onViewChanging) {
|
||||
if (options.cancel) {
|
||||
return Promise.reject({ cancelled: true });
|
||||
}
|
||||
@ -160,15 +158,15 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi
|
||||
onViewChanging();
|
||||
onViewChange(view, options, true);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
ViewManager.prototype.currentView = function () {
|
||||
currentView() {
|
||||
return currentView;
|
||||
};
|
||||
}
|
||||
|
||||
ViewManager.prototype.dispatchPageEvents = function (value) {
|
||||
dispatchPageEvents(value) {
|
||||
dispatchPageEvents = value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new ViewManager();
|
||||
});
|
||||
export default new ViewManager();
|
||||
|
@ -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;
|
||||
@ -29,6 +31,7 @@ define(['require', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'conne
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
scrollHelper = scrollHelper.default || scrollHelper;
|
||||
var fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
|
@ -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++) {
|
||||
const 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,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', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, 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;
|
||||
@ -169,9 +170,6 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
|
||||
name: globalize.translate('HeaderSchedule')
|
||||
}, {
|
||||
name: globalize.translate('TabSeries')
|
||||
}, {
|
||||
name: globalize.translate('ButtonSearch'),
|
||||
cssClass: 'searchTabButton'
|
||||
}];
|
||||
}
|
||||
|
||||
@ -255,9 +253,6 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize',
|
||||
case 5:
|
||||
depends.push('controllers/livetv/livetvseriestimers');
|
||||
break;
|
||||
|
||||
case 6:
|
||||
depends.push('scripts/searchtab');
|
||||
}
|
||||
|
||||
require(depends, function (controllerFactory) {
|
||||
|
@ -1,13 +1,18 @@
|
||||
define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import libraryBrowser from 'libraryBrowser';
|
||||
import imageLoader from 'imageLoader';
|
||||
import listView from 'listView';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import * as userSettings from 'userSettings';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-itemscontainer';
|
||||
|
||||
loading = loading.default || loading;
|
||||
libraryBrowser = libraryBrowser.default || libraryBrowser;
|
||||
/* eslint-disable indent */
|
||||
|
||||
return function (view, params, tabContent) {
|
||||
export default function (view, params, tabContent) {
|
||||
function getPageData(context) {
|
||||
var key = getSavedQueryKey(context);
|
||||
var pageData = data[key];
|
||||
const key = getSavedQueryKey(context);
|
||||
let pageData = data[key];
|
||||
|
||||
if (!pageData) {
|
||||
pageData = data[key] = {
|
||||
@ -47,9 +52,9 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
return context.savedQueryKey;
|
||||
}
|
||||
|
||||
function onViewStyleChange() {
|
||||
var viewStyle = self.getCurrentViewStyle();
|
||||
var itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
const onViewStyleChange = () => {
|
||||
const viewStyle = this.getCurrentViewStyle();
|
||||
const itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
|
||||
if (viewStyle == 'List') {
|
||||
itemsContainer.classList.add('vertical-list');
|
||||
@ -60,13 +65,13 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
}
|
||||
|
||||
itemsContainer.innerHTML = '';
|
||||
}
|
||||
};
|
||||
|
||||
function reloadItems(page) {
|
||||
const reloadItems = (page) => {
|
||||
loading.show();
|
||||
isLoading = true;
|
||||
var query = getQuery(page);
|
||||
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) {
|
||||
const query = getQuery(page);
|
||||
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => {
|
||||
function onNextPageClick() {
|
||||
if (isLoading) {
|
||||
return;
|
||||
@ -90,8 +95,8 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
}
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
var html;
|
||||
var pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
let html;
|
||||
const pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
startIndex: query.StartIndex,
|
||||
limit: query.Limit,
|
||||
totalRecordCount: result.TotalRecordCount,
|
||||
@ -101,7 +106,7 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
sortButton: false,
|
||||
filterButton: false
|
||||
});
|
||||
var viewStyle = self.getCurrentViewStyle();
|
||||
const viewStyle = this.getCurrentViewStyle();
|
||||
if (viewStyle == 'Thumb') {
|
||||
html = cardBuilder.getCardsHtml({
|
||||
items: result.Items,
|
||||
@ -155,22 +160,21 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
showTitle: true
|
||||
});
|
||||
}
|
||||
var i;
|
||||
var length;
|
||||
var elems = tabContent.querySelectorAll('.paging');
|
||||
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].innerHTML = pagingHtml;
|
||||
let elems = tabContent.querySelectorAll('.paging');
|
||||
|
||||
for (const elem of elems) {
|
||||
elem.innerHTML = pagingHtml;
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnNextPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onNextPageClick);
|
||||
for (const elem of elems) {
|
||||
elem.addEventListener('click', onNextPageClick);
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnPreviousPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onPreviousPageClick);
|
||||
for (const elem of elems) {
|
||||
elem.addEventListener('click', onPreviousPageClick);
|
||||
}
|
||||
|
||||
if (!result.Items.length) {
|
||||
@ -182,28 +186,27 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
var itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
const itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
itemsContainer.innerHTML = html;
|
||||
imageLoader.lazyChildren(itemsContainer);
|
||||
libraryBrowser.saveQueryValues(getSavedQueryKey(page), query);
|
||||
loading.hide();
|
||||
isLoading = false;
|
||||
|
||||
require(['autoFocuser'], function (autoFocuser) {
|
||||
import('autoFocuser').then(({default: autoFocuser}) => {
|
||||
autoFocuser.autoFocus(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var data = {};
|
||||
var isLoading = false;
|
||||
const data = {};
|
||||
let isLoading = false;
|
||||
|
||||
self.getCurrentViewStyle = function () {
|
||||
this.getCurrentViewStyle = function () {
|
||||
return getPageData(tabContent).view;
|
||||
};
|
||||
|
||||
function initPage(tabContent) {
|
||||
const initPage = (tabContent) => {
|
||||
tabContent.querySelector('.btnSort').addEventListener('click', function (e) {
|
||||
libraryBrowser.showSortMenu({
|
||||
items: [{
|
||||
@ -230,36 +233,37 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB
|
||||
button: e.target
|
||||
});
|
||||
});
|
||||
var btnSelectView = tabContent.querySelector('.btnSelectView');
|
||||
const btnSelectView = tabContent.querySelector('.btnSelectView');
|
||||
btnSelectView.addEventListener('click', function (e) {
|
||||
libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
|
||||
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
|
||||
});
|
||||
btnSelectView.addEventListener('layoutchange', function (e) {
|
||||
var viewStyle = e.detail.viewStyle;
|
||||
const viewStyle = e.detail.viewStyle;
|
||||
getPageData(tabContent).view = viewStyle;
|
||||
libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle);
|
||||
getQuery(tabContent).StartIndex = 0;
|
||||
onViewStyleChange();
|
||||
reloadItems(tabContent);
|
||||
});
|
||||
tabContent.querySelector('.btnNewCollection').addEventListener('click', function () {
|
||||
require(['collectionEditor'], function (collectionEditor) {
|
||||
var serverId = ApiClient.serverInfo().Id;
|
||||
tabContent.querySelector('.btnNewCollection').addEventListener('click', () => {
|
||||
import('collectionEditor').then(({default: collectionEditor}) => {
|
||||
const serverId = ApiClient.serverInfo().Id;
|
||||
new collectionEditor.showEditor({
|
||||
items: [],
|
||||
serverId: serverId
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
initPage(tabContent);
|
||||
onViewStyleChange();
|
||||
|
||||
self.renderTab = function () {
|
||||
this.renderTab = function () {
|
||||
reloadItems(tabContent);
|
||||
};
|
||||
|
||||
self.destroy = function () {};
|
||||
};
|
||||
});
|
||||
this.destroy = function () {};
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
@ -1,13 +1,18 @@
|
||||
define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost', 'globalize', 'appRouter', 'dom', 'emby-button'], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) {
|
||||
'use strict';
|
||||
import layoutManager from 'layoutManager';
|
||||
import loading from 'loading';
|
||||
import libraryBrowser from 'libraryBrowser';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import lazyLoader from 'lazyLoader';
|
||||
import globalize from 'globalize';
|
||||
import appRouter from 'appRouter';
|
||||
import 'emby-button';
|
||||
|
||||
loading = loading.default || loading;
|
||||
libraryBrowser = libraryBrowser.default || libraryBrowser;
|
||||
/* eslint-disable indent */
|
||||
|
||||
return function (view, params, tabContent) {
|
||||
export default function (view, params, tabContent) {
|
||||
function getPageData() {
|
||||
var key = getSavedQueryKey();
|
||||
var pageData = data[key];
|
||||
const key = getSavedQueryKey();
|
||||
let pageData = data[key];
|
||||
|
||||
if (!pageData) {
|
||||
pageData = data[key] = {
|
||||
@ -37,7 +42,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
|
||||
|
||||
function getPromise() {
|
||||
loading.show();
|
||||
var query = getQuery();
|
||||
const query = getQuery();
|
||||
return ApiClient.getGenres(ApiClient.getCurrentUserId(), query);
|
||||
}
|
||||
|
||||
@ -53,18 +58,18 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
|
||||
return enableScrollX() ? 'overflowPortrait' : 'portrait';
|
||||
}
|
||||
|
||||
function fillItemsContainer(entry) {
|
||||
var elem = entry.target;
|
||||
var id = elem.getAttribute('data-id');
|
||||
var viewStyle = self.getCurrentViewStyle();
|
||||
var limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9;
|
||||
const fillItemsContainer = (entry) => {
|
||||
const elem = entry.target;
|
||||
const id = elem.getAttribute('data-id');
|
||||
const viewStyle = this.getCurrentViewStyle();
|
||||
let limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9;
|
||||
|
||||
if (enableScrollX()) {
|
||||
limit = 10;
|
||||
}
|
||||
|
||||
var enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary';
|
||||
var query = {
|
||||
const enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary';
|
||||
const query = {
|
||||
SortBy: 'SortName',
|
||||
SortOrder: 'Ascending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
@ -126,17 +131,17 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
|
||||
tabContent.querySelector('.btnMoreFromGenre' + id + ' .material-icons').classList.remove('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function reloadItems(context, promise) {
|
||||
var query = getQuery();
|
||||
const query = getQuery();
|
||||
promise.then(function (result) {
|
||||
var elem = context.querySelector('#items');
|
||||
var html = '';
|
||||
var items = result.Items;
|
||||
const elem = context.querySelector('#items');
|
||||
let html = '';
|
||||
const items = result.Items;
|
||||
|
||||
for (var i = 0, length = items.length; i < length; i++) {
|
||||
var item = items[i];
|
||||
for (let i = 0, length = items.length; i < length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<div class="sectionTitleContainer sectionTitleContainer-cards padded-left">';
|
||||
@ -151,7 +156,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
if (enableScrollX()) {
|
||||
var scrollXClass = 'scrollX hiddenScrollX';
|
||||
let scrollXClass = 'scrollX hiddenScrollX';
|
||||
|
||||
if (layoutManager.tv) {
|
||||
scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale';
|
||||
@ -182,37 +187,37 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader
|
||||
});
|
||||
}
|
||||
|
||||
function fullyReload() {
|
||||
self.preRender();
|
||||
self.renderTab();
|
||||
}
|
||||
const fullyReload = () => {
|
||||
this.preRender();
|
||||
this.renderTab();
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var data = {};
|
||||
const data = {};
|
||||
|
||||
self.getViewStyles = function () {
|
||||
this.getViewStyles = function () {
|
||||
return 'Poster,PosterCard,Thumb,ThumbCard'.split(',');
|
||||
};
|
||||
|
||||
self.getCurrentViewStyle = function () {
|
||||
this.getCurrentViewStyle = function () {
|
||||
return getPageData().view;
|
||||
};
|
||||
|
||||
self.setCurrentViewStyle = function (viewStyle) {
|
||||
this.setCurrentViewStyle = function (viewStyle) {
|
||||
getPageData().view = viewStyle;
|
||||
libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle);
|
||||
fullyReload();
|
||||
};
|
||||
|
||||
self.enableViewSelection = true;
|
||||
var promise;
|
||||
this.enableViewSelection = true;
|
||||
let promise;
|
||||
|
||||
self.preRender = function () {
|
||||
this.preRender = function () {
|
||||
promise = getPromise();
|
||||
};
|
||||
|
||||
self.renderTab = function () {
|
||||
this.renderTab = function () {
|
||||
reloadItems(tabContent, promise);
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
@ -1,12 +1,18 @@
|
||||
define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, AlphaPicker, listView, cardBuilder, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import * as userSettings from 'userSettings';
|
||||
import events from 'events';
|
||||
import libraryBrowser from 'libraryBrowser';
|
||||
import AlphaPicker from 'alphaPicker';
|
||||
import listView from 'listView';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-itemscontainer';
|
||||
|
||||
loading = loading.default || loading;
|
||||
libraryBrowser = libraryBrowser.default || libraryBrowser;
|
||||
/* eslint-disable indent */
|
||||
|
||||
return function (view, params, tabContent, options) {
|
||||
function onViewStyleChange() {
|
||||
if (self.getCurrentViewStyle() == 'List') {
|
||||
export default function (view, params, tabContent, options) {
|
||||
const onViewStyleChange = () => {
|
||||
if (this.getCurrentViewStyle() == 'List') {
|
||||
itemsContainer.classList.add('vertical-list');
|
||||
itemsContainer.classList.remove('vertical-wrap');
|
||||
} else {
|
||||
@ -15,13 +21,13 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
}
|
||||
|
||||
itemsContainer.innerHTML = '';
|
||||
}
|
||||
};
|
||||
|
||||
function updateFilterControls() {
|
||||
if (self.alphaPicker) {
|
||||
self.alphaPicker.value(query.NameStartsWithOrGreater);
|
||||
const updateFilterControls = () => {
|
||||
if (this.alphaPicker) {
|
||||
this.alphaPicker.value(query.NameStartsWithOrGreater);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function fetchData() {
|
||||
isLoading = true;
|
||||
@ -54,7 +60,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
updateFilterControls();
|
||||
var pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
const pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
startIndex: query.StartIndex,
|
||||
limit: query.Limit,
|
||||
totalRecordCount: result.TotalRecordCount,
|
||||
@ -64,35 +70,30 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
sortButton: false,
|
||||
filterButton: false
|
||||
});
|
||||
var i;
|
||||
var length;
|
||||
var elems = tabContent.querySelectorAll('.paging');
|
||||
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].innerHTML = pagingHtml;
|
||||
for (const elem of tabContent.querySelectorAll('.paging')) {
|
||||
elem.innerHTML = pagingHtml;
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnNextPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onNextPageClick);
|
||||
for (const elem of tabContent.querySelectorAll('.btnNextPage')) {
|
||||
elem.addEventListener('click', onNextPageClick);
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnPreviousPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onPreviousPageClick);
|
||||
for (const elem of tabContent.querySelectorAll('.btnPreviousPage')) {
|
||||
elem.addEventListener('click', onPreviousPageClick);
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
loading.hide();
|
||||
|
||||
require(['autoFocuser'], function (autoFocuser) {
|
||||
import('autoFocuser').then(({default: autoFocuser}) => {
|
||||
autoFocuser.autoFocus(tabContent);
|
||||
});
|
||||
}
|
||||
|
||||
function getItemsHtml(items) {
|
||||
var html;
|
||||
var viewStyle = self.getCurrentViewStyle();
|
||||
const getItemsHtml = (items) => {
|
||||
let html;
|
||||
const viewStyle = this.getCurrentViewStyle();
|
||||
|
||||
if (viewStyle == 'Thumb') {
|
||||
html = cardBuilder.getCardsHtml({
|
||||
@ -156,22 +157,22 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
};
|
||||
|
||||
function initPage(tabContent) {
|
||||
const initPage = (tabContent) => {
|
||||
itemsContainer.fetchData = fetchData;
|
||||
itemsContainer.getItemsHtml = getItemsHtml;
|
||||
itemsContainer.afterRefresh = afterRefresh;
|
||||
var alphaPickerElement = tabContent.querySelector('.alphaPicker');
|
||||
let alphaPickerElement = tabContent.querySelector('.alphaPicker');
|
||||
|
||||
if (alphaPickerElement) {
|
||||
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
|
||||
var newValue = e.detail.value;
|
||||
let newValue = e.detail.value;
|
||||
query.NameStartsWithOrGreater = newValue;
|
||||
query.StartIndex = 0;
|
||||
itemsContainer.refreshItems();
|
||||
});
|
||||
self.alphaPicker = new AlphaPicker.default({
|
||||
this.alphaPicker = new AlphaPicker({
|
||||
element: alphaPickerElement,
|
||||
valueChangeEvent: 'click'
|
||||
});
|
||||
@ -181,14 +182,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
itemsContainer.classList.add('padded-right-withalphapicker');
|
||||
}
|
||||
|
||||
var btnFilter = tabContent.querySelector('.btnFilter');
|
||||
const btnFilter = tabContent.querySelector('.btnFilter');
|
||||
|
||||
if (btnFilter) {
|
||||
btnFilter.addEventListener('click', function () {
|
||||
self.showFilterMenu();
|
||||
btnFilter.addEventListener('click', () => {
|
||||
this.showFilterMenu();
|
||||
});
|
||||
}
|
||||
var btnSort = tabContent.querySelector('.btnSort');
|
||||
const btnSort = tabContent.querySelector('.btnSort');
|
||||
|
||||
if (btnSort) {
|
||||
btnSort.addEventListener('click', function (e) {
|
||||
@ -231,24 +232,23 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
});
|
||||
});
|
||||
}
|
||||
var btnSelectView = tabContent.querySelector('.btnSelectView');
|
||||
const btnSelectView = tabContent.querySelector('.btnSelectView');
|
||||
btnSelectView.addEventListener('click', function (e) {
|
||||
libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
|
||||
libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle, 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(','));
|
||||
});
|
||||
btnSelectView.addEventListener('layoutchange', function (e) {
|
||||
var viewStyle = e.detail.viewStyle;
|
||||
let viewStyle = e.detail.viewStyle;
|
||||
userSettings.set(savedViewKey, viewStyle);
|
||||
query.StartIndex = 0;
|
||||
onViewStyleChange();
|
||||
itemsContainer.refreshItems();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
var savedQueryKey = params.topParentId + '-' + options.mode;
|
||||
var savedViewKey = savedQueryKey + '-view';
|
||||
var query = {
|
||||
let itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
const savedQueryKey = params.topParentId + '-' + options.mode;
|
||||
const savedViewKey = savedQueryKey + '-view';
|
||||
let query = {
|
||||
SortBy: 'SortName,ProductionYear',
|
||||
SortOrder: 'Ascending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
@ -264,7 +264,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
query['Limit'] = userSettings.libraryPageSize();
|
||||
}
|
||||
|
||||
var isLoading = false;
|
||||
let isLoading = false;
|
||||
|
||||
if (options.mode === 'favorites') {
|
||||
query.IsFavorite = true;
|
||||
@ -272,14 +272,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
|
||||
query = userSettings.loadQuerySettings(savedQueryKey, query);
|
||||
|
||||
self.showFilterMenu = function () {
|
||||
require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) {
|
||||
var filterDialog = new filterDialogFactory({
|
||||
this.showFilterMenu = function () {
|
||||
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
|
||||
let filterDialog = new filterDialogFactory({
|
||||
query: query,
|
||||
mode: 'movies',
|
||||
serverId: ApiClient.serverId()
|
||||
});
|
||||
events.on(filterDialog, 'filterchange', function () {
|
||||
events.on(filterDialog, 'filterchange', () => {
|
||||
query.StartIndex = 0;
|
||||
itemsContainer.refreshItems();
|
||||
});
|
||||
@ -287,22 +287,23 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser',
|
||||
});
|
||||
};
|
||||
|
||||
self.getCurrentViewStyle = function () {
|
||||
this.getCurrentViewStyle = function () {
|
||||
return userSettings.get(savedViewKey) || 'Poster';
|
||||
};
|
||||
|
||||
self.initTab = function () {
|
||||
this.initTab = function () {
|
||||
initPage(tabContent);
|
||||
onViewStyleChange();
|
||||
};
|
||||
|
||||
self.renderTab = function () {
|
||||
this.renderTab = function () {
|
||||
itemsContainer.refreshItems();
|
||||
updateFilterControls();
|
||||
};
|
||||
|
||||
self.destroy = function () {
|
||||
this.destroy = function () {
|
||||
itemsContainer = null;
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
@ -1,7 +1,20 @@
|
||||
define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu', 'mainTabsManager', 'cardBuilder', 'dom', 'imageLoader', 'playbackManager', 'globalize', 'emby-scroller', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager, globalize) {
|
||||
'use strict';
|
||||
import events from 'events';
|
||||
import layoutManager from 'layoutManager';
|
||||
import inputManager from 'inputManager';
|
||||
import * as userSettings from 'userSettings';
|
||||
import libraryMenu from 'libraryMenu';
|
||||
import * as mainTabsManager from 'mainTabsManager';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import dom from 'dom';
|
||||
import imageLoader from 'imageLoader';
|
||||
import playbackManager from 'playbackManager';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-scroller';
|
||||
import 'emby-itemscontainer';
|
||||
import 'emby-tabs';
|
||||
import 'emby-button';
|
||||
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
/* eslint-disable indent */
|
||||
|
||||
function enableScrollX() {
|
||||
return !layoutManager.desktop;
|
||||
@ -16,7 +29,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function loadLatest(page, userId, parentId) {
|
||||
var options = {
|
||||
const options = {
|
||||
IncludeItemTypes: 'Movie',
|
||||
Limit: 18,
|
||||
Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
|
||||
@ -26,8 +39,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
EnableTotalRecordCount: false
|
||||
};
|
||||
ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) {
|
||||
var allowBottomPadding = !enableScrollX();
|
||||
var container = page.querySelector('#recentlyAddedItems');
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = page.querySelector('#recentlyAddedItems');
|
||||
cardBuilder.buildCards(items, {
|
||||
itemsContainer: container,
|
||||
shape: getPortraitShape(),
|
||||
@ -45,8 +58,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function loadResume(page, userId, parentId) {
|
||||
var screenWidth = dom.getWindowSize().innerWidth;
|
||||
var options = {
|
||||
let screenWidth = dom.getWindowSize().innerWidth;
|
||||
const options = {
|
||||
SortBy: 'DatePlayed',
|
||||
SortOrder: 'Descending',
|
||||
IncludeItemTypes: 'Movie',
|
||||
@ -67,8 +80,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
page.querySelector('#resumableSection').classList.add('hide');
|
||||
}
|
||||
|
||||
var allowBottomPadding = !enableScrollX();
|
||||
var container = page.querySelector('#resumableItems');
|
||||
const allowBottomPadding = !enableScrollX();
|
||||
const container = page.querySelector('#resumableItems');
|
||||
cardBuilder.buildCards(result.Items, {
|
||||
itemsContainer: container,
|
||||
preferThumb: true,
|
||||
@ -88,8 +101,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function getRecommendationHtml(recommendation) {
|
||||
var html = '';
|
||||
var title = '';
|
||||
let html = '';
|
||||
let title = '';
|
||||
|
||||
switch (recommendation.RecommendationType) {
|
||||
case 'SimilarToRecentlyPlayed':
|
||||
@ -113,7 +126,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
|
||||
html += '<div class="verticalSection">';
|
||||
html += '<h2 class="sectionTitle sectionTitle-cards padded-left">' + title + '</h2>';
|
||||
var allowBottomPadding = true;
|
||||
const allowBottomPadding = true;
|
||||
|
||||
if (enableScrollX()) {
|
||||
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-mousewheel="false" data-centerfocus="true">';
|
||||
@ -141,8 +154,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function loadSuggestions(page, userId, parentId) {
|
||||
var screenWidth = dom.getWindowSize().innerWidth;
|
||||
var url = ApiClient.getUrl('Movies/Recommendations', {
|
||||
let screenWidth = dom.getWindowSize().innerWidth;
|
||||
let url = ApiClient.getUrl('Movies/Recommendations', {
|
||||
userId: userId,
|
||||
categoryLimit: 6,
|
||||
ItemLimit: screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 6 : 5,
|
||||
@ -157,9 +170,9 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
return;
|
||||
}
|
||||
|
||||
var html = recommendations.map(getRecommendationHtml).join('');
|
||||
const html = recommendations.map(getRecommendationHtml).join('');
|
||||
page.querySelector('.noItemsMessage').classList.add('hide');
|
||||
var recs = page.querySelector('.recommendations');
|
||||
let recs = page.querySelector('.recommendations');
|
||||
recs.innerHTML = html;
|
||||
imageLoader.lazyChildren(recs);
|
||||
|
||||
@ -169,7 +182,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function autoFocus(page) {
|
||||
require(['autoFocuser'], function (autoFocuser) {
|
||||
import('autoFocuser').then(({default: autoFocuser}) => {
|
||||
autoFocuser.autoFocus(page);
|
||||
});
|
||||
}
|
||||
@ -195,17 +208,16 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
|
||||
function initSuggestedTab(page, tabContent) {
|
||||
var containers = tabContent.querySelectorAll('.itemsContainer');
|
||||
const containers = tabContent.querySelectorAll('.itemsContainer');
|
||||
|
||||
for (var i = 0, length = containers.length; i < length; i++) {
|
||||
setScrollClasses(containers[i], enableScrollX());
|
||||
for (const container of containers) {
|
||||
setScrollClasses(container, enableScrollX());
|
||||
}
|
||||
}
|
||||
|
||||
function loadSuggestionsTab(view, params, tabContent) {
|
||||
var parentId = params.topParentId;
|
||||
var userId = ApiClient.getCurrentUserId();
|
||||
console.debug('loadSuggestionsTab');
|
||||
const parentId = params.topParentId;
|
||||
const userId = ApiClient.getCurrentUserId();
|
||||
loadResume(tabContent, userId, parentId);
|
||||
loadLatest(tabContent, userId, parentId);
|
||||
loadSuggestions(tabContent, userId, parentId);
|
||||
@ -224,9 +236,6 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
name: globalize.translate('TabCollections')
|
||||
}, {
|
||||
name: globalize.translate('TabGenres')
|
||||
}, {
|
||||
name: globalize.translate('ButtonSearch'),
|
||||
cssClass: 'searchTabButton'
|
||||
}];
|
||||
}
|
||||
|
||||
@ -249,13 +258,13 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
}
|
||||
|
||||
return function (view, params) {
|
||||
export default function (view, params) {
|
||||
function onBeforeTabChange(e) {
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
|
||||
}
|
||||
|
||||
function onTabChange(e) {
|
||||
var newIndex = parseInt(e.detail.selectedTabIndex);
|
||||
const newIndex = parseInt(e.detail.selectedTabIndex);
|
||||
loadTab(view, newIndex);
|
||||
}
|
||||
|
||||
@ -267,52 +276,50 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
mainTabsManager.setTabs(view, currentTabIndex, getTabs, getTabContainers, onBeforeTabChange, onTabChange);
|
||||
}
|
||||
|
||||
function getTabController(page, index, callback) {
|
||||
var depends = [];
|
||||
const getTabController = (page, index, callback) => {
|
||||
let depends = '';
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
depends.push('controllers/movies/movies');
|
||||
depends = 'controllers/movies/movies';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
depends = 'controllers/movies/moviesrecommended.js';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
depends.push('controllers/movies/movietrailers');
|
||||
depends = 'controllers/movies/movietrailers';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
depends.push('controllers/movies/movies');
|
||||
depends = 'controllers/movies/movies';
|
||||
break;
|
||||
|
||||
case 4:
|
||||
depends.push('controllers/movies/moviecollections');
|
||||
depends = 'controllers/movies/moviecollections';
|
||||
break;
|
||||
|
||||
case 5:
|
||||
depends.push('controllers/movies/moviegenres');
|
||||
depends = 'controllers/movies/moviegenres';
|
||||
break;
|
||||
|
||||
case 6:
|
||||
depends.push('scripts/searchtab');
|
||||
}
|
||||
|
||||
require(depends, function (controllerFactory) {
|
||||
var tabContent;
|
||||
import(depends).then(({default: controllerFactory}) => {
|
||||
let tabContent;
|
||||
|
||||
if (index === suggestionsTabIndex) {
|
||||
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
|
||||
self.tabContent = tabContent;
|
||||
this.tabContent = tabContent;
|
||||
}
|
||||
|
||||
var controller = tabControllers[index];
|
||||
let controller = tabControllers[index];
|
||||
|
||||
if (!controller) {
|
||||
tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']");
|
||||
|
||||
if (index === suggestionsTabIndex) {
|
||||
controller = self;
|
||||
controller = this;
|
||||
} else if (index === 6) {
|
||||
controller = new controllerFactory(view, tabContent, {
|
||||
collectionType: 'movies',
|
||||
@ -335,7 +342,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
|
||||
callback(controller);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function preLoadTab(page, index) {
|
||||
getTabController(page, index, function (controller) {
|
||||
@ -347,12 +354,12 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
|
||||
function loadTab(page, index) {
|
||||
currentTabIndex = index;
|
||||
getTabController(page, index, function (controller) {
|
||||
getTabController(page, index, ((controller) => {
|
||||
if (renderedTabs.indexOf(index) == -1) {
|
||||
renderedTabs.push(index);
|
||||
controller.renderTab();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function onPlaybackStop(e, state) {
|
||||
@ -370,22 +377,21 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
|
||||
var suggestionsTabIndex = 1;
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
|
||||
const suggestionsTabIndex = 1;
|
||||
|
||||
self.initTab = function () {
|
||||
var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
|
||||
this.initTab = function () {
|
||||
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
|
||||
initSuggestedTab(view, tabContent);
|
||||
};
|
||||
|
||||
self.renderTab = function () {
|
||||
var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
|
||||
this.renderTab = function () {
|
||||
let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
|
||||
loadSuggestionsTab(view, params, tabContent);
|
||||
};
|
||||
|
||||
var tabControllers = [];
|
||||
var renderedTabs = [];
|
||||
let tabControllers = [];
|
||||
let renderedTabs = [];
|
||||
view.addEventListener('viewshow', function (e) {
|
||||
initTabs();
|
||||
if (!view.getAttribute('data-title')) {
|
||||
@ -405,15 +411,14 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu'
|
||||
events.on(playbackManager, 'playbackstop', onPlaybackStop);
|
||||
inputManager.on(window, onInputCommand);
|
||||
});
|
||||
view.addEventListener('viewbeforehide', function (e) {
|
||||
view.addEventListener('viewbeforehide', function () {
|
||||
inputManager.off(window, onInputCommand);
|
||||
});
|
||||
view.addEventListener('viewdestroy', function (e) {
|
||||
tabControllers.forEach(function (t) {
|
||||
if (t.destroy) {
|
||||
t.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
for (const tabController of tabControllers) {
|
||||
if (tabController.destroy) {
|
||||
tabController.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
@ -1,13 +1,20 @@
|
||||
define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, userSettings, globalize) {
|
||||
'use strict';
|
||||
import loading from 'loading';
|
||||
import events from 'events';
|
||||
import libraryBrowser from 'libraryBrowser';
|
||||
import imageLoader from 'imageLoader';
|
||||
import AlphaPicker from 'alphaPicker';
|
||||
import listView from 'listView';
|
||||
import cardBuilder from 'cardBuilder';
|
||||
import * as userSettings from 'userSettings';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-itemscontainer';
|
||||
|
||||
loading = loading.default || loading;
|
||||
libraryBrowser = libraryBrowser.default || libraryBrowser;
|
||||
/* eslint-disable indent */
|
||||
|
||||
return function (view, params, tabContent) {
|
||||
export default function (view, params, tabContent) {
|
||||
function getPageData(context) {
|
||||
var key = getSavedQueryKey(context);
|
||||
var pageData = data[key];
|
||||
const key = getSavedQueryKey(context);
|
||||
let pageData = data[key];
|
||||
|
||||
if (!pageData) {
|
||||
pageData = data[key] = {
|
||||
@ -46,11 +53,11 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
return context.savedQueryKey;
|
||||
}
|
||||
|
||||
function reloadItems() {
|
||||
const reloadItems = () => {
|
||||
loading.show();
|
||||
isLoading = true;
|
||||
var query = getQuery(tabContent);
|
||||
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) {
|
||||
const query = getQuery(tabContent);
|
||||
ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => {
|
||||
function onNextPageClick() {
|
||||
if (isLoading) {
|
||||
return;
|
||||
@ -75,7 +82,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
updateFilterControls(tabContent);
|
||||
var pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
const pagingHtml = libraryBrowser.getQueryPagingHtml({
|
||||
startIndex: query.StartIndex,
|
||||
limit: query.Limit,
|
||||
totalRecordCount: result.TotalRecordCount,
|
||||
@ -85,8 +92,8 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
sortButton: false,
|
||||
filterButton: false
|
||||
});
|
||||
var html;
|
||||
var viewStyle = self.getCurrentViewStyle();
|
||||
let html;
|
||||
const viewStyle = this.getCurrentViewStyle();
|
||||
|
||||
if (viewStyle == 'Thumb') {
|
||||
html = cardBuilder.getCardsHtml({
|
||||
@ -142,22 +149,20 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
});
|
||||
}
|
||||
|
||||
var i;
|
||||
var length;
|
||||
var elems = tabContent.querySelectorAll('.paging');
|
||||
let elems = tabContent.querySelectorAll('.paging');
|
||||
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].innerHTML = pagingHtml;
|
||||
for (const elem of elems) {
|
||||
elem.innerHTML = pagingHtml;
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnNextPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onNextPageClick);
|
||||
for (const elem of elems) {
|
||||
elem.addEventListener('click', onNextPageClick);
|
||||
}
|
||||
|
||||
elems = tabContent.querySelectorAll('.btnPreviousPage');
|
||||
for (i = 0, length = elems.length; i < length; i++) {
|
||||
elems[i].addEventListener('click', onPreviousPageClick);
|
||||
for (const elem of elems) {
|
||||
elem.addEventListener('click', onPreviousPageClick);
|
||||
}
|
||||
|
||||
if (!result.Items.length) {
|
||||
@ -169,27 +174,26 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
var itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
const itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
itemsContainer.innerHTML = html;
|
||||
imageLoader.lazyChildren(itemsContainer);
|
||||
libraryBrowser.saveQueryValues(getSavedQueryKey(tabContent), query);
|
||||
loading.hide();
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function updateFilterControls(tabContent) {
|
||||
var query = getQuery(tabContent);
|
||||
self.alphaPicker.value(query.NameStartsWithOrGreater);
|
||||
}
|
||||
const updateFilterControls = (tabContent) => {
|
||||
const query = getQuery(tabContent);
|
||||
this.alphaPicker.value(query.NameStartsWithOrGreater);
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var data = {};
|
||||
var isLoading = false;
|
||||
const data = {};
|
||||
let isLoading = false;
|
||||
|
||||
self.showFilterMenu = function () {
|
||||
require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) {
|
||||
var filterDialog = new filterDialogFactory({
|
||||
this.showFilterMenu = function () {
|
||||
import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => {
|
||||
const filterDialog = new filterDialogFactory({
|
||||
query: getQuery(tabContent),
|
||||
mode: 'movies',
|
||||
serverId: ApiClient.serverId()
|
||||
@ -202,21 +206,21 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
});
|
||||
};
|
||||
|
||||
self.getCurrentViewStyle = function () {
|
||||
this.getCurrentViewStyle = function () {
|
||||
return getPageData(tabContent).view;
|
||||
};
|
||||
|
||||
function initPage(tabContent) {
|
||||
var alphaPickerElement = tabContent.querySelector('.alphaPicker');
|
||||
var itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
const initPage = (tabContent) => {
|
||||
const alphaPickerElement = tabContent.querySelector('.alphaPicker');
|
||||
const itemsContainer = tabContent.querySelector('.itemsContainer');
|
||||
alphaPickerElement.addEventListener('alphavaluechanged', function (e) {
|
||||
var newValue = e.detail.value;
|
||||
var query = getQuery(tabContent);
|
||||
const newValue = e.detail.value;
|
||||
const query = getQuery(tabContent);
|
||||
query.NameStartsWithOrGreater = newValue;
|
||||
query.StartIndex = 0;
|
||||
reloadItems();
|
||||
});
|
||||
self.alphaPicker = new AlphaPicker.default({
|
||||
this.alphaPicker = new AlphaPicker({
|
||||
element: alphaPickerElement,
|
||||
valueChangeEvent: 'click'
|
||||
});
|
||||
@ -226,7 +230,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
itemsContainer.classList.add('padded-right-withalphapicker');
|
||||
|
||||
tabContent.querySelector('.btnFilter').addEventListener('click', function () {
|
||||
self.showFilterMenu();
|
||||
this.showFilterMenu();
|
||||
});
|
||||
tabContent.querySelector('.btnSort').addEventListener('click', function (e) {
|
||||
libraryBrowser.showSortMenu({
|
||||
@ -260,15 +264,16 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', '
|
||||
button: e.target
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
initPage(tabContent);
|
||||
|
||||
self.renderTab = function () {
|
||||
this.renderTab = function () {
|
||||
reloadItems();
|
||||
updateFilterControls(tabContent);
|
||||
};
|
||||
|
||||
self.destroy = function () {};
|
||||
};
|
||||
});
|
||||
this.destroy = function () {};
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
@ -190,9 +190,6 @@ import 'flexStyles';
|
||||
name: globalize.translate('TabSongs')
|
||||
}, {
|
||||
name: globalize.translate('TabGenres')
|
||||
}, {
|
||||
name: globalize.translate('ButtonSearch'),
|
||||
cssClass: 'searchTabButton'
|
||||
}];
|
||||
}
|
||||
|
||||
@ -294,10 +291,6 @@ import 'flexStyles';
|
||||
case 6:
|
||||
depends = 'controllers/music/musicgenres';
|
||||
break;
|
||||
|
||||
case 7:
|
||||
depends = 'scripts/searchtab';
|
||||
break;
|
||||
}
|
||||
|
||||
import(depends).then(({default: controllerFactory}) => {
|
||||
|
@ -30,9 +30,6 @@ import 'emby-button';
|
||||
name: globalize.translate('TabNetworks')
|
||||
}, {
|
||||
name: globalize.translate('TabEpisodes')
|
||||
}, {
|
||||
name: globalize.translate('ButtonSearch'),
|
||||
cssClass: 'searchTabButton'
|
||||
}];
|
||||
}
|
||||
|
||||
@ -217,10 +214,6 @@ import 'emby-button';
|
||||
case 6:
|
||||
depends = 'controllers/shows/episodes';
|
||||
break;
|
||||
|
||||
case 7:
|
||||
depends = 'scripts/searchtab';
|
||||
break;
|
||||
}
|
||||
|
||||
import(depends).then(({default: controllerFactory}) => {
|
||||
|
@ -230,3 +230,18 @@
|
||||
margin: 0;
|
||||
padding: 0.5em 0.75em;
|
||||
}
|
||||
|
||||
/* FIXME: 'sliderContainer' is used to wrap slider's pieces */
|
||||
.sliderContainer-settings {
|
||||
margin-bottom: 1.8em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sliderContainer-settings .mdl-slider-container {
|
||||
height: 2.83em; /* similar to emby-input with its 110% font-size */
|
||||
}
|
||||
|
||||
.sliderLabel {
|
||||
display: block;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
@ -150,6 +150,16 @@ import 'emby-input';
|
||||
this.classList.add('show-focus');
|
||||
}
|
||||
|
||||
const topContainer = dom.parentWithClass(this, 'sliderContainer-settings');
|
||||
|
||||
if (topContainer && this.getAttribute('label')) {
|
||||
const label = this.ownerDocument.createElement('label');
|
||||
label.innerHTML = this.getAttribute('label');
|
||||
label.classList.add('sliderLabel');
|
||||
label.htmlFor = this.id;
|
||||
topContainer.insertBefore(label, topContainer.firstChild);
|
||||
}
|
||||
|
||||
const containerElement = this.parentNode;
|
||||
containerElement.classList.add('mdl-slider-container');
|
||||
|
||||
|
@ -1,354 +1,357 @@
|
||||
define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser, dom) {
|
||||
"use strict";
|
||||
/* Cleaning this file properly is not neecessary, since it's an outdated library
|
||||
* and will be replaced soon by a Vue component.
|
||||
*/
|
||||
|
||||
browser = browser.default || browser;
|
||||
import browser from 'browser';
|
||||
import dom from 'dom';
|
||||
import 'css!./navdrawer';
|
||||
import 'scrollStyles';
|
||||
|
||||
return function (options) {
|
||||
function getTouches(e) {
|
||||
return e.changedTouches || e.targetTouches || e.touches;
|
||||
export default function (options) {
|
||||
function getTouches(e) {
|
||||
return e.changedTouches || e.targetTouches || e.touches;
|
||||
}
|
||||
|
||||
function onMenuTouchStart(e) {
|
||||
options.target.classList.remove('transition');
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
menuTouchStartX = touch.clientX;
|
||||
menuTouchStartY = touch.clientY;
|
||||
menuTouchStartTime = new Date().getTime();
|
||||
}
|
||||
|
||||
function setVelocity(deltaX) {
|
||||
var time = new Date().getTime() - (menuTouchStartTime || 0);
|
||||
velocity = Math.abs(deltaX) / time;
|
||||
}
|
||||
|
||||
function onMenuTouchMove(e) {
|
||||
var isOpen = self.visible;
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
var endY = touch.clientY || 0;
|
||||
var deltaX = endX - (menuTouchStartX || 0);
|
||||
var deltaY = endY - (menuTouchStartY || 0);
|
||||
setVelocity(deltaX);
|
||||
|
||||
if (isOpen && dragMode !== 1 && deltaX > 0) {
|
||||
dragMode = 2;
|
||||
}
|
||||
|
||||
function onMenuTouchStart(e) {
|
||||
options.target.classList.remove("transition");
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
menuTouchStartX = touch.clientX;
|
||||
menuTouchStartY = touch.clientY;
|
||||
menuTouchStartTime = new Date().getTime();
|
||||
if (dragMode === 0 && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) {
|
||||
dragMode = 1;
|
||||
scrollContainer.addEventListener('scroll', disableEvent);
|
||||
self.showMask();
|
||||
} else if (dragMode === 0 && Math.abs(deltaY) >= 5) {
|
||||
dragMode = 2;
|
||||
}
|
||||
|
||||
function setVelocity(deltaX) {
|
||||
var time = new Date().getTime() - (menuTouchStartTime || 0);
|
||||
velocity = Math.abs(deltaX) / time;
|
||||
if (dragMode === 1) {
|
||||
newPos = currentPos + deltaX;
|
||||
self.changeMenuPos();
|
||||
}
|
||||
}
|
||||
|
||||
function onMenuTouchMove(e) {
|
||||
var isOpen = self.visible;
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
var endY = touch.clientY || 0;
|
||||
var deltaX = endX - (menuTouchStartX || 0);
|
||||
var deltaY = endY - (menuTouchStartY || 0);
|
||||
setVelocity(deltaX);
|
||||
function onMenuTouchEnd(e) {
|
||||
options.target.classList.add('transition');
|
||||
scrollContainer.removeEventListener('scroll', disableEvent);
|
||||
dragMode = 0;
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
var endY = touch.clientY || 0;
|
||||
var deltaX = endX - (menuTouchStartX || 0);
|
||||
var deltaY = endY - (menuTouchStartY || 0);
|
||||
currentPos = deltaX;
|
||||
self.checkMenuState(deltaX, deltaY);
|
||||
}
|
||||
|
||||
if (isOpen && 1 !== dragMode && deltaX > 0) {
|
||||
dragMode = 2;
|
||||
}
|
||||
function onEdgeTouchStart(e) {
|
||||
if (isPeeking) {
|
||||
onMenuTouchMove(e);
|
||||
} else {
|
||||
if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) {
|
||||
isPeeking = true;
|
||||
|
||||
if (0 === dragMode && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) {
|
||||
dragMode = 1;
|
||||
scrollContainer.addEventListener("scroll", disableEvent);
|
||||
self.showMask();
|
||||
} else if (0 === dragMode && Math.abs(deltaY) >= 5) {
|
||||
dragMode = 2;
|
||||
}
|
||||
|
||||
if (1 === dragMode) {
|
||||
newPos = currentPos + deltaX;
|
||||
self.changeMenuPos();
|
||||
}
|
||||
}
|
||||
|
||||
function onMenuTouchEnd(e) {
|
||||
options.target.classList.add("transition");
|
||||
scrollContainer.removeEventListener("scroll", disableEvent);
|
||||
dragMode = 0;
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
var endY = touch.clientY || 0;
|
||||
var deltaX = endX - (menuTouchStartX || 0);
|
||||
var deltaY = endY - (menuTouchStartY || 0);
|
||||
currentPos = deltaX;
|
||||
self.checkMenuState(deltaX, deltaY);
|
||||
}
|
||||
|
||||
function onEdgeTouchStart(e) {
|
||||
if (isPeeking) {
|
||||
onMenuTouchMove(e);
|
||||
} else {
|
||||
if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) {
|
||||
isPeeking = true;
|
||||
|
||||
if (e.type === "touchstart") {
|
||||
dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
|
||||
dom.addEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
|
||||
}
|
||||
|
||||
onMenuTouchStart(e);
|
||||
if (e.type === 'touchstart') {
|
||||
dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
|
||||
dom.addEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
|
||||
}
|
||||
|
||||
onMenuTouchStart(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onEdgeTouchMove(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEdgeTouchStart(e);
|
||||
function onEdgeTouchMove(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEdgeTouchStart(e);
|
||||
}
|
||||
|
||||
function onEdgeTouchEnd(e) {
|
||||
if (isPeeking) {
|
||||
isPeeking = false;
|
||||
dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {});
|
||||
onMenuTouchEnd(e);
|
||||
}
|
||||
}
|
||||
|
||||
function onEdgeTouchEnd(e) {
|
||||
if (isPeeking) {
|
||||
isPeeking = false;
|
||||
dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {});
|
||||
onMenuTouchEnd(e);
|
||||
}
|
||||
}
|
||||
function disableEvent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function disableEvent(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
function onBackgroundTouchStart(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
backgroundTouchStartX = touch.clientX;
|
||||
backgroundTouchStartTime = new Date().getTime();
|
||||
}
|
||||
|
||||
function onBackgroundTouchStart(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
backgroundTouchStartX = touch.clientX;
|
||||
backgroundTouchStartTime = new Date().getTime();
|
||||
}
|
||||
function onBackgroundTouchMove(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
|
||||
function onBackgroundTouchMove(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
|
||||
if (endX <= options.width && self.isVisible) {
|
||||
countStart++;
|
||||
var deltaX = endX - (backgroundTouchStartX || 0);
|
||||
|
||||
if (countStart == 1) {
|
||||
startPoint = deltaX;
|
||||
}
|
||||
if (deltaX < 0 && dragMode !== 2) {
|
||||
dragMode = 1;
|
||||
newPos = deltaX - startPoint + options.width;
|
||||
self.changeMenuPos();
|
||||
var time = new Date().getTime() - (backgroundTouchStartTime || 0);
|
||||
velocity = Math.abs(deltaX) / time;
|
||||
}
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function onBackgroundTouchEnd(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
if (endX <= options.width && self.isVisible) {
|
||||
countStart++;
|
||||
var deltaX = endX - (backgroundTouchStartX || 0);
|
||||
self.checkMenuState(deltaX);
|
||||
countStart = 0;
|
||||
}
|
||||
|
||||
function onMaskTransitionEnd() {
|
||||
var classList = mask.classList;
|
||||
|
||||
if (!classList.contains("backdrop")) {
|
||||
classList.add("hide");
|
||||
if (countStart == 1) {
|
||||
startPoint = deltaX;
|
||||
}
|
||||
if (deltaX < 0 && dragMode !== 2) {
|
||||
dragMode = 1;
|
||||
newPos = deltaX - startPoint + options.width;
|
||||
self.changeMenuPos();
|
||||
var time = new Date().getTime() - (backgroundTouchStartTime || 0);
|
||||
velocity = Math.abs(deltaX) / time;
|
||||
}
|
||||
}
|
||||
|
||||
var self;
|
||||
var defaults;
|
||||
var mask;
|
||||
var newPos = 0;
|
||||
var currentPos = 0;
|
||||
var startPoint = 0;
|
||||
var countStart = 0;
|
||||
var velocity = 0;
|
||||
options.target.classList.add("transition");
|
||||
var dragMode = 0;
|
||||
var scrollContainer = options.target.querySelector(".mainDrawer-scrollContainer");
|
||||
scrollContainer.classList.add("scrollY");
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
var TouchMenuLA = function () {
|
||||
self = this;
|
||||
defaults = {
|
||||
width: 260,
|
||||
handleSize: 10,
|
||||
disableMask: false,
|
||||
maxMaskOpacity: 0.5
|
||||
};
|
||||
this.isVisible = false;
|
||||
this.initialize();
|
||||
function onBackgroundTouchEnd(e) {
|
||||
var touches = getTouches(e);
|
||||
var touch = touches[0] || {};
|
||||
var endX = touch.clientX || 0;
|
||||
var deltaX = endX - (backgroundTouchStartX || 0);
|
||||
self.checkMenuState(deltaX);
|
||||
countStart = 0;
|
||||
}
|
||||
|
||||
function onMaskTransitionEnd() {
|
||||
var classList = mask.classList;
|
||||
|
||||
if (!classList.contains('backdrop')) {
|
||||
classList.add('hide');
|
||||
}
|
||||
}
|
||||
|
||||
var self;
|
||||
var defaults;
|
||||
var mask;
|
||||
var newPos = 0;
|
||||
var currentPos = 0;
|
||||
var startPoint = 0;
|
||||
var countStart = 0;
|
||||
var velocity = 0;
|
||||
options.target.classList.add('transition');
|
||||
var dragMode = 0;
|
||||
var scrollContainer = options.target.querySelector('.mainDrawer-scrollContainer');
|
||||
scrollContainer.classList.add('scrollY');
|
||||
|
||||
var TouchMenuLA = function () {
|
||||
self = this;
|
||||
defaults = {
|
||||
width: 260,
|
||||
handleSize: 10,
|
||||
disableMask: false,
|
||||
maxMaskOpacity: 0.5
|
||||
};
|
||||
this.isVisible = false;
|
||||
this.initialize();
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.initElements = function () {
|
||||
options.target.classList.add("touch-menu-la");
|
||||
options.target.style.width = options.width + "px";
|
||||
options.target.style.left = -options.width + "px";
|
||||
TouchMenuLA.prototype.initElements = function () {
|
||||
options.target.classList.add('touch-menu-la');
|
||||
options.target.style.width = options.width + 'px';
|
||||
options.target.style.left = -options.width + 'px';
|
||||
|
||||
if (!options.disableMask) {
|
||||
mask = document.createElement("div");
|
||||
mask.className = "tmla-mask hide";
|
||||
document.body.appendChild(mask);
|
||||
dom.addEventListener(mask, dom.whichTransitionEvent(), onMaskTransitionEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var menuTouchStartX;
|
||||
var menuTouchStartY;
|
||||
var menuTouchStartTime;
|
||||
var edgeContainer = document.querySelector(".mainDrawerHandle");
|
||||
var isPeeking = false;
|
||||
|
||||
TouchMenuLA.prototype.animateToPosition = function (pos) {
|
||||
requestAnimationFrame(function () {
|
||||
options.target.style.transform = pos ? "translateX(" + pos + "px)" : "none";
|
||||
if (!options.disableMask) {
|
||||
mask = document.createElement('div');
|
||||
mask.className = 'tmla-mask hide';
|
||||
document.body.appendChild(mask);
|
||||
dom.addEventListener(mask, dom.whichTransitionEvent(), onMaskTransitionEnd, {
|
||||
passive: true
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.changeMenuPos = function () {
|
||||
if (newPos <= options.width) {
|
||||
this.animateToPosition(newPos);
|
||||
}
|
||||
};
|
||||
var menuTouchStartX;
|
||||
var menuTouchStartY;
|
||||
var menuTouchStartTime;
|
||||
var edgeContainer = document.querySelector('.mainDrawerHandle');
|
||||
var isPeeking = false;
|
||||
|
||||
TouchMenuLA.prototype.clickMaskClose = function () {
|
||||
mask.addEventListener("click", function () {
|
||||
TouchMenuLA.prototype.animateToPosition = function (pos) {
|
||||
requestAnimationFrame(function () {
|
||||
options.target.style.transform = pos ? 'translateX(' + pos + 'px)' : 'none';
|
||||
});
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.changeMenuPos = function () {
|
||||
if (newPos <= options.width) {
|
||||
this.animateToPosition(newPos);
|
||||
}
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.clickMaskClose = function () {
|
||||
mask.addEventListener('click', function () {
|
||||
self.close();
|
||||
});
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.checkMenuState = function (deltaX, deltaY) {
|
||||
if (velocity >= 0.4) {
|
||||
if (deltaX >= 0 || Math.abs(deltaY || 0) >= 70) {
|
||||
self.open();
|
||||
} else {
|
||||
self.close();
|
||||
});
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.checkMenuState = function (deltaX, deltaY) {
|
||||
if (velocity >= 0.4) {
|
||||
if (deltaX >= 0 || Math.abs(deltaY || 0) >= 70) {
|
||||
self.open();
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if (newPos >= 100) {
|
||||
self.open();
|
||||
} else {
|
||||
if (newPos) {
|
||||
self.close();
|
||||
}
|
||||
} else {
|
||||
if (newPos >= 100) {
|
||||
self.open();
|
||||
} else {
|
||||
if (newPos) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.open = function () {
|
||||
this.animateToPosition(options.width);
|
||||
currentPos = options.width;
|
||||
this.isVisible = true;
|
||||
options.target.classList.add("drawer-open");
|
||||
self.showMask();
|
||||
self.invoke(options.onChange);
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.close = function () {
|
||||
this.animateToPosition(0);
|
||||
currentPos = 0;
|
||||
self.isVisible = false;
|
||||
options.target.classList.remove("drawer-open");
|
||||
self.hideMask();
|
||||
self.invoke(options.onChange);
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.toggle = function () {
|
||||
if (self.isVisible) {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
}
|
||||
};
|
||||
|
||||
var backgroundTouchStartX;
|
||||
var backgroundTouchStartTime;
|
||||
|
||||
TouchMenuLA.prototype.showMask = function () {
|
||||
mask.classList.remove("hide");
|
||||
mask.classList.add("backdrop");
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.hideMask = function () {
|
||||
mask.classList.add("hide");
|
||||
mask.classList.remove("backdrop");
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.invoke = function (fn) {
|
||||
if (fn) {
|
||||
fn.apply(self);
|
||||
}
|
||||
};
|
||||
|
||||
var _edgeSwipeEnabled;
|
||||
|
||||
TouchMenuLA.prototype.setEdgeSwipeEnabled = function (enabled) {
|
||||
if (!options.disableEdgeSwipe) {
|
||||
if (browser.touch) {
|
||||
if (enabled) {
|
||||
if (!_edgeSwipeEnabled) {
|
||||
_edgeSwipeEnabled = true;
|
||||
dom.addEventListener(edgeContainer, "touchstart", onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(edgeContainer, "touchend", onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (_edgeSwipeEnabled) {
|
||||
_edgeSwipeEnabled = false;
|
||||
dom.removeEventListener(edgeContainer, "touchstart", onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(edgeContainer, "touchend", onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.initialize = function () {
|
||||
options = Object.assign(defaults, options || {});
|
||||
|
||||
if (browser.edge) {
|
||||
options.disableEdgeSwipe = true;
|
||||
}
|
||||
|
||||
self.initElements();
|
||||
|
||||
if (browser.touch) {
|
||||
dom.addEventListener(options.target, "touchstart", onMenuTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, "touchmove", onMenuTouchMove, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, "touchend", onMenuTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, "touchcancel", onMenuTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, "touchstart", onBackgroundTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, "touchmove", onBackgroundTouchMove, {});
|
||||
dom.addEventListener(mask, "touchend", onBackgroundTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, "touchcancel", onBackgroundTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
self.clickMaskClose();
|
||||
};
|
||||
|
||||
return new TouchMenuLA();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
TouchMenuLA.prototype.open = function () {
|
||||
this.animateToPosition(options.width);
|
||||
currentPos = options.width;
|
||||
this.isVisible = true;
|
||||
options.target.classList.add('drawer-open');
|
||||
self.showMask();
|
||||
self.invoke(options.onChange);
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.close = function () {
|
||||
this.animateToPosition(0);
|
||||
currentPos = 0;
|
||||
self.isVisible = false;
|
||||
options.target.classList.remove('drawer-open');
|
||||
self.hideMask();
|
||||
self.invoke(options.onChange);
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.toggle = function () {
|
||||
if (self.isVisible) {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
}
|
||||
};
|
||||
|
||||
var backgroundTouchStartX;
|
||||
var backgroundTouchStartTime;
|
||||
|
||||
TouchMenuLA.prototype.showMask = function () {
|
||||
mask.classList.remove('hide');
|
||||
mask.classList.add('backdrop');
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.hideMask = function () {
|
||||
mask.classList.add('hide');
|
||||
mask.classList.remove('backdrop');
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.invoke = function (fn) {
|
||||
if (fn) {
|
||||
fn.apply(self);
|
||||
}
|
||||
};
|
||||
|
||||
var _edgeSwipeEnabled;
|
||||
|
||||
TouchMenuLA.prototype.setEdgeSwipeEnabled = function (enabled) {
|
||||
if (!options.disableEdgeSwipe) {
|
||||
if (browser.touch) {
|
||||
if (enabled) {
|
||||
if (!_edgeSwipeEnabled) {
|
||||
_edgeSwipeEnabled = true;
|
||||
dom.addEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (_edgeSwipeEnabled) {
|
||||
_edgeSwipeEnabled = false;
|
||||
dom.removeEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TouchMenuLA.prototype.initialize = function () {
|
||||
options = Object.assign(defaults, options || {});
|
||||
|
||||
if (browser.edge) {
|
||||
options.disableEdgeSwipe = true;
|
||||
}
|
||||
|
||||
self.initElements();
|
||||
|
||||
if (browser.touch) {
|
||||
dom.addEventListener(options.target, 'touchstart', onMenuTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, 'touchmove', onMenuTouchMove, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, 'touchend', onMenuTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(options.target, 'touchcancel', onMenuTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, 'touchstart', onBackgroundTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, 'touchmove', onBackgroundTouchMove, {});
|
||||
dom.addEventListener(mask, 'touchend', onBackgroundTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(mask, 'touchcancel', onBackgroundTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
self.clickMaskClose();
|
||||
};
|
||||
|
||||
return new TouchMenuLA();
|
||||
}
|
||||
|
@ -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
@ -1132,7 +1132,7 @@ function tryRemoveElement(elem) {
|
||||
*/
|
||||
getCueCss(appearance, selector) {
|
||||
return `${selector}::cue {
|
||||
${appearance.text.map((s) => `${s.name}:${s.value}!important;`).join('')}
|
||||
${appearance.text.map((s) => s.value !== undefined && s.value !== '' ? `${s.name}:${s.value}!important;` : '').join('')}
|
||||
}`;
|
||||
}
|
||||
|
||||
@ -1150,7 +1150,7 @@ function tryRemoveElement(elem) {
|
||||
document.getElementsByTagName('head')[0].appendChild(styleElem);
|
||||
}
|
||||
|
||||
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings(), true), '.htmlvideoplayer');
|
||||
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings()), '.htmlvideoplayer');
|
||||
});
|
||||
}
|
||||
|
||||
@ -1195,17 +1195,28 @@ function tryRemoveElement(elem) {
|
||||
|
||||
// download the track json
|
||||
this.fetchSubtitles(track, item).then(function (data) {
|
||||
// show in ui
|
||||
console.debug(`downloaded ${data.TrackEvents.length} track events`);
|
||||
// add some cues to show the text
|
||||
// in safari, the cues need to be added before setting the track mode to showing
|
||||
for (const trackEvent of data.TrackEvents) {
|
||||
const trackCueObject = window.VTTCue || window.TextTrackCue;
|
||||
const cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
|
||||
import('userSettings').then((userSettings) => {
|
||||
// show in ui
|
||||
console.debug(`downloaded ${data.TrackEvents.length} track events`);
|
||||
|
||||
trackElement.addCue(cue);
|
||||
}
|
||||
trackElement.mode = 'showing';
|
||||
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings();
|
||||
const cueLine = parseInt(subtitleAppearance.verticalPosition, 10);
|
||||
|
||||
// add some cues to show the text
|
||||
// in safari, the cues need to be added before setting the track mode to showing
|
||||
for (const trackEvent of data.TrackEvents) {
|
||||
const trackCueObject = window.VTTCue || window.TextTrackCue;
|
||||
const cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
|
||||
|
||||
if (cue.line === 'auto') {
|
||||
cue.line = cueLine;
|
||||
}
|
||||
|
||||
trackElement.addCue(cue);
|
||||
}
|
||||
|
||||
trackElement.mode = 'showing';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,16 +33,22 @@ video::-webkit-media-controls {
|
||||
text-shadow: 0.14em 0.14em 0.14em rgba(0, 0, 0, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: inherit;
|
||||
line-height: normal; /* Restore value. See -webkit-media-text-track-container 'line-height' */
|
||||
}
|
||||
|
||||
.htmlvideoplayer-moveupsubtitles::-webkit-media-text-track-display {
|
||||
/* style the text itself */
|
||||
margin-top: -2em;
|
||||
.htmlvideoplayer::-webkit-media-text-track-container {
|
||||
font-size: 170% !important; /* Override element inline style */
|
||||
line-height: 50%; /* Child element cannot set line height smaller than its parent has. This allow smaller values for children */
|
||||
}
|
||||
|
||||
.htmlvideoplayer::-webkit-media-text-track-display {
|
||||
max-width: 70%;
|
||||
margin-left: 15%;
|
||||
}
|
||||
|
||||
.videoSubtitles {
|
||||
position: fixed;
|
||||
bottom: 10%;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@ -53,7 +59,6 @@ video::-webkit-media-controls {
|
||||
.videoSubtitlesInner {
|
||||
max-width: 70%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
padding: 0.25em;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'], function (playbackManager, events, serverNotifications, connectionManager) {
|
||||
'use strict';
|
||||
|
||||
serverNotifications = serverNotifications.default || serverNotifications;
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
|
||||
function getActivePlayerId() {
|
||||
|
@ -1,8 +1,10 @@
|
||||
define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'syncPlayManager', 'groupSelectionMenu', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, syncPlayManager, groupSelectionMenu, browser, globalize, imageHelper) {
|
||||
'use strict';
|
||||
|
||||
viewManager = viewManager.default || viewManager;
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
browser = browser.default || browser;
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
|
||||
function renderHeader() {
|
||||
var html = '';
|
||||
@ -806,6 +808,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
||||
navDrawerScrollContainer.addEventListener('click', onMainDrawerClick);
|
||||
return new Promise(function (resolve, reject) {
|
||||
require(['navdrawer'], function (navdrawer) {
|
||||
navdrawer = navdrawer.default || navdrawer;
|
||||
|
||||
navDrawerInstance = new navdrawer(getNavDrawerOptions());
|
||||
|
||||
if (!layoutManager.tv) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,137 +1,138 @@
|
||||
define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
|
||||
'use strict';
|
||||
import focusManager from 'focusManager';
|
||||
import dom from 'dom';
|
||||
import 'scrollStyles';
|
||||
|
||||
focusManager = focusManager.default || focusManager;
|
||||
function getBoundingClientRect(elem) {
|
||||
// Support: BlackBerry 5, iOS 3 (original iPhone)
|
||||
// If we don't have gBCR, just use 0,0 rather than error
|
||||
if (elem.getBoundingClientRect) {
|
||||
return elem.getBoundingClientRect();
|
||||
} else {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
function getBoundingClientRect(elem) {
|
||||
// Support: BlackBerry 5, iOS 3 (original iPhone)
|
||||
// If we don't have gBCR, just use 0,0 rather than error
|
||||
if (elem.getBoundingClientRect) {
|
||||
return elem.getBoundingClientRect();
|
||||
} else {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
export function getPosition(scrollContainer, item, horizontal) {
|
||||
const slideeOffset = getBoundingClientRect(scrollContainer);
|
||||
const itemOffset = getBoundingClientRect(item);
|
||||
|
||||
let offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
|
||||
let size = horizontal ? itemOffset.width : itemOffset.height;
|
||||
if (!size && size !== 0) {
|
||||
size = item[horizontal ? 'offsetWidth' : 'offsetHeight'];
|
||||
}
|
||||
|
||||
function getPosition(scrollContainer, item, horizontal) {
|
||||
var slideeOffset = getBoundingClientRect(scrollContainer);
|
||||
var itemOffset = getBoundingClientRect(item);
|
||||
const currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop;
|
||||
|
||||
var offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
|
||||
var size = horizontal ? itemOffset.width : itemOffset.height;
|
||||
if (!size && size !== 0) {
|
||||
size = item[horizontal ? 'offsetWidth' : 'offsetHeight'];
|
||||
}
|
||||
offset += currentStart;
|
||||
|
||||
var currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop;
|
||||
const frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight;
|
||||
|
||||
offset += currentStart;
|
||||
const currentEnd = currentStart + frameSize;
|
||||
|
||||
var frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight;
|
||||
|
||||
var currentEnd = currentStart + frameSize;
|
||||
|
||||
var isVisible = offset >= currentStart && (offset + size) <= currentEnd;
|
||||
|
||||
return {
|
||||
start: offset,
|
||||
center: (offset - (frameSize / 2) + (size / 2)),
|
||||
end: offset - frameSize + size,
|
||||
size: size,
|
||||
isVisible: isVisible
|
||||
};
|
||||
}
|
||||
|
||||
function toCenter(container, elem, horizontal, skipWhenVisible) {
|
||||
var pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.center, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.center);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.center);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toStart(container, elem, horizontal, skipWhenVisible) {
|
||||
var pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.start, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.start);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.start);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocus(e, scrollSlider, horizontal) {
|
||||
var focused = focusManager.focusableParent(e.target);
|
||||
|
||||
if (focused) {
|
||||
toCenter(scrollSlider, focused, horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocusHorizontal(e) {
|
||||
centerOnFocus(e, this, true);
|
||||
}
|
||||
function centerOnFocusVertical(e) {
|
||||
centerOnFocus(e, this, false);
|
||||
}
|
||||
const isVisible = offset >= currentStart && (offset + size) <= currentEnd;
|
||||
|
||||
return {
|
||||
getPosition: getPosition,
|
||||
centerFocus: {
|
||||
on: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
},
|
||||
off: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
toCenter: toCenter,
|
||||
toStart: toStart
|
||||
start: offset,
|
||||
center: (offset - (frameSize / 2) + (size / 2)),
|
||||
end: offset - frameSize + size,
|
||||
size: size,
|
||||
isVisible: isVisible
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function toCenter(container, elem, horizontal, skipWhenVisible) {
|
||||
const pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.center, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.center);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.center);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function toStart(container, elem, horizontal, skipWhenVisible) {
|
||||
const pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.start, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.start);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.start);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocus(e, scrollSlider, horizontal) {
|
||||
const focused = focusManager.focusableParent(e.target);
|
||||
|
||||
if (focused) {
|
||||
toCenter(scrollSlider, focused, horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocusHorizontal(e) {
|
||||
centerOnFocus(e, this, true);
|
||||
}
|
||||
|
||||
function centerOnFocusVertical(e) {
|
||||
centerOnFocus(e, this, false);
|
||||
}
|
||||
|
||||
export const centerFocus = {
|
||||
on: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
},
|
||||
off: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
getPosition: getPosition,
|
||||
centerFocus: centerFocus,
|
||||
toCenter: toCenter,
|
||||
toStart: toStart
|
||||
};
|
||||
|
@ -1,57 +0,0 @@
|
||||
define(['searchFields', 'searchResults', 'events'], function (SearchFields, SearchResults, events) {
|
||||
'use strict';
|
||||
|
||||
SearchFields = SearchFields.default || SearchFields;
|
||||
SearchResults = SearchResults.default || SearchResults;
|
||||
|
||||
function init(instance, tabContent, options) {
|
||||
tabContent.innerHTML = '<div class="padded-left padded-right searchFields"></div><div class="searchResults padded-top" style="padding-top:1.5em;"></div>';
|
||||
instance.searchFields = new SearchFields({
|
||||
element: tabContent.querySelector('.searchFields')
|
||||
});
|
||||
instance.searchResults = new SearchResults({
|
||||
element: tabContent.querySelector('.searchResults'),
|
||||
serverId: ApiClient.serverId(),
|
||||
parentId: options.parentId,
|
||||
collectionType: options.collectionType
|
||||
});
|
||||
events.on(instance.searchFields, 'search', function (e, value) {
|
||||
instance.searchResults.search(value);
|
||||
});
|
||||
}
|
||||
|
||||
function SearchTab(view, tabContent, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
init(this, tabContent, options);
|
||||
|
||||
self.preRender = function () {};
|
||||
|
||||
self.renderTab = function () {
|
||||
var searchFields = this.searchFields;
|
||||
|
||||
if (searchFields) {
|
||||
searchFields.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SearchTab.prototype.destroy = function () {
|
||||
var searchFields = this.searchFields;
|
||||
|
||||
if (searchFields) {
|
||||
searchFields.destroy();
|
||||
}
|
||||
|
||||
this.searchFields = null;
|
||||
var searchResults = this.searchResults;
|
||||
|
||||
if (searchResults) {
|
||||
searchResults.destroy();
|
||||
}
|
||||
|
||||
this.searchResults = null;
|
||||
};
|
||||
|
||||
return SearchTab;
|
||||
});
|
@ -1,214 +1,216 @@
|
||||
define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, syncPlayManager, events, inputManager, focusManager, appRouter) {
|
||||
'use strict';
|
||||
import connectionManager from 'connectionManager';
|
||||
import playbackManager from 'playbackManager';
|
||||
import syncPlayManager from 'syncPlayManager';
|
||||
import events from 'events';
|
||||
import inputManager from 'inputManager';
|
||||
import focusManager from 'focusManager';
|
||||
import appRouter from 'appRouter';
|
||||
|
||||
playbackManager = playbackManager.default || playbackManager;
|
||||
focusManager = focusManager.default || focusManager;
|
||||
const serverNotifications = {};
|
||||
|
||||
var serverNotifications = {};
|
||||
function notifyApp() {
|
||||
inputManager.notify();
|
||||
}
|
||||
|
||||
function notifyApp() {
|
||||
inputManager.notify();
|
||||
}
|
||||
|
||||
function displayMessage(cmd) {
|
||||
var args = cmd.Arguments;
|
||||
if (args.TimeoutMs) {
|
||||
require(['toast'], function (toast) {
|
||||
toast({ title: args.Header, text: args.Text });
|
||||
});
|
||||
} else {
|
||||
require(['alert'], function (alert) {
|
||||
alert.default({ title: args.Header, text: args.Text });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function displayContent(cmd, apiClient) {
|
||||
if (!playbackManager.isPlayingLocally(['Video', 'Book'])) {
|
||||
appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId());
|
||||
}
|
||||
}
|
||||
|
||||
function playTrailers(apiClient, itemId) {
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
|
||||
playbackManager.playTrailers(item);
|
||||
function displayMessage(cmd) {
|
||||
const args = cmd.Arguments;
|
||||
if (args.TimeoutMs) {
|
||||
import('toast').then(({default: toast}) => {
|
||||
toast({ title: args.Header, text: args.Text });
|
||||
});
|
||||
} else {
|
||||
import('alert').then(({default: alert}) => {
|
||||
alert({ title: args.Header, text: args.Text });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processGeneralCommand(cmd, apiClient) {
|
||||
console.debug('Received command: ' + cmd.Name);
|
||||
switch (cmd.Name) {
|
||||
case 'Select':
|
||||
inputManager.handleCommand('select');
|
||||
return;
|
||||
case 'Back':
|
||||
inputManager.handleCommand('back');
|
||||
return;
|
||||
case 'MoveUp':
|
||||
inputManager.handleCommand('up');
|
||||
return;
|
||||
case 'MoveDown':
|
||||
inputManager.handleCommand('down');
|
||||
return;
|
||||
case 'MoveLeft':
|
||||
inputManager.handleCommand('left');
|
||||
return;
|
||||
case 'MoveRight':
|
||||
inputManager.handleCommand('right');
|
||||
return;
|
||||
case 'PageUp':
|
||||
inputManager.handleCommand('pageup');
|
||||
return;
|
||||
case 'PageDown':
|
||||
inputManager.handleCommand('pagedown');
|
||||
return;
|
||||
case 'PlayTrailers':
|
||||
playTrailers(apiClient, cmd.Arguments.ItemId);
|
||||
break;
|
||||
case 'SetRepeatMode':
|
||||
playbackManager.setRepeatMode(cmd.Arguments.RepeatMode);
|
||||
break;
|
||||
case 'SetShuffleQueue':
|
||||
playbackManager.setQueueShuffleMode(cmd.Arguments.ShuffleMode);
|
||||
break;
|
||||
case 'VolumeUp':
|
||||
inputManager.handleCommand('volumeup');
|
||||
return;
|
||||
case 'VolumeDown':
|
||||
inputManager.handleCommand('volumedown');
|
||||
return;
|
||||
case 'ChannelUp':
|
||||
inputManager.handleCommand('channelup');
|
||||
return;
|
||||
case 'ChannelDown':
|
||||
inputManager.handleCommand('channeldown');
|
||||
return;
|
||||
case 'Mute':
|
||||
inputManager.handleCommand('mute');
|
||||
return;
|
||||
case 'Unmute':
|
||||
inputManager.handleCommand('unmute');
|
||||
return;
|
||||
case 'ToggleMute':
|
||||
inputManager.handleCommand('togglemute');
|
||||
return;
|
||||
case 'SetVolume':
|
||||
notifyApp();
|
||||
playbackManager.setVolume(cmd.Arguments.Volume);
|
||||
break;
|
||||
case 'SetAudioStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'SetSubtitleStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'ToggleFullscreen':
|
||||
inputManager.handleCommand('togglefullscreen');
|
||||
return;
|
||||
case 'GoHome':
|
||||
inputManager.handleCommand('home');
|
||||
return;
|
||||
case 'GoToSettings':
|
||||
inputManager.handleCommand('settings');
|
||||
return;
|
||||
case 'DisplayContent':
|
||||
displayContent(cmd, apiClient);
|
||||
break;
|
||||
case 'GoToSearch':
|
||||
inputManager.handleCommand('search');
|
||||
return;
|
||||
case 'DisplayMessage':
|
||||
displayMessage(cmd);
|
||||
break;
|
||||
case 'ToggleOsd':
|
||||
// todo
|
||||
break;
|
||||
case 'ToggleContextMenu':
|
||||
// todo
|
||||
break;
|
||||
case 'TakeScreenShot':
|
||||
// todo
|
||||
break;
|
||||
case 'SendKey':
|
||||
// todo
|
||||
break;
|
||||
case 'SendString':
|
||||
// todo
|
||||
focusManager.sendText(cmd.Arguments.String);
|
||||
break;
|
||||
default:
|
||||
console.debug('processGeneralCommand does not recognize: ' + cmd.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
notifyApp();
|
||||
function displayContent(cmd, apiClient) {
|
||||
if (!playbackManager.isPlayingLocally(['Video', 'Book'])) {
|
||||
appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId());
|
||||
}
|
||||
}
|
||||
|
||||
function onMessageReceived(e, msg) {
|
||||
var apiClient = this;
|
||||
if (msg.MessageType === 'Play') {
|
||||
notifyApp();
|
||||
var serverId = apiClient.serverInfo().Id;
|
||||
if (msg.Data.PlayCommand === 'PlayNext') {
|
||||
playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else if (msg.Data.PlayCommand === 'PlayLast') {
|
||||
playbackManager.queue({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else {
|
||||
playbackManager.play({
|
||||
ids: msg.Data.ItemIds,
|
||||
startPositionTicks: msg.Data.StartPositionTicks,
|
||||
mediaSourceId: msg.Data.MediaSourceId,
|
||||
audioStreamIndex: msg.Data.AudioStreamIndex,
|
||||
subtitleStreamIndex: msg.Data.SubtitleStreamIndex,
|
||||
startIndex: msg.Data.StartIndex,
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
} else if (msg.MessageType === 'Playstate') {
|
||||
if (msg.Data.Command === 'Stop') {
|
||||
inputManager.handleCommand('stop');
|
||||
} else if (msg.Data.Command === 'Pause') {
|
||||
inputManager.handleCommand('pause');
|
||||
} else if (msg.Data.Command === 'Unpause') {
|
||||
inputManager.handleCommand('play');
|
||||
} else if (msg.Data.Command === 'PlayPause') {
|
||||
inputManager.handleCommand('playpause');
|
||||
} else if (msg.Data.Command === 'Seek') {
|
||||
playbackManager.seek(msg.Data.SeekPositionTicks);
|
||||
} else if (msg.Data.Command === 'NextTrack') {
|
||||
inputManager.handleCommand('next');
|
||||
} else if (msg.Data.Command === 'PreviousTrack') {
|
||||
inputManager.handleCommand('previous');
|
||||
} else {
|
||||
notifyApp();
|
||||
}
|
||||
} else if (msg.MessageType === 'GeneralCommand') {
|
||||
var cmd = msg.Data;
|
||||
processGeneralCommand(cmd, apiClient);
|
||||
} else if (msg.MessageType === 'UserDataChanged') {
|
||||
if (msg.Data.UserId === apiClient.getCurrentUserId()) {
|
||||
for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) {
|
||||
events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]);
|
||||
}
|
||||
}
|
||||
} else if (msg.MessageType === 'SyncPlayCommand') {
|
||||
syncPlayManager.processCommand(msg.Data, apiClient);
|
||||
} else if (msg.MessageType === 'SyncPlayGroupUpdate') {
|
||||
syncPlayManager.processGroupUpdate(msg.Data, apiClient);
|
||||
} else {
|
||||
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
||||
}
|
||||
}
|
||||
function bindEvents(apiClient) {
|
||||
events.off(apiClient, 'message', onMessageReceived);
|
||||
events.on(apiClient, 'message', onMessageReceived);
|
||||
}
|
||||
|
||||
connectionManager.getApiClients().forEach(bindEvents);
|
||||
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
||||
bindEvents(newApiClient);
|
||||
function playTrailers(apiClient, itemId) {
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
|
||||
playbackManager.playTrailers(item);
|
||||
});
|
||||
return serverNotifications;
|
||||
}
|
||||
|
||||
function processGeneralCommand(cmd, apiClient) {
|
||||
console.debug('Received command: ' + cmd.Name);
|
||||
switch (cmd.Name) {
|
||||
case 'Select':
|
||||
inputManager.handleCommand('select');
|
||||
return;
|
||||
case 'Back':
|
||||
inputManager.handleCommand('back');
|
||||
return;
|
||||
case 'MoveUp':
|
||||
inputManager.handleCommand('up');
|
||||
return;
|
||||
case 'MoveDown':
|
||||
inputManager.handleCommand('down');
|
||||
return;
|
||||
case 'MoveLeft':
|
||||
inputManager.handleCommand('left');
|
||||
return;
|
||||
case 'MoveRight':
|
||||
inputManager.handleCommand('right');
|
||||
return;
|
||||
case 'PageUp':
|
||||
inputManager.handleCommand('pageup');
|
||||
return;
|
||||
case 'PageDown':
|
||||
inputManager.handleCommand('pagedown');
|
||||
return;
|
||||
case 'PlayTrailers':
|
||||
playTrailers(apiClient, cmd.Arguments.ItemId);
|
||||
break;
|
||||
case 'SetRepeatMode':
|
||||
playbackManager.setRepeatMode(cmd.Arguments.RepeatMode);
|
||||
break;
|
||||
case 'SetShuffleQueue':
|
||||
playbackManager.setQueueShuffleMode(cmd.Arguments.ShuffleMode);
|
||||
break;
|
||||
case 'VolumeUp':
|
||||
inputManager.handleCommand('volumeup');
|
||||
return;
|
||||
case 'VolumeDown':
|
||||
inputManager.handleCommand('volumedown');
|
||||
return;
|
||||
case 'ChannelUp':
|
||||
inputManager.handleCommand('channelup');
|
||||
return;
|
||||
case 'ChannelDown':
|
||||
inputManager.handleCommand('channeldown');
|
||||
return;
|
||||
case 'Mute':
|
||||
inputManager.handleCommand('mute');
|
||||
return;
|
||||
case 'Unmute':
|
||||
inputManager.handleCommand('unmute');
|
||||
return;
|
||||
case 'ToggleMute':
|
||||
inputManager.handleCommand('togglemute');
|
||||
return;
|
||||
case 'SetVolume':
|
||||
notifyApp();
|
||||
playbackManager.setVolume(cmd.Arguments.Volume);
|
||||
break;
|
||||
case 'SetAudioStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'SetSubtitleStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'ToggleFullscreen':
|
||||
inputManager.handleCommand('togglefullscreen');
|
||||
return;
|
||||
case 'GoHome':
|
||||
inputManager.handleCommand('home');
|
||||
return;
|
||||
case 'GoToSettings':
|
||||
inputManager.handleCommand('settings');
|
||||
return;
|
||||
case 'DisplayContent':
|
||||
displayContent(cmd, apiClient);
|
||||
break;
|
||||
case 'GoToSearch':
|
||||
inputManager.handleCommand('search');
|
||||
return;
|
||||
case 'DisplayMessage':
|
||||
displayMessage(cmd);
|
||||
break;
|
||||
case 'ToggleOsd':
|
||||
// todo
|
||||
break;
|
||||
case 'ToggleContextMenu':
|
||||
// todo
|
||||
break;
|
||||
case 'TakeScreenShot':
|
||||
// todo
|
||||
break;
|
||||
case 'SendKey':
|
||||
// todo
|
||||
break;
|
||||
case 'SendString':
|
||||
// todo
|
||||
focusManager.sendText(cmd.Arguments.String);
|
||||
break;
|
||||
default:
|
||||
console.debug('processGeneralCommand does not recognize: ' + cmd.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
notifyApp();
|
||||
}
|
||||
|
||||
function onMessageReceived(e, msg) {
|
||||
const apiClient = this;
|
||||
if (msg.MessageType === 'Play') {
|
||||
notifyApp();
|
||||
const serverId = apiClient.serverInfo().Id;
|
||||
if (msg.Data.PlayCommand === 'PlayNext') {
|
||||
playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else if (msg.Data.PlayCommand === 'PlayLast') {
|
||||
playbackManager.queue({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else {
|
||||
playbackManager.play({
|
||||
ids: msg.Data.ItemIds,
|
||||
startPositionTicks: msg.Data.StartPositionTicks,
|
||||
mediaSourceId: msg.Data.MediaSourceId,
|
||||
audioStreamIndex: msg.Data.AudioStreamIndex,
|
||||
subtitleStreamIndex: msg.Data.SubtitleStreamIndex,
|
||||
startIndex: msg.Data.StartIndex,
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
} else if (msg.MessageType === 'Playstate') {
|
||||
if (msg.Data.Command === 'Stop') {
|
||||
inputManager.handleCommand('stop');
|
||||
} else if (msg.Data.Command === 'Pause') {
|
||||
inputManager.handleCommand('pause');
|
||||
} else if (msg.Data.Command === 'Unpause') {
|
||||
inputManager.handleCommand('play');
|
||||
} else if (msg.Data.Command === 'PlayPause') {
|
||||
inputManager.handleCommand('playpause');
|
||||
} else if (msg.Data.Command === 'Seek') {
|
||||
playbackManager.seek(msg.Data.SeekPositionTicks);
|
||||
} else if (msg.Data.Command === 'NextTrack') {
|
||||
inputManager.handleCommand('next');
|
||||
} else if (msg.Data.Command === 'PreviousTrack') {
|
||||
inputManager.handleCommand('previous');
|
||||
} else {
|
||||
notifyApp();
|
||||
}
|
||||
} else if (msg.MessageType === 'GeneralCommand') {
|
||||
const cmd = msg.Data;
|
||||
processGeneralCommand(cmd, apiClient);
|
||||
} else if (msg.MessageType === 'UserDataChanged') {
|
||||
if (msg.Data.UserId === apiClient.getCurrentUserId()) {
|
||||
for (let i = 0, length = msg.Data.UserDataList.length; i < length; i++) {
|
||||
events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]);
|
||||
}
|
||||
}
|
||||
} else if (msg.MessageType === 'SyncPlayCommand') {
|
||||
syncPlayManager.processCommand(msg.Data, apiClient);
|
||||
} else if (msg.MessageType === 'SyncPlayGroupUpdate') {
|
||||
syncPlayManager.processGroupUpdate(msg.Data, apiClient);
|
||||
} else {
|
||||
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
||||
}
|
||||
}
|
||||
function bindEvents(apiClient) {
|
||||
events.off(apiClient, 'message', onMessageReceived);
|
||||
events.on(apiClient, 'message', onMessageReceived);
|
||||
}
|
||||
|
||||
connectionManager.getApiClients().forEach(bindEvents);
|
||||
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
||||
bindEvents(newApiClient);
|
||||
});
|
||||
|
||||
export default serverNotifications;
|
||||
|
@ -15,6 +15,10 @@ function saveServerPreferences(instance) {
|
||||
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
|
||||
}
|
||||
|
||||
const defaultSubtitleAppearanceSettings = {
|
||||
verticalPosition: -3
|
||||
};
|
||||
|
||||
export class UserSettings {
|
||||
constructor() {
|
||||
}
|
||||
@ -412,7 +416,7 @@ export class UserSettings {
|
||||
*/
|
||||
getSubtitleAppearanceSettings(key) {
|
||||
key = key || 'localplayersubtitleappearance3';
|
||||
return JSON.parse(this.get(key, false) || '{}');
|
||||
return Object.assign(defaultSubtitleAppearanceSettings, JSON.parse(this.get(key, false) || '{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,23 +1,20 @@
|
||||
define([], function () {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
openUrl: function (url, target) {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.openUrl(url, target);
|
||||
} else {
|
||||
window.open(url, target || '_blank');
|
||||
}
|
||||
},
|
||||
enableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.enableFullscreen();
|
||||
}
|
||||
},
|
||||
disableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.disableFullscreen();
|
||||
}
|
||||
// TODO: This seems like a good candidate for deprecation
|
||||
export default {
|
||||
openUrl: function (url, target) {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.openUrl(url, target);
|
||||
} else {
|
||||
window.open(url, target || '_blank');
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
enableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.enableFullscreen();
|
||||
}
|
||||
},
|
||||
disableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.disableFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -347,8 +347,10 @@ function initClient() {
|
||||
}
|
||||
|
||||
function getLayoutManager(layoutManager, appHost) {
|
||||
if (appHost.default.getDefaultLayout) {
|
||||
layoutManager.defaultLayout = appHost.default.getDefaultLayout();
|
||||
layoutManager = layoutManager.default || layoutManager;
|
||||
appHost = appHost.default || appHost;
|
||||
if (appHost.getDefaultLayout) {
|
||||
layoutManager.defaultLayout = appHost.getDefaultLayout();
|
||||
}
|
||||
|
||||
layoutManager.init();
|
||||
@ -810,8 +812,8 @@ function initClient() {
|
||||
define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency);
|
||||
define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency);
|
||||
define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) {
|
||||
window.ViewManager = viewManager;
|
||||
viewManager.dispatchPageEvents(true);
|
||||
window.ViewManager = viewManager.default;
|
||||
viewManager.default.dispatchPageEvents(true);
|
||||
return viewManager;
|
||||
});
|
||||
define('slideshow', [componentsPath + '/slideshow/slideshow'], returnFirstDependency);
|
||||
|
@ -99,9 +99,9 @@
|
||||
"DeleteUserConfirmation": "هل انت متاكد من انك تريد حذف هذا المستخدم ؟",
|
||||
"DeviceAccessHelp": "هذه الميزة تنطبق حصرياً على الأجهزة التي يمكن التعرف عليها فردياً ولن تمنع المتصفح من الدخول عليها. ترشيح الوصول لأجهزة المستخدم ستمنع المستخدمين من استعمال الأجهزة الجديدة إلى أن يتم اعتمادهم من هنا.",
|
||||
"DrmChannelsNotImported": "القنوات المجهزة بإدارة الحقوق الرقمية DRM لن تورّد.",
|
||||
"EasyPasswordHelp": "الرمز الشخصي الميسرالخاص بك يمكنك من الاتصال إلى خادم مكتبتك، عبر تطبيقات أمبي على الأجهزة أو الدخول على حسابك في الشبكة الداخلية.",
|
||||
"EasyPasswordHelp": "الرمز pin الميسر الخاص بك يستخدم للوصول بدون اتصل عبر التطبيقات المدعومة أو الدخول على حسابك في الشبكة الداخلية.",
|
||||
"EnablePhotos": "عرض الصور",
|
||||
"EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى",
|
||||
"EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى.",
|
||||
"ErrorAddingListingsToSchedulesDirect": "كان هناك خطأ في إضافة الاصطفاف لخدمة \"Schedules Direct\" الخاصة بك. خدمة \"Schedules Direct\" لا تسمح إلا بعدد محدود من الاصطفافات لكل حساب. قد تحتاج إلى تسجيل الدخول إلى موقع \"Schedules Direct\" لإزالة الاصطفافات الأخرى من حسابك قبل المتابعة.",
|
||||
"ErrorAddingMediaPathToVirtualFolder": "كان هناك خطأ في إضافة مسار الوسائط. الرجاء التأكد من صحة المسار وأن خادم أمبي لديه صلاحية الوصول إلى الموقع.",
|
||||
"ErrorAddingTunerDevice": "كان هناك خطأ في إضافة جهاز المولف. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.",
|
||||
@ -111,7 +111,7 @@
|
||||
"ErrorPleaseSelectLineup": "الرجاء اختيار اصطفاف ثم المحاولة مرة أخرى. إن لم تتوفر أية اصطفافات، فالرجاء التأكد من اسم المستخدم وكلمة المرور الخاصة بك، وتأكد من صحة رمزك البريدي.",
|
||||
"ErrorSavingTvProvider": "كان هناك خطأ في حفظ مزود التلفزة. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.",
|
||||
"ExitFullscreen": "الخروج من الشاشة الكاملة",
|
||||
"ExtractChapterImagesHelp": "استخلاص صور الأبواب سيسمح لتطبيقات أمبي أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل قدرة المعالج بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.",
|
||||
"ExtractChapterImagesHelp": "استخلاص صور الفصول سيسمح للتطبيقات أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل موارد الجهاز بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.",
|
||||
"FFmpegSavePathNotFound": "لم نستطع تحديد موقع ffmpeg باستخدام المسار الذي أدخلته. سوف نحتاج تطبيق FFprobe أيضاً ويجب أن يتواجد في نفس المكان. إن هذه الأجزاء تكون بالعادة محزومة معاً في نفس ملف الإنزال. الرجاء التأكد من المسار المدخل والمحاولة مرة أخرى.",
|
||||
"FastForward": "التقديم السريع",
|
||||
"FileNotFound": "الملف غير موجود.",
|
||||
@ -129,7 +129,7 @@
|
||||
"GuideProviderSelectListings": "إختر المبوبات",
|
||||
"H264CrfHelp": "معامل المعدل الثابت CRF هو الجودة الافتراضية لإعدادات مشفر x264. بإمكانك إعطاء قيمة تتراوح بين 0 و 51، وكلما قلت القيمة فسينتج عن ذلك جودة أفضل (على حساب حجم تخزين أعلى). القيم المعقول تتراوح بين 18 و 28. الافتراضي لـ x264 هي 23، لذا فبإمكانك استخدام هذه القيمة كنقطة بداية.",
|
||||
"EncoderPresetHelp": "اختر قيمة أعلى لتحسين السرة والأداء وقيمة أقل لتحسين الجودة.",
|
||||
"HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية آلي.",
|
||||
"HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية بلا None.",
|
||||
"HeaderAccessSchedule": "جدول الدخولات",
|
||||
"HeaderAccessScheduleHelp": "إنشئ جدول دخولات لكي تتمكن من تحديد ساعات للدخول.",
|
||||
"HeaderActiveDevices": "الأجهزة المفعّلة",
|
||||
@ -145,7 +145,7 @@
|
||||
"HeaderAllowMediaDeletionFrom": "السماح بحذف الوسائط من قبل",
|
||||
"HeaderApiKey": "مفتاح API",
|
||||
"HeaderApiKeys": "مفاتيح API",
|
||||
"HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بخادم أمبي. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بحساب أمبي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.",
|
||||
"HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بالخادم. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بمستخدم عادي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.",
|
||||
"HeaderApp": "التطبيق",
|
||||
"HeaderAudioSettings": "إعدادات الصوت",
|
||||
"HeaderBooks": "الكتب",
|
||||
@ -232,7 +232,7 @@
|
||||
"HeaderPreferredMetadataLanguage": "اللغة المفضلة لواصفات البيانات",
|
||||
"HeaderProfile": "الحساب",
|
||||
"HeaderProfileInformation": "معلومات العريضة",
|
||||
"HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل خادم أمبي في الجهاز",
|
||||
"HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل الخادم في للعملاء.",
|
||||
"HeaderRecentlyPlayed": "تم تشغيله مؤخراً",
|
||||
"HeaderRecordingPostProcessing": "تطبيق ما-بعد-المعالجة للتسجيل",
|
||||
"HeaderRemoteControl": "التحكم عن بعد",
|
||||
@ -254,7 +254,7 @@
|
||||
"HeaderSelectServerCachePath": "إختر مسار كاشة الخادم",
|
||||
"HeaderSelectServerCachePathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم كاشة لملفات الخادم. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
|
||||
"HeaderSelectTranscodingPath": "إختر المسار المؤقت للتشفير البيني",
|
||||
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم للملفات المؤقتة للتشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
|
||||
"HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.",
|
||||
"HeaderSendMessage": "أرسل رسالة",
|
||||
"HeaderSeries": "المسلسلات",
|
||||
"HeaderServerSettings": "إعدادات الخادم",
|
||||
@ -291,17 +291,17 @@
|
||||
"HeaderXmlSettings": "إعدادات xml",
|
||||
"HeaderYears": "السنوات",
|
||||
"HeadersFolders": "مجلدات",
|
||||
"ImportFavoriteChannelsHelp": "عند التفعيل، فقط القنوات التي علّمت في المفضلة على هذا المولف ستورد إلى النظام.",
|
||||
"ImportMissingEpisodesHelp": "عند التمكين، المعلومات الناقصة للحلقات ستورّد إلى قاعدة بيانات أمبي وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكنبات.",
|
||||
"ImportFavoriteChannelsHelp": "فقط القنوات التي علّمت في المفضلة على جهاز المولف ستورد.",
|
||||
"ImportMissingEpisodesHelp": "المعلومات الناقصة للحلقات ستورّد إلى قاعدة بياناتك وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكتبات.",
|
||||
"LabelAbortedByServerShutdown": "(تم إهماله بسبب عملية إغلاق الخادم)",
|
||||
"LabelAccessDay": "يوم الأسبوع:",
|
||||
"LabelAccessEnd": "تاريخ النهاية",
|
||||
"LabelAccessStart": "تاريخ البداية",
|
||||
"LabelAccessEnd": "وقت النهاية:",
|
||||
"LabelAccessStart": "وقت البداية:",
|
||||
"LabelAirDays": "أيام البث:",
|
||||
"LabelAirTime": "وقت البث:",
|
||||
"LabelAlbum": "الألبوم",
|
||||
"LabelAlbum": "الألبوم:",
|
||||
"LabelAlbumArtHelp": "PN المستخدمة في رسومات الألبوم، داخل سمة dlna:profileID في upnp:albumArtURI. بعض الأجهزة تحتاج قيمة محددة، مهما كان حجم الصورة.",
|
||||
"LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم",
|
||||
"LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم:",
|
||||
"LabelAlbumArtMaxHeightHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.",
|
||||
"LabelAlbumArtMaxWidth": "العرض الأقصى لرسوم الألبوم:",
|
||||
"LabelAlbumArtMaxWidthHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.",
|
||||
@ -310,42 +310,42 @@
|
||||
"LabelAll": "الجميع",
|
||||
"LabelAllowHWTranscoding": "السماح بالتشفير البيني بعتاد الحاسب",
|
||||
"LabelAppName": "اسم التطبيق",
|
||||
"LabelAppNameExample": "مثال: Sickbeard، NzbDrone",
|
||||
"LabelAppNameExample": "مثال: Sickbeard، Sonarr",
|
||||
"LabelArtists": "الفنانون:",
|
||||
"LabelArtistsHelp": "فصل الاستعمالات المتعددة ;",
|
||||
"LabelArtistsHelp": "افصل بين الفنانين ب ; فاصلة منقوطة.",
|
||||
"LabelAudioLanguagePreference": "اللغة المفضلة للصوت:",
|
||||
"LabelBindToLocalNetworkAddress": "إربطه إلى عنوان شبكة محلي:",
|
||||
"LabelBindToLocalNetworkAddressHelp": "هذا خياري. امتطي عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم أمبي.",
|
||||
"LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل (بالثواني)",
|
||||
"LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين يث رسائل قيد التشغيل",
|
||||
"LabelCache": "ذاكرة الكاشة",
|
||||
"LabelCachePath": "مسار ذاكرة الكاشة:",
|
||||
"LabelCachePathHelp": "حدد موقع مخصص لملفات كاشة الخادم، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة التلقائية.",
|
||||
"LabelBindToLocalNetworkAddressHelp": "تجاوز عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم جيلليفن.",
|
||||
"LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل",
|
||||
"LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين بث رسائل قيد التشغيل.",
|
||||
"LabelCache": "مَخبأ (كاش):",
|
||||
"LabelCachePath": "مسار ذاكرة الكاش:",
|
||||
"LabelCachePathHelp": "حدد موقع مخصص لملفات الخادم المؤقتة، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة الافتراضية.",
|
||||
"LabelCancelled": "تم الإلغاء",
|
||||
"LabelCollection": "المجموعة",
|
||||
"LabelCollection": "المجموعة:",
|
||||
"LabelCommunityRating": "تقييم المجتمع:",
|
||||
"LabelContentType": "نوع المحتوى",
|
||||
"LabelContentType": "نوع المحتوى:",
|
||||
"LabelCountry": "البلد:",
|
||||
"LabelCurrentPassword": "كلمة السر الحالية:",
|
||||
"LabelCustomCertificatePath": "مسار شهادة ssl مخصص:",
|
||||
"LabelCustomCertificatePath": "مسار شهادة SSL المخصص:",
|
||||
"LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.",
|
||||
"LabelCustomCss": "تنيسق CSS مخصوص:",
|
||||
"LabelCustomCssHelp": "طبق تنسيق css مخصوصة لواجهة الويب.",
|
||||
"LabelCustomCss": "تنيسق CSS مخصص:",
|
||||
"LabelCustomCssHelp": "طبق تنسيقك css المخصص لواجهة الويب.",
|
||||
"LabelCustomDeviceDisplayName": "اسم العرض:",
|
||||
"LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام",
|
||||
"LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام الاسم المبلغ من الجهاز.",
|
||||
"LabelDateAddedBehavior": "كيف يتصرف المحتوى الجديد نحو \"تاريخ الإضافة\" الخاص به:",
|
||||
"LabelDateAddedBehaviorHelp": "إذا استعرضت قيمة واصفات البيانا فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.",
|
||||
"LabelDateAddedBehaviorHelp": "إذا اخذت واصفات البيانات قيمة، فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.",
|
||||
"LabelDay": "اليوم:",
|
||||
"LabelDeathDate": "تاريخ الوفاة:",
|
||||
"LabelDefaultUser": "المستخدم الافتراضي",
|
||||
"LabelDefaultUser": "المستخدم الافتراضي:",
|
||||
"LabelDefaultUserHelp": "لتحديد مكتبة المستخدم التي تظهر على الأجهزة المتصلة. بإمكان الامتطاء على هذه القيمة لكل جهاز عن طريق عرائض الأجهزة.",
|
||||
"LabelDeviceDescription": "وصف الجهاز",
|
||||
"LabelDidlMode": "طور didl:",
|
||||
"LabelDidlMode": "طور DIDL:",
|
||||
"LabelDisplayMissingEpisodesWithinSeasons": "أظهر الحلقات المفقودة في مجلدات المواسم",
|
||||
"LabelDisplayName": "الاسم المعروض:",
|
||||
"LabelDisplaySpecialsWithinSeasons": "أظهر الحلقات الخاصة في المواسم التي بثت فيها",
|
||||
"LabelDownMixAudioScale": "تعزيز الصوت عند تقليل توزيع قنوات الصوت:",
|
||||
"LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة بـ 1 للمحافظة على القيمة الأصلية للصوت.",
|
||||
"LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة ب 1 للمحافظة على القيمة الأصلية للصوت.",
|
||||
"LabelDownloadLanguages": "إنزال اللغة:",
|
||||
"LabelDynamicExternalId": "معرفة {0}:",
|
||||
"LabelEasyPinCode": "الرمز الشخصي الميسر:",
|
||||
@ -354,8 +354,8 @@
|
||||
"LabelEnableAutomaticPortMap": "فعل الخاصية الآلية في التوفيق بين المنافذ",
|
||||
"LabelEnableAutomaticPortMapHelp": "حاول التوفيق بين المنفذ العالمي والمنفذ المحلي آلياً باستخدام آلية UPnP. هذه الخاصية قد لا تعمل مع بعض أنواع الراوترات.",
|
||||
"LabelEnableBlastAliveMessages": "بث رسائل قيد التشغيل",
|
||||
"LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك",
|
||||
"LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء (بالثواني)",
|
||||
"LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك.",
|
||||
"LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء",
|
||||
"LabelEnableDlnaClientDiscoveryIntervalHelp": "يحدد الفترة بالثواني بين عمليات بحث SSDP التي يقوم بها أمبي.",
|
||||
"LabelEnableDlnaDebugLogging": "تفعيل خاصية كشوفات أخطاء DLNA",
|
||||
"LabelEnableDlnaDebugLoggingHelp": "هذه ستنشئ سجلات كشفية ضخمة ولا ينبغي تفعيلها إلا عند الحاجة إليها بغرض استكشاف الأخطاء وحصرها.",
|
||||
@ -930,7 +930,7 @@
|
||||
"Backdrops": "خلفيات متغيرة للصفحة",
|
||||
"Backdrop": "خلفية متغيرة للصفحة",
|
||||
"Auto": "تلقائي",
|
||||
"AuthProviderHelp": "حدد مقدم المصادقات ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.",
|
||||
"AuthProviderHelp": "اختار مقدم المصادقة ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.",
|
||||
"AroundTime": "حول",
|
||||
"AttributeNew": "جديد",
|
||||
"AspectRatio": "نسبة العرض الى الارتفاع",
|
||||
@ -1038,7 +1038,7 @@
|
||||
"Director": "المخرج",
|
||||
"DirectPlaying": "بث بدون تحويل الصيغة",
|
||||
"DirectStreaming": "البث المباشر",
|
||||
"DirectStreamHelp2": "البث المباشر للملف يستخدم طاقة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.",
|
||||
"DirectStreamHelp2": "البث المباشر للملف يستخدم قوة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.",
|
||||
"DirectStreamHelp1": "الوسائط متوافقة مع الجهاز فيما يتعلق بالدقة ونوع الوسائط (H.264 ، AC3 ، إلخ) ، ولكنها في حاوية ملفات غير متوافقة (mkv ، avi ، wmv ، إلخ). سيتم إعادة حزم الفيديو في الوقت الحقيقي قبل بثه إلى الجهاز.",
|
||||
"DetectingDevices": "يتم الكشف عن الأجهزة",
|
||||
"Desktop": "سطح المكتب",
|
||||
@ -1139,5 +1139,51 @@
|
||||
"Dislike": "لم يعجبنى",
|
||||
"ButtonSyncPlay": "SyncPlay",
|
||||
"ExtraLarge": "كبير جدا",
|
||||
"EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل."
|
||||
"EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل.",
|
||||
"LabelDroppedFrames": "الاطارات الساقطة:",
|
||||
"LabelDropImageHere": "اسقط صورة هنا، او ضغط تصفح.",
|
||||
"LabelDisplayOrder": "ترتيب المعروض:",
|
||||
"LabelDisplayMode": "وضع المعروض:",
|
||||
"LabelDisplayLanguageHelp": "ترجمة جيلليفين هو مشروع مستمر.",
|
||||
"LabelDisplayLanguage": "لغة العرض:",
|
||||
"LabelDiscNumber": "رقم القرص:",
|
||||
"LabelDeinterlaceMethod": "طريقة تقليل التشابك:",
|
||||
"LabelDefaultScreen": "الشاشة الافتراضية:",
|
||||
"LabelDateTimeLocale": "وقت و تاريخ محلي:",
|
||||
"LabelDateAdded": "تاريخ الاضافة:",
|
||||
"LabelCustomRating": "تقييم مخصص:",
|
||||
"LabelCriticRating": "تقييم النقاد:",
|
||||
"LabelCorruptedFrames": "الإطارات التالفة:",
|
||||
"LabelChannels": "القنوات:",
|
||||
"LabelCertificatePasswordHelp": "اذا تطلبت شهادتك الامنية كلمة مرور، من فضلك ادخلها هنا.",
|
||||
"LabelCertificatePassword": "كلمة مرور الشهادة الامنية:",
|
||||
"LabelBurnSubtitles": "الترجمات المحروقة:",
|
||||
"LabelBlockContentWithTags": "احجب العناصر بالعلامات:",
|
||||
"LabelBitrate": "معدل البت:",
|
||||
"LabelBirthYear": "عام الميلاد:",
|
||||
"LabelBirthDate": "تاريخ الميلاد:",
|
||||
"LabelAutomaticallyRefreshInternetMetadataEvery": "حدث وصف البيانات تلقائيا من الانترنت:",
|
||||
"LabelAuthProvider": "مقدم التصديق:",
|
||||
"LabelAudioSampleRate": "سرعة معينة الصوت:",
|
||||
"LabelAudioCodec": "ترميز الصوت:",
|
||||
"LabelAudioChannels": "قنوات الصوت:",
|
||||
"LabelAudioBitrate": "معدل بث الصوت:",
|
||||
"LabelAudioBitDepth": "عمق بث الصوت:",
|
||||
"LabelAudio": "الصوت",
|
||||
"LabelAllowedRemoteAddressesMode": "وضع مرشح عنوان المضيف IP البعيد:",
|
||||
"LabelAllowedRemoteAddresses": "مرشح عنوان المضيف IP البعيد:",
|
||||
"LabelAirsBeforeSeason": "عروض بث قبل الموسم:",
|
||||
"LabelAirsBeforeEpisode": "عروض بث قبل الحلقة:",
|
||||
"LabelAirsAfterSeason": "عروض بث بعد الموسم:",
|
||||
"Label3DFormat": "صيغة ثلاثية الابعاد:",
|
||||
"Kids": "اطفال",
|
||||
"Items": "عناصر",
|
||||
"ItemCount": "{0} عنصر",
|
||||
"InstantMix": "خلط فوري",
|
||||
"HeaderSyncPlayEnabled": "تزامن اللعب ممكَّن",
|
||||
"HeaderSyncPlaySelectGroup": "انضم لمجموعة",
|
||||
"EnableDetailsBannerHelp": "اظهر صوره اللافته اعلى عنصر تفاصيل الصفحة.",
|
||||
"EnableDetailsBanner": "لافتة التفاصيل",
|
||||
"EnableDecodingColorDepth10Vp9": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل VP9",
|
||||
"EnableDecodingColorDepth10Hevc": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل HEVC"
|
||||
}
|
||||
|
@ -1542,5 +1542,8 @@
|
||||
"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:"
|
||||
}
|
||||
|
@ -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 +1544,8 @@
|
||||
"ButtonCast": "Cast",
|
||||
"ButtonPlayer": "Player",
|
||||
"StopPlayback": "Stop playback",
|
||||
"ClearQueue": "Clear queue"
|
||||
"ClearQueue": "Clear queue",
|
||||
"LabelSubtitleVerticalPosition": "Vertical position:",
|
||||
"SubtitleVerticalPositionHelp": "Line number where text appears. Positive numbers indicate top down. Negative numbers indicate bottom up.",
|
||||
"Preview": "Preview"
|
||||
}
|
||||
|
@ -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 :"
|
||||
}
|
||||
|
@ -471,7 +471,7 @@
|
||||
"TabTrailers": "טריילרים",
|
||||
"TabTranscoding": "קידוד",
|
||||
"TabUpcoming": "בקרוב",
|
||||
"Tags": "תגים",
|
||||
"Tags": "מילות מפתח",
|
||||
"TellUsAboutYourself": "ספר לנו על עצמך",
|
||||
"ThisWizardWillGuideYou": "אשף זה יעזור לך בהתליך ההתקנה.",
|
||||
"Thursday": "חמישי",
|
||||
@ -588,7 +588,7 @@
|
||||
"MessageConfirmRestart": "האם אתה בטוח שברצונך לאתחל את שרת ה-Jellyfin?",
|
||||
"HeaderThisUserIsCurrentlyDisabled": "משתמש זה אינו פעיל כרגע",
|
||||
"HeaderTaskTriggers": "טריגרים של המשימה",
|
||||
"HeaderTags": "תגיות",
|
||||
"HeaderTags": "מילות מפתח",
|
||||
"HeaderStopRecording": "עצור הקלטה",
|
||||
"HeaderSortOrder": "סדר מיון",
|
||||
"HeaderSortBy": "מיין לפי",
|
||||
@ -867,5 +867,7 @@
|
||||
"OptionEveryday": "כל יום",
|
||||
"OptionEnableExternalContentInSuggestions": "הפעל תוכן חיצוני בהמלצות",
|
||||
"OptionEnableAccessToAllLibraries": "אפשר גישה לכל הספריות",
|
||||
"OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים"
|
||||
"OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים",
|
||||
"HeaderSyncPlaySelectGroup": "הצטרף לקבוצה",
|
||||
"TabUsers": "משתמשים"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -1542,5 +1542,8 @@
|
||||
"ButtonPlayer": "Проигрыватель",
|
||||
"PreviousTrack": "Перейти к предыдущему",
|
||||
"NextTrack": "Перейти к следующему",
|
||||
"LabelUnstable": "Нестабильная"
|
||||
"LabelUnstable": "Нестабильная",
|
||||
"LabelSubtitleVerticalPosition": "Вертикальная позиция:",
|
||||
"SubtitleVerticalPositionHelp": "Номер строки, где появляется текст. Положительные числа означают сверху вниз. Отрицательные числа означают снизу вверх.",
|
||||
"Preview": "Предварительный просмотр"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
24
yarn.lock
24
yarn.lock
@ -3020,10 +3020,10 @@ css-has-pseudo@^0.10.0:
|
||||
postcss "^7.0.6"
|
||||
postcss-selector-parser "^5.0.0-rc.4"
|
||||
|
||||
css-loader@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.0.tgz#b57efb92ac8f0cd85bf92d89df9634ef1f51b8bf"
|
||||
integrity sha512-ko7a9b0iFpWtk9eSI/C8IICvZeGtYnjxYjw45rJprokXj/+kBd/siX4vAIBq9Uij8Jubc4jL1EvSnTjCEwaHSw==
|
||||
css-loader@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.1.tgz#9f48fd7eae1219d629a3f085ba9a9102ca1141a7"
|
||||
integrity sha512-MoqmF1if7Z0pZIEXA4ZF9PgtCXxWbfzfJM+3p+OYfhcrwcqhaCRb74DSnfzRl7e024xEiCRn5hCvfUbTf2sgFA==
|
||||
dependencies:
|
||||
camelcase "^6.0.0"
|
||||
cssesc "^3.0.0"
|
||||
@ -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