diff --git a/package.json b/package.json index 79bb6170f2..1191639a60 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "fast-text-encoding": "^1.0.3", "flv.js": "^1.5.0", "headroom.js": "^0.11.0", - "hls.js": "^0.14.7", + "hls.js": "^0.14.8", "howler": "^2.2.0", "intersection-observer": "^0.11.0", "jellyfin-apiclient": "^1.4.1", @@ -80,7 +80,7 @@ "sortablejs": "^1.10.2", "swiper": "^5.4.5", "webcomponents.js": "^0.7.24", - "whatwg-fetch": "^3.3.1" + "whatwg-fetch": "^3.4.0" }, "babel": { "presets": [ @@ -125,6 +125,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", @@ -163,6 +165,8 @@ "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", @@ -218,6 +222,7 @@ "src/controllers/dashboard/users/userpasswordpage.js", "src/controllers/dashboard/users/userprofilespage.js", "src/controllers/home.js", + "src/controllers/list.js", "src/controllers/edititemmetadata.js", "src/controllers/favorites.js", "src/controllers/hometab.js", @@ -278,6 +283,7 @@ "src/elements/emby-tabs/emby-tabs.js", "src/elements/emby-textarea/emby-textarea.js", "src/elements/emby-toggle/emby-toggle.js", + "src/libraries/screensavermanager.js", "src/libraries/navdrawer/navdrawer.js", "src/libraries/scroller.js", "src/plugins/backdropScreensaver/plugin.js", diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 37fb66e0d9..df74dc2a16 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -2,6 +2,8 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', 'use strict'; focusManager = focusManager.default || focusManager; + layoutManager = layoutManager.default || layoutManager; + function onSubmit(e) { e.preventDefault(); return false; diff --git a/src/components/guide/guide-settings.js b/src/components/guide/guide-settings.js index a644c9c9b0..b3133ec272 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -1,6 +1,7 @@ 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) { diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 05fa2b608d..c8fd8021e3 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -4,6 +4,7 @@ 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; diff --git a/src/components/itemsrefresher.js b/src/components/itemsrefresher.js index 5ce9a3b6e4..3883e6e490 100644 --- a/src/components/itemsrefresher.js +++ b/src/components/itemsrefresher.js @@ -1,131 +1,130 @@ -define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) { - 'use strict'; +import playbackManager from 'playbackManager'; +import serverNotifications from 'serverNotifications'; +import events from 'events'; - serverNotifications = serverNotifications.default || serverNotifications; - playbackManager = playbackManager.default || playbackManager; +function onUserDataChanged(e, apiClient, userData) { + const instance = this; - function onUserDataChanged(e, apiClient, userData) { - var instance = this; - - var eventsToMonitor = getEventsToMonitor(instance); - - // TODO: Check user data change reason? - if (eventsToMonitor.indexOf('markfavorite') !== -1) { - instance.notifyRefreshNeeded(); - } else if (eventsToMonitor.indexOf('markplayed') !== -1) { - instance.notifyRefreshNeeded(); - } - } - - function getEventsToMonitor(instance) { - var options = instance.options; - var monitor = options ? options.monitorEvents : null; - if (monitor) { - return monitor.split(','); - } - - return []; - } - - function onTimerCreated(e, apiClient, data) { - var instance = this; - - if (getEventsToMonitor(instance).indexOf('timers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onSeriesTimerCreated(e, apiClient, data) { - var instance = this; - if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onTimerCancelled(e, apiClient, data) { - var instance = this; - - if (getEventsToMonitor(instance).indexOf('timers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onSeriesTimerCancelled(e, apiClient, data) { - var instance = this; - if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onLibraryChanged(e, apiClient, data) { - var instance = this; - var eventsToMonitor = getEventsToMonitor(instance); - if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { - // yes this is an assumption - return; - } - - var itemsAdded = data.ItemsAdded || []; - var itemsRemoved = data.ItemsRemoved || []; - if (!itemsAdded.length && !itemsRemoved.length) { - return; - } - - var options = instance.options || {}; - var parentId = options.parentId; - if (parentId) { - var foldersAddedTo = data.FoldersAddedTo || []; - var foldersRemovedFrom = data.FoldersRemovedFrom || []; - var collectionFolders = data.CollectionFolders || []; - - if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { - return; - } - } + const eventsToMonitor = getEventsToMonitor(instance); + // TODO: Check user data change reason? + if (eventsToMonitor.indexOf('markfavorite') !== -1) { + instance.notifyRefreshNeeded(); + } else if (eventsToMonitor.indexOf('markplayed') !== -1) { instance.notifyRefreshNeeded(); } +} - function onPlaybackStopped(e, stopInfo) { - var instance = this; +function getEventsToMonitor(instance) { + const options = instance.options; + const monitor = options ? options.monitorEvents : null; + if (monitor) { + return monitor.split(','); + } - var state = stopInfo.state; + return []; +} - var eventsToMonitor = getEventsToMonitor(instance); - if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { - if (eventsToMonitor.indexOf('videoplayback') !== -1) { - instance.notifyRefreshNeeded(true); - return; - } - } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { - if (eventsToMonitor.indexOf('audioplayback') !== -1) { - instance.notifyRefreshNeeded(true); - return; - } +function onTimerCreated(e, apiClient, data) { + const instance = this; + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onSeriesTimerCreated(e, apiClient, data) { + const instance = this; + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onTimerCancelled(e, apiClient, data) { + const instance = this; + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onSeriesTimerCancelled(e, apiClient, data) { + const instance = this; + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onLibraryChanged(e, apiClient, data) { + const instance = this; + const eventsToMonitor = getEventsToMonitor(instance); + if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { + // yes this is an assumption + return; + } + + const itemsAdded = data.ItemsAdded || []; + const itemsRemoved = data.ItemsRemoved || []; + if (!itemsAdded.length && !itemsRemoved.length) { + return; + } + + const options = instance.options || {}; + const parentId = options.parentId; + if (parentId) { + const foldersAddedTo = data.FoldersAddedTo || []; + const foldersRemovedFrom = data.FoldersRemovedFrom || []; + const collectionFolders = data.CollectionFolders || []; + + if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { + return; } } - function addNotificationEvent(instance, name, handler, owner) { - var localHandler = handler.bind(instance); + instance.notifyRefreshNeeded(); +} + +function onPlaybackStopped(e, stopInfo) { + const instance = this; + + const state = stopInfo.state; + + const eventsToMonitor = getEventsToMonitor(instance); + if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { + if (eventsToMonitor.indexOf('videoplayback') !== -1) { + instance.notifyRefreshNeeded(true); + return; + } + } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { + if (eventsToMonitor.indexOf('audioplayback') !== -1) { + instance.notifyRefreshNeeded(true); + return; + } + } +} + +function addNotificationEvent(instance, name, handler, owner) { + const localHandler = handler.bind(instance); + owner = owner || serverNotifications; + events.on(owner, name, localHandler); + instance['event_' + name] = localHandler; +} + +function removeNotificationEvent(instance, name, owner) { + const handler = instance['event_' + name]; + if (handler) { owner = owner || serverNotifications; - events.on(owner, name, localHandler); - instance['event_' + name] = localHandler; + events.off(owner, name, handler); + instance['event_' + name] = null; } +} - function removeNotificationEvent(instance, name, owner) { - var handler = instance['event_' + name]; - if (handler) { - owner = owner || serverNotifications; - events.off(owner, name, handler); - instance['event_' + name] = null; - } - } - - function ItemsRefresher(options) { +class ItemsRefresher { + constructor(options) { this.options = options || {}; addNotificationEvent(this, 'UserDataChanged', onUserDataChanged); @@ -137,18 +136,18 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager); } - ItemsRefresher.prototype.pause = function () { + pause() { clearRefreshInterval(this, true); this.paused = true; - }; + } - ItemsRefresher.prototype.resume = function (options) { + resume(options) { this.paused = false; - var refreshIntervalEndTime = this.refreshIntervalEndTime; + const refreshIntervalEndTime = this.refreshIntervalEndTime; if (refreshIntervalEndTime) { - var remainingMs = refreshIntervalEndTime - new Date().getTime(); + const remainingMs = refreshIntervalEndTime - new Date().getTime(); if (remainingMs > 0 && !this.needsRefresh) { resetRefreshInterval(this, remainingMs); } else { @@ -162,9 +161,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM } return Promise.resolve(); - }; + } - ItemsRefresher.prototype.refreshItems = function () { + refreshItems() { if (!this.fetchData) { return Promise.resolve(); } @@ -177,15 +176,15 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM this.needsRefresh = false; return this.fetchData().then(onDataFetched.bind(this)); - }; + } - ItemsRefresher.prototype.notifyRefreshNeeded = function (isInForeground) { + notifyRefreshNeeded(isInForeground) { if (this.paused) { this.needsRefresh = true; return; } - var timeout = this.refreshTimeout; + const timeout = this.refreshTimeout; if (timeout) { clearTimeout(timeout); } @@ -195,44 +194,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM } else { this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000); } - }; - - function clearRefreshInterval(instance, isPausing) { - if (instance.refreshInterval) { - clearInterval(instance.refreshInterval); - instance.refreshInterval = null; - - if (!isPausing) { - instance.refreshIntervalEndTime = null; - } - } } - function resetRefreshInterval(instance, intervalMs) { - clearRefreshInterval(instance); - - if (!intervalMs) { - var options = instance.options; - if (options) { - intervalMs = options.refreshIntervalMs; - } - } - - if (intervalMs) { - instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs); - instance.refreshIntervalEndTime = new Date().getTime() + intervalMs; - } - } - - function onDataFetched(result) { - resetRefreshInterval(this); - - if (this.afterRefresh) { - this.afterRefresh(result); - } - } - - ItemsRefresher.prototype.destroy = function () { + destroy() { clearRefreshInterval(this); removeNotificationEvent(this, 'UserDataChanged'); @@ -245,7 +209,42 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM this.fetchData = null; this.options = null; - }; + } +} - return ItemsRefresher; -}); +function clearRefreshInterval(instance, isPausing) { + if (instance.refreshInterval) { + clearInterval(instance.refreshInterval); + instance.refreshInterval = null; + + if (!isPausing) { + instance.refreshIntervalEndTime = null; + } + } +} + +function resetRefreshInterval(instance, intervalMs) { + clearRefreshInterval(instance); + + if (!intervalMs) { + const options = instance.options; + if (options) { + intervalMs = options.refreshIntervalMs; + } + } + + if (intervalMs) { + instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs); + instance.refreshIntervalEndTime = new Date().getTime() + intervalMs; + } +} + +function onDataFetched(result) { + resetRefreshInterval(this); + + if (this.afterRefresh) { + this.afterRefresh(result); + } +} + +export default ItemsRefresher; diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 85d78f8ff4..88a7265f8b 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -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(); diff --git a/src/components/recordingcreator/recordingcreator.js b/src/components/recordingcreator/recordingcreator.js index 9c2d8fcb68..d54dd219a8 100644 --- a/src/components/recordingcreator/recordingcreator.js +++ b/src/components/recordingcreator/recordingcreator.js @@ -1,6 +1,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) { 'use strict'; + layoutManager = layoutManager.default || layoutManager; scrollHelper = scrollHelper.default || scrollHelper; var currentDialog; diff --git a/src/components/recordingcreator/recordingeditor.js b/src/components/recordingcreator/recordingeditor.js index 7117778eb0..e9aca85347 100644 --- a/src/components/recordingcreator/recordingeditor.js +++ b/src/components/recordingcreator/recordingeditor.js @@ -3,6 +3,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c scrollHelper = scrollHelper.default || scrollHelper; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; var currentDialog; var recordingDeleted = false; diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index b5ac4c9a8b..c6c2a123b4 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -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; diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 38728ec6c6..29b2452cbb 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -6,6 +6,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f 'use strict'; browser = browser.default || browser; + layoutManager = layoutManager.default || layoutManager; focusManager = focusManager.default || focusManager; /** diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index d1cea0c490..3a0e139939 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -1,6 +1,7 @@ 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'; + layoutManager = layoutManager.default || layoutManager; focusManager = focusManager.default || focusManager; function onSubmit(e) { diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index b52b911cb9..dfd7f9446c 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -1,428 +1,437 @@ -define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'connectionManager', 'loading', 'focusManager', 'dom', 'apphost', 'emby-select', 'listViewStyle', 'paper-icon-button-light', 'css!./../formdialog', 'material-icons', 'css!./subtitleeditor', 'emby-button', 'flexStyles'], function (dialogHelper, require, layoutManager, globalize, userSettings, connectionManager, loading, focusManager, dom, appHost) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import layoutManager from 'layoutManager'; +import globalize from 'globalize'; +import * as userSettings from 'userSettings'; +import connectionManager from 'connectionManager'; +import loading from 'loading'; +import focusManager from 'focusManager'; +import dom from 'dom'; +import 'emby-select'; +import 'listViewStyle'; +import 'paper-icon-button-light'; +import 'css!./../formdialog'; +import 'material-icons'; +import 'css!./subtitleeditor'; +import 'emby-button'; +import 'flexStyles'; - loading = loading.default || loading; - focusManager = focusManager.default || focusManager; +let currentItem; +let hasChanges; - var currentItem; - var hasChanges; +function downloadRemoteSubtitles(context, id) { + let url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id; - function downloadRemoteSubtitles(context, id) { - var url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id; + let apiClient = connectionManager.getApiClient(currentItem.ServerId); + apiClient.ajax({ - var apiClient = connectionManager.getApiClient(currentItem.ServerId); - apiClient.ajax({ + type: 'POST', + url: apiClient.getUrl(url) - type: 'POST', - url: apiClient.getUrl(url) + }).then(function () { + hasChanges = true; + + import('toast').then(({default: toast}) => { + toast(globalize.translate('MessageDownloadQueued')); + }); + + focusManager.autoFocus(context); + }); +} + +function deleteLocalSubtitle(context, index) { + let msg = globalize.translate('MessageAreYouSureDeleteSubtitles'); + + import('confirm').then(({default: confirm}) => { + confirm({ + + title: globalize.translate('ConfirmDeletion'), + text: msg, + confirmText: globalize.translate('Delete'), + primary: 'delete' }).then(function () { - hasChanges = true; + loading.show(); - require(['toast'], function (toast) { - toast(globalize.translate('MessageDownloadQueued')); - }); + let itemId = currentItem.Id; + let url = 'Videos/' + itemId + '/Subtitles/' + index; - focusManager.autoFocus(context); - }); - } + let apiClient = connectionManager.getApiClient(currentItem.ServerId); - function deleteLocalSubtitle(context, index) { - var msg = globalize.translate('MessageAreYouSureDeleteSubtitles'); + apiClient.ajax({ - require(['confirm'], function (confirm) { - confirm.default({ - - title: globalize.translate('ConfirmDeletion'), - text: msg, - confirmText: globalize.translate('Delete'), - primary: 'delete' + type: 'DELETE', + url: apiClient.getUrl(url) }).then(function () { - loading.show(); - - var itemId = currentItem.Id; - var url = 'Videos/' + itemId + '/Subtitles/' + index; - - var apiClient = connectionManager.getApiClient(currentItem.ServerId); - - apiClient.ajax({ - - type: 'DELETE', - url: apiClient.getUrl(url) - - }).then(function () { - hasChanges = true; - reload(context, apiClient, itemId); - }); + hasChanges = true; + reload(context, apiClient, itemId); }); }); - } + }); +} - function fillSubtitleList(context, item) { - var streams = item.MediaStreams || []; +function fillSubtitleList(context, item) { + let streams = item.MediaStreams || []; - var subs = streams.filter(function (s) { - return s.Type === 'Subtitle'; - }); + let subs = streams.filter(function (s) { + return s.Type === 'Subtitle'; + }); - var html = ''; + let html = ''; - if (subs.length) { - html += '

' + globalize.translate('MySubtitles') + '

'; + if (subs.length) { + html += '

' + globalize.translate('MySubtitles') + '

'; - html += '
'; + html += '
'; - 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 += ''; - - itemHtml += '
'; - - itemHtml += '
'; - itemHtml += s.DisplayTitle || ''; - itemHtml += '
'; - - if (s.Path) { - itemHtml += '
' + (s.Path) + '
'; - } - - itemHtml += ''; - itemHtml += '
'; - - if (!layoutManager.tv) { - if (s.Path) { - itemHtml += ''; - } - } - - itemHtml += ''; - - return itemHtml; - }).join(''); - - html += '
'; - } - - 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 ''; - }); - - 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 += '
'; - } - html += '

' + provider + '

'; - html += '
'; - 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 += ''; + itemHtml += '<' + tagName + ' class="' + className + '" data-index="' + s.Index + '">'; - var bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line'; + itemHtml += ''; - html += '
'; + itemHtml += '
'; - html += '
' + (result.Name) + '
'; - html += '
'; + itemHtml += '
'; + itemHtml += s.DisplayTitle || ''; + itemHtml += '
'; - if (result.Format) { - html += '' + globalize.translate('FormatValue', result.Format) + ''; + if (s.Path) { + itemHtml += '
' + (s.Path) + '
'; } - if (result.DownloadCount != null) { - html += '' + globalize.translate('DownloadsValue', result.DownloadCount) + ''; - } - html += '
'; - - if (result.Comment) { - html += '
' + (result.Comment) + '
'; - } - - if (result.IsHashMatch) { - html += '
' + globalize.translate('PerfectMatch') + '
'; - } - - html += '
'; + itemHtml += ''; + itemHtml += '
'; if (!layoutManager.tv) { - html += ''; + if (s.Path) { + itemHtml += ''; + } } - html += ''; + itemHtml += ''; + + return itemHtml; + }).join(''); + + html += '
'; + } + + 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 ''; + }); + + 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 += ''; + } + html += '

' + provider + '

'; + html += '
'; + lastProvider = provider; } - if (results.length) { - html += '
'; + 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 += ''; + + let bodyClass = result.Comment || result.IsHashMatch ? 'three-line' : 'two-line'; + + html += '
'; + + html += '
' + (result.Name) + '
'; + html += '
'; + + if (result.Format) { + html += '' + globalize.translate('FormatValue', result.Format) + ''; + } + + if (result.DownloadCount != null) { + html += '' + globalize.translate('DownloadsValue', result.DownloadCount) + ''; + } + html += '
'; + + if (result.Comment) { + html += '
' + (result.Comment) + '
'; + } + + if (result.IsHashMatch) { + html += '
' + globalize.translate('PerfectMatch') + '
'; + } + + html += '
'; + + if (!layoutManager.tv) { + html += ''; + } + + html += ''; + } + + if (results.length) { + html += ''; + } + + let elem = context.querySelector('.subtitleResults'); + elem.innerHTML = html; + + loading.hide(); +} + +function searchForSubtitles(context, language) { + userSettings.set('subtitleeditor-language', language); + + loading.show(); + + let apiClient = connectionManager.getApiClient(currentItem.ServerId); + let url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language); + + apiClient.getJSON(url).then(function (results) { + renderSearchResults(context, results); + }); +} + +function reload(context, apiClient, itemId) { + context.querySelector('.noSearchResults').classList.add('hide'); + + function onGetItem(item) { + currentItem = item; + + fillSubtitleList(context, item); + let file = item.Path || ''; + let index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\')); + if (index > -1) { + file = file.substring(index + 1); + } + + if (file) { + context.querySelector('.pathValue').innerHTML = file; + context.querySelector('.originalFile').classList.remove('hide'); + } else { + context.querySelector('.pathValue').innerHTML = ''; + context.querySelector('.originalFile').classList.add('hide'); + } loading.hide(); } - function searchForSubtitles(context, language) { - userSettings.set('subtitleeditor-language', language); + if (typeof itemId === 'string') { + apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem); + } else { + onGetItem(itemId); + } +} - loading.show(); +function onSearchSubmit(e) { + let form = this; - var apiClient = connectionManager.getApiClient(currentItem.ServerId); - var url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language); + let lang = form.querySelector('#selectLanguage', form).value; - apiClient.getJSON(url).then(function (results) { - renderSearchResults(context, results); - }); + searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang); + + e.preventDefault(); + return false; +} + +function onSubtitleListClick(e) { + let btnDelete = dom.parentWithClass(e.target, 'btnDelete'); + if (btnDelete) { + let index = btnDelete.getAttribute('data-index'); + let context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog'); + deleteLocalSubtitle(context, index); + } +} + +function onSubtitleResultsClick(e) { + let subtitleId; + let context; + + let btnOptions = dom.parentWithClass(e.target, 'btnOptions'); + if (btnOptions) { + subtitleId = btnOptions.getAttribute('data-subid'); + context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog'); + showDownloadOptions(btnOptions, context, subtitleId); } - function reload(context, apiClient, itemId) { - context.querySelector('.noSearchResults').classList.add('hide'); + let btnDownload = dom.parentWithClass(e.target, 'btnDownload'); + if (btnDownload) { + subtitleId = btnDownload.getAttribute('data-subid'); + context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog'); + downloadRemoteSubtitles(context, subtitleId); + } +} - function onGetItem(item) { - currentItem = item; +function showDownloadOptions(button, context, subtitleId) { + let items = []; - fillSubtitleList(context, item); - var file = item.Path || ''; - var index = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\')); - if (index > -1) { - file = file.substring(index + 1); + items.push({ + name: globalize.translate('Download'), + id: 'download' + }); + + import('actionsheet').then(({default: actionsheet}) => { + actionsheet.show({ + items: items, + positionTo: button + + }).then(function (id) { + switch (id) { + case 'download': + downloadRemoteSubtitles(context, subtitleId); + break; + default: + break; } + }); + }); +} - if (file) { - context.querySelector('.pathValue').innerHTML = file; - context.querySelector('.originalFile').classList.remove('hide'); - } else { - context.querySelector('.pathValue').innerHTML = ''; - context.querySelector('.originalFile').classList.add('hide'); - } +function centerFocus(elem, horiz, on) { + import('scrollHelper').then(({default: scrollHelper}) => { + let fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); +} - loading.hide(); - } +function showEditorInternal(itemId, serverId, template) { + hasChanges = false; - if (typeof itemId === 'string') { - apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(onGetItem); + let apiClient = connectionManager.getApiClient(serverId); + return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { + let dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; } else { - onGetItem(itemId); - } - } - - function onSearchSubmit(e) { - var form = this; - - var lang = form.querySelector('#selectLanguage', form).value; - - searchForSubtitles(dom.parentWithClass(form, 'formDialogContent'), lang); - - e.preventDefault(); - return false; - } - - function onSubtitleListClick(e) { - var btnDelete = dom.parentWithClass(e.target, 'btnDelete'); - if (btnDelete) { - var index = btnDelete.getAttribute('data-index'); - var context = dom.parentWithClass(btnDelete, 'subtitleEditorDialog'); - deleteLocalSubtitle(context, index); - } - } - - function onSubtitleResultsClick(e) { - var subtitleId; - var context; - - var btnOptions = dom.parentWithClass(e.target, 'btnOptions'); - if (btnOptions) { - subtitleId = btnOptions.getAttribute('data-subid'); - context = dom.parentWithClass(btnOptions, 'subtitleEditorDialog'); - showDownloadOptions(btnOptions, context, subtitleId); + dialogOptions.size = 'small'; } - var btnDownload = dom.parentWithClass(e.target, 'btnDownload'); - if (btnDownload) { - subtitleId = btnDownload.getAttribute('data-subid'); - context = dom.parentWithClass(btnDownload, 'subtitleEditorDialog'); - downloadRemoteSubtitles(context, subtitleId); + let dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + dlg.classList.add('subtitleEditorDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'core'); + + dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File'); + + dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit); + + let btnSubmit = dlg.querySelector('.btnSubmit'); + + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, true); + dlg.querySelector('.btnSearchSubtitles').classList.add('hide'); + } else { + btnSubmit.classList.add('hide'); } - } - function showDownloadOptions(button, context, subtitleId) { - var items = []; + let editorContent = dlg.querySelector('.formDialogContent'); - items.push({ - name: globalize.translate('Download'), - id: 'download' + dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick); + dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick); + + apiClient.getCultures().then(function (languages) { + fillLanguages(editorContent, apiClient, languages); }); - require(['actionsheet'], function (actionsheet) { - actionsheet.show({ - items: items, - positionTo: button - - }).then(function (id) { - switch (id) { - case 'download': - downloadRemoteSubtitles(context, subtitleId); - break; - default: - break; - } - }); + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); }); - } - - function centerFocus(elem, horiz, on) { - require(['scrollHelper'], function (scrollHelper) { - scrollHelper = scrollHelper.default || scrollHelper; - var fn = on ? 'on' : 'off'; - scrollHelper.centerFocus[fn](elem, horiz); - }); - } - - function showEditorInternal(itemId, serverId, template) { - hasChanges = false; - - var apiClient = connectionManager.getApiClient(serverId); - return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { - var dialogOptions = { - removeOnClose: true, - scrollY: false - }; - - if (layoutManager.tv) { - dialogOptions.size = 'fullscreen'; - } else { - dialogOptions.size = 'small'; - } - - var dlg = dialogHelper.createDialog(dialogOptions); - - dlg.classList.add('formDialog'); - dlg.classList.add('subtitleEditorDialog'); - - dlg.innerHTML = globalize.translateHtml(template, 'core'); - - dlg.querySelector('.originalSubtitleFileLabel').innerHTML = globalize.translate('File'); - - dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit); - - var btnSubmit = dlg.querySelector('.btnSubmit'); - - if (layoutManager.tv) { - centerFocus(dlg.querySelector('.formDialogContent'), false, true); - dlg.querySelector('.btnSearchSubtitles').classList.add('hide'); - } else { - btnSubmit.classList.add('hide'); - } - - var editorContent = dlg.querySelector('.formDialogContent'); - - dlg.querySelector('.subtitleList').addEventListener('click', onSubtitleListClick); - dlg.querySelector('.subtitleResults').addEventListener('click', onSubtitleResultsClick); - - apiClient.getCultures().then(function (languages) { - fillLanguages(editorContent, apiClient, languages); - }); - - dlg.querySelector('.btnCancel').addEventListener('click', function () { - dialogHelper.close(dlg); - }); - - return new Promise(function (resolve, reject) { - dlg.addEventListener('close', function () { - if (layoutManager.tv) { - centerFocus(dlg.querySelector('.formDialogContent'), false, false); - } - - if (hasChanges) { - resolve(); - } else { - reject(); - } - }); - - dialogHelper.open(dlg); - - reload(editorContent, apiClient, item); - }); - }); - } - - function showEditor(itemId, serverId) { - loading.show(); return new Promise(function (resolve, reject) { - require(['text!./subtitleeditor.template.html'], function (template) { - showEditorInternal(itemId, serverId, template).then(resolve, reject); - }); - }); - } + dlg.addEventListener('close', function () { + if (layoutManager.tv) { + centerFocus(dlg.querySelector('.formDialogContent'), false, false); + } - return { - show: showEditor - }; -}); + if (hasChanges) { + resolve(); + } else { + reject(); + } + }); + + dialogHelper.open(dlg); + + reload(editorContent, apiClient, item); + }); + }); +} + +function showEditor(itemId, serverId) { + loading.show(); + + return new Promise(function (resolve, reject) { + import('text!./subtitleeditor.template.html').then(({default: template}) => { + showEditorInternal(itemId, serverId, template).then(resolve, reject); + }); + }); +} + +export default { + show: showEditor +}; diff --git a/src/components/subtitlesync/subtitlesync.js b/src/components/subtitlesync/subtitlesync.js index 203d88535f..efb2087a1b 100644 --- a/src/components/subtitlesync/subtitlesync.js +++ b/src/components/subtitlesync/subtitlesync.js @@ -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 '

' + + subtitleSyncSlider.getBubbleHtml = function (value) { + const newOffset = getOffsetFromPercentage(value); + return '

' + (newOffset > 0 ? '+' : '') + parseFloat(newOffset) + 's' + '

'; - }; + }; - 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; diff --git a/src/components/tunerPicker.js b/src/components/tunerPicker.js index 5d597f6654..6b2b1c00e6 100644 --- a/src/components/tunerPicker.js +++ b/src/components/tunerPicker.js @@ -3,6 +3,7 @@ define(['dialogHelper', 'dom', 'layoutManager', 'connectionManager', 'globalize' browser = browser.default || browser; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; focusManager = focusManager.default || focusManager; scrollHelper = scrollHelper.default || scrollHelper; diff --git a/src/components/viewSettings/viewSettings.js b/src/components/viewSettings/viewSettings.js index 9207f8ddbb..ed1629466c 100644 --- a/src/components/viewSettings/viewSettings.js +++ b/src/components/viewSettings/viewSettings.js @@ -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; diff --git a/src/controllers/itemDetails/index.js b/src/controllers/itemDetails/index.js index df2855d69a..b2cdc63234 100644 --- a/src/controllers/itemDetails/index.js +++ b/src/controllers/itemDetails/index.js @@ -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); } diff --git a/src/controllers/list.js b/src/controllers/list.js index 14c6768757..0027c576f1 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -1,12 +1,21 @@ -define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, playbackManager, AlphaPicker) { - 'use strict'; +import globalize from 'globalize'; +import listView from 'listView'; +import layoutManager from 'layoutManager'; +import * as userSettings from 'userSettings'; +import focusManager from 'focusManager'; +import cardBuilder from 'cardBuilder'; +import loading from 'loading'; +import connectionManager from 'connectionManager'; +import AlphaNumericShortcuts from 'alphaNumericShortcuts'; +import playbackManager from 'playbackManager'; +import AlphaPicker from 'alphaPicker'; +import 'emby-itemscontainer'; +import 'emby-scroller'; - playbackManager = playbackManager.default || playbackManager; - loading = loading.default || loading; - focusManager = focusManager.default || focusManager; +/* eslint-disable indent */ function getInitialLiveTvQuery(instance, params) { - var query = { + const query = { UserId: connectionManager.getApiClient(params.serverId).getCurrentUserId(), StartIndex: 0, Fields: 'ChannelInfo,PrimaryImageAspectRatio', @@ -63,7 +72,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function modifyQueryWithFilters(instance, query) { - var sortValues = instance.getSortValues(); + const sortValues = instance.getSortValues(); if (!query.SortBy) { query.SortBy = sortValues.sortBy; @@ -72,9 +81,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' query.Fields = query.Fields ? query.Fields + ',PrimaryImageAspectRatio' : 'PrimaryImageAspectRatio'; query.ImageTypeLimit = 1; - var hasFilters; - var queryFilters = []; - var filters = instance.getFilters(); + let hasFilters; + const queryFilters = []; + const filters = instance.getFilters(); if (filters.IsPlayed) { queryFilters.push('IsPlayed'); @@ -168,21 +177,21 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function updateSortText(instance) { - var btnSortText = instance.btnSortText; + const btnSortText = instance.btnSortText; if (btnSortText) { - var options = instance.getSortMenuOptions(); - var values = instance.getSortValues(); - var sortBy = values.sortBy; + const options = instance.getSortMenuOptions(); + const values = instance.getSortValues(); + const sortBy = values.sortBy; - for (var i = 0, length = options.length; i < length; i++) { - if (sortBy === options[i].value) { - btnSortText.innerHTML = globalize.translate('SortByValue', options[i].name); + for (const option of options) { + if (sortBy === option.value) { + btnSortText.innerHTML = globalize.translate('SortByValue', option.name); break; } } - var btnSortIcon = instance.btnSortIcon; + const btnSortIcon = instance.btnSortIcon; if (btnSortIcon) { setSortButtonIcon(btnSortIcon, values.sortOrder === 'Descending' ? 'arrow_downward' : 'arrow_upward'); @@ -202,10 +211,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' function updateAlphaPickerState(instance, numItems) { if (instance.alphaPicker) { - var alphaPicker = instance.alphaPickerElement; + const alphaPicker = instance.alphaPickerElement; if (alphaPicker) { - var values = instance.getSortValues(); + const values = instance.getSortValues(); if (numItems == null) { numItems = 100; @@ -223,7 +232,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function getItems(instance, params, item, sortBy, startIndex, limit) { - var apiClient = connectionManager.getApiClient(params.serverId); + const apiClient = connectionManager.getApiClient(params.serverId); instance.queryRecursive = false; if (params.type === 'Recordings') { @@ -252,7 +261,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' if (!item) { instance.queryRecursive = true; - var method = 'getItems'; + let method = 'getItems'; if (params.type === 'MusicArtist') { method = 'getArtists'; @@ -275,7 +284,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' if (item.Type === 'Genre' || item.Type === 'MusicGenre' || item.Type === 'Studio' || item.Type === 'Person') { instance.queryRecursive = true; - var query = { + const query = { StartIndex: startIndex, Limit: limit, Fields: 'PrimaryImageAspectRatio,SortName', @@ -324,8 +333,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' return Promise.resolve(null); } - var apiClient = connectionManager.getApiClient(params.serverId); - var itemId = params.genreId || params.musicGenreId || params.studioId || params.personId || params.parentId; + const apiClient = connectionManager.getApiClient(params.serverId); + const itemId = params.genreId || params.musicGenreId || params.studioId || params.personId || params.parentId; if (itemId) { return apiClient.getItem(apiClient.getCurrentUserId(), itemId); @@ -335,9 +344,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function showViewSettingsMenu() { - var instance = this; + const instance = this; - require(['viewSettings'], function (ViewSettings) { + import('viewSettings').then(({default: ViewSettings}) => { new ViewSettings().show({ settingsKey: instance.getSettingsKey(), settings: instance.getViewSettings(), @@ -350,9 +359,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function showFilterMenu() { - var instance = this; + const instance = this; - require(['filterMenu'], function (FilterMenu) { + import('filterMenu').then(({default: FilterMenu}) => { new FilterMenu().show({ settingsKey: instance.getSettingsKey(), settings: instance.getFilters(), @@ -369,9 +378,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function showSortMenu() { - var instance = this; + const instance = this; - require(['sortMenu'], function (SortMenu) { + import('sortMenu').then(({default: SortMenu}) => { new SortMenu().show({ settingsKey: instance.getSettingsKey(), settings: instance.getSortValues(), @@ -387,10 +396,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function onNewItemClick() { - var instance = this; + const instance = this; - require(['playlistEditor'], function (playlistEditor) { - new playlistEditor.showEditor({ + import('playlistEditor').then(({default: playlistEditor}) => { + new playlistEditor({ items: [], serverId: instance.params.serverId }); @@ -398,22 +407,23 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function hideOrShowAll(elems, hide) { - for (var i = 0, length = elems.length; i < length; i++) { + for (const elem of elems) { if (hide) { - elems[i].classList.add('hide'); + elem.classList.add('hide'); } else { - elems[i].classList.remove('hide'); + elem.classList.remove('hide'); } } } function bindAll(elems, eventName, fn) { - for (var i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener(eventName, fn); + for (const elem of elems) { + elem.addEventListener(eventName, fn); } } - function ItemsView(view, params) { +class ItemsView { + constructor(view, params) { function fetchData() { return getItems(self, params, self.currentItem).then(function (result) { if (self.totalItemCount == null) { @@ -426,7 +436,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function getItemsHtml(items) { - var settings = self.getViewSettings(); + const settings = self.getViewSettings(); if (settings.imageType === 'list') { return listView.getListViewHtml({ @@ -434,13 +444,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' }); } - var shape; - var preferThumb; - var preferDisc; - var preferLogo; - var defaultShape; - var item = self.currentItem; - var lines = settings.showTitle ? 2 : 0; + let shape; + let preferThumb; + let preferDisc; + let preferLogo; + let defaultShape; + const item = self.currentItem; + let lines = settings.showTitle ? 2 : 0; if (settings.imageType === 'banner') { shape = 'banner'; @@ -464,7 +474,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' shape = 'autoVertical'; } - var posterOptions = { + let posterOptions = { shape: shape, showTitle: settings.showTitle, showYear: settings.showTitle, @@ -497,19 +507,19 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' lines = 1; } else if (params.type === 'Programs') { lines = settings.showTitle ? 1 : 0; - var showParentTitle = settings.showTitle && params.IsMovie !== 'true'; + const showParentTitle = settings.showTitle && params.IsMovie !== 'true'; if (showParentTitle) { lines++; } - var showAirTime = settings.showTitle && params.type !== 'Recordings'; + const showAirTime = settings.showTitle && params.type !== 'Recordings'; if (showAirTime) { lines++; } - var showYear = settings.showTitle && params.IsMovie === 'true' && params.type === 'Recordings'; + const showYear = settings.showTitle && params.IsMovie === 'true' && params.type === 'Recordings'; if (showYear) { lines++; @@ -542,13 +552,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' function initAlphaPicker() { self.scroller = view.querySelector('.scrollFrameY'); - var alphaPickerElement = self.alphaPickerElement; + const alphaPickerElement = self.alphaPickerElement; alphaPickerElement.classList.add('alphaPicker-fixed-right'); alphaPickerElement.classList.add('focuscontainer-right'); self.itemsContainer.parentNode.classList.add('padded-right-withalphapicker'); - self.alphaPicker = new AlphaPicker.default({ + self.alphaPicker = new AlphaPicker({ element: alphaPickerElement, itemsContainer: layoutManager.tv ? self.itemsContainer : null, itemClass: 'card', @@ -653,7 +663,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function play() { - var currentItem = self.currentItem; + const currentItem = self.currentItem; if (currentItem && !self.hasFilters) { playbackManager.play({ @@ -669,7 +679,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function queue() { - var currentItem = self.currentItem; + const currentItem = self.currentItem; if (currentItem && !self.hasFilters) { playbackManager.queue({ @@ -685,7 +695,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } function shuffle() { - var currentItem = self.currentItem; + const currentItem = self.currentItem; if (currentItem && !self.hasFilters) { playbackManager.shuffle(currentItem); @@ -698,7 +708,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } } - var self = this; + const self = this; self.params = params; this.itemsContainer = view.querySelector('.itemsContainer'); @@ -712,20 +722,17 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' this.itemsContainer.setAttribute('data-refreshinterval', '300000'); } - var i; - var length; - var btnViewSettings = view.querySelectorAll('.btnViewSettings'); + const btnViewSettings = view.querySelectorAll('.btnViewSettings'); - for (i = 0, length = btnViewSettings.length; i < length; i++) { - btnViewSettings[i].addEventListener('click', showViewSettingsMenu.bind(this)); + for (const btnViewSetting of btnViewSettings) { + btnViewSetting.addEventListener('click', showViewSettingsMenu.bind(this)); } - var filterButtons = view.querySelectorAll('.btnFilter'); + const filterButtons = view.querySelectorAll('.btnFilter'); this.filterButtons = filterButtons; - var hasVisibleFilters = this.getVisibleFilters().length; + const hasVisibleFilters = this.getVisibleFilters().length; - for (i = 0, length = filterButtons.length; i < length; i++) { - var btnFilter = filterButtons[i]; + for (const btnFilter of filterButtons) { btnFilter.addEventListener('click', showFilterMenu.bind(this)); if (hasVisibleFilters) { @@ -735,10 +742,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } } - var sortButtons = view.querySelectorAll('.btnSort'); + const sortButtons = view.querySelectorAll('.btnSort'); - for (this.sortButtons = sortButtons, i = 0, length = sortButtons.length; i < length; i++) { - var sortButton = sortButtons[i]; + this.sortButtons = sortButtons; + for (const sortButton of sortButtons) { sortButton.addEventListener('click', showSortMenu.bind(this)); if (params.type !== 'nextup') { @@ -753,7 +760,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' self.itemsContainer.fetchData = fetchData; self.itemsContainer.getItemsHtml = getItemsHtml; view.addEventListener('viewshow', function (e) { - var isRestored = e.detail.isRestored; + const isRestored = e.detail.isRestored; if (!isRestored) { loading.show(); @@ -765,7 +772,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' getItem(params).then(function (item) { setTitle(item); self.currentItem = item; - var refresh = !isRestored; + const refresh = !isRestored; self.itemsContainer.resume({ refresh: refresh }).then(function () { @@ -780,7 +787,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' initAlphaPicker(); } - var itemType = item ? item.Type : null; + const itemType = item ? item.Type : null; if (itemType === 'MusicGenre' || params.type !== 'Programs' && itemType !== 'Channel') { hideOrShowAll(view.querySelectorAll('.btnPlay'), false); @@ -807,18 +814,18 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' bindAll(view.querySelectorAll('.btnShuffle'), 'click', shuffle); } - self.alphaNumericShortcuts = new AlphaNumericShortcuts.default({ + self.alphaNumericShortcuts = new AlphaNumericShortcuts({ itemsContainer: self.itemsContainer }); }); view.addEventListener('viewhide', function (e) { - var itemsContainer = self.itemsContainer; + const itemsContainer = self.itemsContainer; if (itemsContainer) { itemsContainer.pause(); } - var alphaNumericShortcuts = self.alphaNumericShortcuts; + const alphaNumericShortcuts = self.alphaNumericShortcuts; if (alphaNumericShortcuts) { alphaNumericShortcuts.destroy(); @@ -846,8 +853,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' }); } - ItemsView.prototype.getFilters = function () { - var basekey = this.getSettingsKey(); + getFilters() { + const basekey = this.getSettingsKey(); return { IsPlayed: userSettings.getFilter(basekey + '-filter-IsPlayed') === 'true', IsUnplayed: userSettings.getFilter(basekey + '-filter-IsUnplayed') === 'true', @@ -866,39 +873,37 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' HasThemeVideo: userSettings.getFilter(basekey + '-filter-HasThemeVideo'), GenreIds: userSettings.getFilter(basekey + '-filter-GenreIds') }; - }; + } - ItemsView.prototype.getSortValues = function () { - var basekey = this.getSettingsKey(); + getSortValues() { + const basekey = this.getSettingsKey(); return { sortBy: userSettings.getFilter(basekey + '-sortby') || this.getDefaultSortBy(), sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending' }; - }; + } - ItemsView.prototype.getDefaultSortBy = function () { - var params = this.params; - var sortNameOption = this.getNameSortOption(params); + getDefaultSortBy() { + const sortNameOption = this.getNameSortOption(this.params); - if (params.type) { + if (this.params.type) { return sortNameOption.value; } return 'IsFolder,' + sortNameOption.value; - }; + } - ItemsView.prototype.getSortMenuOptions = function () { - var sortBy = []; - var params = this.params; + getSortMenuOptions() { + const sortBy = []; - if (params.type === 'Programs') { + if (this.params.type === 'Programs') { sortBy.push({ name: globalize.translate('AirDate'), value: 'StartDate,SortName' }); } - var option = this.getNameSortOption(params); + let option = this.getNameSortOption(this.params); if (option) { sortBy.push(option); @@ -916,7 +921,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' sortBy.push(option); } - if (params.type !== 'Programs') { + if (this.params.type !== 'Programs') { sortBy.push({ name: globalize.translate('DateAdded'), value: 'DateCreated,SortName' @@ -929,8 +934,8 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' sortBy.push(option); } - if (!params.type) { - option = this.getNameSortOption(params); + if (!this.params.type) { + option = this.getNameSortOption(this.params); sortBy.push({ name: globalize.translate('Folders'), value: 'IsFolder,' + option.value @@ -956,9 +961,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' value: 'Runtime,SortName' }); return sortBy; - }; + } - ItemsView.prototype.getNameSortOption = function (params) { + getNameSortOption(params) { if (params.type === 'Episode') { return { name: globalize.translate('Name'), @@ -970,9 +975,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' name: globalize.translate('Name'), value: 'SortName' }; - }; + } - ItemsView.prototype.getPlayCountSortOption = function () { + getPlayCountSortOption() { if (this.params.type === 'Programs') { return null; } @@ -981,9 +986,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' name: globalize.translate('PlayCount'), value: 'PlayCount,SortName' }; - }; + } - ItemsView.prototype.getDatePlayedSortOption = function () { + getDatePlayedSortOption() { if (this.params.type === 'Programs') { return null; } @@ -992,9 +997,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' name: globalize.translate('DatePlayed'), value: 'DatePlayed,SortName' }; - }; + } - ItemsView.prototype.getCriticRatingSortOption = function () { + getCriticRatingSortOption() { if (this.params.type === 'Programs') { return null; } @@ -1003,18 +1008,18 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' name: globalize.translate('CriticRating'), value: 'CriticRating,SortName' }; - }; + } - ItemsView.prototype.getCommunityRatingSortOption = function () { + getCommunityRatingSortOption() { return { name: globalize.translate('CommunityRating'), value: 'CommunityRating,SortName' }; - }; + } - ItemsView.prototype.getVisibleFilters = function () { - var filters = []; - var params = this.params; + getVisibleFilters() { + const filters = []; + const params = this.params; if (!(params.type === 'nextup')) { if (params.type === 'Programs') { @@ -1038,16 +1043,15 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } return filters; - }; + } - ItemsView.prototype.setFilterStatus = function (hasFilters) { + setFilterStatus(hasFilters) { this.hasFilters = hasFilters; - var filterButtons = this.filterButtons; + const filterButtons = this.filterButtons; if (filterButtons.length) { - for (var i = 0, length = filterButtons.length; i < length; i++) { - var btnFilter = filterButtons[i]; - var bubble = btnFilter.querySelector('.filterButtonBubble'); + for (const btnFilter of filterButtons) { + let bubble = btnFilter.querySelector('.filterButtonBubble'); if (!bubble) { if (!hasFilters) { @@ -1066,10 +1070,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } } } - }; + } - ItemsView.prototype.getFilterMenuOptions = function () { - var params = this.params; + getFilterMenuOptions() { + const params = this.params; return { IsAiring: params.IsAiring, IsMovie: params.IsMovie, @@ -1079,11 +1083,11 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' IsSeries: params.IsSeries, Recursive: this.queryRecursive }; - }; + } - ItemsView.prototype.getVisibleViewSettings = function () { - var item = (this.params, this.currentItem); - var fields = ['showTitle']; + getVisibleViewSettings() { + const item = (this.params, this.currentItem); + const fields = ['showTitle']; if (!item || item.Type !== 'PhotoAlbum' && item.Type !== 'ChannelFolderItem') { fields.push('imageType'); @@ -1091,13 +1095,13 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' fields.push('viewType'); return fields; - }; + } - ItemsView.prototype.getViewSettings = function () { - var basekey = this.getSettingsKey(); - var params = this.params; - var item = this.currentItem; - var showTitle = userSettings.get(basekey + '-showTitle'); + getViewSettings() { + const basekey = this.getSettingsKey(); + const params = this.params; + const item = this.currentItem; + let showTitle = userSettings.get(basekey + '-showTitle'); if (showTitle === 'true') { showTitle = true; @@ -1109,7 +1113,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' showTitle = true; } - var imageType = userSettings.get(basekey + '-imageType'); + let imageType = userSettings.get(basekey + '-imageType'); if (!imageType && params.type === 'nextup') { imageType = 'thumb'; @@ -1121,10 +1125,10 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' imageType: imageType || 'primary', viewType: userSettings.get(basekey + '-viewType') || 'images' }; - }; + } - ItemsView.prototype.getItemTypes = function () { - var params = this.params; + getItemTypes() { + const params = this.params; if (params.type === 'nextup') { return ['Episode']; @@ -1135,12 +1139,12 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } return []; - }; + } - ItemsView.prototype.getSettingsKey = function () { - var values = []; + getSettingsKey() { + const values = []; values.push('items'); - var params = this.params; + const params = this.params; if (params.type) { values.push(params.type); @@ -1197,7 +1201,9 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' } return values.join('-'); - }; + } +} - return ItemsView; -}); +export default ItemsView; + +/* eslint-enable indent */ diff --git a/src/controllers/livetv/livetvsuggested.js b/src/controllers/livetv/livetvsuggested.js index 05e852fd0b..6e560bcd89 100644 --- a/src/controllers/livetv/livetvsuggested.js +++ b/src/controllers/livetv/livetvsuggested.js @@ -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; diff --git a/src/libraries/screensavermanager.js b/src/libraries/screensavermanager.js index 557b31e0f4..5c24ec63d0 100644 --- a/src/libraries/screensavermanager.js +++ b/src/libraries/screensavermanager.js @@ -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; diff --git a/src/scripts/livetvcomponents.js b/src/scripts/livetvcomponents.js index fd1b48d0eb..2f4e71642c 100644 --- a/src/scripts/livetvcomponents.js +++ b/src/scripts/livetvcomponents.js @@ -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; } diff --git a/src/scripts/site.js b/src/scripts/site.js index aa74411af4..d01e2042e6 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -350,6 +350,7 @@ function initClient() { } function getLayoutManager(layoutManager, appHost) { + layoutManager = layoutManager.default || layoutManager; if (appHost.getDefaultLayout) { layoutManager.defaultLayout = appHost.getDefaultLayout(); } diff --git a/src/strings/de.json b/src/strings/de.json index 1c87a95a95..0aa4d2172f 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -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:" } diff --git a/src/strings/fr.json b/src/strings/fr.json index 6d8fb68ecf..20b96f3a52 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -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 :" } diff --git a/src/strings/nl.json b/src/strings/nl.json index 860783b083..36b947855d 100644 --- a/src/strings/nl.json +++ b/src/strings/nl.json @@ -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", diff --git a/src/strings/ro.json b/src/strings/ro.json index f8aa986264..a0f090676b 100644 --- a/src/strings/ro.json +++ b/src/strings/ro.json @@ -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" } diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index 423ee7797c..077ea67804 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -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" } diff --git a/yarn.lock b/yarn.lock index ad55e16176..758ded53fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5465,10 +5465,10 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -hls.js@^0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.7.tgz#47fbd2662b13121ab17c07aea06b1c07828240cf" - integrity sha512-9JY0D9nwMrfQPRWc8/kEJTKK0TYfDTzIs6Xq+gdCvasRxdvQKQ2T76rdueTkS0AsFV6sQlJN0wxbnI44aRvvUA== +hls.js@^0.14.8: + version "0.14.8" + resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.8.tgz#c2c6ca7005524c81eece316c2a4a199258bd0590" + integrity sha512-4fh8k/sl1SmYXsT4Om8AY5fKa5tUUtAxup2sffrSMh5MNk4Kt4FOZxbjqTGL5VwkroY1oJ9twSciNQNFbPA/WQ== dependencies: eventemitter3 "^4.0.3" url-toolkit "^2.1.6" @@ -11994,10 +11994,10 @@ webworkify@^1.5.0: resolved "https://registry.yarnpkg.com/webworkify/-/webworkify-1.5.0.tgz#734ad87a774de6ebdd546e1d3e027da5b8f4a42c" integrity sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g== -whatwg-fetch@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.3.1.tgz#6c1acf37dec176b0fd6bc9a74b616bec2f612935" - integrity sha512-faXTmGDcLuEPBpJwb5LQfyxvubKiE+RlbmmweFGKjvIPFj4uHTTfdtTIkdTRhC6OSH9S9eyYbx8kZ0UEaQqYTA== +whatwg-fetch@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz#e11de14f4878f773fbebcde8871b2c0699af8b30" + integrity sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ== which-module@^1.0.0: version "1.0.0"