From 7e7613d5de732bc5b59164613252b680895fba77 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Fri, 8 May 2020 19:06:44 -0300 Subject: [PATCH 001/155] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2eae7e6933..65bb24e9fc 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -35,6 +35,7 @@ - [Thibault Nocchi](https://github.com/ThibaultNocchi) - [MrTimscampi](https://github.com/MrTimscampi) - [Sarab Singh](https://github.com/sarab97) + - [GuilhermeHideki](https://github.com/GuilhermeHideki) # Emby Contributors From 934797e07409e7e56cbed10f67504eaf4f0c9d41 Mon Sep 17 00:00:00 2001 From: Guilherme Danno Date: Sun, 3 May 2020 20:45:14 -0300 Subject: [PATCH 002/155] feat: add mixins file --- src/styles/_mixins.scss | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/styles/_mixins.scss diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000000..ed23ab0833 --- /dev/null +++ b/src/styles/_mixins.scss @@ -0,0 +1,23 @@ +@mixin background-cover($position) { + background-position: $position; + background-repeat: no-repeat; + background-size: cover; +} + +@mixin circle($size) { + @include square($size); + border-radius: 100%; +} + +@mixin position($position, $top: null, $right: null, $bottom: null, $left: null) { + position: $position; + top: $top; + right: $right; + bottom: $bottom; + left: $left; +} + +@mixin square($size) { + height: $size; + width: $size; +} From 9e92bfaae78b3bd1f1dfdc9ba02ced18729bef95 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sun, 14 Jun 2020 23:37:48 +0300 Subject: [PATCH 003/155] Fix subtitle line spacing --- .../subtitleappearancehelper.js | 71 ++++++------------- src/plugins/htmlVideoPlayer/plugin.js | 2 +- src/plugins/htmlVideoPlayer/style.css | 6 ++ 3 files changed, 30 insertions(+), 49 deletions(-) diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index bec8156aca..503c81472f 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -1,55 +1,30 @@ define([], function () { 'use strict'; - function getTextStyles(settings, isCue) { + function getTextStyles(settings) { var list = []; - if (isCue) { - switch (settings.textSize || '') { - - case 'smaller': - list.push({ name: 'font-size', value: '.5em' }); - break; - case 'small': - list.push({ name: 'font-size', value: '.7em' }); - break; - case 'large': - list.push({ name: 'font-size', value: '1.3em' }); - break; - case 'larger': - list.push({ name: 'font-size', value: '1.72em' }); - break; - case 'extralarge': - list.push({ name: 'font-size', value: '2em' }); - break; - default: - case 'medium': - break; - } - } else { - switch (settings.textSize || '') { - - case 'smaller': - list.push({ name: 'font-size', value: '.8em' }); - break; - case 'small': - list.push({ name: 'font-size', value: 'inherit' }); - break; - case 'larger': - list.push({ name: 'font-size', value: '2em' }); - break; - case 'extralarge': - list.push({ name: 'font-size', value: '2.2em' }); - break; - case 'large': - list.push({ name: 'font-size', value: '1.72em' }); - break; - default: - case 'medium': - list.push({ name: 'font-size', value: '1.36em' }); - break; - } + switch (settings.textSize || '') { + case 'smaller': + list.push({ name: 'font-size', value: '.8em' }); + break; + case 'small': + list.push({ name: 'font-size', value: 'inherit' }); + break; + case 'larger': + list.push({ name: 'font-size', value: '2em' }); + break; + case 'extralarge': + list.push({ name: 'font-size', value: '2.2em' }); + break; + case 'large': + list.push({ name: 'font-size', value: '1.72em' }); + break; + default: + case 'medium': + list.push({ name: 'font-size', value: '1.36em' }); + break; } switch (settings.dropShadow || '') { @@ -122,10 +97,10 @@ define([], function () { return []; } - function getStyles(settings, isCue) { + function getStyles(settings) { return { - text: getTextStyles(settings, isCue), + text: getTextStyles(settings), window: getWindowStyles(settings) }; } diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index cc312bb956..9302612539 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1178,7 +1178,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa document.getElementsByTagName('head')[0].appendChild(styleElem); } - styleElem.innerHTML = getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings(), true), '.htmlvideoplayer'); + styleElem.innerHTML = getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings()), '.htmlvideoplayer'); }); } diff --git a/src/plugins/htmlVideoPlayer/style.css b/src/plugins/htmlVideoPlayer/style.css index b83a7816f5..0f63e72d7e 100644 --- a/src/plugins/htmlVideoPlayer/style.css +++ b/src/plugins/htmlVideoPlayer/style.css @@ -33,6 +33,12 @@ video::-webkit-media-controls { text-shadow: 0.14em 0.14em 0.14em rgba(0, 0, 0, 1); -webkit-font-smoothing: antialiased; font-family: inherit; + line-height: normal; /* Restore value. See -webkit-media-text-track-container 'line-height' */ +} + +.htmlvideoplayer::-webkit-media-text-track-container { + font-size: 170% !important; /* Override element inline style */ + line-height: 50%; /* Child element cannot set line height smaller than its parent has. This allow smaller values for children */ } .htmlvideoplayer-moveupsubtitles::-webkit-media-text-track-display { From 597b4258d9eba3a663082632149511d4500264ce Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 7 Jul 2020 01:06:47 +0300 Subject: [PATCH 004/155] Add subtitle position --- .../subtitleappearancehelper.js | 39 ++++++++-- .../subtitlesettings/subtitlesettings.js | 71 ++++++++++++++++++- .../subtitlesettings.template.html | 53 ++++++++++++++ src/elements/emby-slider/emby-slider.css | 15 ++++ src/elements/emby-slider/emby-slider.js | 10 +++ src/plugins/htmlVideoPlayer/plugin.js | 39 +++++----- src/plugins/htmlVideoPlayer/style.css | 9 ++- src/scripts/settings/userSettings.js | 6 +- src/strings/en-us.json | 5 +- src/strings/ru.json | 5 +- 10 files changed, 219 insertions(+), 33 deletions(-) diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index 503c81472f..e611228d9a 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -1,7 +1,7 @@ define([], function () { 'use strict'; - function getTextStyles(settings) { + function getTextStyles(settings, preview) { var list = []; @@ -89,19 +89,44 @@ define([], function () { break; } + if (!preview) { + const pos = parseInt(settings.verticalPosition); + const lineHeight = 1.35; // FIXME: It is better to read this value from element + const line = Math.abs(pos * lineHeight); + if (pos < 0) { + list.push({ name: 'min-height', value: `${line}em` }); + list.push({ name: 'margin-top', value: '' }); + } else { + list.push({ name: 'min-height', value: '' }); + list.push({ name: 'margin-top', value: `${line}em` }); + } + } + return list; } - function getWindowStyles(settings) { + function getWindowStyles(settings, preview) { + const list = []; - return []; + if (!preview) { + const pos = parseInt(settings.verticalPosition); + if (pos < 0) { + list.push({ name: 'top', value: '' }); + list.push({ name: 'bottom', value: '0' }); + } else { + list.push({ name: 'top', value: '0' }); + list.push({ name: 'bottom', value: '' }); + } + } + + return list; } - function getStyles(settings) { + function getStyles(settings, preview) { return { - text: getTextStyles(settings), - window: getWindowStyles(settings) + text: getTextStyles(settings, preview), + window: getWindowStyles(settings, preview) }; } @@ -117,7 +142,7 @@ define([], function () { function applyStyles(elements, appearanceSettings) { - var styles = getStyles(appearanceSettings); + var styles = getStyles(appearanceSettings, !!elements.preview); if (elements.text) { applyStyleList(styles.text, elements.text); diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index d728360d05..819631184c 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -1,4 +1,4 @@ -define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events) { +define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loading', 'connectionManager', 'subtitleAppearanceHelper', 'dom', 'events', 'layoutManager', 'listViewStyle', 'emby-select', 'emby-input', 'emby-checkbox', 'emby-slider', 'flexStyles'], function (require, globalize, appSettings, appHost, focusManager, loading, connectionManager, subtitleAppearanceHelper, dom, events, layoutManager) { 'use strict'; function populateLanguages(select, languages) { @@ -21,6 +21,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi appearanceSettings.font = context.querySelector('#selectFont').value; appearanceSettings.textBackground = context.querySelector('#inputTextBackground').value; appearanceSettings.textColor = context.querySelector('#inputTextColor').value; + appearanceSettings.verticalPosition = context.querySelector('#sliderVerticalPosition').value; return appearanceSettings; } @@ -47,6 +48,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi context.querySelector('#inputTextBackground').value = appearanceSettings.textBackground || 'transparent'; context.querySelector('#inputTextColor').value = appearanceSettings.textColor || '#ffffff'; context.querySelector('#selectFont').value = appearanceSettings.font || ''; + context.querySelector('#sliderVerticalPosition').value = appearanceSettings.verticalPosition; context.querySelector('#selectSubtitleBurnIn').value = appSettings.get('subtitleburnin') || ''; @@ -134,10 +136,45 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi var elements = { window: view.querySelector('.subtitleappearance-preview-window'), - text: view.querySelector('.subtitleappearance-preview-text') + text: view.querySelector('.subtitleappearance-preview-text'), + preview: true }; subtitleAppearanceHelper.applyStyles(elements, appearanceSettings); + + subtitleAppearanceHelper.applyStyles({ + window: view.querySelector('.subtitleappearance-fullpreview-window'), + text: view.querySelector('.subtitleappearance-fullpreview-text') + }, appearanceSettings); + } + + const subtitlePreviewDelay = 1000; + let subtitlePreviewTimer; + + function showSubtitlePreview(persistent) { + clearTimeout(subtitlePreviewTimer); + + this._fullPreview.classList.remove('subtitleappearance-fullpreview-hide'); + + if (persistent) { + this._refFullPreview++; + } + + if (this._refFullPreview === 0) { + subtitlePreviewTimer = setTimeout(hideSubtitlePreview.bind(this), subtitlePreviewDelay); + } + } + + function hideSubtitlePreview(persistent) { + clearTimeout(subtitlePreviewTimer); + + if (persistent) { + this._refFullPreview--; + } + + if (this._refFullPreview === 0) { + this._fullPreview.classList.add('subtitleappearance-fullpreview-hide'); + } } function embed(options, self) { @@ -162,6 +199,36 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi if (appHost.supports('subtitleappearancesettings')) { options.element.querySelector('.subtitleAppearanceSection').classList.remove('hide'); + + self._fullPreview = options.element.querySelector('.subtitleappearance-fullpreview'); + self._refFullPreview = 0; + + const sliderVerticalPosition = options.element.querySelector('#sliderVerticalPosition'); + sliderVerticalPosition.addEventListener('input', onAppearanceFieldChange); + sliderVerticalPosition.addEventListener('input', () => showSubtitlePreview.call(self)); + + const eventPrefix = window.PointerEvent ? 'pointer' : 'mouse'; + sliderVerticalPosition.addEventListener(`${eventPrefix}enter`, () => showSubtitlePreview.call(self, true)); + sliderVerticalPosition.addEventListener(`${eventPrefix}leave`, () => hideSubtitlePreview.call(self, true)); + + if (layoutManager.tv) { + sliderVerticalPosition.addEventListener('focus', () => showSubtitlePreview.call(self, true)); + sliderVerticalPosition.addEventListener('blur', () => hideSubtitlePreview.call(self, true)); + + // Give CustomElements time to attach + setTimeout(() => { + sliderVerticalPosition.classList.add('focusable'); + sliderVerticalPosition.enableKeyboardDragging(); + }, 0); + } + + options.element.querySelector('.chkPreview').addEventListener('change', (e) => { + if (e.target.checked) { + showSubtitlePreview.call(self, true); + } else { + hideSubtitlePreview.call(self, true); + } + }); } self.loadData(); diff --git a/src/components/subtitlesettings/subtitlesettings.template.html b/src/components/subtitlesettings/subtitlesettings.template.html index 716296a257..2f49fa4c3e 100644 --- a/src/components/subtitlesettings/subtitlesettings.template.html +++ b/src/components/subtitlesettings/subtitlesettings.template.html @@ -38,6 +38,45 @@ ${HeaderSubtitleAppearance} + + +
+
+
+ ${HeaderSubtitleAppearance} +
+ ${TheseSettingsAffectSubtitlesOnThisDevice} +
+
+
+
@@ -89,6 +128,20 @@
+ +
+
+ +
+
${SubtitleVerticalPositionHelp}
+
+ +
+ +
'; @@ -56,11 +74,11 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateUserInHeader(user) { - var hasImage; + let hasImage; if (user && user.name) { if (user.imageUrl) { - var url = user.imageUrl; + const url = user.imageUrl; updateHeaderUserButton(url); hasImage = true; } @@ -87,9 +105,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' headerCastButton.classList.remove('hide'); } - var policy = user.Policy ? user.Policy : user.localUser.Policy; + const policy = user.Policy ? user.Policy : user.localUser.Policy; - var apiClient = getCurrentApiClient(); + const apiClient = getCurrentApiClient(); if (headerSyncButton && policy && policy.SyncPlayAccess !== 'None' && apiClient.isMinServerVersion('10.6.0')) { headerSyncButton.classList.remove('hide'); } @@ -139,7 +157,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' mainDrawerButton.addEventListener('click', toggleMainDrawer); } - var headerBackButton = skinHeader.querySelector('.headerBackButton'); + const headerBackButton = skinHeader.querySelector('.headerBackButton'); if (headerBackButton) { headerBackButton.addEventListener('click', onBackClick); @@ -181,7 +199,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function onCastButtonClicked() { - var btn = this; + const btn = this; require(['playerSelectionMenu'], function (playerSelectionMenu) { playerSelectionMenu.show(btn); @@ -189,12 +207,12 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function onSyncButtonClicked() { - var btn = this; + const btn = this; groupSelectionMenu.show(btn); } function onSyncPlayEnabled(event, enabled) { - var icon = headerSyncButton.querySelector('span'); + const icon = headerSyncButton.querySelector('span'); icon.classList.remove('sync', 'sync_disabled', 'sync_problem'); if (enabled) { icon.classList.add('sync'); @@ -204,7 +222,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function onSyncPlaySyncing(event, is_syncing, syncMethod) { - var icon = headerSyncButton.querySelector('span'); + const icon = headerSyncButton.querySelector('span'); icon.classList.remove('sync', 'sync_disabled', 'sync_problem'); if (is_syncing) { icon.classList.add('sync_problem'); @@ -250,7 +268,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function refreshLibraryInfoInDrawer(user, drawer) { - var html = ''; + let html = ''; html += '
'; html += '' + globalize.translate('ButtonHome') + ''; @@ -286,12 +304,12 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' // add buttons to navigation drawer navDrawerScrollContainer.innerHTML = html; - var btnSettings = navDrawerScrollContainer.querySelector('.btnSettings'); + const btnSettings = navDrawerScrollContainer.querySelector('.btnSettings'); if (btnSettings) { btnSettings.addEventListener('click', onSettingsClick); } - var btnLogout = navDrawerScrollContainer.querySelector('.btnLogout'); + const btnLogout = navDrawerScrollContainer.querySelector('.btnLogout'); if (btnLogout) { btnLogout.addEventListener('click', onLogoutClick); } @@ -313,20 +331,20 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateDashboardMenuSelectedItem() { - var links = navDrawerScrollContainer.querySelectorAll('.navMenuOption'); - var currentViewId = viewManager.currentView().id; + const links = navDrawerScrollContainer.querySelectorAll('.navMenuOption'); + const currentViewId = viewManager.currentView().id; - for (var i = 0, length = links.length; i < length; i++) { - var link = links[i]; - var selected = false; - var pageIds = link.getAttribute('data-pageids'); + for (let i = 0, length = links.length; i < length; i++) { + let link = links[i]; + let selected = false; + let pageIds = link.getAttribute('data-pageids'); if (pageIds) { pageIds = pageIds.split('|'); selected = -1 != pageIds.indexOf(currentViewId); } - var pageUrls = link.getAttribute('data-pageurls'); + let pageUrls = link.getAttribute('data-pageurls'); if (pageUrls) { pageUrls = pageUrls.split('|'); @@ -335,7 +353,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' if (selected) { link.classList.add('navMenuOption-selected'); - var title = ''; + let title = ''; link = link.querySelector('.navMenuOptionText') || link; title += (link.innerText || link.textContent).trim(); LibraryMenu.setTitle(title); @@ -346,7 +364,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function createToolsMenuList(pluginItems) { - var links = [{ + const links = [{ name: globalize.translate('TabServer') }, { name: globalize.translate('TabDashboard'), @@ -458,8 +476,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function addPluginPagesToMainMenu(links, pluginItems, section) { - for (var i = 0, length = pluginItems.length; i < length; i++) { - var pluginItem = pluginItems[i]; + for (let i = 0, length = pluginItems.length; i < length; i++) { + const pluginItem = pluginItems[i]; if (pluginItem.EnableInMainMenu && pluginItem.MenuSection === section) { links.push({ @@ -479,10 +497,10 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function getToolsLinkHtml(item) { - var menuHtml = ''; - var pageIds = item.pageIds ? item.pageIds.join('|') : ''; + let menuHtml = ''; + let pageIds = item.pageIds ? item.pageIds.join('|') : ''; pageIds = pageIds ? ' data-pageids="' + pageIds + '"' : ''; - var pageUrls = item.pageUrls ? item.pageUrls.join('|') : ''; + let pageUrls = item.pageUrls ? item.pageUrls.join('|') : ''; pageUrls = pageUrls ? ' data-pageurls="' + pageUrls + '"' : ''; menuHtml += ''; @@ -498,11 +516,11 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' function getToolsMenuHtml(apiClient) { return getToolsMenuLinks(apiClient).then(function (items) { - var item; - var menuHtml = ''; + let item; + let menuHtml = ''; menuHtml += '
'; - for (var i = 0; i < items.length; i++) { + for (let i = 0; i < items.length; i++) { item = items[i]; if (item.href) { @@ -520,7 +538,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' function createDashboardMenu(apiClient) { return getToolsMenuHtml(apiClient).then(function (toolsMenuHtml) { - var html = ''; + let html = ''; html += ''; @@ -531,24 +549,24 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function onSidebarLinkClick() { - var section = this.getElementsByClassName('sectionName')[0]; - var text = section ? section.innerHTML : this.innerHTML; + const section = this.getElementsByClassName('sectionName')[0]; + const text = section ? section.innerHTML : this.innerHTML; LibraryMenu.setTitle(text); } function getUserViews(apiClient, userId) { return apiClient.getUserViews({}, userId).then(function (result) { - var items = result.Items; - var list = []; + const items = result.Items; + const list = []; - for (var i = 0, length = items.length; i < length; i++) { - var view = items[i]; + for (let i = 0, length = items.length; i < length; i++) { + const view = items[i]; list.push(view); if ('livetv' == view.CollectionType) { view.ImageTags = {}; view.icon = 'live_tv'; - var guideView = Object.assign({}, view); + const guideView = Object.assign({}, view); guideView.Name = globalize.translate('ButtonGuide'); guideView.ImageTags = {}; guideView.icon = 'dvr'; @@ -562,7 +580,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function showBySelector(selector, show) { - var elem = document.querySelector(selector); + const elem = document.querySelector(selector); if (elem) { if (show) { @@ -592,17 +610,17 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' showBySelector('.libraryMenuDownloads', false); } - var userId = Dashboard.getCurrentUserId(); - var apiClient = getCurrentApiClient(); - var libraryMenuOptions = document.querySelector('.libraryMenuOptions'); + const userId = Dashboard.getCurrentUserId(); + const apiClient = getCurrentApiClient(); + const libraryMenuOptions = document.querySelector('.libraryMenuOptions'); if (libraryMenuOptions) { getUserViews(apiClient, userId).then(function (result) { - var items = result; - var html = `

${globalize.translate('HeaderMedia')}

`; + const items = result; + let html = `

${globalize.translate('HeaderMedia')}

`; html += items.map(function (i) { - var icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType); - var itemId = i.Id; + const icon = i.icon || imageHelper.getLibraryIcon(i.CollectionType); + const itemId = i.Id; const linkHtml = ` @@ -612,8 +630,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' return linkHtml; }).join(''); libraryMenuOptions.innerHTML = html; - var elem = libraryMenuOptions; - var sidebarLinks = elem.querySelectorAll('.navMenuOption'); + const elem = libraryMenuOptions; + const sidebarLinks = elem.querySelectorAll('.navMenuOption'); for (const sidebarLink of sidebarLinks) { sidebarLink.removeEventListener('click', onSidebarLinkClick); @@ -642,9 +660,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateCastIcon() { - var context = document; - var info = playbackManager.getPlayerInfo(); - var icon = headerCastButton.querySelector('.material-icons'); + const context = document; + const info = playbackManager.getPlayerInfo(); + const icon = headerCastButton.querySelector('.material-icons'); icon.classList.remove('cast_connected', 'cast'); @@ -660,18 +678,16 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateLibraryNavLinks(page) { - var i; - var length; - var isLiveTvPage = page.classList.contains('liveTvPage'); - var isChannelsPage = page.classList.contains('channelsPage'); - var isEditorPage = page.classList.contains('metadataEditorPage'); - var isMySyncPage = page.classList.contains('mySyncPage'); - var id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains('allLibraryPage') ? '' : getTopParentId() || ''; - var elems = document.getElementsByClassName('lnkMediaFolder'); + const isLiveTvPage = page.classList.contains('liveTvPage'); + const isChannelsPage = page.classList.contains('channelsPage'); + const isEditorPage = page.classList.contains('metadataEditorPage'); + const isMySyncPage = page.classList.contains('mySyncPage'); + const id = isLiveTvPage || isChannelsPage || isEditorPage || isMySyncPage || page.classList.contains('allLibraryPage') ? '' : getTopParentId() || ''; + const elems = document.getElementsByClassName('lnkMediaFolder'); - for (var i = 0, length = elems.length; i < length; i++) { - var lnkMediaFolder = elems[i]; - var itemId = lnkMediaFolder.getAttribute('data-itemid'); + for (let i = 0, length = elems.length; i < length; i++) { + const lnkMediaFolder = elems[i]; + const itemId = lnkMediaFolder.getAttribute('data-itemid'); if (isChannelsPage && 'channels' === itemId) { lnkMediaFolder.classList.add('navMenuOption-selected'); @@ -692,7 +708,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateMenuForPageType(isDashboardPage, isLibraryPage) { - var newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3; + const newPageType = isDashboardPage ? 2 : isLibraryPage ? 1 : 3; if (currentPageType !== newPageType) { currentPageType = newPageType; @@ -703,7 +719,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' skinHeader.classList.remove('headroomDisabled'); } - var bodyClassList = document.body.classList; + const bodyClassList = document.body.classList; if (isLibraryPage) { bodyClassList.add('libraryDocument'); @@ -740,7 +756,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function updateTitle(page) { - var title = page.getAttribute('data-title'); + const title = page.getAttribute('data-title'); if (title) { LibraryMenu.setTitle(title); @@ -765,7 +781,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' function initHeadRoom(elem) { require(['headroom'], function (Headroom) { - var headroom = new Headroom(elem); + const headroom = new Headroom(elem); headroom.init(); }); } @@ -785,7 +801,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } function getNavDrawerOptions() { - var drawerWidth = screen.availWidth - 50; + let drawerWidth = screen.availWidth - 50; drawerWidth = Math.max(drawerWidth, 240); drawerWidth = Math.min(drawerWidth, 320); return { @@ -816,25 +832,25 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' }); } - var navDrawerElement; - var navDrawerScrollContainer; - var navDrawerInstance; - var mainDrawerButton; - var headerHomeButton; - var currentDrawerType; - var pageTitleElement; - var headerBackButton; - var headerUserButton; - var currentUser; - var headerCastButton; - var headerSearchButton; - var headerAudioPlayerButton; - var headerSyncButton; - var enableLibraryNavDrawer = layoutManager.desktop; - var enableLibraryNavDrawerHome = !layoutManager.tv; - var skinHeader = document.querySelector('.skinHeader'); - var requiresUserRefresh = true; - window.LibraryMenu = { + let navDrawerElement; + let navDrawerScrollContainer; + let navDrawerInstance; + let mainDrawerButton; + let headerHomeButton; + let currentDrawerType; + let pageTitleElement; + let headerBackButton; + let headerUserButton; + let currentUser; + let headerCastButton; + let headerSearchButton; + let headerAudioPlayerButton; + let headerSyncButton; + const enableLibraryNavDrawer = layoutManager.desktop; + const enableLibraryNavDrawerHome = !layoutManager.tv; + const skinHeader = document.querySelector('.skinHeader'); + let requiresUserRefresh = true; + const LibraryMenu = { getTopParentId: getTopParentId, onHardwareMenuButtonClick: function () { toggleMainDrawer(); @@ -873,7 +889,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' title = ''; } - var html = title; + const html = title; if (!pageTitleElement) { pageTitleElement = document.querySelector('.pageTitle'); @@ -896,18 +912,18 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } } }; - var currentPageType; + let currentPageType; pageClassOn('pagebeforeshow', 'page', function (e) { if (!this.classList.contains('withTabs')) { LibraryMenu.setTabs(null); } }); pageClassOn('pageshow', 'page', function (e) { - var page = this; - var isDashboardPage = page.classList.contains('type-interior'); - var isHomePage = page.classList.contains('homePage'); - var isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage'); - var apiClient = getCurrentApiClient(); + const page = this; + const isDashboardPage = page.classList.contains('type-interior'); + const isHomePage = page.classList.contains('homePage'); + const isLibraryPage = !isDashboardPage && page.classList.contains('libraryPage'); + const apiClient = getCurrentApiClient(); if (isDashboardPage) { if (mainDrawerButton) { @@ -944,7 +960,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' renderHeader(); events.on(connectionManager, 'localusersignedin', function (e, user) { - var currentApiClient = connectionManager.getApiClient(user.ServerId); + const currentApiClient = connectionManager.getApiClient(user.ServerId); currentDrawerType = null; currentUser = { @@ -968,5 +984,9 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' events.on(syncPlayManager, 'syncing', onSyncPlaySyncing); loadNavDrawer(); - return LibraryMenu; -}); + + window.LibraryMenu = LibraryMenu; + +export default LibraryMenu; + +/* eslint-enable indent */ From 00a545dae350daf20b671ee184ebb9f7e5430534 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 28 Jul 2020 22:09:56 +0100 Subject: [PATCH 013/155] extraction of functions --- src/scripts/libraryMenu.js | 129 ++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 86a8d38079..594bb79f20 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -850,74 +850,74 @@ import 'flexStyles'; const enableLibraryNavDrawerHome = !layoutManager.tv; const skinHeader = document.querySelector('.skinHeader'); let requiresUserRefresh = true; - const LibraryMenu = { - getTopParentId: getTopParentId, - onHardwareMenuButtonClick: function () { - toggleMainDrawer(); - }, - setTabs: function (type, selectedIndex, builder) { - require(['mainTabsManager'], function (mainTabsManager) { - if (type) { - mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { - return []; - }); - } else { - mainTabsManager.setTabs(null); - } - }); - }, - setDefaultTitle: function () { - if (!pageTitleElement) { - pageTitleElement = document.querySelector('.pageTitle'); - } - if (pageTitleElement) { - pageTitleElement.classList.add('pageTitleWithLogo'); - pageTitleElement.classList.add('pageTitleWithDefaultLogo'); - pageTitleElement.style.backgroundImage = null; - pageTitleElement.innerHTML = ''; - } - - document.title = 'Jellyfin'; - }, - setTitle: function (title) { - if (null == title) { - return void LibraryMenu.setDefaultTitle(); - } - - if ('-' === title) { - title = ''; - } - - const html = title; - - if (!pageTitleElement) { - pageTitleElement = document.querySelector('.pageTitle'); - } - - if (pageTitleElement) { - pageTitleElement.classList.remove('pageTitleWithLogo'); - pageTitleElement.classList.remove('pageTitleWithDefaultLogo'); - pageTitleElement.style.backgroundImage = null; - pageTitleElement.innerHTML = html || ''; - } - - document.title = title || 'Jellyfin'; - }, - setTransparentMenu: function (transparent) { - if (transparent) { - skinHeader.classList.add('semiTransparent'); + function setTabs (type, selectedIndex, builder) { + require(['mainTabsManager'], function (mainTabsManager) { + if (type) { + mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { + return []; + }); } else { - skinHeader.classList.remove('semiTransparent'); + mainTabsManager.setTabs(null); } + }); + } + + function setDefaultTitle () { + if (!pageTitleElement) { + pageTitleElement = document.querySelector('.pageTitle'); } - }; + + if (pageTitleElement) { + pageTitleElement.classList.add('pageTitleWithLogo'); + pageTitleElement.classList.add('pageTitleWithDefaultLogo'); + pageTitleElement.style.backgroundImage = null; + pageTitleElement.innerHTML = ''; + } + + document.title = 'Jellyfin'; + } + + function setTitle (title) { + if (null == title) { + return void LibraryMenu.setDefaultTitle(); + } + + if ('-' === title) { + title = ''; + } + + const html = title; + + if (!pageTitleElement) { + pageTitleElement = document.querySelector('.pageTitle'); + } + + if (pageTitleElement) { + pageTitleElement.classList.remove('pageTitleWithLogo'); + pageTitleElement.classList.remove('pageTitleWithDefaultLogo'); + pageTitleElement.style.backgroundImage = null; + pageTitleElement.innerHTML = html || ''; + } + + document.title = title || 'Jellyfin'; + } + + function setTransparentMenu (transparent) { + if (transparent) { + skinHeader.classList.add('semiTransparent'); + } else { + skinHeader.classList.remove('semiTransparent'); + } + } + let currentPageType; pageClassOn('pagebeforeshow', 'page', function (e) { if (!this.classList.contains('withTabs')) { LibraryMenu.setTabs(null); } }); + pageClassOn('pageshow', 'page', function (e) { const page = this; const isDashboardPage = page.classList.contains('type-interior'); @@ -974,10 +974,12 @@ import 'flexStyles'; updateUserInHeader(user); }); }); + events.on(connectionManager, 'localusersignedout', function () { currentUser = {}; updateUserInHeader(); }); + events.on(playbackManager, 'playerchange', updateCastIcon); events.on(syncPlayManager, 'enabled', onSyncPlayEnabled); @@ -985,6 +987,17 @@ import 'flexStyles'; loadNavDrawer(); + const LibraryMenu = { + getTopParentId: getTopParentId, + onHardwareMenuButtonClick: function () { + toggleMainDrawer(); + }, + setTabs: setTabs, + setDefaultTitle: setDefaultTitle, + setTitle: setTitle, + setTransparentMenu: setTransparentMenu + }; + window.LibraryMenu = LibraryMenu; export default LibraryMenu; From 215a623a18cf859414cadb4051fefbce7051812e Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 28 Jul 2020 22:13:49 +0100 Subject: [PATCH 014/155] remove require --- src/scripts/libraryMenu.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 594bb79f20..f010fa15e3 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -64,7 +64,7 @@ import 'flexStyles'; } function lazyLoadViewMenuBarImages() { - require(['imageLoader'], function (imageLoader) { + import('imageLoader').then(({default: imageLoader}) => { imageLoader.lazyChildren(skinHeader); }); } @@ -201,7 +201,7 @@ import 'flexStyles'; function onCastButtonClicked() { const btn = this; - require(['playerSelectionMenu'], function (playerSelectionMenu) { + import('playerSelectionMenu').then(({default: playerSelectionMenu}) => { playerSelectionMenu.show(btn); }); } @@ -780,7 +780,7 @@ import 'flexStyles'; } function initHeadRoom(elem) { - require(['headroom'], function (Headroom) { + import('headroom').then(({default: Headroom}) => { const headroom = new Headroom(elem); headroom.init(); }); @@ -820,7 +820,7 @@ import 'flexStyles'; navDrawerScrollContainer = navDrawerElement.querySelector('.scrollContainer'); navDrawerScrollContainer.addEventListener('click', onMainDrawerClick); return new Promise(function (resolve, reject) { - require(['navdrawer'], function (navdrawer) { + import('navdrawer').then(({default: navdrawer}) => { navDrawerInstance = new navdrawer(getNavDrawerOptions()); if (!layoutManager.tv) { @@ -852,7 +852,7 @@ import 'flexStyles'; let requiresUserRefresh = true; function setTabs (type, selectedIndex, builder) { - require(['mainTabsManager'], function (mainTabsManager) { + import('mainTabsManager').then(({default: mainTabsManager}) => { if (type) { mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { return []; From 6ef3be136a1e2f98e0d292cfdc108b94eb3eb470 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Thu, 30 Jul 2020 23:36:52 +0300 Subject: [PATCH 015/155] Add parseInt radix --- src/components/subtitlesettings/subtitleappearancehelper.js | 4 ++-- src/plugins/htmlVideoPlayer/plugin.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/subtitlesettings/subtitleappearancehelper.js b/src/components/subtitlesettings/subtitleappearancehelper.js index 4b8b7aed2a..904c018bfc 100644 --- a/src/components/subtitlesettings/subtitleappearancehelper.js +++ b/src/components/subtitlesettings/subtitleappearancehelper.js @@ -89,7 +89,7 @@ function getTextStyles(settings, preview) { } if (!preview) { - const pos = parseInt(settings.verticalPosition); + const pos = parseInt(settings.verticalPosition, 10); const lineHeight = 1.35; // FIXME: It is better to read this value from element const line = Math.abs(pos * lineHeight); if (pos < 0) { @@ -108,7 +108,7 @@ function getWindowStyles(settings, preview) { const list = []; if (!preview) { - const pos = parseInt(settings.verticalPosition); + const pos = parseInt(settings.verticalPosition, 10); if (pos < 0) { list.push({ name: 'top', value: '' }); list.push({ name: 'bottom', value: '0' }); diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index 77e3253641..7ef35adf41 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1200,7 +1200,7 @@ function tryRemoveElement(elem) { console.debug(`downloaded ${data.TrackEvents.length} track events`); const subtitleAppearance = userSettings.getSubtitleAppearanceSettings(); - const cueLine = parseInt(subtitleAppearance.verticalPosition); + const cueLine = parseInt(subtitleAppearance.verticalPosition, 10); // add some cues to show the text // in safari, the cues need to be added before setting the track mode to showing From 997054ab12572c52000cc1ec0186bfbf4f609679 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 31 Jul 2020 15:54:47 +0100 Subject: [PATCH 016/155] remove require This reverts commit bef8a4509d0b151e7a2e85f549bfca49ce952341. --- src/controllers/home.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/controllers/home.js b/src/controllers/home.js index f3e9e8e22b..392da0e2d2 100644 --- a/src/controllers/home.js +++ b/src/controllers/home.js @@ -1,6 +1,5 @@ import TabbedView from 'tabbedView'; import globalize from 'globalize'; -import require from 'require'; import 'emby-tabs'; import 'emby-button'; import 'emby-scroller'; @@ -17,30 +16,24 @@ function getDefaultTabIndex() { return 0; } -function getRequirePromise(deps) { - return new Promise(function (resolve, reject) { - require(deps, resolve); - }); -} - function getTabController(index) { if (null == index) { throw new Error('index cannot be null'); } - const depends = []; + let depends = ''; switch (index) { case 0: - depends.push('controllers/hometab'); + depends = 'controllers/hometab'; break; case 1: - depends.push('controllers/favorites'); + depends = 'controllers/favorites'; } const instance = this; - return getRequirePromise(depends).then(function (controllerFactory) { + return import(depends).then(({ default: controllerFactory }) => { let controller = instance.tabControllers[index]; if (!controller) { From 39627f364a52bd1103ca72496a3a3a4e1fd0340e Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 31 Jul 2020 16:12:49 +0100 Subject: [PATCH 017/155] Migration of tabbedview to ES6 module --- package.json | 1 + src/components/tabbedview/tabbedview.js | 60 +++++++++++++------------ 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 2fd4b90ce5..07ac3f4b54 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "src/components/syncPlay/playbackPermissionManager.js", "src/components/syncPlay/syncPlayManager.js", "src/components/syncPlay/timeSyncManager.js", + "src/components/tabbedview/tabbedview.js", "src/controllers/session/addServer/index.js", "src/controllers/session/forgotPassword/index.js", "src/controllers/session/redeemPassword/index.js", diff --git a/src/components/tabbedview/tabbedview.js b/src/components/tabbedview/tabbedview.js index 8bd3afd372..710a0e3c40 100644 --- a/src/components/tabbedview/tabbedview.js +++ b/src/components/tabbedview/tabbedview.js @@ -1,30 +1,33 @@ -define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (backdrop, mainTabsManager, layoutManager) { - 'use strict'; +import backdrop from 'backdrop'; +import * as mainTabsManager from 'mainTabsManager'; +import layoutManager from 'layoutManager'; +import 'emby-tabs'; - function onViewDestroy(e) { - var tabControllers = this.tabControllers; +function onViewDestroy(e) { + var tabControllers = this.tabControllers; - if (tabControllers) { - tabControllers.forEach(function (t) { - if (t.destroy) { - t.destroy(); - } - }); + if (tabControllers) { + tabControllers.forEach(function (t) { + if (t.destroy) { + t.destroy(); + } + }); - this.tabControllers = null; - } - - this.view = null; - this.params = null; - this.currentTabController = null; - this.initialTabIndex = null; + this.tabControllers = null; } - function onBeforeTabChange() { + this.view = null; + this.params = null; + this.currentTabController = null; + this.initialTabIndex = null; +} - } +function onBeforeTabChange() { - function TabbedView(view, params) { +} + +class TabbedView { + constructor(view, params) { this.tabControllers = []; this.view = view; this.params = params; @@ -85,7 +88,7 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function ( view.addEventListener('viewdestroy', onViewDestroy.bind(this)); } - TabbedView.prototype.onResume = function (options) { + onResume(options) { this.setTitle(); backdrop.clearBackdrop(); @@ -96,19 +99,18 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function ( } else if (currentTabController && currentTabController.onResume) { currentTabController.onResume({}); } - }; + } - TabbedView.prototype.onPause = function () { + onPause() { var currentTabController = this.currentTabController; if (currentTabController && currentTabController.onPause) { currentTabController.onPause(); } - }; - - TabbedView.prototype.setTitle = function () { + } + setTitle() { Emby.Page.setTitle(''); - }; + } +} - return TabbedView; -}); +export default TabbedView; From 38d25172d5def42c19de33153fed8e7accaee286 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 31 Jul 2020 17:06:04 +0100 Subject: [PATCH 018/155] Fix type and applysuggestions to home.js --- .../dashboard/plugins/available/index.js | 2 +- .../dashboard/plugins/installed/index.js | 2 +- src/controllers/home.js | 98 +++++++++---------- src/controllers/itemDetails/index.js | 2 +- src/controllers/movies/moviesrecommended.js | 2 +- 5 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/controllers/dashboard/plugins/available/index.js b/src/controllers/dashboard/plugins/available/index.js index 473b1b8961..274cde8824 100644 --- a/src/controllers/dashboard/plugins/available/index.js +++ b/src/controllers/dashboard/plugins/available/index.js @@ -1,7 +1,7 @@ define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) { 'use strict'; - libraryMenu = LibraryMenu.default || libraryMenu; + libraryMenu = libraryMenu.default || libraryMenu; function reloadList(page) { loading.show(); diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index 014fbf16c2..02fd954620 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -1,7 +1,7 @@ define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'], function (loading, libraryMenu, dom, globalize) { 'use strict'; - libraryMenu = LibraryMenu.default || libraryMenu; + libraryMenu = libraryMenu.default || libraryMenu; function deletePlugin(page, uniqueid, name) { var msg = globalize.translate('UninstallPluginConfirmation', name); diff --git a/src/controllers/home.js b/src/controllers/home.js index 392da0e2d2..bfee2c50fb 100644 --- a/src/controllers/home.js +++ b/src/controllers/home.js @@ -4,67 +4,65 @@ import 'emby-tabs'; import 'emby-button'; import 'emby-scroller'; -function getTabs() { - return [{ - name: globalize.translate('Home') - }, { - name: globalize.translate('Favorites') - }]; -} - -function getDefaultTabIndex() { - return 0; -} - -function getTabController(index) { - if (null == index) { - throw new Error('index cannot be null'); - } - - let depends = ''; - - switch (index) { - case 0: - depends = 'controllers/hometab'; - break; - - case 1: - depends = 'controllers/favorites'; - } - - const instance = this; - return import(depends).then(({ default: controllerFactory }) => { - let controller = instance.tabControllers[index]; - - if (!controller) { - controller = new controllerFactory(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params); - instance.tabControllers[index] = controller; - } - - return controller; - }); -} - -class HomeView { +class HomeView extends TabbedView { constructor(view, params) { - TabbedView.call(this, view, params); + super(view, params); } + setTitle() { Emby.Page.setTitle(null); } + onPause() { - TabbedView.prototype.onPause.call(this); + super.onPause(this); document.querySelector('.skinHeader').classList.remove('noHomeButtonHeader'); } + onResume(options) { - TabbedView.prototype.onResume.call(this, options); + super.onResume(this, options); document.querySelector('.skinHeader').classList.add('noHomeButtonHeader'); } + + getDefaultTabIndex() { + return 0; + } + + getTabs() { + return [{ + name: globalize.translate('Home') + }, { + name: globalize.translate('Favorites') + }]; + } + + getTabController(index) { + if (null == index) { + throw new Error('index cannot be null'); + } + + let depends = ''; + + switch (index) { + case 0: + depends = 'controllers/hometab'; + break; + + case 1: + depends = 'controllers/favorites'; + } + + const instance = this; + return import(depends).then(({ default: controllerFactory }) => { + let controller = instance.tabControllers[index]; + + if (!controller) { + controller = new controllerFactory(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params); + instance.tabControllers[index] = controller; + } + + return controller; + }); + } } -Object.assign(HomeView.prototype, TabbedView.prototype); -HomeView.prototype.getTabs = getTabs; -HomeView.prototype.getDefaultTabIndex = getDefaultTabIndex; -HomeView.prototype.getTabController = getTabController; - export default HomeView; diff --git a/src/controllers/itemDetails/index.js b/src/controllers/itemDetails/index.js index 49412c4ac4..d02268b9d4 100644 --- a/src/controllers/itemDetails/index.js +++ b/src/controllers/itemDetails/index.js @@ -1,7 +1,7 @@ define(['loading', 'appRouter', 'layoutManager', 'connectionManager', 'userSettings', 'cardBuilder', 'datetime', 'mediaInfo', 'backdrop', 'listView', 'itemContextMenu', 'itemHelper', 'dom', 'indicators', 'imageLoader', 'libraryMenu', 'globalize', 'browser', 'events', 'playbackManager', 'scrollStyles', 'emby-itemscontainer', 'emby-checkbox', 'emby-button', 'emby-playstatebutton', 'emby-ratingbutton', 'emby-scroller', 'emby-select'], function (loading, appRouter, layoutManager, connectionManager, userSettings, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, dom, indicators, imageLoader, libraryMenu, globalize, browser, events, playbackManager) { 'use strict'; - libraryMenu = LibraryMenu.default || libraryMenu; + libraryMenu = libraryMenu.default || libraryMenu; function getPromise(apiClient, params) { var id = params.id; diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index 96e98452db..09ecd3c034 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -1,7 +1,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu', 'mainTabsManager', 'cardBuilder', 'dom', 'imageLoader', 'playbackManager', 'globalize', 'emby-scroller', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager, globalize) { 'use strict'; - libraryMenu = LibraryMenu.default || libraryMenu; + libraryMenu = libraryMenu.default || libraryMenu; function enableScrollX() { return !layoutManager.desktop; From ab52727254019450db9a9fab25bc5eb4cddc504a Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Sat, 1 Aug 2020 15:32:57 +0200 Subject: [PATCH 019/155] Migrated livetvseriestimer to es6 module --- src/controllers/livetv/livetvseriestimers.js | 90 ++++++++++---------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/controllers/livetv/livetvseriestimers.js b/src/controllers/livetv/livetvseriestimers.js index c5d0da60d7..d645a66370 100644 --- a/src/controllers/livetv/livetvseriestimers.js +++ b/src/controllers/livetv/livetvseriestimers.js @@ -1,50 +1,52 @@ -define(['datetime', 'cardBuilder', 'imageLoader', 'apphost', 'loading', 'paper-icon-button-light', 'emby-button'], function (datetime, cardBuilder, imageLoader, appHost, loading) { - 'use strict'; +import cardBuilder from 'cardBuilder'; +import imageLoader from 'imageLoader'; +import loading from 'loading'; +import 'paper-icon-button-light'; +import 'emby-button'; - function renderTimers(context, timers) { - var html = ''; - html += cardBuilder.getCardsHtml({ - items: timers, - shape: 'auto', - defaultShape: 'portrait', - showTitle: true, - cardLayout: false, - preferThumb: 'auto', - coverImage: true, - overlayText: false, - showSeriesTimerTime: true, - showSeriesTimerChannel: true, - centerText: true, - overlayMoreButton: true, - lines: 3 - }); - var elem = context.querySelector('#items'); - elem.innerHTML = html; - imageLoader.lazyChildren(elem); - loading.hide(); - } +function renderTimers(context, timers) { + const html = cardBuilder.getCardsHtml({ + items: timers, + shape: 'auto', + defaultShape: 'portrait', + showTitle: true, + cardLayout: false, + preferThumb: 'auto', + coverImage: true, + overlayText: false, + showSeriesTimerTime: true, + showSeriesTimerChannel: true, + centerText: true, + overlayMoreButton: true, + lines: 3 + }); + const elem = context.querySelector('#items'); + elem.innerHTML = html; + imageLoader.lazyChildren(elem); + loading.hide(); +} - function reload(context, promise) { - loading.show(); - promise.then(function (result) { - renderTimers(context, result.Items); - }); - } +function reload(context, promise) { + loading.show(); + promise.then(function (result) { + renderTimers(context, result.Items); + }); +} - var query = { - SortBy: 'SortName', - SortOrder: 'Ascending' +const query = { + SortBy: 'SortName', + SortOrder: 'Ascending' +}; + +export default function (view, params, tabContent) { + let timersPromise; + const self = this; + + self.preRender = function () { + timersPromise = ApiClient.getLiveTvSeriesTimers(query); }; - return function (view, params, tabContent) { - var timersPromise; - var self = this; - self.preRender = function () { - timersPromise = ApiClient.getLiveTvSeriesTimers(query); - }; - - self.renderTab = function () { - reload(tabContent, timersPromise); - }; + self.renderTab = function () { + reload(tabContent, timersPromise); }; -}); +}; From 0c8e1994b79eebee5056c03bc1056b3d658c94ca Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Sat, 1 Aug 2020 15:36:16 +0200 Subject: [PATCH 020/155] fixed linting errors --- src/controllers/livetv/livetvseriestimers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/livetv/livetvseriestimers.js b/src/controllers/livetv/livetvseriestimers.js index d645a66370..4f6bfaaa6a 100644 --- a/src/controllers/livetv/livetvseriestimers.js +++ b/src/controllers/livetv/livetvseriestimers.js @@ -49,4 +49,4 @@ export default function (view, params, tabContent) { self.renderTab = function () { reload(tabContent, timersPromise); }; -}; +} From b082613ed98bd550a9e6a6abdb5bf6aff687264f Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Aug 2020 17:44:03 +0100 Subject: [PATCH 021/155] Migration of viewManager to ES6 module --- package.json | 1 + src/components/appRouter.js | 2 + src/components/viewManager/viewManager.js | 229 +++++++++++----------- src/scripts/libraryMenu.js | 2 + src/scripts/site.js | 4 +- 5 files changed, 123 insertions(+), 115 deletions(-) diff --git a/package.json b/package.json index 5db40a0efe..d8306aa9e2 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "src/components/syncPlay/playbackPermissionManager.js", "src/components/syncPlay/syncPlayManager.js", "src/components/syncPlay/timeSyncManager.js", + "src/components/viewManager/viewManager.js", "src/controllers/session/addServer/index.js", "src/controllers/session/forgotPassword/index.js", "src/controllers/session/redeemPassword/index.js", diff --git a/src/components/appRouter.js b/src/components/appRouter.js index 138d58e5c0..9ed6e39d22 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -1,6 +1,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) { 'use strict'; + viewManager = viewManager.default || viewManager; + var appRouter = { showLocalLogin: function (serverId, manualLogin) { var pageName = manualLogin ? 'manuallogin' : 'login'; diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index 058ba4ebb2..695535e7b8 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -1,134 +1,137 @@ -define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], function (viewContainer, focusManager, queryString, layoutManager) { - 'use strict'; +import viewContainer from 'viewContainer'; +import focusManager from 'focusManager'; +import queryString from 'queryString'; +import layoutManager from 'layoutManager'; - var currentView; - var dispatchPageEvents; +let currentView; +let dispatchPageEvents; - viewContainer.setOnBeforeChange(function (newView, isRestored, options) { - var lastView = currentView; - if (lastView) { - var beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true); +viewContainer.setOnBeforeChange(function (newView, isRestored, options) { + const lastView = currentView; + if (lastView) { + const beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true); - if (!beforeHideResult) { - // todo: cancel - } - } - - var eventDetail = getViewEventDetail(newView, options, isRestored); - - if (!newView.initComplete) { - newView.initComplete = true; - - if (typeof options.controllerFactory === 'function') { - new options.controllerFactory(newView, eventDetail.detail.params); - } else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') { - new options.controllerFactory.default(newView, eventDetail.detail.params); - } - - if (!options.controllerFactory || dispatchPageEvents) { - dispatchViewEvent(newView, eventDetail, 'viewinit'); - } - } - - dispatchViewEvent(newView, eventDetail, 'viewbeforeshow'); - }); - - function onViewChange(view, options, isRestore) { - var lastView = currentView; - if (lastView) { - dispatchViewEvent(lastView, null, 'viewhide'); - } - - currentView = view; - - var eventDetail = getViewEventDetail(view, options, isRestore); - - if (!isRestore) { - if (options.autoFocus !== false) { - focusManager.autoFocus(view); - } - } else if (!layoutManager.mobile) { - if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) { - focusManager.focus(view.activeElement); - } else { - focusManager.autoFocus(view); - } - } - - view.dispatchEvent(new CustomEvent('viewshow', eventDetail)); - - if (dispatchPageEvents) { - view.dispatchEvent(new CustomEvent('pageshow', eventDetail)); + if (!beforeHideResult) { + // todo: cancel } } - function getProperties(view) { - var props = view.getAttribute('data-properties'); + const eventDetail = getViewEventDetail(newView, options, isRestored); - if (props) { - return props.split(','); + if (!newView.initComplete) { + newView.initComplete = true; + + if (typeof options.controllerFactory === 'function') { + new options.controllerFactory(newView, eventDetail.detail.params); + } else if (options.controllerFactory && typeof options.controllerFactory.default === 'function') { + new options.controllerFactory.default(newView, eventDetail.detail.params); } - return []; + if (!options.controllerFactory || dispatchPageEvents) { + dispatchViewEvent(newView, eventDetail, 'viewinit'); + } } - function dispatchViewEvent(view, eventInfo, eventName, isCancellable) { - if (!eventInfo) { - eventInfo = { - detail: { - type: view.getAttribute('data-type'), - properties: getProperties(view) - }, - bubbles: true, - cancelable: isCancellable - }; - } + dispatchViewEvent(newView, eventDetail, 'viewbeforeshow'); +}); - eventInfo.cancelable = isCancellable || false; - - var eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo)); - - if (dispatchPageEvents) { - eventInfo.cancelable = false; - view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo)); - } - - return eventResult; +function onViewChange(view, options, isRestore) { + const lastView = currentView; + if (lastView) { + dispatchViewEvent(lastView, null, 'viewhide'); } - function getViewEventDetail(view, options, isRestore) { - var url = options.url; - var index = url.indexOf('?'); - var params = index === -1 ? {} : queryString.parse(url.substring(index + 1)); + currentView = view; - return { + const eventDetail = getViewEventDetail(view, options, isRestore); + + if (!isRestore) { + if (options.autoFocus !== false) { + focusManager.autoFocus(view); + } + } else if (!layoutManager.mobile) { + if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) { + focusManager.focus(view.activeElement); + } else { + focusManager.autoFocus(view); + } + } + + view.dispatchEvent(new CustomEvent('viewshow', eventDetail)); + + if (dispatchPageEvents) { + view.dispatchEvent(new CustomEvent('pageshow', eventDetail)); + } +} + +function getProperties(view) { + const props = view.getAttribute('data-properties'); + + if (props) { + return props.split(','); + } + + return []; +} + +function dispatchViewEvent(view, eventInfo, eventName, isCancellable) { + if (!eventInfo) { + eventInfo = { detail: { type: view.getAttribute('data-type'), - properties: getProperties(view), - params: params, - isRestored: isRestore, - state: options.state, - - // The route options - options: options.options || {} + properties: getProperties(view) }, bubbles: true, - cancelable: false + cancelable: isCancellable }; } - function resetCachedViews() { - // Reset all cached views whenever the skin changes - viewContainer.reset(); + eventInfo.cancelable = isCancellable || false; + + const eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo)); + + if (dispatchPageEvents) { + eventInfo.cancelable = false; + view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo)); } - document.addEventListener('skinunload', resetCachedViews); + return eventResult; +} - function ViewManager() { +function getViewEventDetail(view, options, isRestore) { + const url = options.url; + const index = url.indexOf('?'); + const params = index === -1 ? {} : queryString.parse(url.substring(index + 1)); + + return { + detail: { + type: view.getAttribute('data-type'), + properties: getProperties(view), + params: params, + isRestored: isRestore, + state: options.state, + + // The route options + options: options.options || {} + }, + bubbles: true, + cancelable: false + }; +} + +function resetCachedViews() { + // Reset all cached views whenever the skin changes + viewContainer.reset(); +} + +document.addEventListener('skinunload', resetCachedViews); + +class ViewManager { + constructor() { } - ViewManager.prototype.loadView = function (options) { - var lastView = currentView; + loadView(options) { + const lastView = currentView; // Record the element that has focus if (lastView) { @@ -142,9 +145,9 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi viewContainer.loadView(options).then(function (view) { onViewChange(view, options); }); - }; + } - ViewManager.prototype.tryRestoreView = function (options, onViewChanging) { + tryRestoreView(options, onViewChanging) { if (options.cancel) { return Promise.reject({ cancelled: true }); } @@ -158,15 +161,15 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], functi onViewChanging(); onViewChange(view, options, true); }); - }; + } - ViewManager.prototype.currentView = function () { + currentView() { return currentView; - }; + } - ViewManager.prototype.dispatchPageEvents = function (value) { + dispatchPageEvents(value) { dispatchPageEvents = value; - }; + } +} - return new ViewManager(); -}); +export default new ViewManager(); diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 961d89dc05..d199af0ede 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -1,6 +1,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'syncPlayManager', 'groupSelectionMenu', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, syncPlayManager, groupSelectionMenu, browser, globalize, imageHelper) { 'use strict'; + viewManager = viewManager.default || viewManager; + function renderHeader() { var html = ''; html += '
'; diff --git a/src/scripts/site.js b/src/scripts/site.js index 2beb45959a..6aec5379a8 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -827,8 +827,8 @@ function initClient() { define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency); define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency); define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) { - window.ViewManager = viewManager; - viewManager.dispatchPageEvents(true); + window.ViewManager = viewManager.default; + viewManager.default.dispatchPageEvents(true); return viewManager; }); define('slideshow', [componentsPath + '/slideshow/slideshow'], returnFirstDependency); From 275676f7126655981fa4600661be4519049da358 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Aug 2020 17:58:21 +0100 Subject: [PATCH 022/155] remove defaults --- src/controllers/list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index c7c4b2a31d..aea791c167 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -558,7 +558,7 @@ class ItemsView { 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', @@ -817,7 +817,7 @@ class ItemsView { bindAll(view.querySelectorAll('.btnShuffle'), 'click', shuffle); } - self.alphaNumericShortcuts = new AlphaNumericShortcuts.default({ + self.alphaNumericShortcuts = new AlphaNumericShortcuts({ itemsContainer: self.itemsContainer }); }); From 6b54d25b4d36b44eb357244ea8a06028d240c567 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Aug 2020 18:06:40 +0100 Subject: [PATCH 023/155] Update list.js --- src/controllers/list.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index 7dab6f5f35..b9237887d0 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -14,9 +14,6 @@ import 'emby-scroller'; /* eslint-disable indent */ - playbackManager = playbackManager.default || playbackManager; - loading = loading.default || loading; - function getInitialLiveTvQuery(instance, params) { const query = { UserId: connectionManager.getApiClient(params.serverId).getCurrentUserId(), @@ -523,7 +520,7 @@ class ItemsView { } const showYear = settings.showTitle && params.IsMovie === 'true' && params.type === 'Recordings'; - + if (showYear) { lines++; } @@ -858,6 +855,7 @@ class ItemsView { self.alphaPickerElement = null; }); } + getFilters() { const basekey = this.getSettingsKey(); return { @@ -879,6 +877,7 @@ class ItemsView { GenreIds: userSettings.getFilter(basekey + '-filter-GenreIds') }; } + getSortValues() { const basekey = this.getSettingsKey(); return { @@ -886,6 +885,7 @@ class ItemsView { sortOrder: userSettings.getFilter(basekey + '-sortorder') === 'Descending' ? 'Descending' : 'Ascending' }; } + getDefaultSortBy() { const params = this.params; const sortNameOption = this.getNameSortOption(params); @@ -896,6 +896,7 @@ class ItemsView { return 'IsFolder,' + sortNameOption.value; } + getSortMenuOptions() { const sortBy = []; const params = this.params; @@ -966,6 +967,7 @@ class ItemsView { }); return sortBy; } + getNameSortOption(params) { if (params.type === 'Episode') { return { @@ -979,6 +981,7 @@ class ItemsView { value: 'SortName' }; } + getPlayCountSortOption() { if (this.params.type === 'Programs') { return null; @@ -988,8 +991,8 @@ class ItemsView { name: globalize.translate('PlayCount'), value: 'PlayCount,SortName' }; - } + getDatePlayedSortOption() { if (this.params.type === 'Programs') { return null; @@ -1000,12 +1003,9 @@ class ItemsView { value: 'DatePlayed,SortName' }; } + getCriticRatingSortOption() { if (this.params.type === 'Programs') { - }; - - ItemsView.prototype.getCriticRatingSortOption = function () { - if () { return null; } @@ -1014,12 +1014,14 @@ class ItemsView { value: 'CriticRating,SortName' }; } + getCommunityRatingSortOption() { return { name: globalize.translate('CommunityRating'), value: 'CommunityRating,SortName' }; } + getVisibleFilters() { const filters = []; const params = this.params; @@ -1047,6 +1049,7 @@ class ItemsView { return filters; } + setFilterStatus(hasFilters) { this.hasFilters = hasFilters; const filterButtons = this.filterButtons; @@ -1074,6 +1077,7 @@ class ItemsView { } } } + getFilterMenuOptions() { const params = this.params; return { @@ -1086,6 +1090,7 @@ class ItemsView { Recursive: this.queryRecursive }; } + getVisibleViewSettings() { const item = (this.params, this.currentItem); const fields = ['showTitle']; @@ -1097,6 +1102,7 @@ class ItemsView { fields.push('viewType'); return fields; } + getViewSettings() { const basekey = this.getSettingsKey(); const params = this.params; @@ -1126,6 +1132,7 @@ class ItemsView { viewType: userSettings.get(basekey + '-viewType') || 'images' }; } + getItemTypes() { const params = this.params; @@ -1139,6 +1146,7 @@ class ItemsView { return []; } + getSettingsKey() { const values = []; values.push('items'); From 57fb1331666e1f57cfe0111cbb15a5d5a071c969 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Aug 2020 18:09:17 +0100 Subject: [PATCH 024/155] fix yoda --- src/libraries/navdrawer/navdrawer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/navdrawer/navdrawer.js b/src/libraries/navdrawer/navdrawer.js index d70ef90961..41e7af3339 100644 --- a/src/libraries/navdrawer/navdrawer.js +++ b/src/libraries/navdrawer/navdrawer.js @@ -32,19 +32,19 @@ define(['browser', 'dom', 'css!./navdrawer', 'scrollStyles'], function (browser, var deltaY = endY - (menuTouchStartY || 0); setVelocity(deltaX); - if (isOpen && 1 !== dragMode && deltaX > 0) { + if (isOpen && dragMode !== 1 && deltaX > 0) { dragMode = 2; } - if (0 === dragMode && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) { + if (dragMode === 0 && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) { dragMode = 1; scrollContainer.addEventListener('scroll', disableEvent); self.showMask(); - } else if (0 === dragMode && Math.abs(deltaY) >= 5) { + } else if (dragMode === 0 && Math.abs(deltaY) >= 5) { dragMode = 2; } - if (1 === dragMode) { + if (dragMode === 1) { newPos = currentPos + deltaX; self.changeMenuPos(); } From a7ad147aad2dda101ec26aa7e44d8a1008448bee Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Aug 2020 18:48:57 +0100 Subject: [PATCH 025/155] Migration of xmltv and livetvguideprovide to ES6 module --- package.json | 2 + src/components/tvproviders/schedulesdirect.js | 543 +++++++++--------- src/components/tvproviders/xmltv.js | 356 ++++++------ src/controllers/livetvguideprovider.js | 2 +- 4 files changed, 456 insertions(+), 447 deletions(-) diff --git a/package.json b/package.json index 22fd43308a..43d806b8de 100644 --- a/package.json +++ b/package.json @@ -166,6 +166,8 @@ "src/components/syncPlay/playbackPermissionManager.js", "src/components/syncPlay/syncPlayManager.js", "src/components/syncPlay/timeSyncManager.js", + "src/components/tvproviders/schedulesdirect.js", + "src/components/tvproviders/xmltv.js", "src/components/viewContainer.js", "src/controllers/session/addServer/index.js", "src/controllers/session/forgotPassword/index.js", diff --git a/src/components/tvproviders/schedulesdirect.js b/src/components/tvproviders/schedulesdirect.js index a0c29f48b8..9d60310e98 100644 --- a/src/components/tvproviders/schedulesdirect.js +++ b/src/components/tvproviders/schedulesdirect.js @@ -1,299 +1,304 @@ -define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'listViewStyle', 'emby-input', 'emby-select', 'emby-button', 'flexStyles'], function ($, loading, globalize) { - 'use strict'; +import $ from 'jQuery'; +import loading from 'loading'; +import globalize from 'globalize'; +import 'emby-checkbox'; +import 'emby-input'; +import 'listViewStyle'; +import 'paper-icon-button-light'; +import 'emby-select'; +import 'emby-button'; +import 'flexStyles'; - loading = loading.default || loading; +export default function (page, providerId, options) { + function reload() { + loading.show(); + ApiClient.getNamedConfiguration('livetv').then(function (config) { + var info = config.ListingProviders.filter(function (i) { + return i.Id === providerId; + })[0] || {}; + listingsId = info.ListingsId; + $('#selectListing', page).val(info.ListingsId || ''); + page.querySelector('.txtUser').value = info.Username || ''; + page.querySelector('.txtPass').value = ''; + page.querySelector('.txtZipCode').value = info.ZipCode || ''; - return function (page, providerId, options) { - function reload() { - loading.show(); - ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (i) { - return i.Id === providerId; - })[0] || {}; - listingsId = info.ListingsId; - $('#selectListing', page).val(info.ListingsId || ''); - page.querySelector('.txtUser').value = info.Username || ''; - page.querySelector('.txtPass').value = ''; - page.querySelector('.txtZipCode').value = info.ZipCode || ''; - - if (info.Username && info.Password) { - page.querySelector('.listingsSection').classList.remove('hide'); - } else { - page.querySelector('.listingsSection').classList.add('hide'); - } - - page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; - - if (info.EnableAllTuners) { - page.querySelector('.selectTunersSection').classList.add('hide'); - } else { - page.querySelector('.selectTunersSection').classList.remove('hide'); - } - - setCountry(info); - refreshTunerDevices(page, info, config.TunerHosts); - }); - } - - function setCountry(info) { - ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/SchedulesDirect/Countries')).then(function (result) { - var i; - var length; - var countryList = []; - - for (var region in result) { - var countries = result[region]; - - if (countries.length && region !== 'ZZZ') { - for (i = 0, length = countries.length; i < length; i++) { - countryList.push({ - name: countries[i].fullName, - value: countries[i].shortName - }); - } - } - } - - countryList.sort(function (a, b) { - if (a.name > b.name) { - return 1; - } - - if (a.name < b.name) { - return -1; - } - - return 0; - }); - $('#selectCountry', page).html(countryList.map(function (c) { - return ''; - }).join('')).val(info.Country || ''); - $(page.querySelector('.txtZipCode')).trigger('change'); - }, function () { // ApiClient.getJSON() error handler - Dashboard.alert({ - message: globalize.translate('ErrorGettingTvLineups') - }); - }); - loading.hide(); - } - - function sha256(str) { - if (!self.TextEncoder) { - return Promise.resolve(''); + if (info.Username && info.Password) { + page.querySelector('.listingsSection').classList.remove('hide'); + } else { + page.querySelector('.listingsSection').classList.add('hide'); } - var buffer = new TextEncoder('utf-8').encode(str); - return crypto.subtle.digest('SHA-256', buffer).then(function (hash) { - return hex(hash); - }); - } + page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; - function hex(buffer) { - var hexCodes = []; - var view = new DataView(buffer); - - for (var i = 0; i < view.byteLength; i += 4) { - var value = view.getUint32(i); - var stringValue = value.toString(16); - var paddedValue = ('00000000' + stringValue).slice(-'00000000'.length); - hexCodes.push(paddedValue); + if (info.EnableAllTuners) { + page.querySelector('.selectTunersSection').classList.add('hide'); + } else { + page.querySelector('.selectTunersSection').classList.remove('hide'); } - return hexCodes.join(''); - } + setCountry(info); + refreshTunerDevices(page, info, config.TunerHosts); + }); + } - function submitLoginForm() { - loading.show(); - sha256(page.querySelector('.txtPass').value).then(function (passwordHash) { - var info = { - Type: 'SchedulesDirect', - Username: page.querySelector('.txtUser').value, - EnableAllTuners: true, - Password: passwordHash, - Pw: page.querySelector('.txtPass').value - }; - var id = providerId; + function setCountry(info) { + ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/SchedulesDirect/Countries')).then(function (result) { + var i; + var length; + var countryList = []; - if (id) { - info.Id = id; + for (var region in result) { + var countries = result[region]; + + if (countries.length && region !== 'ZZZ') { + for (i = 0, length = countries.length; i < length; i++) { + countryList.push({ + name: countries[i].fullName, + value: countries[i].shortName + }); + } + } + } + + countryList.sort(function (a, b) { + if (a.name > b.name) { + return 1; } - ApiClient.ajax({ - type: 'POST', - url: ApiClient.getUrl('LiveTv/ListingProviders', { - ValidateLogin: true - }), - data: JSON.stringify(info), - contentType: 'application/json', - dataType: 'json' - }).then(function (result) { - Dashboard.processServerConfigurationUpdateResult(); - providerId = result.Id; - reload(); - }, function () { - Dashboard.alert({ // ApiClient.ajax() error handler - message: globalize.translate('ErrorSavingTvProvider') - }); - }); + if (a.name < b.name) { + return -1; + } + + return 0; }); + $('#selectCountry', page).html(countryList.map(function (c) { + return ''; + }).join('')).val(info.Country || ''); + $(page.querySelector('.txtZipCode')).trigger('change'); + }, function () { // ApiClient.getJSON() error handler + Dashboard.alert({ + message: globalize.translate('ErrorGettingTvLineups') + }); + }); + loading.hide(); + } + + function sha256(str) { + if (!self.TextEncoder) { + return Promise.resolve(''); } - function submitListingsForm() { - var selectedListingsId = $('#selectListing', page).val(); + var buffer = new TextEncoder('utf-8').encode(str); + return crypto.subtle.digest('SHA-256', buffer).then(function (hash) { + return hex(hash); + }); + } - if (!selectedListingsId) { - return void Dashboard.alert({ - message: globalize.translate('ErrorPleaseSelectLineup') - }); - } + function hex(buffer) { + var hexCodes = []; + var view = new DataView(buffer); - loading.show(); + for (var i = 0; i < view.byteLength; i += 4) { + var value = view.getUint32(i); + var stringValue = value.toString(16); + var paddedValue = ('00000000' + stringValue).slice(-'00000000'.length); + hexCodes.push(paddedValue); + } + + return hexCodes.join(''); + } + + function submitLoginForm() { + loading.show(); + sha256(page.querySelector('.txtPass').value).then(function (passwordHash) { + var info = { + Type: 'SchedulesDirect', + Username: page.querySelector('.txtUser').value, + EnableAllTuners: true, + Password: passwordHash, + Pw: page.querySelector('.txtPass').value + }; var id = providerId; - ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (i) { - return i.Id === id; - })[0]; - info.ZipCode = page.querySelector('.txtZipCode').value; - info.Country = $('#selectCountry', page).val(); - info.ListingsId = selectedListingsId; - info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; - info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (i) { - return i.checked; - }).map(function (i) { - return i.getAttribute('data-id'); - }); - ApiClient.ajax({ - type: 'POST', - url: ApiClient.getUrl('LiveTv/ListingProviders', { - ValidateListings: true - }), - data: JSON.stringify(info), - contentType: 'application/json' - }).then(function (result) { - loading.hide(); - if (options.showConfirmation) { - Dashboard.processServerConfigurationUpdateResult(); - } - - Events.trigger(self, 'submitted'); - }, function () { - loading.hide(); - Dashboard.alert({ - message: globalize.translate('ErrorAddingListingsToSchedulesDirect') - }); - }); - }); - } - - function refreshListings(value) { - if (!value) { - return void $('#selectListing', page).html(''); + if (id) { + info.Id = id; } - loading.show(); ApiClient.ajax({ - type: 'GET', - url: ApiClient.getUrl('LiveTv/ListingProviders/Lineups', { - Id: providerId, - Location: value, - Country: $('#selectCountry', page).val() + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { + ValidateLogin: true }), + data: JSON.stringify(info), + contentType: 'application/json', dataType: 'json' }).then(function (result) { - $('#selectListing', page).html(result.map(function (o) { - return ''; - })); - - if (listingsId) { - $('#selectListing', page).val(listingsId); - } - - loading.hide(); - }, function (result) { - Dashboard.alert({ - message: globalize.translate('ErrorGettingTvLineups') + Dashboard.processServerConfigurationUpdateResult(); + providerId = result.Id; + reload(); + }, function () { + Dashboard.alert({ // ApiClient.ajax() error handler + message: globalize.translate('ErrorSavingTvProvider') }); - refreshListings(''); + }); + }); + } + + function submitListingsForm() { + var selectedListingsId = $('#selectListing', page).val(); + + if (!selectedListingsId) { + return void Dashboard.alert({ + message: globalize.translate('ErrorPleaseSelectLineup') + }); + } + + loading.show(); + var id = providerId; + ApiClient.getNamedConfiguration('livetv').then(function (config) { + var info = config.ListingProviders.filter(function (i) { + return i.Id === id; + })[0]; + info.ZipCode = page.querySelector('.txtZipCode').value; + info.Country = $('#selectCountry', page).val(); + info.ListingsId = selectedListingsId; + info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; + info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (i) { + return i.checked; + }).map(function (i) { + return i.getAttribute('data-id'); + }); + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { + ValidateListings: true + }), + data: JSON.stringify(info), + contentType: 'application/json' + }).then(function (result) { loading.hide(); - }); - } - function getTunerName(providerId) { - switch (providerId = providerId.toLowerCase()) { - case 'm3u': - return 'M3U Playlist'; - case 'hdhomerun': - return 'HDHomerun'; - case 'satip': - return 'DVB'; - default: - return 'Unknown'; - } - } - - function refreshTunerDevices(page, providerInfo, devices) { - var html = ''; - - for (var i = 0, length = devices.length; i < length; i++) { - var device = devices[i]; - html += '
'; - var enabledTuners = providerInfo.EnabledTuners || []; - var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; - var checkedAttribute = isChecked ? ' checked' : ''; - html += ''; - html += '
'; - html += '
'; - html += device.FriendlyName || getTunerName(device.Type); - html += '
'; - html += '
'; - html += device.Url; - html += '
'; - html += '
'; - html += '
'; - } - - page.querySelector('.tunerList').innerHTML = html; - } - - var listingsId; - var self = this; - - self.submit = function () { - page.querySelector('.btnSubmitListingsContainer').click(); - }; - - self.init = function () { - options = options || {}; - - // Only hide the buttons if explicitly set to false; default to showing if undefined or null - // FIXME: rename this option to clarify logic - var hideCancelButton = options.showCancelButton === false; - page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); - - var hideSubmitButton = options.showSubmitButton === false; - page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); - - $('.formLogin', page).on('submit', function () { - submitLoginForm(); - return false; - }); - $('.formListings', page).on('submit', function () { - submitListingsForm(); - return false; - }); - $('.txtZipCode', page).on('change', function () { - refreshListings(this.value); - }); - page.querySelector('.chkAllTuners').addEventListener('change', function (e) { - if (e.target.checked) { - page.querySelector('.selectTunersSection').classList.add('hide'); - } else { - page.querySelector('.selectTunersSection').classList.remove('hide'); + if (options.showConfirmation) { + Dashboard.processServerConfigurationUpdateResult(); } + + Events.trigger(self, 'submitted'); + }, function () { + loading.hide(); + Dashboard.alert({ + message: globalize.translate('ErrorAddingListingsToSchedulesDirect') + }); }); - $('.createAccountHelp', page).html(globalize.translate('MessageCreateAccountAt', '
http://www.schedulesdirect.org')); - reload(); - }; + }); + } + + function refreshListings(value) { + if (!value) { + return void $('#selectListing', page).html(''); + } + + loading.show(); + ApiClient.ajax({ + type: 'GET', + url: ApiClient.getUrl('LiveTv/ListingProviders/Lineups', { + Id: providerId, + Location: value, + Country: $('#selectCountry', page).val() + }), + dataType: 'json' + }).then(function (result) { + $('#selectListing', page).html(result.map(function (o) { + return ''; + })); + + if (listingsId) { + $('#selectListing', page).val(listingsId); + } + + loading.hide(); + }, function (result) { + Dashboard.alert({ + message: globalize.translate('ErrorGettingTvLineups') + }); + refreshListings(''); + loading.hide(); + }); + } + + function getTunerName(providerId) { + switch (providerId = providerId.toLowerCase()) { + case 'm3u': + return 'M3U Playlist'; + case 'hdhomerun': + return 'HDHomerun'; + case 'satip': + return 'DVB'; + default: + return 'Unknown'; + } + } + + function refreshTunerDevices(page, providerInfo, devices) { + var html = ''; + + for (var i = 0, length = devices.length; i < length; i++) { + var device = devices[i]; + html += '
'; + var enabledTuners = providerInfo.EnabledTuners || []; + var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; + var checkedAttribute = isChecked ? ' checked' : ''; + html += ''; + html += '
'; + html += '
'; + html += device.FriendlyName || getTunerName(device.Type); + html += '
'; + html += '
'; + html += device.Url; + html += '
'; + html += '
'; + html += '
'; + } + + page.querySelector('.tunerList').innerHTML = html; + } + + var listingsId; + var self = this; + + self.submit = function () { + page.querySelector('.btnSubmitListingsContainer').click(); }; -}); + + self.init = function () { + options = options || {}; + + // Only hide the buttons if explicitly set to false; default to showing if undefined or null + // FIXME: rename this option to clarify logic + var hideCancelButton = options.showCancelButton === false; + page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); + + var hideSubmitButton = options.showSubmitButton === false; + page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); + + $('.formLogin', page).on('submit', function () { + submitLoginForm(); + return false; + }); + $('.formListings', page).on('submit', function () { + submitListingsForm(); + return false; + }); + $('.txtZipCode', page).on('change', function () { + refreshListings(this.value); + }); + page.querySelector('.chkAllTuners').addEventListener('change', function (e) { + if (e.target.checked) { + page.querySelector('.selectTunersSection').classList.add('hide'); + } else { + page.querySelector('.selectTunersSection').classList.remove('hide'); + } + }); + $('.createAccountHelp', page).html(globalize.translate('MessageCreateAccountAt', 'http://www.schedulesdirect.org')); + reload(); + }; +}; diff --git a/src/components/tvproviders/xmltv.js b/src/components/tvproviders/xmltv.js index 2203bc1fea..84e222fe09 100644 --- a/src/components/tvproviders/xmltv.js +++ b/src/components/tvproviders/xmltv.js @@ -1,191 +1,193 @@ -define(['jQuery', 'loading', 'globalize', 'emby-checkbox', 'emby-input', 'listViewStyle', 'paper-icon-button-light'], function ($, loading, globalize) { - 'use strict'; +import $ from 'jQuery'; +import loading from 'loading'; +import globalize from 'globalize'; +import 'emby-checkbox'; +import 'emby-input'; +import 'listViewStyle'; +import 'paper-icon-button-light'; - loading = loading.default || loading; +export default function (page, providerId, options) { + function getListingProvider(config, id) { + if (config && id) { + var result = config.ListingProviders.filter(function (provider) { + return provider.Id === id; + })[0]; - return function (page, providerId, options) { - function getListingProvider(config, id) { - if (config && id) { - var result = config.ListingProviders.filter(function (provider) { - return provider.Id === id; - })[0]; - - if (result) { - return Promise.resolve(result); - } - - return getListingProvider(); + if (result) { + return Promise.resolve(result); } - return ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/Default')); + return getListingProvider(); } - function reload() { - loading.show(); - ApiClient.getNamedConfiguration('livetv').then(function (config) { - getListingProvider(config, providerId).then(function (info) { - page.querySelector('.txtPath').value = info.Path || ''; - page.querySelector('.txtKids').value = (info.KidsCategories || []).join('|'); - page.querySelector('.txtNews').value = (info.NewsCategories || []).join('|'); - page.querySelector('.txtSports').value = (info.SportsCategories || []).join('|'); - page.querySelector('.txtMovies').value = (info.MovieCategories || []).join('|'); - page.querySelector('.txtMoviePrefix').value = info.MoviePrefix || ''; - page.querySelector('.txtUserAgent').value = info.UserAgent || ''; - page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; + return ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/Default')); + } - if (page.querySelector('.chkAllTuners').checked) { - page.querySelector('.selectTunersSection').classList.add('hide'); - } else { - page.querySelector('.selectTunersSection').classList.remove('hide'); - } + function reload() { + loading.show(); + ApiClient.getNamedConfiguration('livetv').then(function (config) { + getListingProvider(config, providerId).then(function (info) { + page.querySelector('.txtPath').value = info.Path || ''; + page.querySelector('.txtKids').value = (info.KidsCategories || []).join('|'); + page.querySelector('.txtNews').value = (info.NewsCategories || []).join('|'); + page.querySelector('.txtSports').value = (info.SportsCategories || []).join('|'); + page.querySelector('.txtMovies').value = (info.MovieCategories || []).join('|'); + page.querySelector('.txtMoviePrefix').value = info.MoviePrefix || ''; + page.querySelector('.txtUserAgent').value = info.UserAgent || ''; + page.querySelector('.chkAllTuners').checked = info.EnableAllTuners; - refreshTunerDevices(page, info, config.TunerHosts); - loading.hide(); - }); - }); - } - - function getCategories(txtInput) { - var value = txtInput.value; - - if (value) { - return value.split('|'); - } - - return []; - } - - function submitListingsForm() { - loading.show(); - var id = providerId; - ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (provider) { - return provider.Id === id; - })[0] || {}; - info.Type = 'xmltv'; - info.Path = page.querySelector('.txtPath').value; - info.MoviePrefix = page.querySelector('.txtMoviePrefix').value || null; - info.UserAgent = page.querySelector('.txtUserAgent').value || null; - info.MovieCategories = getCategories(page.querySelector('.txtMovies')); - info.KidsCategories = getCategories(page.querySelector('.txtKids')); - info.NewsCategories = getCategories(page.querySelector('.txtNews')); - info.SportsCategories = getCategories(page.querySelector('.txtSports')); - info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; - info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (tuner) { - return tuner.checked; - }).map(function (tuner) { - return tuner.getAttribute('data-id'); - }); - ApiClient.ajax({ - type: 'POST', - url: ApiClient.getUrl('LiveTv/ListingProviders', { - ValidateListings: true - }), - data: JSON.stringify(info), - contentType: 'application/json' - }).then(function (result) { - loading.hide(); - - if (options.showConfirmation !== false) { - Dashboard.processServerConfigurationUpdateResult(); - } - - Events.trigger(self, 'submitted'); - }, function () { - loading.hide(); - Dashboard.alert({ - message: globalize.translate('ErrorAddingXmlTvFile') - }); - }); - }); - } - - function getTunerName(providerId) { - switch (providerId = providerId.toLowerCase()) { - case 'm3u': - return 'M3U Playlist'; - case 'hdhomerun': - return 'HDHomerun'; - case 'satip': - return 'DVB'; - default: - return 'Unknown'; - } - } - - function refreshTunerDevices(page, providerInfo, devices) { - var html = ''; - - for (var i = 0, length = devices.length; i < length; i++) { - var device = devices[i]; - html += '
'; - var enabledTuners = providerInfo.EnabledTuners || []; - var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; - var checkedAttribute = isChecked ? ' checked' : ''; - html += ''; - html += '
'; - html += '
'; - html += device.FriendlyName || getTunerName(device.Type); - html += '
'; - html += '
'; - html += device.Url; - html += '
'; - html += '
'; - html += '
'; - } - - page.querySelector('.tunerList').innerHTML = html; - } - - function onSelectPathClick(e) { - var page = $(e.target).parents('.xmltvForm')[0]; - - require(['directorybrowser'], function (directoryBrowser) { - var picker = new directoryBrowser.default(); - picker.show({ - includeFiles: true, - callback: function (path) { - if (path) { - var txtPath = page.querySelector('.txtPath'); - txtPath.value = path; - txtPath.focus(); - } - picker.close(); - } - }); - }); - } - - var self = this; - - self.submit = function () { - page.querySelector('.btnSubmitListings').click(); - }; - - self.init = function () { - options = options || {}; - - // Only hide the buttons if explicitly set to false; default to showing if undefined or null - // FIXME: rename this option to clarify logic - var hideCancelButton = options.showCancelButton === false; - page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); - - var hideSubmitButton = options.showSubmitButton === false; - page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); - - $('form', page).on('submit', function () { - submitListingsForm(); - return false; - }); - page.querySelector('#btnSelectPath').addEventListener('click', onSelectPathClick); - page.querySelector('.chkAllTuners').addEventListener('change', function (evt) { - if (evt.target.checked) { + if (page.querySelector('.chkAllTuners').checked) { page.querySelector('.selectTunersSection').classList.add('hide'); } else { page.querySelector('.selectTunersSection').classList.remove('hide'); } + + refreshTunerDevices(page, info, config.TunerHosts); + loading.hide(); }); - reload(); - }; + }); + } + + function getCategories(txtInput) { + var value = txtInput.value; + + if (value) { + return value.split('|'); + } + + return []; + } + + function submitListingsForm() { + loading.show(); + var id = providerId; + ApiClient.getNamedConfiguration('livetv').then(function (config) { + var info = config.ListingProviders.filter(function (provider) { + return provider.Id === id; + })[0] || {}; + info.Type = 'xmltv'; + info.Path = page.querySelector('.txtPath').value; + info.MoviePrefix = page.querySelector('.txtMoviePrefix').value || null; + info.UserAgent = page.querySelector('.txtUserAgent').value || null; + info.MovieCategories = getCategories(page.querySelector('.txtMovies')); + info.KidsCategories = getCategories(page.querySelector('.txtKids')); + info.NewsCategories = getCategories(page.querySelector('.txtNews')); + info.SportsCategories = getCategories(page.querySelector('.txtSports')); + info.EnableAllTuners = page.querySelector('.chkAllTuners').checked; + info.EnabledTuners = info.EnableAllTuners ? [] : $('.chkTuner', page).get().filter(function (tuner) { + return tuner.checked; + }).map(function (tuner) { + return tuner.getAttribute('data-id'); + }); + ApiClient.ajax({ + type: 'POST', + url: ApiClient.getUrl('LiveTv/ListingProviders', { + ValidateListings: true + }), + data: JSON.stringify(info), + contentType: 'application/json' + }).then(function (result) { + loading.hide(); + + if (options.showConfirmation !== false) { + Dashboard.processServerConfigurationUpdateResult(); + } + + Events.trigger(self, 'submitted'); + }, function () { + loading.hide(); + Dashboard.alert({ + message: globalize.translate('ErrorAddingXmlTvFile') + }); + }); + }); + } + + function getTunerName(providerId) { + switch (providerId = providerId.toLowerCase()) { + case 'm3u': + return 'M3U Playlist'; + case 'hdhomerun': + return 'HDHomerun'; + case 'satip': + return 'DVB'; + default: + return 'Unknown'; + } + } + + function refreshTunerDevices(page, providerInfo, devices) { + var html = ''; + + for (var i = 0, length = devices.length; i < length; i++) { + var device = devices[i]; + html += '
'; + var enabledTuners = providerInfo.EnabledTuners || []; + var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; + var checkedAttribute = isChecked ? ' checked' : ''; + html += ''; + html += '
'; + html += '
'; + html += device.FriendlyName || getTunerName(device.Type); + html += '
'; + html += '
'; + html += device.Url; + html += '
'; + html += '
'; + html += '
'; + } + + page.querySelector('.tunerList').innerHTML = html; + } + + function onSelectPathClick(e) { + var page = $(e.target).parents('.xmltvForm')[0]; + + import('directorybrowser').then(({default: directoryBrowser}) => { + var picker = new directoryBrowser(); + picker.show({ + includeFiles: true, + callback: function (path) { + if (path) { + var txtPath = page.querySelector('.txtPath'); + txtPath.value = path; + txtPath.focus(); + } + picker.close(); + } + }); + }); + } + + var self = this; + + self.submit = function () { + page.querySelector('.btnSubmitListings').click(); }; -}); + + self.init = function () { + options = options || {}; + + // Only hide the buttons if explicitly set to false; default to showing if undefined or null + // FIXME: rename this option to clarify logic + var hideCancelButton = options.showCancelButton === false; + page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); + + var hideSubmitButton = options.showSubmitButton === false; + page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); + + $('form', page).on('submit', function () { + submitListingsForm(); + return false; + }); + page.querySelector('#btnSelectPath').addEventListener('click', onSelectPathClick); + page.querySelector('.chkAllTuners').addEventListener('change', function (evt) { + if (evt.target.checked) { + page.querySelector('.selectTunersSection').classList.add('hide'); + } else { + page.querySelector('.selectTunersSection').classList.remove('hide'); + } + }); + reload(); + }; +}; diff --git a/src/controllers/livetvguideprovider.js b/src/controllers/livetvguideprovider.js index 3ec04fd2af..2948fd5be2 100644 --- a/src/controllers/livetvguideprovider.js +++ b/src/controllers/livetvguideprovider.js @@ -11,7 +11,7 @@ define(['events', 'loading', 'globalize'], function (events, loading, globalize) var url = 'components/tvproviders/' + type + '.js'; require([url], function (factory) { - var instance = new factory(page, providerId, {}); + var instance = new factory.default(page, providerId, {}); events.on(instance, 'submitted', onListingsSubmitted); instance.init(); }); From 4af9b91c3b8d5cf6fabb858c481a2e070ad8269f Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 3 Aug 2020 07:40:48 +0100 Subject: [PATCH 026/155] Update var declerations --- src/components/tvproviders/schedulesdirect.js | 58 +++++++++---------- src/components/tvproviders/xmltv.js | 34 +++++------ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/components/tvproviders/schedulesdirect.js b/src/components/tvproviders/schedulesdirect.js index 9d60310e98..de469e1845 100644 --- a/src/components/tvproviders/schedulesdirect.js +++ b/src/components/tvproviders/schedulesdirect.js @@ -13,7 +13,7 @@ export default function (page, providerId, options) { function reload() { loading.show(); ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (i) { + const info = config.ListingProviders.filter(function (i) { return i.Id === providerId; })[0] || {}; listingsId = info.ListingsId; @@ -43,12 +43,12 @@ export default function (page, providerId, options) { function setCountry(info) { ApiClient.getJSON(ApiClient.getUrl('LiveTv/ListingProviders/SchedulesDirect/Countries')).then(function (result) { - var i; - var length; - var countryList = []; + let i; + let length; + const countryList = []; - for (var region in result) { - var countries = result[region]; + for (const region in result) { + const countries = result[region]; if (countries.length && region !== 'ZZZ') { for (i = 0, length = countries.length; i < length; i++) { @@ -88,20 +88,20 @@ export default function (page, providerId, options) { return Promise.resolve(''); } - var buffer = new TextEncoder('utf-8').encode(str); + const buffer = new TextEncoder('utf-8').encode(str); return crypto.subtle.digest('SHA-256', buffer).then(function (hash) { return hex(hash); }); } function hex(buffer) { - var hexCodes = []; - var view = new DataView(buffer); + const hexCodes = []; + const view = new DataView(buffer); - for (var i = 0; i < view.byteLength; i += 4) { - var value = view.getUint32(i); - var stringValue = value.toString(16); - var paddedValue = ('00000000' + stringValue).slice(-'00000000'.length); + for (let i = 0; i < view.byteLength; i += 4) { + const value = view.getUint32(i); + const stringValue = value.toString(16); + const paddedValue = ('00000000' + stringValue).slice(-'00000000'.length); hexCodes.push(paddedValue); } @@ -111,14 +111,14 @@ export default function (page, providerId, options) { function submitLoginForm() { loading.show(); sha256(page.querySelector('.txtPass').value).then(function (passwordHash) { - var info = { + const info = { Type: 'SchedulesDirect', Username: page.querySelector('.txtUser').value, EnableAllTuners: true, Password: passwordHash, Pw: page.querySelector('.txtPass').value }; - var id = providerId; + const id = providerId; if (id) { info.Id = id; @@ -145,7 +145,7 @@ export default function (page, providerId, options) { } function submitListingsForm() { - var selectedListingsId = $('#selectListing', page).val(); + const selectedListingsId = $('#selectListing', page).val(); if (!selectedListingsId) { return void Dashboard.alert({ @@ -154,9 +154,9 @@ export default function (page, providerId, options) { } loading.show(); - var id = providerId; + const id = providerId; ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (i) { + const info = config.ListingProviders.filter(function (i) { return i.Id === id; })[0]; info.ZipCode = page.querySelector('.txtZipCode').value; @@ -239,14 +239,14 @@ export default function (page, providerId, options) { } function refreshTunerDevices(page, providerInfo, devices) { - var html = ''; + let html = ''; - for (var i = 0, length = devices.length; i < length; i++) { - var device = devices[i]; + for (let i = 0, length = devices.length; i < length; i++) { + const device = devices[i]; html += '
'; - var enabledTuners = providerInfo.EnabledTuners || []; - var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; - var checkedAttribute = isChecked ? ' checked' : ''; + const enabledTuners = providerInfo.EnabledTuners || []; + const isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; + const checkedAttribute = isChecked ? ' checked' : ''; html += ''; html += '
'; html += '
'; @@ -262,8 +262,8 @@ export default function (page, providerId, options) { page.querySelector('.tunerList').innerHTML = html; } - var listingsId; - var self = this; + let listingsId; + const self = this; self.submit = function () { page.querySelector('.btnSubmitListingsContainer').click(); @@ -274,10 +274,10 @@ export default function (page, providerId, options) { // Only hide the buttons if explicitly set to false; default to showing if undefined or null // FIXME: rename this option to clarify logic - var hideCancelButton = options.showCancelButton === false; + const hideCancelButton = options.showCancelButton === false; page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); - var hideSubmitButton = options.showSubmitButton === false; + const hideSubmitButton = options.showSubmitButton === false; page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); $('.formLogin', page).on('submit', function () { @@ -301,4 +301,4 @@ export default function (page, providerId, options) { $('.createAccountHelp', page).html(globalize.translate('MessageCreateAccountAt', 'http://www.schedulesdirect.org')); reload(); }; -}; +} diff --git a/src/components/tvproviders/xmltv.js b/src/components/tvproviders/xmltv.js index 84e222fe09..a75b29eeb3 100644 --- a/src/components/tvproviders/xmltv.js +++ b/src/components/tvproviders/xmltv.js @@ -9,7 +9,7 @@ import 'paper-icon-button-light'; export default function (page, providerId, options) { function getListingProvider(config, id) { if (config && id) { - var result = config.ListingProviders.filter(function (provider) { + const result = config.ListingProviders.filter(function (provider) { return provider.Id === id; })[0]; @@ -49,7 +49,7 @@ export default function (page, providerId, options) { } function getCategories(txtInput) { - var value = txtInput.value; + const value = txtInput.value; if (value) { return value.split('|'); @@ -60,9 +60,9 @@ export default function (page, providerId, options) { function submitListingsForm() { loading.show(); - var id = providerId; + const id = providerId; ApiClient.getNamedConfiguration('livetv').then(function (config) { - var info = config.ListingProviders.filter(function (provider) { + const info = config.ListingProviders.filter(function (provider) { return provider.Id === id; })[0] || {}; info.Type = 'xmltv'; @@ -117,14 +117,14 @@ export default function (page, providerId, options) { } function refreshTunerDevices(page, providerInfo, devices) { - var html = ''; + let html = ''; - for (var i = 0, length = devices.length; i < length; i++) { - var device = devices[i]; + for (let i = 0, length = devices.length; i < length; i++) { + const device = devices[i]; html += '
'; - var enabledTuners = providerInfo.EnabledTuners || []; - var isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; - var checkedAttribute = isChecked ? ' checked' : ''; + const enabledTuners = providerInfo.EnabledTuners || []; + const isChecked = providerInfo.EnableAllTuners || enabledTuners.indexOf(device.Id) !== -1; + const checkedAttribute = isChecked ? ' checked' : ''; html += ''; html += '
'; html += '
'; @@ -141,15 +141,15 @@ export default function (page, providerId, options) { } function onSelectPathClick(e) { - var page = $(e.target).parents('.xmltvForm')[0]; + const page = $(e.target).parents('.xmltvForm')[0]; import('directorybrowser').then(({default: directoryBrowser}) => { - var picker = new directoryBrowser(); + const picker = new directoryBrowser(); picker.show({ includeFiles: true, callback: function (path) { if (path) { - var txtPath = page.querySelector('.txtPath'); + const txtPath = page.querySelector('.txtPath'); txtPath.value = path; txtPath.focus(); } @@ -159,7 +159,7 @@ export default function (page, providerId, options) { }); } - var self = this; + const self = this; self.submit = function () { page.querySelector('.btnSubmitListings').click(); @@ -170,10 +170,10 @@ export default function (page, providerId, options) { // Only hide the buttons if explicitly set to false; default to showing if undefined or null // FIXME: rename this option to clarify logic - var hideCancelButton = options.showCancelButton === false; + const hideCancelButton = options.showCancelButton === false; page.querySelector('.btnCancel').classList.toggle('hide', hideCancelButton); - var hideSubmitButton = options.showSubmitButton === false; + const hideSubmitButton = options.showSubmitButton === false; page.querySelector('.btnSubmitListings').classList.toggle('hide', hideSubmitButton); $('form', page).on('submit', function () { @@ -190,4 +190,4 @@ export default function (page, providerId, options) { }); reload(); }; -}; +} From bafe857271606fd65227e5176f490673b66fabed Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Mon, 3 Aug 2020 14:20:31 +0200 Subject: [PATCH 027/155] Move focusManager to ES6 --- package.json | 1 + src/components/focusManager.js | 7 ++++--- src/controllers/list.js | 2 +- src/libraries/scroller.js | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f794b8b6da..eae461bb4f 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "src/components/favoriteitems.js", "src/components/fetchhelper.js", "src/components/filterdialog/filterdialog.js", + "src/components/focusManager.js", "src/components/groupedcards.js", "src/components/homeScreenSettings/homeScreenSettings.js", "src/components/homesections/homesections.js", diff --git a/src/components/focusManager.js b/src/components/focusManager.js index 9c495bf952..1f947871de 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -1,4 +1,6 @@ -define(['dom', 'scrollManager'], function (dom, scrollManager) { +import dom from 'dom'; +import scrollManager from 'scrollManager'; + 'use strict'; var scopes = []; @@ -472,7 +474,7 @@ define(['dom', 'scrollManager'], function (dom, scrollManager) { } } - return { + export default { autoFocus: autoFocus, focus: focus, focusableParent: focusableParent, @@ -505,4 +507,3 @@ define(['dom', 'scrollManager'], function (dom, scrollManager) { focusLast: focusLast, moveFocus: moveFocus }; -}); diff --git a/src/controllers/list.js b/src/controllers/list.js index 68e81f05dd..a35b7a0cf9 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -771,7 +771,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' loading.hide(); if (refresh) { - focusManager.autoFocus(self.itemsContainer); + focusManager.default.autoFocus(self.itemsContainer); } }); diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index c0cb3e557c..d869d8c8be 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -697,7 +697,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc function onFrameClick(e) { if (e.which === 1) { - var focusableParent = focusManager.focusableParent(e.target); + var focusableParent = focusManager.default.focusableParent(e.target); if (focusableParent && focusableParent !== document.activeElement) { focusableParent.focus(); } From 32e3df78010380c5c27d205a063fe647452d736e Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Mon, 3 Aug 2020 14:20:31 +0200 Subject: [PATCH 028/155] Move focusManager to ES6 --- package.json | 1 + src/components/focusManager.js | 7 ++++--- src/controllers/list.js | 2 +- src/libraries/scroller.js | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f794b8b6da..eae461bb4f 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "src/components/favoriteitems.js", "src/components/fetchhelper.js", "src/components/filterdialog/filterdialog.js", + "src/components/focusManager.js", "src/components/groupedcards.js", "src/components/homeScreenSettings/homeScreenSettings.js", "src/components/homesections/homesections.js", diff --git a/src/components/focusManager.js b/src/components/focusManager.js index 9c495bf952..1f947871de 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -1,4 +1,6 @@ -define(['dom', 'scrollManager'], function (dom, scrollManager) { +import dom from 'dom'; +import scrollManager from 'scrollManager'; + 'use strict'; var scopes = []; @@ -472,7 +474,7 @@ define(['dom', 'scrollManager'], function (dom, scrollManager) { } } - return { + export default { autoFocus: autoFocus, focus: focus, focusableParent: focusableParent, @@ -505,4 +507,3 @@ define(['dom', 'scrollManager'], function (dom, scrollManager) { focusLast: focusLast, moveFocus: moveFocus }; -}); diff --git a/src/controllers/list.js b/src/controllers/list.js index 68e81f05dd..a35b7a0cf9 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -771,7 +771,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' loading.hide(); if (refresh) { - focusManager.autoFocus(self.itemsContainer); + focusManager.default.autoFocus(self.itemsContainer); } }); diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index c0cb3e557c..d869d8c8be 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -697,7 +697,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc function onFrameClick(e) { if (e.which === 1) { - var focusableParent = focusManager.focusableParent(e.target); + var focusableParent = focusManager.default.focusableParent(e.target); if (focusableParent && focusableParent !== document.activeElement) { focusableParent.focus(); } From 133e1c9085649f45f275e0d5a0a77d026ec28069 Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Mon, 3 Aug 2020 14:28:20 +0200 Subject: [PATCH 029/155] Disable linter for this file --- src/components/focusManager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/focusManager.js b/src/components/focusManager.js index 1f947871de..74df694ff6 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -1,3 +1,5 @@ +/* eslint-disable indent */ + import dom from 'dom'; import scrollManager from 'scrollManager'; From df1f9470e64ce97b6d78269750b00171df07a42a Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Mon, 3 Aug 2020 14:46:48 +0200 Subject: [PATCH 030/155] ESLint is a magnificent piece of software that everybody should love /sarcasm --- src/components/focusManager.js | 72 ++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/components/focusManager.js b/src/components/focusManager.js index 74df694ff6..b55f933db6 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -3,7 +3,9 @@ import dom from 'dom'; import scrollManager from 'scrollManager'; - 'use strict'; +/* eslint-disable no-unused-expressions */ +'use strict'; +/* eslint-enable no-unused-expressions */ var scopes = []; function pushScope(elem) { @@ -476,36 +478,38 @@ import scrollManager from 'scrollManager'; } } - export default { - autoFocus: autoFocus, - focus: focus, - focusableParent: focusableParent, - getFocusableElements: getFocusableElements, - moveLeft: function (sourceElement, options) { - var container = options ? options.container : null; - var focusableElements = options ? options.focusableElements : null; - nav(sourceElement, 0, container, focusableElements); - }, - moveRight: function (sourceElement, options) { - var container = options ? options.container : null; - var focusableElements = options ? options.focusableElements : null; - nav(sourceElement, 1, container, focusableElements); - }, - moveUp: function (sourceElement, options) { - var container = options ? options.container : null; - var focusableElements = options ? options.focusableElements : null; - nav(sourceElement, 2, container, focusableElements); - }, - moveDown: function (sourceElement, options) { - var container = options ? options.container : null; - var focusableElements = options ? options.focusableElements : null; - nav(sourceElement, 3, container, focusableElements); - }, - sendText: sendText, - isCurrentlyFocusable: isCurrentlyFocusable, - pushScope: pushScope, - popScope: popScope, - focusFirst: focusFirst, - focusLast: focusLast, - moveFocus: moveFocus - }; +/* eslint-enable indent */ + +export default { + autoFocus: autoFocus, + focus: focus, + focusableParent: focusableParent, + getFocusableElements: getFocusableElements, + moveLeft: function (sourceElement, options) { + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 0, container, focusableElements); + }, + moveRight: function (sourceElement, options) { + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 1, container, focusableElements); + }, + moveUp: function (sourceElement, options) { + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 2, container, focusableElements); + }, + moveDown: function (sourceElement, options) { + var container = options ? options.container : null; + var focusableElements = options ? options.focusableElements : null; + nav(sourceElement, 3, container, focusableElements); + }, + sendText: sendText, + isCurrentlyFocusable: isCurrentlyFocusable, + pushScope: pushScope, + popScope: popScope, + focusFirst: focusFirst, + focusLast: focusLast, + moveFocus: moveFocus +}; From 6f78cdfde2a4094d110cdeeead802ece5c086d64 Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Mon, 3 Aug 2020 15:08:17 +0200 Subject: [PATCH 031/155] Its build as ES6 module, no need for the strict --- src/components/focusManager.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/focusManager.js b/src/components/focusManager.js index b55f933db6..a9ec377cce 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -3,10 +3,6 @@ import dom from 'dom'; import scrollManager from 'scrollManager'; -/* eslint-disable no-unused-expressions */ -'use strict'; -/* eslint-enable no-unused-expressions */ - var scopes = []; function pushScope(elem) { scopes.push(elem); From 8dced0300d02a506a6c895a3c1d0bc4ee0e6f688 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Mon, 3 Aug 2020 20:51:40 +0200 Subject: [PATCH 032/155] Added livetvseriestimers.js to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 17d175b7e7..d9b7c0970e 100644 --- a/package.json +++ b/package.json @@ -207,6 +207,7 @@ "src/controllers/searchpage.js", "src/controllers/livetvtuner.js", "src/controllers/livetvstatus.js", + "src/controllers/livetv/livetvseriestimers.js", "src/controllers/shows/episodes.js", "src/controllers/shows/tvgenres.js", "src/controllers/shows/tvlatest.js", From b9317ff0627e20b233c7184ee4fb73e883ab7f41 Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 4 Aug 2020 03:51:04 +0900 Subject: [PATCH 033/155] remove unused strings from source --- src/strings/af.json | 3 --- src/strings/ar.json | 12 ------------ src/strings/be-by.json | 2 -- src/strings/bg-bg.json | 25 ------------------------- src/strings/ca.json | 14 -------------- src/strings/cs.json | 25 ------------------------- src/strings/da.json | 24 ------------------------ src/strings/de.json | 25 ------------------------- src/strings/el.json | 15 --------------- src/strings/en-gb.json | 25 ------------------------- src/strings/en-us.json | 25 ------------------------- src/strings/eo.json | 1 - src/strings/es-ar.json | 25 ------------------------- src/strings/es-mx.json | 25 ------------------------- src/strings/es.json | 25 ------------------------- src/strings/es_419.json | 25 ------------------------- src/strings/fa.json | 25 ------------------------- src/strings/fi.json | 15 --------------- src/strings/fr-ca.json | 6 ------ src/strings/fr.json | 25 ------------------------- src/strings/gsw.json | 3 --- src/strings/he.json | 13 ------------- src/strings/hi-in.json | 4 ---- src/strings/hr.json | 15 --------------- src/strings/hu.json | 25 ------------------------- src/strings/id.json | 7 ------- src/strings/is-is.json | 11 ----------- src/strings/it.json | 25 ------------------------- src/strings/ja.json | 18 ------------------ src/strings/kk.json | 24 ------------------------ src/strings/ko.json | 22 ---------------------- src/strings/lt-lt.json | 12 ------------ src/strings/lv.json | 20 -------------------- src/strings/mr.json | 2 -- src/strings/ms.json | 3 --- src/strings/nb.json | 25 ------------------------- src/strings/nl.json | 25 ------------------------- src/strings/pl.json | 25 ------------------------- src/strings/pt-br.json | 25 ------------------------- src/strings/pt-pt.json | 23 ----------------------- src/strings/pt.json | 22 ---------------------- src/strings/ro.json | 25 ------------------------- src/strings/ru.json | 25 ------------------------- src/strings/sk.json | 25 ------------------------- src/strings/sl-si.json | 23 ----------------------- src/strings/sr.json | 5 ----- src/strings/sv.json | 27 +-------------------------- src/strings/tr.json | 10 ---------- src/strings/uk.json | 4 ---- src/strings/vi.json | 10 ---------- src/strings/zh-cn.json | 25 ------------------------- src/strings/zh-hk.json | 10 ---------- src/strings/zh-tw.json | 25 ------------------------- 53 files changed, 1 insertion(+), 929 deletions(-) diff --git a/src/strings/af.json b/src/strings/af.json index a0df4fc037..3b82f0363c 100644 --- a/src/strings/af.json +++ b/src/strings/af.json @@ -36,7 +36,6 @@ "AddToPlaylist": "Voeg by speellys", "AddToPlayQueue": "Plaas in wagtou", "AddToCollection": "Voeg by versameling", - "AddItemToCollectionHelp": "Voeg items by die versamelings deur hulle te soek en regs-kliek of kliek kieslys om hulle by versameling te voeg.", "Add": "Voeg by", "Actor": "Akteur", "AccessRestrictedTryAgainLater": "Toegang is beperk. Probeer weer later .", @@ -84,7 +83,6 @@ "Watched": "Gekyk", "ViewPlaybackInfo": "Beskou terugspeel inligting", "ViewAlbum": "Beskou album", - "VideoRange": "Video reekse", "Vertical": "Vertikaal", "ValueVideoCodec": "Video Kodek: {0}", "ValueTimeLimitSingleHour": "Tyd limiet: 1 uur", @@ -149,7 +147,6 @@ "TabProfiles": "Profiele", "TabProfile": "Profiel", "TabPlaylists": "Speel lyste", - "TabPlaylist": "Speel lys", "TabPlayback": "Terugspeel", "TabPassword": "Wagwoord", "TabParentalControl": "Ouer Beheer", diff --git a/src/strings/ar.json b/src/strings/ar.json index d4ff757824..a9d3d2de41 100644 --- a/src/strings/ar.json +++ b/src/strings/ar.json @@ -1,5 +1,4 @@ { - "AddItemToCollectionHelp": "أضف عناصر إلى المجاميع بالبحث عنهم واستخدام قائمة الزر الأيمن أو قئامة اللمس لإضافتهم إلى المجاميع.", "AdditionalNotificationServices": "تصفح كتالوج الملحقات لتثبيث خدمات إشعارات إضافية.", "Alerts": "التنبيهات", "All": "الكل", @@ -40,7 +39,6 @@ "ButtonHelp": "المساعدة", "ButtonHome": "الرئيسية", "ButtonInfo": "معلومات", - "ButtonLearnMore": "إعرف المزيد", "ButtonLibraryAccess": "صلاحيات المكتبة", "ButtonManualLogin": "الدخول اليدوي", "ButtonMore": "المزيد", @@ -85,7 +83,6 @@ "ButtonTrailer": "العرض الإعلاني", "ButtonUninstall": "إزالة التثبيت", "ButtonUp": "أعلى", - "ButtonViewWebsite": "أنظر الموقع الإلكتروني", "ButtonWebsite": "موقع إلكتروني", "ChannelAccessHelp": "إختر قناة لمشاركتها مع هذا المستخدم. المدراء سيكونون قادرين على تغيير إعدادات القنوات باستخدام مدير واصفات البيانات.", "Channels": "القنوات", @@ -151,7 +148,6 @@ "HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بخادم أمبي. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بحساب أمبي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.", "HeaderApp": "التطبيق", "HeaderAudioSettings": "إعدادات الصوت", - "HeaderAutomaticUpdates": "التحديثات الآلية", "HeaderBooks": "الكتب", "HeaderBranding": "وسومات البرنامج", "HeaderCastAndCrew": "الممثلين وطاقم العمل", @@ -313,8 +309,6 @@ "LabelAlbumArtists": "فنانو الألبومات:", "LabelAll": "الجميع", "LabelAllowHWTranscoding": "السماح بالتشفير البيني بعتاد الحاسب", - "LabelAllowServerAutoRestart": "السماح للخادم أن يعيد التشغيل آلياً لتفعيل التحديثات", - "LabelAllowServerAutoRestartHelp": "الخادم سيعيد التشغيل في فترات الركود فقط، حين لا يكون هناك أي مستخدمين متصلين.", "LabelAppName": "اسم التطبيق", "LabelAppNameExample": "مثال: Sickbeard، NzbDrone", "LabelArtists": "الفنانون:", @@ -552,7 +546,6 @@ "LabelXDlnaCapHelp": "تحدد محتوى عنصر X_DLNACAP في النطاق الاسمي لـ urn:schemas-dlna-org:device-1-0 .", "LabelXDlnaDoc": "وثيقة X-Dlna:", "LabelXDlnaDocHelp": "تحدد محتوى عنصر X_DLNADOC في النطاق الاسمي لـ urn:schemas-dlna-org:device-1-0 .", - "LabelYourFirstName": "الإسم الاول:", "LabelYoureDone": "تم الانتهاء!", "LabelZipCode": "الرمز البريدي:", "LabelffmpegPath": "مسار ffmpeg:", @@ -603,7 +596,6 @@ "MessageFileReadError": "حصل خطأ أثناء قراءة الملف. الرجاء المحاولة مرة اخرى.", "MessageForgotPasswordFileCreated": "الملف التالي قد أنشيء على خادمك وهو يحتوي على التوجيهات لكيفية البدء:", "MessageForgotPasswordInNetworkRequired": "الرجاء المحاولة من خلال شبكة المنزل لبدء عملية إعادة إعداد كملة السر.", - "MessageInstallPluginFromApp": "هذا الملحق يجب أن يثبت من داخل التطبيق الذي تريد استخدامه بداخله.", "MessageInvalidForgotPasswordPin": "لقد تم إدخال رمز شخصي غير صحيح أو منتهي الصلاحية. الرجاء المحاولة مرة أخرى.", "MessageInvalidUser": "اسم المستخدم أو كلمة السر غير صحيحة. الرجاء المحاولة مرة أخرى.", "MessageItemSaved": "تم حفظ العنصر.", @@ -834,7 +826,6 @@ "TabParentalControl": "التحكم الأبوي", "TabPassword": "كلمة السر", "TabPlayback": "تشغيل", - "TabPlaylist": "قائمة التشغيل", "TabPlaylists": "قوائم التشغيل", "TabPlugins": "الملحقات", "TabProfile": "عريضة", @@ -938,7 +929,6 @@ "Banner": "بانر", "Backdrops": "خلفيات متغيرة للصفحة", "Backdrop": "خلفية متغيرة للصفحة", - "AutoBasedOnLanguageSetting": "تلقائي ( بناءً على إعدادات اللغة)", "Auto": "تلقائي", "AuthProviderHelp": "حدد مقدم المصادقات ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.", "AroundTime": "حول", @@ -997,7 +987,6 @@ "MediaInfoStreamTypeEmbeddedImage": "الصورة المضمنة", "MediaInfoStreamTypeData": "البيانات", "MediaInfoStreamTypeAudio": "الصوت", - "MediaInfoSoftware": "البرمجيات", "MediaIsBeingConverted": "يتم تحويل الوسط الى صيغة متوافقة مع الحهاز الذي يشغل الوسط.", "MediaInfoStreamTypeVideo": "فيديو", "ContinueWatching": "اكمل المشاهدة", @@ -1059,7 +1048,6 @@ "DefaultSubtitlesHelp": "يتم تحميل الترجمات استنادًا إلى العلامات الافتراضية والقسرية في البيانات الوصفية المضمنة. سيتم اعتبار تفضيلات اللغة عند توفر خيارات متعددة.", "DefaultMetadataLangaugeDescription": "هذه هي إعداداتك الافتراضية ويمكن تخصيصها على أساس كل مكتبة.", "Default": "افتراضي", - "CopyStreamURLError": "توجد مشكله في نسخ الرابط", "CopyStreamURL": "نسخ عنوان الرابط", "Continuing": "مستمر", "CopyStreamURLSuccess": "URL copied successfully.", diff --git a/src/strings/be-by.json b/src/strings/be-by.json index 28cf51bcde..e8726bf216 100644 --- a/src/strings/be-by.json +++ b/src/strings/be-by.json @@ -11,10 +11,8 @@ "LabelFinish": "Гатова", "LabelNext": "Наступнае", "LabelPrevious": "Папярэдняе", - "LabelYourFirstName": "Ваша імя:", "LabelYoureDone": "Вы скончылі!", "MoreUsersCanBeAddedLater": "Потым можна дадаць яшчэ карыстальнікаў праз «Інфапанэль».", - "TabPlaylist": "Плэйліст", "TellUsAboutYourself": "Раскажыце пра сябе", "ThisWizardWillGuideYou": "Гэты памочнік правядзе вас праз усе фазы ўстаноўкі і налады. Спачатку абярыце упадабаную мову.", "UserProfilesIntro": "У Jellyfin існуе ўбудаваная падтрымка для карыстальніцкіх профіляў, дазваляючы кожнаму карыстальніку валодаць сваімі ўласнымі параметрамі адлюстравання, станам прайгравання і кіраваннем ўтрымання.", diff --git a/src/strings/bg-bg.json b/src/strings/bg-bg.json index f24c10d980..6eee5e403b 100644 --- a/src/strings/bg-bg.json +++ b/src/strings/bg-bg.json @@ -14,7 +14,6 @@ "AttributeNew": "Нови", "Audio": "Звук", "Auto": "Автоматично", - "AutoBasedOnLanguageSetting": "Автоматично (според езика)", "Backdrop": "Фон", "Backdrops": "Фонове", "BirthDateValue": "Роден/а на: {0}", @@ -45,7 +44,6 @@ "ButtonHelp": "Помощ", "ButtonHome": "Начало", "ButtonInfo": "Сведения", - "ButtonLearnMore": "Научете повече", "ButtonLibraryAccess": "Достъп до библиотеката", "ButtonManualLogin": "Вход с име и парола", "ButtonMore": "Още", @@ -159,7 +157,6 @@ "HeaderApiKeys": "ППИ ключове", "HeaderApp": "Програма", "HeaderAudioSettings": "Настройки на звука", - "HeaderAutomaticUpdates": "Автоматични обновления", "HeaderBooks": "Книги", "HeaderCastAndCrew": "Артисти и изпълнители", "HeaderCastCrew": "Артисти и изпълнители", @@ -285,8 +282,6 @@ "LabelAlbumArtMaxWidth": "Максимална ширина на албумното изкуство:", "LabelAlbumArtPN": "ПН на албумното изкуство:", "LabelAlbumArtists": "Изпълнители на албума:", - "LabelAllowServerAutoRestart": "Разрешаване на сървъра автоматично да се пуска повторно за прилагане на обновления", - "LabelAllowServerAutoRestartHelp": "Сървърът ще се пуска наново само през ненатоварено време, когато няма активни потребители.", "LabelAppName": "Име", "LabelArtists": "Изпълнители:", "LabelArtistsHelp": "Отделете няколко с ;", @@ -307,7 +302,6 @@ "LabelCustomCssHelp": "Добавете собствен стил към уеб-интерфейса.", "LabelCustomDeviceDisplayName": "Показвано име:", "LabelCustomRating": "Оценка по избор:", - "LabelDashboardTheme": "Облик на сървърното табло:", "LabelDateAdded": "Дата на добавяне:", "LabelDateTimeLocale": "Местоположение за дата и час:", "LabelDay": "Ден:", @@ -452,13 +446,11 @@ "LabelUsername": "Потребителско име:", "LabelVersion": "Версия:", "LabelYear": "Година:", - "LabelYourFirstName": "Първото ви име:", "LabelYoureDone": "Готови сте!", "Large": "Голям", "LatestFromLibrary": "Последни {0}", "LibraryAccessHelp": "Изберете библиотеките, които да споделите с потребителя. Администраторите ще могат да редактират всички папки, използвайки управлението на метаданни.", "Like": "Харесване", - "LinksValue": "Препратки: {0}", "List": "Списък", "Live": "На живо", "LiveTV": "Телевизия на живо", @@ -715,7 +707,6 @@ "TabParentalControl": "Родителски контрол", "TabPassword": "Парола", "TabPlayback": "Възпроизвеждане", - "TabPlaylist": "Списък", "TabPlaylists": "Списъци", "TabPlugins": "Приставки", "TabProfile": "Профил", @@ -817,7 +808,6 @@ "ReplaceExistingImages": "Заменяне на текущите изображения", "Channels": "Канали", "Categories": "Категории", - "ButtonViewWebsite": "Преглед на сайта", "ButtonUp": "Нагоре", "ButtonTrailer": "Предварителен откъс", "ButtonStart": "Пускане", @@ -830,7 +820,6 @@ "ButtonConnect": "Свързване", "AllowOnTheFlySubtitleExtraction": "Позволява моментално извличане на поднадписи", "AllowHWTranscodingHelp": "Позволява на тунера да прекодира моментално. Това може да помогне за редуциране на прекодирането от сървъра.", - "AddItemToCollectionHelp": "Добавяне към колекция чрез търсенето им и използване на дясно-щракване с мишката или контекстното меню.", "Absolute": "Aбсолютен", "LabelLanNetworks": "Локални мрежи:", "LabelKodiMetadataSaveImagePathsHelp": "Това е препоръчително, ако наименованието на изображенията не са съобразени с изискванията на Kodi.", @@ -885,7 +874,6 @@ "ErrorDefault": "Възникна грешка при изпълнение на заявката. Моля опитайте по-късно.", "CustomDlnaProfilesHelp": "Създаване на персонализиран профил за целево устройство или заменяне на системния профил.", "CopyStreamURL": "Копиране URL на стрийма", - "CopyStreamURLError": "Възникна грешка при копиране на URL.", "CopyStreamURLSuccess": "URL се копира успешно.", "Connect": "Свързване", "ConfirmEndPlayerSession": "Искате ли да изключите Jellyfin на {0}?", @@ -1140,7 +1128,6 @@ "LabelNumber": "Номер:", "LabelNotificationEnabled": "Включване на известие", "LabelNewsCategories": "Категории новини:", - "LabelNightly": "Тестов", "LabelStable": "Стабилна", "LabelChromecastVersion": "Версия на Chromecast", "LabelMusicStreamingTranscodingBitrateHelp": "Посочете максимален битрейт при поточно предаване на музика.", @@ -1223,7 +1210,6 @@ "XmlDocumentAttributeListHelp": "Тези атрибути се прилагат към коренния елемент на всеки XML отговор.", "Whitelist": "Бял списък", "ViewPlaybackInfo": "Вижте информация за възпроизвеждането", - "VideoRange": "Диапазон на видео", "ValueTimeLimitSingleHour": "Времеви лимит: 1 час", "ValueTimeLimitMultiHour": "Времеви лимит {0} часове", "ValueContainer": "Контейнер: {0}", @@ -1284,12 +1270,10 @@ "LabelStreamType": "Вид на потока:", "LabelStopping": "Спиране", "LabelSportsCategories": "Спортни категории:", - "LabelSoundEffects": "Звукови ефекти:", "LabelSortTitle": "Подреди по заглавие:", "LabelSonyAggregationFlags": "\"Флагове\" за статистическа обработка на Сони:", "LabelSkipIfGraphicalSubsPresentHelp": "Наличието на текстови версии на субтитрите ще доведе до по-ефективна доставка и намаляване на вероятността от транскодиране на видеото.", "LabelSkipIfAudioTrackPresentHelp": "Махнете отметката ,за да се гарантира ,че всички видеофайлове имат субтитри,независимо от езика на аудиото им.", - "LabelSkin": "Облик:", "LabelSize": "Размер:", "LabelSimultaneousConnectionLimit": "Ограничение на броя едновременни потоци:", "LabelServerName": "Име на сървъра:", @@ -1306,7 +1290,6 @@ "MessageConfirmAppExit": "Искате ли да излезете?", "MessageAreYouSureDeleteSubtitles": "Сигурни ли се ,че искате да изтриете файла със субтитри?", "MediaIsBeingConverted": "Медията е конвертирана във формат ,който е съвместим с устройството ,което ще я възпроизведе.", - "MediaInfoSoftware": "Софтуер", "MediaInfoTimestamp": "Времеви отпечатък", "MediaInfoSampleRate": "Кадрова честота", "MediaInfoRefFrames": "Ref кадри", @@ -1320,8 +1303,6 @@ "LiveBroadcasts": "Предавания на живо", "LeaveBlankToNotSetAPassword": "Можете да оставите това поле празно и да не задавате парола.", "LearnHowYouCanContribute": "Научете как можете да допринесете.", - "LaunchWebAppOnStartupHelp": "Отвори уеб клиента във браузъра по подразбиране при първото стартиране на сървъра.Това няма да се случи при използване на функцията на сървъра за рестартиране.", - "LaunchWebAppOnStartup": "Стартирай уеб интерфейса ,когато се стартира сървъра", "LanNetworksHelp": "Списък разделен със запетая съдържащ ИП адреси или записи за ИП/мрежова маски отнасящи се за мрежи ,които ще се считат за локални ,когато се налагат ограничения в честотната лента.Ако е зададено всички други ИП адреси ще се считат за принадлежащи към външни мрежи и за тях ще важат правилата за ограничения на външни ИП -та.Ако полето е празно ще се счита ,че само подмрежата на сървъра е част от локалната мрежа.", "LabelffmpegPathHelp": "Пътят към файла на приложението ffmpeg или папката, съдържаща ffmpeg.", "LabelffmpegPath": "Път към FFmpeg:", @@ -1383,10 +1364,8 @@ "MessageLeaveEmptyToInherit": "Оставете празни, за да наследите настройки от родителски елемент или глобалната стойност по подразбиране.", "MessageItemsAdded": "Добавени са елементи.", "MessageItemSaved": "Елементът е запазен.", - "MessageUnauthorizedUser": "Понастоящем нямате право да получите достъп до сървъра. Моля, свържете се с администратора на вашия сървър за повече информация.", "MessageInvalidUser": "Невалидно потребителско име или парола. Моля, опитайте отново.", "MessageInvalidForgotPasswordPin": "Въведен е невалиден или изтекъл пин код. Моля, опитайте отново.", - "MessageInstallPluginFromApp": "Този плъгин трябва да бъде инсталиран от приложението, в което възнамерявате да го използвате.", "MessageImageTypeNotSelected": "Изберете типът изображение от падащото меню.", "MessageImageFileTypeAllowed": "Поддържат се само файлове с разширение JPEG и PNG.", "MessageForgotPasswordInNetworkRequired": "Опитайте пак в домашната мрежа да повторите процеса по нулиране на паролата.", @@ -1413,12 +1392,10 @@ "SaveSubtitlesIntoMediaFoldersHelp": "Съхраняването на субтитрите при видео файлове ще позволи по-лесното им управление.", "SaveSubtitlesIntoMediaFolders": "Запазване на субтитрите в папките с медията", "SaveChanges": "Запазете промените", - "RunAtStartup": "Пускай при стартиране", "RepeatOne": "Повтори един път", "RepeatMode": "Режим на повторение", "RepeatEpisodes": "Повтори епизодите", "RepeatAll": "Повтори всички", - "ReleaseGroup": "Издаден от група", "RefreshQueued": "Назначено е обновяване.", "RefreshDialogHelp": "Метаданните се обновяват въз основа на настройките и интернет услугите, които са активирани от таблото за управление на сървъра.", "Recordings": "Записи", @@ -1428,7 +1405,6 @@ "RecommendationStarring": "В главните роли {0}", "RecommendationDirectedBy": "Режисьор {0}", "Rate": "Оценка", - "QueueAllFromHere": "Поред всичко от тук", "ProductionLocations": "Места на заснемане", "Previous": "Предишен", "PreferEmbeddedEpisodeInfosOverFileNames": "Предпочитай \"вградената\" информация за епизода вместо името на файла", @@ -1457,7 +1433,6 @@ "PackageInstallFailed": "Инсталирането на {0} версия {1}) е неуспешно.", "PackageInstallCompleted": "Инсталирането на {0} версия {1}) е завършено.", "PackageInstallCancelled": "Инсталирането на {0} версия {1}) е отменено.", - "OtherArtist": "Друг изпълнител", "OptionWeekends": "Почивни дни", "OptionWeekdays": "Делници", "OptionTvdbRating": "Рейтинг според ТВДБ", diff --git a/src/strings/ca.json b/src/strings/ca.json index cf6031e240..7250a6cb31 100644 --- a/src/strings/ca.json +++ b/src/strings/ca.json @@ -39,7 +39,6 @@ "ButtonGuide": "Guia", "ButtonHelp": "Ajuda", "ButtonHome": "Inici", - "ButtonLearnMore": "Aprèn més", "ButtonLibraryAccess": "Accés a la biblioteca", "ButtonManualLogin": "Inici de sessió manual", "ButtonMore": "Més", @@ -75,7 +74,6 @@ "ButtonSubmit": "Envia", "ButtonSubtitles": "Subtítols", "ButtonTrailer": "Tràiler", - "ButtonViewWebsite": "Veure website", "CancelRecording": "Cancel·la enregistrament", "CancelSeries": "Cancel·la sèrie", "ChannelAccessHelp": "Selecciona els canals a compartir amb aquest usuari. Els administradors podran editar tots els canals emprant el gestor de metadades.", @@ -138,7 +136,6 @@ "HeaderApiKeys": "Claus Api", "HeaderApiKeysHelp": "Les aplicacions externes requereixen una Api key pere tal de poder-se comunicar amb el Servidor d'Jellyfin. Les claus són emeses iniciant sessió amb un compte d'Jellyfin, o concedint manualment una clau a l'aplicació.", "HeaderAudioSettings": "Preferències d'Àudio", - "HeaderAutomaticUpdates": "Actualitzacions Automàtiques", "HeaderBooks": "Llibres", "HeaderBranding": "Aparença", "HeaderCancelRecording": "Cancel·lar Enregistrament", @@ -271,8 +268,6 @@ "LabelAlbum": "Àlbum:", "LabelAlbumArtMaxHeight": "Alçada màxima de l'art de l'àlbum:", "LabelAlbumArtMaxWidth": "Amplada màxima de l'art de l'àlbum:", - "LabelAllowServerAutoRestart": "Permetre el servidor reiniciar-se automàticament per aplicar actualitzacions", - "LabelAllowServerAutoRestartHelp": "El servidor només es reiniciarà durant períodes d'inactivitat, quan no tingui usuaris actius.", "LabelArtists": "Artistes:", "LabelArtistsHelp": "Separa'n varis emprant ;", "LabelAudioLanguagePreference": "Preferència de l'idioma de l'àudio:", @@ -292,7 +287,6 @@ "LabelCustomCss": "CSS propi:", "LabelCustomCssHelp": "Aplica el teu propi css a la interfície web.", "LabelCustomDeviceDisplayName": "Nom a mostrar:", - "LabelDashboardTheme": "Tema del tauler de control del servidor:", "LabelDateAdded": "Data afegit:", "LabelDay": "Dia:", "LabelDeathDate": "Data de defunció:", @@ -413,9 +407,7 @@ "LabelSendNotificationToUsers": "Envia la notificació a:", "LabelSerialNumber": "Nombre de sèrie", "LabelSeriesRecordingPath": "Directori de gravació de sèries (opcional):", - "LabelSkin": "Aspecte:", "LabelSortTitle": "Títol d'endreçat:", - "LabelSoundEffects": "Efectes de so:", "LabelSource": "Font:", "LabelStartWhenPossible": "Inicia quan sigui possible:", "LabelStatus": "Estat:", @@ -439,7 +431,6 @@ "LabelUsername": "Nom d'usuari:", "LabelValue": "Valor:", "LabelYear": "Any:", - "LabelYourFirstName": "El teu nom:", "LabelYoureDone": "Ja està!", "LatestFromLibrary": "Novetats a {0}", "LibraryAccessHelp": "Selecciona els directoris dels multimèdia a compartir amb aquest usuari. Els administradors podran editar tots els directoris emprant el gestor de metadades.", @@ -604,7 +595,6 @@ "Producer": "Productor", "Programs": "Programes", "Quality": "Qualitat", - "QueueAllFromHere": "Afegeix tots a la cua des d'aquí", "RecentlyWatched": "Reproduït recentment", "RecommendationBecauseYouWatched": "Ja que has vist {0}", "Record": "Grava", @@ -623,7 +613,6 @@ "ReplaceAllMetadata": "Reemplaça totes les metadades", "ReplaceExistingImages": "Reemplaça imatges existents", "ResumeAt": "Reprodueix des de {0}", - "RunAtStartup": "Arrenca en iniciar", "Saturday": "Dissabte", "Save": "Desa", "Screenshots": "Captures de pantalla", @@ -681,7 +670,6 @@ "TabParentalControl": "Control Parental", "TabPassword": "Contrasenya", "TabPlayback": "Reproducció", - "TabPlaylist": "Llista de reproducció", "TabPlaylists": "Llistes de reproducció", "TabPlugins": "Complements", "TabProfile": "Perfil", @@ -790,7 +778,6 @@ "AirDate": "Data d'emissió", "AdditionalNotificationServices": "Examineu el catàleg de complements per instal·lar serveis de notificació addicionals.", "AddedOnValue": "Afegit {0}", - "AddItemToCollectionHelp": "Afegiu els elements a les col·leccions buscant-los i fent clic amb el botó dret o toqueu els menús per afegir-los a una col·lecció.", "Actor": "Actor", "Absolute": "Absolut", "ClientSettings": "Configuració del client", @@ -808,7 +795,6 @@ "BookLibraryHelp": "Els àudio i llibres de text són compatibles. Reviseu la {0} guia de denominació de llibres {1}.", "Backdrops": "Fons", "Backdrop": "Fons", - "AutoBasedOnLanguageSetting": "Auto (basada en la configuració de l’idioma)", "Artist": "Artista", "AllowedRemoteAddressesHelp": "Llista d’adreces IP o d’entrades IP / netmasca separades per comes per a xarxes que podran connectar-se de forma remota. Si es deixa en blanc, es permetran totes les adreces remotes.", "AllowFfmpegThrottlingHelp": "Quan un transcòdi o un remux estigui prou lluny de la posició de reproducció actual, feu una pausa en el procés perquè consumirà menys recursos. Això és més útil per mirar sense buscar sovint. Desactiveu-la si teniu problemes de reproducció.", diff --git a/src/strings/cs.json b/src/strings/cs.json index 3ca8676c1b..80415c24af 100644 --- a/src/strings/cs.json +++ b/src/strings/cs.json @@ -2,7 +2,6 @@ "AccessRestrictedTryAgainLater": "Přístup je v současné době omezen. Prosím zkuste to znovu později.", "Actor": "Herec", "Add": "Přidat", - "AddItemToCollectionHelp": "Přidat položky do kolekce jejich vyhledáním a použitím pravého tlačítka myši nebo kliknutím na příslušnou položku v nabídce.", "AddToCollection": "Přidat do kolekce", "AddToPlayQueue": "Přidat do fronty k přehrání", "AddToPlaylist": "Přidat do playlistu", @@ -63,7 +62,6 @@ "ButtonGuide": "Programový průvodce", "ButtonHelp": "Nápověda", "ButtonHome": "Domů", - "ButtonLearnMore": "Zjistit více", "ButtonLibraryAccess": "Přístup ke knihovně", "ButtonManualLogin": "Manuální přihlášení", "ButtonMore": "Více", @@ -105,7 +103,6 @@ "ButtonTrailer": "Upoutávka", "ButtonUninstall": "Odinstalovat", "ButtonUp": "Zesílit", - "ButtonViewWebsite": "Přejít na webové stránky", "ButtonWebsite": "Webové stránky", "CancelRecording": "Zrušit nahrávání", "CancelSeries": "Ukončit Seriál", @@ -239,7 +236,6 @@ "HeaderApp": "Aplikace", "HeaderAudioBooks": "Audio knihy", "HeaderAudioSettings": "Nastavení zvuku", - "HeaderAutomaticUpdates": "Automatické aktualizace", "HeaderBooks": "Knihy", "HeaderBranding": "Branding", "HeaderCancelRecording": "Zrušit nahrávání", @@ -436,8 +432,6 @@ "LabelAlbumArtists": "Alba umělce:", "LabelAll": "Vše", "LabelAllowHWTranscoding": "Povolit hardwarové překódování", - "LabelAllowServerAutoRestart": "Povolit automatický restart serveru pro provedení aktualizace", - "LabelAllowServerAutoRestartHelp": "Server bude restartován pouze v době nečinnosti, pokud nejsou aktivní žádní uživatelé.", "LabelAppName": "Název aplikace", "LabelAppNameExample": "Příklad: Sickbeard, Sonarr", "LabelArtists": "Umělci:", @@ -465,7 +459,6 @@ "LabelCustomDeviceDisplayName": "Jméno pro zobrazení:", "LabelCustomDeviceDisplayNameHelp": "Nahradit vlastním názvem zobrazení nebo ponechte prázdné, aby název byl určen zařízením.", "LabelCustomRating": "Vlastní hodnocení:", - "LabelDashboardTheme": "Téma nástěnky serveru:", "LabelDateAdded": "Datum přidání:", "LabelDateAddedBehavior": "Nový obsah řadit dle data:", "LabelDateAddedBehaviorHelp": "Pokud je hodnota metadat přítomna, bude vždy použita před některou z těchto možností.", @@ -668,7 +661,6 @@ "LabelSonyAggregationFlags": "Agregační příznaky Sony:", "LabelSonyAggregationFlagsHelp": "Určuje obsah prvku aggregationFlags ve jmenném prostoru urn:schemas-sonycom:av.", "LabelSortTitle": "Třídit dle názvu:", - "LabelSoundEffects": "Zvukové efekty:", "LabelSource": "Zdroj:", "LabelSportsCategories": "Sportovní kategorie:", "LabelStartWhenPossible": "Začít jakmile je to možné:", @@ -713,7 +705,6 @@ "LabelXDlnaDoc": "Dokumentace X-DLNA:", "LabelXDlnaDocHelp": "Určuje obsah prvku X_DLNADOC ve jmenném prostoru urn:schemas-dlna-org:device-1-0.", "LabelYear": "Rok:", - "LabelYourFirstName": "Vaše jméno:", "LabelYoureDone": "Hotovo!", "LabelZipCode": "PSČ:", "LabelffmpegPath": "FFmpeg - cesta:", @@ -775,7 +766,6 @@ "MessageFileReadError": "Došlo k chybě při čtení souboru. Prosím zkuste to znovu.", "MessageForgotPasswordFileCreated": "Následující soubor byl vytvořen na serveru a obsahuje pokyny, jak postupovat:", "MessageForgotPasswordInNetworkRequired": "Zkuste to prosím znovu uvnitř vaší domácí sítě pro zahájení procesu resetování hesla.", - "MessageInstallPluginFromApp": "Tento plugin musí být nainstalován z aplikace, kterou chcete použít.", "MessageInvalidForgotPasswordPin": "Neplatné zadání pin kódu. Prosím, zkuste to znovu.", "MessageInvalidUser": "Neplatné uživatelské jméno nebo heslo. Zkuste znovu.", "MessageItemSaved": "Položka uložena.", @@ -995,7 +985,6 @@ "ProductionLocations": "Místo výroby", "Programs": "Programy", "Quality": "Kvalita", - "QueueAllFromHere": "Zařadit vše do fronty", "RecentlyWatched": "Nedávno shlédnuté", "RecommendationBecauseYouLike": "Protože se vám líbí {0}", "RecommendationBecauseYouWatched": "Protože jste sledovali {0}", @@ -1024,7 +1013,6 @@ "ReplaceExistingImages": "Nahradit existující obrázky", "ResumeAt": "Obnovit přehrávání od {0}", "Rewind": "Přetočit zpět", - "RunAtStartup": "Spustit po startu", "Runtime": "Délka", "Saturday": "Sobota", "Save": "Uložit", @@ -1186,7 +1174,6 @@ "AllowedRemoteAddressesHelp": "Seznam IP adres nebo síťových masek oddělených čárkou pro sítě, ze kterých se lze vzdáleně připojit. Pokud necháte prázdné, všechny adresy budou povoleny.", "AnyLanguage": "Jakýkoli jazyk", "Ascending": "Vzestupně", - "AutoBasedOnLanguageSetting": "Automaticky (na základě jazykového nastavení)", "Banner": "Výřez plakátu", "Blacklist": "Zakázat vše kromě výjimek", "Browse": "Procházet", @@ -1282,7 +1269,6 @@ "LabelRemoteClientBitrateLimitHelp": "Volitelný limit datového toku pro všechna síťová zařízení. To je užitečné, aby se zabránilo požadavkům na vyšší přenosovou rychlost než zvládne vaše připojení k internetu. To může mít za následek zvýšení zátěže procesoru, aby bylo možné převádět videa za běhu na nižší datový tok.", "LabelServerHost": "Host:", "LabelSimultaneousConnectionLimit": "Limit současně běžících streamů:", - "LabelSkin": "Vzhled:", "LabelSortBy": "Řadit podle:", "LabelSortOrder": "Pořadí řazení:", "LabelSpecialSeasonsDisplayName": "Zobrazovaný název pro zvláštní sezónu:", @@ -1296,12 +1282,10 @@ "LabelVideo": "Video", "LabelVideoCodec": "Video kodek:", "LeaveBlankToNotSetAPassword": "Můžete ponechat prázdné pro nastavení bez hesla.", - "LinksValue": "Odkazy: {0}", "LiveTV": "Televize", "Logo": "Logo", "ManageLibrary": "Spravovat knihovnu", "MediaInfoDefault": "Výchozí", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeVideo": "Video", @@ -1383,7 +1367,6 @@ "TabInfo": "Info", "TabLiveTV": "Televize", "TabMetadata": "Metadata", - "TabPlaylist": "Playlist", "TabServer": "Server", "TagsValue": "Tagy: {0}", "ThemeSongs": "Tematická hudba", @@ -1397,14 +1380,11 @@ "ValueOneAlbum": "1 album", "ValueOneSong": "1 skladba", "Vertical": "Svisle", - "VideoRange": "Rozsah videa", "ViewPlaybackInfo": "Zobrazení informací o přehrávání", "Whitelist": "Povolit vše kromě výjimek", "HeaderHome": "Domů", "DashboardOperatingSystem": "Operační systém: {0}", "DashboardArchitecture": "Architektura: {0}", - "LaunchWebAppOnStartup": "Spustit webové rozhraní po spustění serveru", - "LaunchWebAppOnStartupHelp": "Otevře se webový klient ve vašem výchozím webovém prohlížeči, když server se spustí. K tomu nedochází při použití funkce restartování serveru.", "MessageNoServersAvailable": "Pomocí automatického zjišťování nebyly nalezeny žádné servery.", "OptionBanner": "Banner", "OptionList": "Seznam", @@ -1446,7 +1426,6 @@ "HeaderNavigation": "Navigace", "ButtonSplit": "Rozdělit", "MessageConfirmAppExit": "Přejete si odejít?", - "CopyStreamURLError": "Při kopírování URL došlo k chybě.", "LabelVideoResolution": "Rozlišení videa:", "LabelStreamType": "Typ streamu:", "LabelPlayerDimensions": "Zobrazené rozlišení:", @@ -1460,11 +1439,9 @@ "BoxSet": "Sbírka", "Track": "Stopa", "Season": "Sezóna", - "ReleaseGroup": "Vydavatel", "PreferEmbeddedEpisodeInfosOverFileNames": "Preferovat vloženou informaci o epizodě před názvem souboru", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Používat informaci o epizodě z vložených metadat, pokud jsou k dispozici.", "Person": "Osoba", - "OtherArtist": "Ostatní interpreti", "Movie": "Film", "Episode": "Epizoda", "ClientSettings": "Nastavení klienta", @@ -1490,12 +1467,10 @@ "LabelDeinterlaceMethod": "Metoda odstranění prokládání:", "DeinterlaceMethodHelp": "Vyberte metodu odstranění prokládání obrazu při překódování obsahu.", "UnsupportedPlayback": "Jellyfin nedokáže dešifrovat obsah chráněný Správou digitálních práv (DRM), ale pokusí se zobrazit veškerý obsah, včetně toho chráněného. Některé soubory se nemusí vůbec zobrazit kvůli šifrování nebo jiným nepodporovaným funkcím, např.: interaktivním názvům.", - "MessageUnauthorizedUser": "Momentálně nemáte oprávnění k přístupu na server. Další informace získáte od správce serveru.", "Filter": "Filtr", "New": "Nové", "ButtonTogglePlaylist": "Playlist", "ButtonToggleContextMenu": "Více", - "LabelNightly": "Aktualizace každou noc", "LabelStable": "Stabilní", "LabelChromecastVersion": "Verze Chromecastu", "ApiKeysCaption": "Seznam povolených API klíčů", diff --git a/src/strings/da.json b/src/strings/da.json index 0f4433baff..89bdd61808 100644 --- a/src/strings/da.json +++ b/src/strings/da.json @@ -1,7 +1,6 @@ { "Actor": "Skuespiller", "Add": "Tilføj", - "AddItemToCollectionHelp": "Tilføj emner til samlinger ved at fremsøge dem, og herefter ved højre klik eller tap-menu at tilføje dem til samlinger.", "AddToCollection": "Tilføj til samling", "AddToPlayQueue": "Tilføj til afspilningskø", "AddToPlaylist": "Tilføj til afspilningsliste", @@ -58,7 +57,6 @@ "ButtonGotIt": "Forstået", "ButtonHelp": "Hjælp", "ButtonHome": "Hjem", - "ButtonLearnMore": "Lær mere", "ButtonLibraryAccess": "Biblioteksadgang", "ButtonManualLogin": "Manuel Login", "ButtonMore": "Mere", @@ -98,7 +96,6 @@ "ButtonSubtitles": "Undertekster", "ButtonUninstall": "Afinstaller", "ButtonUp": "Op", - "ButtonViewWebsite": "Besøg hjemmeside", "ButtonWebsite": "Hjemmeside", "CancelRecording": "Annuller optagelse", "CancelSeries": "Annuller serie", @@ -201,7 +198,6 @@ "HeaderApiKeys": "API Nøgler", "HeaderApiKeysHelp": "Eksterne applikationer skal have en API-nøgle for at kunne kommunikere med Jellyfin. Nøgler udstedes ved at logge ind med en Jellyfin konto, eller ved manuelt at tildele applikationen en nøgle.", "HeaderAudioSettings": "Lydindstillinger", - "HeaderAutomaticUpdates": "Automatiske opdateringer", "HeaderBlockItemsWithNoRating": "Klokér titler uden eller med ukendt bedømmelses information:", "HeaderBooks": "Bøger", "HeaderCancelRecording": "Annuller Optagelse", @@ -395,8 +391,6 @@ "LabelAlbumArtists": "Albumartister:", "LabelAll": "Alle", "LabelAllowHWTranscoding": "Tillad hardware-omkodning", - "LabelAllowServerAutoRestart": "Tillad serveren at genstarte automatisk for at påføre opdateringer", - "LabelAllowServerAutoRestartHelp": "Serveren vil kun genstarte i inaktive perioder, når ingen brugere er aktive.", "LabelAllowedRemoteAddresses": "Fjernadgang IP adresse filter:", "LabelAllowedRemoteAddressesMode": "Fjernadgang IP adresse filter mode:", "LabelAppName": "App navn", @@ -657,7 +651,6 @@ "LabelVersionInstalled": "{0} installeret", "LabelXDlnaCapHelp": "Angiver indholdet i X_DLNACAP elementet i urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Angiver indholdet i X_DLNADOC elementet i urn:schemas-dlna-org:device-1-0.", - "LabelYourFirstName": "Dit fornavn:", "LabelYoureDone": "Du er færdig!", "LabelZipCode": "Postnummer:", "LabelffmpegPath": "FFmpeg sti:", @@ -709,7 +702,6 @@ "MessageFileReadError": "Der opstod en fejl i forsøget på at læse filen.", "MessageForgotPasswordFileCreated": "Den følgende fil er blevet oprettet på din server og indeholder instruktioner vedrørende hvordan du skal fortsætte:", "MessageForgotPasswordInNetworkRequired": "Prøv igen inde i dit hjemmenetværk for at igangsætte nulstilling af din adgangskode.", - "MessageInstallPluginFromApp": "Dette plugin skal installeres fra den app, du har til hensigt at bruge det i.", "MessageInvalidForgotPasswordPin": "En ugyldig eller udløbet pinkode blev indtastet. Prøv igen.", "MessageInvalidUser": "Ukendt brugernavn eller adgangskode. Prøv igen.", "MessageItemSaved": "Element gemt.", @@ -914,7 +906,6 @@ "ProductionLocations": "Produktionslokationer", "Programs": "Programmer", "Quality": "Kvalitet", - "QueueAllFromHere": "Set alt her i kø", "RecentlyWatched": "Nyligt sete", "RecommendationBecauseYouLike": "Fordi du kan lide {0}", "RecommendationBecauseYouWatched": "Fordi du har set {0}", @@ -1090,7 +1081,6 @@ "Art": "Kunst", "Ascending": "Stigende", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Automatisk (baseret på sprogindstilling)", "Backdrop": "Baggrund", "Backdrops": "Baggrunde", "Banner": "Banner", @@ -1199,7 +1189,6 @@ "LabelCache": "Cache:", "LabelCertificatePassword": "Adgangskode til certifikat:", "LabelCertificatePasswordHelp": "Hvis dit certifikat kræver en adgangskode, skriv det venligst her.", - "LabelDashboardTheme": "Server dashboard tema:", "LabelDateTimeLocale": "Dato og tid område:", "LabelDefaultScreen": "Standard skærm:", "LabelDisplayLanguage": "Visningssprog:", @@ -1226,12 +1215,10 @@ "LabelScreensaver": "Pauseskærm:", "LabelSelectFolderGroups": "Gruppér automatisk indhold fra følgende mapper ind i visninger som Film, Musik og TV:", "LabelSelectFolderGroupsHelp": "Mapper der ikke er valgt vil blive vist for sig selv i deres egen visning.", - "LabelSkin": "Tema Skin:", "LabelSkipBackLength": "Gå tilbage længde:", "LabelSkipForwardLength": "Gå fremad længde:", "LabelSortBy": "Sortér efter:", "LabelSortOrder": "Sorteringsorden:", - "LabelSoundEffects": "Lydeffekter:", "LabelStatus": "Status:", "LabelSubtitles": "Undertekster", "LabelTVHomeScreen": "TV modus hjemmeskærm:", @@ -1253,7 +1240,6 @@ "LearnHowYouCanContribute": "Lær hvordan du kan bidrage.", "LeaveBlankToNotSetAPassword": "Du kan lade dette felt være tomt hvis du ikke ønsker adgangskode.", "Like": "Favorit", - "LinksValue": "Link: {0}", "List": "Liste", "Live": "Live", "LiveTV": "Se Live TV", @@ -1266,7 +1252,6 @@ "MediaInfoLayout": "Udlæg", "MediaInfoRefFrames": "Ref billeder", "MediaInfoSampleRate": "Sample rate", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeVideo": "Video", "MediaIsBeingConverted": "Mediet bliver konverteret til et format der er kompatibel med enheden der afspiller mediet.", @@ -1323,7 +1308,6 @@ "RepeatAll": "Gentag alle", "RepeatMode": "Gentagelses tilstand", "RepeatOne": "Gentag én", - "RunAtStartup": "Kør ved opstart", "ScanForNewAndUpdatedFiles": "Skan for nye og opdaterede filer", "Schedule": "Tidsplan", "Screenshot": "Skærmbillede", @@ -1348,7 +1332,6 @@ "TabLiveTV": "Live TV", "TabLogs": "Log", "TabMetadata": "Metadata", - "TabPlaylist": "Afspilningsliste", "TabServer": "Server", "TabStreaming": "Streamer", "Tags": "Mærker", @@ -1371,7 +1354,6 @@ "ValueSpecialEpisodeName": "Special - {0}", "ValueVideoCodec": "Video Codek: {0}", "Vertical": "Vertikal", - "VideoRange": "Video rækkevidde", "Watched": "Set", "Whitelist": "Hvidliste", "Yes": "Ja", @@ -1407,14 +1389,12 @@ "SubtitleOffset": "Undertekst Offset", "SelectAdminUsername": "Vælg et brugernavn til administrator kontoen.", "Season": "Sæson", - "ReleaseGroup": "Release Group", "Premiere": "Premiere", "PreferEmbeddedEpisodeInfosOverFileNames": "Foretrækker integreret episode information frem for filnavne", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dette bruger episode informationen fra de integrerede metadata, hvis den er tilgængelig.", "PlaybackData": "Afspilningsdata", "Person": "Person", "PasswordResetProviderHelp": "Vælg en leverandør af nulstil adgangskode, der skal bruges, når denne bruger anmoder om en nulstilling af adgangskode", - "OtherArtist": "Anden kunstner", "OptionThumbCard": "Thumb card", "OptionThumb": "Thumb", "OptionRandom": "Tilfældig", @@ -1437,8 +1417,6 @@ "MediaInfoStreamTypeSubtitle": "Undertekst", "MediaInfoStreamTypeEmbeddedImage": "Indlejret billede", "MediaInfoStreamTypeAudio": "Lyd", - "LaunchWebAppOnStartupHelp": "Åben web klienten i den standard web browser når serveren starter første gang. Dette vil ikke ske når restart server funktionen benyttes.", - "LaunchWebAppOnStartup": "Åben webinterfacet når serveren startes", "LabelWeb": "Web:", "LabelVideoResolution": "Videoopløsning:", "LabelVideoBitrate": "Video bitrate:", @@ -1478,7 +1456,6 @@ "FetchingData": "Henter yderligere data", "Episode": "Afsnit", "DeinterlaceMethodHelp": "Vælg hvilken konverteringsmulighed der skal bruges til transkodning af indhold.", - "CopyStreamURLError": "Der skete en fejl med at kopiere URL'en.", "CopyStreamURLSuccess": "URL blev kopieret.", "CopyStreamURL": "Kopiér stream URL", "ClientSettings": "Klient Indstillinger", @@ -1490,7 +1467,6 @@ "EveryXHours": "Hver {0} time", "OnApplicationStartup": "Ved programstart", "UnsupportedPlayback": "Jellyfin kan ikke dekryptere indhold, der er beskyttet af DRM, men alt indhold vil blive forsøgt afspillet uanset, inklusive beskyttede titler. Nogle filer kan eventuelt vises med sort skærm på grund af kryptering eller andre funktioner, der ikke understøttes, såsom interaktive titler.", - "MessageUnauthorizedUser": "Du har ikke tilladelse til at tilgå serveren på dette tidspunkt. Kontakt din serveradministrator for mere information.", "Filter": "Filtrer", "New": "Nye", "ButtonTogglePlaylist": "Spilleliste", diff --git a/src/strings/de.json b/src/strings/de.json index db1ef10305..1c87a95a95 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "Der Zugriff ist derzeit eingeschränkt. Bitte versuche es später erneut.", "Actor": "Darsteller(in)", "Add": "Hinzufügen", - "AddItemToCollectionHelp": "Fügen Sie Elemente zu Sammlungen hinzu, indem Sie sie suchen und deren Rechtsklick- oder Antippmenü benutzen.", "AddToCollection": "Zur Sammlung hinzufügen", "AddToPlayQueue": "Zur Warteschlange hinzufügen", "AddToPlaylist": "Zur Wiedergabeliste hinzufügen", @@ -36,7 +35,6 @@ "Ascending": "Aufsteigend", "AspectRatio": "Seitenverhältnis", "AttributeNew": "Neu", - "AutoBasedOnLanguageSetting": "Automatisch (basierend auf Spracheinstellung)", "Backdrop": "Hintergrund", "Backdrops": "Hintergründe", "BirthDateValue": "Geboren: {0}", @@ -73,7 +71,6 @@ "ButtonGotIt": "Verstanden", "ButtonGuide": "TV Guide", "ButtonHelp": "Hilfe", - "ButtonLearnMore": "Erfahre mehr", "ButtonLibraryAccess": "Bibliothekszugang", "ButtonManualLogin": "Manuelle Anmeldung", "ButtonMore": "Mehr", @@ -115,7 +112,6 @@ "ButtonSubtitles": "Untertitel", "ButtonUninstall": "Deinstallieren", "ButtonUp": "Hoch", - "ButtonViewWebsite": "Besuche die Website", "CancelRecording": "Aufnahme abbrechen", "CancelSeries": "Serien abbrechen", "Categories": "Kategorien", @@ -267,7 +263,6 @@ "HeaderAppearsOn": "Erscheint auf", "HeaderAudioBooks": "Hörbücher", "HeaderAudioSettings": "Audioeinstellungen", - "HeaderAutomaticUpdates": "Automatische Updates", "HeaderBlockItemsWithNoRating": "Blockiere Inhalte mit keiner oder nicht erkannter Altersfreigabe:", "HeaderBooks": "Bücher", "HeaderBranding": "Branding / CSS", @@ -478,8 +473,6 @@ "LabelAlbumArtists": "Alben Interpreten:", "LabelAll": "Alle", "LabelAllowHWTranscoding": "Erlaube Hardware Transkodierung", - "LabelAllowServerAutoRestart": "Erlaube dem Server sich automatisch neuzustarten, um Updates durchzuführen", - "LabelAllowServerAutoRestartHelp": "Der Server startet nur wenn keine Nutzer aktiv sind neu.", "LabelAllowedRemoteAddresses": "Remote-IP Adressen Filter:", "LabelAllowedRemoteAddressesMode": "Remote IP Adressen Filtermodus:", "LabelAppName": "App Name", @@ -515,7 +508,6 @@ "LabelCustomDeviceDisplayName": "Angezeigter Name:", "LabelCustomDeviceDisplayNameHelp": "Lege einen individuellen Anzeigenamen fest oder lasse das Feld leer, um den vom gerät übermittelten Namen zu nutzen.", "LabelCustomRating": "Eigene Bewertung:", - "LabelDashboardTheme": "Server Dashboard Theme:", "LabelDateAdded": "Hinzugefügt am:", "LabelDateAddedBehavior": "Verhalten für Hinzufügedatum bei neuen Inhalten:", "LabelDateAddedBehaviorHelp": "Wenn ein Metadatenwert vorhanden ist, wird dieser immer gegenüber den anderen Optionen bevorzugt werden.", @@ -732,7 +724,6 @@ "LabelSortBy": "Sortiert nach:", "LabelSortOrder": "Sortierreihenfolge:", "LabelSortTitle": "Sortierungs Titel:", - "LabelSoundEffects": "Soundeffekte:", "LabelSource": "Quelle:", "LabelSpecialSeasonsDisplayName": "Anzeigename für Serien-Specials:", "LabelSportsCategories": "Sportkategorie:", @@ -779,7 +770,6 @@ "LabelXDlnaDoc": "X-DLNA Dokument:", "LabelXDlnaDocHelp": "Legt den Inhalt des X_DLNADOC Elements in der urn:schemas-dlna-org:device-1-0 namespace fest.", "LabelYear": "Jahr:", - "LabelYourFirstName": "Vorname:", "LabelYoureDone": "Du bist fertig!", "LabelZipCode": "PLZ:", "LabelffmpegPath": "FFmpeg Verzeichnis:", @@ -839,7 +829,6 @@ "MessageFileReadError": "Es gab einen Fehler beim Lesen der Datei. Bitte versuche es erneut.", "MessageForgotPasswordFileCreated": "Die folgende Datei wurde auf deinem Server erstellt und enthält eine Anleitung, wie fortgefahren werden muss:", "MessageForgotPasswordInNetworkRequired": "Bitte versuche es erneut innerhalb deines Heimnetzwerks, um die Passwort Zurücksetzung zu starten.", - "MessageInstallPluginFromApp": "Dieses Plugin muss von der App aus installiert werden, in der du es benutzen willst.", "MessageInvalidForgotPasswordPin": "Ein ungültiger oder abgelaufener PIN-Code wurde eingegeben. Bitte versuche es noch einmal.", "MessageInvalidUser": "Falscher Benutzername oder Passwort. Bitte versuche es noch einmal.", "MessageItemSaved": "Element gespeichert.", @@ -1060,7 +1049,6 @@ "ProductionLocations": "Drehorte", "Programs": "Programme", "Quality": "Qualität", - "QueueAllFromHere": "Setze alles von hier auf Warteschlange", "Raised": "Angehoben", "Rate": "Bewerte", "RecentlyWatched": "Kürzlich gesehen", @@ -1091,7 +1079,6 @@ "ReplaceExistingImages": "Ersetze vorhandene Bilder", "ResumeAt": "Fortsetzen bei {0}", "Rewind": "Zurückspulen", - "RunAtStartup": "Nach Hochfahren automatisch starten", "Runtime": "Laufzeit", "Saturday": "Samstag", "Save": "Speichern", @@ -1178,7 +1165,6 @@ "TabParentalControl": "Kindersicherung", "TabPassword": "Passwort", "TabPlayback": "Wiedergabe", - "TabPlaylist": "Wiedergabeliste", "TabPlaylists": "Wiedergabelisten", "TabProfile": "Profil", "TabProfiles": "Profile", @@ -1292,7 +1278,6 @@ "LabelName": "Name:", "LabelProfileCodecs": "Codecs:", "LabelProfileContainer": "Container:", - "LabelSkin": "Textur:", "Art": "Coverkunst", "Name": "Name", "Songs": "Songs", @@ -1304,7 +1289,6 @@ "LabelVersion": "Version:", "LabelVideo": "Video", "LeaveBlankToNotSetAPassword": "Dieses Feld frei lassen, um kein Passwort zu setzen.", - "LinksValue": "Links: {0}", "MessageImageFileTypeAllowed": "Nur JPEG- und PNG-Dateien werden unterstützt.", "MessageImageTypeNotSelected": "Bitte wähle einen Bildtyp aus dem Drop-Down Menü aus.", "Normal": "Normal", @@ -1386,7 +1370,6 @@ "DashboardVersionNumber": "Version: {0}", "DashboardServerName": "Server: {0}", "LabelWeb": "Web:", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Daten", "MediaInfoStreamTypeEmbeddedImage": "Eingebettetes Bild", @@ -1421,8 +1404,6 @@ "DashboardOperatingSystem": "Betriebssystem: {0}", "DashboardArchitecture": "Architektur: {0}", "LabelVideoCodec": "Videocodec:", - "LaunchWebAppOnStartup": "Das Webinterface öffnen, wenn der Server startet", - "LaunchWebAppOnStartupHelp": "Öffne den Webclient in Standard-Webbrowser, wenn der Server zum ersten Mal gestartet wird. Dies tritt bei Verwendung der Neustart-Serverfunktion nicht auf.", "MusicArtist": "Interpret", "MusicAlbum": "Musikalbum", "MoreMediaInfo": "Medieninformation", @@ -1441,11 +1422,9 @@ "MusicLibraryHelp": "Überprüfe den {0}Musikbenennungsguide{1}.", "OptionRandom": "Zufällig", "TabNetworking": "Netzwerk", - "VideoRange": "Videobereich", "ButtonSplit": "Trennen", "SelectAdminUsername": "Bitte wählen Sie einen Benutzernamen für den Administrator-Account.", "HeaderNavigation": "Navigation", - "CopyStreamURLError": "Beim Kopieren der URL ist ein Fehler aufgetreten.", "MessageConfirmAppExit": "Wirklich verlassen?", "LabelVideoResolution": "Videoauflösung:", "LabelStreamType": "Streamtyp:", @@ -1473,9 +1452,7 @@ "PathNotFound": "Der Pfad konnte nicht gefunden werden. Bitte versichere dich dass der Pfad korrekt ist und versuche es erneut.", "Track": "Track", "Season": "Staffel", - "ReleaseGroup": "Veröffentlichungs-Gruppe", "Person": "Person", - "OtherArtist": "Andere Künstler", "Movie": "Film", "Episode": "Episode", "Artist": "Künstler", @@ -1491,12 +1468,10 @@ "UnsupportedPlayback": "Jellyfin kann keine DRM-geschützten Inhalte entschlüsseln, aber es wird versucht, alle Inhalte unabhängig davon zu entschlüsseln, einschließlich geschützter Titel. Einige Dateien können aufgrund der Verschlüsselung oder anderer nicht unterstützter Funktionen, wie z.B. interaktive Titel, komplett schwarz erscheinen.", "Filter": "Filter", "New": "Neu", - "MessageUnauthorizedUser": "Sie sind im Moment nicht berechtigt, auf den Server zuzugreifen. Bitte kontaktieren Sie Ihren Server-Administrator für weitere Informationen.", "HeaderFavoritePlaylists": "Lieblings-Wiedergabeliste", "ButtonTogglePlaylist": "Wiedergabeliste", "ButtonToggleContextMenu": "Mehr", "ApiKeysCaption": "Liste der aktuell aktivierten API-Schlüssel", - "LabelNightly": "Nightly", "LabelStable": "Stable", "LabelChromecastVersion": "Chromecast Version", "HeaderDVR": "DVR", diff --git a/src/strings/el.json b/src/strings/el.json index fe22f110c3..c390f7bde4 100644 --- a/src/strings/el.json +++ b/src/strings/el.json @@ -32,7 +32,6 @@ "AttributeNew": "Νέο", "Audio": "Ήχος", "Auto": "Αυτόματο", - "AutoBasedOnLanguageSetting": "Αυτόματα (με βάση τη ρύθμιση γλώσσας)", "Backdrop": "Φόντο", "Backdrops": "Σκηνικά", "Banner": "Πανό", @@ -73,7 +72,6 @@ "ButtonHelp": "Βοήθεια", "ButtonHome": "Αρχική", "ButtonInfo": "Πληροφορία", - "ButtonLearnMore": "Μάθετε περισσότερα", "ButtonLibraryAccess": "Πρόσβαση στη βιβλιοθήκη", "ButtonManualLogin": "Χειροκίνητη Είσοδος", "ButtonMore": "Περισσότερα", @@ -116,7 +114,6 @@ "ButtonTrailer": "Τρέϊλερ", "ButtonUninstall": "Απεγκατάσταση", "ButtonUp": "Επάνω", - "ButtonViewWebsite": "Εμφάνιση ιστοσελίδας", "ButtonWebsite": "Ιστοσελίδα", "CancelRecording": "Ακύρωση Εγγραφής", "CancelSeries": "Ακύρωση Σειράς", @@ -260,7 +257,6 @@ "HeaderAppearsOn": "Εμφανίζεται σε", "HeaderAudioBooks": "Μουσικά Βιβλία", "HeaderAudioSettings": "Ρυθμίσεις Ήχου", - "HeaderAutomaticUpdates": "Αυτόματες Ανανεώσεις", "HeaderBlockItemsWithNoRating": "Αποκλεισμός στοιχείων χωρίς ή μη αναγνωρισμένων πληροφοριών αξιολόγησης:", "HeaderBooks": "Βιβλία", "HeaderCancelRecording": "Ακύρωση Εγγραφής", @@ -443,8 +439,6 @@ "LabelAlbumArtPN": "PN άλμπουμ art:", "LabelAlbumArtists": "Καλλιτέχνες του 'Αλμπουμ:", "LabelAll": "Όλα", - "LabelAllowServerAutoRestart": "Αυτόματη επανεκκίνηση του σέρβερ για να εγκαταστήσει τις αναβαθμίσεις", - "LabelAllowServerAutoRestartHelp": "Ο server θα κάνει επανεκκίνηση μόνο κατά τη διάρκεια αδρανών περιόδων, όταν δεν υπάρχουν ενεργοί χρήστες.", "LabelAppName": "Όνομα App", "LabelAppNameExample": "Παράδειγμα: Sickbeard, NzbDrone", "LabelArtists": "Καλλιτέχνες:", @@ -474,7 +468,6 @@ "LabelCustomCssHelp": "Εφαρμόστε το δικό σας προσαρμοσμένο css στην διεπαφή ιστού.", "LabelCustomDeviceDisplayName": "Εμφάνιση ονόματος:", "LabelCustomRating": "Προσαρμοσμένη αξιολόγηση:", - "LabelDashboardTheme": "Θέμα εμφάνισης πίνακα ελέγχου server:", "LabelDateAdded": "Ημερνία προσθήκης:", "LabelDateTimeLocale": "Ημερομηνία τοπική ώρα:", "LabelDay": "Ημέρα:", @@ -655,7 +648,6 @@ "LabelSortBy": "Ταξινόμηση κατά:", "LabelSortOrder": "Σειρά ταξινόμησης:", "LabelSortTitle": "Τίτλος ταξινόμησης:", - "LabelSoundEffects": "Ηχητικά Εφέ:", "LabelSource": "Πηγή:", "LabelSpecialSeasonsDisplayName": "Ειδικό εμφανιζόμενο όνομα σεζόν:", "LabelStartWhenPossible": "Έναρξη όταν είναι δυνατό:", @@ -696,7 +688,6 @@ "LabelXDlnaCapHelp": "Καθορίζει το περιεχόμενο του στοιχείου X_DLNACAP στο urn:schemas-dlna-org:device-1-0 namespace.", "LabelXDlnaDocHelp": "Καθορίζει το περιεχόμενο του στοιχείου X_DLNACAP στο urn:schemas-dlna-org:device-1-0 namespace.", "LabelYear": "Έτος:", - "LabelYourFirstName": "Το όνομά σας:", "LabelYoureDone": "Είστε Έτοιμοι!", "LabelZipCode": "Ταχυδ/κός κώδικας:", "Large": "Μεγάλο", @@ -704,7 +695,6 @@ "LearnHowYouCanContribute": "Μάθετε πώς μπορείτε να συμβάλλετε.", "LibraryAccessHelp": "Επιλέξτε τους φακέλους μέσων για να το μοιραστείτε με αυτόν το χρήστη. Οι διαχειριστές θα έχουν τη δυνατότητα να επεξεργάζεστε όλα φακέλους χρησιμοποιώντας τα μεταδεδομένα manager.", "Like": "Μου αρέσει", - "LinksValue": "Σύνδεσμοι: {0}", "List": "Λίστα", "Live": "Ζωντανά", "LiveBroadcasts": "Ζωντανές εκπομπές", @@ -749,7 +739,6 @@ "MessageFileReadError": "Παρουσιάστηκε σφάλμα κατά την ανάγνωση του αρχείου. Παρακαλώ προσπάθησε ξανά.", "MessageForgotPasswordFileCreated": "Το ακόλουθο αρχείο έχει δημιουργηθεί στο διακομιστή σας και περιέχει οδηγίες για το πώς να συνεχίσετε:", "MessageForgotPasswordInNetworkRequired": "Παρακαλώ δοκιμάστε ξανά μέσα στο οικιακό σας δίκτυο για να ξεκινήσετε τη διαδικασία επαναφοράς κωδικού πρόσβασης.", - "MessageInstallPluginFromApp": "Αυτό το πρόσθετο πρέπει να εγκατασταθεί την εφαρμογή που σκοπεύετε να χρησιμοποιήσετε.", "MessageInvalidForgotPasswordPin": "Καταχωρήθηκε ένα άκυρο ή ληγμένο PIN. Παρακαλώ προσπαθήστε ξανά.", "MessageInvalidUser": "Μη έγκυρο όνομα ή κωδικός. Παρακαλώ προσπαθήστε ξανά.", "MessageItemSaved": "Το στοιχείο αποθηκεύτηκε.", @@ -955,7 +944,6 @@ "ProductionLocations": "Τοποθεσίες παραγωγής", "Programs": "Προγράμματα", "Quality": "Ποιότητα", - "QueueAllFromHere": "Τοποθετήστε στην ουρά αναπαραγωγής όλα από εδώ", "Raised": "Αυξήθηκε", "RecentlyWatched": "Πρόσφατα αναπαραχθέντα", "RecommendationBecauseYouLike": "Επειδή σας αρέσει {0}", @@ -983,7 +971,6 @@ "ReplaceExistingImages": "Αντικατάσταση υπαρχουσών εικόνων", "ResumeAt": "Συνέχιση από {0}", "Rewind": "Αναπαραγωγή προς τα πίσω", - "RunAtStartup": "Εκτέλεση κατά την εκκίνηση", "Runtime": "Χρόνος εκτέλεσης", "Saturday": "Σάββατο", "Save": "Αποθήκευση", @@ -1068,7 +1055,6 @@ "TabParentalControl": "Γονικός έλεγχος", "TabPassword": "Κωδικός", "TabPlayback": "Αναπαραγωγή", - "TabPlaylist": "Λίστα", "TabPlaylists": "Λίστες αναπαραγωγής", "TabPlugins": "Πρόσθετα", "TabProfile": "Προφίλ", @@ -1201,7 +1187,6 @@ "AllowMediaConversionHelp": "Παραχώρηση ή στέρηση πρόσβασης στην λειτουργία μετατροπής μέσων.", "AllowHWTranscodingHelp": "Επιτρέπει τον δέκτη να επανακωδικοποιεί τις ροές σε πραγματικό χρόνο. Αυτό μπορεί να μειώσει τον φόρτο κωδικοποίησης τον σέρβερ.", "Alerts": "Προειδοποίηση", - "AddItemToCollectionHelp": "Προσθέστε στις συλλογές κάνοντας αναζήτηση και δεξί κλικ ή μέσω των μενού.", "MediaInfoStreamTypeVideo": "Βίντεο", "MediaInfoStreamTypeSubtitle": "Υπότιτλος", "MediaInfoStreamTypeData": "Δεδομένα", diff --git a/src/strings/en-gb.json b/src/strings/en-gb.json index e1c2e52a30..b3ec646101 100644 --- a/src/strings/en-gb.json +++ b/src/strings/en-gb.json @@ -53,7 +53,6 @@ "AccessRestrictedTryAgainLater": "Access is currently restricted. Please try again later.", "Actor": "Actor", "Add": "Add", - "AddItemToCollectionHelp": "Add items to collections by searching for them and using their right-click or tap menus to add them to a collection.", "AddToCollection": "Add to collection", "AddToPlayQueue": "Add to play queue", "AddToPlaylist": "Add to playlist", @@ -88,7 +87,6 @@ "Audio": "Audio", "AuthProviderHelp": "Select an Authentication Provider to be used to authenticate this user's password.", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (based on language setting)", "Backdrop": "Backdrop", "Backdrops": "Backdrops", "Banner": "Banner", @@ -130,7 +128,6 @@ "ButtonHelp": "Help", "ButtonHome": "Home", "ButtonInfo": "Info", - "ButtonLearnMore": "Learn more", "ButtonLibraryAccess": "Library access", "ButtonManualLogin": "Manual Login", "ButtonMore": "More", @@ -176,7 +173,6 @@ "ButtonTrailer": "Trailer", "ButtonUninstall": "Uninstall", "ButtonUp": "Up", - "ButtonViewWebsite": "View website", "ButtonWebsite": "Website", "CancelRecording": "Cancel recording", "CancelSeries": "Cancel series", @@ -327,7 +323,6 @@ "HeaderAppearsOn": "Appears On", "HeaderAudioBooks": "Audio Books", "HeaderAudioSettings": "Audio Settings", - "HeaderAutomaticUpdates": "Automatic Updates", "HeaderBooks": "Books", "HeaderBranding": "Branding", "HeaderCancelRecording": "Cancel Recording", @@ -483,7 +478,6 @@ "TabResumeSettings": "Resume", "TabResponses": "Responses", "TabRecordings": "Recordings", - "TabPlaylist": "Playlist", "TabPlayback": "Playback", "TabOther": "Other", "TabNotifications": "Notifications", @@ -548,7 +542,6 @@ "SaveSubtitlesIntoMediaFolders": "Save subtitles into media folders", "Saturday": "Saturday", "Runtime": "Runtime", - "RunAtStartup": "Run at startup", "Rewind": "Rewind", "ResumeAt": "Resume from {0}", "ReplaceExistingImages": "Replace existing images", @@ -576,7 +569,6 @@ "RecommendationBecauseYouWatched": "Because you watched {0}", "Rate": "Rate", "Raised": "Raised", - "QueueAllFromHere": "Queue all from here", "Quality": "Quality", "Producer": "Producer", "Primary": "Primary", @@ -713,7 +705,6 @@ "MessageNoAvailablePlugins": "No available plugins.", "MessageInvalidUser": "Invalid username or password. Please try again.", "MessageInvalidForgotPasswordPin": "An invalid or expired pin code was entered. Please try again.", - "MessageInstallPluginFromApp": "This plugin must be installed from within the app you intend to use it in.", "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", "MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.", @@ -756,7 +747,6 @@ "LibraryAccessHelp": "Select the libraries to share with this user. Administrators will be able to edit all folders using the metadata manager.", "LeaveBlankToNotSetAPassword": "You can leave this field blank to set no password.", "LearnHowYouCanContribute": "Learn how you can contribute.", - "LaunchWebAppOnStartupHelp": "Open the web client in your default web browser when the server initially starts. This will not occur when using the restart server function.", "LanNetworksHelp": "Comma separated list of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. If left blank, only the server's subnet is considered to be on the local network.", "LabelffmpegPathHelp": "The path to the ffmpeg application file, or folder containing ffmpeg.", "LabelffmpegPath": "FFmpeg path:", @@ -805,14 +795,12 @@ "LabelStartWhenPossible": "Start when possible:", "LabelSpecialSeasonsDisplayName": "Special season display name:", "LabelSource": "Source:", - "LabelSoundEffects": "Sound effects:", "LabelSortBy": "Sort by:", "LabelSonyAggregationFlagsHelp": "Determines the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.", "LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery and decrease the likelihood of video transcoding.", "LabelSkipIfAudioTrackPresentHelp": "Uncheck this to ensure all videos have subtitles, regardless of audio language.", "LabelSkipIfAudioTrackPresent": "Skip if the default audio track matches the download language", "LabelSkipBackLength": "Skip back length:", - "LabelSkin": "Skin:", "LabelSize": "Size:", "LabelSimultaneousConnectionLimit": "Simultaneous stream limit:", "LabelServerHost": "Host:", @@ -857,8 +845,6 @@ "MessageEnablingOptionLongerScans": "Enabling this option may result in significantly longer library scans.", "MessageConfirmRecordingCancellation": "Cancel recording?", "MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?", - "LaunchWebAppOnStartup": "Launch the web interface when starting the server", - "LabelYourFirstName": "Your first name:", "OnlyForcedSubtitles": "Only Forced", "Off": "Off", "NumLocationsValue": "{0} folders", @@ -1161,8 +1147,6 @@ "LabelAppName": "App name", "LabelAllowedRemoteAddressesMode": "Remote IP address filter mode:", "LabelAllowedRemoteAddresses": "Remote IP address filter:", - "LabelAllowServerAutoRestartHelp": "The server will only restart during idle periods when no users are active.", - "LabelAllowServerAutoRestart": "Allow the server to restart automatically to apply updates", "LabelAllowHWTranscoding": "Allow hardware transcoding", "LabelAll": "All", "LabelAlbumArtists": "Album artists:", @@ -1253,7 +1237,6 @@ "Watched": "Watched", "ViewPlaybackInfo": "View playback info", "ViewAlbum": "View album", - "VideoRange": "Video range", "SubtitleAppearanceSettingsAlsoPassedToCastDevices": "These settings also apply to any Chromecast playback started by this device.", "Studios": "Studios", "StopRecording": "Stop recording", @@ -1266,7 +1249,6 @@ "OptionContinuing": "Continuing", "OptionCommunityRating": "Community Rating", "MessageUnsetContentHelp": "Content will be displayed as plain folders. For best results use the metadata manager to set the content types of sub-folders.", - "LinksValue": "Links: {0}", "LabelSelectVersionToInstall": "Select version to install:", "LabelMetadataPathHelp": "Specify a custom location for downloaded artwork and metadata.", "OptionLikes": "Likes", @@ -1320,7 +1302,6 @@ "Up": "Up", "SearchForCollectionInternetMetadata": "Search the internet for artwork and metadata", "MediaInfoStreamTypeSubtitle": "Subtitle", - "MediaInfoSoftware": "Software", "ValueOneSeries": "1 series", "MediaInfoBitrate": "Bitrate", "LabelVideo": "Video", @@ -1330,7 +1311,6 @@ "LabelInternetQuality": "Internet quality:", "LabelFileOrUrl": "File or URL:", "LabelDateAdded": "Date added:", - "LabelDashboardTheme": "Server dashboard theme:", "LabelCustomRating": "Custom rating:", "LabelCollection": "Collection:", "LabelChannels": "Channels:", @@ -1455,7 +1435,6 @@ "LabelPlayerDimensions": "Player dimensions:", "LabelDroppedFrames": "Dropped frames:", "LabelCorruptedFrames": "Corrupted frames:", - "CopyStreamURLError": "There was an error copying the URL.", "NoCreatedLibraries": "Seems like you haven't created any libraries yet. {0}Would you like to create one now?{1}", "AskAdminToCreateLibrary": "Ask an administrator to create a library.", "PlaybackErrorNoCompatibleStream": "This client isn't compatible with the media and the server isn't sending a compatible media format.", @@ -1477,11 +1456,9 @@ "Yadif": "YADIF", "Track": "Track", "Season": "Season", - "ReleaseGroup": "Release Group", "PreferEmbeddedEpisodeInfosOverFileNames": "Prefer embedded episode information over filenames", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode information from the embedded metadata if available.", "Person": "Person", - "OtherArtist": "Other Artist", "Movie": "Movie", "LabelLibraryPageSizeHelp": "Sets the amount of items to show on a library page. Set to 0 in order to disable paging.", "LabelLibraryPageSize": "Library page size:", @@ -1494,7 +1471,6 @@ "AlbumArtist": "Album Artist", "Album": "Album", "UnsupportedPlayback": "Jellyfin cannot decrypt content protected by DRM but all content will be attempted regardless, including protected titles. Some files may appear completely black due to encryption or other unsupported features, such as interactive titles.", - "MessageUnauthorizedUser": "You are not authorized to access the server at this time. Please contact your server administrator for more information.", "ButtonTogglePlaylist": "Playlist", "ButtonToggleContextMenu": "More", "HeaderDVR": "DVR", @@ -1546,7 +1522,6 @@ "LabelSyncPlayTimeOffset": "Time offset with the server:", "LabelRequireHttpsHelp": "If checked, the server will automatically redirect all requests over HTTP to HTTPS. This has no effect if the server is not listening on HTTPS.", "LabelRequireHttps": "Require HTTPS", - "LabelNightly": "Nightly", "LabelStable": "Stable", "LabelChromecastVersion": "Chromecast Version", "LabelEnableHttpsHelp": "Enables the server to listen on the configured HTTPS port. A valid certificate must also be configured in order for this to take effect.", diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 82d2d796f1..54c8cf8880 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "Access is currently restricted. Please try again later.", "Actor": "Actor", "Add": "Add", - "AddItemToCollectionHelp": "Add items to collections by searching for them and using their right-click or tap menus to add them to a collection.", "AddToCollection": "Add to collection", "AddToPlayQueue": "Add to play queue", "AddToPlaylist": "Add to playlist", @@ -47,7 +46,6 @@ "Audio": "Audio", "AuthProviderHelp": "Select an authentication provider to be used to authenticate this user's password.", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (based on language setting)", "Backdrop": "Backdrop", "Backdrops": "Backdrops", "Banner": "Banner", @@ -93,7 +91,6 @@ "ButtonHelp": "Help", "ButtonHome": "Home", "ButtonInfo": "Info", - "ButtonLearnMore": "Learn more", "ButtonLibraryAccess": "Library access", "ButtonManualLogin": "Manual Login", "ButtonMore": "More", @@ -142,7 +139,6 @@ "ButtonTrailer": "Trailer", "ButtonUninstall": "Uninstall", "ButtonUp": "Up", - "ButtonViewWebsite": "View website", "ButtonWebsite": "Website", "CancelRecording": "Cancel recording", "CancelSeries": "Cancel series", @@ -171,7 +167,6 @@ "Continuing": "Continuing", "CopyStreamURL": "Copy Stream URL", "CopyStreamURLSuccess": "URL copied successfully.", - "CopyStreamURLError": "There was an error copying the URL.", "CriticRating": "Critic rating", "CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.", "DateAdded": "Date added", @@ -319,7 +314,6 @@ "HeaderAppearsOn": "Appears On", "HeaderAudioBooks": "Audio Books", "HeaderAudioSettings": "Audio Settings", - "HeaderAutomaticUpdates": "Automatic Updates", "HeaderBlockItemsWithNoRating": "Block items with no or unrecognized rating information:", "HeaderBooks": "Books", "HeaderBranding": "Branding", @@ -558,8 +552,6 @@ "LabelAlbumArtists": "Album artists:", "LabelAll": "All", "LabelAllowHWTranscoding": "Allow hardware transcoding", - "LabelAllowServerAutoRestart": "Allow the server to restart automatically to apply updates", - "LabelAllowServerAutoRestartHelp": "The server will only restart during idle periods when no users are active.", "LabelAllowedRemoteAddresses": "Remote IP address filter:", "LabelAllowedRemoteAddressesMode": "Remote IP address filter mode:", "LabelAppName": "App name", @@ -605,7 +597,6 @@ "LabelCustomDeviceDisplayName": "Display name:", "LabelCustomDeviceDisplayNameHelp": "Supply a custom display name or leave empty to use the name reported by the device.", "LabelCustomRating": "Custom rating:", - "LabelDashboardTheme": "Server dashboard theme:", "LabelDateAdded": "Date added:", "LabelDateAddedBehavior": "Date added behavior for new content:", "LabelDateAddedBehaviorHelp": "If a metadata value is present, it will always be used before either of these options.", @@ -759,7 +750,6 @@ "LabelName": "Name:", "LabelChromecastVersion": "Chromecast Version", "LabelStable": "Stable", - "LabelNightly": "Nightly", "LabelUnstable": "Unstable", "LabelNewName": "New name:", "LabelNewPassword": "New password:", @@ -842,7 +832,6 @@ "LabelServerName": "Server name:", "LabelSimultaneousConnectionLimit": "Simultaneous stream limit:", "LabelSize": "Size:", - "LabelSkin": "Skin:", "LabelSkipBackLength": "Skip back length:", "LabelSkipForwardLength": "Skip forward length:", "LabelSkipIfAudioTrackPresent": "Skip if the default audio track matches the download language", @@ -854,7 +843,6 @@ "LabelSortBy": "Sort by:", "LabelSortOrder": "Sort order:", "LabelSortTitle": "Sort title:", - "LabelSoundEffects": "Sound effects:", "LabelSource": "Source:", "LabelSpecialSeasonsDisplayName": "Special season display name:", "LabelSportsCategories": "Sports categories:", @@ -934,7 +922,6 @@ "LabelXDlnaDoc": "X-DLNA doc:", "LabelXDlnaDocHelp": "Determines the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.", "LabelYear": "Year:", - "LabelYourFirstName": "Your first name:", "LabelYoureDone": "You're Done!", "LabelZipCode": "Zip Code:", "LabelffmpegPath": "FFmpeg path:", @@ -942,13 +929,10 @@ "LanNetworksHelp": "Comma separated list of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. If left blank, only the server's subnet is considered to be on the local network.", "Large": "Large", "LatestFromLibrary": "Latest {0}", - "LaunchWebAppOnStartup": "Launch the web interface when starting the server", - "LaunchWebAppOnStartupHelp": "Open the web client in your default web browser when the server initially starts. This will not occur when using the restart server function.", "LearnHowYouCanContribute": "Learn how you can contribute.", "LeaveBlankToNotSetAPassword": "You can leave this field blank to set no password.", "LibraryAccessHelp": "Select the libraries to share with this user. Administrators will be able to edit all folders using the metadata manager.", "Like": "Like", - "LinksValue": "Links: {0}", "List": "List", "Live": "Live", "LiveBroadcasts": "Live broadcasts", @@ -984,7 +968,6 @@ "MediaInfoSampleRate": "Sample rate", "MediaInfoSize": "Size", "MediaInfoTimestamp": "Timestamp", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeEmbeddedImage": "Embedded Image", @@ -1016,10 +999,8 @@ "MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.", "MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.", "MessageImageTypeNotSelected": "Please select an image type from the drop-down menu.", - "MessageInstallPluginFromApp": "This plugin must be installed from within the app you intend to use it in.", "MessageInvalidForgotPasswordPin": "An invalid or expired pin code was entered. Please try again.", "MessageInvalidUser": "Invalid username or password. Please try again.", - "MessageUnauthorizedUser": "You are not authorized to access the server at this time. Please contact your server administrator for more information.", "MessageItemSaved": "Item saved.", "MessageItemsAdded": "Items added.", "MessageLeaveEmptyToInherit": "Leave empty to inherit settings from a parent item or the global default value.", @@ -1266,7 +1247,6 @@ "OptionWeekends": "Weekends", "OptionWeekly": "Weekly", "OriginalAirDateValue": "Original air date: {0}", - "OtherArtist": "Other Artist", "Overview": "Overview", "PackageInstallCancelled": "{0} (version {1}) installation cancelled.", "PackageInstallCompleted": "{0} (version {1}) installation completed.", @@ -1315,7 +1295,6 @@ "ProductionLocations": "Production locations", "Programs": "Programs", "Quality": "Quality", - "QueueAllFromHere": "Queue all from here", "Raised": "Raised", "Rate": "Rate", "RecentlyWatched": "Recently watched", @@ -1334,7 +1313,6 @@ "RefreshMetadata": "Refresh metadata", "RefreshQueued": "Refresh queued.", "ReleaseDate": "Release date", - "ReleaseGroup": "Release Group", "RememberMe": "Remember Me", "RemoveFromCollection": "Remove from collection", "RemoveFromPlaylist": "Remove from playlist", @@ -1347,7 +1325,6 @@ "ReplaceExistingImages": "Replace existing images", "ResumeAt": "Resume from {0}", "Rewind": "Rewind", - "RunAtStartup": "Run at startup", "Runtime": "Runtime", "Saturday": "Saturday", "Save": "Save", @@ -1455,7 +1432,6 @@ "TabParentalControl": "Parental Control", "TabPassword": "Password", "TabPlayback": "Playback", - "TabPlaylist": "Playlist", "TabPlaylists": "Playlists", "TabPlugins": "Plugins", "TabProfile": "Profile", @@ -1527,7 +1503,6 @@ "ValueTimeLimitSingleHour": "Time limit: 1 hour", "ValueVideoCodec": "Video Codec: {0}", "Vertical": "Vertical", - "VideoRange": "Video range", "ViewAlbum": "View album", "ViewAlbumArtist": "View album artist", "ViewPlaybackInfo": "View playback info", diff --git a/src/strings/eo.json b/src/strings/eo.json index e2eebac174..2a4adda538 100644 --- a/src/strings/eo.json +++ b/src/strings/eo.json @@ -18,7 +18,6 @@ "AddedOnValue": "aldonis {0}", "AddToPlaylist": "aldoni al playlist", "AddToPlayQueue": "Aldonu ludi voston", - "AddItemToCollectionHelp": "Aldonu erojn al kolektoj serĉante ilin kaj uzante ĝian alklakon aŭ frapetu menuojn por aldoni ilin al kolekto.", "Add": "Aldoni", "AccessRestrictedTryAgainLater": "Aliro nuntempe estas restriktita. Bonvolu reprovi poste." } diff --git a/src/strings/es-ar.json b/src/strings/es-ar.json index 52c98fbc52..c8a1dbb87c 100644 --- a/src/strings/es-ar.json +++ b/src/strings/es-ar.json @@ -12,7 +12,6 @@ "LabelFinish": "Terminar", "LabelNext": "Siguiente", "LabelPrevious": "Anterior", - "LabelYourFirstName": "Su nombre:", "LabelYoureDone": "Ha terminado!", "MoreUsersCanBeAddedLater": "Se pueden agregar más usuarios más tarde desde el tablero.", "NewCollectionNameExample": "Ejemplo: Colección de Star Wars", @@ -50,7 +49,6 @@ "AccessRestrictedTryAgainLater": "El acceso está actualmente restringido. Por favor intente nuevamente más tarde.", "Actor": "Actor", "Add": "Agregar", - "AddItemToCollectionHelp": "Agregue elementos a las colecciones buscándolos y usando el click derecho o tocando el menú para añadirlos a una colección.", "AddToCollection": "Añadir a la colección", "AddToPlayQueue": "Añadir a la cola de reproducción", "AddToPlaylist": "Añadir a la lista de reproducción", @@ -78,7 +76,6 @@ "AttributeNew": "Nuevo", "Audio": "Audio", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (basado en configuración de idioma)", "Backdrop": "Fondo", "AllowHWTranscodingHelp": "Permita que el sintonizador transcodifique transmisiones sobre la marcha. Esto puede ayudar a reducir la transcodificación requerida por el servidor.", "AllowedRemoteAddressesHelp": "Lista separada por comas de direcciones IP o IP/máscara de red para redes a las que se les permitirá conectarse de forma remota. Si se deja vacía, todas las direcciones remotas serán permitidas.", @@ -128,7 +125,6 @@ "ButtonHelp": "Ayuda", "ButtonHome": "Inicio", "ButtonInfo": "Información", - "ButtonLearnMore": "Aprender más", "ButtonLibraryAccess": "Acceso a la biblioteca", "ButtonManualLogin": "Inicio de sesión manual", "ButtonMore": "Más", @@ -172,7 +168,6 @@ "ButtonTrailer": "Avance", "ButtonUninstall": "Desinstalar", "ButtonUp": "Arriba", - "ButtonViewWebsite": "Ver sitio web", "ButtonWebsite": "Sitio web", "CancelRecording": "Cancelar grabación", "CancelSeries": "Cancelar serie", @@ -310,7 +305,6 @@ "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos", "Shows": "Programas", - "CopyStreamURLError": "Hubo un error copiando la URL.", "CopyStreamURLSuccess": "URL copiada con éxito.", "CopyStreamURL": "Copiar URL de transmisión", "ButtonSplit": "Dividir", @@ -322,7 +316,6 @@ "HeaderBranding": "Marca", "HeaderBooks": "Libros", "HeaderBlockItemsWithNoRating": "Bloquear elementos con rating de información vacía o no reconocible:", - "HeaderAutomaticUpdates": "Actualizaciones Automáticas", "HeaderAudioSettings": "Configuraciones de audio", "HeaderAudioBooks": "Audiolibros", "HeaderAppearsOn": "Aparece en", @@ -587,8 +580,6 @@ "LabelAudioBitrate": "Velocidad de bits de audio:", "LabelAudio": "Audio", "LabelAllowedRemoteAddresses": "Filtro de dirección IP remota:", - "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante los períodos de inactividad cuando no haya usuarios activos.", - "LabelAllowServerAutoRestart": "Permita que el servidor se reinicie automáticamente para aplicar actualizaciones", "LabelAllowHWTranscoding": "Permitir transcodificación con hardware", "LabelAlbumArtists": "Artistas del álbum:", "LabelContentType": "Tipo de contenido:", @@ -685,7 +676,6 @@ "LabelDateAddedBehaviorHelp": "Si un valor de metadatos está presente, siempre se usará antes de cualquiera de estas opciones.", "LabelDateAddedBehavior": "Comportamiento de fecha agregada para contenido nuevo:", "LabelDateAdded": "Fecha agregada:", - "LabelDashboardTheme": "Tema del panel del servidor:", "LabelCustomRating": "Calificación personalizada:", "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjelo vacío para usar el nombre informado por el dispositivo.", "LabelCustomDeviceDisplayName": "Nombre para mostrar:", @@ -738,7 +728,6 @@ "LabelSportsCategories": "Categorías de deportes:", "LabelSpecialSeasonsDisplayName": "Nombre de la temporada especial:", "LabelSource": "Fuente:", - "LabelSoundEffects": "Efectos de sonido:", "LabelSortTitle": "Ordenar título:", "LabelSortOrder": "Orden de clasificación:", "LabelSortBy": "Ordenar por:", @@ -749,7 +738,6 @@ "LabelSkipIfAudioTrackPresentHelp": "Desmarque esto para asegurarse de que todos los videos tengan subtítulos, independientemente del idioma de audio.", "LabelSkipIfAudioTrackPresent": "Omita si la pista de audio predeterminada coincide con el idioma de descarga", "LabelSkipBackLength": "Saltar de nuevo la longitud:", - "LabelSkin": "Piel:", "LabelSize": "Tamaño:", "LabelSimultaneousConnectionLimit": "Límite de transmisiones simultáneas:", "LabelServerName": "Nombre del servidor:", @@ -827,7 +815,6 @@ "LabelNewPasswordConfirm": "Nueva contraseña confirmada:", "LabelNewPassword": "Nueva contraseña:", "LabelNewName": "Nuevo nombre:", - "LabelNightly": "Nocturna", "LabelStable": "Estable", "LabelChromecastVersion": "Versión de Chromecast", "LabelName": "Nombre:", @@ -994,10 +981,8 @@ "MessageLeaveEmptyToInherit": "Deje en blanco para heredar la configuración de un elemento primario o el valor predeterminado global.", "MessageItemsAdded": "Artículos añadidos.", "MessageItemSaved": "Artículo guardado.", - "MessageUnauthorizedUser": "No tiene autorización para acceder al servidor en este momento. Póngase en contacto con el administrador del servidor para obtener más información.", "MessageInvalidUser": "Usuario o contraseña inválidos. Inténtalo de nuevo.", "MessageInvalidForgotPasswordPin": "Se ingresó un código PIN no válido o caducado. Inténtalo de nuevo.", - "MessageInstallPluginFromApp": "Este complemento debe instalarse desde la aplicación en la que desea utilizarlo.", "MessageImageTypeNotSelected": "Seleccione un tipo de imagen del menú desplegable.", "MessageImageFileTypeAllowed": "Solo se admiten archivos JPEG y PNG.", "MessageForgotPasswordInNetworkRequired": "Intente nuevamente dentro de su red doméstica para iniciar el proceso de restablecimiento de contraseña.", @@ -1029,7 +1014,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Software", "MediaInfoTimestamp": "Marca de tiempo", "MediaInfoSize": "Tamaño", "MediaInfoSampleRate": "Frecuencia de muestreo", @@ -1063,11 +1047,9 @@ "LiveBroadcasts": "Transmisiones en vivo", "Live": "En vivo", "List": "Lista", - "LinksValue": "Enlaces: {0}", "Like": "Me gusta", "LeaveBlankToNotSetAPassword": "Puede dejar este campo en blanco para no establecer una contraseña.", "LearnHowYouCanContribute": "Aprende cómo puedes contribuir.", - "LaunchWebAppOnStartup": "Iniciar la interfaz web al iniciar el servidor", "LatestFromLibrary": "Últimos {0}", "Large": "Grande", "LabelffmpegPathHelp": "La ruta al archivo de la aplicación ffmpeg o la carpeta que contiene ffmpeg.", @@ -1173,7 +1155,6 @@ "MusicLibraryHelp": "Revise la {0}guía de nomenclatura musical{1}.", "MovieLibraryHelp": "Revise la {0}guía de nombres de películas{1}.", "LibraryAccessHelp": "Seleccione las bibliotecas para compartir con este usuario. Los administradores podrán editar todas las carpetas con el administrador de metadatos.", - "LaunchWebAppOnStartupHelp": "Abra el cliente web en su navegador web predeterminado cuando el servidor arranque inicialmente. Esto no ocurrirá cuando use la función de reinicio del servidor.", "LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para redes que se considerarán en la red local cuando se impongan restricciones de ancho de banda. Si se establece, todas las demás direcciones IP se considerarán en la red externa y estarán sujetas a las restricciones de ancho de banda externo. Si se deja en blanco, solo se considera que la subred del servidor está en la red local.", "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el espacio de nombres urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDoc": "Doc. X-DLNA:", @@ -1219,7 +1200,6 @@ "RecentlyWatched": "Recientemente visto", "Rate": "Velocidad", "Raised": "Elevado", - "QueueAllFromHere": "Hacer cola todo desde aquí", "Quality": "Calidad", "Programs": "Programas", "ProductionLocations": "Ubicaciones de producción", @@ -1264,7 +1244,6 @@ "PackageInstallCompleted": "Instalación {0} (versión {1}) completada.", "PackageInstallCancelled": "Instalación de {0} (versión {1}) cancelada.", "Overview": "Visión general", - "OtherArtist": "Otro artista", "OriginalAirDateValue": "Fecha de emisión original: {0}", "OptionWeekly": "Semanal", "OptionWeekends": "Fines de semana", @@ -1354,7 +1333,6 @@ "RemoveFromPlaylist": "Eliminar de la lista de reproducción", "RemoveFromCollection": "Eliminar de la colección", "RememberMe": "Recuérdame", - "ReleaseGroup": "Grupo de lanzamiento", "ButtonCast": "Transmitir", "ButtonSyncPlay": "SyncPlay", "EnableBlurHashHelp": "Las imágenes que aún se están cargando se mostrarán con un marcador de posición borroso", @@ -1386,7 +1364,6 @@ "Watched": "Visto", "ViewPlaybackInfo": "Ver información de reproducción", "ViewAlbum": "Ver álbum", - "VideoRange": "Rango de video", "Vertical": "Vertical", "ValueTimeLimitSingleHour": "Límite de tiempo: 1 hora", "ValueTimeLimitMultiHour": "Límite de tiempo: {0} horas", @@ -1453,7 +1430,6 @@ "TabProfile": "Perfil", "TabPlugins": "Complementos", "TabPlaylists": "Listas de reproducción", - "TabPlaylist": "Lista de reproducción", "TabPlayback": "Reproducción", "TabPassword": "Contraseña", "TabParentalControl": "Control parental", @@ -1557,7 +1533,6 @@ "Save": "Guardar", "Saturday": "Sábado", "Runtime": "Tiempo de ejecución", - "RunAtStartup": "Ejecutar en el arranque", "Rewind": "Rebobinar", "ResumeAt": "Reanudar desde {0}", "ButtonPlayer": "Reproductor", diff --git a/src/strings/es-mx.json b/src/strings/es-mx.json index 98b9fd4ab0..b0923c0228 100644 --- a/src/strings/es-mx.json +++ b/src/strings/es-mx.json @@ -2,7 +2,6 @@ "Absolute": "Absoluto", "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.", "Add": "Agregar", - "AddItemToCollectionHelp": "Agrega elementos a las colecciones buscándolos y utilizando sus menúes al hacer clic derecho o al tocarlos para agregarlos a una colección.", "AddToCollection": "Agregar a colección", "AddToPlayQueue": "Agregar a la cola de reproducción", "AddToPlaylist": "Agregar a lista de reproducción", @@ -37,7 +36,6 @@ "Ascending": "Ascendente", "AspectRatio": "Relación de aspecto", "AttributeNew": "Nuevo", - "AutoBasedOnLanguageSetting": "Auto (basado en la configuración del idioma)", "Backdrop": "Imagen de fondo", "Backdrops": "Imágenes de fondo", "Banner": "Banner", @@ -80,7 +78,6 @@ "ButtonGuide": "Guía", "ButtonHelp": "Ayuda", "ButtonHome": "Inicio", - "ButtonLearnMore": "Aprender más", "ButtonLibraryAccess": "Acceso a biblioteca(s)", "ButtonManualLogin": "Inicio de sesión manual", "ButtonMore": "Más", @@ -124,7 +121,6 @@ "ButtonSubtitles": "Subtítulos", "ButtonUninstall": "Desinstalar", "ButtonUp": "Arriba", - "ButtonViewWebsite": "Ver sitio web", "ButtonWebsite": "Sitio web", "CancelRecording": "Cancelar grabación", "CancelSeries": "Cancelar serie", @@ -284,7 +280,6 @@ "HeaderAppearsOn": "Aparece en", "HeaderAudioBooks": "Audiolibros", "HeaderAudioSettings": "Configuración de audio", - "HeaderAutomaticUpdates": "Actualizaciones automáticas", "HeaderBlockItemsWithNoRating": "Bloquear elementos sin clasificación o con información de clasificación desconocida:", "HeaderBooks": "Libros", "HeaderBranding": "Establecer marca", @@ -502,8 +497,6 @@ "LabelAlbumArtists": "Artistas del álbum:", "LabelAll": "Todos", "LabelAllowHWTranscoding": "Permitir transcodificación por hardware", - "LabelAllowServerAutoRestart": "Permite al servidor reiniciarse automáticamente para aplicar actualizaciones", - "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante los períodos de inactividad cuando no haya usuarios activos.", "LabelAllowedRemoteAddresses": "Filtro de direcciones IP remotas:", "LabelAllowedRemoteAddressesMode": "Modo de filtrado de direcciones IP remotas:", "LabelAppName": "Nombre de la aplicación", @@ -540,7 +533,6 @@ "LabelCustomDeviceDisplayName": "Nombre a mostrar:", "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjalo vacío para usar el nombre reportado por el dispositivo.", "LabelCustomRating": "Calificación personalizada:", - "LabelDashboardTheme": "Tema del panel de control del servidor:", "LabelDateAdded": "Fecha de adición:", "LabelDateAddedBehavior": "Comportamiento de la fecha de adición para nuevo contenido:", "LabelDateAddedBehaviorHelp": "Si un valor de metadatos está presente, siempre se utilizará antes de cualquiera de estas opciones.", @@ -751,7 +743,6 @@ "LabelServerHost": "Servidor:", "LabelServerHostHelp": "192.168.1.100:8096 o https://miservidor.com", "LabelSimultaneousConnectionLimit": "Límite de transmisiones simultáneas:", - "LabelSkin": "Apariencia:", "LabelSkipBackLength": "Longitud de salto hacia atrás:", "LabelSkipForwardLength": "Longitud de salto hacia adelante:", "LabelSkipIfAudioTrackPresent": "Omitir si la pista de audio por defecto coincide con el idioma de descarga", @@ -763,7 +754,6 @@ "LabelSortBy": "Ordenar por:", "LabelSortOrder": "Clasificar ordenado:", "LabelSortTitle": "Título para ordenar:", - "LabelSoundEffects": "Efectos de sonido:", "LabelSource": "Fuente:", "LabelSpecialSeasonsDisplayName": "Nombre de la temporada de especiales:", "LabelSportsCategories": "Categorías de deportes:", @@ -816,7 +806,6 @@ "LabelXDlnaDoc": "Documento X-DLNA:", "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el namespace urn:schemas-dlna-org:device-1-0.", "LabelYear": "Año:", - "LabelYourFirstName": "Tu nombre:", "LabelYoureDone": "¡Has terminado!", "LabelZipCode": "Código postal:", "LabelffmpegPath": "Ruta del FFmpeg:", @@ -827,7 +816,6 @@ "LearnHowYouCanContribute": "Aprende cómo puedes contribuir.", "LibraryAccessHelp": "Selecciona las bibliotecas que deseas compartir con este usuario. Los administradores podrán editar todas las carpetas utilizando el gestor de metadatos.", "Like": "Me gusta", - "LinksValue": "Enlaces: {0}", "List": "Lista", "Live": "En vivo", "LiveBroadcasts": "Emisiones en vivo", @@ -885,7 +873,6 @@ "MessageFileReadError": "Hubo un error al leer el archivo. Por favor, intenta de nuevo.", "MessageForgotPasswordFileCreated": "El siguiente archivo fue creado en tu servidor y contiene instrucciones de como proceder:", "MessageForgotPasswordInNetworkRequired": "Por favor, intenta de nuevo dentro de tu red local para iniciar el proceso de restablecimiento de contraseña.", - "MessageInstallPluginFromApp": "Este complemento debe ser instalado desde dentro de la aplicación en la que deseas usarlo.", "MessageInvalidForgotPasswordPin": "Se ha introducido un código PIN inválido o expirado. Por favor, inténtalo de nuevo.", "MessageInvalidUser": "Nombre de usuario o contraseña inválidos. Por favor, intenta de nuevo.", "MessageItemSaved": "Elemento guardado.", @@ -1120,7 +1107,6 @@ "ProductionLocations": "Lugares de producción", "Programs": "Programas", "Quality": "Calidad", - "QueueAllFromHere": "Encolar todos desde aquí", "Raised": "Elevado", "Rate": "Calificación", "RecentlyWatched": "Visto recientemente", @@ -1151,7 +1137,6 @@ "ReplaceExistingImages": "Reemplazar imágenes existentes", "ResumeAt": "Reanudar desde {0}", "Rewind": "Rebobinar", - "RunAtStartup": "Ejecutar al iniciar", "Runtime": "Duración", "Saturday": "Sábado", "Save": "Guardar", @@ -1243,7 +1228,6 @@ "TabParentalControl": "Control parental", "TabPassword": "Contraseña", "TabPlayback": "Reproducción", - "TabPlaylist": "Lista de reproducción", "TabPlaylists": "Listas de reproducción", "TabPlugins": "Complementos", "TabProfile": "Perfil", @@ -1310,7 +1294,6 @@ "ValueTimeLimitMultiHour": "Límite de tiempo: {0} horas", "ValueTimeLimitSingleHour": "Límite de tiempo: 1 hora", "ValueVideoCodec": "Códec de video: {0}", - "VideoRange": "Rango de video", "ViewAlbum": "Ver álbum", "ViewPlaybackInfo": "Ver información de reproducción", "Watched": "Visto", @@ -1365,11 +1348,8 @@ "DashboardArchitecture": "Arquitectura: {0}", "LabelVideo": "Video", "LabelWeb": "Web:", - "LaunchWebAppOnStartup": "Iniciar la interfaz web al iniciar el servidor", - "LaunchWebAppOnStartupHelp": "Abre el cliente web en su navegador web predeterminado cuando se inicia el servidor. Esto no ocurrirá cuando se utilice la función de reinicio del servidor.", "LeaveBlankToNotSetAPassword": "Puedes dejar este campo en blanco para no establecer ninguna contraseña.", "MediaInfoCodec": "Códec", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Dato", "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", @@ -1440,7 +1420,6 @@ "LabelSize": "Tamaño:", "SelectAdminUsername": "Por favor, selecciona un nombre de usuario para la cuenta de administrador.", "LabelDroppedFrames": "Cuadros saltados:", - "CopyStreamURLError": "Hubo un error al copiar la URL.", "ButtonSplit": "Dividir", "WeeklyAt": "{0}s a las {1}", "OnApplicationStartup": "Cuando se inicia la aplicación", @@ -1456,12 +1435,10 @@ "PathNotFound": "No se pudo encontrar la ruta. Por favor, asegúrate de que la ruta es válida e inténtalo de nuevo.", "Track": "Pista", "Season": "Temporada", - "ReleaseGroup": "Grupo que lo estrenó", "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir información del episodio incrustada a los nombres de archivo", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Esto utiliza la información del episodio desde los metadatos incrustados si están disponibles.", "PlaybackErrorNoCompatibleStream": "Este cliente no es compatible con los medios y el servidor no está enviando un formato de medios compatible.", "Person": "Persona", - "OtherArtist": "Otro artista", "OptionRandom": "Aleatorio", "OptionForceRemoteSourceTranscoding": "Forzar transcodificación de fuentes remotas (como TV en vivo)", "NoCreatedLibraries": "Parece que no has creado ninguna biblioteca todavía. {0}¿Quisieras crear una ahora?{1}", @@ -1488,7 +1465,6 @@ "DeinterlaceMethodHelp": "Seleccione el método de desentrelazado que se usará al transcodificar contenido entrelazado.", "Filter": "Filtro", "New": "Nuevo", - "MessageUnauthorizedUser": "No estás autorizado para acceder al servidor en este momento. Por favor, contacta al administrador del servidor para más información.", "LabelLibraryPageSizeHelp": "Establece el número de elementos a mostrar en una página de biblioteca. Establece en 0 para deshabilitar el paginado.", "LabelLibraryPageSize": "Tamaño de las páginas de las bibliotecas:", "HeaderFavoritePlaylists": "Listas de reproducción favoritas", @@ -1499,7 +1475,6 @@ "SaveChanges": "Guardar cambios", "LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes a través de HTTP a HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.", "LabelRequireHttps": "Requerir HTTPS", - "LabelNightly": "Nocturno", "LabelStable": "Estable", "LabelChromecastVersion": "Versión de Chromecast", "LabelEnableHttpsHelp": "Permite al servidor escuchar en el puerto HTTPS configurado. Un certificado válido también debe ser configurado para que esto tenga efecto.", diff --git a/src/strings/es.json b/src/strings/es.json index 44456fb83e..d720b515f4 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1,7 +1,6 @@ { "AccessRestrictedTryAgainLater": "Actualmente el acceso está restringido. Por favor, inténtalo de nuevo más tarde.", "Add": "Añadir", - "AddItemToCollectionHelp": "Puedes añadir elementos a las colecciones buscándolos en tu biblioteca. Una vez hecho esto, abre el menú y selecciona 'Añadir a una colección'.", "AddToCollection": "Añadir a una colección", "AddToPlaylist": "Añadir a una lista de reproducción", "AddedOnValue": "Añadido {0}", @@ -69,7 +68,6 @@ "ButtonGuide": "Guía", "ButtonHelp": "Ayuda", "ButtonHome": "Inicio", - "ButtonLearnMore": "Aprende más", "ButtonLibraryAccess": "Acceso a la biblioteca", "ButtonManualLogin": "Acceder manualmente", "ButtonMore": "Más", @@ -115,7 +113,6 @@ "ButtonTrailer": "Tráiler", "ButtonUninstall": "Desinstalar", "ButtonUp": "Arriba", - "ButtonViewWebsite": "Ver sitio web", "ButtonWebsite": "Sitio web", "CancelRecording": "Cancelar grabación", "CancelSeries": "Cancelar series", @@ -237,7 +234,6 @@ "HeaderApiKeysHelp": "Las aplicaciones externas requieren de una clave API para comunicarse con el servidor Jellyfin. Las claves se facilitan iniciando sesión con una cuenta de Jellyfin, u otorgando manualmente una clave a la aplicación.", "HeaderAudioBooks": "Audiolibros", "HeaderAudioSettings": "Ajustes de audio", - "HeaderAutomaticUpdates": "Actualizaciones automáticas", "HeaderBlockItemsWithNoRating": "Bloquear artículos sin valoraciones o si son desconocidas:", "HeaderBooks": "Libros", "HeaderCancelRecording": "Cancelar grabación", @@ -448,8 +444,6 @@ "LabelAlbumArtists": "Artistas de los álbumes:", "LabelAll": "Todo", "LabelAllowHWTranscoding": "Activar la conversión acelerada por hardware", - "LabelAllowServerAutoRestart": "Permitir al servidor reiniciarse automáticamente para aplicar las actualizaciones", - "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante periodos de reposo, cuando no haya usuarios activos.", "LabelAllowedRemoteAddresses": "Filtro de dirección IP remota:", "LabelAllowedRemoteAddressesMode": "Modo de filtro de dirección IP remota:", "LabelAppName": "Nombre de la aplicación", @@ -742,7 +736,6 @@ "LabelXDlnaCapHelp": "Determina el contenido del elemento X_DLNACAP en el espacio de nombre urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el espacio de nombreurn:schemas-dlna-org:device-1-0.", "LabelYear": "Año:", - "LabelYourFirstName": "Tu nombre:", "LabelYoureDone": "¡Ya está!", "LabelZipCode": "Código postal:", "LabelffmpegPath": "Ruta de ffmpeg:", @@ -807,7 +800,6 @@ "MessageFileReadError": "Ha habido un error leyendo el fichero. Por favor, inténtalo más tarde.", "MessageForgotPasswordFileCreated": "Se ha creado el siguiente archivo en tu servidor y contiene instrucciones de cómo proceder:", "MessageForgotPasswordInNetworkRequired": "Por favor, inténtalo de nuevo desde tu red de casa para iniciar el proceso de restablecimiento de la contraseña.", - "MessageInstallPluginFromApp": "Este complemento debe instalarse desde la aplicación en la que lo vayas a usar.", "MessageInvalidForgotPasswordPin": "Ha introducido un código PIN inválido o expirado. Por favor, inténtelo de nuevo.", "MessageInvalidUser": "Usuario o contraseña inválidos. Por favor, inténtalo otra vez.", "MessageItemSaved": "Elemento grabado.", @@ -1027,7 +1019,6 @@ "ProductionLocations": "Localizaciones de producción", "Programs": "Programas", "Quality": "Calidad", - "QueueAllFromHere": "En cola todos desde aquí", "Rate": "Califica", "RecentlyWatched": "Vistos recientemente", "RecommendationBecauseYouLike": "Ya que te ha gustado {0}, quizá te pueda interesar", @@ -1138,7 +1129,6 @@ "TabParentalControl": "Control parental", "TabPassword": "Contraseña", "TabPlayback": "Reproducción", - "TabPlaylist": "Lista de reproducción", "TabPlaylists": "Listas de reproducción", "TabProfile": "Perfil", "TabProfiles": "Perfiles", @@ -1224,7 +1214,6 @@ "Ascending": "Ascendente", "Audio": "Audio", "Auto": "Automático", - "AutoBasedOnLanguageSetting": "Automático (basado en la configuración de idioma)", "Banner": "Pancarta", "BurnSubtitlesHelp": "Determina si el servidor debe grabar los subtítulos en el vídeo al transcodificar. Desactivar esta opción puede mejorar el rendimiento. Seleccione 'Auto' para grabar formatos basados en imágenes (VOBSUB, PGS, SUB/IDX) y ciertos subtítulos ASS o SSA.", "ButtonInfo": "Información", @@ -1282,7 +1271,6 @@ "Horizontal": "Horizontal", "LabelAudio": "Audio", "LabelBurnSubtitles": "Incrustar subtítulos:", - "LabelDashboardTheme": "Tema para la interfaz del servidor:", "LabelDateTimeLocale": "Fecha y hora local:", "LabelDefaultScreen": "Pantalla por defecto:", "LabelDisplayLanguage": "Mostrar idioma:", @@ -1292,12 +1280,10 @@ "LabelParentNumber": "Número del elemento padre:", "LabelPersonRoleHelp": "Ejemplo: Conductor de camión de helados", "LabelServerHost": "Host:", - "LabelSkin": "Tema:", "LabelSkipBackLength": "Tiempo de retroceso:", "LabelSkipForwardLength": "Tiempo de avance:", "LabelSortBy": "Ordenar por:", "LabelSortOrder": "Orden:", - "LabelSoundEffects": "Efectos de sonido:", "LabelSubtitles": "Subtítulos", "LabelTVHomeScreen": "Modo televisión en pantalla de inicio:", "LabelVersion": "Versión:", @@ -1306,7 +1292,6 @@ "LabelXDlnaDoc": "X-DLNA doc:", "LearnHowYouCanContribute": "Descubre cómo puedes contribuir.", "LeaveBlankToNotSetAPassword": "Puedes dejarlo en blanco para no configurar una contraseña.", - "LinksValue": "Enlaces: {0}", "List": "Lista", "Logo": "Logo", "MediaIsBeingConverted": "El medio está siendo convertido en un formato compatible con el dispositivo que lo está reproduciendo.", @@ -1351,9 +1336,6 @@ "DashboardOperatingSystem": "Sistema operativo: {0}", "DashboardArchitecture": "Arquitectura: {0}", "LabelWeb": "Web:", - "LaunchWebAppOnStartup": "Iniciar la aplicación web al iniciar el servidor", - "LaunchWebAppOnStartupHelp": "Abrir el cliente web en el navegador por defecto al iniciar el servidor. Esto no ocurrirá al utilizar la función de reinicio del servidor.", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Datos", "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", @@ -1385,7 +1367,6 @@ "Premiere": "Estreno", "Raised": "Elevación", "RefreshDialogHelp": "Las etiquetas se actualizan basándose en las configuraciones y los servicios de internet activados desde el panel de control de Jellyfin.", - "RunAtStartup": "Ejecutar al iniciar", "Series": "Series", "SeriesDisplayOrderHelp": "Ordena los episodios por fecha de emisión, orden de DVD o número absoluto.", "ShowTitle": "Mostrar título", @@ -1412,7 +1393,6 @@ "ValueMinutes": "{0} min", "ValueSeriesCount": "{0} series", "Vertical": "Vertical", - "VideoRange": "Rango de vídeo", "ThemeVideos": "Vídeos de tema", "TabNetworking": "Redes", "CopyStreamURL": "Copiar URL de Stream", @@ -1447,7 +1427,6 @@ "MessageConfirmAppExit": "¿Quieres salir?", "EnableFasterAnimationsHelp": "Las animaciones y transiciones durarán menos tiempo", "EnableFasterAnimations": "Animaciones más rápidas", - "CopyStreamURLError": "Ha habido un error copiando la dirección.", "AllowFfmpegThrottlingHelp": "Las conversiones se pausarán cuando se adelanten lo suficiente desde la posición en la que se encuentre el reproductor. Puede reducir la carga en el servidor y es útil cuando se reproduce de forma continua, sin saltar entre intervalos de tiempo, pero puede que tengas que desactivarlo si experimentas problemas en la reproducción o cambias de posición frecuentemente mientras reproduces contenido.", "PlaybackErrorNoCompatibleStream": "Este contenido no es compatible con este dispositivo y no se puede reproducir: No se puede obtener del servidor en un formato compatible.", "OptionForceRemoteSourceTranscoding": "Forzar la conversión para fuentes externas (como la televisión en directo)", @@ -1476,9 +1455,7 @@ "OnApplicationStartup": "Al iniciarse el servidor", "Track": "Pista", "Season": "Temporada", - "ReleaseGroup": "Grupo de salida", "Person": "Persona", - "OtherArtist": "Otro artista", "Movie": "Película", "Episode": "Episodio", "BoxSet": "Box Set", @@ -1492,14 +1469,12 @@ "UnsupportedPlayback": "No es posible desencriptar contenido protegido mediante DRM; sin embargo se intentará su reproducción. Algunos archivos pueden aparecer completamente negros debido a encriptación u otras características no soportadas, como títulos interactivos.", "YadifBob": "YADIF Bob", "Yadif": "YADIF", - "MessageUnauthorizedUser": "No tiene autorización para acceder al servidor en este momento. Póngase en contacto con el administrador del servidor para obtener más información.", "ButtonTogglePlaylist": "Lista de reproducción", "ButtonToggleContextMenu": "Más", "Filter": "Filtro", "New": "Nuevo", "HeaderFavoritePlaylists": "Lista reproducción favorita", "ApiKeysCaption": "Lista de las claves API actuales", - "LabelNightly": "Nightly", "LabelStable": "Estable", "LabelChromecastVersion": "Versión de Chromecast", "HeaderServerAddressSettings": "Configuración de la dirección del Servidor", diff --git a/src/strings/es_419.json b/src/strings/es_419.json index fad50d979a..54dd68e9ed 100644 --- a/src/strings/es_419.json +++ b/src/strings/es_419.json @@ -34,7 +34,6 @@ "AddToPlaylist": "Agregar a lista de reproducción", "AddToPlayQueue": "Agregar a la cola de reproducción", "AddToCollection": "Agregar a colección", - "AddItemToCollectionHelp": "Agrega elementos a las colecciones buscándolos y utilizando sus menúes al hacer clic derecho o al tocarlos para agregarlos a una colección.", "Add": "Agregar", "Actor": "Actor", "AccessRestrictedTryAgainLater": "El acceso está restringido actualmente. Por favor, inténtalo más tarde.", @@ -42,13 +41,11 @@ "YadifBob": "YADIF Bob", "Trailers": "Trailers", "TabTrailers": "Trailers", - "ReleaseGroup": "Grupo que lo estrenó", "OptionThumbCard": "Miniatura de imagen", "OptionResElement": "elemento reanudable", "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", "OptionBluray": "Blu-ray", "OptionBlockTrailers": "Trailers", - "LabelNightly": "Construcciones nocturnas", "HeaderVideos": "Videos", "Director": "Director", "Depressed": "No presionado", @@ -83,7 +80,6 @@ "Watched": "Visto", "ViewPlaybackInfo": "Ver información de reproducción", "ViewAlbum": "Ver álbum", - "VideoRange": "Rango de video", "Vertical": "Vertical", "ValueVideoCodec": "Códec de video: {0}", "ValueTimeLimitSingleHour": "Límite de tiempo: 1 hora", @@ -152,7 +148,6 @@ "TabProfile": "Perfil", "TabPlugins": "Complementos", "TabPlaylists": "Listas de reproducción", - "TabPlaylist": "Lista de reproducción", "TabPlayback": "Reproducción", "TabPassword": "Contraseña", "TabParentalControl": "Control parental", @@ -378,10 +373,8 @@ "MessageLeaveEmptyToInherit": "Dejar vacío para heredar la configuración de un elemento superior o del valor predeterminado global.", "MessageItemsAdded": "Elementos agregados.", "MessageItemSaved": "Elemento guardado.", - "MessageUnauthorizedUser": "No estás autorizado para acceder al servidor en este momento. Por favor, contacta al administrador del servidor para más información.", "MessageInvalidUser": "Nombre de usuario o contraseña inválidos. Por favor, intenta de nuevo.", "MessageInvalidForgotPasswordPin": "Se ha introducido un código PIN inválido o expirado. Por favor, inténtalo de nuevo.", - "MessageInstallPluginFromApp": "Este complemento debe ser instalado desde dentro de la aplicación en la que deseas usarlo.", "MessageImageTypeNotSelected": "Por favor, selecciona un tipo de imagen del menú desplegable.", "MessageImageFileTypeAllowed": "Solo son soportados archivos JPEG y PNG.", "MessageForgotPasswordInNetworkRequired": "Por favor, intenta de nuevo dentro de tu red local para iniciar el proceso de restablecimiento de contraseña.", @@ -392,13 +385,10 @@ "MessageDirectoryPickerLinuxInstruction": "Para Linux en Arch Linux, CentOS, Debian, Fedora, openSUSE o Ubuntu, debes conceder al usuario del servicio al menos permisos de lectura a tus ubicaciones de almacenamiento.", "MessageDirectoryPickerBSDInstruction": "Para BSD, quizás necesites configurar el almacenamiento dentro de tu «FreeNAS Jail» de manera que permita a Jellyfin accederlo.", "List": "Lista", - "LinksValue": "Enlaces: {0}", "Like": "Me gusta", "LibraryAccessHelp": "Selecciona las bibliotecas que deseas compartir con este usuario. Los administradores podrán editar todas las carpetas utilizando el gestor de metadatos.", "LeaveBlankToNotSetAPassword": "Puedes dejar este campo en blanco para no establecer ninguna contraseña.", "LearnHowYouCanContribute": "Aprende cómo puedes contribuir.", - "LaunchWebAppOnStartupHelp": "Abre el cliente web en su navegador web predeterminado cuando se inicia el servidor. Esto no ocurrirá cuando se utilice la función de reinicio del servidor.", - "LaunchWebAppOnStartup": "Iniciar la interfaz web al iniciar el servidor", "LatestFromLibrary": "Últimas - {0}", "Large": "Grande", "LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para las redes que se considerarán en la red local al aplicar las restricciones de ancho de banda. Si se establecen, todas las demás direcciones IP se considerarán como parte de la red externa y estarán sujetas a las restricciones de ancho de banda externa. Si se deja en blanco, solo se considera a la subred del servidor estar en la red local.", @@ -406,7 +396,6 @@ "LabelffmpegPath": "Ruta del FFmpeg:", "LabelZipCode": "Código postal:", "LabelYoureDone": "¡Has terminado!", - "LabelYourFirstName": "Tu nombre:", "LabelYear": "Año:", "LabelXDlnaDocHelp": "Determina el contenido del elemento X_DLNADOC en el namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDoc": "Documento X-DLNA:", @@ -486,7 +475,6 @@ "LabelSportsCategories": "Categorías de deportes:", "LabelSpecialSeasonsDisplayName": "Nombre de la temporada de especiales:", "LabelSource": "Fuente:", - "LabelSoundEffects": "Efectos de sonido:", "LabelSortTitle": "Título para ordenar:", "LabelSortOrder": "Clasificar ordenado:", "LabelSortBy": "Ordenar por:", @@ -560,7 +548,6 @@ "Save": "Guardar", "Saturday": "Sábado", "Runtime": "Duración", - "RunAtStartup": "Ejecutar al iniciar", "Rewind": "Rebobinar", "ResumeAt": "Reanudar desde {0}", "ReplaceExistingImages": "Reemplazar imágenes existentes", @@ -591,7 +578,6 @@ "RecentlyWatched": "Visto recientemente", "Rate": "Calificación", "Raised": "Elevado", - "QueueAllFromHere": "Encolar todos desde aquí", "Quality": "Calidad", "Programs": "Programas", "ProductionLocations": "Lugares de producción", @@ -637,7 +623,6 @@ "PackageInstallCompleted": "Instalación completada de {0} (versión {1}).", "PackageInstallCancelled": "Instalación cancelada de {0} (versión {1}).", "Overview": "Resumen", - "OtherArtist": "Otro artista", "OriginalAirDateValue": "Fecha de emisión original: {0}", "OptionWeekly": "Semanal", "OptionWeekends": "Fines de semana", @@ -674,7 +659,6 @@ "OptionProfileAudio": "Audio", "OptionPremiereDate": "Fecha de estreno", "OptionPosterCard": "Ficha de póster", - "LabelSkin": "Apariencia:", "LabelSize": "Tamaño:", "LabelSimultaneousConnectionLimit": "Límite de transmisiones simultáneas:", "LabelServerName": "Nombre del servidor:", @@ -869,7 +853,6 @@ "LabelDateAddedBehaviorHelp": "Si un valor de metadatos está presente, siempre se utilizará antes de cualquiera de estas opciones.", "LabelDateAddedBehavior": "Comportamiento de la fecha de adición para nuevo contenido:", "LabelDateAdded": "Fecha de adición:", - "LabelDashboardTheme": "Tema del panel de control del servidor:", "LabelCustomRating": "Calificación personalizada:", "LabelCustomDeviceDisplayNameHelp": "Proporcione un nombre personalizado para mostrar o déjalo vacío para usar el nombre reportado por el dispositivo.", "LabelCustomDeviceDisplayName": "Nombre a mostrar:", @@ -917,7 +900,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Imagen incrustada", "MediaInfoStreamTypeData": "Dato", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Software", "MediaInfoTimestamp": "Fecha y hora", "MediaInfoSize": "Tamaño", "MediaInfoSampleRate": "Tasa de muestreo", @@ -980,8 +962,6 @@ "LabelAppName": "Nombre de la aplicación", "LabelAllowedRemoteAddressesMode": "Modo de filtrado de direcciones IP remotas:", "LabelAllowedRemoteAddresses": "Filtro de direcciones IP remotas:", - "LabelAllowServerAutoRestartHelp": "El servidor solo se reiniciará durante los períodos de inactividad cuando no haya usuarios activos.", - "LabelAllowServerAutoRestart": "Permite al servidor reiniciarse automáticamente para aplicar actualizaciones", "LabelAllowHWTranscoding": "Permitir transcodificación por hardware", "LabelAll": "Todos", "LabelAlbumArtists": "Artistas del álbum:", @@ -1241,7 +1221,6 @@ "HeaderBranding": "Establecer marca", "HeaderBooks": "Libros", "HeaderBlockItemsWithNoRating": "Bloquear elementos sin clasificación o con información de clasificación desconocida:", - "HeaderAutomaticUpdates": "Actualizaciones automáticas", "HeaderAudioSettings": "Configuración de audio", "HeaderAudioBooks": "Audiolibros", "HeaderAppearsOn": "Aparece en", @@ -1381,7 +1360,6 @@ "DateAdded": "Fecha de adición", "CustomDlnaProfilesHelp": "Crear un perfil personalizado para un nuevo dispositivo o reemplazar un perfil del sistema.", "CriticRating": "Calificación de los críticos", - "CopyStreamURLError": "Hubo un error al copiar la URL.", "CopyStreamURLSuccess": "URL copiada con éxito.", "CopyStreamURL": "Copiar la URL de la transmisión", "Continuing": "Continuando", @@ -1415,7 +1393,6 @@ "CancelSeries": "Cancelar serie", "CancelRecording": "Cancelar grabación", "ButtonWebsite": "Sitio web", - "ButtonViewWebsite": "Ver sitio web", "ButtonUp": "Arriba", "ButtonUninstall": "Desinstalar", "ButtonTrailer": "Trailer", @@ -1464,7 +1441,6 @@ "ButtonMore": "Más", "ButtonManualLogin": "Inicio de sesión manual", "ButtonLibraryAccess": "Acceso a biblioteca(s)", - "ButtonLearnMore": "Aprender más", "ButtonInfo": "Info", "ButtonHome": "Inicio", "ButtonHelp": "Ayuda", @@ -1508,7 +1484,6 @@ "Banner": "Banner", "Backdrops": "Imágenes de fondo", "Backdrop": "Imagen de fondo", - "AutoBasedOnLanguageSetting": "Auto (basado en la configuración del idioma)", "Auto": "Auto", "AuthProviderHelp": "Selecciona un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", "Audio": "Audio", diff --git a/src/strings/fa.json b/src/strings/fa.json index fa2e67474c..7d27860aaa 100644 --- a/src/strings/fa.json +++ b/src/strings/fa.json @@ -64,7 +64,6 @@ "LabelSelectUsers": "انتخاب کاربران:", "LabelTimeLimitHours": "محدودیت زمان (ساعت):", "LabelTypeMetadataDownloaders": "{0} دانلود کننده فراداده:", - "LabelYourFirstName": "اسم کوچک شما:", "LabelYoureDone": "به پایان رسید!", "LibraryAccessHelp": "انتخاب پوشه های رسانه برای اشتراک گذاری با این کاربر. مدیر سیستم میتواند با استفاده از مدیریت متاداده همه ی پوشه ها را ویرایش کند.", "ManageLibrary": "مدیریت کتابخانه", @@ -99,7 +98,6 @@ "TabNetworks": "شبکه ها", "TabNotifications": "اعلان ها", "TabPassword": "رمز عبور", - "TabPlaylist": "لیست پخش", "TabProfile": "پروفایل", "TabProfiles": "پروفایل ها", "TabShows": "سریال ها", @@ -168,7 +166,6 @@ "ButtonMore": "بیشتر", "ButtonManualLogin": "ورود دستی", "ButtonLibraryAccess": "دسترسی به کتابخانه", - "ButtonLearnMore": "بیشتر بدانید", "ButtonInfo": "اطلاعات", "ButtonHome": "خانه", "ButtonHelp": "کمک", @@ -202,7 +199,6 @@ "Banner": "سرصفحه", "Backdrops": "پس زمینه‌ها", "Backdrop": "پس زمینه", - "AutoBasedOnLanguageSetting": "خودکار (بر اساس تنظیمات زبانی)", "Auto": "خودکار", "Audio": "صدا", "AttributeNew": "جدید", @@ -278,7 +274,6 @@ "DatePlayed": "تاریخ پخش شده", "DateAdded": "تاریخ اضافه شده", "CriticRating": "امتیاز منتقدان", - "CopyStreamURLError": "در کپی کردن آدرس خطایی رخ داد.", "CopyStreamURLSuccess": "آدرس با موفقیت کپی شد.", "CopyStreamURL": "کپی آدرس پخش", "Continuing": "ادامه", @@ -299,7 +294,6 @@ "CancelSeries": "لغو سریال‌ها", "CancelRecording": "لغو ضبط", "ButtonWebsite": "وبسایت", - "ButtonViewWebsite": "بازدید وبسایت", "ButtonUp": "بالا", "ButtonUninstall": "حذف نصب", "ButtonTrailer": "تریلر", @@ -417,7 +411,6 @@ "LabelSkipIfAudioTrackPresent": "اگر صدای پیش‌فرض با زبان دانلودی یکسان است پرش کن", "LabelSkipForwardLength": "میزان رفتن به جلو:", "LabelSkipBackLength": "میزان بازگشت به عقب:", - "LabelSkin": "پوسته:", "LabelSize": "سایز:", "LabelSimultaneousConnectionLimit": "محدودیت پخش همزمان:", "LabelServerName": "نام سرور:", @@ -545,7 +538,6 @@ "Watched": "مشاهده شده", "ViewPlaybackInfo": "مشاهده اطلاعات پخش", "ViewAlbum": "مشاهده آلبوم", - "VideoRange": "محدوده ویدیو", "Vertical": "عمودی", "ValueVideoCodec": "کدک ویدیو: {0}", "ValueTimeLimitSingleHour": "محدودیت زمانی: 1 ساعت", @@ -566,7 +558,6 @@ "ValueDiscNumber": "دیسک {0}", "LabelImportOnlyFavoriteChannels": "محدود کردن کانال‌هایی که به عنوان مورد علاقه انتخاب شده‌اند", "LabelDateAdded": "تاریخ اضافه شده:", - "LabelDashboardTheme": "تم داشبورد سرور:", "LabelCustomRating": "امتیازدهی سفارشی:", "LabelCustomDeviceDisplayName": "نام نمایشی:", "LabelCustomCssHelp": "ظاهر سفارشی مورد نظر خود را در رابط وب اعمال کنید.", @@ -592,7 +583,6 @@ "LabelSportsCategories": "دسته‌بندی‌های ورزشی:", "LabelSpecialSeasonsDisplayName": "نام نمایشی فصل مخصوص:", "LabelSource": "منبع:", - "LabelSoundEffects": "جلوه‌های صدا:", "LabelSortTitle": "مرتب‌سازی عنوان:", "LabelSortOrder": "ترتیب مرتب‌سازی:", "LabelSortBy": "مرتب‌سازی بر اساس:", @@ -694,10 +684,8 @@ "MessageNoAvailablePlugins": "افزونه‌ای موجود نیست.", "MessageItemsAdded": "آیتم‌ها اضافه شدند.", "MessageItemSaved": "آیتم ذخیره شد.", - "MessageUnauthorizedUser": "در حال حاضر مجاز به دسترسی به سرور نیستید. لطفا برای اطلاعات بیشتر با مدیر سرور خود تماس بگیرید.", "MessageInvalidUser": "نام کاربری یا گذرواژه نامعتبر است. لطفا دوباره تلاش کنید.", "MessageInvalidForgotPasswordPin": "کد پین نامعتبر یا منقضی شده وارد شد. لطفا دوباره تلاش کنید.", - "MessageInstallPluginFromApp": "این افزونه باید از داخل برنامه‌ای که قصد استفاده از آن را دارید نصب شود.", "HeaderResetPassword": "بازنشانی گذرواژه", "PasswordResetConfirmation": "آیا واقعا تمایل به بازنشانی گذرواژه دارید؟", "PasswordResetComplete": "گذرواژه بازنشانی شد.", @@ -706,7 +694,6 @@ "PackageInstallCompleted": "{0} (نسخه {1})نصب به پایان رسید.", "PackageInstallCancelled": "{0} ( نسخه {1})نصب لغو شد.", "Overview": "بررسی اجمالی", - "OtherArtist": "هنرمند دیگر", "OriginalAirDateValue": "زمان پخش اصلی : {0}", "OptionWeekly": "هفتگی", "OptionWeekends": "آخر هفته ها", @@ -754,7 +741,6 @@ "RecentlyWatched": "اخیرا مشاهده شده", "Rate": "ارزیابی کن", "Raised": "مطرح شده", - "QueueAllFromHere": "همه را از اینجا در صف قرار بده", "Quality": "کیفیت", "Programs": "برنامه ها", "ProductionLocations": "محل تولید", @@ -794,7 +780,6 @@ "LabelSeasonNumber": "شماره فصل:", "ConfigureDateAdded": "تنظیم کنید که چگونه تاریخ اضافه شده در داشبورد سرور Jellyfin تحت تنظیمات کتابخانه تعیین می‌شود", "CinemaModeConfigurationHelp": "حالت سینما تجربه تئاتر گونه را مستقیم به اتاق نشیمن شما می‌آورد با قابلیت پخش تریلرها و پیش نمایش‌ها قبل از سایر ویژگی‌های اصلی.", - "LaunchWebAppOnStartup": "نمای وب هنگامی که سرور آغاز به کار می‌کند باز بشود", "NoSubtitles": "خالی", "NoSubtitleSearchResultsFound": "نتیجه‌ای یافت نشد.", "MessageNoPluginConfiguration": "این افزونه هیچ تنظیماتی برای پیکربندی ندارد.", @@ -813,7 +798,6 @@ "MusicArtist": "هنرمند موسیقی", "MusicAlbum": "آلبوم موسیقی", "Movie": "فیلم", - "AddItemToCollectionHelp": "افزودن موارد به مجموعه ها با جستجوی آنها و استفاده از منوهای راست کلیک یا ضربه بزنید تا آنها را به مجموعه اضافه کنید.", "AllowFfmpegThrottlingHelp": "هنگامی که یک transcode یا remux به اندازه کافی پیش از موقعیت پخش فعلی می شود ، روند را متوقف می کند تا منابع کمتری مصرف کند. این بیشتر مفید است در هنگام تماشای بدون به دنبال اغلب. اگر مسائل مربوط به پخش را تجربه کنید ، این را خاموش کنید.", "DefaultSubtitlesHelp": "زیرنویس ها بر اساس پرچم های پیش فرض و اجباری در ابرداده تعبیه شده بارگذاری می شوند. تنظیمات زبان در نظر گرفته می شوند زمانی که گزینه های متعدد در دسترس هستند.", "DeinterlaceMethodHelp": "روش deinterlacing برای استفاده از زمانی که transcoding محتوای هم آمیختن را انتخاب کنید.", @@ -934,7 +918,6 @@ "RefreshMetadata": "Refresh metadata", "RefreshQueued": "Refresh queued.", "ReleaseDate": "Release date", - "ReleaseGroup": "Release Group", "RememberMe": "Remember Me", "RemoveFromCollection": "Remove from collection", "RemoveFromPlaylist": "Remove from playlist", @@ -1139,8 +1122,6 @@ "LabelAlbumArtists": "Album artists:", "LabelAll": "All", "LabelAllowHWTranscoding": "Allow hardware transcoding", - "LabelAllowServerAutoRestart": "Allow the server to restart automatically to apply updates", - "LabelAllowServerAutoRestartHelp": "The server will only restart during idle periods when no users are active.", "LabelAllowedRemoteAddresses": "Remote IP address filter:", "LabelAllowedRemoteAddressesMode": "Remote IP address filter mode:", "LabelAppName": "App name", @@ -1199,7 +1180,6 @@ "LabelName": "Name:", "LabelChromecastVersion": "Chromecast Version", "LabelStable": "Stable", - "LabelNightly": "Nightly", "LabelNewName": "New name:", "LabelNewsCategories": "News categories:", "LabelNotificationEnabled": "Enable this notification", @@ -1270,7 +1250,6 @@ "HeaderAppearsOn": "Appears On", "HeaderAudioBooks": "Audio Books", "HeaderAudioSettings": "Audio Settings", - "HeaderAutomaticUpdates": "Automatic Updates", "HeaderBranding": "Branding", "HeaderConfigureRemoteAccess": "Configure Remote Access", "HeaderConfirmPluginInstallation": "Confirm Plugin Installation", @@ -1344,11 +1323,9 @@ "LabelXDlnaDocHelp": "Determines the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.", "LabelffmpegPathHelp": "The path to the ffmpeg application file, or folder containing ffmpeg.", "LanNetworksHelp": "Comma separated list of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. If left blank, only the server's subnet is considered to be on the local network.", - "LaunchWebAppOnStartupHelp": "Open the web client in your default web browser when the server initially starts. This will not occur when using the restart server function.", "LearnHowYouCanContribute": "Learn how you can contribute.", "LeaveBlankToNotSetAPassword": "You can leave this field blank to set no password.", "Like": "Like", - "LinksValue": "Links: {0}", "List": "List", "Live": "Live", "LiveBroadcasts": "Live broadcasts", @@ -1382,7 +1359,6 @@ "MediaInfoSampleRate": "Sample rate", "MediaInfoSize": "Size", "MediaInfoTimestamp": "Timestamp", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeEmbeddedImage": "Embedded Image", @@ -1440,7 +1416,6 @@ "ReplaceExistingImages": "Replace existing images", "ResumeAt": "Resume from {0}", "Rewind": "Rewind", - "RunAtStartup": "Run at startup", "Runtime": "Runtime", "Saturday": "Saturday", "Save": "Save", diff --git a/src/strings/fi.json b/src/strings/fi.json index ab385484f0..73f3c608ab 100644 --- a/src/strings/fi.json +++ b/src/strings/fi.json @@ -29,7 +29,6 @@ "LabelPrevious": "Edellinen", "LabelSaveLocalMetadata": "Tallenna kuvamateriaali mediakansioihin", "LabelSaveLocalMetadataHelp": "Kuvamateriaalin ja metadatan tallentaminen suoraan kansioihin missä niitä on helppo muuttaa.", - "LabelYourFirstName": "Etunimesi:", "LabelYoureDone": "Valmista!", "LibraryAccessHelp": "Valitse kirjastot, jotka haluat jakaa tämän käyttäjän kanssa. Järjestelmänvalvoja pystyy muokkaamaan kaikkia kansioita käyttäen metadatan hallintatyökalua.", "MaxParentalRatingHelp": "Suuremman luokituksen sisältö piilotetaan käyttäjältä.", @@ -67,7 +66,6 @@ "AllLibraries": "Kaikki kirjastot", "AllowOnTheFlySubtitleExtraction": "Salli tekstitysten purkaminen lennossa", "AccessRestrictedTryAgainLater": "Pääsy on toistaiseksi estetty. Yritä myöhemmin uudelleen.", - "AddItemToCollectionHelp": "Lisää nimikkeitä etsimällä niitä ja käyttämällä hiiren oikeaa nappia tai valikkoa lisätäksesi ne kokoelmaan.", "Aired": "Esityspäivä", "AllowHWTranscodingHelp": "Salli virittimen muuntaa bittivirtaa lennossa. Tämä voi vähentää muunnoksen tarvetta Jellyfin-palvelimella.", "AllowMediaConversion": "Salli median muunto", @@ -90,7 +88,6 @@ "Audio": "Ääni", "AuthProviderHelp": "Valitse todentamispalvelu, jota käytetään tämän käyttäjän salasanan todentamisessa.", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Automaattinen (perustuu kieliasetukseen)", "Backdrop": "Tausta", "Backdrops": "Taustat", "Banner": "Lippu", @@ -129,7 +126,6 @@ "ButtonHelp": "Apua", "ButtonHome": "Koti", "ButtonInfo": "Tiedot", - "ButtonLearnMore": "Lue lisää", "ButtonLibraryAccess": "Kiraston pääsy", "ButtonManualLogin": "Manuaalinen kirjautuminen", "ButtonMore": "Lisää", @@ -173,7 +169,6 @@ "ButtonTrailer": "Traileri", "ButtonUninstall": "Poista asennus", "ButtonUp": "Ylös", - "ButtonViewWebsite": "Näytä nettisivusto", "ButtonWebsite": "Nettisivusto", "CancelRecording": "Peruuta tallennus", "Categories": "Kategoriat", @@ -282,7 +277,6 @@ "EnableBackdropsHelp": "Näytä taustat tietyillä sivuilla selatessasi kirjastoa.", "EnableExternalVideoPlayersHelp": "Videota soitettaessa näytetään erillinen valikko.", "Depressed": "Painettu", - "CopyStreamURLError": "Osoitteen kopioidessa tapahtui virhe.", "ButtonSplit": "jaa", "AskAdminToCreateLibrary": "Pyydä järjestelmän ylläpitäjää luomaan kirjasto.", "EnableStreamLooping": "Looppaa suoralähetykset", @@ -384,7 +378,6 @@ "MessagePluginConfigurationRequiresLocalAccess": "Kirjaudu suoraan paikalliselle palvelimellesi muokataksesi tätä liitännäistä.", "MessagePleaseEnsureInternetMetadata": "Varmista, että metadatan lataus on käytössä.", "MessageNoServersAvailable": "Automaattisen palvelintunnistuksen avulla ei löydy palvelimia.", - "MessageUnauthorizedUser": "Sinulla ei ole lupaa käyttää palvelinta tällä hetkellä. Ota yhteyttä palvelimen järjestelmänvalvojaan saadaksesi lisätietoja.", "MessageInvalidForgotPasswordPin": "PIN-koodi on kelpaa tai vanhentunut. Yritä uudelleen.", "MessageImageTypeNotSelected": "Valitse kuvatyyppi pudotusvalikosta.", "MessageImageFileTypeAllowed": "Vain JPEG ja PNG tiedostomuotoja tuetaan.", @@ -425,8 +418,6 @@ "LabelAppName": "Sovelluksen nimi", "LabelAllowedRemoteAddressesMode": "Etä-IP-osoitesuodattimen tila:", "LabelAllowedRemoteAddresses": "Etä-IP-osoitesuodatin:", - "LabelAllowServerAutoRestartHelp": "Palvelin käynnistyy uudelleen vain hiljaisina aikoina, kun yksikään käyttäjä ei ole aktiivinen.", - "LabelAllowServerAutoRestart": "Salli palvelimen automaattinen uudelleenkäynnistys päivitysten asentamiseksi", "LabelAllowHWTranscoding": "Salli laitteistolla transkoodaus", "LabelAlbumArtMaxWidth": "Albumin kuvan maksimileveys:", "LabelAlbumArtMaxHeight": "Albumin kuvan maksimikorkeus:", @@ -551,7 +542,6 @@ "MediaInfoStreamTypeSubtitle": "Tekstitys", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Ohjelmisto", "MediaInfoTimestamp": "Aikaleima", "MediaInfoResolution": "Resoluutio", "MediaInfoSize": "Koko", @@ -575,7 +565,6 @@ "LiveBroadcasts": "Suorat lähetykset", "Live": "Suora", "List": "Lista", - "LinksValue": "Linkkejä: {0}", "LearnHowYouCanContribute": "Katso, miten voit auttaa.", "Large": "Suuri", "LabelffmpegPath": "FFmpeg polku:", @@ -693,7 +682,6 @@ "TabRecordings": "Tallennukset", "TabPlugins": "Liitännäiset", "TabPlaylists": "Soittolistat", - "TabPlaylist": "Soittolista", "TabPlayback": "Toistaminen", "TabNfoSettings": "NFO-asetukset", "TabNetworks": "Verkot", @@ -952,7 +940,6 @@ "GroupBySeries": "Ryhmitä sarjan perusteella", "Fullscreen": "Kokonäyttötila", "HeaderBooks": "Kirjat", - "HeaderAutomaticUpdates": "Automaattiset päivitykset", "HeaderAudioBooks": "Äänikirjat", "HeaderApiKeys": "API-avaimet", "HeaderApiKey": "API-avain", @@ -1001,7 +988,6 @@ "LabelDeviceDescription": "Laitteen kuvaus", "LabelDefaultScreen": "Oletusnäyttö:", "LabelDefaultUser": "Oletuskäyttäjä:", - "LabelDashboardTheme": "Palvelimen päänäkymän teema:", "LabelCustomCertificatePathHelp": "Polku PKCS # 12-tiedostoon, joka sisältää sertifikaatin ja yksityisen avaimen, jotta TLS-tuki voidaan sallia henkilökohtaiselle verkkotunnukselle.", "LabelCustomCertificatePath": "Mukautetun SSL-sertifikaatin polku:", "LabelContentType": "Sisältötyyppi:", @@ -1155,7 +1141,6 @@ "LabelTranscodingAudioCodec": "Audio codec:", "LabelSubtitleDownloaders": "Tekstitysten lataajat:", "LabelSpecialSeasonsDisplayName": "Erikoiskauden näyttönimi:", - "LabelSoundEffects": "Ääniefektit:", "LabelSortTitle": "Lajitteluotsikko:", "LabelSkipIfAudioTrackPresent": "Ohita, jos oletusääniraita vastaa latauskieltä", "LabelSkipBackLength": "Taaksepäin hyppäämisen pituus:", diff --git a/src/strings/fr-ca.json b/src/strings/fr-ca.json index 2e3b99b946..9229aabf56 100644 --- a/src/strings/fr-ca.json +++ b/src/strings/fr-ca.json @@ -36,7 +36,6 @@ "LabelPlaylist": "Liste de lecture :", "LabelPrevious": "Précédent", "LabelYear": "Année :", - "LabelYourFirstName": "Votre prénom :", "LabelYoureDone": "Vous avez terminé !", "Live": "En direct", "MessageItemsAdded": "Éléments ajoutés.", @@ -80,7 +79,6 @@ "Absolute": "Absolu", "AccessRestrictedTryAgainLater": "L'accès est actuellement restreint. Veuillez réessayer plus tard.", "Actor": "Acteur(trice)", - "AddItemToCollectionHelp": "Ajoutez des éléments à des collections en les recherchant et en utilisant leurs menus contextuels (clic droit ou appuyez longtemps).", "AddToPlayQueue": "Ajouter à la file d'attente", "AddedOnValue": "Ajouté le {0}", "AdditionalNotificationServices": "Visitez le catalogue d'extensions pour installer des services de notifications supplémentaires.", @@ -126,7 +124,6 @@ "Ascending": "Croissant", "Audio": "Audio", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (basé sur le réglage de la langue)", "Backdrop": "Arrière-plan", "Backdrops": "Arrière-plans", "Banner": "Bannière", @@ -168,7 +165,6 @@ "ButtonHelp": "Aide", "ButtonHome": "Accueil", "ButtonInfo": "Informations", - "ButtonLearnMore": "En savoir plus", "ButtonLibraryAccess": "Accès à la médiathèque", "ButtonManualLogin": "Connexion manuelle", "ButtonMore": "Plus", @@ -192,7 +188,6 @@ "DatePlayed": "Date écoutée", "DateAdded": "Date d'ajout", "CriticRating": "Évaluation critique", - "CopyStreamURLError": "Une erreur est survenue en essayant de copier l'URL.", "CopyStreamURLSuccess": "L'URL a été copié avec succès.", "CopyStreamURL": "Copier l'URL du stream", "ContinueWatching": "Continuer à visionner", @@ -209,7 +204,6 @@ "CancelSeries": "Annuler la série", "CancelRecording": "Annuler l'enregistrement", "ButtonWebsite": "Site web", - "ButtonViewWebsite": "Voir le site web", "ButtonUp": "Vers le haut", "ButtonUninstall": "Désinstaller", "ButtonTogglePlaylist": "Liste de lecture", diff --git a/src/strings/fr.json b/src/strings/fr.json index a4fbc078ef..6d8fb68ecf 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "L'accès est actuellement restreint. Veuillez réessayer plus tard.", "Actor": "Acteur(trice)", "Add": "Ajouter", - "AddItemToCollectionHelp": "Ajoutez des éléments à des collections en les recherchant et en utilisant leurs menus contextuels (clic droit ou appui long) pour les ajouter à une collection.", "AddToCollection": "Ajouter à la collection", "AddToPlayQueue": "Ajouter à la file d'attente", "AddToPlaylist": "Ajouter à la liste de lecture", @@ -36,7 +35,6 @@ "Ascending": "Croissant", "AspectRatio": "Format d'image", "AttributeNew": "Nouveau", - "AutoBasedOnLanguageSetting": "Auto (basé sur le réglage de la langue)", "Backdrop": "Arrière-plan", "Backdrops": "Arrière-plans", "Banner": "Bannière", @@ -79,7 +77,6 @@ "ButtonHelp": "Aide", "ButtonHome": "Accueil", "ButtonInfo": "Informations", - "ButtonLearnMore": "En savoir plus", "ButtonLibraryAccess": "Accès à la médiathèque", "ButtonManualLogin": "Connexion manuelle", "ButtonMore": "Plus", @@ -124,7 +121,6 @@ "ButtonTrailer": "Bande-annonce", "ButtonUninstall": "Désinstaller", "ButtonUp": "Haut", - "ButtonViewWebsite": "Voir le site", "ButtonWebsite": "Site Web", "CancelRecording": "Annuler l'enregistrement", "CancelSeries": "Annuler la série", @@ -282,7 +278,6 @@ "HeaderAppearsOn": "Apparait dans", "HeaderAudioBooks": "Livres audios", "HeaderAudioSettings": "Réglages audio", - "HeaderAutomaticUpdates": "Mises à jour automatiques", "HeaderBlockItemsWithNoRating": "Bloquer les éléments avec des informations de classification inconnues ou n'en disposant pas :", "HeaderBooks": "Livres", "HeaderBranding": "Slogan", @@ -498,8 +493,6 @@ "LabelAlbumArtists": "Artistes de l'album :", "LabelAll": "Tout", "LabelAllowHWTranscoding": "Autoriser le transcodage matériel", - "LabelAllowServerAutoRestart": "Autoriser le redémarrage automatique du serveur pour appliquer les mises à jour", - "LabelAllowServerAutoRestartHelp": "Le serveur ne redémarrera que pendant les périodes d'inactivité quand aucun utilisateur n'est connecté.", "LabelAllowedRemoteAddresses": "Filtre d'adresse IP distante :", "LabelAllowedRemoteAddressesMode": "Type de filtre des adresses IP distantes :", "LabelAppName": "Nom de l'application", @@ -536,7 +529,6 @@ "LabelCustomDeviceDisplayName": "Nom d'affichage :", "LabelCustomDeviceDisplayNameHelp": "Entrez un nom d'affichage personnalisé ou laissez vide pour utiliser le nom rapporté par l'appareil.", "LabelCustomRating": "Note personnalisée :", - "LabelDashboardTheme": "Thème du tableau de bord du serveur :", "LabelDateAdded": "Date d'ajout :", "LabelDateAddedBehavior": "Choix de la date d'ajout pour le nouveau contenu :", "LabelDateAddedBehaviorHelp": "Si une métadonnée est présente, elle sera toujours utilisée avant ces options.", @@ -749,7 +741,6 @@ "LabelServerHost": "Nom d'hôte :", "LabelServerHostHelp": "192.168.1.1:8096 ou https://monserveur.com", "LabelSimultaneousConnectionLimit": "Limite de flux simultanée :", - "LabelSkin": "Habillage :", "LabelSkipBackLength": "Durée des sauts en arrière :", "LabelSkipForwardLength": "Durée des sauts en avant :", "LabelSkipIfAudioTrackPresent": "Sauter si la piste audio correspond à la langue de téléchargement", @@ -761,7 +752,6 @@ "LabelSortBy": "Trier par :", "LabelSortOrder": "Ordre de tri :", "LabelSortTitle": "Titre de tri :", - "LabelSoundEffects": "Effets sonores :", "LabelSource": "Source :", "LabelSpecialSeasonsDisplayName": "Nom d'affichage de la saison spécial :", "LabelSportsCategories": "Catégories des sports :", @@ -815,7 +805,6 @@ "LabelXDlnaDoc": "Doc X-DLNA :", "LabelXDlnaDocHelp": "Détermine le contenu de l'élément X_DLNADOC dans l'espace de nom urn:schemas-dlna-org:device-1-0.", "LabelYear": "Année :", - "LabelYourFirstName": "Votre prénom :", "LabelYoureDone": "Vous avez terminé !", "LabelZipCode": "Code postal :", "LabelffmpegPath": "Chemin vers FFmpeg :", @@ -826,7 +815,6 @@ "LearnHowYouCanContribute": "Voir comment vous pouvez contribuer.", "LibraryAccessHelp": "Sélectionnez les médiathèques à partager avec cet utilisateur. Les administrateurs pourront modifier tous les dossiers en utilisant le gestionnaire de métadonnées.", "Like": "J'aime", - "LinksValue": "Liens: {0}", "List": "Liste", "Live": "En direct", "LiveBroadcasts": "Diffusions en direct", @@ -882,7 +870,6 @@ "MessageFileReadError": "Une erreur est survenue lors de la lecture du fichier. Veuillez réessayer.", "MessageForgotPasswordFileCreated": "Le fichier suivant a été créé sur votre serveur et contient les instructions sur la procédure à suivre :", "MessageForgotPasswordInNetworkRequired": "Veuillez réessayer à partir de votre réseau local pour démarrer la procédure de réinitialisation du mot de passe.", - "MessageInstallPluginFromApp": "Cette extension doit-être installée depuis l'application dans laquelle vous voulez l'utiliser.", "MessageInvalidForgotPasswordPin": "Le code PIN saisi est invalide ou a expiré. Veuillez réessayer.", "MessageInvalidUser": "Nom d'utilisateur ou mot de passe incorrect. Réessayez.", "MessageItemSaved": "Élément enregistré.", @@ -1113,7 +1100,6 @@ "ProductionLocations": "Sites de production", "Programs": "Programmes", "Quality": "Qualité", - "QueueAllFromHere": "Tout mettre en file d'attente à partir d'ici", "Raised": "Élevé", "Rate": "Débit", "RecentlyWatched": "Lu récemment", @@ -1144,7 +1130,6 @@ "ReplaceExistingImages": "Remplacer les images existantes", "ResumeAt": "Reprendre à {0}", "Rewind": "Rembobiner", - "RunAtStartup": "Exécuter au démarrage", "Runtime": "Durée", "Saturday": "Samedi", "Save": "Sauvegarder", @@ -1229,7 +1214,6 @@ "TabParentalControl": "Contrôle Parental", "TabPassword": "Mot de passe", "TabPlayback": "Lecture", - "TabPlaylist": "Liste de lecture", "TabPlaylists": "Listes de lecture", "TabProfile": "Profil", "TabProfiles": "Profils", @@ -1294,7 +1278,6 @@ "ValueTimeLimitSingleHour": "Limite de temps : 1 heure", "ValueVideoCodec": "Codec Vidéo : {0}", "Vertical": "Verticale", - "VideoRange": "Gamme vidéo", "ViewAlbum": "Voir l'album", "ViewPlaybackInfo": "Voir les informations de lecture", "Watched": "Lu", @@ -1380,7 +1363,6 @@ "DashboardVersionNumber": "Version : {0}", "DashboardServerName": "Serveur : {0}", "LabelWeb": "Web :", - "MediaInfoSoftware": "Logiciel", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Données", "MediaInfoStreamTypeSubtitle": "Sous-titres", @@ -1391,8 +1373,6 @@ "LabelUserLoginAttemptsBeforeLockout": "Tentatives de connexion échouées avant que l'utilisateur ne soit verrouillé :", "DashboardOperatingSystem": "Système d'Exploitation: {0}", "DashboardArchitecture": "Architecture : {0}", - "LaunchWebAppOnStartup": "Démarrer l'interface web dans mon navigateur quand le serveur est démarré", - "LaunchWebAppOnStartupHelp": "Ouvrir l'application dans votre navigateur internet quand le serveur est démarré pour la première fois. Cela ne se produira pas quand le serveur redémarre.", "MediaInfoStreamTypeEmbeddedImage": "Miniature", "MessageNoCollectionsAvailable": "Les collections vous permettent de profiter de groupes personnalisés de Films, Séries et d'Albums. Cliquer sur le bouton + pour commencer à créer des collections.", "MessageNoServersAvailable": "Aucun serveur n'a été trouvé en utilisant la recherche automatique de serveur.", @@ -1450,7 +1430,6 @@ "LabelPlayerDimensions": "Dimension du lecteur :", "LabelDroppedFrames": "Images perdues :", "LabelCorruptedFrames": "Images corrompues :", - "CopyStreamURLError": "Une erreur est survenue lors de la copie de l'URL.", "AskAdminToCreateLibrary": "Demander à un administrateur de créer une médiathèque.", "AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment en avant de la position de lecture, le processus se mettra en pause afin d’économiser des ressources. Plus utile lors d’une lecture continue. À désactiver en cas de problèmes de lecture.", "AllowFfmpegThrottling": "Adapter la vitesse du transcodage", @@ -1461,9 +1440,7 @@ "ClientSettings": "Paramètres Client", "Track": "Piste", "Season": "Saison", - "ReleaseGroup": "Groupe de Parution", "Person": "Personne", - "OtherArtist": "Autre Artiste", "Movie": "Film", "Episode": "Épisode", "BoxSet": "Coffret", @@ -1489,7 +1466,6 @@ "LabelLibraryPageSize": "Taille de la page de la médiathèque :", "LabelLibraryPageSizeHelp": "Définit la quantité d'éléments à afficher sur une page de médiathèque. Définir à 0 afin de désactiver la pagination.", "UnsupportedPlayback": "Jellyfin ne peut pas décoder du contenu protégé par un système de gestion des droits numériques, mais une tentative de lecture sera effectuée sur tout le contenu, y compris les titres protégés. Certains fichiers peuvent apparaître complètement noir, du fait de protections ou de fonctionnalités non supportées, comme les titres interactifs.", - "MessageUnauthorizedUser": "Vous n'êtes pas autorisé à accéder au serveur pour le moment. Veuillez contacter l'administrateur de votre serveur pour plus d'informations.", "ButtonTogglePlaylist": "Liste de lecture", "ButtonToggleContextMenu": "Plus", "Filter": "Filtre", @@ -1507,7 +1483,6 @@ "SaveChanges": "Enregistrer les modifications", "LabelRequireHttpsHelp": "Si activé, le serveur va automatiquement rediriger toutes les requêtes en HTTP vers HTTPS. Cette option n'a aucun effet si le serveur n'écoute pas HTTPS.", "LabelRequireHttps": "Nécessite HTTPS", - "LabelNightly": "De nuit", "LabelStable": "Stable", "EnableDetailsBanner": "Bannière des détails", "EnableDetailsBannerHelp": "Affichez une image de bannière en haut de la page de détails de l'article.", diff --git a/src/strings/gsw.json b/src/strings/gsw.json index 231cb131e5..bf9852e512 100644 --- a/src/strings/gsw.json +++ b/src/strings/gsw.json @@ -20,7 +20,6 @@ "FolderTypeTvShows": "TV", "Friday": "Friitig", "HeaderAddUser": "Erstell en User", - "HeaderAutomaticUpdates": "Automatischi Updates", "HeaderDeviceAccess": "Grät Zuegriff", "HeaderEasyPinCode": "Eifache Pin Code", "HeaderFrequentlyPlayed": "Vell gspellt", @@ -49,7 +48,6 @@ "LabelSaveLocalMetadataHelp": "Wennd Bilder und Metadate direkt i d'Medieordner speicherisch, chasch sie eifach weder finde und au bearbeite.", "LabelSelectUsers": "Wähl User:", "LabelTimeLimitHours": "Ziitlimit (h):", - "LabelYourFirstName": "Din Vorname:", "LabelYoureDone": "Du besch fertig!", "LibraryAccessHelp": "Wähl en Medieordner us, um de mit dem User z'teile. Administratore werded immer d'Möglichkeit ha alli Verzeichnis mitm Metadate Manager z'bearbeite.", "MaxParentalRatingHelp": "Date mit enere höhere Kindersicherig werded vo dem User versteckt.", @@ -112,7 +110,6 @@ "TabNetworks": "Studios", "TabNotifications": "Mitteilige", "TabPassword": "Passwort", - "TabPlaylist": "Playliste", "TabProfile": "Profil", "TabProfiles": "Profil", "TabShows": "Serie", diff --git a/src/strings/he.json b/src/strings/he.json index 29824e4c3e..3db7c86f26 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -88,7 +88,6 @@ "HeaderAddToPlaylist": "הוסף לרשימת ניגון", "HeaderAddUser": "הוסף משתמש", "HeaderAdditionalParts": "חלקים נוספים", - "HeaderAutomaticUpdates": "עידכונים אוטומטים", "HeaderCancelRecording": "ביטול הקלטה", "HeaderCancelSeries": "בטל סדרה", "HeaderCastCrew": "שחקנים וצוות", @@ -151,8 +150,6 @@ "LabelAirsBeforeSeason": "באוויר לפני העונה:", "LabelAlbum": "אלבום:", "LabelAlbumArtists": "אלבום אומנים:", - "LabelAllowServerAutoRestart": "אפשר לשרת להתחיל אוטומטית כדי לאפשר את העידכונים", - "LabelAllowServerAutoRestartHelp": "השרת יתחיל מחדש רק כשאר אין משתמשים פעילים", "LabelArtists": "אומנים:", "LabelArtistsHelp": "הפרד מרובים באמצעות;", "LabelAudioLanguagePreference": "שפת קול מועדפת:", @@ -266,7 +263,6 @@ "LabelUser": "משתמש:", "LabelUserLibrary": "ספריית משתמש:", "LabelYear": "שנה:", - "LabelYourFirstName": "שמך הפרטי:", "LabelYoureDone": "סיימת!", "LibraryAccessHelp": "בחר את ספריות המדיה אשר ישותפו עם המשתמש. מנהלים יוכלו לערות את כל התיקיות באמצעות עורך המידע.", "Like": "אוהב", @@ -393,7 +389,6 @@ "Premieres": "בכורות", "Producer": "במאי", "ProductionLocations": "מיקומי ייצור", - "QueueAllFromHere": "הוסף הכל מכאן לתור", "RecentlyWatched": "נצפה לאחרונה", "Record": "הקלט", "RecordSeries": "הקלט סדרה", @@ -464,7 +459,6 @@ "TabNetworks": "רשתות", "TabNotifications": "התראות", "TabPassword": "סיסמא", - "TabPlaylist": "רשימת נגינה", "TabProfile": "פרופיל", "TabProfiles": "פרופילים", "TabRecordings": "הקלטות", @@ -548,7 +542,6 @@ "AllowOnTheFlySubtitleExtraction": "אפשר חילוץ כתוביות בזמן אמת", "AllowHWTranscodingHelp": "אפשר למלקט לקודד הזרמות בזמן אמת. זה עשוי לעזור בהפחתת הקידוד שנעשה ע\"י השרת.", "AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX)", - "AddItemToCollectionHelp": "הוסף חפצים לאוסף על ידי חיפושם ושימוש בתפריט הלחצן הימני או לחצן התפריט כדי להוסיפם לאוסף.", "Songs": "שירים", "Shows": "סדרות", "DownloadsValue": "{0} הורדות", @@ -578,7 +571,6 @@ "ButtonParentalControl": "בקרת הורים", "ButtonNetwork": "רשת", "ButtonMore": "עוד", - "ButtonLearnMore": "למד עוד", "ButtonInfo": "מידע", "ButtonHome": "בית", "ButtonHelp": "עזרה", @@ -695,13 +687,11 @@ "DirectPlaying": "ניגון ישיר", "DetectingDevices": "מזהה מכשירים", "DefaultMetadataLangaugeDescription": "אלו הגדרות ברירת המחדל שלך וניתן להתאים אותן לכל ספרייה בנפרד.", - "CopyStreamURLError": "אירעה שגיאה במהלך העתקת הקישור.", "CopyStreamURLSuccess": "הקישור הועתק בהצלחה.", "CopyStreamURL": "העתק קישור זרם", "Connect": "התחבר", "ConfirmEndPlayerSession": "‫האם לכבות את Jellyfin על {0}?", "CommunityRating": "דירוג קהילה", - "ButtonViewWebsite": "צפה באתר האינטרנט", "ButtonWebsite": "אתר אינטרנט", "ButtonUp": "למעלה", "ButtonSubmit": "שלח", @@ -726,7 +716,6 @@ "ButtonDownload": "הורדה", "ButtonDown": "למטה", "ButtonChangeServer": "החלף שרת", - "AutoBasedOnLanguageSetting": "אוטומטי (לפי הגדרות שפה)", "ButtonBack": "חזרה", "OptionBanner": "באנר", "ButtonAudioTracks": "רצועות שמע", @@ -739,7 +728,6 @@ "BoxRear": "מארז (מאחור)", "BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0}במדריך מתן שמות לספרים{1}.", "Desktop": "שולחן עבודה", - "MessageUnauthorizedUser": "אין לך גישה לשרת ברגע זה. אנא צור קשר עם מנהל השרת למידע נוסף.", "MessageDeleteTaskTrigger": "האם אתה בטוח שברצונך למחוק את מפעיל המשימה הזה?", "LastSeen": "נראה לאחרונה ב-{0}", "PersonRole": "כ-{0}", @@ -767,7 +755,6 @@ "Raised": "מורם", "LabelSpecialSeasonsDisplayName": "שם תצוגת \"עונה מיוחדת\":", "LabelSource": "מקור:", - "LabelSoundEffects": "אפקטי סאונד:", "ButtonTogglePlaylist": "רשימת ניגון", "ButtonToggleContextMenu": "עוד", "ButtonSyncPlay": "SyncPlay", diff --git a/src/strings/hi-in.json b/src/strings/hi-in.json index 7e9bb01563..e7e45dfa20 100644 --- a/src/strings/hi-in.json +++ b/src/strings/hi-in.json @@ -5,7 +5,6 @@ "LabelFinish": "समाप्त", "LabelNext": "अगला", "LabelPrevious": "पिछला", - "LabelYourFirstName": "आपका प्रथम नामः", "LabelYoureDone": "आपने पूरा कर लिया है!", "MoreUsersCanBeAddedLater": "अधिक उपयोगकर्ताओं को बाद में डैशबोर्ड के अंतर्गत जोड़ा जा सकता है।", "TellUsAboutYourself": "हमें अपने बारे में बताएं", @@ -43,7 +42,6 @@ "ButtonMore": "अधिक", "ButtonManualLogin": "मैनुअल लॉगिन", "ButtonLibraryAccess": "पुस्तकालय का उपयोग", - "ButtonLearnMore": "और अधिक जानें", "ButtonInfo": "जानकारी", "ButtonHome": "घर", "ButtonHelp": "मदद", @@ -86,7 +84,6 @@ "Banner": "झंडा", "Backdrops": "पृष्ठभूमि", "Backdrop": "पृष्ठभूमि", - "AutoBasedOnLanguageSetting": "ऑटो (भाषा सेटिंग के आधार पर)", "Auto": "ऑटो", "AuthProviderHelp": "इस उपयोगकर्ता के पासवर्ड को प्रमाणित करने के लिए एक प्रमाणीकरण प्रदाता का उपयोग करें।", "Audio": "नया", @@ -112,7 +109,6 @@ "AlbumArtist": "चित्राधार कलाकार", "AllowOnTheFlySubtitleExtraction": "मक्खी पर उपशीर्षक निष्कर्षण की अनुमति दें", "Album": "एल्बम", - "AddItemToCollectionHelp": "उनके लिए खोज करके संग्रह में आइटम जोड़ें और उन्हें संग्रह में जोड़ने के लिए उनके राइट-क्लिक या टैप मेनू का उपयोग करें।", "ButtonSyncPlay": "SyncPlay", "MessageBrowsePluginCatalog": "उपलब्ध प्लगिन्स देखने के लिए हमारे कैटलॉग को ब्राउज़ करें।", "Browse": "ब्राउज़", diff --git a/src/strings/hr.json b/src/strings/hr.json index ed534a8d95..622df7775e 100644 --- a/src/strings/hr.json +++ b/src/strings/hr.json @@ -1,7 +1,6 @@ { "Actor": "Glumac", "Add": "Dodaj", - "AddItemToCollectionHelp": "Pretraživanjem stavaka i korištenjem desnog klika ili izbornika dodavanja u kolekciju možete ih dodati u kolekciju.", "AddToCollection": "Dodaj u kolekciju", "AddToPlaylist": "Dodaj u popis", "AdditionalNotificationServices": "Pretražite katalog dodataka kako bi instalirali dodatne servise za obavijesti.", @@ -44,7 +43,6 @@ "ButtonGuide": "Vodič", "ButtonHelp": "Pomoć", "ButtonHome": "Početna", - "ButtonLearnMore": "Nauči još", "ButtonLibraryAccess": "Pristup biblioteci", "ButtonManualLogin": "Ručna prijava", "ButtonMore": "Više", @@ -88,7 +86,6 @@ "ButtonTrailer": "Kratki video", "ButtonUninstall": "Ukloni", "ButtonUp": "Gore", - "ButtonViewWebsite": "Posjeti web stranice", "ButtonWebsite": "Web stranica", "CancelRecording": "Prekini snimanje", "CancelSeries": "Odustani od serije", @@ -176,7 +173,6 @@ "HeaderApiKeysHelp": "Vanjske aplikacije moraju imati API ključ kako bi komunicirale s Jellyfin Serverom. Ključevi se izdaju prijavom s Jellyfin računom ili ručnim odobravanjem zahtjeva ključa.", "HeaderApp": "Aplikacija", "HeaderAudioSettings": "Postavke zvuka", - "HeaderAutomaticUpdates": "Automatske nadogradnje", "HeaderBooks": "Knjige", "HeaderBranding": "Brendiranje", "HeaderCancelRecording": "Prekini snimanje", @@ -346,8 +342,6 @@ "LabelAlbumArtists": "Izvođači albuma:", "LabelAll": "Sve", "LabelAllowHWTranscoding": "Dopusti hardversko konvertiranje", - "LabelAllowServerAutoRestart": "Dopusti serveru da se automatski resetira kako bi proveo nadogradnje", - "LabelAllowServerAutoRestartHelp": "Server će se resetirati samo dok je u statusu mirovanja kada nema aktivnih korisnika.", "LabelAppName": "Ime aplikacije", "LabelAppNameExample": "Primjer: Sickbeard, Sonarr", "LabelArtists": "Izvođači:", @@ -596,7 +590,6 @@ "LabelVersionInstalled": "{0} instaliran", "LabelXDlnaCapHelp": "Određuje sadržaj X_DLNACAP elementa u urn:shemas-dlna-org:device-1-0 namespace.", "LabelXDlnaDocHelp": "Određuje sadržaj X_DLNADOC elementa u urn:schemas-dlna-org:device-1-0 namespace.", - "LabelYourFirstName": "Ime:", "LabelYoureDone": "Završeno!", "LabelZipCode": "Poštanski broj:", "LabelffmpegPath": "FFmpeg putanja:", @@ -653,7 +646,6 @@ "MessageFileReadError": "Prilikom učitavanja datoteke desila se greška. Pokušajte ponovno.", "MessageForgotPasswordFileCreated": "Sljedeća datoteka je stvorena na vašem poslužitelju i sadrži upute o tome kako postupiti:", "MessageForgotPasswordInNetworkRequired": "Molim pokušajte ponovno unutar kućne mreže za pokretanje postupka za poništavanje zaporke.", - "MessageInstallPluginFromApp": "Ovaj dodatak mora biti instaliran unutar aplikacije u kojoj ga namjeravate koristiti.", "MessageInvalidForgotPasswordPin": "Upisan je neispravan ili zastarjele pin. Molim, pokušajte ponovno.", "MessageInvalidUser": "Pogrešno korisničko ime ili lozinka. Molim, pokušajte ponovo.", "MessageItemSaved": "Stavka je snimljena.", @@ -846,7 +838,6 @@ "Premieres": "Premijere", "Producer": "Producent", "ProductionLocations": "Lokacije proizvodnje", - "QueueAllFromHere": "Stavi u red čekanja sve odavde", "RecentlyWatched": "Nedavno pogledano", "RecommendationBecauseYouLike": "Zato što volite {0}", "RecommendationBecauseYouWatched": "Zato što ste gledali {0}", @@ -934,7 +925,6 @@ "TabParentalControl": "Roditeljska kontrola", "TabPassword": "Lozinka", "TabPlayback": "Reprodukcija", - "TabPlaylist": "Lista izvođenja", "TabPlaylists": "Popisi", "TabPlugins": "Dodaci", "TabProfile": "Profil", @@ -1048,7 +1038,6 @@ "Album": "Album", "AddToPlayQueue": "Dodaj u red izvođenja", "Banner": "Zaglavlje", - "AutoBasedOnLanguageSetting": "Automatski (prema jezičnim postavkama)", "AspectRatio": "Omjer", "Ascending": "Uzlazno", "Art": "Grafike", @@ -1069,7 +1058,6 @@ "Box": "Kutija", "AskAdminToCreateLibrary": "Traži administratora da kreira biblioteku.", "PictureInPicture": "Slika u slici", - "OtherArtist": "Ostali izvođači", "OptionThumb": "Sličica", "OptionProtocolHttp": "HTTP", "OptionProfileVideo": "Video", @@ -1109,7 +1097,6 @@ "MediaInfoStreamTypeSubtitle": "Prijevod", "MediaInfoStreamTypeData": "Podaci", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Softver", "Logo": "Logo", "List": "Lista", "LabelYear": "Godina:", @@ -1133,7 +1120,6 @@ "MillisecondsUnit": "ms", "LabelSubtitles": "Prijevodi", "LabelStatus": "Status:", - "LabelSoundEffects": "Zvučni efekti:", "LabelSortOrder": "Redoslijed sortiranja:", "LabelSortBy": "Sortiranje po:", "LabelSize": "Veličina:", @@ -1229,7 +1215,6 @@ "DatePlayed": "Datum reprodukcije", "DateAdded": "Datum dodavanja", "CriticRating": "Rejting kritičara", - "CopyStreamURLError": "Došlo je do greške prilikom kopiranja URLa.", "ConfirmEndPlayerSession": "Da li želite ugasiti Jellyfin na {0}?", "CommunityRating": "Rejting zajednice", "Browse": "Pretraži", diff --git a/src/strings/hu.json b/src/strings/hu.json index b097531d06..a25e02882c 100644 --- a/src/strings/hu.json +++ b/src/strings/hu.json @@ -12,7 +12,6 @@ "Ascending": "Növekvő", "AttributeNew": "Új", "Audio": "Audió", - "AutoBasedOnLanguageSetting": "Automatikus (a nyelvi beállítások alapján)", "BirthDateValue": "Született: {0}", "BirthPlaceValue": "Születési hely: {0}", "Books": "Könyvek", @@ -131,7 +130,6 @@ "HeaderAlbums": "Albumok", "HeaderAudioBooks": "Hangos könyvek", "HeaderAudioSettings": "Audió Beállítások", - "HeaderAutomaticUpdates": "Automatikus frissitések", "HeaderCastAndCrew": "Szereplők és Stáb", "HeaderCastCrew": "Szereplők és Stáb", "HeaderChannels": "Csatornák", @@ -228,8 +226,6 @@ "Label3DFormat": "3D formátum:", "LabelAlbumArtists": "Album előadók:", "LabelAll": "Összes", - "LabelAllowServerAutoRestart": "Automatikus újraindítás engedélyezése a szervernek a frissítések telepítéséhez", - "LabelAllowServerAutoRestartHelp": "A szerver csak akkor indul újra ha nincs felhasználói tevékenység.", "LabelArtists": "Előadók:", "LabelAudio": "Audió", "LabelAudioLanguagePreference": "Audió nyelvének beállítása:", @@ -244,7 +240,6 @@ "LabelCustomDeviceDisplayName": "Megjelenítendő név:", "LabelCustomDeviceDisplayNameHelp": "Adj meg egy egyedi nevet, vagy hagyd üresen a készülék által elküldött név használatához.", "LabelCustomRating": "Egyéni értékelés:", - "LabelDashboardTheme": "Szerver vezérlőpult kinézete:", "LabelDateAdded": "Hozzáadva:", "LabelDateTimeLocale": "Dátum és idő formátum:", "LabelDay": "Nap:", @@ -337,7 +332,6 @@ "LabelVersionInstalled": "{0} telepítve", "LabelVideo": "Videó", "LabelYear": "Év:", - "LabelYourFirstName": "Keresztneved:", "LabelYoureDone": "Készen vagy!", "LatestFromLibrary": "Nemrég hozzáadott {0}", "Like": "Tettszik", @@ -529,7 +523,6 @@ "TabParentalControl": "Szülői Felügyelet", "TabPassword": "Jelszó", "TabPlayback": "Lejátszás", - "TabPlaylist": "Lejátszási lista", "TabPlaylists": "Lejátszási listák", "TabPlugins": "Bővítmények", "TabProfile": "Profil", @@ -605,7 +598,6 @@ "Blacklist": "Feketelista", "BookLibraryHelp": "Lehetőség van audió és hangoskönyvek visszajátszására. Nézd meg a {0} könyvelnevezési útmutatót {1}.", "MessageBrowsePluginCatalog": "Böngéssz a Bővítmény katalógusunkban a rendelkezésre álló bővítmények megtekintéséhez.", - "AddItemToCollectionHelp": "Adj elemeket a gyűjteményekhez, ehhez keresed meg őket, majd kattints jobb egérgombbal, vagy kattints a menüre és add hozzá a gyűjteményhez.", "AllowedRemoteAddressesHelp": "Vesszővel válaszd el az IP-címek vagy IP / netmask címek listáját annak a hálózatnak amelyből távolról csatlakozhatnak. Ha üresen marad, az összes távoli cím megengedett.", "BoxRear": "Box (hátsó)", "ButtonArrowLeft": "Bal", @@ -614,14 +606,12 @@ "ButtonEditOtherUserPreferences": "A felhasználó profiljának, képének és személyes beállításainak szerkesztése.", "ButtonFullscreen": "Teljes képernyő", "ButtonInfo": "Info", - "ButtonLearnMore": "Tudj meg többet", "ButtonNetwork": "Hálózat", "ButtonOk": "Ok", "ButtonRevoke": "Visszavon", "ButtonSelectView": "Válassz nézetet", "ButtonStart": "Start", "ButtonUp": "Fel", - "ButtonViewWebsite": "Webhely megtekintése", "CancelRecording": "Felvétel törlése", "CancelSeries": "Sorozat törlése", "Categories": "Kategóriák", @@ -962,11 +952,9 @@ "LabelMinResumePercentage": "Minimum folytatás százalékban:", "LabelMinScreenshotDownloadWidth": "Minimális képernyőkép letöltési szélesség:", "LabelPreferredSubtitleLanguage": "Alapértelmezett feliratnyelv:", - "LabelSkin": "Kinézet:", "LabelSkipBackLength": "Ugrás vissza hossza:", "LabelSkipForwardLength": "Ugrás előre hossza:", "LabelSkipIfGraphicalSubsPresent": "Kihagyás, ha a videó már tartalmaz beágyazott feliratokat", - "LabelSoundEffects": "Hanghatások:", "LabelSportsCategories": "Sport kategóriák:", "LabelStartWhenPossible": "Elindul, amint lehetséges:", "LabelStopWhenPossible": "Leáll, amint lehetséges:", @@ -993,7 +981,6 @@ "LearnHowYouCanContribute": "Ismerd meg, hogyan járulhatsz hozzá.", "LeaveBlankToNotSetAPassword": "Ha nem szeretnél jelszót beállítani, hagyd ezt a mezőt üresen.", "LibraryAccessHelp": "Válaszd ki azokat a könyvtárakat amelyeket megosztani kívánsz ezzel a felhasználóval. A rendszergazdák a Metaadat Manager segítségével szerkeszthetik az összes mappát.", - "LinksValue": "Linkek: {0}", "List": "Lista", "LiveTV": "Élő TV", "Logo": "Logo", @@ -1024,7 +1011,6 @@ "MessageEnablingOptionLongerScans": "Ennek az opciónak a bekapcsolása jelentősen hosszabb könyvtárbeolvasást eredményezhet.", "MessageImageFileTypeAllowed": "Csak JPEG és PNG fájlok támogatottak.", "MessageImageTypeNotSelected": "Kérlek válaszd ki a kép típusát a legördülő menüből.", - "MessageInstallPluginFromApp": "Ezt a bővítményt azon alkalmazásból kell telepíteni, amelyben használni kívánod.", "MessageInvalidForgotPasswordPin": "Érvénytelen vagy lejárt PIN kódot írtál be. Kérlek próbáld újra.", "MessageInvalidUser": "Érvénytelen felhasználónév vagy jelszó. Kérlek próbáld újra.", "MessageItemSaved": "Elem mentve.", @@ -1190,7 +1176,6 @@ "RemoveFromPlaylist": "Lejátszási listáról eltávolítani", "RepeatEpisodes": "Epizódok ismétlése", "ResumeAt": "Folytatás: {0}", - "RunAtStartup": "Futtassa indításkor", "SaveSubtitlesIntoMediaFolders": "Mentse a feliratokat a média mappákba", "SaveSubtitlesIntoMediaFoldersHelp": "A feliratok tárolása a videofájlok mellett lehetővé teszi, hogy könnyebben kezelhetők legyenek.", "Schedule": "Ütemezés", @@ -1252,7 +1237,6 @@ "ValueTimeLimitMultiHour": "időlimit: {0} óra", "ValueTimeLimitSingleHour": "Időlimit: 1 óra", "Vertical": "Függőleges", - "VideoRange": "Videó tartomány", "ViewAlbum": "Album megtekintése", "Whitelist": "Fehérlista", "WizardCompleted": "Ez most minden amire szükség volt. A Jellyfin megkezdte a médiakönyvtáraddal kapcsolatos információk gyűjtését. Nézz meg néhány alkalmazásunkat, majd kattints a Befejezés gombra a Vezérlőpult megtekintéséhez.", @@ -1270,7 +1254,6 @@ "DashboardVersionNumber": "Verzió: {0}", "DashboardServerName": "Szerver: {0}", "LabelWeb": "Web:", - "MediaInfoSoftware": "Szoftver", "MediaInfoStreamTypeAudio": "Audió", "MediaInfoStreamTypeSubtitle": "Felirat", "MediaInfoStreamTypeVideo": "Videó", @@ -1278,7 +1261,6 @@ "LabelUserLoginAttemptsBeforeLockout": "Sikertelen bejelentkezési kísérletek a felhasználó zárolása előtt:", "DashboardOperatingSystem": "Operációs rendszer: {0}", "DashboardArchitecture": "Platform: {0}", - "LaunchWebAppOnStartup": "Indítsa el a webes felületet a szerver indításakor", "MessageNoCollectionsAvailable": "A gyűjtemények lehetővé teszik Filmek, Sorozatok és Albumok egyéni csoportosítását. A gyűjtemények létrehozásához kattints a + gombra.", "MessageNoServersAvailable": "Az automatikus kiszolgálókeresés nem talált szervert.", "OptionLoginAttemptsBeforeLockout": "Meghatározza, hogy hány érvénytelen bejelentkezési kísérlet történhet zárolás előtt.", @@ -1374,7 +1356,6 @@ "HeaderFavoritePeople": "Kedvenc emberek", "HeaderApp": "Alkalmazás", "GroupVersions": "Verziók csoportosítása", - "CopyStreamURLError": "Hiba történt az URL másolása közben.", "OptionSubstring": "Szövegrészlet", "ButtonSplit": "Szétvág", "Absolute": "Abszolút", @@ -1382,7 +1363,6 @@ "SubtitleOffset": "Felirat eltolása", "SeriesDisplayOrderHelp": "Rakd sorba az epizódokat az adásba kerülésük dátuma, a DVD sorszám, vagy az abszolút számozás szerint.", "SelectAdminUsername": "Kérjük válassz felhasználónevet az adminisztrátor fiók számára.", - "QueueAllFromHere": "Az összes sorba állítása innen", "OptionThumbCard": "Miniatűr kártya", "OptionThumb": "Miniatűr", "OptionSpecialEpisode": "Különkiadások", @@ -1408,7 +1388,6 @@ "MetadataSettingChangeHelp": "A metaadat beállítások módosítása az ezután újonnan hozzáadott médiát fogja befolyásolni. A már meglévő tartalom frissítéséhez nyisd meg a részletek képernyőt, és kattints a frissítésre, vagy végezz tömeges frissítést a Metaadat Managerben.", "MessageConfirmDeleteGuideProvider": "Biztosan törölni szeretnéd ezt a műsorújság szolgáltatót?", "MessageConfirmAppExit": "Ki szeretnél lépni?", - "LaunchWebAppOnStartupHelp": "A web kliens indítása az alapértelmezett böngészőben a szerver indítása után. A kliens nem fog elindulni a szerver újraindítása után.", "LabelVideoResolution": "Videó felbontás:", "LabelVideoCodec": "Videó kodek:", "LabelVideoBitrate": "Videó bitráta:", @@ -1473,7 +1452,6 @@ "Track": "Szám", "Season": "Évad", "Person": "Személy", - "OtherArtist": "Más előadók", "Movie": "Film", "Episode": "Epizód", "ClientSettings": "Kliens beállítások", @@ -1488,15 +1466,12 @@ "UnsupportedPlayback": "Jellyfin nem tud DRM-titkosított tartalmak dekriptálására, ettől függetlenül a lejátszással mindig megpróbálkozik. Néhány fájl emiatt teljesen fekete képernyőt ad, amely vagy a titkosítás miatt van, vagy nem olyan nem támogatott tartalmak miatt, mint az interaktív címek.", "YadifBob": "YADIF Bob", "Yadif": "YADIF", - "ReleaseGroup": "Kiadócsoport", - "MessageUnauthorizedUser": "Jelenleg nincs jogosultságod a szerverhez való hozzáféréshez. Kérjük, lépj kapcsolatba az adminisztrátorral további információkért!", "ButtonTogglePlaylist": "Lejátszási listák", "ButtonToggleContextMenu": "Továbbiak", "Filter": "Szűrés", "New": "Új", "HeaderFavoritePlaylists": "Kedvenc lejátszási listák", "ApiKeysCaption": "A jelenleg engedélyezett API kulcsok listája", - "LabelNightly": "Éjszakai", "LabelStable": "Stabil", "LabelChromecastVersion": "Chromecast verzió", "LabelEnableHttpsHelp": "Figyelés a megadott HTTPS porton. Érvényes tanúsítványt is be kell állítani az érvénybe léptetéshez.", diff --git a/src/strings/id.json b/src/strings/id.json index b0eb49d9df..cb840a4989 100644 --- a/src/strings/id.json +++ b/src/strings/id.json @@ -15,7 +15,6 @@ "LabelSaveLocalMetadata": "Simpan artwork dan metadata ke dalam folder media", "LabelSaveLocalMetadataHelp": "Menyimpan artwork dan metadata langsung ke folder media akan meletakkan mereka di tempat yang mudah diedit.", "LabelTimeLimitHours": "Batas waktu (jam):", - "LabelYourFirstName": "Nama depan anda:", "LabelYoureDone": "Kamu sudah selesai!", "MessageNothingHere": "Tidak ada disini.", "MessagePleaseEnsureInternetMetadata": "Pastikan unduh metadata dari internet diaktifkan.", @@ -25,7 +24,6 @@ "OptionEnableAccessToAllLibraries": "Aktifkan akses ke semua pustaka", "ParentalRating": "Parental Rating", "TabAccess": "Akses", - "TabPlaylist": "Daftar Putar", "TabProfile": "Profil", "TellUsAboutYourself": "Beritahu kami tentang anda", "ThisWizardWillGuideYou": "Panduan ini akan memandu Anda melalui proses setup. Untuk memulai, silahkan pilih bahasa yang Anda gunakan.", @@ -62,7 +60,6 @@ "DatePlayed": "Tanggal dimainkan", "DateAdded": "Tanggal ditambahkan", "CriticRating": "Kritik peringkat", - "CopyStreamURLError": "Terdapat galat dalam penyalinan pranala.", "CopyStreamURLSuccess": "Pranala berhasil disalin.", "CopyStreamURL": "Salin Pranala Stream", "Continuing": "Melanjutkan", @@ -82,7 +79,6 @@ "Categories": "Kategori", "CancelRecording": "Batalkan perekaman", "ButtonWebsite": "Situs web", - "ButtonViewWebsite": "Tampilkan situs web", "ButtonUp": "Atas", "ButtonTrailer": "Cuplikan", "ButtonSubmit": "Kirim", @@ -119,7 +115,6 @@ "ButtonNetwork": "Jaringan", "ButtonMore": "Lebih banyak", "ButtonLibraryAccess": "Akses pustaka", - "ButtonLearnMore": "Pelajari lebih lanjut", "ButtonInfo": "Info", "ButtonHome": "Beranda", "ButtonHelp": "Bantuan", @@ -162,7 +157,6 @@ "Banner": "Spanduk", "Backdrops": "Latar belakang", "Backdrop": "Latar belakang", - "AutoBasedOnLanguageSetting": "Auto (berdasarkan pengaturan bahasa)", "Auto": "Auto", "AuthProviderHelp": "Pilih Penyedia Autentikasi yang akan digunakan untuk mengautentikasi kata sandi pengguna ini.", "Audio": "Audio", @@ -191,7 +185,6 @@ "AddToPlaylist": "Tambah ke dalam daftar putar", "AddToPlayQueue": "Tambah ke dalam antrean putar", "AddToCollection": "Tambah ke dalam koleksi", - "AddItemToCollectionHelp": "Tambahkan item ke dalam koleksi melalui pencarian dan gunakan klik kanan atau ketuk menu untuk menambahkannya ke dalam koleksi.", "AccessRestrictedTryAgainLater": "Akses sedang dibatasi. Mohon tunggu beberapa saat lagi", "Absolute": "Absolut", "Songs": "Lagu", diff --git a/src/strings/is-is.json b/src/strings/is-is.json index f46de26e59..66ea452a64 100644 --- a/src/strings/is-is.json +++ b/src/strings/is-is.json @@ -23,7 +23,6 @@ "LabelNext": "Næsta", "LabelPrevious": "Fyrra", "LabelTimeLimitHours": "Tímamörk (í klukkustundum):", - "LabelYourFirstName": "Fyrra nafn:", "MoreUsersCanBeAddedLater": "Þú getur bætt við fleiri notendum síðar undir stjórnborðinu.", "NextUp": "Næst á dagskrá", "OptionEnableAccessFromAllDevices": "Virkja aðgang frá öllum tækjum", @@ -31,7 +30,6 @@ "TabAccess": "Aðgangur", "TabNotifications": "Tilkynningar", "TabPassword": "Lykilorð", - "TabPlaylist": "Afspilunar listi", "WelcomeToProject": "Velkomin/n í Jellyfin!", "Anytime": "Hvenær sem er", "Genres": "Tegundir", @@ -52,7 +50,6 @@ "Actor": "Leikari", "Add": "Bæta við", "AddToCollection": "Bæta í safn", - "AutoBasedOnLanguageSetting": "Sjálfkrafa (byggt á tungumálastillingum)", "MessageBrowsePluginCatalog": "Skoða viðbætur sem eru í boði í viðbóta safninu okkar.", "BurnSubtitlesHelp": "Ákveður hvort þjónninn eigi að brenna textann inn í myndaskránna þegar verið er að umbreyta skrársniðinu. Með því að forðast að brenna inn textann er hægt að minnka álag á þjóninn (tölvuna). Veljið sjálfkrafa til þess að brenna texta byggðan á myndum (VOBSUB, PGS, SUB/IDX, ofl) og ákveðna ASS/SSA texta.", "OptionSaveMetadataAsHidden": "Geyma gagnagögn (metadata) og myndir sem leynilegar skrár", @@ -80,7 +77,6 @@ "AddedOnValue": "Bætti við {0}", "AirDate": "Frumsýningardagur", "Aired": "Frumsýnt", - "AddItemToCollectionHelp": "Þú getur bætt við efni í söfn með því að leita og svo hægri smella eða ýta á valmyndina.", "AddToPlaylist": "Bæta á spilunarlista", "AdditionalNotificationServices": "Skoða viðbætur til þess að bæta við fleiri tilkynningarþjónustum.", "Alerts": "Viðvaranir", @@ -177,7 +173,6 @@ "HeaderCastCrew": "Leikarar og Áhöfn", "HeaderCastAndCrew": "Leikarar og Áhöfn", "HeaderBooks": "Bækur", - "HeaderAutomaticUpdates": "Sjálfvirkar Uppfærslur", "HeaderAlbums": "Plötur", "HeaderAdmin": "Stjórnandi", "GuideProviderLogin": "Innskrá", @@ -214,7 +209,6 @@ "Categories": "Flokkar", "CancelRecording": "Hætta við upptöku", "ButtonWebsite": "Vefsiða", - "ButtonViewWebsite": "Skoða vefsíðu", "ButtonUp": "Upp", "ButtonUninstall": "Fjarlægja", "ButtonTrailer": "Sýnishorn", @@ -252,7 +246,6 @@ "ButtonMore": "Meira", "ButtonManualLogin": "Handvirkt Auðkenni", "ButtonLibraryAccess": "Aðgangur að safni", - "ButtonLearnMore": "Læra meira", "ButtonInfo": "Upplýsingar", "ButtonHome": "Heim", "ButtonHelp": "Hjálp", @@ -385,7 +378,6 @@ "DatePlayed": "Dagsetning spilað", "DateAdded": "Dagsetning bætt við", "CriticRating": "Einkunn gagnrýnanda", - "CopyStreamURLError": "Villa varð við afritun vefslóðar.", "CopyStreamURLSuccess": "Afrit af vefslóð tókst.", "CopyStreamURL": "Afrita vefslóð streymis", "Continuing": "Áframhaldandi", @@ -419,7 +411,6 @@ "SaveChanges": "Vista breytingar", "Save": "Vista", "Saturday": "Laugardagur", - "RunAtStartup": "Keyra við ræsingu", "Rewind": "Spóla til baka", "AlbumArtist": "Höfundur plötu", "OptionHasTrailer": "Sýnishorn", @@ -533,13 +524,11 @@ "LabelDroppedFrames": "Felldir rammar:", "LabelDiscNumber": "Númer disks:", "LabelDeviceDescription": "Lýsing tækis", - "LabelDashboardTheme": "Þema mælaborðs:", "LabelCustomCss": "Sérsniðin CSS:", "LabelCriticRating": "Einkunn gagnrýnanda:", "LabelCorruptedFrames": "Skemmdir rammar:", "LabelCancelled": "Hætt við", "LabelAppName": "Heiti forrits", - "LabelAllowServerAutoRestart": "Leyfa netþjóni að endurræsa sig sjálfkrafa til þess að uppfæra sig", "LabelAllowHWTranscoding": "Leyfa vélbúnaðarumkóðun", "Label3DFormat": "3D snið:", "HeaderIdentification": "Auðkenning", diff --git a/src/strings/it.json b/src/strings/it.json index cf2daf77b2..dd32c25ef3 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "L'accesso è attualmente limitato. Si prega di riprovare più tardi.", "Actor": "Attore", "Add": "Aggiungi", - "AddItemToCollectionHelp": "Aggiungi elementi alle collezioni ricercandoli e utilizzando il pulsante destro del mouse o tocca i menu per aggiungerli a una raccolta.", "AddToCollection": "Aggiunto alla collezione", "AddToPlayQueue": "Aggiungi alla coda di riproduzione", "AddToPlaylist": "Aggiungi alla playlist", @@ -34,7 +33,6 @@ "Ascending": "Crescente", "AspectRatio": "Rapporto d'Aspetto", "AttributeNew": "Nuovo", - "AutoBasedOnLanguageSetting": "Auto (basato sull'impostazione della lingua)", "Backdrop": "Sfondo", "Backdrops": "Sfondi", "BirthDateValue": "Nato il: {0}", @@ -72,7 +70,6 @@ "ButtonGotIt": "Ho capito", "ButtonGuide": "Guida", "ButtonHelp": "Aiuto", - "ButtonLearnMore": "saperne di più", "ButtonLibraryAccess": "Accesso biblioteca", "ButtonManualLogin": "Accesso Manuale", "ButtonMore": "Altro", @@ -115,7 +112,6 @@ "ButtonSubtitles": "Sottotitoli", "ButtonUninstall": "Disinstalla", "ButtonUp": "Su", - "ButtonViewWebsite": "Visualizza sito web", "ButtonWebsite": "Web", "CancelRecording": "Annulla la registrazione", "CancelSeries": "Annulla Serie TV", @@ -272,7 +268,6 @@ "HeaderApiKeysHelp": "Le Applicazioni esterne devono avere una chiave API per comunicare con il Server Jellyfin. Le chiavi sono emesse accedendo con un account Jellyfin, o fornendo manualmente una chiave all'applicazione.", "HeaderAudioBooks": "Audiolibri", "HeaderAudioSettings": "Impostazioni audio", - "HeaderAutomaticUpdates": "Aggiornamenti Automatici", "HeaderBlockItemsWithNoRating": "Blocca elementi sconosciuti o senza informazioni:", "HeaderBooks": "Libri", "HeaderBranding": "Personalizza", @@ -483,8 +478,6 @@ "LabelAlbumArtists": "Artisti album:", "LabelAll": "Tutti", "LabelAllowHWTranscoding": "Consenti transcodifica hardware", - "LabelAllowServerAutoRestart": "Consenti al server di Riavviarsi automaticamente per applicare gli aggiornamenti", - "LabelAllowServerAutoRestartHelp": "Il server si Riavvierà solamente quando nessun utente è connesso.", "LabelAllowedRemoteAddresses": "Filtro indirizzo IP Remoto:", "LabelAllowedRemoteAddressesMode": "Modalità filtro indirizzo IP remoto:", "LabelAppName": "Nome app", @@ -520,7 +513,6 @@ "LabelCustomDeviceDisplayName": "Nome da visualizzare:", "LabelCustomDeviceDisplayNameHelp": "Fornire un nome di visualizzazione personalizzato o lasciare vuoto per utilizzare il nome riportato dal dispositivo.", "LabelCustomRating": "Voto personalizzato:", - "LabelDashboardTheme": "Tema dashboard del server:", "LabelDateAdded": "Aggiunto il:", "LabelDateAddedBehavior": "Data di comportamento per i nuovi contenuti:", "LabelDateAddedBehaviorHelp": "Se un valore di metadati è presente sarà sempre utilizzato prima una di queste opzioni.", @@ -735,7 +727,6 @@ "LabelSortBy": "Ordina per:", "LabelSortOrder": "Ordinato per:", "LabelSortTitle": "Titolo per ordinamento:", - "LabelSoundEffects": "Effetti sonori:", "LabelSource": "Origine:", "LabelSpecialSeasonsDisplayName": "Nome della stagione speciale:", "LabelSportsCategories": "Categorie sport:", @@ -784,7 +775,6 @@ "LabelXDlnaCapHelp": "Determina il contenuto dell'elemento X_DLNACAP in urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Determina il contenuto dell'elemento X_DLNACAP nella urn: schemas-DLNA-org: dispositivo 1-0 namespace.", "LabelYear": "Anno:", - "LabelYourFirstName": "Il tuo nome:", "LabelYoureDone": "Hai Finito!", "LabelZipCode": "Cap:", "LabelffmpegPath": "Percorso FFmpeg:", @@ -847,7 +837,6 @@ "MessageFileReadError": "Si è verificato un errore durante la lettura del file. Si prega di riprovare.", "MessageForgotPasswordFileCreated": "Il seguente file è stato creato sul server e contiene le istruzioni su come procedere:", "MessageForgotPasswordInNetworkRequired": "Riprova all'interno della rete domestica per avviare il processo di reimpostazione della password.", - "MessageInstallPluginFromApp": "Questo plugin deve essere installato dall'app in cui vuoi farlo funzionare.", "MessageInvalidForgotPasswordPin": "É stato inserito un codice pin invalido o scaduto . Riprova.", "MessageInvalidUser": "Utente o password errato. Riprova.", "MessageItemSaved": "Elemento salvato.", @@ -1069,7 +1058,6 @@ "ProductionLocations": "Sedi di produzione", "Programs": "Programmi", "Quality": "Qualità", - "QueueAllFromHere": "In coda tutto da qui in poi", "Raised": "Rilievo", "Rate": "Vota", "RecentlyWatched": "Visti di recente", @@ -1100,7 +1088,6 @@ "ReplaceExistingImages": "Sovrascrivi immagini esistenti", "ResumeAt": "Riprendi da {0}", "Rewind": "Riavvolgi", - "RunAtStartup": "Esegui all'avvio", "Runtime": "Durata", "Saturday": "Sabato", "Save": "Salva", @@ -1252,7 +1239,6 @@ "ValueTimeLimitSingleHour": "Tempo limite: 1 ora", "ValueVideoCodec": "Codec Video: {0}", "Vertical": "Verticale", - "VideoRange": "Range del Video", "ViewAlbum": "Visualizza album", "ViewPlaybackInfo": "Vedi info sulla riproduzione", "Watched": "Visto", @@ -1382,7 +1368,6 @@ "LabelPlaylist": "Playlist:", "LabelPlayMethod": "Metodo di riproduzione:", "LabelPleaseRestart": "Le modifiche avranno effetto dopo aver manualmente ricaricato il client web.", - "LabelSkin": "Skin:", "LabelTranscodes": "Trascodifiche:", "LabelTranscodingFramerate": "Framerate di trascodifica:", "LabelTranscodingProgress": "Progresso di trascodifica:", @@ -1391,12 +1376,8 @@ "LabelVideo": "Video", "DashboardArchitecture": "Architettura: {0}", "LabelWeb": "Web:", - "LaunchWebAppOnStartup": "Lancia l'interfaccia web quando viene avviato il server", - "LaunchWebAppOnStartupHelp": "Apri il client web nel tuo web browser quando il server si avvia inizialmente. Ciò non accadrà quando si usa la funzione riavvio server.", "LeaveBlankToNotSetAPassword": "Puoi lasciare questo campo vuoto per non impostare alcuna password.", - "LinksValue": "Link: {0}", "MediaInfoTimestamp": "Orario", - "MediaInfoSoftware": "Software", "Mobile": "Mobile", "MoreMediaInfo": "Informazioni sui Media", "MusicAlbum": "Album Musicale", @@ -1432,7 +1413,6 @@ "TabLogs": "Log", "TabNetworking": "Rete", "TabPassword": "Password", - "TabPlaylist": "Playlist", "TabPlugins": "Plugin", "TabServer": "Server", "TabStreaming": "Streaming", @@ -1444,7 +1424,6 @@ "OptionRandom": "Casuale", "MessageConfirmAppExit": "Vuoi uscire?", "HeaderNavigation": "Navigazione", - "CopyStreamURLError": "Si è verificato un errore nel copiare l'indirizzo.", "PlaybackErrorNoCompatibleStream": "Il client è incompatibile con il media e il server non sta inviando un formato compatibile.", "OptionForceRemoteSourceTranscoding": "Forza la transcodifica da fonti di media remoti (come LiveTV)", "NoCreatedLibraries": "Sembra che tu non abbia ancora creato delle librerie. {0}Vuoi crearne una adesso?{1}", @@ -1480,16 +1459,13 @@ "Yadif": "YADIF", "Track": "Traccia", "Season": "Stagione", - "OtherArtist": "Altri Artisti", "Movie": "Film", "LabelLibraryPageSizeHelp": "Numero di elementi presenti nella paginazione della libreria. Il valore 0 disabilita la paginazione.", "LabelLibraryPageSize": "Elementi nella paginazione della libreria:", "Episode": "Episodio", "BoxSet": "Cofanetto", "AlbumArtist": "Artisti dell'Album", - "ReleaseGroup": "Release Group", "UnsupportedPlayback": "Jellyfin non è in grado di decriptare i contenuti protetti da DRM ma tutti i contenuti verranno tentati a prescindere, compresi quelli protetti. Alcuni file potrebbero apparire completamente neri a causa della crittografia o di altre funzionalità non supportate, come i titoli interattivi.", - "MessageUnauthorizedUser": "Non sei autorizzato ad accedere in questo momento al server. Contatta l'amministratore per ulteriori dettagli.", "ButtonTogglePlaylist": "Playlist", "ButtonToggleContextMenu": "Altro", "HeaderFavoritePlaylists": "Playlist Favorite", @@ -1508,7 +1484,6 @@ "TabDVR": "DVR", "SaveChanges": "Salva modifiche", "HeaderDVR": "DVR", - "LabelNightly": "Nightly", "SyncPlayAccessHelp": "Selezionare il livello d'accesso di questo utente a SyncPlay che permetterà di riprodurre contemporaneamente su diversi dispositivi.", "MessageSyncPlayErrorMedia": "Impossibile abilitare SyncPlay! Errore media.", "MessageSyncPlayErrorMissingSession": "Impossibile abilitare SyncPlay! Sessione mancante.", diff --git a/src/strings/ja.json b/src/strings/ja.json index 53d3650cef..4e0a0a4fed 100644 --- a/src/strings/ja.json +++ b/src/strings/ja.json @@ -39,7 +39,6 @@ "Audio": "オーディオ", "AuthProviderHelp": "ユーザーのパスワードを認証するために使用する認証プロバイダを選択してください。", "Auto": "自動", - "AutoBasedOnLanguageSetting": "自動選択(設定されている言語を優先)", "Backdrop": "背景", "Backdrops": "背景", "Banner": "バナー", @@ -83,7 +82,6 @@ "ButtonHelp": "ヘルプ", "ButtonHome": "ホーム", "ButtonInfo": "情報", - "ButtonLearnMore": "もっと詳しく", "ButtonLibraryAccess": "ライブラリへアクセス", "ButtonManualLogin": "マニュアルログイン", "ButtonMore": "もっと", @@ -127,7 +125,6 @@ "ButtonTrailer": "予告", "ButtonUninstall": "アンインストール", "ButtonUp": "上", - "ButtonViewWebsite": "ウェブサイトで見る", "ButtonWebsite": "ウェブサイト", "CancelRecording": "レコーディングをキャンセル", "CancelSeries": "中止したシリーズ", @@ -223,7 +220,6 @@ "HeaderMoreLikeThis": "これに似たもの", "InstantMix": "インスタントミックス", "MoreFromValue": "もっと詳しく {0}", - "AddItemToCollectionHelp": "アイテムをコレクションに追加するには右クリックメニューかタップメニューから追加してください。", "AttributeNew": "新規", "ButtonNew": "新規", "ButtonOff": "オフ", @@ -300,7 +296,6 @@ "HeaderAppearsOn": "表示", "HeaderAudioBooks": "オーディオブック", "HeaderAudioSettings": "音声設定", - "HeaderAutomaticUpdates": "自動更新", "HeaderBlockItemsWithNoRating": "評価情報がない、または認識できないアイテムをブロックします。", "HeaderBooks": "ブック", "HeaderBranding": "ブランディング", @@ -765,7 +760,6 @@ "TabCodecs": "コーデック", "TabContainers": "コンテナ", "Rewind": "巻き戻す", - "RunAtStartup": "スタートアップに起動", "Runtime": "実行時間", "Saturday": "土曜日", "SaveSubtitlesIntoMediaFolders": "字幕をメディアフォルダーに保存", @@ -803,7 +797,6 @@ "ValueTimeLimitMultiHour": "タイムリミット: {0} 時間", "ValueVideoCodec": "映像コーデック: {0}", "Vertical": "垂直", - "VideoRange": "映像範囲", "ViewAlbum": "アルバムを見る", "ViewPlaybackInfo": "プレイバック情報を見る", "Watched": "視聴済み", @@ -849,8 +842,6 @@ "LabelAlbumArtPN": "アルバムアートPN:", "LabelAlbumArtists": "アルバムアーティスト:", "LabelAllowHWTranscoding": "ハードウェアトランスコーディングを許可", - "LabelAllowServerAutoRestart": "アップデートを適応するためにサーバーの再起動を許可", - "LabelAllowServerAutoRestartHelp": "サーバーはユーザーがログインしていないときのみ再起動します。", "LabelAllowedRemoteAddresses": "リモートIPアドレスフィルター:", "LabelAppNameExample": "例: スケートボード、ソナー", "LabelArtists": "アーティスト:", @@ -870,7 +861,6 @@ "LabelCommunityRating": "コミュニティ評価:", "LabelContentType": "コンテンツタイプ:", "LabelCountry": "国:", - "LabelDashboardTheme": "サーバーダッシュボードテーマ:", "LabelPublicHttpsPortHelp": "公開ポート番号はローカルHTTPSポートにマッピングしてください。", "LabelAlbumArtMaxWidth": "アルバムアート最大高さ:", "LabelAlbumArtMaxHeight": "アルバムアート最大高さ:", @@ -929,10 +919,8 @@ "LabelSelectVersionToInstall": "インストールするバージョンを選択:", "LabelSerialNumber": "シリアルナンバー", "LabelServerHost": "ホスト:", - "LabelSkin": "スキン:", "Premiere": "初日", "LabelSaveLocalMetadata": "アートワークをメディアフォルダーに保存", - "LabelSoundEffects": "音響効果:", "LabelSource": "ソース:", "LabelSportsCategories": "スポーツカテゴリ:", "LabelStatus": "ステータス:", @@ -960,13 +948,11 @@ "DashboardArchitecture": "アーキテクチャ: {0}", "LabelVideo": "映像", "LabelVideoBitrate": "映像ビットレート:", - "LabelYourFirstName": "名前:", "Share": "共有", "LabelSize": "大きさ:", "LatestFromLibrary": "最新 {0}", "LearnHowYouCanContribute": "コントリビュートする方法を知る。", "LabelTagline": "キャッチフレーズ:", - "LinksValue": "リンク: {0}", "Live": "ライブ", "LiveBroadcasts": "ライブブロードキャスト", "LiveTV": "ライブTV", @@ -983,7 +969,6 @@ "MediaInfoResolution": "解像度", "MediaInfoSampleRate": "サンプルレート", "MediaInfoSize": "大きさ", - "MediaInfoSoftware": "ソフトウェア", "MediaInfoStreamTypeAudio": "音声", "MediaInfoStreamTypeData": "データ", "MediaInfoStreamTypeEmbeddedImage": "埋め込み画像", @@ -1070,7 +1055,6 @@ "TabNotifications": "通知", "TabOther": "その他", "TabParentalControl": "ペアレンタルコントロール", - "TabPlaylist": "プレイリスト", "TabPlaylists": "プレイリスト", "TabPlugins": "プラグイン", "TabProfile": "プロフィール", @@ -1110,7 +1094,6 @@ "LabelBindToLocalNetworkAddress": "ローカルネットワークアドレスにバインド:", "LabelDownMixAudioScale": "ダウンミキシング時の音声ブースト:", "HeaderNavigation": "ナビゲーション", - "CopyStreamURLError": "URLのコピー中にエラーが発生しました。", "ButtonSplit": "分ける", "LabelEnableDlnaServer": "DLNAサーバーの有効化", "LabelEnableDlnaDebugLogging": "DLNAデバッグログの有効化", @@ -1200,7 +1183,6 @@ "LabelNumberOfGuideDaysHelp": "多くの日数分のガイドデータをダウンロードするとより先のスケジュールとリストを見ることができるようになりますが,ダウンロードに時間がかかるようになります。自動に設定するとチャンネル数を基に選択されます。", "LabelNumberOfGuideDays": "ガイドデータをダウンロードする日数:", "LabelNewsCategories": "ニュースのカテゴリ:", - "LabelNightly": "最新・不安定版", "LabelStable": "安定版", "LabelChromecastVersion": "Chromecastバージョン", "LabelMusicStreamingTranscodingBitrateHelp": "音楽ストリーミングの最大ビットレートを指定します。", diff --git a/src/strings/kk.json b/src/strings/kk.json index e4eb57a72d..909af11da3 100644 --- a/src/strings/kk.json +++ b/src/strings/kk.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "Aǵymda qatynaý shektelgen. Áreketti keıin qaıtalańyz.", "Actor": "Aktór", "Add": "Ústeý", - "AddItemToCollectionHelp": "Tarmaqtardy izdep jáne tintýirdiń oń jaq túımeshign basyp jıyntyqtarǵa tarmaqtardy ústeńiz nemese jıyntyqqa ústeý úshin mázirlerdi túrtińiz.", "AddToCollection": "Jıyntyqqa ústeý", "AddToPlayQueue": "Oınatý kezegine ústeý", "AddToPlaylist": "Oınatý tizimine ústeý", @@ -40,7 +39,6 @@ "AttributeNew": "Jańa", "Audio": "Dybys", "Auto": "Avtomatty", - "AutoBasedOnLanguageSetting": "Avtomatty (til teńshelimi negizinde)", "Backdrop": "Artqy sýret", "Backdrops": "Artqy sýretter", "Banner": "Baner", @@ -84,7 +82,6 @@ "ButtonHelp": "Anyqtama", "ButtonHome": "Basqyǵa", "ButtonInfo": "Aqparatqa", - "ButtonLearnMore": "Kóbirek bilý", "ButtonLibraryAccess": "Tasyǵyshhanǵa qatynaý", "ButtonManualLogin": "Qolmen kirý", "ButtonMore": "Kóbirek", @@ -130,7 +127,6 @@ "ButtonTrailer": "Treıler", "ButtonUninstall": "Ornatymdy joıý", "ButtonUp": "Joǵaryǵa", - "ButtonViewWebsite": "Ýeb-saıtyn qaraý", "ButtonWebsite": "Ýeb-saıty", "CancelRecording": "Jazýdy boldyrmaý", "CancelSeries": "Telehıkaıany boldyrmaý", @@ -294,7 +290,6 @@ "HeaderAppearsOn": "Kórýge bolady", "HeaderAudioBooks": "Dybystyq kitaptar", "HeaderAudioSettings": "Dybys parametrleri", - "HeaderAutomaticUpdates": "Avtomatty jańartýlar", "HeaderBlockItemsWithNoRating": "Jastas sanaty týraly aqparaty joq nemese ol tanylmaǵan mazmundy qursaýlaý:", "HeaderBooks": "Kitaptar", "HeaderBranding": "Bezendirý", @@ -515,8 +510,6 @@ "LabelAlbumArtists": "Álbom oryndaýshylary:", "LabelAll": "Barlyq", "LabelAllowHWTranscoding": "Apparattyq qaıta kodtaýǵa ruqsat etý", - "LabelAllowServerAutoRestart": "Jańartýlardy qoldaný úshin serverge qaıta iske qosylýdy ruqsat etý", - "LabelAllowServerAutoRestartHelp": "Tek qana eshqandaı paıdalýnshylar belsendi emes áreketsiz mezgilderde server qaıta iske qosylady.", "LabelAllowedRemoteAddresses": "Qashyqtaǵy IP-mekenjaı súzgisi:", "LabelAllowedRemoteAddressesMode": "Qashyqtaǵy IP-mekenjaı súzgisiniń rejimi:", "LabelAppName": "Qoldanba aty", @@ -554,7 +547,6 @@ "LabelCustomDeviceDisplayName": "Beınelený aty:", "LabelCustomDeviceDisplayNameHelp": "Beınelenetin teńshelgen atyn usynyńyz nemese qurylǵy arqyly baıandalǵan atyn paıdalaný úshin bos qaldyryńyz.", "LabelCustomRating": "Teńshelgen sanat:", - "LabelDashboardTheme": "Server taqtasynyń taqyryby:", "LabelDateAdded": "Ústelgen kúni:", "LabelDateAddedBehavior": "Jańa mazmun úshin qosylǵan kúni tártibi:", "LabelDateAddedBehaviorHelp": "Eger metaderekterde máni bolsa, bul qaısybir osy opsıalarydyń aldynda árqashanda paıdalanylady.", @@ -766,7 +758,6 @@ "LabelServerHost": "Host:", "LabelServerHostHelp": "192.168.1.100:8096 nemese https://myserver.com", "LabelSimultaneousConnectionLimit": "Bir mezgildegi aǵyndardyń shegi:", - "LabelSkin": "Muqaba:", "LabelSkipBackLength": "Artqa ótkizip jiberý uzaqtyǵy:", "LabelSkipForwardLength": "Alǵa ótkizip jiberý uzaqtyǵy:", "LabelSkipIfAudioTrackPresent": "Eger ádepki dybys jolshyǵy júktep alynatyn tilge sáıkes kelse ótkizip jiberý", @@ -778,7 +769,6 @@ "LabelSortBy": "Suryptaý tásili:", "LabelSortOrder": "Suryptaý reti:", "LabelSortTitle": "Ataý boıynsha suryptaý:", - "LabelSoundEffects": "Dybystyq áserleri:", "LabelSource": "Qaınar kózi:", "LabelSpecialSeasonsDisplayName": "Arnaıy maýsymdyń beıneleý aty:", "LabelSportsCategories": "Sporttyq sanattary:", @@ -832,7 +822,6 @@ "LabelXDlnaDoc": "X-DLNA tásimi:", "LabelXDlnaDocHelp": "urn:schemas-dlna-org:device-1-0 ataýlar keńistigindegi X_DLNADOC elementi mazmunyn anyqtaıdy.", "LabelYear": "Jyl:", - "LabelYourFirstName": "Atyńyz:", "LabelYoureDone": "Siz daıynsyz!", "LabelZipCode": "Poshta kody:", "LabelffmpegPath": "FFmpeg joly:", @@ -843,7 +832,6 @@ "LearnHowYouCanContribute": "Qalaı úles qosýynyńyz múmkin týraly úırenińiz.", "LibraryAccessHelp": "Bul paıdalanýshymen ortaqtasý úshin tasyǵyshhanalardy bólekteńiz. Metaderek retteýshini paıdalanyp ákimshiler barlyq qaltalardy óńdeýi múmkin.", "Like": "Unaıdy", - "LinksValue": "Siltemeler: {0}", "List": "Tizim", "Live": "Tikeleı", "LiveBroadcasts": "Tikeleı taratymdar", @@ -902,7 +890,6 @@ "MessageFileReadError": "Faıl oqý kezinde qate oryn aldy. Áreketti keıin qaıtalańyz.", "MessageForgotPasswordFileCreated": "Kelesi faıl serverińizde jasaldy jáne qalaı kirisý týraly nusqaýlar ishinde bar:", "MessageForgotPasswordInNetworkRequired": "Paróldi ysyrý prosesi úshin áreketti úılik jelińizdiń ishinde qaıtalańyz.", - "MessageInstallPluginFromApp": "Bul plagın qandaı qoldanbaǵa taǵaıyndalsa, sonyń ishinen ornatylýy tıisti.", "MessageInvalidForgotPasswordPin": "Jaramsyz nemese merzimi aıaqtalǵan PIN-kod engizildi. Áreketti qaıtalańyz.", "MessageInvalidUser": "Jaramsyz paıdalanýshy aty nemese paról. Áreketti qaıtalańyz.", "MessageItemSaved": "Tarmaq saqtaldy.", @@ -1144,7 +1131,6 @@ "ProductionLocations": "Óndirý oryndary", "Programs": "Kórsetimder", "Quality": "Sapasy", - "QueueAllFromHere": "Bul aradan bárin kezekke", "Raised": "Dónesti", "Rate": "Baǵalaý", "RecentlyWatched": "Jýyqta qaralǵan", @@ -1175,7 +1161,6 @@ "ReplaceExistingImages": "Bar sýretterdi aýystyrý", "ResumeAt": "{0} bastap jalǵastyrý", "Rewind": "Shegindirý", - "RunAtStartup": "Iske qosylýdan bastap oryndaý", "Runtime": "Uzaqtyǵy", "Saturday": "senbi", "Save": "Saqtaý", @@ -1271,7 +1256,6 @@ "TabParentalControl": "Mazmundy basqarý", "TabPassword": "Paról", "TabPlayback": "Oınatý", - "TabPlaylist": "Oınatý tizimi", "TabPlaylists": "Oınatý tizimderi", "TabPlugins": "Plagınder", "TabProfile": "Profaıl", @@ -1342,7 +1326,6 @@ "ValueTimeLimitSingleHour": "Ýaqyt shegi: 1 saǵat", "ValueVideoCodec": "Beıne kodegi: {0}", "Vertical": "Tiginen", - "VideoRange": "Beıne aýqymy", "ViewAlbum": "Álbomdy qaraý", "ViewPlaybackInfo": "Oınatý týraly aqparat", "Watched": "Qaralǵan", @@ -1385,9 +1368,6 @@ "DashboardOperatingSystem": "Operasıalyq júıe: {0}", "DashboardArchitecture": "Arhıtektýrasy: {0}", "LabelWeb": "Ýeb:", - "LaunchWebAppOnStartup": "Serverdi iske qosqan kezde ýeb-ınterfeısti iske qosý", - "LaunchWebAppOnStartupHelp": "Server bastapqyda iske qosylǵan kezde, ýeb-klıent ádepki sholǵyshta ashylady. Bul serverdi qaıta iske qosý fýnksıasyn qoldanǵanda oryn almaıdy.", - "MediaInfoSoftware": "Baǵdarlamalyq jasaqtama", "MediaInfoStreamTypeAudio": "Dybys", "MediaInfoStreamTypeData": "Derekter", "MediaInfoStreamTypeEmbeddedImage": "Endirilgen sýret", @@ -1450,7 +1430,6 @@ "LabelDroppedFrames": "Ótkizilgen kadrlar:", "LabelCorruptedFrames": "Búlingen kadrlar:", "HeaderNavigation": "Sharlaý", - "CopyStreamURLError": "URL kóshirgende qate oryn aldy.", "ButtonSplit": "Bólý", "AskAdminToCreateLibrary": "Tasýǵyshanany jasaý úshin ákimshiden suraý.", "AllowFfmpegThrottling": "Qaıta kodtaýdy retteý", @@ -1473,9 +1452,7 @@ "Yadif": "YADIF", "Track": "Jolshyq", "Season": "Maýsym", - "ReleaseGroup": "Shyǵarýshy top", "Person": "Tulǵa", - "OtherArtist": "Basqa oryndaýshy", "Movie": "Fılm", "LabelLibraryPageSize": "Tasyǵyshhana betiniń ólshemi:", "Episode": "Bólim", @@ -1497,7 +1474,6 @@ "MillisecondsUnit": "ms", "LabelSyncPlayTimeOffset": "Server ýaqtynan aýytqýy:", "LabelRequireHttps": "HTTPS qajet etedi", - "LabelNightly": "Túngi", "LabelStable": "Turaqty", "LabelChromecastVersion": "Chromecast nusqasy", "LabelEnableHttps": "HTTPS qosý", diff --git a/src/strings/ko.json b/src/strings/ko.json index 721a6969bb..e18dc5f537 100644 --- a/src/strings/ko.json +++ b/src/strings/ko.json @@ -38,7 +38,6 @@ "ButtonHelp": "도움말", "ButtonHome": "홈", "ButtonInfo": "정보", - "ButtonLearnMore": "더 알아보기", "ButtonManualLogin": "수동 로그인", "ButtonMore": "더 보기", "ButtonNetwork": "네트워크", @@ -76,7 +75,6 @@ "ButtonSubmit": "제출", "ButtonSubtitles": "자막", "ButtonUninstall": "제거", - "ButtonViewWebsite": "웹사이트 보기", "ButtonWebsite": "웹사이트", "ChannelAccessHelp": "이 사용자와 공유할 채널을 선택합니다. 관리자는 메타데이터 매니저를 사용하여 모든 채널을 수정할 수 있습니다.", "CinemaModeConfigurationHelp": "시네마 모드는 본 영화 전에 예고편과 소개 영상 등을 재생하여 사용자의 거실에서 극장의 경험을 제공합니다.", @@ -135,7 +133,6 @@ "HeaderApiKeys": "API 키", "HeaderApp": "앱", "HeaderAudioSettings": "오디오 설정", - "HeaderAutomaticUpdates": "자동 업데이트", "HeaderBooks": "도서", "HeaderBranding": "브랜딩", "HeaderCastCrew": "배역 및 제작진", @@ -274,8 +271,6 @@ "LabelAlbumArtPN": "앨범 아트 PN:", "LabelAlbumArtists": "앨범 아티스트:", "LabelAll": "모두", - "LabelAllowServerAutoRestart": "서버가 자동으로 업데이트를 적용하도록 재시작 허용", - "LabelAllowServerAutoRestartHelp": "서버를 활성화된 사용자가 없는 유휴 기간에 다시 시작합니다.", "LabelAppName": "앱 이름", "LabelArtists": "아티스트:", "LabelArtistsHelp": "; 를 사용하여 여러 개 분리", @@ -466,7 +461,6 @@ "LabelUserLibraryHelp": "장치에 어떤 사용자 라이브러리를 보여줄 지 선택합니다. 기본 설정을 사용하려면 비워두십시오.", "LabelUsername": "사용자명:", "LabelVersionInstalled": "{0} 설치됨", - "LabelYourFirstName": "이름:", "LabelYoureDone": "완료!", "LabelZipCode": "우편 번호:", "LibraryAccessHelp": "이 사용자와 공유할 라이브러리를 선택합니다. 관리자는 메타데이터 관리자를 사용하여 모든 폴더를 수정할 수 있습니다.", @@ -659,7 +653,6 @@ "PleaseSelectTwoItems": "최소 두 개의 항목을 선택하세요.", "Premiere": "프리미어", "Producer": "프로듀서", - "QueueAllFromHere": "여기부터 모두 대기열에 추가", "RecommendationBecauseYouLike": "{0} 을(를) 좋아하기 때문에", "RecommendationBecauseYouWatched": "{0} 을(를) 시청했기 때문에", "RecommendationDirectedBy": "{0} 감독", @@ -722,7 +715,6 @@ "TabParentalControl": "자녀 보호", "TabPassword": "비밀번호", "TabPlayback": "재생", - "TabPlaylist": "재생목록", "TabPlaylists": "재생목록", "TabPlugins": "플러그인", "TabProfile": "프로필", @@ -800,7 +792,6 @@ "Shows": "쇼", "Songs": "노래", "Sync": "동기화", - "AddItemToCollectionHelp": "항목을 컬렉션에 추가하려면 검색한 뒤 우클릭이나 탭 매뉴를 이용하십시오.", "AddToCollection": "컬렉션에 추가", "AddToPlayQueue": "재생 대기열에 추가", "AddedOnValue": "{0} 추가됨", @@ -825,7 +816,6 @@ "AspectRatio": "종횡비", "AuthProviderHelp": "이 사용자의 비밀번호를 인증할 때 사용할 인증 서비스 제공자를 선택하십시오.", "Auto": "자동", - "AutoBasedOnLanguageSetting": "자동 (언어 설정을 따름)", "Backdrop": "배경", "Banner": "배너", "BookLibraryHelp": "오디오 혹은 텍스트 도서가 지원됩니다. {0}도서 작명 가이드{1}를 참고하십시오.", @@ -896,7 +886,6 @@ "Yes": "예", "Whitelist": "화이트리스트", "ViewPlaybackInfo": "재생 정보 보기", - "VideoRange": "비디오 길이", "ValueSeconds": "{0}초", "Upload": "업로드", "Unrated": "평점을 매기지 않음", @@ -1004,7 +993,6 @@ "MediaInfoStreamTypeEmbeddedImage": "내장된 이미지", "MediaInfoStreamTypeData": "데이터", "MediaInfoStreamTypeAudio": "오디오", - "MediaInfoSoftware": "소프트웨어", "MediaInfoTimestamp": "타임스탬프", "MediaInfoSize": "크기", "MediaInfoLevel": "수준", @@ -1070,7 +1058,6 @@ "LabelDynamicExternalId": "{0} ID:", "LabelDisplayLanguageHelp": "Jellyfin 번역은 진행 중인 프로젝트입니다.", "LabelDisplayLanguage": "표시 언어:", - "LabelDashboardTheme": "서버 대시보드 테마:", "LabelChannels": "채널:", "LabelCancelled": "취소됨", "LabelBitrate": "비트레이트:", @@ -1104,7 +1091,6 @@ "MessageDownloadQueued": "다운로드 대기 중.", "MessageDirectoryPickerLinuxInstruction": "Linux on Arch Linux, CentOS, Debian, Fedora, OpenSUSE, Ubuntu의 경우 서비스 사용자에게 최소한 저장 위치에 대한 읽기 권한을 부여해야 합니다.", "MessageDirectoryPickerBSDInstruction": "BSD의 경우, Jellyfin이 FreeNAS Jail에 액세스할 수 있도록 하려면 FreeNAS Jail 내에 스토리지를 구성해야 할 수도 있습니다.", - "LinksValue": "링크: {0}", "LatestFromLibrary": "최근 {0}", "LabelYear": "년도:", "LabelVaapiDeviceHelp": "하드웨어 가속에 쓰이는 렌더 노드입니다.", @@ -1113,7 +1099,6 @@ "LabelTranscodingThreadCountHelp": "트랜스코딩에 사용할 스레드의 최대 갯수를 선택하십시오. 스레드의 갯수를 줄이면 CPU사용량이 줄어들지만, 부드러운 재생에 필요한 만큼 빠르게 변환되지 않을 수 있습니다.", "LabelTranscodingThreadCount": "트랜스코딩 스레드 수:", "LabelTextBackgroundColor": "글자 배경 색깔:", - "LabelSoundEffects": "음향 효과:", "LabelSortTitle": "제목 정렬:", "LabelSortOrder": "정렬 순서:", "LabelSortBy": "정렬 기준:", @@ -1167,7 +1152,6 @@ "LiveBroadcasts": "실시간 방송", "LabelTypeMetadataDownloaders": "{0} 메타데이터 다운로더:", "LabelType": "유형:", - "LabelSkin": "스킨:", "LabelPleaseRestart": "변경사항은 웹 클라이언트를 다시 불러오면 적용됩니다.", "LabelPlayMethod": "재생 방식:", "LabelPersonRoleHelp": "예시: Ice cream truch driver", @@ -1254,7 +1238,6 @@ "H264CrfHelp": "CRF(고정 레이트 팩터)는 x264 인코더의 기본 품질 설정입니다. 0에서 51 사이의 값을 설정할 수 있습니다. 값이 작을수록 품질이 향상됩니다(파일 크기가 커지면서). Sane 값은 18과 28 사이입니다. x264의 기본값은 23이므로 시작점으로 사용할 수 있습니다.", "LabelSeasonNumber": "시즌 번호:", "LabelPlayer": "재생기:", - "LaunchWebAppOnStartup": "서버를 시작할 때 웹 인터페이스 실행", "MediaInfoBitDepth": "비트뎁스", "LabelPostProcessor": "후처리 애플리케이션:", "RefreshQueued": "새로 고침 대기 중", @@ -1282,7 +1265,6 @@ "Features": "기능", "ErrorPleaseSelectLineup": "라인업을 선택하고 다시 시도하십시오. 이용 가능한 라인업이 없으면 계정, 비밀번호, 우편번호가 정확한지 확인하십시오.", "ErrorAddingListingsToSchedulesDirect": "Schedules Direct 계정에 라인업을 추가하는 중에 오류가 발생했습니다. Schedules Direct는 계정 당 제한된 수의 라인업만이 허용됩니다. 계속하려면 Schedules Direct 웹사이트에 로그인하여 다른 항목을 삭제해야 할 수 있습니다.", - "CopyStreamURLError": "URL을 복사하는 중에 오류가 발생했습니다.", "ColorTransfer": "컬러 변환", "AskAdminToCreateLibrary": "라이브러리를 생성하려면 관리자에게 문의하십시오.", "LabelCorruptedFrames": "손상된 프레임:", @@ -1306,7 +1288,6 @@ "AllowFfmpegThrottlingHelp": "트랜스코딩이나 리먹스 작업이 현재 재생 중인 위치를 넘어 충분히 진행되면 리소스를 절약하기 위해 작업을 중지합니다. 이는 재생 구간을 자주 변경하지 않을 경우에 가장 적합합니다. 재생 시 문제가 발생하면 이 항목을 비활성화하십시오.", "AllowFfmpegThrottling": "트랜스코딩 시 스로틀링", "MessageLeaveEmptyToInherit": "상위 항목의 설정이나 전역 설정값을 그대로 적용하기 위해서는 공백으로 두십시오.", - "MessageInstallPluginFromApp": "이 플러그인은 사용할 앱 내부에서 설치해야 합니다.", "MessageImageTypeNotSelected": "드롭다운 메뉴에서 이미지 유형을 선택하십시오.", "MessageCreateAccountAt": "{0}에서 계정 만들기", "MessageConfirmRevokeApiKey": "정말 api 키를 무효화하시겠습니까? Jellyfin 서버와의 연결이 예고 없이 중단될 수 있습니다.", @@ -1315,7 +1296,6 @@ "MediaInfoRefFrames": "참조 프레임", "MediaInfoPixelFormat": "픽셀 형식", "MapChannels": "채널 매핑", - "LaunchWebAppOnStartupHelp": "서버가 처음 시작되면 웹 브라우저에서 웹 클라이언트를 실행하십시오. 서버 재시작의 경우에는 적용되지 않습니다.", "Large": "크게", "LanNetworksHelp": "대역폭을 강제로 제한할 때 로컬 네트워크로 간주되는 쉼표로 구분된 IP 주소 및 IP/서브넷 마스크 목록입니다. 지정될 경우 모든 다른 IP 주소는 외부 네트워크로 간주되며 외부 대역폭 제한이 적용됩니다. 공백일 경우 서버의 서브넷만이 로컬 네트워크로 간주됩니다.", "LabelffmpegPathHelp": "ffmpeg 실행 파일 혹은 ffmpeg를 포함하는 폴더 경로입니다.", @@ -1416,13 +1396,11 @@ "NoSubtitlesHelp": "자막을 자동으로 불러오지 않습니다. 재생 중에 수동으로 켤 수 있습니다.", "MusicLibraryHelp": "{0}음악 이름 지정 규칙{1}을 확인하십시오.", "MovieLibraryHelp": "{0}영화 이름 지정 규칙{1}을 확인하십시오.", - "MessageUnauthorizedUser": "현재 서버에 접속할 권한이 없습니다. 자세한 정보는 서버 관리자에게 문의하십시오.", "HeaderFavoritePlaylists": "즐겨찾는 플레이리스트", "ButtonTogglePlaylist": "플레이리스트", "ButtonToggleContextMenu": "더보기", "Rate": "평", "PerfectMatch": "정확히 일치", - "OtherArtist": "다른 아티스트", "ButtonSyncPlay": "SyncPlay", "HeaderDVR": "DVR", "EnableDecodingColorDepth10Vp9": "10비트 VP9하드웨어 디코딩 사용합니다", diff --git a/src/strings/lt-lt.json b/src/strings/lt-lt.json index 8599c332c5..4a259afc8e 100644 --- a/src/strings/lt-lt.json +++ b/src/strings/lt-lt.json @@ -99,7 +99,6 @@ "HeaderAddToPlaylist": "Pridėti į grojaraštį", "HeaderAddUser": "Pridėti vartotoją", "HeaderAdditionalParts": "Papildomos dalys", - "HeaderAutomaticUpdates": "Automatiniai atnaujinimai", "HeaderCancelRecording": "Atšaukti įrašymą", "HeaderCancelSeries": "Atšaukti laidą", "HeaderCastCrew": "Kūrėjai", @@ -161,8 +160,6 @@ "LabelAirsBeforeSeason": "Rodoma prieš sezoną:", "LabelAlbum": "Albumas:", "LabelAlbumArtists": "Albumo atlikėjai:", - "LabelAllowServerAutoRestart": "Leisti serveriui automatiškai persikrauti pritaikant atnaujinimus", - "LabelAllowServerAutoRestartHelp": "Serveris persikraus tik neveikimo metu, kai nebus aktyvus nei vienas vartotojas.", "LabelArtists": "Atlikėjai:", "LabelArtistsHelp": "Atskirti kelis naudojant ;", "LabelAudioLanguagePreference": "Garso kalbos pageidavimas:", @@ -289,7 +286,6 @@ "LabelUseNotificationServices": "Naudoti šias paslaugas:", "LabelUser": "Vartotojas:", "LabelYear": "Metai:", - "LabelYourFirstName": "Jūsų vardas:", "LabelYoureDone": "Baigta!", "LatestFromLibrary": "Vėliausi {0}", "LibraryAccessHelp": "Pasirinkite medijos aplankus, kuriuos norite dalintis su šiuo vartotoju. Administratoriai galės redaguoti visus aplankus per metaduomenų valdymą.", @@ -421,7 +417,6 @@ "Premieres": "Premieras", "Producer": "Prodiuseris", "ProductionLocations": "Filmavimo vietos", - "QueueAllFromHere": "Į eilę viską nuo čia", "RecentlyWatched": "Nesenai žiūrėta", "Record": "Įrašyti", "RecordSeries": "Įrašyti laidą", @@ -489,7 +484,6 @@ "TabNotifications": "Pranešimai", "TabOther": "Kita", "TabPassword": "Slaptažodis", - "TabPlaylist": "Grojaraštis", "TabProfile": "Profilis", "TabProfiles": "Profiliai", "TabRecordings": "Įrašai", @@ -561,7 +555,6 @@ "ButtonFullscreen": "Per visą ekraną", "ButtonGuide": "Gidas", "ButtonInfo": "Info", - "ButtonLearnMore": "Sužinoti daugiau", "ButtonLibraryAccess": "Mediatekos prieiga", "ButtonMore": "Daugiau", "ButtonNetwork": "Tinklas", @@ -581,7 +574,6 @@ "ButtonStart": "Pradėti", "ButtonUninstall": "Pašalinti", "ButtonUp": "Aukštyn", - "ButtonViewWebsite": "Žiūrėti svetainę", "ButtonWebsite": "Svetainė", "ChangingMetadataImageSettingsNewContent": "Metaduomenų ar iliustracijų pakeitimai bus pritaikyti tik naujai pridėtam turiniui. Norint pritaikyti pakeitimus esančiam turiniui reikės atnaujinti metaduomenis rankiniu būdu.", "Channels": "Kanalai", @@ -613,7 +605,6 @@ "AllLibraries": "Visos bibliotekos", "AllowMediaConversionHelp": "Leisti arba uždrausti medijos konvertavimą.", "AlwaysPlaySubtitles": "Visada rodyti subtitrus", - "AutoBasedOnLanguageSetting": "Auto (pagal kalbos parinktį)", "BookLibraryHelp": "Garso ir tekstinės knygos yra palaikomos. Peržiūrėkite {0} knygų vardinimo gidą {1}.", "ButtonEditOtherUserPreferences": "Keisti šio vartotojo profilį, paveikslą ir asmeninius nustatymus.", "ButtonResetEasyPassword": "Atstatyti pin kodą", @@ -644,7 +635,6 @@ "AllComplexFormats": "Visi Sudėtingi Formatai (ASS, SSA, VOBSUB, PGS, SUB/IDX, t.t.)", "AllowHWTranscodingHelp": "Leisti imtuvui perkoduoti srautus grojant. Tai gali sumažinti perkodavimus reikalingus serveriui.", "AuthProviderHelp": "Pasirinkite autentifikavimo paslaugos teikėją šio vartotojo slaptažodžio autentifikavimui.", - "AddItemToCollectionHelp": "Pridėkite įrašus į kolekciją. Suraskite įrašą, bei naudokite jo meniu, kad pridėti į kolekciją.", "AllowedRemoteAddressesHelp": "IP adresų atskirtų kableliais sąrašas ar IP/netmask įrašai tinklams, kurie turės teisę prisijungti nuotoliniu būdu. Visi adresai bus leidžiami, jei įrašas tuščias.", "HeaderMyMedia": "Mediateka", "HeaderMyDevice": "Mano įrenginys", @@ -817,7 +807,6 @@ "HeaderVideoQuality": "Vaizdo įrašo kokybė", "HeaderVideoType": "Video įrašo tipas", "HeaderVideoTypes": "Video tipai", - "LabelDashboardTheme": "Serverio puslapio tema:", "LabelDownloadLanguages": "Kalbos parsiuntimui:", "LabelDropShadow": "Mesti šešėlį:", "LabelEasyPinCode": "Greitas PID kodas:", @@ -1004,7 +993,6 @@ "HeaderFavoritePlaylists": "Mėgstami Grojaraščiai", "ApiKeysCaption": "Įjungtų API raktų sąrašas", "Episode": "Episodas", - "CopyStreamURLError": "Klaida kopijuojant URL.", "ClientSettings": "Kliento Nustatymai", "ButtonTogglePlaylist": "Grojaraštis", "ButtonToggleContextMenu": "Daugiau", diff --git a/src/strings/lv.json b/src/strings/lv.json index 4a0addb70c..9346b653f4 100644 --- a/src/strings/lv.json +++ b/src/strings/lv.json @@ -42,7 +42,6 @@ "MediaInfoStreamTypeSubtitle": "Subtitri", "MediaInfoStreamTypeData": "Dati", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Programmatūras", "MediaInfoSize": "Lielums", "MediaInfoResolution": "Izšķirtspēja", "MediaInfoProfile": "Profils", @@ -97,7 +96,6 @@ "LabelSportsCategories": "Sporta kategorijas:", "LabelSpecialSeasonsDisplayName": "Speciālās sezonas displeja nosaukums:", "LabelSource": "Avots:", - "LabelSoundEffects": "Skaņas efekti:", "LabelSortTitle": "Kārtošanas nosaukums:", "LabelSortOrder": "Kārtošanas secība:", "LabelSortBy": "Kārtot pēc:", @@ -349,16 +347,13 @@ "LiveBroadcasts": "Tiešraides:", "Live": "Tiešraide", "List": "Saraksts", - "LinksValue": "Linki: {0}", "Like": "Patīk", "LeaveBlankToNotSetAPassword": "Tu vari atstāt šo lauku tukšu, lai neiestatītu paroli.", - "LaunchWebAppOnStartup": "Palaist web interfeisu kad serveris tiek startēts", "LatestFromLibrary": "Jaunākais {0}", "Large": "Liels", "LabelffmpegPath": "FFmped ceļš:", "LabelZipCode": "Zip Kods:", "LabelYoureDone": "Esi pabeidzis!", - "LabelYourFirstName": "Tavs vārds:", "HeaderFavoritePeople": "Cilvēku Favorīti", "HeaderFavoriteMovies": "Filmu Favorīti", "HeaderFavoriteBooks": "Grāmatu Favorīti", @@ -388,7 +383,6 @@ "HeaderCancelSeries": "Atcelt Sēriju", "HeaderCancelRecording": "Atcelt Ierakstus", "HeaderBooks": "Grāmatas", - "HeaderAutomaticUpdates": "Automātiskie Atjauninājumi", "HeaderAudioSettings": "Audio Iestatījumi", "HeaderAudioBooks": "Audio Grāmatas", "HeaderApp": "Lietotne", @@ -473,7 +467,6 @@ "DatePlayed": "Atskaņošanas datums", "DateAdded": "Pievienošanas datums", "CriticRating": "Kritiķu reitings", - "CopyStreamURLError": "Kļūda kopējot URL.", "CopyStreamURLSuccess": "URL veiksmīgi nokopēts.", "CopyStreamURL": "Kopēt Straumes URL", "Continuing": "Turpina", @@ -490,7 +483,6 @@ "CancelSeries": "Atcelt sēriju", "CancelRecording": "Atcelt ierakstu", "ButtonWebsite": "Web vietne", - "ButtonViewWebsite": "Skatīt web vietni", "ButtonUninstall": "Atinstalēt", "ButtonTrailer": "Treileri", "ButtonSubtitles": "Subtitri", @@ -532,7 +524,6 @@ "ButtonNetwork": "Tīkls", "ButtonMore": "Vairāk", "ButtonLibraryAccess": "Bibliotēku piekļuve", - "ButtonLearnMore": "Uzzināt vairāk", "ButtonInfo": "Info", "ButtonHome": "Mājas", "ButtonHelp": "Palīdzība", @@ -594,7 +585,6 @@ "Banner": "Karogattēls", "Backdrops": "Foni", "Backdrop": "Fons", - "AutoBasedOnLanguageSetting": "Auto (atkarībā no valodas iestatījumiem)", "Auto": "Auto", "Audio": "Audio", "AttributeNew": "Jauns", @@ -664,7 +654,6 @@ "Watched": "Skatīts", "ViewPlaybackInfo": "Skatīt atskaņošanas info", "ViewAlbum": "Skatīt albumu", - "VideoRange": "Video platums", "Vertical": "Vertikāls", "ValueVideoCodec": "Video Kodeks: {0}", "ValueTimeLimitSingleHour": "Laika limits: 1 stunda", @@ -724,7 +713,6 @@ "TabProfile": "Profils", "TabPlugins": "Paplašinājumi", "TabPlaylists": "Atskaņošanas Saraksti", - "TabPlaylist": "Atskaņošanas Saraksts", "TabPlayback": "Atskaņošana", "TabPassword": "Parole", "TabParentalControl": "Vecāku Pārvaldība", @@ -839,7 +827,6 @@ "Menu": "Izvēlne", "LabelTriggerType": "Trigera Veids:", "LabelSkipIfGraphicalSubsPresent": "Izlaist ja video jau satur iegultus subtitrus", - "LabelSkin": "Izskats:", "LabelSimultaneousConnectionLimit": "Vienlaicīgo straumju limits:", "LabelServerHostHelp": "192.168.1.100:8096 vai https://myserver.com", "LabelServerHost": "Resursdators:", @@ -897,8 +884,6 @@ "LabelAudioCodec": "Audio kodeks:", "LabelAudioChannels": "Audio kanāli:", "LabelAudioBitrate": "Audio bitu-ātrums:", - "LabelAllowServerAutoRestartHelp": "Serveris restartēties tikai brīžos, kad neviens lietotājs nav aktīvs.", - "LabelAllowServerAutoRestart": "Atļaut serverim automātiski restartēties, lai uzstādītu atjauninājumus", "LabelAllowHWTranscoding": "Atļaut aparatūras trans-kodēšanu", "LabelAlbumArtMaxWidthHelp": "Maksimālā albumu vāku izšķirtspēja caur upnp:albumArtURI.", "LabelAlbumArtMaxWidth": "Albumu vāku maksimālais platums:", @@ -1025,7 +1010,6 @@ "ErrorGettingTvLineups": "Notika kļūda lejupielādējot TV sarakstus. Lūdzu pārliecinies, ka tava informācija ir pareiza un mēģini vēlreiz.", "DisplayMissingEpisodesWithinSeasonsHelp": "Tam arī jābūt iespējotam priekš TV bibliotēkām servera konfigurācijā.", "DefaultMetadataLangaugeDescription": "Šie ir jūsu noklusējumi, kas var tikt rediģēti atkarībā no bibliotēkas.", - "AddItemToCollectionHelp": "Pievieno vienumus kolekcijām tos meklējot un izmantojot to labā taustiņa vai spiediena izvēlnes lai pievienotu tos.", "LabelPleaseRestart": "Izmaiņas tiks pielietotas pēc manuālas web klienta pārlādes.", "LabelPersonRole": "Loma:", "LabelMusicStreamingTranscodingBitrateHelp": "Iestati maksimālo mūzikas straumēšanas bitu ātrumu.", @@ -1099,7 +1083,6 @@ "LabelDownMixAudioScale": "Audio pastiprinājums lejupmiksējot:", "LabelDisplayMissingEpisodesWithinSeasons": "Rādīt trūkstošās epizodes sezonās", "LabelDateAddedBehaviorHelp": "Ja atrodas metadatu vērtība, tā vienmēr tiks izmantota pirms jebkuras no šīm opcijām.", - "LabelDashboardTheme": "Servera vadības paneļa tēma:", "LabelCustomDeviceDisplayNameHelp": "Ievadi pielāgotu displeja vārdu vai atstāj tukšu lai izmantotu ierīces noteikto.", "LabelCachePathHelp": "Nosaki pielāgotu atrašanās vietu priekš keša datnēm kā attēliem. Atstāj tukšu lai izmantotu servera noklusējumu.", "LabelAllowedRemoteAddressesMode": "Attālās IP adreses filtra režīms:", @@ -1146,7 +1129,6 @@ "PackageInstallCompleted": "{0} (versija {1}) instalācija pabeigta.", "PackageInstallCancelled": "{0} (versija {1}) instalācija atcelta.", "Overview": "Pārskats", - "OtherArtist": "Cits izpildītājs", "OriginalAirDateValue": "Oriģinālais tiešraides datums: {0}", "OptionWeekly": "Iknedēļu", "OptionWeekends": "Nedēļas nogalēs", @@ -1224,8 +1206,6 @@ "MoreUsersCanBeAddedLater": "Papildus lietotāji var tikt pievienoti vēlāk no vadības paneļa.", "MessagePluginConfigurationRequiresLocalAccess": "Lai konfigurētu šo paplašinājumu lūdzu tieši ieej savā lokālajā serverī.", "MessagePleaseEnsureInternetMetadata": "Lūdzu pārliecinies vai metadatu lejupielāde no interneta ir iespējota.", - "MessageUnauthorizedUser": "Jūs neesat autorizēti lai piekļūtu serverim šajā brīdī. Lūdzu sazinieties ar savu servera administratoru priekš papildus informācijas.", - "MessageInstallPluginFromApp": "Šis paplašinājums ir jāuzstāda no lietotnes, kurā jūs to vēlaties izmantot.", "LabelEmbedAlbumArtDidl": "Ievietot albumu vākus iekš Didl", "LabelSelectFolderGroups": "Automātiski grupēt saturu no sekojošām datnēm skatos kā Filmas, Mūzika un TV:", "AllowFfmpegThrottlingHelp": "Kad trans-kodējums vai remux tiek pietiekami tālu priekšā pašreizējai atskaņošanas vietai, process tiks pauzēts lai patērētu mazāk resursu. Tas ir noderīgākais skatoties bez biežas pārlēkšanas. Atspējo šo ja saskaries ar atskaņošanas problēmām.", diff --git a/src/strings/mr.json b/src/strings/mr.json index 37cbc403de..9ad45d4765 100644 --- a/src/strings/mr.json +++ b/src/strings/mr.json @@ -6,7 +6,6 @@ "ButtonNew": "नवीन", "ButtonNetwork": "नेटवर्क", "ButtonMore": "अजून", - "ButtonLearnMore": "अधिक माहिती", "ButtonInfo": "माहिती", "ButtonHelp": "मदत", "ButtonGuide": "गाईड", @@ -80,7 +79,6 @@ "Categories": "वर्ग", "CancelRecording": "रेकॉर्डिंग रद्द करा", "ButtonWebsite": "संकेतस्थळ", - "ButtonViewWebsite": "संकेतस्थळ पाहा", "ButtonUp": "वर", "ButtonTrailer": "ट्रेलर", "ButtonSubtitles": "सबटायटल", diff --git a/src/strings/ms.json b/src/strings/ms.json index db01453815..a8a5bba2ee 100644 --- a/src/strings/ms.json +++ b/src/strings/ms.json @@ -13,7 +13,6 @@ "AccessRestrictedTryAgainLater": "Akses dihalang pada masa ini. Sila cuba sebentar lagi.", "Actor": "Pelakon", "Add": "Tambah", - "AddItemToCollectionHelp": "Tambah item ke koleksi melalui carian dan menggunakan menu klik kanan atau ketik menu tersebut untuk menambahkan ke koleksi.", "AddToCollection": "Tambah ke dalam koleksi", "AddToPlayQueue": "Tambah ke giliran main", "AddToPlaylist": "Tambah pada senarai main", @@ -49,7 +48,6 @@ "AttributeNew": "Terbaru", "Audio": "Audio", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (berdasar tetapan bahasa)", "Backdrop": "Latar belakang", "Backdrops": "Latar belakang", "BirthDateValue": "Lahir: {0}", @@ -73,7 +71,6 @@ "ButtonChangeServer": "Tukar pelayan", "ButtonConnect": "Sambung", "ButtonLibraryAccess": "Akses pustaka", - "ButtonLearnMore": "Ketahui lebih lanjut", "ButtonInfo": "Info", "ButtonHome": "Mula", "ButtonHelp": "Pertolongan", diff --git a/src/strings/nb.json b/src/strings/nb.json index 8df80afaa8..ee7f9e9ea5 100644 --- a/src/strings/nb.json +++ b/src/strings/nb.json @@ -2,7 +2,6 @@ "Absolute": "Absolutt", "Actor": "Skuespiller", "Add": "Legg til", - "AddItemToCollectionHelp": "Legg til elementer i samlinger ved å søke etter dem og bruke deres høyreklikk eller peke-menyer for å legge dem til en samling.", "AddToCollection": "Legg til i samling", "AddToPlayQueue": "Legg til i avspillingskø", "AddToPlaylist": "Legg til i spilleliste", @@ -60,7 +59,6 @@ "ButtonGotIt": "Skjønner", "ButtonHelp": "Hjelp", "ButtonHome": "Hjem", - "ButtonLearnMore": "Lær mer", "ButtonLibraryAccess": "Bibliotektilgang", "ButtonManualLogin": "Manuell Login", "ButtonMore": "Mer", @@ -101,7 +99,6 @@ "ButtonSubtitles": "Undertekster", "ButtonUninstall": "Avinstaller", "ButtonUp": "Opp", - "ButtonViewWebsite": "Vis nettsted", "ButtonWebsite": "Nettsted", "CancelRecording": "Avbryt opptak", "CancelSeries": "Avbryt serie", @@ -211,7 +208,6 @@ "HeaderApiKeysHelp": "Eksterne programmer trenger en API-nøkkel for å kunne kommunisere med Jellyfin-serveren. Nøklene utstedes ved å logge på med en Jellyfin-konto, eller ved å manuelt gi programmet en nøkkel.", "HeaderAudioBooks": "Lydbøker", "HeaderAudioSettings": "Lydinnstillinger", - "HeaderAutomaticUpdates": "Automatiske oppdateringer", "HeaderBooks": "Bøker", "HeaderBranding": "Merking", "HeaderCancelRecording": "Avbryt opptak", @@ -402,8 +398,6 @@ "LabelAlbumArtists": "Albumartister:", "LabelAll": "Alle", "LabelAllowHWTranscoding": "Tillat maskinvare-omkoding", - "LabelAllowServerAutoRestart": "Tillat at serveren restartes automatisk for å installere oppdateringer", - "LabelAllowServerAutoRestartHelp": "Serveren vil kun restartes i inaktive perioder når ingen brukere er aktive.", "LabelAppName": "Applikasjonsnavn", "LabelAppNameExample": "Eksempel: Sickbeard, Sonarr", "LabelArtists": "Artister:", @@ -662,7 +656,6 @@ "LabelVersionInstalled": "{0} installert", "LabelXDlnaCapHelp": "Bestemmer innholdet i X_DLNACAP-elementet i urn:schemas-dlna-org:device-1-0-domenet.", "LabelXDlnaDocHelp": "Bestemmer innholdet i X_DLNADOC-elementet i urn:schemas-dlna-org:device-1-0-domenet.", - "LabelYourFirstName": "Fornavnet ditt:", "LabelYoureDone": "Du er ferdig!", "LabelZipCode": "Postnummer:", "LabelffmpegPath": "Filbane til FFmpeg:", @@ -717,7 +710,6 @@ "MessageFileReadError": "En feil oppstod når filen skulle leses. Vennligst prøv igjen.", "MessageForgotPasswordFileCreated": "Følgende fil er opprettet på serveren og inneholder instruksjoner om hvordan du kan fortsette:", "MessageForgotPasswordInNetworkRequired": "Vennligst prøv igjen fra hjemmenettverket ditt for å starte prosessen med å gjenopprette passordet ditt.", - "MessageInstallPluginFromApp": "Dette programtillegget må installeres direkte i appen du har tenkt å bruke den i.", "MessageInvalidForgotPasswordPin": "Ugyldig eller utgått PIN kode angitt. Vennligst prøv igjen.", "MessageInvalidUser": "Ugyldig brukernavn eller passord. Vennligst prøv igjen.", "MessageItemSaved": "Element lagret.", @@ -921,7 +913,6 @@ "ProductionLocations": "Produksjonslokasjoner", "Programs": "Programmer", "Quality": "Kvalitet", - "QueueAllFromHere": "Sett alt herfra i kø", "Rate": "Vurdér", "RecentlyWatched": "Nylig sett", "RecommendationBecauseYouLike": "Fordi du liker {0}", @@ -1022,7 +1013,6 @@ "TabParentalControl": "Foreldrekontroll", "TabPassword": "Passord", "TabPlayback": "Avspilling", - "TabPlaylist": "Spilleliste", "TabPlaylists": "Spillelister", "TabPlugins": "Programtillegg", "TabProfile": "Profil", @@ -1074,7 +1064,6 @@ "ValueTimeLimitMultiHour": "Tidsgrense: {0} timer", "ValueTimeLimitSingleHour": "Tidsgrense: 1 time", "ValueVideoCodec": "Videokodek: {0}", - "VideoRange": "Videoområde", "ViewAlbum": "Vis album", "ViewPlaybackInfo": "Vis avspillingsinformasjon", "Watched": "Sett", @@ -1115,7 +1104,6 @@ "Banner": "Banner", "Backdrops": "Bakgrunner", "Backdrop": "Bakgrunn", - "AutoBasedOnLanguageSetting": "Automatisk (basert på språkinstillingene)", "Ascending": "Stigende", "Art": "Omslagsbilde", "AnyLanguage": "Hvilket som helst språk", @@ -1190,7 +1178,6 @@ "LabelDateTimeLocale": "Datoformat:", "LabelType": "Type:", "Large": "Stor", - "MediaInfoSoftware": "Programvare", "DirectStreamHelp1": "Mediet støttes av enheten med tanke på oppløsning og medietype (H.264, AC3, osv), men den støtter ikke filkontaineren (mkv, avi, wmv, osv). Videoen vil ompakkes fortløpende før den sendes til enheten.", "EnableBackdrops": "Bakgrunner", "EnableThemeVideos": "Temavideoer", @@ -1225,7 +1212,6 @@ "LabelAllowedRemoteAddressesMode": "Modus for filter for eksterne IP-adresser:", "LabelDiscNumber": "Platenummer:", "LabelDisplayLanguage": "Visningsspråk:", - "LinksValue": "Linker: {0}", "OptionAuto": "Automatisk", "OptionAutomatic": "Automatisk", "OptionHomeVideos": "Fotografier", @@ -1250,8 +1236,6 @@ "LabelSkipForwardLength": "Lengde for fremoverhopp:", "LabelTriggerType": "Utløsertype:", "LanNetworksHelp": "Kommaseparert liste over IP-adresser eller IP/nettverksmaske for nettverk som skal regnes som lokalt nettverk når båndbreddebegrensninger skal håndheves. Hvis satt, vil alle andre IP-adresser bli regnet for å være på eksternt nettverk og vil dermed være underlagt båndbreddebegrensningene for eksterne nettverk. Hvis tomt, vil kun serverens subnettverk bli regnet for å være på det lokale nettverket.", - "LaunchWebAppOnStartup": "Start web-grensesnittet når serveren starter", - "LaunchWebAppOnStartupHelp": "Åpne web-klienten i din standard nettleser når serveren starter opp. Dette vil ikke skje ved omstart av serveren.", "LearnHowYouCanContribute": "Finn ut hvordan du kan bidra.", "SeriesYearToPresent": "{0} - Nå", "LabelBaseUrlHelp": "Legger til en egendefinert undermappe til serverens nettadresse. For eksempel: http://example.com/<baseurl>", @@ -1312,7 +1296,6 @@ "LabelBurnSubtitles": "Brenn inn undertekst:", "LabelCache": "Mellomlagring:", "LabelCustomCertificatePathHelp": "Filbanen til en PKCS#12-fil med et sertifikat og privatnøkkel for å aktivere TLS-støtte på et eget domene.", - "LabelDashboardTheme": "Tema for serveroversikt:", "LabelDefaultScreen": "Standardskjerm:", "LabelDropShadow": "Underskygge:", "LabelDynamicExternalId": "{0} ID:", @@ -1330,8 +1313,6 @@ "LabelServerName": "Servernavn:", "LabelSimultaneousConnectionLimit": "Begrensing på samtidige strømmer:", "LabelSize": "Størrelse:", - "LabelSkin": "Utseende:", - "LabelSoundEffects": "Lydeffekter:", "LabelSpecialSeasonsDisplayName": "Visningsnavn for spesialsesong:", "LabelStatus": "Status:", "LabelSubtitleDownloaders": "Kilder for undertekst:", @@ -1398,7 +1379,6 @@ "Playlists": "Spillelister", "Previous": "Forrige", "Primary": "Primær", - "RunAtStartup": "Kjør ved oppstart", "SaveSubtitlesIntoMediaFolders": "Lagre undertekster i mediemapper", "SaveSubtitlesIntoMediaFoldersHelp": "Lagring av undertekster ved siden av videofilene vil gjøre det lettere å behandle dem.", "Screenshot": "Skjermbilde", @@ -1444,7 +1424,6 @@ "SelectAdminUsername": "Vennligst velg et brukernavn for administrator-kontoen.", "HeaderNavigation": "Navigering", "MessageConfirmAppExit": "Vil du avslutte?", - "CopyStreamURLError": "Det var en feil under kopiering av URL'en.", "LabelVideoResolution": "Oppløsning på video:", "LabelPlayerDimensions": "Dimensjoner på avspiller:", "LabelCorruptedFrames": "Korrupte bilder:", @@ -1495,11 +1474,9 @@ "ShowLess": "Vis mindre", "Season": "Sesong", "SaveChanges": "Lagre endringer", - "ReleaseGroup": "Utgivelsesgruppe", "PreferEmbeddedEpisodeInfosOverFileNames": "Foretrekk innebygd episodeinformasjon framfor filnavn", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dette bruker episodeinformasjonen fra innebygd metadata hvis tilgjengelig.", "Person": "Person", - "OtherArtist": "Annen artist", "Movie": "Film", "MessageSyncPlayErrorMedia": "Kunne ikke aktivere SyncPlay! Mediefeil.", "MessageSyncPlayErrorMissingSession": "Kunne ikke aktivere SyncPlay! Mangler sesjon.", @@ -1516,7 +1493,6 @@ "MessageSyncPlayUserJoined": "{0} har blitt med i gruppen.", "MessageSyncPlayDisabled": "SyncPlay deaktivert.", "MessageSyncPlayEnabled": "SyncPlay aktivert.", - "MessageUnauthorizedUser": "Du har ikke autorisert tilgang til serveren akkurat nå. Vennligst kontakt serveradministratoren din for mer informasjon.", "LabelSyncPlayAccess": "SyncPlay-tilgang", "LabelSyncPlayAccessNone": "Deaktivert for denne brukeren", "LabelSyncPlayAccessJoinGroups": "Tillat brukeren å bli med i grupper", @@ -1531,7 +1507,6 @@ "LabelSyncPlayTimeOffset": "Tidsforskyvning mot serveren:", "LabelRequireHttpsHelp": "Hvis valgt, vil serveren automatisk omdirigere alle HTTP-forespørsler til HTTPS. Dette har ingen effekt dersom serveren ikke lytter etter HTTPS.", "LabelRequireHttps": "Krev HTTPS", - "LabelNightly": "Nattlig", "LabelStable": "Stabil", "LabelChromecastVersion": "Chromecast-versjon", "LabelEnableHttpsHelp": "Aktiverer at serveren skal lytte på den valgte HTTPS-porten. Et gyldig sertifikat må også være konfigurert for at dette skal tre i kraft.", diff --git a/src/strings/nl.json b/src/strings/nl.json index c27ea0ef96..39b2ea1b5e 100644 --- a/src/strings/nl.json +++ b/src/strings/nl.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "Toegang is momenteel beperkt, probeer later opnieuw.", "Actor": "Acteur", "Add": "Toevoegen", - "AddItemToCollectionHelp": "Voeg items aan uw collecties toe door te zoeken en gebruik rechts klikken met de muis of tik op menu's om ze toe te voegen aan een verzameling.", "AddToCollection": "Toevoegen aan Collectie", "AddToPlayQueue": "Toevoegen aan wachtrij", "AddToPlaylist": "Toevoegen aan afspeellijst", @@ -38,7 +37,6 @@ "AttributeNew": "Nieuw", "Audio": "Geluid", "Auto": "Automatisch", - "AutoBasedOnLanguageSetting": "Automatisch (gebaseerd op taal instelling)", "Backdrop": "Achtergrond", "Backdrops": "Achtergronden", "BirthDateValue": "Geboren: {0}", @@ -77,7 +75,6 @@ "ButtonGuide": "Gids", "ButtonHelp": "Hulp", "ButtonHome": "Start", - "ButtonLearnMore": "Meer informatie", "ButtonLibraryAccess": "Bibliotheek toegang", "ButtonManualLogin": "Handmatige Aanmelding", "ButtonMore": "Meer", @@ -118,7 +115,6 @@ "ButtonSubmit": "Uitvoeren", "ButtonSubtitles": "Ondertiteling", "ButtonUp": "Omhoog", - "ButtonViewWebsite": "Bekijk website", "CancelRecording": "Opname annuleren", "CancelSeries": "Annuleer series", "Categories": "Categorieën", @@ -276,7 +272,6 @@ "HeaderAppearsOn": "Verschijnt op", "HeaderAudioBooks": "Luisterboeken", "HeaderAudioSettings": "Audio Instellingen", - "HeaderAutomaticUpdates": "Automatische updates", "HeaderBlockItemsWithNoRating": "Blokkeer items met geen of niet herkende beoordelingsinformatie:", "HeaderBooks": "Boeken", "HeaderBranding": "Huisstijl", @@ -480,8 +475,6 @@ "LabelAlbumArtists": "Album artiesten:", "LabelAll": "Alles", "LabelAllowHWTranscoding": "Hardware transcoding toestaan", - "LabelAllowServerAutoRestart": "Automatisch herstarten van de server toestaan om updates toe te passen", - "LabelAllowServerAutoRestartHelp": "De server zal alleen opnieuw opstarten tijdens inactieve perioden, wanneer er geen gebruikers actief zijn.", "LabelAllowedRemoteAddresses": "Externe IP-adressen filter:", "LabelAllowedRemoteAddressesMode": "Externe IP-adressen filter modus:", "LabelAppName": "Applicatie Naam", @@ -517,7 +510,6 @@ "LabelCustomDeviceDisplayName": "Weergave naam:", "LabelCustomDeviceDisplayNameHelp": "Geef een eigen weergave naam op of laat deze leeg om de naam te gebruiken die het apparaat opgeeft.", "LabelCustomRating": "Aangepaste classificatie:", - "LabelDashboardTheme": "Server dashboard thema:", "LabelDateAdded": "Datum toegevoegd:", "LabelDateAddedBehavior": "Datum toegevoegd gedrag voor nieuwe content:", "LabelDateAddedBehaviorHelp": "Als metadata gegevens aanwezig zijn hebben deze voorrang op deze opties.", @@ -731,7 +723,6 @@ "LabelSortBy": "Sorteren op:", "LabelSortOrder": "Sorteer volgorde:", "LabelSortTitle": "Sorteer titel:", - "LabelSoundEffects": "Geluidseffecten:", "LabelSource": "Bron:", "LabelSpecialSeasonsDisplayName": "De weergavenaam van een speciaal seizoen:", "LabelSportsCategories": "Sport categorieën:", @@ -774,7 +765,6 @@ "LabelXDlnaCapHelp": "Bepaalt de inhoud van het X_DLNACAP element in de urn: schemas-dlna-org:device-1-0 namespace.", "LabelXDlnaDocHelp": "Bepaalt de inhoud van het X_DLNADOC element in de urn:schemas-dlna-org:device-1-0 namespace.", "LabelYear": "Jaar:", - "LabelYourFirstName": "Uw voornaam:", "LabelYoureDone": "Gereed!", "LabelZipCode": "Postcode:", "LabelffmpegPath": "FFmpeg pad:", @@ -832,7 +822,6 @@ "MessageFileReadError": "Er is een fout opgetreden bij het lezen van het bestand. Probeer het opnieuw.", "MessageForgotPasswordFileCreated": "Het volgende bestand is gecreëerd op uw server en bevat instructies om verder te gaan:", "MessageForgotPasswordInNetworkRequired": "Probeer de wachtwoord herstel procedure opnieuw vanuit uw thuisnetwerk.", - "MessageInstallPluginFromApp": "Deze plugin moet geïnstalleerd worden vanuit de app waarin u het wilt gebruiken.", "MessageInvalidForgotPasswordPin": "Er is een ongeldige of verlopen pincode ingegeven. Probeer opnieuw.", "MessageInvalidUser": "Incorrecte gebruikersnaam of wachtwoord. Probeer opnieuw.", "MessageItemSaved": "Item opgeslagen.", @@ -1054,7 +1043,6 @@ "ProductionLocations": "Productie Locaties", "Programs": "Programma's", "Quality": "Kwaliteit", - "QueueAllFromHere": "Plaats in de wachtrij vanaf hier", "Raised": "Verhoogd", "Rate": "Waardeer", "RecentlyWatched": "Onlangs bekeken", @@ -1085,7 +1073,6 @@ "ReplaceExistingImages": "Bestaande afbeeldingen vervangen", "ResumeAt": "Hervatten vanaf {0}", "Rewind": "Terugspoelen", - "RunAtStartup": "Uitvoeren bij opstarten", "Runtime": "Speelduur", "Saturday": "Zaterdag", "Save": "Opslaan", @@ -1171,7 +1158,6 @@ "TabParentalControl": "Ouderlijk toezicht", "TabPassword": "Wachtwoord", "TabPlayback": "Afspelen", - "TabPlaylist": "Afspeellijst", "TabPlaylists": "Afspeellijst", "TabProfile": "Profiel", "TabProfiles": "Profielen", @@ -1228,7 +1214,6 @@ "ValueTimeLimitMultiHour": "Tijdslimiet: {0} uren", "ValueTimeLimitSingleHour": "Tijdslimiet: 1 uur", "Vertical": "Verticaal", - "VideoRange": "Videobereik", "ViewAlbum": "Bekijk album", "ViewPlaybackInfo": "Bekijk afspelen info", "Watched": "Bekeken", @@ -1302,7 +1287,6 @@ "LabelProfileVideoCodecs": "Video codecs:", "LabelProtocolInfo": "Protocol info:", "LabelServerName": "Server naam:", - "LabelSkin": "Uiterlijk:", "ButtonAddImage": "Voeg afbeelding toe", "LabelSize": "Grootte:", "CopyStreamURLSuccess": "URL succesvol gekopieerd.", @@ -1317,7 +1301,6 @@ "LabelBaseUrl": "Basis URL:", "LabelTranscodingProgress": "Transcoderen voortgang:", "LabelTriggerType": "Signaal Type:", - "LaunchWebAppOnStartup": "Lanceer de web interface wanneer de server start", "MediaInfoBitrate": "Bitrate", "MediaInfoInterlaced": "Interlaced", "ValueSeriesCount": "{0} series", @@ -1399,7 +1382,6 @@ "LabelTranscodes": "Transcoderen:", "DashboardOperatingSystem": "Besturingssysteem: {0}", "LabelWeb": "Web:", - "LaunchWebAppOnStartupHelp": "Open de web cliënt in uw standaard browser wanneer de server voor de eerste keer start. Dit zal niet voorkomen tijdens gebruik van de server herstart functie.", "LeaveBlankToNotSetAPassword": "U kunt dit veld leeg laten om geen wachtwoord in te stellen.", "DashboardServerName": "Server: {0}", "LabelVideoBitrate": "Video bitrate:", @@ -1412,13 +1394,11 @@ "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeSubtitle": "Ondertiteling", "MediaInfoStreamTypeVideo": "Video", - "LinksValue": "Links: {0}", "Logo": "Logo", "MediaInfoCodecTag": "Codec tag", "MediaInfoContainer": "Container", "MediaInfoFramerate": "Beeldverversing", "MediaInfoRefFrames": "Ref beeld", - "MediaInfoSoftware": "Software", "MessageImageFileTypeAllowed": "Alleen JPEG en PNG bestanden worden ondersteund.", "MessageImageTypeNotSelected": "Selecteer een afbeelding type van het menu alstublieft .", "MessageNoCollectionsAvailable": "Collecties staan u toe om te genieten van gepersonaliseerde groeperingen van Films, Series en Albums te maken. Klik op de + knop om te beginnen met het maken van collecties.", @@ -1443,7 +1423,6 @@ "AlbumArtist": "Album Artiest", "Album": "Album", "DeinterlaceMethodHelp": "Selecteer de deinterlacingmethode die u wilt gebruiken bij het transcoderen van geïnterlinieerde inhoud.", - "CopyStreamURLError": "Er trad een fout op tijdens het kopieren van de URL.", "ClientSettings": "Client instellingen", "ButtonSplit": "Splitsen", "BoxSet": "Box Set", @@ -1460,16 +1439,13 @@ "HeaderNavigation": "Navigeren", "Episode": "Aflevering", "Season": "Seizoen", - "ReleaseGroup": "Uitgave groep", "PreferEmbeddedEpisodeInfosOverFileNames": "Verkies ingeladen afleveringsinformatie boven bestandsnaam", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dit gebruikt de afleveringsinformatie van de ingeladen metadata als deze aanwezig is.", "PlaybackErrorNoCompatibleStream": "Deze machine is niet leesbaar met de media en de server verstuurd geen leesbare media formaten.", "Person": "Persoon", - "OtherArtist": "Andere Artiesten", "OptionForceRemoteSourceTranscoding": "Forceer het transcoderen van op afstand bediende media bronnen (zoals LiveTV)", "NoCreatedLibraries": "Het lijkt erop dat er geen bibliotheek is gecreëerd. {0}Wilt u er nu een aanmaken?{1}", "Movie": "Film", - "MessageUnauthorizedUser": "U bent niet gemachtigd om toegang tot de server te krijgen op dit moment. Neem contact op met de server beheerder voor meer informatie.", "MessageConfirmAppExit": "Wilt u afsluiten?", "LabelVideoResolution": "Video resolutie:", "LabelStreamType": "Stream type:", @@ -1529,7 +1505,6 @@ "MillisecondsUnit": "ms", "LabelSyncPlayTimeOffset": "Tijd offset met de server:", "LabelRequireHttps": "HTTPS verplichten", - "LabelNightly": "Nightly", "LabelStable": "Stabiel", "LabelChromecastVersion": "Chromecast versie", "LabelEnableHttpsHelp": "Hiermee kan de server luisteren op de geconfigureerde HTTPS-poort. Om dit te laten werken moet ook een geldig certificaat worden geconfigureerd.", diff --git a/src/strings/pl.json b/src/strings/pl.json index c77e0ac1b8..b4612c14af 100644 --- a/src/strings/pl.json +++ b/src/strings/pl.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "Dostęp jest aktualnie ograniczony. Spróbuj ponownie później.", "Actor": "Aktor", "Add": "Dodaj", - "AddItemToCollectionHelp": "Dodaj obiekty do kolekcji wyszukując je i użyj prawy przycisk myszy lub dotknij menu, aby dodać je do kolekcji.", "AddToCollection": "Dodaj do kolekcji", "AddToPlayQueue": "Dodaj do kolejki odtwarzania", "AddToPlaylist": "Dodaj do listy", @@ -40,7 +39,6 @@ "AttributeNew": "Nowy", "Audio": "Dźwięk", "Auto": "Automatycznie", - "AutoBasedOnLanguageSetting": "Automatyczna (w oparciu o ustawienia językowe)", "Backdrop": "Fototapeta", "Backdrops": "Fototapety", "Banner": "Baner", @@ -84,7 +82,6 @@ "ButtonHelp": "Pomoc", "ButtonHome": "Start", "ButtonInfo": "Informacje", - "ButtonLearnMore": "Dowiedz się więcej", "ButtonLibraryAccess": "Dostęp do biblioteki", "ButtonManualLogin": "Logowanie manualne", "ButtonMore": "Więcej", @@ -128,7 +125,6 @@ "ButtonTrailer": "Zwiastun", "ButtonUninstall": "Odinstaluj", "ButtonUp": "Góra", - "ButtonViewWebsite": "Odwiedź stronę", "ButtonWebsite": "Strona WWW", "CancelRecording": "Anuluj nagranie", "CancelSeries": "Anuluj nagrywanie serialu", @@ -292,7 +288,6 @@ "HeaderAppearsOn": "Występuje", "HeaderAudioBooks": "Książka mówiona", "HeaderAudioSettings": "Ustawienia dźwięku", - "HeaderAutomaticUpdates": "Aktualizacje", "HeaderBlockItemsWithNoRating": "Blokuj pozycje z brakującą lub nierozpoznaną kategorią wiekową:", "HeaderBooks": "Książki", "HeaderBranding": "Dostosowywanie", @@ -513,8 +508,6 @@ "LabelAlbumArtists": "Wykonawcy albumów:", "LabelAll": "Wszystkie", "LabelAllowHWTranscoding": "Zezwalaj na sprzętowe transkodowanie", - "LabelAllowServerAutoRestart": "Zezwalaj na ponowne uruchomienie serwera, w celu instalacji aktualizacji", - "LabelAllowServerAutoRestartHelp": "Serwer będzie ponownie uruchamiany tylko w trakcie bezczynności, kiedy nie ma aktywnych użytkowników.", "LabelAllowedRemoteAddresses": "Filtr adresów IP:", "LabelAllowedRemoteAddressesMode": "Tryb filtra adresów IP:", "LabelAppName": "Nazwa Aplikacji", @@ -552,7 +545,6 @@ "LabelCustomDeviceDisplayName": "Nazwa wyświetlana:", "LabelCustomDeviceDisplayNameHelp": "Dostarcz własną nazwę wyświetlania lub zostaw puste i użyj nazwy dostarczonej przez urządzenie.", "LabelCustomRating": "Kategoria wiekowa własna:", - "LabelDashboardTheme": "Motyw kokpitu serwera:", "LabelDateAdded": "Data dodania:", "LabelDateAddedBehavior": "Data dodania dla nowej zawartości:", "LabelDateAddedBehaviorHelp": "Jeśli istnieją metadane będą one użyte zawsze przed którąkolwiek z tych opcji.", @@ -763,7 +755,6 @@ "LabelServerHost": "Serwer:", "LabelServerHostHelp": "192.168.1.100:8096 or https://myserver.com", "LabelSimultaneousConnectionLimit": "Limit jednoczesnych transmisji:", - "LabelSkin": "Skóra:", "LabelSkipBackLength": "Długość skoku wstecz:", "LabelSkipForwardLength": "Długość skoku wprzód:", "LabelSkipIfAudioTrackPresent": "Pomijaj, jeżeli domyślna ścieżka dźwiękowa jest w języku pobierania", @@ -775,7 +766,6 @@ "LabelSortBy": "Sortuj po:", "LabelSortOrder": "Porządek sortowania:", "LabelSortTitle": "Tytuł sortowania:", - "LabelSoundEffects": "Efekty dźwiękowe:", "LabelSource": "Źródło:", "LabelSpecialSeasonsDisplayName": "Nazwa sezonu odcinków specjalnych:", "LabelSportsCategories": "Kategorie wydarzeń sportowych:", @@ -827,7 +817,6 @@ "LabelXDlnaCapHelp": "Określa zawartość elementu X_DLNACAP w przestrzeni nazw urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Określa zawartość elementu X_DLNADOC w przestrzeni nazw urn:schemas-dlna-org:device-1-0.", "LabelYear": "Rok:", - "LabelYourFirstName": "Twoje imię:", "LabelYoureDone": "Zakończono!", "LabelZipCode": "Kod pocztowy:", "LabelffmpegPath": "Folder aplikacji FFmpeg:", @@ -838,7 +827,6 @@ "LearnHowYouCanContribute": "Dowiedz się jak możesz pomóc.", "LibraryAccessHelp": "Wybierz biblioteki udostępniane temu użytkownikowi. Administratorzy będą mogli edytować wszystkie foldery używając menedżera metadanych.", "Like": "Lubię", - "LinksValue": "Łącza: {0}", "List": "Lista", "Live": "Na żywo", "LiveBroadcasts": "Transmisje na żywo", @@ -895,7 +883,6 @@ "MessageFileReadError": "Podczas wczytywania plików wystąpił błąd. Spróbuj ponownie później.", "MessageForgotPasswordFileCreated": "Plik zawierający instrukcje z dalszymi krokami został utworzony na serwerze:", "MessageForgotPasswordInNetworkRequired": "Spróbuj ponownie zainicjować czyszczenie hasła, tym razem używając swojej sieci domowej.", - "MessageInstallPluginFromApp": "Wtyczka musi być zainstalowana bezpośrednio z aplikacji, w której ma być używana.", "MessageInvalidForgotPasswordPin": "Nieprawidłowy lub wygasły PIN został wpisany. Proszę spróbować ponownie.", "MessageInvalidUser": "Nieprawidłowa nazwa użytkownika lub hasło. Spróbuj ponownie.", "MessageItemSaved": "Obiekt zapisany.", @@ -1135,7 +1122,6 @@ "ProductionLocations": "Kraje", "Programs": "Programy", "Quality": "Jakość", - "QueueAllFromHere": "Kolejkuj wszystko z tej lokalizacji", "Raised": "Wypukły", "Rate": "Oceń", "RecentlyWatched": "Ostatnio obejrzane", @@ -1166,7 +1152,6 @@ "ReplaceExistingImages": "Zastępuj istniejące obrazy", "ResumeAt": "Wznów odtwarzanie od {0}", "Rewind": "Do tyłu", - "RunAtStartup": "Uruchamiaj po starcie", "Runtime": "Czas trwania", "Saturday": "Sobota", "Save": "Zapisz", @@ -1262,7 +1247,6 @@ "TabParentalControl": "Kontrola rodzicielska", "TabPassword": "Hasło", "TabPlayback": "Odtwarzanie", - "TabPlaylist": "Lista odtwarzania", "TabPlaylists": "Listy odtwarzania", "TabPlugins": "Wtyczki", "TabProfile": "Profil", @@ -1331,7 +1315,6 @@ "ValueTimeLimitSingleHour": "Limit czasu: 1 godzina", "ValueVideoCodec": "Kodek wideo: {0}", "Vertical": "Wertykalny", - "VideoRange": "Zakres wideo", "ViewAlbum": "Podgląd albumu", "ViewPlaybackInfo": "Wyświetlaj informacje o odtwarzaniu", "Watched": "Obejrzany", @@ -1375,11 +1358,8 @@ "LabelWeb": "Sieć:", "LabelXDlnaCap": "Limit X-DLNA:", "LabelXDlnaDoc": "Dokumentacja X-DLNA:", - "LaunchWebAppOnStartup": "Uruchom aplikację w przeglądarce internetowej, gdy uruchomi się serwer Jellyfin", - "LaunchWebAppOnStartupHelp": "Otwóż aplikację internetową w domyślnej przeglądarce, gdy uruchomi się serwer. Nie nastąpi to w przypadku korzystania z funkcji ponownego uruchomienia serwera.", "LeaveBlankToNotSetAPassword": "Pozostaw puste, aby nie ustawiać hasła.", "Logo": "Logo", - "MediaInfoSoftware": "Oprogramowanie", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Dane", "MediaInfoStreamTypeEmbeddedImage": "Osadzony Obraz", @@ -1447,7 +1427,6 @@ "LabelPlayerDimensions": "Rozmiar odtwarzacza:", "LabelDroppedFrames": "Upuszczone klatki:", "LabelCorruptedFrames": "Uszkodzone klatki:", - "CopyStreamURLError": "Wystąpił błąd podczas kopiowania adresu URL.", "AskAdminToCreateLibrary": "Poproś administratora o stworzenie biblioteki.", "AllowFfmpegThrottlingHelp": "Kiedy transkodowanie lub remuxowanie dotrze wystarczająco daleko od aktualnej pozycji odtwarzania, zatrzymaj proces aby zużywać mniej zasobów. Jest to najbardziej użyteczne podczas oglądania bez częstego przeskakiwania. Wyłącz jeśli zaobserwujesz problemy z odtwarzaniem.", "AllowFfmpegThrottling": "Ograniczaj transkodowanie", @@ -1460,9 +1439,7 @@ "AlbumArtist": "Album artysty", "Album": "Album", "Person": "Osoba", - "OtherArtist": "Inny artysta", "Movie": "Film", - "MessageUnauthorizedUser": "Nie masz dostępu do zasobów serwera. Skontaktuj się z administratorem sieci, aby uzyskać więcej informacji.", "LabelLibraryPageSizeHelp": "Ustaw liczbę pozycji pokazywanych na stronie biblioteki. Ustaw 0, aby wyłączyć podział na strony.", "LabelLibraryPageSize": "Rozmiar strony biblioteki:", "LabelDeinterlaceMethod": "Metoda usuwania przeplotu:", @@ -1506,7 +1483,6 @@ "ShowLess": "Pokaż mniej", "Season": "Sezon", "SaveChanges": "Zapisz zmiany", - "ReleaseGroup": "Zwolnij Grupę", "PreferEmbeddedEpisodeInfosOverFileNames": "Preferuj wbudowane informacje o odcinku przed nazwami plików", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Używa informacji o odcinku z dołączonych metadanych jeśli są dostępne.", "MessageSyncPlayErrorMedia": "Nie udało się uruchomić SyncPlay! Błąd mediów.", @@ -1548,7 +1524,6 @@ "EnableFasterAnimations": "Szybsze animacje", "LabelRequireHttpsHelp": "Jeśli zaznaczone, serwer automatycznie przekieruje wszystkie połączenia HTTP do HTTPS. Ta opcja nie zadziała jeśli serwer nie nasłuchuje na HTTPS.", "LabelRequireHttps": "Wymagaj HTTPS", - "LabelNightly": "Nocny", "LabelStable": "Stabilny", "LabelChromecastVersion": "Wersja Chromecast", "LabelEnableHttpsHelp": "Pozwala serwerowi na nasłuchiwanie na skonfigurowanym porcie HTTPS. Prawidłowy certyfikat musi być skonfigurowany by ta opcja zadziałała.", diff --git a/src/strings/pt-br.json b/src/strings/pt-br.json index 3b99379518..e398e47fa2 100644 --- a/src/strings/pt-br.json +++ b/src/strings/pt-br.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "O acesso está atualmente restrito. Por favor, tente novamente mais tarde.", "Actor": "Ator", "Add": "Adicionar", - "AddItemToCollectionHelp": "Adiciona itens às coletâneas buscando por eles, usando o botão direito do mouse ou clicando nos menus para os adicionar a uma coletânea.", "AddToCollection": "Adicionar à coletânea", "AddToPlayQueue": "Adicionar à fila de reprodução", "AddToPlaylist": "Adicionar à lista de reprodução", @@ -38,7 +37,6 @@ "AspectRatio": "Proporção da tela", "AttributeNew": "Novo", "Audio": "Áudio", - "AutoBasedOnLanguageSetting": "Automático (baseado na configuração do idioma)", "Backdrop": "Imagem de Fundo", "Backdrops": "Imagens de Fundo", "BirthDateValue": "Nascimento: {0}", @@ -78,7 +76,6 @@ "ButtonGuide": "Guia", "ButtonHelp": "Ajuda", "ButtonHome": "Início", - "ButtonLearnMore": "Saiba mais", "ButtonLibraryAccess": "Acesso à biblioteca", "ButtonManualLogin": "Login Manual", "ButtonMore": "Mais", @@ -123,7 +120,6 @@ "ButtonSubtitles": "Legendas", "ButtonUninstall": "Desinstalar", "ButtonUp": "Cima", - "ButtonViewWebsite": "Ver site", "CancelRecording": "Cancelar gravação", "CancelSeries": "Cancelar série", "Categories": "Categorias", @@ -279,7 +275,6 @@ "HeaderAppearsOn": "Aparece em", "HeaderAudioBooks": "Livros de Áudio", "HeaderAudioSettings": "Configurações de Áudio", - "HeaderAutomaticUpdates": "Atualizações Automáticas", "HeaderBlockItemsWithNoRating": "Bloquear itens com avaliação desconhecida ou sem avaliação:", "HeaderBooks": "Livros", "HeaderBranding": "Marca", @@ -496,8 +491,6 @@ "LabelAlbumArtists": "Artistas do álbum:", "LabelAll": "Todos", "LabelAllowHWTranscoding": "Permitir a transcodificação de hardware", - "LabelAllowServerAutoRestart": "Permitir ao servidor reiniciar automaticamente para aplicar as atualizações", - "LabelAllowServerAutoRestartHelp": "O servidor só reiniciará durante os períodos ociosos quando nenhum usuário estiver ativo.", "LabelAllowedRemoteAddresses": "Filtro de endereço IP remoto:", "LabelAllowedRemoteAddressesMode": "Modo do filtro de endereço IP remoto:", "LabelAppName": "Nome do app", @@ -534,7 +527,6 @@ "LabelCustomDeviceDisplayName": "Nome para exibição:", "LabelCustomDeviceDisplayNameHelp": "Fornece um nome para exibição ou deixe em branco para usar o nome informado pelo dispositivo.", "LabelCustomRating": "Avaliação personalizada:", - "LabelDashboardTheme": "Tema do painel do servidor:", "LabelDateAdded": "Data de adição:", "LabelDateAddedBehavior": "Comportamento da data de adição para novo conteúdo:", "LabelDateAddedBehaviorHelp": "Se um valor de metadados estiver presente, ele sempre será utilizado antes destas opções.", @@ -755,7 +747,6 @@ "LabelSortBy": "Ordenar por:", "LabelSortOrder": "Ordem:", "LabelSortTitle": "Ordenar por título:", - "LabelSoundEffects": "Efeitos sonoros:", "LabelSource": "Fonte:", "LabelSpecialSeasonsDisplayName": "Nome de exibição da temporada especial:", "LabelSportsCategories": "Categorias de esportes:", @@ -804,7 +795,6 @@ "LabelXDlnaCapHelp": "Determina o conteúdo do elemento X_DLNACAP no namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Determina o conteúdo do elemento X_DLNADOC no namespace urn:schemas-dlna-org:device-1-0.", "LabelYear": "Ano:", - "LabelYourFirstName": "Seu primeiro nome:", "LabelYoureDone": "Pronto!", "LabelZipCode": "CEP:", "LabelffmpegPath": "Local do FFmpeg:", @@ -868,7 +858,6 @@ "MessageFileReadError": "Ocorreu um erro ao ler o arquivo. Por favor, tente novamente.", "MessageForgotPasswordFileCreated": "O seguinte arquivo foi criado no seu servidor e contém instruções de como proceder:", "MessageForgotPasswordInNetworkRequired": "Por favor, tente novamente dentro da rede local para iniciar o processo para redefinir a senha.", - "MessageInstallPluginFromApp": "Este plugin deve ser instalado de dentro do app em que deseja usá-lo.", "MessageInvalidForgotPasswordPin": "Foi digitado um código PIN inválido ou expirado. Por favor, tente novamente.", "MessageInvalidUser": "Usuário ou senha inválidos. Por favor, tente novamente.", "MessageItemSaved": "Item salvo.", @@ -1096,7 +1085,6 @@ "ProductionLocations": "Locais de produção", "Programs": "Programas", "Quality": "Qualidade", - "QueueAllFromHere": "Enfileirar todas a partir daqui", "Raised": "Criado", "Rate": "Avaliação", "RecentlyWatched": "Assistido recentemente", @@ -1127,7 +1115,6 @@ "ReplaceExistingImages": "Substituir imagens existentes", "ResumeAt": "Retomar de {0}", "Rewind": "Retroceder", - "RunAtStartup": "Executar ao iniciar", "Runtime": "Duração", "Saturday": "Sábado", "Save": "Salvar", @@ -1217,7 +1204,6 @@ "TabParentalControl": "Controle dos Pais", "TabPassword": "Senha", "TabPlayback": "Reprodução", - "TabPlaylist": "Lista de Reprodução", "TabPlaylists": "Listas de Reprodução", "TabProfile": "Perfil", "TabProfiles": "Perfis", @@ -1278,7 +1264,6 @@ "ValueTimeLimitMultiHour": "Limite de tempo: {0} horas", "ValueTimeLimitSingleHour": "Limite de tempo: 1 hora", "ValueVideoCodec": "Codec de Vídeo: {0}", - "VideoRange": "Faixa de vídeo", "ViewAlbum": "Exibir álbum", "ViewPlaybackInfo": "Exibir informação de reprodução", "Watched": "Assistido", @@ -1317,11 +1302,9 @@ "LabelCache": "Cache:", "LabelLogs": "Registros:", "LabelProfileCodecs": "Codecs:", - "LabelSkin": "Tema:", "LabelStatus": "Status:", "LabelTag": "Marcador:", "LeaveBlankToNotSetAPassword": "Caso não queira definir uma senha, deixe em branco.", - "LinksValue": "Links: {0}", "Logo": "Logo", "MediaInfoCodec": "Codec", "MediaInfoFramerate": "Taxa de quadros", @@ -1357,9 +1340,6 @@ "LabelTranscodingContainer": "Formato:", "LabelXDlnaCap": "X-DLNA cap:", "LabelXDlnaDoc": "X-DLNA doc:", - "LaunchWebAppOnStartup": "Executar a interface web quando iniciar o servidor", - "LaunchWebAppOnStartupHelp": "Abre o cliente web no seu navegador padrão quando o servidor iniciar. Isso não ocorrerá ao usar a função de reiniciar o servidor.", - "MediaInfoSoftware": "Software", "MediaInfoStreamTypeAudio": "Áudio", "MediaInfoStreamTypeData": "Dados", "MediaInfoStreamTypeEmbeddedImage": "Imagem Incorporada", @@ -1449,7 +1429,6 @@ "LabelPlayerDimensions": "Dimensões do player:", "LabelCorruptedFrames": "Quadros corrompidos:", "HeaderNavigation": "Navegação", - "CopyStreamURLError": "Houve um erro ao copiar a URL.", "ButtonSplit": "Dividir", "AskAdminToCreateLibrary": "Peça a um administrador para criar uma biblioteca.", "AllowFfmpegThrottling": "Transcodes do Acelerador", @@ -1475,9 +1454,7 @@ "Yadif": "YADIF", "Track": "Trilha", "Season": "Temporada", - "ReleaseGroup": "Grupo de Lançamento", "Person": "Pessoa", - "OtherArtist": "Outro Artista", "Movie": "Filme", "LabelLibraryPageSizeHelp": "Selecione a quantidade de itens a aparecer na página da biblioteca. Coloque 0 para desabilitar a paginação.", "LabelLibraryPageSize": "Tamanho da página da biblioteca:", @@ -1489,7 +1466,6 @@ "AlbumArtist": "Artista do Album", "Album": "Album", "UnsupportedPlayback": "O Jellyfin não pode descriptografar conteúdo protegido por DRM, porém mesmo assim fará uma tentativa para todo tipo de conteúdo, incluindo títulos protegidos. A imagem de alguns arquivos pode aparecer completamente preta devido a criptografia ou outros recursos não suportados, como títulos interativos.", - "MessageUnauthorizedUser": "Você não está autorizado a acessar o servidor neste momento. Por favor, contate o administrador do servidor para mais informações.", "ButtonTogglePlaylist": "Playlist", "ButtonToggleContextMenu": "Mais", "Filter": "Filtro", @@ -1500,7 +1476,6 @@ "SaveChanges": "Salvar mudanças", "LabelRequireHttpsHelp": "Se selecionado, o servidor vai automaticamente redirecionar todas as solicitações HTTP para HTTPS. Isso não terá efeito se o servidor não estiver escutando HTTPS.", "LabelRequireHttps": "Necessita HTTPS", - "LabelNightly": "Nightly", "LabelStable": "Estável", "LabelChromecastVersion": "Versão do Chromecast", "LabelEnableHttpsHelp": "Permite que o servidor escute na porta HTTPS configurada. Um certificado válido também deve ser configurado para que isso entre em vigor.", diff --git a/src/strings/pt-pt.json b/src/strings/pt-pt.json index f48f47befd..56712f348a 100644 --- a/src/strings/pt-pt.json +++ b/src/strings/pt-pt.json @@ -31,7 +31,6 @@ "ButtonHelp": "Ajuda", "ButtonHome": "Início", "ButtonInfo": "Informação", - "ButtonLearnMore": "Saiba mais", "ButtonManualLogin": "Início de Sessão Manual", "ButtonMore": "Mais", "ButtonNetwork": "Rede", @@ -69,7 +68,6 @@ "ButtonSubmit": "Enviar", "ButtonSubtitles": "Legendas", "ButtonUninstall": "Desinstalar", - "ButtonViewWebsite": "Ver website", "ChannelAccessHelp": "Selecione os canais para partilhar com este utilizador. Os administradores poderão editar todos os canais utilizando o gestor de metadados.", "CinemaModeConfigurationHelp": "O modo cinema traz a experiência do cinema para a sua sala, possibilitando reproduzir trailers e introduções personalizadas antes da longa-metragem.", "Composer": "Compositor", @@ -123,7 +121,6 @@ "HeaderApiKeysHelp": "As aplicações externas necessitam de uma chave da API para comunicar com o Jellyfin Server. As chaves são emitidas ao entrar com uma conta Jellyfin ou concedendo manualmente a chave à aplicação.", "HeaderApp": "Aplicação", "HeaderAudioSettings": "Configurações de Áudio", - "HeaderAutomaticUpdates": "Atualizações automáticas", "HeaderBlockItemsWithNoRating": "Bloquear conteúdo sem informação de classificação etária ou com informação desconhecida:", "HeaderBranding": "Marca", "HeaderCastCrew": "Elenco e Equipa", @@ -274,8 +271,6 @@ "LabelAlbumArtPN": "PN da capa do álbum:", "LabelAlbumArtists": "Artistas do Álbum:", "LabelAll": "Todos", - "LabelAllowServerAutoRestart": "Permitir ao servidor reiniciar automaticamente para aplicar atualizações", - "LabelAllowServerAutoRestartHelp": "O servidor irá reiniciar apenas durante períodos em que não esteja a ser usado, quando nenhum utilizador estiver ativo.", "LabelAppName": "Nome da aplicação", "LabelAppNameExample": "Exemplo: Sickbeard, Sonarr", "LabelArtists": "Artistas:", @@ -490,7 +485,6 @@ "LabelVersionInstalled": "{0} instalado", "LabelXDlnaCapHelp": "Determina o conteúdo do elemento X_DLNACAP no namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Determina o conteúdo do elemento X_DLNADOC no namespace urn:schemas-dlna-org:device-1-0.", - "LabelYourFirstName": "O seu primeiro nome:", "LabelYoureDone": "Concluiu!", "LabelZipCode": "CEP:", "LibraryAccessHelp": "Escolha as Bibliotecas a partilhar com este utilizador. Os Administradores poderão editar todas as pastas, usando o Gestor de Metadados.", @@ -710,7 +704,6 @@ "TabParentalControl": "Controlo Parental", "TabPassword": "Palavra-passe", "TabPlayback": "Reprodução", - "TabPlaylist": "Lista de Reprodução", "TabPlaylists": "Listas de Reprodução", "TabPlugins": "Extensões", "TabProfile": "Perfil", @@ -749,7 +742,6 @@ "Writer": "Autor", "XmlDocumentAttributeListHelp": "Estes atributos são aplicados ao elemento principal de cada resposta XML.", "AccessRestrictedTryAgainLater": "Acesso atualmente restrito. Por favor, tente mais tarde.", - "AddItemToCollectionHelp": "Adicione itens às coleções pesquisando-os e utilizando o respetivo menu de toque ou clique direito para os adicionar a uma coleção.", "AddToCollection": "Adicionar à coleção", "AddToPlayQueue": "Adicionar à fila de reprodução", "AddedOnValue": "Adicionado {0}", @@ -777,7 +769,6 @@ "AspectRatio": "Proporção", "AuthProviderHelp": "Selecione um mecanismo de autenticação a ser utilizado para validar as credenciais deste utilizador.", "Auto": "Automático", - "AutoBasedOnLanguageSetting": "Automático (baseado no idioma definido)", "BirthDateValue": "Nascimento: {0}", "BirthPlaceValue": "Local de nascimento: {0}", "Blacklist": "Lista Negra", @@ -941,7 +932,6 @@ "LabelDefaultScreen": "Ecrã por defeito:", "LabelDeathDate": "Data de falecimento:", "LabelDateTimeLocale": "Localização da data/hora:", - "LabelDashboardTheme": "Tema do Painel Principal:", "LabelCertificatePasswordHelp": "Se o certificado requer uma palavra-passe, escreva-a aqui.", "LabelCertificatePassword": "Palavra-passe do certificado:", "LabelBurnSubtitles": "Integrar legendas:", @@ -991,7 +981,6 @@ "SaveSubtitlesIntoMediaFoldersHelp": "Guardar ficheiros de legendas junto aos ficheiros vídeo facilita a gestão.", "SaveSubtitlesIntoMediaFolders": "Guardar legendas nas pastas multimédia", "Runtime": "Duração", - "RunAtStartup": "Executar no arranque", "ResumeAt": "Retomar a partir de {0}", "ReplaceAllMetadata": "Substituir todos os metadados", "RepeatOne": "Repetir este", @@ -1000,7 +989,6 @@ "MessageNoPluginConfiguration": "Esta extensão não é configurável.", "MessagePluginInstallDisclaimer": "As extensões desenvolvidas pela comunidade Jellyfin são uma ótima forma de melhorar a experiência de utilização do Jellyfin, adicionando novas funcionalidades e benefícios. Antes de proceder à instalação, tenha em atenção que estas podem alterar determinados comportamentos no Servidor Jellyfin e provocar efeitos como tempos de atualização da Biblioteca mais longos, processamento adicional em segundo plano e estabilidade do sistema reduzida.", "MessagePluginConfigurationRequiresLocalAccess": "Para configurar esta extensão, inicie sessão localmente no servidor.", - "MessageInstallPluginFromApp": "Esta extensão deverá ser instalada a partir da aplicação onde tem intenção de a utilizar.", "HeaderPluginInstallation": "Instalação de Extensão", "MessagePluginInstalled": "A extensão foi instalada com sucesso. O Servidor Jellyfin necessitará de reiniciar para aplicar as alterações.", "PleaseRestartServerName": "Por favor, reinicie o Servidor Jellyfin - {0}.", @@ -1161,7 +1149,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Imagem Integrada", "MediaInfoStreamTypeData": "Dados", "MediaInfoStreamTypeAudio": "Áudio", - "MediaInfoSoftware": "Software", "MediaInfoTimestamp": "Data e Hora", "MediaInfoSampleRate": "Taxa de Amostragem", "MediaInfoResolution": "Resolução", @@ -1187,12 +1174,10 @@ "LabelMetadataSaversHelp": "Escolha o formato em que deseja guardar metadados.", "LabelRefreshMode": "Modo de actualização:", "LabelRemoteClientBitrateLimitHelp": "Valor-limite de taxa de transmissão para todos os dispositivos fora da rede local. Este valor é opcional e aplica-se a cada transmissão individual. Ao definir este valor previne que dispositivos peçam uma taxa de transmissão acima da sua ligação à internet. Pedir uma taxa de transmissão acima do limite da ligação implica a necessidade de transcodificar o vídeo e num aumento da carga da CPU.", - "LabelSoundEffects": "Efeitos sonoros:", "Home": "Início", "GuideProviderLogin": "Iniciar Sessão", "HeaderSubtitleDownloads": "Transferir legendas", "LabelRecord": "Gravação:", - "LabelSkin": "Máscara:", "LabelMetadataDownloadersHelp": "Ative e ordene os seus provedores de metadados por ordem de preferência. Provedores com menos prioridade só serão usados para completar informação em falta.", "LabelMetadataReadersHelp": "Ordene as suas fontes de metadados por ordem de preferência. O primeiro ficheiro encontrado será utilizado.", "LabelMetadataReaders": "Provedores de metadados:", @@ -1315,7 +1300,6 @@ "LabelSportsCategories": "Categorias de Desporto:", "FetchingData": "A transferir informação adicional", "List": "lista", - "LaunchWebAppOnStartup": "Iniciar a interface web ao iniciar o servidor", "No": "Não", "OptionRegex": "Expressão Regular", "OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)", @@ -1337,7 +1321,6 @@ "MediaInfoAnamorphic": "Anamórfico", "LabelTranscodes": "Transcodificação:", "Whitelist": "Lista branca", - "VideoRange": "Alcance video", "ValueOneAlbum": "1 álbum", "ValueMusicVideoCount": "{0} videoclips", "ValueMovieCount": "{0} filmes", @@ -1387,7 +1370,6 @@ "RefreshMetadata": "Recarregar metadados", "RecentlyWatched": "Vistos recentemente", "Rate": "Avaliação", - "QueueAllFromHere": "Fila a partir daqui", "Quality": "Qualidade", "ProductionLocations": "Localizações de produção", "Primary": "Primário", @@ -1412,9 +1394,7 @@ "MediaInfoDefault": "Padrão", "MediaInfoBitDepth": "Bit profundidade", "Logo": "Logotipo", - "LinksValue": "Ligações: {0}", "Like": "Gosto", - "LaunchWebAppOnStartupHelp": "Abra o cliente web no ser browser padrão quando o servidor iniciar. Isto não acontecerá usando uma função de reiniciar de servidor.", "LabelXDlnaDoc": "X-DLNA doc:", "LabelXDlnaCap": "X-DLNA cap:", "LabelVaapiDeviceHelp": "Este é o nó de renderização usado para aceleração de hardware.", @@ -1429,7 +1409,6 @@ "HeaderNavigation": "Navegação", "EnableStreamLooping": "Auto-cíclico de streams ao vivo", "Down": "Baixo", - "CopyStreamURLError": "Ocorreu um erro a copiar o URL.", "ButtonSplit": "Dividir", "NoCreatedLibraries": "Parece que ainda não foi criada nenhuma biblioteca por enquanto. {0} Gostaria de criar uma biblioteca agora? {1}", "AskAdminToCreateLibrary": "Pergunte a um administrador para criar uma biblioteca.", @@ -1449,7 +1428,6 @@ "LabelRepositoryUrl": "URL do Repositório", "HeaderNewRepository": "Novo Repositório", "MessageNoRepositories": "Sem repositórios.", - "MessageUnauthorizedUser": "Não está autorizado a aceder ao servidor neste momento. Por favor contacte o administador deste servidor para informação mais detalhada.", "LabelSyncPlayAccess": "Acesso \"SyncPlay\"", "LabelSyncPlayAccessNone": "Desativar para este utilizador", "LabelSyncPlayAccessJoinGroups": "Permitir utilizador a aderir a grupos", @@ -1466,7 +1444,6 @@ "EnableFasterAnimations": "Animações Rápidas", "LabelRequireHttpsHelp": "Se selecionado, o servidor irá automaticamente redirecionar todos os pedidos em HTTP para HTTPS. Isto não surte efeito caso o servidor não esteja configurado em HTTPS.", "LabelRequireHttps": "Exigir HTTPS", - "LabelNightly": "\"Nightly\"", "LabelStable": "Estável", "LabelChromecastVersion": "Versão do \"Chromecast\"", "LabelLibraryPageSizeHelp": "Define a quantidade de items a apresentar na página de uma Biblioteca. Para desativar a existência de paginação, introduza o valor 0.", diff --git a/src/strings/pt.json b/src/strings/pt.json index 6b6f793ffb..45b8f0c182 100644 --- a/src/strings/pt.json +++ b/src/strings/pt.json @@ -50,7 +50,6 @@ "TabProfile": "Perfil", "TabPlugins": "Extensões", "TabPlaylists": "Listas de Reprodução", - "TabPlaylist": "Lista de Reprodução", "TabPlayback": "Reprodução", "TabPassword": "Palavra-passe", "TabParentalControl": "Controlo Parental", @@ -117,7 +116,6 @@ "Save": "Guardar", "Saturday": "Sábado", "Runtime": "Duração", - "RunAtStartup": "Executar no arranque", "Rewind": "Retroceder", "ResumeAt": "Retomar a partir de {0}", "ReplaceExistingImages": "Substituir imagens existentes", @@ -416,7 +414,6 @@ "LabelDateAddedBehaviorHelp": "Quando os metadados incluirem um valor, este será utilizado antes destas opções.", "LabelDateAddedBehavior": "Comportamento da data de adição para novo conteúdo:", "LabelDateAdded": "Adicionado a:", - "LabelDashboardTheme": "Tema do Painel Principal:", "LabelCustomRating": "Classificação personalizada:", "LabelCustomDeviceDisplayNameHelp": "Forneça um nome a ser mostrado, ou deixe em branco para utilizar o nome reportado pelo dispositivo.", "LabelCustomDeviceDisplayName": "Nome a ser mostrado:", @@ -454,8 +451,6 @@ "LabelAppName": "Nome da aplicação", "LabelAllowedRemoteAddressesMode": "Tipo de filtro de IP remoto:", "LabelAllowedRemoteAddresses": "Filtro de IP remoto:", - "LabelAllowServerAutoRestartHelp": "O servidor reiniciará apenas durante períodos em que não esteja a ser usado, quando nenhum utilizador estiver activo.", - "LabelAllowServerAutoRestart": "Permitir ao servidor reiniciar automaticamente para instalar actualizações", "LabelAllowHWTranscoding": "Permitir transcodificação por hardware", "LabelAll": "Todos", "LabelAlbumArtists": "Artistas do Álbum:", @@ -706,7 +701,6 @@ "MessageItemSaved": "Item guardado.", "MessageInvalidUser": "Nome de utilizador ou palavra-passe inválidos. Por favor, tente novamente.", "MessageInvalidForgotPasswordPin": "Foi inserido um código PIN inválido ou expirado. Por favor, tente de novo.", - "MessageInstallPluginFromApp": "Esta extensão deverá ser instalada a partir da aplicação em que tem intenção de a utilizar.", "MessageImageTypeNotSelected": "Por favor, seleccione um tipo de imagem da lista.", "MessageImageFileTypeAllowed": "Apenas são suportados ficheiros JPEG ou PNG.", "MessageForgotPasswordInNetworkRequired": "Por favor, volte a tentar o processo de recuperação de palavra-passe quando se encontrar dentro da sua rede local.", @@ -735,7 +729,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Imagem Integrada", "MediaInfoStreamTypeData": "Dados", "MediaInfoStreamTypeAudio": "Áudio", - "MediaInfoSoftware": "Software", "MediaInfoTimestamp": "Data e Hora", "MediaInfoSize": "Tamanho", "MediaInfoSampleRate": "Taxa de Amostragem", @@ -755,7 +748,6 @@ "LatestFromLibrary": "Mais Recentes em {0}", "LabelZipCode": "Código Postal:", "LabelYoureDone": "Concluiu!", - "LabelYourFirstName": "O seu primeiro nome:", "LabelXDlnaDocHelp": "Determina o conteúdo do elemento X_DLNADOC no namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaCapHelp": "Determina o conteúdo do elemento X_DLNACAP no namespace urn:schemas-dlna-org:device-1-0.", "LabelVersionInstalled": "{0} instalado", @@ -953,7 +945,6 @@ "CancelSeries": "Cancelar gravação de série", "CancelRecording": "Cancelar gravação", "ButtonWebsite": "Website", - "ButtonViewWebsite": "Ver website", "ButtonUp": "Para cima", "ButtonUninstall": "Desinstalar", "ButtonTrailer": "Trailer", @@ -999,7 +990,6 @@ "ButtonMore": "Mais", "ButtonManualLogin": "Início de Sessão Manual", "ButtonLibraryAccess": "Acesso à biblioteca", - "ButtonLearnMore": "Saiba mais", "ButtonInfo": "Informação", "ButtonHome": "Início", "ButtonHelp": "Ajuda", @@ -1081,7 +1071,6 @@ "Banner": "Insígnia", "Backdrops": "Imagens de Fundo", "Backdrop": "Imagem de Fundo", - "AutoBasedOnLanguageSetting": "Automático (baseado no idioma definido)", "AuthProviderHelp": "Seleccione um mecanismo de autenticação a ser utilizado para validar as credenciais deste utilizador.", "Audio": "Áudio", "AttributeNew": "Novo", @@ -1114,7 +1103,6 @@ "AddToPlaylist": "Adicionar à lista de reprodução", "AddToPlayQueue": "Adicionar à fila de reprodução", "AddToCollection": "Adicionar à coleção", - "AddItemToCollectionHelp": "Adicione itens às coleções pesquisando-os e utilizando o respetivo menu de toque ou clique direito para os adicionar a uma coleção.", "Add": "Adicionar", "Actor": "Ator", "AccessRestrictedTryAgainLater": "O acesso está atualmente restrito. Por favor, tente mais tarde.", @@ -1147,7 +1135,6 @@ "HeaderCancelRecording": "Cancelar Gravação", "HeaderBooks": "Livros", "HeaderBlockItemsWithNoRating": "Bloquear conteúdo sem informação de classificação etária ou com informação desconhecida:", - "HeaderAutomaticUpdates": "Acualizações automáticas", "HeaderAudioSettings": "Configurações de Áudio", "HeaderAudioBooks": "Livros de Áudio", "HeaderApp": "Aplicação", @@ -1201,7 +1188,6 @@ "Down": "Baixar", "HeaderTags": "Tags", "HeaderNavigation": "Navegar", - "CopyStreamURLError": "Ocorreu um erro ao copiar o URL.", "ButtonSplit": "Dividir", "AskAdminToCreateLibrary": "Peça a um administrador para criar uma biblioteca.", "AllowFfmpegThrottling": "Transcodificação com falhas", @@ -1265,12 +1251,9 @@ "ManageRecording": "Gerenciar gravações", "Logo": "Logo", "List": "Lista", - "LinksValue": "Links: {0}", "Like": "Gostei", "LeaveBlankToNotSetAPassword": "Você pode deixar esse campo em branco para definir nenhuma senha.", "LearnHowYouCanContribute": "Aprenda como você pode contribuir.", - "LaunchWebAppOnStartupHelp": "Abra o cliente da web no seu navegador da web padrão quando o servidor iniciar. Isso não ocorrerá ao usar a função de reinicialização do servidor.", - "LaunchWebAppOnStartup": "Inicie a interface da web ao iniciar o servidor", "Large": "Amplo", "LanNetworksHelp": "Lista separada por vírgula de endereços IP ou entradas de máscara de rede/IP para redes que serão consideradas na rede local ao impor restrições de largura de banda. Se definido, todos os outros endereços IP serão considerados na rede externa e estarão sujeitos às restrições de largura de banda externa. Se deixado em branco, apenas a sub-rede do servidor é considerada na rede local.", "LabelffmpegPathHelp": "O caminho para o arquivo do aplicativo ffmpeg ou pasta que contém o ffmpeg.", @@ -1302,11 +1285,9 @@ "LabelSubtitleDownloaders": "Downloaders de legendas:", "LabelStreamType": "Tipo de fluxo:", "LabelSpecialSeasonsDisplayName": "Nome de exibição da temporada especial:", - "LabelSoundEffects": "Efeitos sonoros:", "LabelSortTitle": "Classificar título:", "LabelSortOrder": "Ordem de classificação:", "LabelSortBy": "Ordenar por:", - "LabelSkin": "Pele:", "LabelRemoteClientBitrateLimitHelp": "Um limite opcional de taxa de bits por fluxo para todos os dispositivos fora da rede. Isso é útil para impedir que os dispositivos solicitem uma taxa de bits mais alta do que a sua conexão à Internet pode suportar. Isso pode resultar no aumento da carga da CPU no servidor para transcodificar vídeos em tempo real para uma taxa de bits mais baixa.", "LabelPlayerDimensions": "Dimensões do reprodutor:", "LabelParentNumber": "Número pai:", @@ -1384,7 +1365,6 @@ "OptionAutomaticallyGroupSeries": "Mesclar automaticamente séries que estão espalhadas por várias pastas", "OptionAllowSyncTranscoding": "Permitir download e sincronização de mídia que requeiram transcodificação", "OptionForceRemoteSourceTranscoding": "Forçar a transcodificação de fontes de mídia remota (como LiveTV)", - "MessageUnauthorizedUser": "Você não está autorizado a acessar o servidor no momento. Entre em contato com o administrador do servidor para obter mais informações.", "PreferEmbeddedTitlesOverFileNames": "Preferir títulos incorporados sobre nomes de arquivos", "OptionSaveMetadataAsHiddenHelp": "Alterar isso será aplicado aos novos metadados salvos daqui para frente. Os arquivos de metadados existentes serão atualizados na próxima vez em que forem salvos pelo Jellyfin Server.", "OptionRegex": "Regex", @@ -1395,12 +1375,10 @@ "PreferEmbeddedTitlesOverFileNamesHelp": "Isso determina o título quando nenhum metadado da Internet ou local está disponível.", "PlaybackErrorNoCompatibleStream": "Este cliente não é compatível com a mídia e o servidor não está enviando um formato de mídia compatível.", "Person": "Pessoa", - "OtherArtist": "Outro artista", "OptionThumbCard": "Cartão de polegar", "OptionPosterCard": "Cartão de pôster", "LabelRequireHttpsHelp": "Se marcado, o servidor redirecionará automaticamente todas as solicitações por HTTP para HTTPS. Isso não terá efeito se o servidor não estiver escutando HTTPS.", "LabelRequireHttps": "Requer HTTPS", - "LabelNightly": "À noite", "LabelChromecastVersion": "Versão do Chromecast", "LabelEnableHttpsHelp": "Permite que o servidor escute na postagem HTTPS configurada. Um certificado válido também deve ser configurado para que isso entre em vigor.", "LabelEnableHttps": "Ativar HTTPS", diff --git a/src/strings/ro.json b/src/strings/ro.json index 3ea72217bd..f8aa986264 100644 --- a/src/strings/ro.json +++ b/src/strings/ro.json @@ -38,7 +38,6 @@ "HeaderActiveRecordings": "Înregistrări active", "HeaderAddScheduledTaskTrigger": "Adaugă declanșator", "HeaderAddUser": "Adaugă Utilizator", - "HeaderAutomaticUpdates": "Actualizare Automată", "HeaderChannels": "Canale", "HeaderContinueWatching": "Vizionează în continuare", "HeaderDeviceAccess": "Accesul Dispozitivelor", @@ -63,8 +62,6 @@ "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.", - "LabelAllowServerAutoRestart": "Permite serverului să se repornească automat pentru a aplica actualizările", - "LabelAllowServerAutoRestartHelp": "Serverul se va reporni doar în timp ce nu are nici o sarcină, când nu este nici un utilizator conectat.", "LabelArtists": "Artisti:", "LabelArtistsHelp": "Separare multiplă utilizând ;", "LabelAudioLanguagePreference": "Preferințe de limbă pentru audio:", @@ -105,7 +102,6 @@ "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.", "LabelTriggerType": "Tip Declanșator:", "LabelUser": "Utilizator:", - "LabelYourFirstName": "Numele tău:", "LabelYoureDone": "Ești Gata!", "LibraryAccessHelp": "Selectează biblioteciile media partajate cu acest utilizator. Administratorii vor avea posibilitatea să modifice toate dosarele utilizând managerul de metadata.", "MaxParentalRatingHelp": "Conținutul cu o limită de vârstă mai mare va fi ascuns pentru acest utilizator.", @@ -202,7 +198,6 @@ "TabNotifications": "Notificări", "TabOther": "Altele", "TabPassword": "Parolă", - "TabPlaylist": "Listă de redare", "TabProfile": "Profil", "TabProfiles": "Profile", "TabRecordings": "Înregistrări", @@ -241,7 +236,6 @@ "AddedOnValue": "Adăugat la {0}", "AddToPlaylist": "Adaugă la playlist", "AddToPlayQueue": "Adaugă la coada de redare", - "AddItemToCollectionHelp": "Adaugă obiectele la colecții căutând-le și folosind meniul de click-dreapta sau apasare pentru a le adăuga la colecție.", "Add": "Adaugă", "Actor": "Artist", "AccessRestrictedTryAgainLater": "Accesul este restricționat. Te rugăm să încerci mai târziu.", @@ -262,7 +256,6 @@ "AspectRatio": "Raportul aspectului", "AuthProviderHelp": "Selectează un Furnizor de Autentificare de folosit pentru autentificarea parolei acestui utilizator.", "Auto": "Auto", - "AutoBasedOnLanguageSetting": "Auto (bazat pe setările limbii)", "Backdrop": "Fundal", "Backdrops": "Fundaluri", "Banner": "Bandieră", @@ -292,7 +285,6 @@ "ButtonPreviousTrack": "Calea anterioară", "ButtonRevoke": "Revocă", "ButtonSettings": "Setări", - "ButtonViewWebsite": "Vezi website", "ChangingMetadataImageSettingsNewContent": "Modificări ale metadatelor sau ale setărilor de descărcare a operelor de artă se va aplica doar conținutului nou adăugat în librăriile tale. Pentru a aplica modificările titlurilor deja existente va trebui reîmprospătată manual metadata lor.", "CinemaModeConfigurationHelp": "Mod cinema aduce experiența cinematografică în sufrageria dumneavoastră prin abilitatea de a rula trailere sau introuri personalizate înaintea titlului principal.", "ConfigureDateAdded": "Configurează cum este determinată data adaugării în tabloul de bord al serverului Jellyfin în setările librariei", @@ -328,7 +320,6 @@ "ButtonGuide": "Ghid", "ButtonHome": "Acasă", "ButtonInfo": "Info", - "ButtonLearnMore": "Mai multe", "ButtonLibraryAccess": "Acces Librarie", "ButtonMore": "Mai mult", "ButtonNetwork": "Rețea", @@ -660,7 +651,6 @@ "LabelSportsCategories": "Categorii sportive:", "LabelSpecialSeasonsDisplayName": "Denumirea afișării sezonului special:", "LabelSource": "Sursă:", - "LabelSoundEffects": "Efecte audio:", "LabelSortTitle": "Sortează titlu:", "LabelSortOrder": "Ordinea de sortare:", "LabelSortBy": "Sortează după:", @@ -672,7 +662,6 @@ "LabelSkipIfAudioTrackPresent": "Ignoră dacă pista audio implicită se potrivește cu limba de descărcare", "LabelSkipForwardLength": "Durata salt înainte:", "LabelSkipBackLength": "Durata salt înapoi:", - "LabelSkin": "Tema:", "LabelSize": "Mărime:", "LabelSimultaneousConnectionLimit": "Limita streamului simultan:", "LabelServerName": "Numele serverului:", @@ -869,7 +858,6 @@ "LabelDateAddedBehaviorHelp": "Dacă există o valoare de metadate, aceasta va fi întotdeauna folosită înainte de oricare dintre aceste opțiuni.", "LabelDateAddedBehavior": "Comportamentul datei adăugării pentru conținut nou:", "LabelDateAdded": "Data adăugării:", - "LabelDashboardTheme": "Tema tabloul de bord al serverului:", "LabelCustomRating": "Evaluare personalizată:", "LabelCustomDeviceDisplayNameHelp": "Furnizați un nume de afișare personalizat sau lăsați gol pentru a utiliza numele raportat de dispozitiv.", "LabelCustomDeviceDisplayName": "Numele afisat:", @@ -1056,7 +1044,6 @@ "MessageItemSaved": "Articol salvat.", "MessageInvalidUser": "Nume de utilizator sau parola incorecte. Vă rugăm să încercați din nou.", "MessageInvalidForgotPasswordPin": "A fost introdus un cod PIN nevalid sau expirat. Vă rugăm să încercați din nou.", - "MessageInstallPluginFromApp": "Acest plugin trebuie instalat din aplicația în care intenționați să îl utilizați.", "MessageImageTypeNotSelected": "Vă rugăm să selectați un tip de imagine din meniul derulant.", "MessageImageFileTypeAllowed": "Sunt acceptate numai fișierele JPEG și PNG.", "MessageForgotPasswordInNetworkRequired": "Încercați din nou în rețeaua de domiciliu pentru a iniția procesul de resetare a parolei.", @@ -1086,7 +1073,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Imaginea încorporată", "MediaInfoStreamTypeData": "Date", "MediaInfoStreamTypeAudio": "Audio", - "MediaInfoSoftware": "Software", "MediaInfoTimestamp": "Data și ora", "MediaInfoSize": "Mărime", "MediaInfoSampleRate": "Rata monstrei", @@ -1121,12 +1107,9 @@ "LiveBroadcasts": "Emisie în direct", "Live": "În direct", "List": "Listă", - "LinksValue": "Linkuri: {0}", "Like": "Îmi place", "LeaveBlankToNotSetAPassword": "Puteți lăsa acest câmp necompletat pentru a nu seta o parolă.", "LearnHowYouCanContribute": "Aflați cum puteți contribui.", - "LaunchWebAppOnStartupHelp": "Deschideți clientul web în browserul dvs. implicit la pornirea inițială a serverului. Acest lucru nu se va produce atunci când se utilizează funcția serverului de repornire.", - "LaunchWebAppOnStartup": "Lansați interfața web la pornirea serverului", "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ă.", @@ -1219,7 +1202,6 @@ "SaveSubtitlesIntoMediaFoldersHelp": "Stocarea subtitrărilor lângă fișierele video le va permite să fie gestionate mai ușor.", "SaveSubtitlesIntoMediaFolders": "Salvați subtitrările în dosarele media", "Runtime": "Timpul de rulare", - "RunAtStartup": "Rulați la pornire", "Rewind": "Derulează", "ResumeAt": "Reluați de la {0}", "ReplaceExistingImages": "Înlocuiți toate imaginile", @@ -1250,7 +1232,6 @@ "RecentlyWatched": "Vizionate recent", "Rate": "Evaluare", "Raised": "Ridicat", - "QueueAllFromHere": "Formează o coadă de aici", "Quality": "Calitatea", "Programs": "Programe", "ProductionLocations": "Locații de producție", @@ -1371,7 +1352,6 @@ "Watched": "Vizionat", "ViewPlaybackInfo": "Vizualizați informațiile despre redare", "ViewAlbum": "Vizualizați albumul", - "VideoRange": "Interval video", "Vertical": "Vertical", "ValueVideoCodec": "Codec Video: {0}", "ValueTimeLimitSingleHour": "Limită de timp: 1 oră", @@ -1444,7 +1424,6 @@ "SystemDlnaProfilesHelp": "Profilele de sistem pot fi numai citite. Modificările aduse unui profil de sistem vor fi salvate într-un nou profil personalizat.", "HeaderNavigation": "Navigare", "MessageConfirmAppExit": "Vrei să ieși?", - "CopyStreamURLError": "A apărut o eroare la copierea adresei URL.", "LabelVideoResolution": "Rezoluția video:", "LabelStreamType": "Tipul streamului:", "LabelPlayerDimensions": "Dimensiunile soft redare:", @@ -1458,11 +1437,9 @@ "AllowFfmpegThrottling": "Limitare Transcod-uri", "Track": "Cale", "Season": "Sezon", - "ReleaseGroup": "Gruparea lansării", "PreferEmbeddedEpisodeInfosOverFileNames": "Preferați informația despre episod încorporată în fișier decât numele fișierelor", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Aceasta folosește informația despre episod din metadatele încorporate, dacă sunt disponibile.", "Person": "Persoană", - "OtherArtist": "Alt artist", "Movie": "Film", "Episode": "Episod", "ClientSettings": "Setări pentru client", @@ -1489,7 +1466,6 @@ "UnsupportedPlayback": "Jellyfin nu poate decripta conținut protejat de DRM, dar tot conținutul va fi încercat indiferent de titlurile protejate. Unele fișiere pot părea complet negre din cauza criptării sau a altor funcții neacceptate, cum ar fi titluri interactive.", "LabelLibraryPageSizeHelp": "Setează cantitatea de elemente de afișat pe o pagină a bibliotecii. Setați la 0 pentru a dezactiva paginarea.", "LabelLibraryPageSize": "Mărimea paginii Bibliotecă:", - "MessageUnauthorizedUser": "Nu sunteți autorizat să accesați serverul în acest moment. Vă rugăm să contactați administratorul serverului pentru mai multe informații.", "ButtonTogglePlaylist": "Listă de redare", "ButtonToggleContextMenu": "Mai mult", "Filter": "Filtru", @@ -1498,7 +1474,6 @@ "ApiKeysCaption": "Lista cheilor API active", "LabelRequireHttpsHelp": "Dacă e selectat, serverul va redirecta automat toate cererile HTTP către HTTPS. Dacă nu se ascultă pe HTTPS, nu are niciun efect.", "LabelRequireHttps": "Trebuie HTTPS", - "LabelNightly": "Ultimă", "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.", diff --git a/src/strings/ru.json b/src/strings/ru.json index b24b8ab3ef..9089e4bea7 100644 --- a/src/strings/ru.json +++ b/src/strings/ru.json @@ -3,7 +3,6 @@ "AccessRestrictedTryAgainLater": "В настоящее время доступ запрещён. Повторите попытку позже.", "Actor": "Актёр", "Add": "Добавить", - "AddItemToCollectionHelp": "Добавляйте элементы в коллекции, выполняя их поиск, и с помощью правой кнопки мыши или касания меню присоедините их к коллекции.", "AddToCollection": "Добавить в коллекцию", "AddToPlayQueue": "Добавить в очередь воспроизведения", "AddToPlaylist": "Добавить в плей-лист", @@ -40,7 +39,6 @@ "AttributeNew": "Новинка", "Audio": "Аудио", "Auto": "Авто", - "AutoBasedOnLanguageSetting": "Авто (на основе настройки языка)", "Backdrop": "Фон", "Backdrops": "Фоны", "Banner": "Баннер", @@ -84,7 +82,6 @@ "ButtonHelp": "Справка", "ButtonHome": "Главное", "ButtonInfo": "Инфо", - "ButtonLearnMore": "Подробнее", "ButtonLibraryAccess": "Доступ к медиатеке", "ButtonManualLogin": "Войти вручную", "ButtonMore": "Ещё", @@ -132,7 +129,6 @@ "ButtonTrailer": "Трейлер", "ButtonUninstall": "Удалить", "ButtonUp": "Вверх", - "ButtonViewWebsite": "См. вебсайт", "ButtonWebsite": "Веб-сайт", "CancelRecording": "Отменить запись", "CancelSeries": "Отменить сериал", @@ -296,7 +292,6 @@ "HeaderAppearsOn": "Фигурирует в", "HeaderAudioBooks": "Аудиокниги", "HeaderAudioSettings": "Параметры аудио", - "HeaderAutomaticUpdates": "Автоматические обновления", "HeaderBlockItemsWithNoRating": "Блокирование элементов с отсутствующей или нераспознанной информацией о возрастной категории:", "HeaderBooks": "Книги", "HeaderBranding": "Оформление", @@ -517,8 +512,6 @@ "LabelAlbumArtists": "Исполнители альбома:", "LabelAll": "Все", "LabelAllowHWTranscoding": "Разрешить аппаратную перекодировку", - "LabelAllowServerAutoRestart": "Разрешить автоматический перезапуск сервера для применения обновлений", - "LabelAllowServerAutoRestartHelp": "Сервер будет перезапускаться только в периоды простоя, когда нет активности пользователей.", "LabelAllowedRemoteAddresses": "Фильтр внешних IP-адресов:", "LabelAllowedRemoteAddressesMode": "Режим фильтра внешних IP-адресов:", "LabelAppName": "Название приложения", @@ -556,7 +549,6 @@ "LabelCustomDeviceDisplayName": "Отображаемое название:", "LabelCustomDeviceDisplayNameHelp": "Приведите произвольное имя для отображения или не заполняйте, чтобы использовать имя, выданное устройством.", "LabelCustomRating": "Произвольная возрастная категория:", - "LabelDashboardTheme": "Тема панели сервера:", "LabelDateAdded": "Дата добавления:", "LabelDateAddedBehavior": "Для нового содержания за дату добавления принимается:", "LabelDateAddedBehaviorHelp": "При наличии значения в метаданных, оно всегда используется приоритетно, чем любая из данных опций.", @@ -768,7 +760,6 @@ "LabelServerHost": "Узел:", "LabelServerHostHelp": "192.168.1.100:8096 или https://myserver.com", "LabelSimultaneousConnectionLimit": "Лимит одновременных потоков:", - "LabelSkin": "Оболочка:", "LabelSkipBackLength": "Время отмотки:", "LabelSkipForwardLength": "Время промотки:", "LabelSkipIfAudioTrackPresent": "Пропустить, если аудиодорожка по умолчанию соответствует загружаемому языку", @@ -780,7 +771,6 @@ "LabelSortBy": "Сортировка по:", "LabelSortOrder": "Порядок сортировки:", "LabelSortTitle": "Сортировка по названию:", - "LabelSoundEffects": "Звуковые эффекты:", "LabelSource": "Источник:", "LabelSpecialSeasonsDisplayName": "Отображаемое название спецсезона:", "LabelSportsCategories": "Спортивные категории:", @@ -834,7 +824,6 @@ "LabelXDlnaDoc": "Схема X-DLNA:", "LabelXDlnaDocHelp": "Определяется содержание из элемента X_DLNADOC во пространстве имён urn:schemas-dlna-org:device-1-0.", "LabelYear": "Год:", - "LabelYourFirstName": "Ваше имя:", "LabelYoureDone": "Вы готовы!", "LabelZipCode": "Почтовый код:", "LabelffmpegPath": "Путь к FFmpeg:", @@ -845,7 +834,6 @@ "LearnHowYouCanContribute": "Изучите, как вы можете внести свой вклад.", "LibraryAccessHelp": "Выделите медиатеки, чтобы дать доступ этому пользователю. Администраторы могут изменять все папки с помощью «Диспетчера метаданных».", "Like": "Нравится", - "LinksValue": "Ссылки: {0}", "List": "Список", "Live": "Трансляция", "LiveBroadcasts": "Прямые трансляции", @@ -905,7 +893,6 @@ "MessageFileReadError": "Произошла ошибка при считывании файла. Повторите попытку позже.", "MessageForgotPasswordFileCreated": "Следующий файл был создан на вашем сервере и содержит инструкции о том, как поступить:", "MessageForgotPasswordInNetworkRequired": "Повторите попытку в пределах своей домашней сети, чтобы начать процесс сброса пароля.", - "MessageInstallPluginFromApp": "Данный плагин должен устанавливаться изнутри приложения, для которого оно предназначено.", "MessageInvalidForgotPasswordPin": "Был введён неверный или истёкший PIN-код. Повторите попытку.", "MessageInvalidUser": "Недопустимое имя пользователя или пароль. Повторите попытку.", "MessageItemSaved": "Элемент сохранён.", @@ -1147,7 +1134,6 @@ "ProductionLocations": "Производ-ные площадки", "Programs": "Передачи", "Quality": "Качество", - "QueueAllFromHere": "В очередь все отсюда", "Raised": "Выпуклая", "Rate": "Оценка", "RecentlyWatched": "Недавно просмотренное", @@ -1178,7 +1164,6 @@ "ReplaceExistingImages": "Замена имеющихся изображений", "ResumeAt": "Возобновить с {0}", "Rewind": "Отмотать", - "RunAtStartup": "Запускать при старте системы", "Runtime": "Длительность", "Saturday": "суббота", "Save": "Сохранить", @@ -1276,7 +1261,6 @@ "TabParentalControl": "Управление содержанием", "TabPassword": "Пароль", "TabPlayback": "Воспроизведение", - "TabPlaylist": "Плей-лист", "TabPlaylists": "Плей-листы", "TabPlugins": "Плагины", "TabProfile": "Профиль", @@ -1347,7 +1331,6 @@ "ValueTimeLimitSingleHour": "Временной лимит: 1 час", "ValueVideoCodec": "Видео кодек: {0}", "Vertical": "Вертикально", - "VideoRange": "Диапазон видео", "ViewAlbum": "Посмотреть альбом", "ViewPlaybackInfo": "Сведения о воспроизводимом", "Watched": "Просмотрено", @@ -1390,9 +1373,6 @@ "DashboardOperatingSystem": "Операционная система: {0}", "DashboardArchitecture": "Архитектура: {0}", "LabelWeb": "Веб:", - "LaunchWebAppOnStartup": "Запустить веб-интерфейс при запуске Jellyfin Server", - "LaunchWebAppOnStartupHelp": "Открывается веб-клиент в браузере по умолчанию при начальном запуске сервера. Это не произойдет при использовании функции перезапуска сервера.", - "MediaInfoSoftware": "ПО", "MediaInfoStreamTypeAudio": "Аудио", "MediaInfoStreamTypeData": "Данные", "MediaInfoStreamTypeEmbeddedImage": "Встроенное изображение", @@ -1453,7 +1433,6 @@ "LabelPlayerDimensions": "Размеры проигрывателя:", "LabelDroppedFrames": "Пропущенные кадры:", "LabelCorruptedFrames": "Испорченные кадры:", - "CopyStreamURLError": "Произошла ошибка при копировании URL.", "OptionForceRemoteSourceTranscoding": "Принудительное перекодирование удалённых источников медиаданных (например, эфирное ТВ)", "NoCreatedLibraries": "Похоже, вы еще не создали ни одной медиатеки. {0}Желаете создать её сейчас?{1}", "AskAdminToCreateLibrary": "Попросите администратора создать медиатеку.", @@ -1474,7 +1453,6 @@ "Track": "Дорожка", "Season": "Сезон", "Person": "Персона", - "OtherArtist": "Другой исполнитель", "Movie": "Фильм", "LabelLibraryPageSize": "Размер страницы медиатеки:", "Episode": "Эпизод", @@ -1486,14 +1464,12 @@ "LastSeen": "Последний раз был {0}", "WriteAccessRequired": "Jellyfin Server требуются права на запись в эту папку. Обеспечьте доступ для записи и попробуйте снова.", "PathNotFound": "Путь не может быть найден. Убедитесь, что путь правильный и попробуйте снова.", - "ReleaseGroup": "Релиз-группа", "PreferEmbeddedEpisodeInfosOverFileNames": "Предпочитать встроенную информацию эпизода вместо имён файлов", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Используется информация об эпизоде из встроенных метаданных, если они доступны.", "LabelLibraryPageSizeHelp": "Устанавливается количество элементов для отображения на странице медиатеки. Установите 0 для отключения нумерации страниц.", "LabelDeinterlaceMethod": "Метод устранения гребёнки:", "DeinterlaceMethodHelp": "Выберите метод устранения гребёнки, который будет использоваться при перекодировании чересстрочного содержания.", "UnsupportedPlayback": "Jellyfin не может расшифровать содержимое, защищенное DRM, но в любом случае будет предпринята попытка расшифровки всего содержимого, включая защищенные заголовки. Некоторые файлы могут выглядеть полностью черными из-за шифрования или других неподдерживаемых функций, таких как интерактивные заголовки.", - "MessageUnauthorizedUser": "В настоящее время у вас нет доступа к серверу. Пожалуйста, свяжитесь с администратором сервера для получения дополнительной информации.", "HeaderFavoritePlaylists": "Избранные плей-листы", "LabelRequireHttpsHelp": "Если этот флажок установлен, сервер будет автоматически перенаправлять все запросы через HTTP на HTTPS. Это не имеет никакого эффекта, если сервер не слушает HTTPS.", "LabelEnableHttpsHelp": "Позволяет серверу слушать HTTPS-порт. Для работы необходим действующий сертификат.", @@ -1501,7 +1477,6 @@ "TabDVR": "DVR", "SaveChanges": "Сохранить изменения", "LabelRequireHttps": "Требуется HTTPS", - "LabelNightly": "Ночная", "LabelStable": "Стабильная", "LabelChromecastVersion": "Версия Chromecast", "LabelEnableHttps": "Включить HTTPS", diff --git a/src/strings/sk.json b/src/strings/sk.json index 7b6331aa4d..44c5751175 100644 --- a/src/strings/sk.json +++ b/src/strings/sk.json @@ -20,7 +20,6 @@ "Ascending": "Vzostupne", "AspectRatio": "Pomer strán", "AttributeNew": "Nové", - "AutoBasedOnLanguageSetting": "Automaticky (na základe nastavenia jazyka)", "Backdrops": "Pozadia", "BirthDateValue": "Narodený/á: {0}", "BirthLocation": "Miesto narodenia", @@ -53,7 +52,6 @@ "ButtonGotIt": "Rozumiem", "ButtonHelp": "Pomoc", "ButtonHome": "Domov", - "ButtonLearnMore": "Zistiť viac", "ButtonManualLogin": "Manuálne prihlásenie", "ButtonMore": "Viac", "ButtonNetwork": "Sieť", @@ -92,7 +90,6 @@ "ButtonTrailer": "Trailer", "ButtonUninstall": "Odinštalovať", "ButtonUp": "Hore", - "ButtonViewWebsite": "Zobraziť webovú stránku", "ButtonWebsite": "Webové stránky", "Categories": "Kategórie", "ChannelAccessHelp": "Zvoľte kanály zdieľané s týmto používateľom. Administrátori budú schopní upraviť všetky kanály použitím správcu metadát.", @@ -183,7 +180,6 @@ "HeaderApiKeys": "Kľúče API", "HeaderAudioBooks": "Audio knihy", "HeaderAudioSettings": "Nastavenia zvuku", - "HeaderAutomaticUpdates": "Automatické aktualizácie", "HeaderBooks": "Knihy", "HeaderCastAndCrew": "Obsadenie a štáb", "HeaderChannels": "Kanály", @@ -314,8 +310,6 @@ "LabelAirTime": "Čas vysielania:", "LabelAll": "Všetky", "LabelAllowHWTranscoding": "Povoliť hardvérové transkódovanie", - "LabelAllowServerAutoRestart": "Povoliť automatický reštart servera pre aplikovanie aktualizácií", - "LabelAllowServerAutoRestartHelp": "Server sa reštartuje iba počas obdobia bez aktivity, keď nie je žiadny používateľ aktívny.", "LabelAllowedRemoteAddresses": "Filter vzdialených IP adries:", "LabelAppName": "Názov aplikácie", "LabelAppNameExample": "Príklad: Sickbeard, Sonarr", @@ -464,7 +458,6 @@ "LabelSkipForwardLength": "Dĺžka skoku dopredu:", "LabelSkipIfGraphicalSubsPresent": "Preskočiť ak video obsahuje vložené titulky", "LabelSortBy": "Zoradiť podľa:", - "LabelSoundEffects": "Zvukové efekty:", "LabelSource": "Zdroj:", "LabelSportsCategories": "Športové kategórie:", "LabelStartWhenPossible": "Spustiť akonáhle je možné:", @@ -493,7 +486,6 @@ "LabelVersion": "Verzia:", "LabelVersionInstalled": "{0} nainštalovaný", "LabelYear": "Rok:", - "LabelYourFirstName": "Meno:", "LabelYoureDone": "Hotovo!", "LabelZipCode": "PSČ:", "LabelffmpegPath": "Cesta k FFmpeg:", @@ -727,7 +719,6 @@ "ReplaceAllMetadata": "Nahradiť všetky metadáta", "ReplaceExistingImages": "Nahradiť existujúce obrázky", "ResumeAt": "Pokračovať od {0}", - "RunAtStartup": "Spustiť pri štarte", "Saturday": "Sobota", "Save": "Uložiť", "SaveSubtitlesIntoMediaFolders": "Ukladať titulky do priečinkov s médiami", @@ -909,10 +900,8 @@ "DashboardArchitecture": "Architektúra: {0}", "LabelWeb": "Web:", "LeaveBlankToNotSetAPassword": "Toto pole môžete nechať prázdne pre nastavenie bez hesla.", - "LinksValue": "Odkazy: {0}", "List": "Zoznam", "Logo": "Logo", - "MediaInfoSoftware": "Softvér", "MediaInfoStreamTypeAudio": "Audio", "MediaInfoStreamTypeData": "Dáta", "MediaInfoStreamTypeSubtitle": "Titulky", @@ -997,7 +986,6 @@ "TabDirectPlay": "Priame prehrávanie", "TabLogs": "Záznamy", "TabPlayback": "Prehrávanie", - "TabPlaylist": "Playlist", "TabPlaylists": "Playlisty", "TabServer": "Server", "TabStreaming": "Streamovanie", @@ -1064,7 +1052,6 @@ "LabelDisplayLanguageHelp": "Preklad Jellyfinu je v neustálom vývoji.", "LabelDefaultUserHelp": "Určuje, ktorá používateľská knižnica by mala byť zobrazená na pripojenom zariadení. Toto nastavenie môže byť prepísané pomocou profilov pre každé zariadenie.", "LabelDateAddedBehaviorHelp": "Pokiaľ majú metadáta hodnotu, bude vždy použitá pred niektorou z týchto možností.", - "LabelDashboardTheme": "Téma dashboardu servera:", "LabelCustomDeviceDisplayNameHelp": "Nahradte vlastným názvom alebo ponechajte prázdne, aby názov určilo zariadenie.", "LabelCustomDeviceDisplayName": "Zobrazený názov:", "LabelCache": "Cache:", @@ -1134,7 +1121,6 @@ "HeaderApiKeysHelp": "Externé aplikácie musia mať vlastný API kľúč, aby mohli komunikovať s Jellyfin Serverom. Kľúče sú vydávané pomocou prihlásenia sa cez Jellyfin účet alebo manuálnym priradením kľúča aplikácií.", "HeaderAdditionalParts": "Dodatočné časti", "HardwareAccelerationWarning": "Povolenie hardvérovej akcelerácie môže spôsobiť nestabilitu v niektorých podmienkach. Uistite sa, že váš operačný systém a grafické ovládače sú plne aktualizované. Pokiaľ máte po zapnutí problémy s prehrávaním videa, budete musieť zmeniť nastavenie späť na Žiadne.", - "AddItemToCollectionHelp": "Pridať položku do kolekcie jej vyhľadaním a použitím pravého tlačítka myši alebo kliknutím na tlačidlo ponuky a pridať do kolekcie.", "EncoderPresetHelp": "Vyberte hodnotu faster pre zlepšenie výkonu alebo hodnotu slower pre zlepšenie kvality.", "H264CrfHelp": "Constant Rate Factor (CRF) je východzím nastavením kvality pre x264 enkodér. Môžete mu nadstaviť hodnotu medzi 0 a 51, kde nižšia hodnota vedie k vyššej kvalite (za cenu väčšieho súboru). Rozumné hodnoty sú medzi 18 a 28. Východzia hodnota pre x264 je 23, ktorú môžete použiť ako začiatočný bod.", "GuideProviderSelectListings": "Výber zobrazenia", @@ -1193,7 +1179,6 @@ "XmlDocumentAttributeListHelp": "Tieto atribúty sú aplikované do koreňového prvku každej XML odpovede.", "Writer": "Napísal", "Whitelist": "Whitelist", - "VideoRange": "Rozsah videa", "UserAgentHelp": "Zadajte vlastnú HTTP hlavičku pre user agenta.", "TitleHostingSettings": "Nastavenie hostingu", "Thumb": "Náhľad", @@ -1218,7 +1203,6 @@ "MessageChangeRecordingPath": "Zmenou priečinku pre nahrávanie sa existujúce nahrávky automaticky nepresunú zo starej lokácie na na novú. Budete ich musieť presunúť ručne, pokiaľ budete chcieť.", "RecordSeries": "Nahrať sériu", "Raised": "Vystupujúce", - "QueueAllFromHere": "Všetko odtiaľto zaradiť do fronty", "Primary": "Primárna", "PreferEmbeddedTitlesOverFileNamesHelp": "Toto určuje východzí názov zobrazenia, pokiaľ nie sú k dispozícií internetové metadáta alebo lokálne metadáta.", "PreferEmbeddedTitlesOverFileNames": "Preferovať vložené názvy nad názvami súborov", @@ -1285,7 +1269,6 @@ "MessageNoServersAvailable": "Žiadne servery neboli nájdené pomocou automatického objavovania serverov.", "MessageNoMovieSuggestionsAvailable": "V súčastnosti nie sú k dispozícií žiadne filmové návrhy. Začnite pozerať a hodnotiť vaše filmy, potom sa sem vráťte pre Vaše odporúčania.", "MessageNoCollectionsAvailable": "Kolekcie vám umožnia užiť si vlastné zoskupenia filmov, seriálov a albumov. Kliknite na tlačítko + pre začatie vytvárania kolekcie.", - "MessageInstallPluginFromApp": "Tento zásuvný modul musí byť nainštalovaný z aplikácie, ktorú chcete používať.", "MessageImageTypeNotSelected": "Prosím, vyberte typ obrázku z rozbalovacieho menu.", "MessageForgotPasswordInNetworkRequired": "Prosím, skúste to znova vo vašej domácej sieti pre zahájenie procesu obnovy hesla.", "MessageForgotPasswordFileCreated": "Nasledujúci súbor bol vytvorený na vašom serveri a obsahuje inštrukcie, ako postupovať:", @@ -1308,8 +1291,6 @@ "MediaInfoBitrate": "Dátový tok", "MediaInfoAnamorphic": "Anamorfné", "MapChannels": "Nájdi kanály", - "LaunchWebAppOnStartupHelp": "Otvorí webového klienta vo vašom východzom webovom prehliadači pri prvotnom spustení servera. Toto nenastane pokiaľ použijete funkciu reštartovania servera.", - "LaunchWebAppOnStartup": "Spustiť webové rozhranie pri štarte servera", "LabelffmpegPathHelp": "Cesta k aplikačnému súboru ffmpeg alebo k priečinku obsahujúcemu ffmpeg.", "LabelXDlnaDocHelp": "Určuje obsah prvku X_DLNADOC v namespace urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDoc": "X-DLNA dokumentácia:", @@ -1347,7 +1328,6 @@ "LabelSkipIfGraphicalSubsPresentHelp": "Textové verzie titulkov môžu mať za následok efektívnejšiu dodávku a zníženie šance na transkódovanie videa.", "LabelSkipIfAudioTrackPresentHelp": "Zrušte zaškrtnutie pre zobrazenie titulkov pri všetkých videách, bez ohľadu na jazyk zvuku.", "LabelSkipIfAudioTrackPresent": "Preskočiť, pokiaľ je východzia zvuková stopa rovnaká ako sťahovaný jazyk", - "LabelSkin": "Vzhľad:", "LabelSize": "Veľkosť:", "LabelSimultaneousConnectionLimit": "Limit simultánnych streamov:", "LabelServerName": "Názov serveru:", @@ -1449,7 +1429,6 @@ "LabelPlayerDimensions": "Rozmery prehrávača:", "LabelDroppedFrames": "Vynechané snímky:", "LabelCorruptedFrames": "Poškodené snímky:", - "CopyStreamURLError": "Pri kopírovaní URL nastala chyba.", "OptionForceRemoteSourceTranscoding": "Vynútiť transkódovanie vzdialených mediálnych zdrojov (ako napr. živá TV)", "NoCreatedLibraries": "Vyzerá to tak, že ste zatiaľ nevytvorili žiadnu knižnicu. {0}Chceli by ste nejakú vytvoriť teraz?{1}", "AskAdminToCreateLibrary": "Pokiaľ chcete vytvoriť knižnicu, musíte sa spýtať administrátora.", @@ -1477,9 +1456,7 @@ "Yadif": "YADIF", "Track": "Stopa", "Season": "Séria", - "ReleaseGroup": "Vydavateľ", "Person": "Osoba", - "OtherArtist": "Ostatný umelci", "Movie": "FIlm", "LabelDeinterlaceMethod": "Metóda odstránenia prekladaného videa:", "Episode": "Epizóda", @@ -1488,7 +1465,6 @@ "AlbumArtist": "Umelec albumu", "LabelLibraryPageSizeHelp": "Určuje množstvo položiek na zobrazenie na stránke knižnice. Nastavte 0 pre vypnutie stránkovania.", "LabelLibraryPageSize": "Veľkosť stránky knižnice:", - "MessageUnauthorizedUser": "Momentálne nemáte oprávnenie na prístup k serveru. Prosím, kontaktujte svojho administrátora serveru pre viac informácií.", "UnsupportedPlayback": "Jellyfin nemôže dešifrovať obsah chránený technológiou DRM, ale pokúsi sa o to, vrátane chránených titulov. Niektoré súbory sa môžu zobraziť ako kompletne čierne z dôvodu, že sú zašifrované alebo obsahujú nepodporované funckie, ako napríklad interaktívne funkcie.", "Filter": "Filter", "New": "Nové", @@ -1496,7 +1472,6 @@ "ButtonTogglePlaylist": "Playlist", "ButtonToggleContextMenu": "Viac", "ApiKeysCaption": "Zoznam v súčasnosti povolených API kľúčov", - "LabelNightly": "Nočná", "LabelStable": "Stabilná", "LabelChromecastVersion": "Chromecast verzia", "TabDVR": "DVR", diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index d5e3fdba67..a8f3bd020c 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -7,7 +7,6 @@ "FolderTypeTvShows": "TV", "HeaderAddToCollection": "Dodaj v Zbirko", "HeaderAddUser": "Dodaj Uporabnika", - "HeaderAutomaticUpdates": "Samodejne Posodobitve", "HeaderEasyPinCode": "Enostavna Pin koda", "HeaderFrequentlyPlayed": "Pogosto Predvajano", "HeaderPaths": "Poti", @@ -27,7 +26,6 @@ "LabelPrevious": "Nazaj", "LabelSelectUsers": "Izberi uporabnike:", "LabelTimeLimitHours": "Časovna omejitev (ure):", - "LabelYourFirstName": "Ime:", "LabelYoureDone": "Koncano!", "MoreUsersCanBeAddedLater": "Uporabnike lahko dodate tudi kasneje preko Nadzorne plošče.", "OptionAllowMediaPlayback": "Dovoli predvajanje vsebin", @@ -53,7 +51,6 @@ "TabMyPlugins": "Moji dodatki", "TabNetworks": "Omrezja", "TabPassword": "Geslo", - "TabPlaylist": "Playlista", "TabProfile": "Profil", "TabProfiles": "Profili", "TabShows": "Oddaje", @@ -100,7 +97,6 @@ "HeaderLiveTV": "TV v živo", "HeaderNextUp": "Sledi", "Movies": "Filmi", - "AddItemToCollectionHelp": "Dodajte elemente v zbirke tako, da jih poiščete in jih z desnim klikom ali dotikom menija dodate v zbirko.", "AllowedRemoteAddressesHelp": "Z vejico ločen seznam IP naslovov ali IP/maska omrežij, ki jim je dovoljen oddaljeni dostop. Če pustite prazno, bodo dovoljeni vsi oddaljeni naslovi.", "AlwaysPlaySubtitles": "Vedno prikaži", "AlwaysPlaySubtitlesHelp": "Podnapisi, ki se ujemajo s prednostnim jezikom bodo naloženi ne glede na jezik zvoka.", @@ -114,7 +110,6 @@ "AttributeNew": "Novo", "Audio": "Zvok", "Auto": "Samodejno", - "AutoBasedOnLanguageSetting": "Samodejno (na podlagi nastavitve jezika)", "Backdrop": "Ozadje", "Backdrops": "Ozadja", "BirthDateValue": "Rojen: {0}", @@ -160,7 +155,6 @@ "ButtonHelp": "Pomoč", "ButtonHome": "Domov", "ButtonInfo": "Info", - "ButtonLearnMore": "Nauči se več", "ButtonLibraryAccess": "Dostop do knjižnic", "ButtonManualLogin": "Ročna prijava", "ButtonMore": "Več", @@ -202,7 +196,6 @@ "ButtonTrailer": "Napovednik", "ButtonUninstall": "Odstrani", "ButtonUp": "Gor", - "ButtonViewWebsite": "Obišči spletno stran", "ButtonWebsite": "Spletna stran", "CancelRecording": "Prekini snemanje", "CancelSeries": "Prekini serijo", @@ -559,8 +552,6 @@ "LabelAlbumArtMaxHeight": "Največja višina slike albuma:", "LabelAlbumArtMaxHeightHelp": "Največja ločljivost slike albuma dostopna preko UPnP:albumArtURI.", "LabelAudioBitDepth": "Bitna globina zvoka:", - "LabelAllowServerAutoRestart": "Dovoli, da se strežnik samodejno znova zažene in uveljavi posodobitve", - "LabelAllowServerAutoRestartHelp": "Strežnik se bo samodejno zagnal zgolj v času mirovanja, ko ne bo aktivnih uporabnikov.", "LabelAllowedRemoteAddresses": "Filter oddaljenih IP naslovov:", "LabelAllowedRemoteAddressesMode": "Način filtra oddaljenih IP naslovov:", "LabelAppName": "Ime aplikacije", @@ -586,7 +577,6 @@ "LabelAlbumArtists": "Izvajalci albuma:", "LabelAll": "Vse", "LabelCustomRating": "Prilagojena ocena:", - "LabelDashboardTheme": "Tema nadzorne plošče strežnika:", "LabelBirthDate": "Datum rojstva:", "LabelCache": "Predpomnilnik:", "LabelCachePath": "Pot predpomnilnika:", @@ -879,13 +869,11 @@ "PleaseSelectTwoItems": "Prosimo izberite vsaj dva elementa.", "Premieres": "Premiere", "Producer": "Producent", - "QueueAllFromHere": "Dodaj vse tukaj v čakalno vrsto", "Premiere": "Premiera", "OptionRuntime": "Trajanje", "OptionSaturday": "Sobota", "MediaInfoLayout": "Razporeditev", "Like": "Všeč mi je", - "LinksValue": "Povezave: {0}", "LabelPlayDefaultAudioTrack": "Predvajaj privzeti zvočni posnetek ne glede na jezik", "LabelOriginalTitle": "Izvirni naslov:", "LabelRefreshMode": "Način osveževanja:", @@ -958,7 +946,6 @@ "MessageNoAvailablePlugins": "Dodatki niso na voljo.", "MessageInvalidUser": "Napačno uporabniško ime ali geslo. Prosimo poskusite znova.", "MessageInvalidForgotPasswordPin": "Vnesena je bila napačna ali pretečena PIN koda. Prosimo, poskusite znova.", - "MessageInstallPluginFromApp": "Ta dodatek mora biti nameščen znotraj aplikacije, v kateri ga nameravate uporabljati.", "MessageImageTypeNotSelected": "Prosimo izberite tip slike v spustnem meniju.", "MessageImageFileTypeAllowed": "Podprte so zgolj JPEG in PNG datoteke.", "MessageForgotPasswordFileCreated": "Sledeča datoteka je bila ustvarjena na vašem strežniku in vsebuje navodila za nadaljevanje:", @@ -988,7 +975,6 @@ "MediaInfoStreamTypeEmbeddedImage": "Vdelana sličica", "MediaInfoStreamTypeData": "Podatki", "MediaInfoStreamTypeAudio": "Zvok", - "MediaInfoSoftware": "Programska oprema", "MediaInfoTimestamp": "Časovni žig", "MediaInfoSize": "Velikost", "MediaInfoSampleRate": "Vzorčna hitrost", @@ -1086,7 +1072,6 @@ "Mute": "Utišaj", "MoveLeft": "Premakni levo", "MoveRight": "Premakni desno", - "LabelSkin": "Preobleka:", "LabelSize": "Velikost:", "LabelSimultaneousConnectionLimit": "Omejitev števila sočasnih predvajanj:", "LabelServerName": "Ime strežnika:", @@ -1125,7 +1110,6 @@ "LabelNumberOfGuideDays": "Število dni vodiča za prenos:", "LabelNumber": "Številka:", "LabelNotificationEnabled": "Omogoči to obvestilo", - "CopyStreamURLError": "Pri kopiranju naslova URL je prišlo do napake.", "AskAdminToCreateLibrary": "Prosite skrbnika, da ustvari knjižnico.", "AllowFfmpegThrottlingHelp": "Ko je prekodiranja dovolj pred mestom predvajanja se proces ustavi, da bo porabljal manj sredstev. To je najbolj uporabno pri gledanju brez pogostega premikanja mesta predvajanja. Če naletite na težave s predvajanjem onemogočite to možnost.", "AllowFfmpegThrottling": "Zaviraj prekodiranje", @@ -1136,7 +1120,6 @@ "LabelStartWhenPossible": "Začni, ko je mogoče:", "LabelSportsCategories": "Športne kategorije:", "LabelSource": "Vir:", - "LabelSoundEffects": "Zvočni učinki:", "LabelSkipIfGraphicalSubsPresentHelp": "Ohranjanje besedilnih različic podnapisov omogoča učinkovitejše predvajanje in zmanjša potrebo po prekodiranju.", "LabelTriggerType": "Tip sprožilca:", "LabelTranscodingVideoCodec": "Video kodek:", @@ -1195,8 +1178,6 @@ "LibraryAccessHelp": "Izberite knjižnice, ki bodo deljenje s tem uporabnikom. Upravitelji bodo lahko urejali metapodatke z upraviteljem metapodatkov.", "LeaveBlankToNotSetAPassword": "To polje lahko pustite prazno za uporabo brez gesla.", "LearnHowYouCanContribute": "Poglejte, kako lahko pomagate.", - "LaunchWebAppOnStartupHelp": "Ob prvem zagonu strežnika se bo v privzetem brskalniku odprl spletni vmesnik. To se ne bo zgodilo pri uporabi možnosti za ponovni zagon.", - "LaunchWebAppOnStartup": "Ob zagonu strežnika zaženi spletni vmesnik", "LabelffmpegPathHelp": "Pot do datoteke aplikacije ffmpeg ali mape, ki jo vsebuje.", "LabelffmpegPath": "Pot FFmpeg:", "LabelZipCode": "Poštna številka:", @@ -1278,7 +1259,6 @@ "LabelRepositoryUrl": "URL repozitorija", "HeaderNewRepository": "Nov repozitorij", "MessageNoRepositories": "Ni repozitorijev.", - "MessageUnauthorizedUser": "Trenutno nimate dovoljenja za dostop do tega strežnika. Kontaktirajte skrbnika strežnika za več informacij.", "MediaInfoAspectRatio": "Razmerje stranic", "MediaInfoAnamorphic": "Anamorfno", "MaxParentalRatingHelp": "Vsebine z višjo oceno bodo za tega uporabnika skrite.", @@ -1294,7 +1274,6 @@ "LabelUserAgent": "Uporabniški agent:", "EnableFasterAnimationsHelp": "Uporabi hitrejše animacije in prehode", "EnableFasterAnimations": "Hitrejše animacije", - "LabelNightly": "Nestabilna", "LabelStable": "Stabilna", "LabelChromecastVersion": "Različica Chromecast", "LabelLibraryPageSizeHelp": "Nastavi število prikazanih vsebin na strani knjižnice. Nastavite na 0 za neskončno dolgo stran.", @@ -1317,7 +1296,6 @@ "SaveChanges": "Shrani spremembe", "Save": "Shrani", "Saturday": "sobota", - "RunAtStartup": "Zaženi ob zagonu", "Rewind": "Previj nazaj", "ReplaceExistingImages": "Zamenjaj obstoječe slike", "ReplaceAllMetadata": "Zamenjaj vse metapodatke", @@ -1328,7 +1306,6 @@ "RememberMe": "Zapomni si me", "RecommendationDirectedBy": "Režija", "Person": "Oseba", - "OtherArtist": "Drugi izvajalci", "OptionProfileVideo": "Video", "OptionPoster": "Plakat", "OptionNone": "Nič", diff --git a/src/strings/sr.json b/src/strings/sr.json index df90a3c0f8..4682f060f1 100644 --- a/src/strings/sr.json +++ b/src/strings/sr.json @@ -27,7 +27,6 @@ "AddToPlaylist": "Додај на листу пуштања", "AddToPlayQueue": "Додај у ред за пуштање", "AddToCollection": "Додај у колекцију", - "AddItemToCollectionHelp": "Додајте ставке у колекцију претрагом па затим десним кликом у менију изаберите да додате у колекцију.", "Add": "Додај", "Actor": "Глумац", "AccessRestrictedTryAgainLater": "Приступ је тренутно ограничен. Покушајте поново касније.", @@ -42,7 +41,6 @@ "ButtonMore": "Више", "ButtonManualLogin": "Ручни логин", "ButtonLibraryAccess": "Приступ библиотеци", - "ButtonLearnMore": "Научи више", "ButtonInfo": "Информације", "ButtonHome": "Почетна страна", "ButtonHelp": "Помоћ", @@ -78,7 +76,6 @@ "BirthPlaceValue": "Место рођења: {0}", "BirthLocation": "Место рођења", "BirthDateValue": "Рођен", - "AutoBasedOnLanguageSetting": "Аутоматски (зависи од подешавања језика)", "Audio": "Звук", "AttributeNew": "Ново", "AroundTime": "Около", @@ -103,7 +100,6 @@ "DateAdded": "Датум додавања", "CustomDlnaProfilesHelp": "Направите прилагођени профил да бисте циљали на нови уређај или прегазили системски профил.", "CriticRating": "Оцена критике", - "CopyStreamURLError": "Десила се грешка приликом копирања адресе.", "CopyStreamURLSuccess": "Адреса копирана успешно.", "CopyStreamURL": "Копирајте адресу стрим-а", "Continuing": "Наставља", @@ -130,7 +126,6 @@ "CancelSeries": "Откажи серију", "CancelRecording": "Откажи снимање", "ButtonWebsite": "Веб сајт", - "ButtonViewWebsite": "Погледајте веб сајт", "ButtonUp": "Горе", "ButtonUninstall": "Деинсталирај", "ButtonTrailer": "Трејлер", diff --git a/src/strings/sv.json b/src/strings/sv.json index 16357498af..ef4e849e40 100644 --- a/src/strings/sv.json +++ b/src/strings/sv.json @@ -2,7 +2,6 @@ "AccessRestrictedTryAgainLater": "För närvarande är åtkomsten begränsad. Försök igen senare.", "Actor": "Skådespelare", "Add": "Lägg till", - "AddItemToCollectionHelp": "Lägg till objekt i samlingar genom att söka efter dem och använda deras högerklick- eller pekmeny för att lägga till dem i en samling.", "AddToCollection": "Lägg till i samling", "AddToPlayQueue": "Lägg till i spelkö", "AddToPlaylist": "Lägg till i spellista", @@ -33,7 +32,6 @@ "AspectRatio": "Bildförhållande", "AttributeNew": "Ny", "Audio": "Ljud", - "AutoBasedOnLanguageSetting": "Automatisk (baserad på språkinställning)", "Backdrop": "Fondbild", "Backdrops": "Fondbilder", "Banner": "Banderoll", @@ -74,7 +72,6 @@ "ButtonGotIt": "Ok", "ButtonHelp": "Hjälp", "ButtonHome": "Hem", - "ButtonLearnMore": "Läs mer", "ButtonLibraryAccess": "Biblioteksåtkomst", "ButtonManualLogin": "Manuell inloggning", "ButtonMore": "Mer", @@ -118,7 +115,6 @@ "ButtonSubtitles": "Undertexter", "ButtonUninstall": "Avinstallera", "ButtonUp": "Upp", - "ButtonViewWebsite": "Gå till hemsidan", "ButtonWebsite": "Hemsida", "CancelRecording": "Avbryt inspelning", "CancelSeries": "Avbryt serie", @@ -263,7 +259,6 @@ "HeaderApiKeysHelp": "Externa applikationer behöver en API-nyckel för att kommunicera med Jellyfin servern. Nycklar skapas genom att logga in med ett Jellyfin-konto eller genom att manuellt skapa en nyckel till applikationen.", "HeaderAudioBooks": "Ljudböcker", "HeaderAudioSettings": "Ljudinställningar", - "HeaderAutomaticUpdates": "Automatiska uppdateringar", "HeaderBlockItemsWithNoRating": "Blockera innehåll med ingen eller okänd åldersgräns:", "HeaderBooks": "Böcker", "HeaderCancelRecording": "Avbryt inspelning", @@ -472,8 +467,6 @@ "LabelAlbumArtists": "Albumartist:", "LabelAll": "Alla", "LabelAllowHWTranscoding": "Tillåt hårdvaruomkodning", - "LabelAllowServerAutoRestart": "Tillåt att servern startas om automatiskt efter uppdateringar", - "LabelAllowServerAutoRestartHelp": "Servern startas om endast då inga användare är inloggade.", "LabelAppName": "Appens namn", "LabelAppNameExample": "Exempel: Sickbeard, Sonarr", "LabelArtists": "Artister:", @@ -508,7 +501,6 @@ "LabelCustomDeviceDisplayName": "Visningsnamn:", "LabelCustomDeviceDisplayNameHelp": "Ange ett anpassat enhetsnamn. Lämna blankt för att använda det namn enheten själv rapporterar.", "LabelCustomRating": "Anpassad åldersgräns:", - "LabelDashboardTheme": "Kontrollpanelstema:", "LabelDateAdded": "Inlagd den:", "LabelDateAddedBehavior": "Hantering av datum för nytt innehåll:", "LabelDateAddedBehaviorHelp": "Om ett metadatavärde finns kommer det att användas i stället för dessa.", @@ -716,7 +708,6 @@ "LabelServerHost": "Värd:", "LabelServerHostHelp": "192.168.1.100:8096 eller https://min.server.com", "LabelSimultaneousConnectionLimit": "Begränsning för samtidiga strömmar:", - "LabelSkin": "Skal:", "LabelSkipBackLength": "'Hoppa bakåt'-längd:", "LabelSkipForwardLength": "'Hoppa framåt'-längd:", "LabelSkipIfAudioTrackPresent": "Hoppa över om det förvalda ljudspårets språk är samma som det hämtade", @@ -728,7 +719,6 @@ "LabelSortBy": "Sortera efter:", "LabelSortOrder": "Sortering:", "LabelSortTitle": "Sorteringstitel:", - "LabelSoundEffects": "Ljudeffekter:", "LabelSource": "Källa:", "LabelSpecialSeasonsDisplayName": "Visningsnamn för specialsäsong:", "LabelSportsCategories": "Sportkategorier:", @@ -774,7 +764,6 @@ "LabelXDlnaCapHelp": "Anger innehållet i elementet X_DLNACAP i namnutrymmet urn:schemas-dlna-org:device-1-0.", "LabelXDlnaDocHelp": "Anger innehållet i elementet X_DLNADOC i namnutrymmet urn:schemas-dlna-org:device-1-0.", "LabelYear": "År:", - "LabelYourFirstName": "Ditt förnamn:", "LabelYoureDone": "Klart!", "LabelZipCode": "Postnummer:", "LabelffmpegPath": "FFmpeg-sökväg:", @@ -784,7 +773,6 @@ "LearnHowYouCanContribute": "Se hur du kan hjälpa till.", "LibraryAccessHelp": "Ange vilka mediemappar den här användaren ska ha tillgång till. Administratörer har rätt att redigera alla mappar i metadatahanteraren.", "Like": "Gilla", - "LinksValue": "Länkar: {0}", "List": "Lista", "LiveBroadcasts": "Livesändningar", "LiveTV": "Live-TV", @@ -839,7 +827,6 @@ "MessageFileReadError": "Ett fel uppstod när filen lästes in. Var god försök igen.", "MessageForgotPasswordFileCreated": "Följande fil har skapats på din server och innehåller information om hur du går vidare:", "MessageForgotPasswordInNetworkRequired": "Försök igen innanför ditt hemma-nätverk, att starta återställningen av lösenordet.", - "MessageInstallPluginFromApp": "Detta tillägg måste installeras inifrån den app det skall användas i.", "MessageInvalidForgotPasswordPin": "Koden har gått ut eller så är den felaktig. Försök igen.", "MessageInvalidUser": "Felaktigt användarnamn eller lösenord. Försök igen.", "MessageItemSaved": "Objektet har sparats.", @@ -1066,7 +1053,6 @@ "ProductionLocations": "Produktionsplatser", "Programs": "Program", "Quality": "Kvalitet", - "QueueAllFromHere": "Köa alla fr o m här", "Raised": "Upphöjd", "Rate": "Betygsätt", "RecentlyWatched": "Nyligen sedda", @@ -1095,7 +1081,6 @@ "ReplaceAllMetadata": "Ersätt all metadata", "ReplaceExistingImages": "Skriv över befintliga bilder", "ResumeAt": "Återuppta från {0}", - "RunAtStartup": "Kör vid uppstart", "Runtime": "Speltid", "Saturday": "Lördag", "Save": "Spara", @@ -1182,7 +1167,6 @@ "TabParentalControl": "Föräldralås", "TabPassword": "Lösenord", "TabPlayback": "Uppspelning", - "TabPlaylist": "Spellista", "TabPlaylists": "Spellistor", "TabPlugins": "Tillägg", "TabProfile": "Profil", @@ -1350,10 +1334,8 @@ "HeaderParentalRatings": "Föräldrabetyg", "HeaderNavigation": "Navigering", "HeaderBranding": "Märke", - "CopyStreamURLError": "Kunde inte kopiera videoadress.", "AskAdminToCreateLibrary": "Fråga en administratör för att skapa ett bibliotek.", "Whitelist": "Vitlista", - "VideoRange": "Video räckvidd", "ValueOneAlbum": "1 album", "ValueMinutes": "{0} min", "ValueContainer": "Behållare: {0}", @@ -1424,14 +1406,11 @@ "MediaInfoStreamTypeEmbeddedImage": "Inbäddad bild", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeAudio": "Ljud", - "MediaInfoSoftware": "Mjukvara", "MediaInfoLayout": "Design", "MediaInfoContainer": "Behållare", "ManageLibrary": "Hantera bibliotek", "Live": "Live", "LeaveBlankToNotSetAPassword": "Du kan lämna detta fält tomt för att inte ange lösenord.", - "LaunchWebAppOnStartupHelp": "Öppna webbgränssnittet i din standardwebbläsare när servern först startar. Detta händer inte när du använder starta om-funktionen.", - "LaunchWebAppOnStartup": "Öppna webbgränssnittet när servern startar", "LanNetworksHelp": "Kommatecken separerad lista på IP adresser eller IP/nätmask inlägg för nätverk som anses vara på lokala nätverket för att tvinga fram bandbredd begränsningar. Om angett, alla andra IP adresser kommer att anses vara på ett externt nätverk och kommer tilldelas till det externa bandbredd begränsningarna. Om lämnat tomt, endast serverns subnet anses vara på det lokala nätverket.", "LabelXDlnaDoc": "X-DLNA-dokumentation:", "LabelXDlnaCap": "X-DLNA-begränsning:", @@ -1468,11 +1447,9 @@ "PathNotFound": "Sökvägen hittades inte. Säkerställ att sökvägen är korrekt och försök igen.", "Track": "Spår", "Season": "Säsong", - "ReleaseGroup": "Releasegrupp", "PreferEmbeddedEpisodeInfosOverFileNames": "Föredra inbäddad avsnittsinformation före filnamn", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Detta använder avsnittets information från inbäddad metadata om tillgängligt.", "Person": "Person", - "OtherArtist": "Annan artist", "Movie": "Film", "Episode": "Avsnitt", "ClientSettings": "Klientinställningar", @@ -1489,7 +1466,6 @@ "Yadif": "YADIF", "Filter": "Filter", "New": "Ny", - "MessageUnauthorizedUser": "Du har inte behörighet att komma åt servern just nu. Kontakta din serveradministratör för mer information.", "HeaderFavoritePlaylists": "Favoritspellista", "OnWakeFromSleep": "Vid start från vilande läge", "UnsupportedPlayback": "Jellyfin kan inte dekryptera inehåll skyddat av DRM men allt inehåll kommer ändå försökas, även skyddade titlar. Vissa filer kan se helt svarta ut på grund av kryptering eller andra funktioner som inte stöds, till exempel interaktiva titlar.", @@ -1563,6 +1539,5 @@ "MessageSyncPlayDisabled": "SyncPlay avaktiverat.", "MessageSyncPlayEnabled": "SyncPlay aktiverat.", "MessageNoGenresAvailable": "Aktivera vissa metadataleverantörer att hämta genrer från internet.", - "LabelRepositoryNameHelp": "Ett eget namn för att särskilja denna förvaringsplats från andra tillagda på din server.", - "LabelNightly": "Nattlig" + "LabelRepositoryNameHelp": "Ett eget namn för att särskilja denna förvaringsplats från andra tillagda på din server." } diff --git a/src/strings/tr.json b/src/strings/tr.json index 194ff3c1c5..8936fa6463 100644 --- a/src/strings/tr.json +++ b/src/strings/tr.json @@ -58,7 +58,6 @@ "Friday": "Cuma", "HeaderActiveRecordings": "Aktif Kayıtlar", "HeaderAddUser": "Kullanıcı Ekle", - "HeaderAutomaticUpdates": "Otomatik Güncelleme", "HeaderChannels": "Kanallar", "HeaderCodecProfile": "Codec Profili", "HeaderContinueWatching": "İzlemeye Devam Et", @@ -92,7 +91,6 @@ "HeaderTaskTriggers": "Görev tetikleyicileri", "HeaderTranscodingProfile": "Kodlama Profili", "HeaderUsers": "Kullanıcılar", - "LabelAllowServerAutoRestart": "Bu sunucuya güncellemeleri uygulamak için yeniden başlama izni ver", "LabelArtists": "Sanatçılar:", "LabelAudioLanguagePreference": "Ses Dili Tercihi:", "LabelCachePath": "Önbellek Yolu:", @@ -130,7 +128,6 @@ "LabelUser": "Kullanıcı:", "LabelUserLibrary": "Kullanıcı Kütüphanesi:", "LabelYear": "Yıl:", - "LabelYourFirstName": "İlk Ad:", "LabelYoureDone": "Bitti!", "LibraryAccessHelp": "Bu kullanıcı ile paylaşmak için kütüphaneleri seçin. Yöneticiler meta yöneticisini kullanarak tüm klasörleri düzenlemesi mümkün olacaktır.", "Live": "Canlı", @@ -227,7 +224,6 @@ "TabNetworks": "Ağlar", "TabNotifications": "Bildirimler", "TabPassword": "Şifre", - "TabPlaylist": "Oynatma listesi", "TabProfile": "Profil", "TabProfiles": "Profiller", "TabRecordings": "Kayıtlar", @@ -287,7 +283,6 @@ "AllEpisodes": "Tüm bölümler", "AllLanguages": "Tüm diller", "AllowMediaConversion": "Medya dönüşümüne izin ver", - "AddItemToCollectionHelp": "Ögeleri koleksiyona eklemek için; arama yapın ve üzerine sağ tıklayın veya sekme menüsünden koleksiyona ekleyin.", "AllowHWTranscodingHelp": "Ayarlayıcının anında akışları dönüştürmesine izin verin. Bu, sunucunun gerektirdiği kodlamanın azaltılmasına yardımcı olabilir.", "ColorSpace": "Renk uzayı", "ButtonConnect": "Bağlan", @@ -348,7 +343,6 @@ "ButtonArrowLeft": "Sol", "ButtonDown": "Aşağı", "ButtonGuide": "Rehber", - "ButtonLearnMore": "Daha fazla bilgi edin", "ButtonLibraryAccess": "Kütüphane erişimi", "ButtonScanAllLibraries": "Tüm Kütüphaneleri Tara", "ButtonSelectView": "Görünüm seç", @@ -387,7 +381,6 @@ "AspectRatio": "En/Boy oranı", "Audio": "Ses", "AuthProviderHelp": "Bu kullanıcının şifresini doğrulamak için kullanılacak bir Kimlik Doğrulama Sağlayıcısı seçin.", - "AutoBasedOnLanguageSetting": "Otomatik (dil ayarına göre)", "Backdrop": "zemin", "Backdrops": "Zeminler", "Banner": "afiş", @@ -402,7 +395,6 @@ "ButtonStart": "Başlat", "ButtonTrailer": "Fragman", "Box": "Kutu", - "ButtonViewWebsite": "Web sitesini görüntüle", "CancelRecording": "Kayıttan Vazgeç", "CancelSeries": "Dizileri iptal et", "ButtonUninstall": "Kaldır", @@ -703,7 +695,6 @@ "HeaderSelectTranscodingPathHelp": "Geçici Video Kodlama dosyaları için bir dosya yolu seçin yada yazın. Dosya yoluna yazma yetkisi gereklidir.", "HeaderSelectTranscodingPath": "Video Kodlaması İçin Geçici Dosya Yolu Seç", "HeaderSelectServerCachePathHelp": "Önbellek dosyaları için bir dosya yolu seçin yada yazın. Dosya yoluna yazma yetkisi gereklidir.", - "CopyStreamURLError": "URL kopyalanırken bir hata oluştu.", "OptionNone": "Hiçbiri", "None": "Hiçbiri", "HeaderNavigation": "Navigasyon", @@ -759,7 +750,6 @@ "LabelStreamType": "Akış türü:", "LabelSubtitleDownloaders": "Altyazı indiriciler:", "LabelStopping": "Durduruluyor", - "LabelSoundEffects": "Ses efektleri:", "LabelSortOrder": "Sıralama düzeni:", "LabelSortBy": "Sıralama türü:", "LabelSkipIfGraphicalSubsPresent": "Video halihazırda gömülü altyazı barındırıyorsa atla", diff --git a/src/strings/uk.json b/src/strings/uk.json index 0b93964474..95948074ea 100644 --- a/src/strings/uk.json +++ b/src/strings/uk.json @@ -17,7 +17,6 @@ "FolderTypeMusic": "Музика", "FolderTypeTvShows": "ТБ", "HeaderAlbums": "Альбоми", - "HeaderAutomaticUpdates": "Автоматичне оновлення", "HeaderBooks": "Книги", "HeaderDeleteDevice": "Видалить пристрій", "HeaderLatestEpisodes": "Нещодавно переглянуті серії", @@ -40,7 +39,6 @@ "LabelNext": "Вперед", "LabelPath": "Шлях:", "LabelPrevious": "Назад", - "LabelYourFirstName": "Ім’я:", "Like": "Подобається", "MediaInfoAspectRatio": "Співвідношення сторін", "MediaInfoChannels": "Канали", @@ -127,7 +125,6 @@ "HeaderFavoriteArtists": "Улюблені виконавці", "HeaderFavoriteShows": "Улюблені шоу", "HeaderContinueWatching": "Продовжити перегляд", - "AddItemToCollectionHelp": "Додайте елементи до колекції за допомогою пошуку або кліком правої кнопкої миші чи натисненням на меню.", "AllowedRemoteAddressesHelp": "Список з комами, в якості розділювачів, визначає IP-адреси та IP/мережеві маски для мереж, яким дозволено підключатись віддалено. Якщо залишити строку пустою, то усі віддалені підключення будуть дозволені.", "AllowRemoteAccessHelp": "Якщо не відмічено прапорцем, усі віддалені підключення будуть заблоковані.", "AllowFfmpegThrottling": "Примусово обмежити перекодування", @@ -145,7 +142,6 @@ "Blacklist": "Чорний список", "BirthLocation": "Місце народження", "Banner": "Обкладинка", - "AutoBasedOnLanguageSetting": "Автоматично (на основі поточної мови)", "Auto": "Автоматично", "AuthProviderHelp": "Оберіть сервіс аутентифікації, який буде використаний з поточним паролем користувача.", "Audio": "Аудіо", diff --git a/src/strings/vi.json b/src/strings/vi.json index 823eca0a9d..b4d79f267b 100644 --- a/src/strings/vi.json +++ b/src/strings/vi.json @@ -23,7 +23,6 @@ "FileReadError": "Có một lỗi xảy ra khi đọc tệp tin này.", "FolderTypeTvShows": "TV", "HeaderAddUser": "Thêm người dùng", - "HeaderAutomaticUpdates": "Tự động cập nhật", "HeaderCustomDlnaProfiles": "Hồ sơ khách hàng", "HeaderFeatureAccess": "Truy cập tính năng", "HeaderFrequentlyPlayed": "Phát thường xuyên", @@ -34,7 +33,6 @@ "HeaderStatus": "Trạng thái", "HeaderSystemDlnaProfiles": "Hồ sơ hệ thống", "HeaderUsers": "dùng", - "LabelAllowServerAutoRestart": "Cho phép máy chủ tự động khởi động lại để áp dụng các bản cập nhật", "LabelAudioLanguagePreference": "Ngôn ngữ thoại ưa thích:", "LabelCountry": "Quốc gia:", "LabelCurrentPassword": "Mật khẩu hiện tại:", @@ -51,7 +49,6 @@ "LabelSaveLocalMetadata": "Lưu các ảnh nghệ thuật và metadata vào trong các thư mục media", "LabelSaveLocalMetadataHelp": "Lưu các ảnh nghệ thuật và metadata vào trong các thư mục media, sẽ đưa chúng vào một nơi bạn có thể chỉnh sửa dễ dàng hơn.", "LabelTime": "Thời gian:", - "LabelYourFirstName": "Tên của bạn:", "LabelYoureDone": "Bạn đã hoàn thành!", "MaxParentalRatingHelp": "Nội dung với đánh giá cao hơn sẽ được ẩn đi từ người dùng này.", "MessageNothingHere": "Không có gì ở đây.", @@ -140,7 +137,6 @@ "ButtonMore": "Thêm", "ButtonManualLogin": "Đăng nhập thủ công", "ButtonLibraryAccess": "Truy cập thư viện", - "ButtonLearnMore": "Tìm hiểu thêm", "ButtonInfo": "Thông tin", "ButtonHome": "Trang chủ", "ButtonHelp": "Giúp đỡ", @@ -178,7 +174,6 @@ "BirthDateValue": "Sinh năm: {0}", "Backdrops": "Phông nền", "Backdrop": "Phông nền", - "AutoBasedOnLanguageSetting": "Tự động (dựa trên cài đặt ngôn ngữ)", "Auto": "Tự động", "AuthProviderHelp": "Chọn Nhà cung cấp xác thực sẽ được sử dụng để xác thực mật khẩu người dùng này.", "Audio": "Âm thanh", @@ -214,7 +209,6 @@ "AddedOnValue": "Đã thêm {0}", "AddToPlaylist": "Thêm vào danh sách phát", "AddToPlayQueue": "Thêm vào hàng đợi", - "AddItemToCollectionHelp": "Thêm các mục vào bộ sưu tập bằng cách tìm kiếm và nhấp chuột phải hoặc nhấn vào menu để thêm chúng vào bộ sưu tập.", "Absolute": "Tuyệt đối", "ButtonSend": "Gửi", "ButtonSelectView": "Chọn chế độ xem", @@ -225,7 +219,6 @@ "Categories": "Phân loại", "CancelRecording": "Ngưng ghi hình", "ButtonWebsite": "Trang web", - "ButtonViewWebsite": "Xem trang web", "ButtonUp": "Lên", "ButtonUninstall": "Gỡ cài đặt", "ButtonTrailer": "Tóm tắt", @@ -244,7 +237,6 @@ "DateAdded": "Ngày thêm vào", "CustomDlnaProfilesHelp": "Tạo một bộ thiết lập tuỳ chọn dành cho một thiết bị mới hoặc thay thế một thiết lập hệ thống.", "CriticRating": "Đánh giá phê bình", - "CopyStreamURLError": "Có lỗi xảy ra lúc sao chép URL.", "CopyStreamURLSuccess": "URL đã được sao chép.", "CopyStreamURL": "Sao Chép URL Phát Sóng", "Continuing": "Tiếp tục", @@ -616,7 +608,6 @@ "LabelAppName": "Tên ứng dụng", "LabelAllowedRemoteAddressesMode": "Chế độ bộ lọc địa chỉ IP từ xa:", "LabelAllowedRemoteAddresses": "Bộ lọc địa chỉ IP từ xa:", - "LabelAllowServerAutoRestartHelp": "Máy chủ chỉ khởi động lại trong thời gian rỗi khi không có người dùng đang sử dụng.", "LabelAllowHWTranscoding": "Cho phép chuyển mã bằng phần cứng", "LabelAll": "Tất Cả", "LabelAlbumArtists": "Nghệ sĩ album:", @@ -700,7 +691,6 @@ "LabelDateAddedBehaviorHelp": "Nếu có giá trị dữ liệu bổ trợ, nó sẽ luôn được sử dụng trước một trong các tùy chọn này.", "LabelDateAddedBehavior": "Ngày thêm hành vi cho nội dung mới:", "LabelDateAdded": "Ngày thêm vào:", - "LabelDashboardTheme": "Chủ đề bảng điều khiển máy chủ:", "LabelCustomRating": "Đánh giá tuỳ chọn:", "HeaderFavoritePlaylists": "Danh Sách Phát Yêu Thích", "ApiKeysCaption": "Danh sách các mã API đang hoạt động", diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index eaf358a6ce..553445404a 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -2,7 +2,6 @@ "AccessRestrictedTryAgainLater": "目前访问受限,请稍后再试。", "Actor": "演员", "Add": "添加", - "AddItemToCollectionHelp": "通过搜索项目并右键或轻触得到的弹出菜单来将项目添加到集合中。", "AddToCollection": "加入集合", "AddToPlayQueue": "添加至播放队列", "AddToPlaylist": "添加至播放列表", @@ -36,7 +35,6 @@ "AttributeNew": "新增", "Audio": "音频", "Auto": "自动", - "AutoBasedOnLanguageSetting": "自动(取决于语言设置)", "Backdrop": "背景", "Backdrops": "背景", "Banner": "横幅", @@ -78,7 +76,6 @@ "ButtonHelp": "帮助", "ButtonHome": "首页", "ButtonInfo": "详情", - "ButtonLearnMore": "了解更多", "ButtonLibraryAccess": "媒体库访问", "ButtonManualLogin": "手动登录", "ButtonMore": "更多", @@ -124,7 +121,6 @@ "ButtonTrailer": "预告片", "ButtonUninstall": "卸载", "ButtonUp": "上", - "ButtonViewWebsite": "浏览网站", "ButtonWebsite": "网站", "CancelRecording": "取消录制", "CancelSeries": "取消系列", @@ -268,7 +264,6 @@ "HeaderApiKeysHelp": "外部应用程序需要 API 密钥才能与 Jellyfin Server 进行通信。使用 Jellyfin 账户进行登录时密钥将会自动生成,您也可以手动为某个应用程序分配一个密钥。", "HeaderAudioBooks": "有声读物", "HeaderAudioSettings": "声音设置", - "HeaderAutomaticUpdates": "自动更新", "HeaderBlockItemsWithNoRating": "通过没有评级和设置不允许的评级锁定内容:", "HeaderBooks": "书籍", "HeaderBranding": "品牌", @@ -476,8 +471,6 @@ "LabelAlbumArtists": "专辑作家:", "LabelAll": "所有", "LabelAllowHWTranscoding": "允许硬件转码", - "LabelAllowServerAutoRestart": "允许服务器自动重启来安装更新", - "LabelAllowServerAutoRestartHelp": "该服务器仅会在空闲和没有活动用户的期间重新启动。", "LabelAllowedRemoteAddresses": "远程IP地址过滤器:", "LabelAllowedRemoteAddressesMode": "远程IP地址过滤器模式:", "LabelAppName": "APP名称", @@ -515,7 +508,6 @@ "LabelCustomDeviceDisplayName": "显示名称:", "LabelCustomDeviceDisplayNameHelp": "自定义设备显示名称或留空则使用设备报告名称。", "LabelCustomRating": "自定义分级:", - "LabelDashboardTheme": "控制台主题:", "LabelDateAdded": "加入日期:", "LabelDateAddedBehavior": "新内容加入的日期:", "LabelDateAddedBehaviorHelp": "如果一个媒体资料的值存在,它总是优先于这些选项前使用。", @@ -726,7 +718,6 @@ "LabelServerHost": "主机:", "LabelServerHostHelp": "192.168.1.100:8096 或 https://myserver.com", "LabelSimultaneousConnectionLimit": "并发流限制:", - "LabelSkin": "皮肤:", "LabelSkipBackLength": "跳过长度:", "LabelSkipForwardLength": "快进时限:", "LabelSkipIfAudioTrackPresent": "如果默认音轨的语言和下载语言一样则跳过", @@ -738,7 +729,6 @@ "LabelSortBy": "排序依据:", "LabelSortOrder": "排序顺序:", "LabelSortTitle": "短标题:", - "LabelSoundEffects": "音效:", "LabelSource": "来源:", "LabelSpecialSeasonsDisplayName": "SP 季显示的名称:", "LabelSportsCategories": "体育分类:", @@ -793,7 +783,6 @@ "LabelXDlnaDoc": "X-DLNA DOC:", "LabelXDlnaDocHelp": "决定在 urn:schemas-dlna-org:device-1-0 namespace 中的 X-Dlna doc 元素的内容。", "LabelYear": "年份:", - "LabelYourFirstName": "你的名字:", "LabelYoureDone": "完成!", "LabelZipCode": "邮编:", "LabelffmpegPath": "FFmpeg 路径:", @@ -857,7 +846,6 @@ "MessageFileReadError": "读取文件发生错误。", "MessageForgotPasswordFileCreated": "已在服务器上创建了以下文件, 并包含有关后续步骤说明:", "MessageForgotPasswordInNetworkRequired": "请连接你的家庭网络后再试一次以开始密码重置流程。", - "MessageInstallPluginFromApp": "这个插件必须从你打算使用的应用程序中安装。", "MessageInvalidForgotPasswordPin": "无效的或过期的 pin 码。请再试一次。", "MessageInvalidUser": "用户名或密码不可用。请重试。", "MessageItemSaved": "项目已保存。", @@ -1097,7 +1085,6 @@ "ProductionLocations": "产地", "Programs": "节目", "Quality": "质量", - "QueueAllFromHere": "这里的全部内容都加入队列", "Rate": "评级", "RecentlyWatched": "最近观看", "RecommendationBecauseYouLike": "因为你喜欢 {0}", @@ -1124,7 +1111,6 @@ "ReplaceExistingImages": "替换现有图片", "ResumeAt": "恢复播放于{0}", "Rewind": "倒回", - "RunAtStartup": "开机时启动", "Runtime": "播放时长", "Saturday": "星期六", "Save": "保存", @@ -1211,7 +1197,6 @@ "TabParentalControl": "家长控制", "TabPassword": "密码", "TabPlayback": "播放", - "TabPlaylist": "播放列表", "TabPlaylists": "播放列表", "TabPlugins": "插件", "TabProfile": "个人配置", @@ -1357,12 +1342,10 @@ "LabelVideo": "视频", "LabelWeb": "网页:", "LeaveBlankToNotSetAPassword": "您可以将此字段留空以设置空密码。", - "LinksValue": "链接:{0}", "LiveBroadcasts": "直播", "LiveTV": "电视直播", "Logo": "商标", "ManageRecording": "管理录音", - "MediaInfoSoftware": "软件", "MediaInfoStreamTypeAudio": "音频", "MediaInfoStreamTypeData": "数据", "MediaInfoStreamTypeEmbeddedImage": "内嵌图片", @@ -1397,15 +1380,12 @@ "Smaller": "更小", "TagsValue": "标签:{0}", "Vertical": "垂直", - "VideoRange": "视频范围", "Depressed": "凹陷", "Uniform": "轮廓", "HeaderHome": "主页", "DashboardOperatingSystem": "操作系统:{0}", "DashboardArchitecture": "架构:{0}", "GroupVersions": "按版本分组", - "LaunchWebAppOnStartup": "当启动服务器时,打开Web界面", - "LaunchWebAppOnStartupHelp": "服务器启动时在默认浏览器中打开网页端。使用重启服务器功能时此项不生效。", "MusicAlbum": "音乐专辑", "MusicArtist": "音乐艺术家", "MusicVideo": "音乐视频", @@ -1448,7 +1428,6 @@ "ButtonSplit": "拆分", "SelectAdminUsername": "请为管理员账户选择一个用户名。", "HeaderNavigation": "导航", - "CopyStreamURLError": "复制URL地址时发生错误。", "MessageConfirmAppExit": "你要退出吗?", "OptionForceRemoteSourceTranscoding": "强制远程转码(像电视直播一样)", "NoCreatedLibraries": "看上去您还未创建任何资料库。{0} 您想现在创建一个吗? {1}", @@ -1461,9 +1440,7 @@ "ClientSettings": "客户端设置", "Track": "音轨", "Season": "季", - "ReleaseGroup": "发行组", "Person": "人物", - "OtherArtist": "其他艺术家", "Movie": "电影", "Episode": "剧集", "BoxSet": "套装", @@ -1489,7 +1466,6 @@ "LabelLibraryPageSize": "媒体库分页阈值:", "LabelLibraryPageSizeHelp": "设置媒体库页面每页要显示的最多媒体个数。设置为 0 以禁用分页。", "UnsupportedPlayback": "Jellyfin无法解密被DRM保护的内容,但仍然会尝试播放包括受保护内容在内的所有内容。某些文件由于被加密或包含不受支持的特性(如互动标题),在播放时可能显示为黑屏。", - "MessageUnauthorizedUser": "您目前无权访问服务器。更多有关信息,请与服务器管理员联系。", "Filter": "过滤", "New": "新的", "HeaderFavoritePlaylists": "收藏的播放列表", @@ -1508,7 +1484,6 @@ "LabelEnableHttps": "启用 HTTPS", "LabelChromecastVersion": "Chromecast版本", "HeaderDVR": "DVR", - "LabelNightly": "开发版", "MessageSyncPlayErrorAccessingGroups": "访问群组列表时发生错误。", "MessageSyncPlayLibraryAccessDenied": "对此内容的访问受到限制。", "MessageSyncPlayCreateGroupDenied": "需要权限以创建群组。", diff --git a/src/strings/zh-hk.json b/src/strings/zh-hk.json index 97e7b5633a..830ab8b400 100644 --- a/src/strings/zh-hk.json +++ b/src/strings/zh-hk.json @@ -47,7 +47,6 @@ "HeaderAddToCollection": "添加到收藏庫", "HeaderAddUser": "添加用戶", "HeaderAdditionalParts": "附加部份", - "HeaderAutomaticUpdates": "自動更新", "HeaderBooks": "書籍", "HeaderBranding": "界面", "HeaderCastCrew": "演員陣容", @@ -86,8 +85,6 @@ "Help": "幫助", "LabelAirsAfterSeason": "已播放劇集季度:", "LabelAirsBeforeSeason": "尚未播放劇集季度:", - "LabelAllowServerAutoRestart": "允許自動重新啟動來更新", - "LabelAllowServerAutoRestartHelp": "只在沒有活躍用戶和空檔時間重新啟動。", "LabelArtists": "藝人:", "LabelArtistsHelp": "分開多重使用 ;", "LabelAudioLanguagePreference": "首選音訊語言:", @@ -173,7 +170,6 @@ "LabelTriggerType": "觸發類型:", "LabelUser": "用戶:", "LabelVersionInstalled": "已安裝 {0}", - "LabelYourFirstName": "您的名字是:", "LabelYoureDone": "大功告成!", "LibraryAccessHelp": "選擇與此用戶共享媒體文件夾。管理員將能夠使用媒體資料瀏覽器而編輯所有文件夾。", "MaxParentalRatingHelp": "此用戶會被隱藏具有較高評價的家長評級內容。", @@ -306,7 +302,6 @@ "TabNotifications": "通知", "TabOther": "其它", "TabPassword": "密碼", - "TabPlaylist": "播放清單", "TabProfile": "簡介", "TabProfiles": "簡介", "TabRecordings": "錄影", @@ -341,7 +336,6 @@ "AsManyAsPossible": "越多越好", "Audio": "音頻", "Auto": "自動", - "AutoBasedOnLanguageSetting": "自動 (基於語言設定)", "BirthLocation": "出生地點", "AllLanguages": "全部語言", "All": "全部", @@ -400,7 +394,6 @@ "AdditionalNotificationServices": "瀏覽插件目錄以安裝其他通知服務。", "AddToPlayQueue": "添加到播放隊列", "AddToCollection": "添加到收藏", - "AddItemToCollectionHelp": "搜尋物件並使用右鍵點擊或點擊菜單將他們添加到收藏中。", "AccessRestrictedTryAgainLater": "目前存取受限。 請稍後再試。", "AllowFfmpegThrottling": "轉碼調節器", "Dislike": "不喜歡", @@ -422,7 +415,6 @@ "ErrorDefault": "處理此請求時發生錯誤,請稍後再嘗試。", "Default": "預設", "DateAdded": "日期已新增", - "CopyStreamURLError": "複製URL時發生錯誤。", "CopyStreamURLSuccess": "成功複製URL。", "CopyStreamURL": "複製直播URL", "ContinueWatching": "繼續觀看", @@ -437,7 +429,6 @@ "CancelSeries": "取消片集", "CancelRecording": "取消錄影", "ButtonWebsite": "網頁", - "ButtonViewWebsite": "瀏覽網頁", "ButtonUninstall": "解除安裝", "ButtonTrailer": "預告", "ButtonTogglePlaylist": "播放清單", @@ -461,7 +452,6 @@ "ButtonOpen": "開啟", "ButtonNetwork": "網絡", "ButtonMore": "更多", - "ButtonLearnMore": "了解更多", "ButtonInfo": "資訊", "ButtonHome": "主頁", "ButtonGuide": "教學", diff --git a/src/strings/zh-tw.json b/src/strings/zh-tw.json index 0bb3504023..086d163240 100644 --- a/src/strings/zh-tw.json +++ b/src/strings/zh-tw.json @@ -60,7 +60,6 @@ "HeaderAddUser": "增加使用者", "HeaderAdditionalParts": "附加部份", "HeaderAdmin": "管理", - "HeaderAutomaticUpdates": "自動更新", "HeaderCastCrew": "拍攝人員及演員", "HeaderChannels": "頻道", "HeaderCustomDlnaProfiles": "自訂設定檔", @@ -96,8 +95,6 @@ "HeaderUsers": "使用者", "Help": "說明", "ItemCount": "{0}個項目", - "LabelAllowServerAutoRestart": "允許伺服器自動重新啟動去安裝更新資料", - "LabelAllowServerAutoRestartHelp": "伺服器只會在沒有使用者在使用時重新啟動。", "LabelAudioLanguagePreference": "音頻語言偏好選項:", "LabelCachePath": "快取路徑:", "LabelCollection": "收藏櫃:", @@ -142,7 +139,6 @@ "LabelTime": "時間:", "LabelTriggerType": "觸發類型:", "LabelUser": "使用者:", - "LabelYourFirstName": "您的名字:", "LabelYoureDone": "完成,耶!", "LibraryAccessHelp": "選擇媒體資料夾與此使用者共享。管理員將可以使用中繼資料管理器編輯所有的媒體資料夾。", "Like": "喜歡", @@ -271,7 +267,6 @@ "TabMyPlugins": "我的插件", "TabNetworks": "網絡", "TabPassword": "密碼", - "TabPlaylist": "播放清單", "TabProfile": "設定", "TabProfiles": "設定", "TabRecordings": "錄影", @@ -324,7 +319,6 @@ "AttributeNew": "新增", "Audio": "音訊", "Auto": "自動", - "AutoBasedOnLanguageSetting": "自動(根據語言設定)", "Backdrop": "背景", "Backdrops": "背景", "Banner": "橫幅", @@ -340,7 +334,6 @@ "ButtonAudioTracks": "音軌", "ButtonBack": "返回", "ButtonChangeServer": "更換伺服器", - "AddItemToCollectionHelp": "利用搜尋並使用右鍵或點擊目錄將項目新增到收藏中。", "AddToCollection": "加入收藏", "AirDate": "播出日期", "Aired": "已播於", @@ -361,7 +354,6 @@ "ButtonFullscreen": "全螢幕", "ButtonHelp": "幫助", "ButtonInfo": "詳細資料", - "ButtonLearnMore": "瞭解更多", "ButtonLibraryAccess": "媒體庫存取", "ButtonManualLogin": "手動登入", "ButtonMore": "更多", @@ -392,7 +384,6 @@ "ButtonTrailer": "預告片", "ButtonUninstall": "解除安裝", "ButtonUp": "上", - "ButtonViewWebsite": "查看網站", "ButtonWebsite": "網站", "CancelRecording": "取消錄影", "CancelSeries": "取消系列", @@ -765,7 +756,6 @@ "HeaderSelectServerCachePathHelp": "瀏覽或者輸入路徑以用於伺服器快取檔案。請確保該資料夾可以被寫入。", "LabelCustomDeviceDisplayNameHelp": "指定自訂的顯示名稱,或者留空以使用設備自己報告的名稱。", "LabelCustomRating": "自訂分級:", - "LabelDashboardTheme": "控制台佈景主題:", "LabelDateAdded": "新增日期:", "LabelDateAddedBehavior": "新内容加入的日期應使用:", "LabelDateTimeLocale": "設定時區:", @@ -986,7 +976,6 @@ "LabelVideoBitrate": "影片比特率:", "MediaInfoSize": "大小", "MediaInfoTimestamp": "時間戳", - "MediaInfoSoftware": "軟體", "MediaInfoStreamTypeData": "檔案", "MediaInfoStreamTypeEmbeddedImage": "內嵌語言", "MediaInfoStreamTypeSubtitle": "字幕", @@ -1099,14 +1088,12 @@ "LabelTranscodingContainer": "影片容器:", "MovieLibraryHelp": "查看 {0}Jellyfin 電影命名指南{1}。", "None": "無", - "LinksValue": "連結:{0}", "OptionAllowMediaPlaybackTranscodingHelp": "由於不支持的媒體格式,限制轉檔可能會導致 Jellyfin 應用程式播放失敗。", "MediaInfoLevel": "等級", "MessageNoTrailersFound": "安裝 Trailer channel 來新增網路上預告片,以增進你的電影體驗。", "OptionHasSpecialFeatures": "特色", "RecommendationStarring": "主演 {0}", "Rewind": "倒帶", - "RunAtStartup": "開機時啟動", "SubtitleOffset": "字幕偏移", "TabPlayback": "播放", "Unrated": "尚未評等", @@ -1167,7 +1154,6 @@ "LabelServerHost": "主機:", "LabelSimultaneousConnectionLimit": "同時串流限制:", "LabelSize": "大小:", - "LabelSkin": "主題:", "LabelSkipBackLength": "跳過長度:", "LabelSkipIfGraphicalSubsPresentHelp": "保留文字版本的字幕會更有效率傳遞,減低影片轉碼的機會。", "LabelStopWhenPossible": "當可能時自動停止:", @@ -1188,7 +1174,6 @@ "LabelWeb": "網站:", "LabelXDlnaCapHelp": "決定在 urn:schemas-dlna-org:device-1-0 namespace 中的 X_DLNACAP 元素的內容。", "LabelXDlnaDocHelp": "決定在 urn:schemas-dlna-org:device-1-0 namespace 中的 X-Dlna doc 元素的內容。", - "LaunchWebAppOnStartup": "在啟動伺服器時啟動使用者介面", "LabelUserRemoteClientBitrateLimitHelp": "覆蓋伺服器重播設定中設置的預設全域值。", "LabelTranscodingThreadCountHelp": "選擇轉檔時要使用的最大執行緒數,減少執行緒數將降低 CPU 使用率,但轉換速度可能不足以提供流暢的播放體驗。", "LabelXDlnaCap": "X-DLNA 上限:", @@ -1276,7 +1261,6 @@ "Programs": "節目", "Quality": "品質", "PackageInstallFailed": "{0} (版本 {1}) 安裝失敗。", - "QueueAllFromHere": "將這裡的全部內容加入佇列", "Raised": "提高", "Rate": "評等", "Recordings": "錄影", @@ -1343,7 +1327,6 @@ "LabelSonyAggregationFlagsHelp": "決定在 urn:schemas-dlna-org:device-1-0 namespace 中的 aggregationFlags 元素的內容。", "LabelSortOrder": "排列順序:", "LabelSortTitle": "短標題:", - "LabelSoundEffects": "音效:", "LabelSource": "來源:", "LabelSpecialSeasonsDisplayName": "SP 季顯示名稱:", "LabelSportsCategories": "體育分類:", @@ -1415,7 +1398,6 @@ "TitlePlayback": "播放", "ValueConditions": "條件:{0}", "Vertical": "垂直", - "VideoRange": "影片範圍", "ViewPlaybackInfo": "查看播放訊息", "XmlTvSportsCategoriesHelp": "有這些類別的節目會被當作體育節目,以「|」來分隔多個項目。", "XmlTvPathHelp": "XML 電視檔案的路徑,Jellyfin 將讀取該檔案並定期檢查其更新,您負責建立和更新檔案。", @@ -1428,7 +1410,6 @@ "LabelDisplaySpecialsWithinSeasons": "顯示劇集季度中的特集", "LabelNumberOfGuideDaysHelp": "下載更多電視指南資料會提供更好時間表查看能力,但將需要更長的下載時間。自動基於頻道數目來選擇。", "LabelOptionalNetworkPath": "(選用)分享的網路資料夾:", - "MessageInstallPluginFromApp": "必須從要在其中使用它的應用程式中安裝此模組。", "OptionResElement": "res 元素", "PinCodeResetComplete": "PIN 碼已被重設。", "PinCodeResetConfirmation": "你確定要重設 PIN 碼?", @@ -1440,12 +1421,10 @@ "XmlDocumentAttributeListHelp": "這些屬性會在每一個XML回應的根元素上應用。", "SkipEpisodesAlreadyInMyLibraryHelp": "劇集將使用季和劇集編號進行比較。", "SelectAdminUsername": "請為管理員賬戶選擇一個用戶名。", - "CopyStreamURLError": "複製連結的時候發生錯誤。", "OptionSaveMetadataAsHiddenHelp": "更改此項將應用於以後保存的元數據。現有元數據文件將在下一次 Jellyfin 伺服器保存它們時被更新。", "OptionAllowRemoteSharedDevicesHelp": "DLNA裝置將被視為共享中,直至有使用者控制。", "OptionForceRemoteSourceTranscoding": "强制遠端轉碼(像電視直播一樣)", "MessageConfirmAppExit": "您要退出嗎?", - "LaunchWebAppOnStartupHelp": "伺服器啓動時在默認游覽器中打開網頁端。使用重啓伺服器功能時此項不生效。", "LabelVideoResolution": "視頻解析度:", "LabelStreamType": "串流類型:", "LabelPlayerDimensions": "播放器尺寸:", @@ -1460,7 +1439,6 @@ "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "這將會使用內建劇集資料。", "PlaybackErrorNoCompatibleStream": "用戶端與該媒體不相容,伺服器也未傳送相容的媒體格式。", "PreferEmbeddedEpisodeInfosOverFileNames": "優先使用內建劇集資訊而不是檔案名稱", - "OtherArtist": "其他歌手", "Artist": "演出者", "AlbumArtist": "專輯歌手", "Album": "專輯", @@ -1480,7 +1458,6 @@ "EveryXHours": "每 {0} 小時", "OnApplicationStartup": "當應用程式啟動時", "Season": "季", - "ReleaseGroup": "發行組織", "Person": "人物", "Movie": "電影", "LabelLibraryPageSizeHelp": "設置媒體庫頁面每頁要顯示的最多媒體個數。設置為 0 來停用分頁。", @@ -1490,7 +1467,6 @@ "DeinterlaceMethodHelp": "選擇對隔行掃描內容進行轉碼時所用的反交錯方法。", "BoxSet": "套裝", "UnsupportedPlayback": "Jellyfin 無法解密受 DRM 保護的內容,但仍然會嘗試播放所有內容。某些檔案由於被加密或包含如互動標題等不受支援的內容,在播放時可能會沒有畫面。", - "MessageUnauthorizedUser": "您目前無權存取伺服器,請與您的伺服器管理員聯繫以獲取更多訊息。", "Filter": "篩選器", "New": "新增", "ApiKeysCaption": "目前已啟用的API金鑰列表", @@ -1501,7 +1477,6 @@ "EnableFasterAnimationsHelp": "使用更快的動畫與過渡效果", "EnableFasterAnimations": "更快的動畫", "LabelRequireHttps": "強制 HTTPS", - "LabelNightly": "開發版", "LabelStable": "穩定版", "LabelChromecastVersion": "Chromecast 版本", "LabelEnableHttpsHelp": "讓伺服器監聽指定的 HTTPS 端口。須設定有效的證書以便使其生效。", From ffa3396a8f5eabd88b89d85f11acb4fece56a619 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Mon, 3 Aug 2020 21:17:44 +0200 Subject: [PATCH 034/155] Migrated livetvschedule.js to ES6 module --- package.json | 1 + src/controllers/livetv/livetvschedule.js | 216 ++++++++++++----------- 2 files changed, 110 insertions(+), 107 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..7d86744574 100644 --- a/package.json +++ b/package.json @@ -222,6 +222,7 @@ "src/controllers/livetvstatus.js", "src/controllers/livetvguideprovider.js", "src/controllers/livetvsettings.js", + "src/controllers/livetv/livetvschedule.js", "src/controllers/shows/episodes.js", "src/controllers/shows/tvgenres.js", "src/controllers/shows/tvlatest.js", diff --git a/src/controllers/livetv/livetvschedule.js b/src/controllers/livetv/livetvschedule.js index 4427bef5d1..df6bcedac9 100644 --- a/src/controllers/livetv/livetvschedule.js +++ b/src/controllers/livetv/livetvschedule.js @@ -1,122 +1,124 @@ -define(['layoutManager', 'cardBuilder', 'apphost', 'imageLoader', 'loading', 'scripts/livetvcomponents', 'emby-button', 'emby-itemscontainer'], function (layoutManager, cardBuilder, appHost, imageLoader, loading) { - 'use strict'; +import layoutManager from 'layoutManager'; +import cardBuilder from 'cardBuilder'; +import imageLoader from 'imageLoader'; +import loading from 'loading'; +import 'scripts/livetvcomponents'; +import 'emby-button'; +import 'emby-itemscontainer'; - loading = loading.default || loading; +function enableScrollX() { + return !layoutManager.desktop; +} - function enableScrollX() { - return !layoutManager.desktop; +function renderRecordings(elem, recordings, cardOptions) { + if (recordings.length) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); } - function renderRecordings(elem, recordings, cardOptions) { - if (recordings.length) { + const recordingItems = elem.querySelector('.recordingItems'); + + if (enableScrollX()) { + recordingItems.classList.add('scrollX'); + + if (layoutManager.tv) { + recordingItems.classList.add('smoothScrollX'); + } + + recordingItems.classList.add('hiddenScrollX'); + recordingItems.classList.remove('vertical-wrap'); + } else { + recordingItems.classList.remove('scrollX'); + recordingItems.classList.remove('smoothScrollX'); + recordingItems.classList.remove('hiddenScrollX'); + recordingItems.classList.add('vertical-wrap'); + } + + recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ + items: recordings, + shape: enableScrollX() ? 'autooverflow' : 'auto', + showTitle: true, + showParentTitle: true, + coverImage: true, + cardLayout: false, + centerText: true, + allowBottomPadding: !enableScrollX(), + preferThumb: 'auto' + }, cardOptions || {})); + imageLoader.lazyChildren(recordingItems); +} + +function getBackdropShape() { + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; +} + +function renderActiveRecordings(context, promise) { + promise.then(function (result) { + renderRecordings(context.querySelector('#activeRecordings'), result.Items, { + shape: enableScrollX() ? 'autooverflow' : 'auto', + defaultShape: getBackdropShape(), + showParentTitle: false, + showParentTitleOrTitle: true, + showTitle: false, + showAirTime: true, + showAirEndTime: true, + showChannelName: true, + coverImage: true, + overlayText: false, + overlayMoreButton: true + }); + }); +} + +function renderTimers(context, timers, options) { + LiveTvHelpers.getTimersHtml(timers, options).then(function (html) { + const elem = context; + + if (html) { elem.classList.remove('hide'); } else { elem.classList.add('hide'); } - var recordingItems = elem.querySelector('.recordingItems'); + elem.querySelector('.recordingItems').innerHTML = html; + imageLoader.lazyChildren(elem); + }); +} - if (enableScrollX()) { - recordingItems.classList.add('scrollX'); +function renderUpcomingRecordings(context, promise) { + promise.then(function (result) { + renderTimers(context.querySelector('#upcomingRecordings'), result.Items); + loading.hide(); + }); +} - if (layoutManager.tv) { - recordingItems.classList.add('smoothScrollX'); - } +export default function (view, params, tabContent) { + let activeRecordingsPromise; + let upcomingRecordingsPromise; + const self = this; + tabContent.querySelector('#upcomingRecordings .recordingItems').addEventListener('timercancelled', function () { + self.preRender(); + self.renderTab(); + }); - recordingItems.classList.add('hiddenScrollX'); - recordingItems.classList.remove('vertical-wrap'); - } else { - recordingItems.classList.remove('scrollX'); - recordingItems.classList.remove('smoothScrollX'); - recordingItems.classList.remove('hiddenScrollX'); - recordingItems.classList.add('vertical-wrap'); - } - - recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ - items: recordings, - shape: enableScrollX() ? 'autooverflow' : 'auto', - showTitle: true, - showParentTitle: true, - coverImage: true, - cardLayout: false, - centerText: true, - allowBottomPadding: !enableScrollX(), - preferThumb: 'auto' - }, cardOptions || {})); - imageLoader.lazyChildren(recordingItems); - } - - function getBackdropShape() { - return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; - } - - function renderActiveRecordings(context, promise) { - promise.then(function (result) { - renderRecordings(context.querySelector('#activeRecordings'), result.Items, { - shape: enableScrollX() ? 'autooverflow' : 'auto', - defaultShape: getBackdropShape(), - showParentTitle: false, - showParentTitleOrTitle: true, - showTitle: false, - showAirTime: true, - showAirEndTime: true, - showChannelName: true, - coverImage: true, - overlayText: false, - overlayMoreButton: true - }); + self.preRender = function () { + activeRecordingsPromise = ApiClient.getLiveTvRecordings({ + UserId: Dashboard.getCurrentUserId(), + IsInProgress: true, + Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', + EnableTotalRecordCount: false, + EnableImageTypes: 'Primary,Thumb,Backdrop' }); - } - - function renderTimers(context, timers, options) { - LiveTvHelpers.getTimersHtml(timers, options).then(function (html) { - var elem = context; - - if (html) { - elem.classList.remove('hide'); - } else { - elem.classList.add('hide'); - } - - elem.querySelector('.recordingItems').innerHTML = html; - imageLoader.lazyChildren(elem); + upcomingRecordingsPromise = ApiClient.getLiveTvTimers({ + IsActive: false, + IsScheduled: true }); - } - - function renderUpcomingRecordings(context, promise) { - promise.then(function (result) { - renderTimers(context.querySelector('#upcomingRecordings'), result.Items); - loading.hide(); - }); - } - - return function (view, params, tabContent) { - var activeRecordingsPromise; - var upcomingRecordingsPromise; - var self = this; - tabContent.querySelector('#upcomingRecordings .recordingItems').addEventListener('timercancelled', function () { - self.preRender(); - self.renderTab(); - }); - - self.preRender = function () { - activeRecordingsPromise = ApiClient.getLiveTvRecordings({ - UserId: Dashboard.getCurrentUserId(), - IsInProgress: true, - Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', - EnableTotalRecordCount: false, - EnableImageTypes: 'Primary,Thumb,Backdrop' - }); - upcomingRecordingsPromise = ApiClient.getLiveTvTimers({ - IsActive: false, - IsScheduled: true - }); - }; - - self.renderTab = function () { - loading.show(); - renderActiveRecordings(tabContent, activeRecordingsPromise); - renderUpcomingRecordings(tabContent, upcomingRecordingsPromise); - }; }; -}); + + self.renderTab = function () { + loading.show(); + renderActiveRecordings(tabContent, activeRecordingsPromise); + renderUpcomingRecordings(tabContent, upcomingRecordingsPromise); + }; +}; From 1d747c9252dc5927aa3a21e8b9d85270eaae13b6 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Mon, 3 Aug 2020 21:21:39 +0200 Subject: [PATCH 035/155] Fixed linting errors --- src/controllers/dashboard/notifications/notifications/index.js | 1 - src/controllers/livetv/livetvschedule.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/dashboard/notifications/notifications/index.js b/src/controllers/dashboard/notifications/notifications/index.js index d12a5f2e61..b97403f8eb 100644 --- a/src/controllers/dashboard/notifications/notifications/index.js +++ b/src/controllers/dashboard/notifications/notifications/index.js @@ -1,5 +1,4 @@ import loading from 'loading'; -import libraryMenu from 'libraryMenu'; import globalize from 'globalize'; import 'listViewStyle'; import 'emby-button'; diff --git a/src/controllers/livetv/livetvschedule.js b/src/controllers/livetv/livetvschedule.js index df6bcedac9..d7bfbad059 100644 --- a/src/controllers/livetv/livetvschedule.js +++ b/src/controllers/livetv/livetvschedule.js @@ -121,4 +121,4 @@ export default function (view, params, tabContent) { renderActiveRecordings(tabContent, activeRecordingsPromise); renderUpcomingRecordings(tabContent, upcomingRecordingsPromise); }; -}; +} From eeec5dfa89bba3af754c8a5dba37b23da38129ef Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 4 Aug 2020 04:23:30 +0900 Subject: [PATCH 036/155] fix linting issue --- src/controllers/dashboard/notifications/notifications/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/dashboard/notifications/notifications/index.js b/src/controllers/dashboard/notifications/notifications/index.js index d12a5f2e61..b97403f8eb 100644 --- a/src/controllers/dashboard/notifications/notifications/index.js +++ b/src/controllers/dashboard/notifications/notifications/index.js @@ -1,5 +1,4 @@ import loading from 'loading'; -import libraryMenu from 'libraryMenu'; import globalize from 'globalize'; import 'listViewStyle'; import 'emby-button'; From 2eb2ec3d8a047fe1939a184301db0f94c8f9e324 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Mon, 3 Aug 2020 21:26:13 +0200 Subject: [PATCH 037/155] Migrated livetvrecordings.js to es6 module --- package.json | 1 + src/controllers/livetv/livetvrecordings.js | 201 +++++++++++---------- 2 files changed, 103 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..e98cb81a5d 100644 --- a/package.json +++ b/package.json @@ -222,6 +222,7 @@ "src/controllers/livetvstatus.js", "src/controllers/livetvguideprovider.js", "src/controllers/livetvsettings.js", + "src/controllers/livetv/livetvrecordings.js", "src/controllers/shows/episodes.js", "src/controllers/shows/tvgenres.js", "src/controllers/shows/tvlatest.js", diff --git a/src/controllers/livetv/livetvrecordings.js b/src/controllers/livetv/livetvrecordings.js index b0259bc89b..cb3bfeb7a6 100644 --- a/src/controllers/livetv/livetvrecordings.js +++ b/src/controllers/livetv/livetvrecordings.js @@ -1,111 +1,114 @@ -define(['layoutManager', 'loading', 'cardBuilder', 'apphost', 'imageLoader', 'scripts/livetvcomponents', 'listViewStyle', 'emby-itemscontainer'], function (layoutManager, loading, cardBuilder, appHost, imageLoader) { - 'use strict'; +import layoutManager from 'layoutManager'; +import loading from 'loading'; +import cardBuilder from 'cardBuilder'; +import appHost from 'apphost'; +import imageLoader from 'imageLoader'; +import 'scripts/livetvcomponents'; +import 'listViewStyle'; +import 'emby-itemscontainer'; - loading = loading.default || loading; - - function renderRecordings(elem, recordings, cardOptions, scrollX) { - if (!elem) { - return; - } - - if (recordings.length) { - elem.classList.remove('hide'); - } else { - elem.classList.add('hide'); - } - - var recordingItems = elem.querySelector('.recordingItems'); - - if (scrollX) { - recordingItems.classList.add('scrollX'); - recordingItems.classList.add('hiddenScrollX'); - recordingItems.classList.remove('vertical-wrap'); - } else { - recordingItems.classList.remove('scrollX'); - recordingItems.classList.remove('hiddenScrollX'); - recordingItems.classList.add('vertical-wrap'); - } - - recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ - items: recordings, - shape: scrollX ? 'autooverflow' : 'auto', - defaultShape: scrollX ? 'overflowBackdrop' : 'backdrop', - showTitle: true, - showParentTitle: true, - coverImage: true, - cardLayout: false, - centerText: true, - allowBottomPadding: !scrollX, - preferThumb: 'auto', - overlayText: false - }, cardOptions || {})); - imageLoader.lazyChildren(recordingItems); +function renderRecordings(elem, recordings, cardOptions, scrollX) { + if (!elem) { + return; } - function renderLatestRecordings(context, promise) { - promise.then(function (result) { - renderRecordings(context.querySelector('#latestRecordings'), result.Items, { - showYear: true, - lines: 2 - }, false); - loading.hide(); - }); + if (recordings.length) { + elem.classList.remove('hide'); + } else { + elem.classList.add('hide'); } - function renderRecordingFolders(context, promise) { - promise.then(function (result) { - renderRecordings(context.querySelector('#recordingFolders'), result.Items, { - showYear: false, - showParentTitle: false - }, false); - }); + const recordingItems = elem.querySelector('.recordingItems'); + + if (scrollX) { + recordingItems.classList.add('scrollX'); + recordingItems.classList.add('hiddenScrollX'); + recordingItems.classList.remove('vertical-wrap'); + } else { + recordingItems.classList.remove('scrollX'); + recordingItems.classList.remove('hiddenScrollX'); + recordingItems.classList.add('vertical-wrap'); } - function onMoreClick(e) { - var type = this.getAttribute('data-type'); - var serverId = ApiClient.serverId(); + recordingItems.innerHTML = cardBuilder.getCardsHtml(Object.assign({ + items: recordings, + shape: scrollX ? 'autooverflow' : 'auto', + defaultShape: scrollX ? 'overflowBackdrop' : 'backdrop', + showTitle: true, + showParentTitle: true, + coverImage: true, + cardLayout: false, + centerText: true, + allowBottomPadding: !scrollX, + preferThumb: 'auto', + overlayText: false + }, cardOptions || {})); + imageLoader.lazyChildren(recordingItems); +} - switch (type) { - case 'latest': - Dashboard.navigate('list.html?type=Recordings&serverId=' + serverId); - } +function renderLatestRecordings(context, promise) { + promise.then(function (result) { + renderRecordings(context.querySelector('#latestRecordings'), result.Items, { + showYear: true, + lines: 2 + }, false); + loading.hide(); + }); +} + +function renderRecordingFolders(context, promise) { + promise.then(function (result) { + renderRecordings(context.querySelector('#recordingFolders'), result.Items, { + showYear: false, + showParentTitle: false + }, false); + }); +} + +function onMoreClick(e) { + const type = this.getAttribute('data-type'); + const serverId = ApiClient.serverId(); + + switch (type) { + case 'latest': + Dashboard.navigate('list.html?type=Recordings&serverId=' + serverId); + } +} + +export default function (view, params, tabContent) { + function enableFullRender() { + return new Date().getTime() - lastFullRender > 300000; } - return function (view, params, tabContent) { - function enableFullRender() { - return new Date().getTime() - lastFullRender > 300000; + let foldersPromise; + let latestPromise; + const self = this; + let lastFullRender = 0; + const moreButtons = tabContent.querySelectorAll('.more'); + + for (let i = 0, length = moreButtons.length; i < length; i++) { + moreButtons[i].addEventListener('click', onMoreClick); + } + + self.preRender = function () { + if (enableFullRender()) { + latestPromise = ApiClient.getLiveTvRecordings({ + UserId: Dashboard.getCurrentUserId(), + Limit: 12, + Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', + EnableTotalRecordCount: false, + EnableImageTypes: 'Primary,Thumb,Backdrop' + }); + foldersPromise = ApiClient.getRecordingFolders(Dashboard.getCurrentUserId()); } - - var foldersPromise; - var latestPromise; - var self = this; - var lastFullRender = 0; - var moreButtons = tabContent.querySelectorAll('.more'); - - for (var i = 0, length = moreButtons.length; i < length; i++) { - moreButtons[i].addEventListener('click', onMoreClick); - } - - self.preRender = function () { - if (enableFullRender()) { - latestPromise = ApiClient.getLiveTvRecordings({ - UserId: Dashboard.getCurrentUserId(), - Limit: 12, - Fields: 'CanDelete,PrimaryImageAspectRatio,BasicSyncInfo', - EnableTotalRecordCount: false, - EnableImageTypes: 'Primary,Thumb,Backdrop' - }); - foldersPromise = ApiClient.getRecordingFolders(Dashboard.getCurrentUserId()); - } - }; - - self.renderTab = function () { - if (enableFullRender()) { - loading.show(); - renderLatestRecordings(tabContent, latestPromise); - renderRecordingFolders(tabContent, foldersPromise); - lastFullRender = new Date().getTime(); - } - }; }; -}); + + self.renderTab = function () { + if (enableFullRender()) { + loading.show(); + renderLatestRecordings(tabContent, latestPromise); + renderRecordingFolders(tabContent, foldersPromise); + lastFullRender = new Date().getTime(); + } + }; +} From 13b81a410114c08564ad97a2681e95b12e8d94e4 Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 4 Aug 2020 04:26:57 +0900 Subject: [PATCH 038/155] add a new script for string management --- scripts/duplicates.py | 33 +++++++++++++++++++++++++++++++++ scripts/{scrm.py => remove.py} | 2 +- scripts/{scdup.py => source.py} | 0 scripts/{scgen.py => unused.py} | 0 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 scripts/duplicates.py rename scripts/{scrm.py => remove.py} (95%) rename scripts/{scdup.py => source.py} (100%) rename scripts/{scgen.py => unused.py} (100%) diff --git a/scripts/duplicates.py b/scripts/duplicates.py new file mode 100644 index 0000000000..bbacffd0f1 --- /dev/null +++ b/scripts/duplicates.py @@ -0,0 +1,33 @@ +import sys +import os +import json + +# load every string in the source language +# print all duplicate values to a file + +cwd = os.getcwd() +source = cwd + '/../src/strings/en-us.json' + +reverse = {} +duplicates = {} + +with open(source) as en: + strings = json.load(en) + for key, value in strings.items(): + if value not in reverse: + reverse[value] = [key] + else: + reverse[value].append(key) + +for key, value in reverse.items(): + if len(value) > 1: + duplicates[key] = reverse[key] + +print('LENGTH: ' + str(len(duplicates))) +with open('duplicates.txt', 'w') as out: + for item in duplicates: + out.write(json.dumps(item) + ': ') + out.write(json.dumps(duplicates[item]) + '\n') + out.close() + +print('DONE') diff --git a/scripts/scrm.py b/scripts/remove.py similarity index 95% rename from scripts/scrm.py rename to scripts/remove.py index 9bd5bc2a48..dba48537aa 100644 --- a/scripts/scrm.py +++ b/scripts/remove.py @@ -11,7 +11,7 @@ langlst = os.listdir(langdir) keys = [] -with open('scout.txt', 'r') as f: +with open('unused.txt', 'r') as f: for line in f: keys.append(line.strip('\n')) diff --git a/scripts/scdup.py b/scripts/source.py similarity index 100% rename from scripts/scdup.py rename to scripts/source.py diff --git a/scripts/scgen.py b/scripts/unused.py similarity index 100% rename from scripts/scgen.py rename to scripts/unused.py From ba3418450ebe8f954d1ad6f2300acdb882725909 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 25 Jul 2020 11:42:21 +0100 Subject: [PATCH 039/155] Migration of movies to ES6 modules --- package.json | 6 + src/controllers/movies/moviecollections.js | 74 +++++++------ src/controllers/movies/moviegenres.js | 60 +++++----- src/controllers/movies/movies.js | 117 ++++++++++---------- src/controllers/movies/moviesrecommended.js | 111 +++++++++++-------- src/controllers/movies/movietrailers.js | 75 +++++++------ 6 files changed, 244 insertions(+), 199 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..678794aebc 100644 --- a/package.json +++ b/package.json @@ -212,6 +212,12 @@ "src/controllers/edititemmetadata.js", "src/controllers/favorites.js", "src/controllers/hometab.js", + "src/controllers/dashboard/plugins/repositories.js", + "src/controllers/movies/moviecollections.js", + "src/controllers/movies/moviegenres.js", + "src/controllers/movies/movies.js", + "src/controllers/movies/moviesrecommended.js", + "src/controllers/movies/movietrailers.js", "src/controllers/playback/nowplaying.js", "src/controllers/playback/videoosd.js", "src/controllers/itemDetails/index.js", diff --git a/src/controllers/movies/moviecollections.js b/src/controllers/movies/moviecollections.js index 7c1a5156d9..530117295c 100644 --- a/src/controllers/movies/moviecollections.js +++ b/src/controllers/movies/moviecollections.js @@ -1,13 +1,19 @@ -define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings, globalize) { - 'use strict'; +import loading from 'loading'; +import events from 'events'; +import libraryBrowser from 'libraryBrowser'; +import imageLoader from 'imageLoader'; +import listView from 'listView'; +import cardBuilder from 'cardBuilder'; +import * as userSettings from 'userSettings'; +import globalize from 'globalize'; +import 'emby-itemscontainer'; - loading = loading.default || loading; - libraryBrowser = libraryBrowser.default || libraryBrowser; +/* eslint-disable indent */ - return function (view, params, tabContent) { + export default function (view, params, tabContent) { function getPageData(context) { - var key = getSavedQueryKey(context); - var pageData = data[key]; + const key = getSavedQueryKey(context); + let pageData = data[key]; if (!pageData) { pageData = data[key] = { @@ -48,8 +54,8 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB } function onViewStyleChange() { - var viewStyle = self.getCurrentViewStyle(); - var itemsContainer = tabContent.querySelector('.itemsContainer'); + const viewStyle = self.getCurrentViewStyle(); + const itemsContainer = tabContent.querySelector('.itemsContainer'); if (viewStyle == 'List') { itemsContainer.classList.add('vertical-list'); @@ -65,7 +71,7 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB function reloadItems(page) { loading.show(); isLoading = true; - var query = getQuery(page); + const query = getQuery(page); ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { function onNextPageClick() { if (isLoading) { @@ -90,8 +96,8 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB } window.scrollTo(0, 0); - var html; - var pagingHtml = libraryBrowser.getQueryPagingHtml({ + let html; + const pagingHtml = libraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, @@ -101,7 +107,7 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB sortButton: false, filterButton: false }); - var viewStyle = self.getCurrentViewStyle(); + const viewStyle = self.getCurrentViewStyle(); if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, @@ -155,22 +161,21 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB showTitle: true }); } - var i; - var length; - var elems = tabContent.querySelectorAll('.paging'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].innerHTML = pagingHtml; + let elems = tabContent.querySelectorAll('.paging'); + + for (const elem of elems) { + elem.innerHTML = pagingHtml; } elems = tabContent.querySelectorAll('.btnNextPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onNextPageClick); + for (const elem of elems) { + elem.addEventListener('click', onNextPageClick); } elems = tabContent.querySelectorAll('.btnPreviousPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onPreviousPageClick); + for (const elem of elems) { + elem.addEventListener('click', onPreviousPageClick); } if (!result.Items.length) { @@ -182,22 +187,22 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB html += '
'; } - var itemsContainer = tabContent.querySelector('.itemsContainer'); + const itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(page), query); loading.hide(); isLoading = false; - require(['autoFocuser'], function (autoFocuser) { + import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(page); }); }); } - var self = this; - var data = {}; - var isLoading = false; + const self = this; + const data = {}; + let isLoading = false; self.getCurrentViewStyle = function () { return getPageData(tabContent).view; @@ -230,21 +235,21 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB button: e.target }); }); - var btnSelectView = tabContent.querySelector('.btnSelectView'); + const btnSelectView = tabContent.querySelector('.btnSelectView'); btnSelectView.addEventListener('click', function (e) { libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); btnSelectView.addEventListener('layoutchange', function (e) { - var viewStyle = e.detail.viewStyle; + const viewStyle = e.detail.viewStyle; getPageData(tabContent).view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle); getQuery(tabContent).StartIndex = 0; onViewStyleChange(); reloadItems(tabContent); }); - tabContent.querySelector('.btnNewCollection').addEventListener('click', function () { - require(['collectionEditor'], function (collectionEditor) { - var serverId = ApiClient.serverInfo().Id; + tabContent.querySelector('.btnNewCollection').addEventListener('click', () => { + import('collectionEditor').then(({default: collectionEditor}) => { + const serverId = ApiClient.serverInfo().Id; new collectionEditor.showEditor({ items: [], serverId: serverId @@ -261,5 +266,6 @@ define(['loading', 'events', 'libraryBrowser', 'imageLoader', 'listView', 'cardB }; self.destroy = function () {}; - }; -}); + } + +/* eslint-enable indent */ diff --git a/src/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index 82ab4d4d4b..56fb88b20f 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -1,13 +1,20 @@ -define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader', 'apphost', 'globalize', 'appRouter', 'dom', 'emby-button'], function (layoutManager, loading, libraryBrowser, cardBuilder, lazyLoader, appHost, globalize, appRouter, dom) { - 'use strict'; +import layoutManager from 'layoutManager'; +import loading from 'loading'; +import libraryBrowser from 'libraryBrowser'; +import cardBuilder from 'cardBuilder'; +import lazyLoader from 'lazyLoader'; +import appHost from 'apphost'; +import globalize from 'globalize'; +import appRouter from 'appRouter'; +import dom from 'dom'; +import 'emby-button'; - loading = loading.default || loading; - libraryBrowser = libraryBrowser.default || libraryBrowser; +/* eslint-disable indent */ - return function (view, params, tabContent) { + export default function (view, params, tabContent) { function getPageData() { - var key = getSavedQueryKey(); - var pageData = data[key]; + const key = getSavedQueryKey(); + let pageData = data[key]; if (!pageData) { pageData = data[key] = { @@ -37,7 +44,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader function getPromise() { loading.show(); - var query = getQuery(); + const query = getQuery(); return ApiClient.getGenres(ApiClient.getCurrentUserId(), query); } @@ -54,17 +61,17 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader } function fillItemsContainer(entry) { - var elem = entry.target; - var id = elem.getAttribute('data-id'); - var viewStyle = self.getCurrentViewStyle(); - var limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9; + const elem = entry.target; + const id = elem.getAttribute('data-id'); + const viewStyle = self.getCurrentViewStyle(); + let limit = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 5 : 9; if (enableScrollX()) { limit = 10; } - var enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary'; - var query = { + const enableImageTypes = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 'Primary,Backdrop,Thumb' : 'Primary'; + const query = { SortBy: 'SortName', SortOrder: 'Ascending', IncludeItemTypes: 'Movie', @@ -129,14 +136,14 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader } function reloadItems(context, promise) { - var query = getQuery(); + const query = getQuery(); promise.then(function (result) { - var elem = context.querySelector('#items'); - var html = ''; - var items = result.Items; + const elem = context.querySelector('#items'); + let html = ''; + const items = result.Items; - for (var i = 0, length = items.length; i < length; i++) { - var item = items[i]; + for (let i = 0, length = items.length; i < length; i++) { + const item = items[i]; html += '
'; html += '
'; @@ -151,7 +158,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader html += ''; html += '
'; if (enableScrollX()) { - var scrollXClass = 'scrollX hiddenScrollX'; + let scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { scrollXClass += 'smoothScrollX padded-top-focusscale padded-bottom-focusscale'; @@ -187,8 +194,8 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader self.renderTab(); } - var self = this; - var data = {}; + const self = this; + const data = {}; self.getViewStyles = function () { return 'Poster,PosterCard,Thumb,ThumbCard'.split(','); @@ -205,7 +212,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader }; self.enableViewSelection = true; - var promise; + let promise; self.preRender = function () { promise = getPromise(); @@ -214,5 +221,6 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader self.renderTab = function () { reloadItems(tabContent, promise); }; - }; -}); + } + +/* eslint-enable indent */ diff --git a/src/controllers/movies/movies.js b/src/controllers/movies/movies.js index ade9dc4b89..3b68849036 100644 --- a/src/controllers/movies/movies.js +++ b/src/controllers/movies/movies.js @@ -1,12 +1,19 @@ -define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', 'alphaPicker', 'listView', 'cardBuilder', 'globalize', 'emby-itemscontainer'], function (loading, layoutManager, userSettings, events, libraryBrowser, AlphaPicker, listView, cardBuilder, globalize) { - 'use strict'; +import loading from 'loading'; +import layoutManager from 'layoutManager'; +import * as userSettings from 'userSettings'; +import events from 'events'; +import libraryBrowser from 'libraryBrowser'; +import AlphaPicker from 'alphaPicker'; +import listView from 'listView'; +import cardBuilder from 'cardBuilder'; +import globalize from 'globalize'; +import 'emby-itemscontainer'; - loading = loading.default || loading; - libraryBrowser = libraryBrowser.default || libraryBrowser; +/* eslint-disable indent */ - return function (view, params, tabContent, options) { - function onViewStyleChange() { - if (self.getCurrentViewStyle() == 'List') { + export default function (view, params, tabContent, options) { + const onViewStyleChange = () => { + if (this.getCurrentViewStyle() == 'List') { itemsContainer.classList.add('vertical-list'); itemsContainer.classList.remove('vertical-wrap'); } else { @@ -15,13 +22,13 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', } itemsContainer.innerHTML = ''; - } + }; - function updateFilterControls() { - if (self.alphaPicker) { - self.alphaPicker.value(query.NameStartsWithOrGreater); + const updateFilterControls = () => { + if (this.alphaPicker) { + this.alphaPicker.value(query.NameStartsWithOrGreater); } - } + }; function fetchData() { isLoading = true; @@ -54,7 +61,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', window.scrollTo(0, 0); updateFilterControls(); - var pagingHtml = libraryBrowser.getQueryPagingHtml({ + const pagingHtml = libraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, @@ -64,35 +71,30 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', sortButton: false, filterButton: false }); - var i; - var length; - var elems = tabContent.querySelectorAll('.paging'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].innerHTML = pagingHtml; + for (const elem of tabContent.querySelectorAll('.paging')) { + elem.innerHTML = pagingHtml; } - elems = tabContent.querySelectorAll('.btnNextPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onNextPageClick); + for (const elem of tabContent.querySelectorAll('.btnNextPage')) { + elem.addEventListener('click', onNextPageClick); } - elems = tabContent.querySelectorAll('.btnPreviousPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onPreviousPageClick); + for (const elem of tabContent.querySelectorAll('.btnPreviousPage')) { + elem.addEventListener('click', onPreviousPageClick); } isLoading = false; loading.hide(); - require(['autoFocuser'], function (autoFocuser) { + import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(tabContent); }); } - function getItemsHtml(items) { - var html; - var viewStyle = self.getCurrentViewStyle(); + const getItemsHtml = (items) => { + let html; + const viewStyle = this.getCurrentViewStyle(); if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ @@ -156,22 +158,22 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', } return html; - } + }; - function initPage(tabContent) { + const initPage = (tabContent) => { itemsContainer.fetchData = fetchData; itemsContainer.getItemsHtml = getItemsHtml; itemsContainer.afterRefresh = afterRefresh; - var alphaPickerElement = tabContent.querySelector('.alphaPicker'); + let alphaPickerElement = tabContent.querySelector('.alphaPicker'); if (alphaPickerElement) { alphaPickerElement.addEventListener('alphavaluechanged', function (e) { - var newValue = e.detail.value; + let newValue = e.detail.value; query.NameStartsWithOrGreater = newValue; query.StartIndex = 0; itemsContainer.refreshItems(); }); - self.alphaPicker = new AlphaPicker.default({ + this.alphaPicker = new AlphaPicker({ element: alphaPickerElement, valueChangeEvent: 'click' }); @@ -181,14 +183,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', itemsContainer.classList.add('padded-right-withalphapicker'); } - var btnFilter = tabContent.querySelector('.btnFilter'); + const btnFilter = tabContent.querySelector('.btnFilter'); if (btnFilter) { - btnFilter.addEventListener('click', function () { - self.showFilterMenu(); + btnFilter.addEventListener('click', () => { + this.showFilterMenu(); }); } - var btnSort = tabContent.querySelector('.btnSort'); + const btnSort = tabContent.querySelector('.btnSort'); if (btnSort) { btnSort.addEventListener('click', function (e) { @@ -231,24 +233,23 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', }); }); } - var btnSelectView = tabContent.querySelector('.btnSelectView'); + const btnSelectView = tabContent.querySelector('.btnSelectView'); btnSelectView.addEventListener('click', function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); + libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle, 'Banner,List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); btnSelectView.addEventListener('layoutchange', function (e) { - var viewStyle = e.detail.viewStyle; + let viewStyle = e.detail.viewStyle; userSettings.set(savedViewKey, viewStyle); query.StartIndex = 0; onViewStyleChange(); itemsContainer.refreshItems(); }); - } + }; - var self = this; - var itemsContainer = tabContent.querySelector('.itemsContainer'); - var savedQueryKey = params.topParentId + '-' + options.mode; - var savedViewKey = savedQueryKey + '-view'; - var query = { + let itemsContainer = tabContent.querySelector('.itemsContainer'); + const savedQueryKey = params.topParentId + '-' + options.mode; + const savedViewKey = savedQueryKey + '-view'; + let query = { SortBy: 'SortName,ProductionYear', SortOrder: 'Ascending', IncludeItemTypes: 'Movie', @@ -264,7 +265,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', query['Limit'] = userSettings.libraryPageSize(); } - var isLoading = false; + let isLoading = false; if (options.mode === 'favorites') { query.IsFavorite = true; @@ -272,14 +273,14 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', query = userSettings.loadQuerySettings(savedQueryKey, query); - self.showFilterMenu = function () { - require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { - var filterDialog = new filterDialogFactory({ + this.showFilterMenu = function () { + import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { + let filterDialog = new filterDialogFactory({ query: query, mode: 'movies', serverId: ApiClient.serverId() }); - events.on(filterDialog, 'filterchange', function () { + events.on(filterDialog, 'filterchange', () => { query.StartIndex = 0; itemsContainer.refreshItems(); }); @@ -287,22 +288,24 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', }); }; - self.getCurrentViewStyle = function () { + this.getCurrentViewStyle = function () { return userSettings.get(savedViewKey) || 'Poster'; }; - self.initTab = function () { + this.initTab = function () { initPage(tabContent); onViewStyleChange(); }; - self.renderTab = function () { + //function renderTab () { + this.renderTab = function () { itemsContainer.refreshItems(); updateFilterControls(); }; - self.destroy = function () { + this.destroy = function () { itemsContainer = null; }; - }; -}); + } + +/* eslint-enable indent */ diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index a633d654cd..5e376637b3 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -1,5 +1,20 @@ -define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu', 'mainTabsManager', 'cardBuilder', 'dom', 'imageLoader', 'playbackManager', 'globalize', 'emby-scroller', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (events, layoutManager, inputManager, userSettings, libraryMenu, mainTabsManager, cardBuilder, dom, imageLoader, playbackManager, globalize) { - 'use strict'; +import events from 'events'; +import layoutManager from 'layoutManager'; +import inputManager from 'inputManager'; +import * as userSettings from 'userSettings'; +import libraryMenu from 'libraryMenu'; +import mainTabsManager from 'mainTabsManager'; +import cardBuilder from 'cardBuilder'; +import dom from 'dom'; +import imageLoader from 'imageLoader'; +import playbackManager from 'playbackManager'; +import globalize from 'globalize'; +import 'emby-scroller'; +import 'emby-itemscontainer'; +import 'emby-tabs'; +import 'emby-button'; + +/* eslint-disable indent */ playbackManager = playbackManager.default || playbackManager; @@ -16,7 +31,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function loadLatest(page, userId, parentId) { - var options = { + const options = { IncludeItemTypes: 'Movie', Limit: 18, Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo', @@ -26,8 +41,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' EnableTotalRecordCount: false }; ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options)).then(function (items) { - var allowBottomPadding = !enableScrollX(); - var container = page.querySelector('#recentlyAddedItems'); + const allowBottomPadding = !enableScrollX(); + const container = page.querySelector('#recentlyAddedItems'); cardBuilder.buildCards(items, { itemsContainer: container, shape: getPortraitShape(), @@ -45,8 +60,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function loadResume(page, userId, parentId) { - var screenWidth = dom.getWindowSize().innerWidth; - var options = { + let screenWidth = dom.getWindowSize().innerWidth; + const options = { SortBy: 'DatePlayed', SortOrder: 'Descending', IncludeItemTypes: 'Movie', @@ -67,8 +82,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' page.querySelector('#resumableSection').classList.add('hide'); } - var allowBottomPadding = !enableScrollX(); - var container = page.querySelector('#resumableItems'); + const allowBottomPadding = !enableScrollX(); + const container = page.querySelector('#resumableItems'); cardBuilder.buildCards(result.Items, { itemsContainer: container, preferThumb: true, @@ -88,8 +103,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function getRecommendationHtml(recommendation) { - var html = ''; - var title = ''; + let html = ''; + let title = ''; switch (recommendation.RecommendationType) { case 'SimilarToRecentlyPlayed': @@ -113,7 +128,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' html += '
'; html += '

' + title + '

'; - var allowBottomPadding = true; + const allowBottomPadding = true; if (enableScrollX()) { html += '
'; @@ -141,8 +156,8 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function loadSuggestions(page, userId, parentId) { - var screenWidth = dom.getWindowSize().innerWidth; - var url = ApiClient.getUrl('Movies/Recommendations', { + let screenWidth = dom.getWindowSize().innerWidth; + let url = ApiClient.getUrl('Movies/Recommendations', { userId: userId, categoryLimit: 6, ItemLimit: screenWidth >= 1920 ? 8 : screenWidth >= 1600 ? 8 : screenWidth >= 1200 ? 6 : 5, @@ -157,9 +172,9 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' return; } - var html = recommendations.map(getRecommendationHtml).join(''); + const html = recommendations.map(getRecommendationHtml).join(''); page.querySelector('.noItemsMessage').classList.add('hide'); - var recs = page.querySelector('.recommendations'); + let recs = page.querySelector('.recommendations'); recs.innerHTML = html; imageLoader.lazyChildren(recs); @@ -169,7 +184,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function autoFocus(page) { - require(['autoFocuser'], function (autoFocuser) { + import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(page); }); } @@ -195,17 +210,16 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function initSuggestedTab(page, tabContent) { - var containers = tabContent.querySelectorAll('.itemsContainer'); + const containers = tabContent.querySelectorAll('.itemsContainer'); - for (var i = 0, length = containers.length; i < length; i++) { - setScrollClasses(containers[i], enableScrollX()); + for (const container of containers) { + setScrollClasses(container, enableScrollX()); } } function loadSuggestionsTab(view, params, tabContent) { - var parentId = params.topParentId; - var userId = ApiClient.getCurrentUserId(); - console.debug('loadSuggestionsTab'); + const parentId = params.topParentId; + const userId = ApiClient.getCurrentUserId(); loadResume(tabContent, userId, parentId); loadLatest(tabContent, userId, parentId); loadSuggestions(tabContent, userId, parentId); @@ -249,13 +263,13 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } } - return function (view, params) { + export default function (view, params) { function onBeforeTabChange(e) { preLoadTab(view, parseInt(e.detail.selectedTabIndex)); } function onTabChange(e) { - var newIndex = parseInt(e.detail.selectedTabIndex); + const newIndex = parseInt(e.detail.selectedTabIndex); loadTab(view, newIndex); } @@ -268,45 +282,47 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' } function getTabController(page, index, callback) { - var depends = []; + let depends = ''; switch (index) { case 0: - depends.push('controllers/movies/movies'); + depends = 'controllers/movies/movies'; break; case 1: + depends = 'controllers/movies/moviesrecommended.js'; break; case 2: - depends.push('controllers/movies/movietrailers'); + depends = 'controllers/movies/movietrailers'; break; case 3: - depends.push('controllers/movies/movies'); + depends = 'controllers/movies/movies'; break; case 4: - depends.push('controllers/movies/moviecollections'); + depends = 'controllers/movies/moviecollections'; break; case 5: - depends.push('controllers/movies/moviegenres'); + depends = 'controllers/movies/moviegenres'; break; case 6: - depends.push('scripts/searchtab'); + depends = 'scripts/searchtab'; + break; } - require(depends, function (controllerFactory) { - var tabContent; + import(depends).then(({default: controllerFactory}) => { + let tabContent; if (index === suggestionsTabIndex) { tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']"); self.tabContent = tabContent; } - var controller = tabControllers[index]; + let controller = tabControllers[index]; if (!controller) { tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']"); @@ -375,17 +391,17 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' var suggestionsTabIndex = 1; self.initTab = function () { - var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); + let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); initSuggestedTab(view, tabContent); }; self.renderTab = function () { - var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); + let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); loadSuggestionsTab(view, params, tabContent); }; - var tabControllers = []; - var renderedTabs = []; + let tabControllers = []; + let renderedTabs = []; view.addEventListener('viewshow', function (e) { initTabs(); if (!view.getAttribute('data-title')) { @@ -408,12 +424,11 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' view.addEventListener('viewbeforehide', function (e) { inputManager.off(window, onInputCommand); }); - view.addEventListener('viewdestroy', function (e) { - tabControllers.forEach(function (t) { - if (t.destroy) { - t.destroy(); - } - }); - }); - }; -}); + for (const tabController of tabControllers) { + if (tabController.destroy) { + tabController.destroy(); + } + } + } + +/* eslint-enable indent */ diff --git a/src/controllers/movies/movietrailers.js b/src/controllers/movies/movietrailers.js index 8d9fe8d090..a892769e47 100644 --- a/src/controllers/movies/movietrailers.js +++ b/src/controllers/movies/movietrailers.js @@ -1,13 +1,21 @@ -define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', 'alphaPicker', 'listView', 'cardBuilder', 'userSettings', 'globalize', 'emby-itemscontainer'], function (layoutManager, loading, events, libraryBrowser, imageLoader, AlphaPicker, listView, cardBuilder, userSettings, globalize) { - 'use strict'; +import layoutManager from 'layoutManager'; +import loading from 'loading'; +import events from 'events'; +import libraryBrowser from 'libraryBrowser'; +import imageLoader from 'imageLoader'; +import AlphaPicker from 'alphaPicker'; +import listView from 'listView'; +import cardBuilder from 'cardBuilder'; +import * as userSettings from 'userSettings'; +import globalize from 'globalize'; +import 'emby-itemscontainer'; - loading = loading.default || loading; - libraryBrowser = libraryBrowser.default || libraryBrowser; +/* eslint-disable indent */ - return function (view, params, tabContent) { + export default function (view, params, tabContent) { function getPageData(context) { - var key = getSavedQueryKey(context); - var pageData = data[key]; + const key = getSavedQueryKey(context); + let pageData = data[key]; if (!pageData) { pageData = data[key] = { @@ -49,7 +57,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' function reloadItems() { loading.show(); isLoading = true; - var query = getQuery(tabContent); + const query = getQuery(tabContent); ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { function onNextPageClick() { if (isLoading) { @@ -75,7 +83,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' window.scrollTo(0, 0); updateFilterControls(tabContent); - var pagingHtml = libraryBrowser.getQueryPagingHtml({ + const pagingHtml = libraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, @@ -85,8 +93,8 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' sortButton: false, filterButton: false }); - var html; - var viewStyle = self.getCurrentViewStyle(); + let html; + const viewStyle = self.getCurrentViewStyle(); if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ @@ -142,22 +150,20 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' }); } - var i; - var length; - var elems = tabContent.querySelectorAll('.paging'); + let elems = tabContent.querySelectorAll('.paging'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].innerHTML = pagingHtml; + for (const elem of elems) { + elem.innerHTML = pagingHtml; } elems = tabContent.querySelectorAll('.btnNextPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onNextPageClick); + for (const elem of elems) { + elem.addEventListener('click', onNextPageClick); } elems = tabContent.querySelectorAll('.btnPreviousPage'); - for (i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onPreviousPageClick); + for (const elem of elems) { + elem.addEventListener('click', onPreviousPageClick); } if (!result.Items.length) { @@ -169,7 +175,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' html += '
'; } - var itemsContainer = tabContent.querySelector('.itemsContainer'); + const itemsContainer = tabContent.querySelector('.itemsContainer'); itemsContainer.innerHTML = html; imageLoader.lazyChildren(itemsContainer); libraryBrowser.saveQueryValues(getSavedQueryKey(tabContent), query); @@ -179,17 +185,17 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' } function updateFilterControls(tabContent) { - var query = getQuery(tabContent); + const query = getQuery(tabContent); self.alphaPicker.value(query.NameStartsWithOrGreater); } - var self = this; - var data = {}; - var isLoading = false; + const self = this; + const data = {}; + let isLoading = false; self.showFilterMenu = function () { - require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { - var filterDialog = new filterDialogFactory({ + import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { + const filterDialog = new filterDialogFactory({ query: getQuery(tabContent), mode: 'movies', serverId: ApiClient.serverId() @@ -207,16 +213,16 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' }; function initPage(tabContent) { - var alphaPickerElement = tabContent.querySelector('.alphaPicker'); - var itemsContainer = tabContent.querySelector('.itemsContainer'); + const alphaPickerElement = tabContent.querySelector('.alphaPicker'); + const itemsContainer = tabContent.querySelector('.itemsContainer'); alphaPickerElement.addEventListener('alphavaluechanged', function (e) { - var newValue = e.detail.value; - var query = getQuery(tabContent); + const newValue = e.detail.value; + const query = getQuery(tabContent); query.NameStartsWithOrGreater = newValue; query.StartIndex = 0; reloadItems(); }); - self.alphaPicker = new AlphaPicker.default({ + self.alphaPicker = new AlphaPicker({ element: alphaPickerElement, valueChangeEvent: 'click' }); @@ -270,5 +276,6 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' }; self.destroy = function () {}; - }; -}); + } + +/* eslint-enable indent */ From f1b2a86c7d8047775bae0b9bf1bae1c60e14ee3e Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 25 Jul 2020 22:36:23 +0100 Subject: [PATCH 040/155] Apply suggestions --- src/controllers/movies/moviecollections.js | 27 +++++++++---------- src/controllers/movies/moviegenres.js | 27 +++++++++---------- src/controllers/movies/movies.js | 1 - src/controllers/movies/movietrailers.js | 31 +++++++++++----------- 4 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/controllers/movies/moviecollections.js b/src/controllers/movies/moviecollections.js index 530117295c..ffedb10eef 100644 --- a/src/controllers/movies/moviecollections.js +++ b/src/controllers/movies/moviecollections.js @@ -53,8 +53,8 @@ import 'emby-itemscontainer'; return context.savedQueryKey; } - function onViewStyleChange() { - const viewStyle = self.getCurrentViewStyle(); + const onViewStyleChange = () => { + const viewStyle = this.getCurrentViewStyle(); const itemsContainer = tabContent.querySelector('.itemsContainer'); if (viewStyle == 'List') { @@ -66,13 +66,13 @@ import 'emby-itemscontainer'; } itemsContainer.innerHTML = ''; - } + }; - function reloadItems(page) { + const reloadItems = (page) => { loading.show(); isLoading = true; const query = getQuery(page); - ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { + ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => { function onNextPageClick() { if (isLoading) { return; @@ -107,7 +107,7 @@ import 'emby-itemscontainer'; sortButton: false, filterButton: false }); - const viewStyle = self.getCurrentViewStyle(); + const viewStyle = this.getCurrentViewStyle(); if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ items: result.Items, @@ -198,17 +198,16 @@ import 'emby-itemscontainer'; autoFocuser.autoFocus(page); }); }); - } + }; - const self = this; const data = {}; let isLoading = false; - self.getCurrentViewStyle = function () { + this.getCurrentViewStyle = function () { return getPageData(tabContent).view; }; - function initPage(tabContent) { + const initPage = (tabContent) => { tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ items: [{ @@ -237,7 +236,7 @@ import 'emby-itemscontainer'; }); const btnSelectView = tabContent.querySelector('.btnSelectView'); btnSelectView.addEventListener('click', function (e) { - libraryBrowser.showLayoutMenu(e.target, self.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); + libraryBrowser.showLayoutMenu(e.target, this.getCurrentViewStyle(), 'List,Poster,PosterCard,Thumb,ThumbCard'.split(',')); }); btnSelectView.addEventListener('layoutchange', function (e) { const viewStyle = e.detail.viewStyle; @@ -256,16 +255,16 @@ import 'emby-itemscontainer'; }); }); }); - } + }; initPage(tabContent); onViewStyleChange(); - self.renderTab = function () { + this.renderTab = function () { reloadItems(tabContent); }; - self.destroy = function () {}; + this.destroy = function () {}; } /* eslint-enable indent */ diff --git a/src/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index 56fb88b20f..f426ac024d 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -60,10 +60,10 @@ import 'emby-button'; return enableScrollX() ? 'overflowPortrait' : 'portrait'; } - function fillItemsContainer(entry) { + const fillItemsContainer = (entry) => { const elem = entry.target; const id = elem.getAttribute('data-id'); - const viewStyle = self.getCurrentViewStyle(); + const viewStyle = this.getCurrentViewStyle(); let limit = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 5 : 9; if (enableScrollX()) { @@ -133,7 +133,7 @@ import 'emby-button'; tabContent.querySelector('.btnMoreFromGenre' + id + ' .material-icons').classList.remove('hide'); } }); - } + }; function reloadItems(context, promise) { const query = getQuery(); @@ -189,36 +189,35 @@ import 'emby-button'; }); } - function fullyReload() { - self.preRender(); - self.renderTab(); - } + const fullyReload = () => { + this.preRender(); + this.renderTab(); + }; - const self = this; const data = {}; - self.getViewStyles = function () { + this.getViewStyles = function () { return 'Poster,PosterCard,Thumb,ThumbCard'.split(','); }; - self.getCurrentViewStyle = function () { + this.getCurrentViewStyle = function () { return getPageData().view; }; - self.setCurrentViewStyle = function (viewStyle) { + this.setCurrentViewStyle = function (viewStyle) { getPageData().view = viewStyle; libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle); fullyReload(); }; - self.enableViewSelection = true; + this.enableViewSelection = true; let promise; - self.preRender = function () { + this.preRender = function () { promise = getPromise(); }; - self.renderTab = function () { + this.renderTab = function () { reloadItems(tabContent, promise); }; } diff --git a/src/controllers/movies/movies.js b/src/controllers/movies/movies.js index 3b68849036..302e8b717e 100644 --- a/src/controllers/movies/movies.js +++ b/src/controllers/movies/movies.js @@ -297,7 +297,6 @@ import 'emby-itemscontainer'; onViewStyleChange(); }; - //function renderTab () { this.renderTab = function () { itemsContainer.refreshItems(); updateFilterControls(); diff --git a/src/controllers/movies/movietrailers.js b/src/controllers/movies/movietrailers.js index a892769e47..1cab0f1999 100644 --- a/src/controllers/movies/movietrailers.js +++ b/src/controllers/movies/movietrailers.js @@ -54,11 +54,11 @@ import 'emby-itemscontainer'; return context.savedQueryKey; } - function reloadItems() { + const reloadItems = () => { loading.show(); isLoading = true; const query = getQuery(tabContent); - ApiClient.getItems(ApiClient.getCurrentUserId(), query).then(function (result) { + ApiClient.getItems(ApiClient.getCurrentUserId(), query).then((result) => { function onNextPageClick() { if (isLoading) { return; @@ -94,7 +94,7 @@ import 'emby-itemscontainer'; filterButton: false }); let html; - const viewStyle = self.getCurrentViewStyle(); + const viewStyle = this.getCurrentViewStyle(); if (viewStyle == 'Thumb') { html = cardBuilder.getCardsHtml({ @@ -182,18 +182,17 @@ import 'emby-itemscontainer'; loading.hide(); isLoading = false; }); - } + }; - function updateFilterControls(tabContent) { + const updateFilterControls = (tabContent) => { const query = getQuery(tabContent); - self.alphaPicker.value(query.NameStartsWithOrGreater); - } + this.alphaPicker.value(query.NameStartsWithOrGreater); + }; - const self = this; const data = {}; let isLoading = false; - self.showFilterMenu = function () { + this.showFilterMenu = function () { import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { const filterDialog = new filterDialogFactory({ query: getQuery(tabContent), @@ -208,11 +207,11 @@ import 'emby-itemscontainer'; }); }; - self.getCurrentViewStyle = function () { + this.getCurrentViewStyle = function () { return getPageData(tabContent).view; }; - function initPage(tabContent) { + const initPage = (tabContent) => { const alphaPickerElement = tabContent.querySelector('.alphaPicker'); const itemsContainer = tabContent.querySelector('.itemsContainer'); alphaPickerElement.addEventListener('alphavaluechanged', function (e) { @@ -222,7 +221,7 @@ import 'emby-itemscontainer'; query.StartIndex = 0; reloadItems(); }); - self.alphaPicker = new AlphaPicker({ + this.alphaPicker = new AlphaPicker({ element: alphaPickerElement, valueChangeEvent: 'click' }); @@ -232,7 +231,7 @@ import 'emby-itemscontainer'; itemsContainer.classList.add('padded-right-withalphapicker'); tabContent.querySelector('.btnFilter').addEventListener('click', function () { - self.showFilterMenu(); + this.showFilterMenu(); }); tabContent.querySelector('.btnSort').addEventListener('click', function (e) { libraryBrowser.showSortMenu({ @@ -266,16 +265,16 @@ import 'emby-itemscontainer'; button: e.target }); }); - } + }; initPage(tabContent); - self.renderTab = function () { + this.renderTab = function () { reloadItems(); updateFilterControls(tabContent); }; - self.destroy = function () {}; + this.destroy = function () {}; } /* eslint-enable indent */ From a09ceeefa35e908ac33d29a1cead39fc25b8cb07 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 25 Jul 2020 23:03:15 +0100 Subject: [PATCH 041/155] removal of self --- src/controllers/movies/moviesrecommended.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index 5e376637b3..dda75a2320 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -281,7 +281,7 @@ import 'emby-button'; mainTabsManager.setTabs(view, currentTabIndex, getTabs, getTabContainers, onBeforeTabChange, onTabChange); } - function getTabController(page, index, callback) { + const getTabController = (page, index, callback) => { let depends = ''; switch (index) { @@ -319,7 +319,7 @@ import 'emby-button'; if (index === suggestionsTabIndex) { tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']"); - self.tabContent = tabContent; + this.tabContent = tabContent; } let controller = tabControllers[index]; @@ -328,7 +328,7 @@ import 'emby-button'; tabContent = view.querySelector(".pageTabContent[data-index='" + index + "']"); if (index === suggestionsTabIndex) { - controller = self; + controller = this; } else if (index === 6) { controller = new controllerFactory(view, tabContent, { collectionType: 'movies', @@ -351,7 +351,7 @@ import 'emby-button'; callback(controller); }); - } + }; function preLoadTab(page, index) { getTabController(page, index, function (controller) { @@ -363,12 +363,14 @@ import 'emby-button'; function loadTab(page, index) { currentTabIndex = index; - getTabController(page, index, function (controller) { + getTabController(page, index, ((controller) => { + initialTabIndex = null; + if (renderedTabs.indexOf(index) == -1) { renderedTabs.push(index); controller.renderTab(); } - }); + })); } function onPlaybackStop(e, state) { @@ -390,12 +392,12 @@ import 'emby-button'; var currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId)); var suggestionsTabIndex = 1; - self.initTab = function () { + this.initTab = function () { let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); initSuggestedTab(view, tabContent); }; - self.renderTab = function () { + this.renderTab = function () { let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); loadSuggestionsTab(view, params, tabContent); }; From f7c3445e1c2c14ebe806e052328ab55432ff15ba Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 28 Jul 2020 21:14:40 +0100 Subject: [PATCH 042/155] remove smells --- src/controllers/movies/moviecollections.js | 1 - src/controllers/movies/moviegenres.js | 2 -- src/controllers/movies/movies.js | 1 - src/controllers/movies/moviesrecommended.js | 2 +- src/controllers/movies/movietrailers.js | 1 - 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/controllers/movies/moviecollections.js b/src/controllers/movies/moviecollections.js index ffedb10eef..b99c918a13 100644 --- a/src/controllers/movies/moviecollections.js +++ b/src/controllers/movies/moviecollections.js @@ -1,5 +1,4 @@ import loading from 'loading'; -import events from 'events'; import libraryBrowser from 'libraryBrowser'; import imageLoader from 'imageLoader'; import listView from 'listView'; diff --git a/src/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index f426ac024d..3ca39601fc 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -3,10 +3,8 @@ import loading from 'loading'; import libraryBrowser from 'libraryBrowser'; import cardBuilder from 'cardBuilder'; import lazyLoader from 'lazyLoader'; -import appHost from 'apphost'; import globalize from 'globalize'; import appRouter from 'appRouter'; -import dom from 'dom'; import 'emby-button'; /* eslint-disable indent */ diff --git a/src/controllers/movies/movies.js b/src/controllers/movies/movies.js index 302e8b717e..91b428ec68 100644 --- a/src/controllers/movies/movies.js +++ b/src/controllers/movies/movies.js @@ -1,5 +1,4 @@ import loading from 'loading'; -import layoutManager from 'layoutManager'; import * as userSettings from 'userSettings'; import events from 'events'; import libraryBrowser from 'libraryBrowser'; diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index dda75a2320..63de12dd35 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -423,7 +423,7 @@ import 'emby-button'; events.on(playbackManager, 'playbackstop', onPlaybackStop); inputManager.on(window, onInputCommand); }); - view.addEventListener('viewbeforehide', function (e) { + view.addEventListener('viewbeforehide', function () { inputManager.off(window, onInputCommand); }); for (const tabController of tabControllers) { diff --git a/src/controllers/movies/movietrailers.js b/src/controllers/movies/movietrailers.js index 1cab0f1999..5f1aa1fe62 100644 --- a/src/controllers/movies/movietrailers.js +++ b/src/controllers/movies/movietrailers.js @@ -1,4 +1,3 @@ -import layoutManager from 'layoutManager'; import loading from 'loading'; import events from 'events'; import libraryBrowser from 'libraryBrowser'; From c13742ff544008829398378192143b8726f98085 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 29 Jul 2020 19:23:45 +0100 Subject: [PATCH 043/155] fix lint --- src/controllers/movies/moviesrecommended.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index 63de12dd35..04f1d26ebf 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -388,9 +388,8 @@ import 'emby-button'; } } - var self = this; - var currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId)); - var suggestionsTabIndex = 1; + let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId)); + const suggestionsTabIndex = 1; this.initTab = function () { let tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']"); From 9cafbb3eeb41a60db9719299bd02098c6cfc7190 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 30 Jul 2020 13:32:29 +0100 Subject: [PATCH 044/155] remove duplicates, defaults and fix --- package.json | 6 ++++++ src/controllers/movies/moviesrecommended.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 678794aebc..ad4c8e1109 100644 --- a/package.json +++ b/package.json @@ -191,6 +191,12 @@ "src/controllers/music/musicplaylists.js", "src/controllers/music/musicrecommended.js", "src/controllers/music/songs.js", + "src/controllers/dashboard/plugins/repositories.js", + "src/controllers/movies/moviecollections.js", + "src/controllers/movies/moviegenres.js", + "src/controllers/movies/movies.js", + "src/controllers/movies/moviesrecommended.js", + "src/controllers/movies/movietrailers.js", "src/controllers/dashboard/mediaLibrary.js", "src/controllers/dashboard/metadataImages.js", "src/controllers/dashboard/metadatanfo.js", diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index 04f1d26ebf..ad8579e292 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -3,7 +3,7 @@ import layoutManager from 'layoutManager'; import inputManager from 'inputManager'; import * as userSettings from 'userSettings'; import libraryMenu from 'libraryMenu'; -import mainTabsManager from 'mainTabsManager'; +import * as mainTabsManager from 'mainTabsManager'; import cardBuilder from 'cardBuilder'; import dom from 'dom'; import imageLoader from 'imageLoader'; From 9bdc1d8bd9d5b3180fe13d8431c92e9cdda502b1 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Mon, 3 Aug 2020 21:34:14 +0200 Subject: [PATCH 045/155] Migrated livetvchannels to es6 module --- package.json | 1 + src/controllers/livetv/livetvchannels.js | 241 ++++++++++++----------- 2 files changed, 122 insertions(+), 120 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..6540af9f43 100644 --- a/package.json +++ b/package.json @@ -222,6 +222,7 @@ "src/controllers/livetvstatus.js", "src/controllers/livetvguideprovider.js", "src/controllers/livetvsettings.js", + "src/controllers/livetv/livetvchannels.js", "src/controllers/shows/episodes.js", "src/controllers/shows/tvgenres.js", "src/controllers/shows/tvlatest.js", diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index f5a0abeb38..8af6411296 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -1,133 +1,134 @@ -define(['cardBuilder', 'imageLoader', 'libraryBrowser', 'loading', 'events', 'userSettings', 'emby-itemscontainer'], function (cardBuilder, imageLoader, libraryBrowser, loading, events, userSettings) { - 'use strict'; +import cardBuilder from 'cardBuilder'; +import imageLoader from 'imageLoader'; +import libraryBrowser from 'libraryBrowser'; +import loading from 'loading'; +import events from 'events'; +import userSettings from 'userSettings'; +import 'emby-itemscontainer'; - loading = loading.default || loading; - libraryBrowser = libraryBrowser.default || libraryBrowser; +export default function (view, params, tabContent) { + function getPageData() { + if (!pageData) { + pageData = { + query: { + StartIndex: 0, + Fields: 'PrimaryImageAspectRatio' + } + }; + } - return function (view, params, tabContent) { - function getPageData() { - if (!pageData) { - pageData = { - query: { - StartIndex: 0, - Fields: 'PrimaryImageAspectRatio' - } - }; + if (userSettings.libraryPageSize() > 0) { + pageData.query['Limit'] = userSettings.libraryPageSize(); + } + + return pageData; + } + + function getQuery() { + return getPageData().query; + } + + function getChannelsHtml(channels) { + return cardBuilder.getCardsHtml({ + items: channels, + shape: 'square', + showTitle: true, + lazy: true, + cardLayout: true, + showDetailsMenu: true, + showCurrentProgram: true, + showCurrentProgramTime: true + }); + } + + function renderChannels(context, result) { + function onNextPageClick() { + if (isLoading) { + return; } if (userSettings.libraryPageSize() > 0) { - pageData.query['Limit'] = userSettings.libraryPageSize(); + query.StartIndex += query.Limit; + } + reloadItems(context); + } + + function onPreviousPageClick() { + if (isLoading) { + return; } - return pageData; - } - - function getQuery() { - return getPageData().query; - } - - function getChannelsHtml(channels) { - return cardBuilder.getCardsHtml({ - items: channels, - shape: 'square', - showTitle: true, - lazy: true, - cardLayout: true, - showDetailsMenu: true, - showCurrentProgram: true, - showCurrentProgramTime: true - }); - } - - function renderChannels(context, result) { - function onNextPageClick() { - if (isLoading) { - return; - } - - if (userSettings.libraryPageSize() > 0) { - query.StartIndex += query.Limit; - } - reloadItems(context); - } - - function onPreviousPageClick() { - if (isLoading) { - return; - } - - if (userSettings.libraryPageSize() > 0) { - query.StartIndex = Math.max(0, query.StartIndex - query.Limit); - } - reloadItems(context); - } - - var query = getQuery(); - context.querySelector('.paging').innerHTML = libraryBrowser.getQueryPagingHtml({ - startIndex: query.StartIndex, - limit: query.Limit, - totalRecordCount: result.TotalRecordCount, - showLimit: false, - updatePageSizeSetting: false, - filterButton: false - }); - var html = getChannelsHtml(result.Items); - var elem = context.querySelector('#items'); - elem.innerHTML = html; - imageLoader.lazyChildren(elem); - var i; - var length; - var elems; - - for (elems = context.querySelectorAll('.btnNextPage'), i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onNextPageClick); - } - - for (elems = context.querySelectorAll('.btnPreviousPage'), i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener('click', onPreviousPageClick); + if (userSettings.libraryPageSize() > 0) { + query.StartIndex = Math.max(0, query.StartIndex - query.Limit); } + reloadItems(context); } - function showFilterMenu(context) { - require(['components/filterdialog/filterdialog'], function ({default: filterDialogFactory}) { - var filterDialog = new filterDialogFactory({ - query: getQuery(), - mode: 'livetvchannels', - serverId: ApiClient.serverId() - }); - events.on(filterDialog, 'filterchange', function () { - reloadItems(context); - }); - filterDialog.show(); - }); - } - - function reloadItems(context, save) { - loading.show(); - isLoading = true; - var query = getQuery(); - var apiClient = ApiClient; - query.UserId = apiClient.getCurrentUserId(); - apiClient.getLiveTvChannels(query).then(function (result) { - renderChannels(context, result); - loading.hide(); - isLoading = false; - - require(['autoFocuser'], function (autoFocuser) { - autoFocuser.autoFocus(view); - }); - }); - } - - var pageData; - var self = this; - var isLoading = false; - tabContent.querySelector('.btnFilter').addEventListener('click', function () { - showFilterMenu(tabContent); + const query = getQuery(); + context.querySelector('.paging').innerHTML = libraryBrowser.getQueryPagingHtml({ + startIndex: query.StartIndex, + limit: query.Limit, + totalRecordCount: result.TotalRecordCount, + showLimit: false, + updatePageSizeSetting: false, + filterButton: false }); + const html = getChannelsHtml(result.Items); + const elem = context.querySelector('#items'); + elem.innerHTML = html; + imageLoader.lazyChildren(elem); + let i; + let length; + let elems; - self.renderTab = function () { - reloadItems(tabContent); - }; + for (elems = context.querySelectorAll('.btnNextPage'), i = 0, length = elems.length; i < length; i++) { + elems[i].addEventListener('click', onNextPageClick); + } + + for (elems = context.querySelectorAll('.btnPreviousPage'), i = 0, length = elems.length; i < length; i++) { + elems[i].addEventListener('click', onPreviousPageClick); + } + } + + function showFilterMenu(context) { + import(['components/filterdialog/filterdialog']).then(({default: filterDialogFactory}) => { + const filterDialog = new filterDialogFactory({ + query: getQuery(), + mode: 'livetvchannels', + serverId: ApiClient.serverId() + }); + events.on(filterDialog, 'filterchange', function () { + reloadItems(context); + }); + filterDialog.show(); + }); + } + + function reloadItems(context, save) { + loading.show(); + isLoading = true; + const query = getQuery(); + const apiClient = ApiClient; + query.UserId = apiClient.getCurrentUserId(); + apiClient.getLiveTvChannels(query).then(function (result) { + renderChannels(context, result); + loading.hide(); + isLoading = false; + + require(['autoFocuser'], function (autoFocuser) { + autoFocuser.autoFocus(view); + }); + }); + } + + let pageData; + const self = this; + let isLoading = false; + tabContent.querySelector('.btnFilter').addEventListener('click', function () { + showFilterMenu(tabContent); + }); + + self.renderTab = function () { + reloadItems(tabContent); }; -}); +} From ddf364adb2e08443d79092810d0b3096243c1645 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 3 Aug 2020 20:46:39 +0100 Subject: [PATCH 046/155] Lint --- src/controllers/movies/moviegenres.js | 4 ++-- src/controllers/movies/moviesrecommended.js | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index 3ca39601fc..ca02ede36d 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -62,13 +62,13 @@ import 'emby-button'; const elem = entry.target; const id = elem.getAttribute('data-id'); const viewStyle = this.getCurrentViewStyle(); - let limit = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 5 : 9; + let limit = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 5 : 9; if (enableScrollX()) { limit = 10; } - const enableImageTypes = 'Thumb' == viewStyle || 'ThumbCard' == viewStyle ? 'Primary,Backdrop,Thumb' : 'Primary'; + const enableImageTypes = viewStyle == 'Thumb' || viewStyle == 'ThumbCard' ? 'Primary,Backdrop,Thumb' : 'Primary'; const query = { SortBy: 'SortName', SortOrder: 'Ascending', diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index ad8579e292..5cba66d2f8 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -16,8 +16,6 @@ import 'emby-button'; /* eslint-disable indent */ - playbackManager = playbackManager.default || playbackManager; - function enableScrollX() { return !layoutManager.desktop; } @@ -364,8 +362,6 @@ import 'emby-button'; function loadTab(page, index) { currentTabIndex = index; getTabController(page, index, ((controller) => { - initialTabIndex = null; - if (renderedTabs.indexOf(index) == -1) { renderedTabs.push(index); controller.renderTab(); From d6501a177adc70bdde05b8ec05b6be5543769382 Mon Sep 17 00:00:00 2001 From: millallo Date: Mon, 3 Aug 2020 21:45:47 +0000 Subject: [PATCH 047/155] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/it/ --- src/strings/it.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strings/it.json b/src/strings/it.json index cf2daf77b2..aa025f3bcd 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -79,7 +79,7 @@ "ButtonNetwork": "Rete", "ButtonNew": "Nuovo", "ButtonNextTrack": "Traccia Successiva", - "ButtonOff": "Spento", + "ButtonOff": "No", "ButtonOpen": "Apri", "ButtonParentalControl": "Controllo parentale", "ButtonPause": "Pausa", @@ -901,7 +901,7 @@ "None": "Nessuno", "Normal": "Normale", "NumLocationsValue": "{0} cartelle", - "Off": "Spento", + "Off": "No", "OneChannel": "Un canale", "OnlyForcedSubtitles": "Solo forzati", "OnlyForcedSubtitlesHelp": "Solo i sottotitoli contrassegnati come forzati saranno caricati.", From de1884842583490efa4e820c3c03e1cc380cc8a7 Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 4 Aug 2020 07:58:32 +0900 Subject: [PATCH 048/155] add code suggestions --- scripts/duplicates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/duplicates.py b/scripts/duplicates.py index bbacffd0f1..2daad94682 100644 --- a/scripts/duplicates.py +++ b/scripts/duplicates.py @@ -21,7 +21,7 @@ with open(source) as en: for key, value in reverse.items(): if len(value) > 1: - duplicates[key] = reverse[key] + duplicates[key] = value print('LENGTH: ' + str(len(duplicates))) with open('duplicates.txt', 'w') as out: From 4b84e6c0aa97d53be1996941591c762126251116 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 4 Aug 2020 00:51:19 +0300 Subject: [PATCH 049/155] Fix old Edge loading --- src/scripts/site.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/scripts/site.js b/src/scripts/site.js index 604f9c7bab..ab8e17971d 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -696,20 +696,12 @@ function initClient() { onError: onRequireJsError }); - require(['fetch']); - require(['polyfill']); - require(['fast-text-encoding']); - require(['intersection-observer']); - require(['classlist-polyfill']); - - // Expose jQuery globally - require(['jQuery'], function(jQuery) { - window.$ = jQuery; - window.jQuery = jQuery; - }); - - require(['css!assets/css/site']); - require(['jellyfin-noto']); + require(['fetch']) + .then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'css!assets/css/site', 'jellyfin-noto'], (jQuery) => { + // Expose jQuery globally + window.$ = jQuery; + window.jQuery = jQuery; + })); // define styles // TODO determine which of these files can be moved to the components themselves From 07a54e93039593b9f66769951f6aa34d044528e8 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 4 Aug 2020 09:39:34 +0300 Subject: [PATCH 050/155] Wait for initialization to complete --- src/scripts/site.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/scripts/site.js b/src/scripts/site.js index ab8e17971d..cd85c35e83 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -615,6 +615,7 @@ function initClient() { } var localApiClient; + let promise; (function () { var urlArgs = 'v=' + (window.dashboardVersion || new Date().getDate()); @@ -696,7 +697,7 @@ function initClient() { onError: onRequireJsError }); - require(['fetch']) + promise = require(['fetch']) .then(() => require(['jQuery', 'polyfill', 'fast-text-encoding', 'intersection-observer', 'classlist-polyfill', 'css!assets/css/site', 'jellyfin-noto'], (jQuery) => { // Expose jQuery globally window.$ = jQuery; @@ -1107,7 +1108,7 @@ function initClient() { }); })(); - return onWebComponentsReady(); + promise.then(onWebComponentsReady); } initClient(); From 947f8ba05de15e4723d9317a28ea419c21c5d078 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 08:55:54 +0100 Subject: [PATCH 051/155] Migration of livetvguide and livetvcomponents to ES6 moduoles --- package.json | 2 + src/controllers/livetv/livetvguide.js | 50 +++--- src/scripts/livetvcomponents.js | 209 ++++++++++++-------------- 3 files changed, 125 insertions(+), 136 deletions(-) diff --git a/package.json b/package.json index 22fd43308a..e3d0c4d144 100644 --- a/package.json +++ b/package.json @@ -209,6 +209,7 @@ "src/controllers/playback/queue/index.js", "src/controllers/playback/video/index.js", "src/controllers/searchpage.js", + "src/controllers/livetv/livetvguide.js", "src/controllers/livetvtuner.js", "src/controllers/livetvstatus.js", "src/controllers/livetvsettings.js", @@ -270,6 +271,7 @@ "src/scripts/inputManager.js", "src/scripts/keyboardNavigation.js", "src/scripts/libraryBrowser.js", + "src/scripts/livetvcomponents.js", "src/scripts/mouseManager.js", "src/scripts/multiDownload.js", "src/scripts/playlists.js", diff --git a/src/controllers/livetv/livetvguide.js b/src/controllers/livetv/livetvguide.js index ec7a7a3f81..77214a87b3 100644 --- a/src/controllers/livetv/livetvguide.js +++ b/src/controllers/livetv/livetvguide.js @@ -1,29 +1,27 @@ -define(['tvguide'], function (tvguide) { - 'use strict'; +import tvguide from 'tvguide'; - return function (view, params, tabContent) { - var guideInstance; - var self = this; +export default function (view, params, tabContent) { + let guideInstance; + const self = this; - self.renderTab = function () { - if (!guideInstance) { - guideInstance = new tvguide({ - element: tabContent, - serverId: ApiClient.serverId() - }); - } - }; - - self.onShow = function () { - if (guideInstance) { - guideInstance.resume(); - } - }; - - self.onHide = function () { - if (guideInstance) { - guideInstance.pause(); - } - }; + self.renderTab = function () { + if (!guideInstance) { + guideInstance = new tvguide({ + element: tabContent, + serverId: ApiClient.serverId() + }); + } }; -}); + + self.onShow = function () { + if (guideInstance) { + guideInstance.resume(); + } + }; + + self.onHide = function () { + if (guideInstance) { + guideInstance.pause(); + } + }; +}; diff --git a/src/scripts/livetvcomponents.js b/src/scripts/livetvcomponents.js index fd1b48d0eb..387972d996 100644 --- a/src/scripts/livetvcomponents.js +++ b/src/scripts/livetvcomponents.js @@ -1,115 +1,104 @@ -define(['layoutManager', 'datetime', 'cardBuilder', 'apphost'], function (layoutManager, datetime, cardBuilder, appHost) { - 'use strict'; +import layoutManager from 'layoutManager'; +import datetime from 'datetime'; +import cardBuilder from 'cardBuilder'; - function enableScrollX() { - return !layoutManager.desktop; +function enableScrollX() { + return !layoutManager.desktop; +} + +function getBackdropShape() { + return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; +} + +function getTimersHtml(timers, options) { + options = options || {}; + const items = timers.map(function (t) { + t.Type = 'Timer'; + return t; + }); + const groups = []; + let currentGroupName = ''; + let currentGroup = []; + + for (let i = 0, length = items.length; i < length; i++) { + const item = items[i]; + let dateText = ''; + + if (options.indexByDate !== false && item.StartDate) { + try { + const premiereDate = datetime.parseISO8601Date(item.StartDate, true); + dateText = datetime.toLocaleDateString(premiereDate, { + weekday: 'long', + month: 'short', + day: 'numeric' + }); + } catch (err) { + console.error('error parsing premiereDate:' + item.StartDate + '; error: ' + err); + } + } + + if (dateText != currentGroupName) { + if (currentGroup.length) { + groups.push({ + name: currentGroupName, + items: currentGroup + }); + } + + currentGroupName = dateText; + currentGroup = [item]; + } else { + currentGroup.push(item); + } } - - function getBackdropShape() { - return enableScrollX() ? 'overflowBackdrop' : 'backdrop'; - } - - function getTimersHtml(timers, options) { - options = options || {}; - var i; - var length; - var items = timers.map(function (t) { - t.Type = 'Timer'; - return t; + if (currentGroup.length) { + groups.push({ + name: currentGroupName, + items: currentGroup }); - var groups = []; - var currentGroupName = ''; - var currentGroup = []; - - for (i = 0, length = items.length; i < length; i++) { - var item = items[i]; - var dateText = ''; - - if (options.indexByDate !== false && item.StartDate) { - try { - var premiereDate = datetime.parseISO8601Date(item.StartDate, true); - dateText = datetime.toLocaleDateString(premiereDate, { - weekday: 'long', - month: 'short', - day: 'numeric' - }); - } catch (err) { - console.error('error parsing premiereDate:' + item.StartDate + '; error: ' + err); - } - } - - if (dateText != currentGroupName) { - if (currentGroup.length) { - groups.push({ - name: currentGroupName, - items: currentGroup - }); - } - - currentGroupName = dateText; - currentGroup = [item]; - } else { - currentGroup.push(item); - } - } - - if (currentGroup.length) { - groups.push({ - name: currentGroupName, - items: currentGroup - }); - } - - var html = ''; - - for (i = 0, length = groups.length; i < length; i++) { - var group = groups[i]; - - if (group.name) { - html += '
'; - html += '

' + group.name + '

'; - } - if (enableScrollX()) { - var scrollXClass = 'scrollX hiddenScrollX'; - - if (layoutManager.tv) { - scrollXClass += ' smoothScrollX'; - } - - html += '
'; - } else { - html += '
'; - } - - html += cardBuilder.getCardsHtml({ - items: group.items, - shape: getBackdropShape(), - showParentTitleOrTitle: true, - showAirTime: true, - showAirEndTime: true, - showChannelName: false, - cardLayout: true, - centerText: false, - action: 'edit', - cardFooterAside: 'none', - preferThumb: true, - defaultShape: null, - coverImage: true, - allowBottomPadding: false, - overlayText: false, - showChannelLogo: true - }); - html += '
'; - - if (group.name) { - html += '
'; - } - } - - return Promise.resolve(html); } + let html = ''; + for (let i = 0, length = groups.length; i < length; i++) { + const group = groups[i]; + if (group.name) { + html += '
'; + html += '

' + group.name + '

'; + } + if (enableScrollX()) { + let scrollXClass = 'scrollX hiddenScrollX'; + if (layoutManager.tv) { + scrollXClass += ' smoothScrollX'; + } + html += '
'; + } else { + html += '
'; + } + html += cardBuilder.getCardsHtml({ + items: group.items, + shape: getBackdropShape(), + showParentTitleOrTitle: true, + showAirTime: true, + showAirEndTime: true, + showChannelName: false, + cardLayout: true, + centerText: false, + action: 'edit', + cardFooterAside: 'none', + preferThumb: true, + defaultShape: null, + coverImage: true, + allowBottomPadding: false, + overlayText: false, + showChannelLogo: true + }); + html += '
'; + if (group.name) { + html += '
'; + } + } + return Promise.resolve(html); +} - window.LiveTvHelpers = { - getTimersHtml: getTimersHtml - }; -}); +window.LiveTvHelpers = { + getTimersHtml: getTimersHtml +}; From 44800858e5181abb1df1b875db7e571d8308acdb Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 09:03:02 +0100 Subject: [PATCH 052/155] lint and replace for with for...of --- src/controllers/livetv/livetvguide.js | 2 +- src/scripts/livetvcomponents.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/controllers/livetv/livetvguide.js b/src/controllers/livetv/livetvguide.js index 77214a87b3..f8b49bd22a 100644 --- a/src/controllers/livetv/livetvguide.js +++ b/src/controllers/livetv/livetvguide.js @@ -24,4 +24,4 @@ export default function (view, params, tabContent) { guideInstance.pause(); } }; -}; +} diff --git a/src/scripts/livetvcomponents.js b/src/scripts/livetvcomponents.js index 387972d996..f1d0b2d3da 100644 --- a/src/scripts/livetvcomponents.js +++ b/src/scripts/livetvcomponents.js @@ -12,16 +12,17 @@ function getBackdropShape() { function getTimersHtml(timers, options) { options = options || {}; + const items = timers.map(function (t) { t.Type = 'Timer'; return t; }); + const groups = []; let currentGroupName = ''; let currentGroup = []; - for (let i = 0, length = items.length; i < length; i++) { - const item = items[i]; + for (const item in items) { let dateText = ''; if (options.indexByDate !== false && item.StartDate) { @@ -51,6 +52,7 @@ function getTimersHtml(timers, options) { currentGroup.push(item); } } + if (currentGroup.length) { groups.push({ name: currentGroupName, @@ -58,12 +60,12 @@ function getTimersHtml(timers, options) { }); } let html = ''; - for (let i = 0, length = groups.length; i < length; i++) { - const group = groups[i]; + for (const group in groups) { if (group.name) { html += '
'; html += '

' + group.name + '

'; } + if (enableScrollX()) { let scrollXClass = 'scrollX hiddenScrollX'; if (layoutManager.tv) { @@ -73,6 +75,7 @@ function getTimersHtml(timers, options) { } else { html += '
'; } + html += cardBuilder.getCardsHtml({ items: group.items, shape: getBackdropShape(), @@ -91,7 +94,9 @@ function getTimersHtml(timers, options) { overlayText: false, showChannelLogo: true }); + html += '
'; + if (group.name) { html += '
'; } From 2feb4c966f8473b95fca2742a1dc29ec3f57c3e3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 10:33:16 +0100 Subject: [PATCH 053/155] Migration of recordingbutton and recordingcreator to ES6 modules --- package.json | 2 + .../recordingcreator/recordingbutton.js | 129 +++---- .../recordingcreator/recordingcreator.js | 333 +++++++++--------- 3 files changed, 245 insertions(+), 219 deletions(-) diff --git a/package.json b/package.json index 5ad12d2011..72036d04e6 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,8 @@ "src/components/playlisteditor/playlisteditor.js", "src/components/playmenu.js", "src/components/prompt/prompt.js", + "src/components/recordingcreator/recordingbutton.js", + "src/components/recordingcreator/recordingcreator.js", "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", diff --git a/src/components/recordingcreator/recordingbutton.js b/src/components/recordingcreator/recordingbutton.js index 3ad4ffa594..78fb0563cf 100644 --- a/src/components/recordingcreator/recordingbutton.js +++ b/src/components/recordingcreator/recordingbutton.js @@ -1,35 +1,42 @@ -define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields'], function (globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events) { - 'use strict'; +import connectionManager from 'connectionManager'; +import dom from 'dom'; +import recordingHelper from 'recordingHelper'; +import 'paper-icon-button-light'; +import 'emby-button'; +import 'css!./recordingfields'; - function onRecordingButtonClick(e) { - var item = this.item; +/*eslint prefer-const: "error"*/ - if (item) { - var serverId = item.ServerId; - var programId = item.Id; - var timerId = item.TimerId; - var timerStatus = item.Status; - var seriesTimerId = item.SeriesTimerId; +function onRecordingButtonClick(e) { + const item = this.item; - var instance = this; + if (item) { + const serverId = item.ServerId; + const programId = item.Id; + const timerId = item.TimerId; + const timerStatus = item.Status; + const seriesTimerId = item.SeriesTimerId; - recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function () { - instance.refresh(serverId, programId); - }); - } + const instance = this; + + recordingHelper.toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId).then(function () { + instance.refresh(serverId, programId); + }); } +} - function setButtonIcon(button, icon) { - var inner = button.querySelector('.material-icons'); - inner.classList.remove('fiber_smart_record'); - inner.classList.remove('fiber_manual_record'); - inner.classList.add(icon); - } +function setButtonIcon(button, icon) { + const inner = button.querySelector('.material-icons'); + inner.classList.remove('fiber_smart_record'); + inner.classList.remove('fiber_manual_record'); + inner.classList.add(icon); +} - function RecordingButton(options) { +class RecordingButton { + constructor(options) { this.options = options; - var button = options.button; + const button = options.button; setButtonIcon(button, 'fiber_manual_record'); @@ -39,7 +46,7 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom' this.refresh(options.itemId, options.serverId); } - var clickFn = onRecordingButtonClick.bind(this); + const clickFn = onRecordingButtonClick.bind(this); this.clickFn = clickFn; dom.addEventListener(button, 'click', clickFn, { @@ -47,39 +54,17 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom' }); } - function getIndicatorIcon(item) { - var status; - - if (item.Type === 'SeriesTimer') { - return 'fiber_smart_record'; - } else if (item.TimerId || item.SeriesTimerId) { - status = item.Status || 'Cancelled'; - } else if (item.Type === 'Timer') { - status = item.Status; - } else { - return 'fiber_manual_record'; - } - - if (item.SeriesTimerId) { - if (status !== 'Cancelled') { - return 'fiber_smart_record'; - } - } - - return 'fiber_manual_record'; - } - - RecordingButton.prototype.refresh = function (serverId, itemId) { - var apiClient = connectionManager.getApiClient(serverId); - var self = this; + refresh(serverId, itemId) { + const apiClient = connectionManager.getApiClient(serverId); + const self = this; apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { self.refreshItem(item); }); - }; + } - RecordingButton.prototype.refreshItem = function (item) { - var options = this.options; - var button = options.button; + refreshItem(item) { + const options = this.options; + const button = options.button; this.item = item; setButtonIcon(button, getIndicatorIcon(item)); @@ -88,15 +73,15 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom' } else { button.classList.remove('recordingIcon-active'); } - }; + } - RecordingButton.prototype.destroy = function () { - var options = this.options; + destroy() { + const options = this.options; if (options) { - var button = options.button; + const button = options.button; - var clickFn = this.clickFn; + const clickFn = this.clickFn; if (clickFn) { dom.removeEventListener(button, 'click', clickFn, { @@ -107,7 +92,29 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom' this.options = null; this.item = null; - }; + } +} - return RecordingButton; -}); +function getIndicatorIcon(item) { + let status; + + if (item.Type === 'SeriesTimer') { + return 'fiber_smart_record'; + } else if (item.TimerId || item.SeriesTimerId) { + status = item.Status || 'Cancelled'; + } else if (item.Type === 'Timer') { + status = item.Status; + } else { + return 'fiber_manual_record'; + } + + if (item.SeriesTimerId) { + if (status !== 'Cancelled') { + return 'fiber_smart_record'; + } + } + + return 'fiber_manual_record'; +} + +export default RecordingButton; diff --git a/src/components/recordingcreator/recordingcreator.js b/src/components/recordingcreator/recordingcreator.js index ca5c475829..b8001fc35e 100644 --- a/src/components/recordingcreator/recordingcreator.js +++ b/src/components/recordingcreator/recordingcreator.js @@ -1,187 +1,204 @@ -define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'datetime', 'imageLoader', 'recordingFields', 'events', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, datetime, imageLoader, recordingFields, events) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import globalize from 'globalize'; +import layoutManager from 'layoutManager'; +import mediaInfo from 'mediaInfo'; +import connectionManager from 'connectionManager'; +import require from 'require'; +import loading from 'loading'; +import scrollHelper from 'scrollHelper'; +import datetime from 'datetime'; +import imageLoader from 'imageLoader'; +import recordingFields from 'recordingFields'; +import events from 'events'; +import 'emby-checkbox'; +import 'emby-button'; +import 'emby-collapse'; +import 'emby-input'; +import 'paper-icon-button-light'; +import 'css!./../formdialog'; +import 'css!./recordingcreator'; +import 'material-icons'; - var currentDialog; - var closeAction; - var currentRecordingFields; +let currentDialog; +let closeAction; +let currentRecordingFields; - function closeDialog() { - dialogHelper.close(currentDialog); +function closeDialog() { + dialogHelper.close(currentDialog); +} + +function init(context) { + context.querySelector('.btnPlay').addEventListener('click', function () { + closeAction = 'play'; + closeDialog(); + }); + + context.querySelector('.btnCancel').addEventListener('click', function () { + closeAction = null; + closeDialog(); + }); +} + +function getImageUrl(item, apiClient, imageHeight) { + const imageTags = item.ImageTags || {}; + + if (item.PrimaryImageTag) { + imageTags.Primary = item.PrimaryImageTag; } - function init(context) { - context.querySelector('.btnPlay').addEventListener('click', function () { - closeAction = 'play'; - closeDialog(); + if (imageTags.Primary) { + return apiClient.getScaledImageUrl(item.Id, { + type: 'Primary', + maxHeight: imageHeight, + tag: item.ImageTags.Primary }); - - context.querySelector('.btnCancel').addEventListener('click', function () { - closeAction = null; - closeDialog(); + } else if (imageTags.Thumb) { + return apiClient.getScaledImageUrl(item.Id, { + type: 'Thumb', + maxHeight: imageHeight, + tag: item.ImageTags.Thumb }); } - function getImageUrl(item, apiClient, imageHeight) { - var imageTags = item.ImageTags || {}; + return null; +} - if (item.PrimaryImageTag) { - imageTags.Primary = item.PrimaryImageTag; +function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) { + if (!refreshRecordingStateOnly) { + const imgUrl = getImageUrl(program, apiClient, 200); + const imageContainer = context.querySelector('.recordingDialog-imageContainer'); + + if (imgUrl) { + imageContainer.innerHTML = ''; + imageContainer.classList.remove('hide'); + + imageLoader.lazyChildren(imageContainer); + } else { + imageContainer.innerHTML = ''; + imageContainer.classList.add('hide'); } - if (imageTags.Primary) { - return apiClient.getScaledImageUrl(item.Id, { - type: 'Primary', - maxHeight: imageHeight, - tag: item.ImageTags.Primary - }); - } else if (imageTags.Thumb) { - return apiClient.getScaledImageUrl(item.Id, { - type: 'Thumb', - maxHeight: imageHeight, - tag: item.ImageTags.Thumb - }); + context.querySelector('.recordingDialog-itemName').innerHTML = program.Name; + context.querySelector('.formDialogHeaderTitle').innerHTML = program.Name; + context.querySelector('.itemGenres').innerHTML = (program.Genres || []).join(' / '); + context.querySelector('.itemOverview').innerHTML = program.Overview || ''; + + const formDialogFooter = context.querySelector('.formDialogFooter'); + const now = new Date(); + if (now >= datetime.parseISO8601Date(program.StartDate, true) && now < datetime.parseISO8601Date(program.EndDate, true)) { + formDialogFooter.classList.remove('hide'); + } else { + formDialogFooter.classList.add('hide'); } - return null; + context.querySelector('.itemMiscInfoPrimary').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(program); } - function renderRecording(context, defaultTimer, program, apiClient, refreshRecordingStateOnly) { - if (!refreshRecordingStateOnly) { - var imgUrl = getImageUrl(program, apiClient, 200); - var imageContainer = context.querySelector('.recordingDialog-imageContainer'); + context.querySelector('.itemMiscInfoSecondary').innerHTML = mediaInfo.getSecondaryMediaInfoHtml(program, { + }); - if (imgUrl) { - imageContainer.innerHTML = ''; - imageContainer.classList.remove('hide'); + loading.hide(); +} - imageLoader.lazyChildren(imageContainer); - } else { - imageContainer.innerHTML = ''; - imageContainer.classList.add('hide'); - } +function reload(context, programId, serverId, refreshRecordingStateOnly) { + loading.show(); - context.querySelector('.recordingDialog-itemName').innerHTML = program.Name; - context.querySelector('.formDialogHeaderTitle').innerHTML = program.Name; - context.querySelector('.itemGenres').innerHTML = (program.Genres || []).join(' / '); - context.querySelector('.itemOverview').innerHTML = program.Overview || ''; + const apiClient = connectionManager.getApiClient(serverId); - var formDialogFooter = context.querySelector('.formDialogFooter'); - var now = new Date(); - if (now >= datetime.parseISO8601Date(program.StartDate, true) && now < datetime.parseISO8601Date(program.EndDate, true)) { - formDialogFooter.classList.remove('hide'); - } else { - formDialogFooter.classList.add('hide'); - } + const promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId }); + const promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()); - context.querySelector('.itemMiscInfoPrimary').innerHTML = mediaInfo.getPrimaryMediaInfoHtml(program); - } + Promise.all([promise1, promise2]).then(function (responses) { + const defaults = responses[0]; + const program = responses[1]; - context.querySelector('.itemMiscInfoSecondary').innerHTML = mediaInfo.getSecondaryMediaInfoHtml(program, { - }); + renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly); + }); +} - loading.hide(); - } +function executeCloseAction(action, programId, serverId) { + if (action === 'play') { + import('playbackManager').then(({default: playbackManager}) => { + const apiClient = connectionManager.getApiClient(serverId); - function reload(context, programId, serverId, refreshRecordingStateOnly) { - loading.show(); - - var apiClient = connectionManager.getApiClient(serverId); - - var promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId }); - var promise2 = apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()); - - Promise.all([promise1, promise2]).then(function (responses) { - var defaults = responses[0]; - var program = responses[1]; - - renderRecording(context, defaults, program, apiClient, refreshRecordingStateOnly); - }); - } - - function executeCloseAction(action, programId, serverId) { - if (action === 'play') { - require(['playbackManager'], function (playbackManager) { - var apiClient = connectionManager.getApiClient(serverId); - - apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) { - playbackManager.default.play({ - ids: [item.ChannelId], - serverId: serverId - }); - }); - }); - return; - } - } - - function showEditor(itemId, serverId) { - return new Promise(function (resolve, reject) { - closeAction = null; - - loading.show(); - - require(['text!./recordingcreator.template.html'], function (template) { - 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('recordingDialog'); - - var html = ''; - - html += globalize.translateHtml(template, 'core'); - - dlg.innerHTML = html; - - currentDialog = dlg; - - function onRecordingChanged() { - reload(dlg, itemId, serverId, true); - } - - dlg.addEventListener('close', function () { - events.off(currentRecordingFields, 'recordingchanged', onRecordingChanged); - executeCloseAction(closeAction, itemId, serverId); - - if (currentRecordingFields && currentRecordingFields.hasChanged()) { - resolve(); - } else { - reject(); - } - }); - - if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); - } - - init(dlg); - - reload(dlg, itemId, serverId); - - currentRecordingFields = new recordingFields({ - parent: dlg.querySelector('.recordingFields'), - programId: itemId, + apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) { + playbackManager.default.play({ + ids: [item.ChannelId], serverId: serverId }); - - events.on(currentRecordingFields, 'recordingchanged', onRecordingChanged); - - dialogHelper.open(dlg); }); }); + return; } +} - return { - show: showEditor - }; -}); +function showEditor(itemId, serverId) { + return new Promise(function (resolve, reject) { + closeAction = null; + + loading.show(); + + import('text!./recordingcreator.template.html').then(({default: template}) => { + const dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + + const dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); + + let html = ''; + + html += globalize.translateHtml(template, 'core'); + + dlg.innerHTML = html; + + currentDialog = dlg; + + function onRecordingChanged() { + reload(dlg, itemId, serverId, true); + } + + dlg.addEventListener('close', function () { + events.off(currentRecordingFields, 'recordingchanged', onRecordingChanged); + executeCloseAction(closeAction, itemId, serverId); + + if (currentRecordingFields && currentRecordingFields.hasChanged()) { + resolve(); + } else { + reject(); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + init(dlg); + + reload(dlg, itemId, serverId); + + currentRecordingFields = new recordingFields({ + parent: dlg.querySelector('.recordingFields'), + programId: itemId, + serverId: serverId + }); + + events.on(currentRecordingFields, 'recordingchanged', onRecordingChanged); + + dialogHelper.open(dlg); + }); + }); +} + +export default { + show: showEditor +}; From 3a35a9c5f2ee664fd9409de1d47acdaac54be1d2 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 11:55:00 +0100 Subject: [PATCH 054/155] lint --- src/controllers/itemDetails/index.js | 2 -- src/controllers/music/musicrecommended.js | 2 -- src/scripts/libraryMenu.js | 3 --- 3 files changed, 7 deletions(-) diff --git a/src/controllers/itemDetails/index.js b/src/controllers/itemDetails/index.js index 15c437d684..df2855d69a 100644 --- a/src/controllers/itemDetails/index.js +++ b/src/controllers/itemDetails/index.js @@ -29,8 +29,6 @@ import 'emby-select'; /* eslint-disable indent */ - libraryMenu = libraryMenu.default || libraryMenu; - function getPromise(apiClient, params) { const id = params.id; diff --git a/src/controllers/music/musicrecommended.js b/src/controllers/music/musicrecommended.js index ad031e42b4..db7dac9547 100644 --- a/src/controllers/music/musicrecommended.js +++ b/src/controllers/music/musicrecommended.js @@ -17,8 +17,6 @@ import 'flexStyles'; /* eslint-disable indent */ - libraryMenu = LibraryMenu.default || libraryMenu; - function itemsPerRow() { const screenWidth = dom.getWindowSize().innerWidth; diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 8b5fce14d3..0780916a7c 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -19,9 +19,6 @@ import 'flexStyles'; /* eslint-disable indent */ - playbackManager = playbackManager.default || playbackManager; - browser = browser.default || browser; - function renderHeader() { let html = ''; html += '
'; From 70204584274b831e6ac9c5187c258cf355c1272a Mon Sep 17 00:00:00 2001 From: Matjaz Zavski Date: Tue, 4 Aug 2020 12:59:21 +0200 Subject: [PATCH 055/155] Update src/controllers/livetv/livetvchannels.js Co-authored-by: Cameron --- src/controllers/livetv/livetvchannels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index 8af6411296..14d7bcfdf4 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -3,7 +3,7 @@ import imageLoader from 'imageLoader'; import libraryBrowser from 'libraryBrowser'; import loading from 'loading'; import events from 'events'; -import userSettings from 'userSettings'; +import * as userSettings from 'userSettings'; import 'emby-itemscontainer'; export default function (view, params, tabContent) { From e3fe8a1964acfd7acdb154ffbe4b10d80ccf4f3c Mon Sep 17 00:00:00 2001 From: Matjaz Zavski Date: Tue, 4 Aug 2020 12:59:28 +0200 Subject: [PATCH 056/155] Update src/controllers/livetv/livetvchannels.js Co-authored-by: Cameron --- src/controllers/livetv/livetvchannels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index 14d7bcfdf4..7cdddda716 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -115,7 +115,7 @@ export default function (view, params, tabContent) { loading.hide(); isLoading = false; - require(['autoFocuser'], function (autoFocuser) { + import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); }); From a54d8338a8b30dc0f6eec1530858ddf124d97822 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Tue, 4 Aug 2020 09:43:10 +0000 Subject: [PATCH 057/155] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sl/ --- src/strings/sl-si.json | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index a8f3bd020c..066254a3dd 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -785,7 +785,7 @@ "MessagePlayAccessRestricted": "Predvajanje te vsebine je trenutno omejeno. Za več informacij se obrnite na skrbnika strežnika.", "NoSubtitlesHelp": "Podnapisi se privzeto ne bodo naložili. Med predvajanjem jih lahko še vedno ročno vklopite.", "MessagePluginConfigurationRequiresLocalAccess": "Za nastavitev tega dodatka se prosimo prijavite neposredno na vaš lokalni strežnik.", - "OptionAllowMediaPlaybackTranscodingHelp": "Omejitev dostopa do prekodiranja lahko povzroči napake pri predvajanju v Jellyfin aplikacijah zaradi nepodprtih formatov predstavnosti.", + "OptionAllowMediaPlaybackTranscodingHelp": "Omejitev dostopa do prekodiranja lahko povzroči napake pri predvajanju v odjemalcih zaradi nepodprtih formatov predstavnosti.", "OptionAllowVideoPlaybackRemuxing": "Dovoli predvajanje videoposnetkov, ki zahtevajo pretvarjanje brez prekodiranja", "ViewPlaybackInfo": "Oglejte si informacije o predvajanju", "WizardCompleted": "To je vse kar potrebujemo za zdaj. Jellyfin je začel zbirati informacije o vaši knjižnici predstavnosti. Oglejte si nekaj naših aplikacij in nato kliknite Zaključi za ogled Nadzorne plošče.", @@ -886,7 +886,7 @@ "MediaInfoExternal": "Zunanji", "Refresh": "Osveži", "MetadataManager": "Upravitelj metapodatkov", - "MetadataSettingChangeHelp": "Spreminjanje nastavitev metapodatkov bo vplivalo na nove vsebine. Za osvežitev obstoječih vsebin, odprite okno s podrobnostmi in kliknite gumb Osveži, ali množično osvežite metapodatke z upraviteljem metapodatkov.", + "MetadataSettingChangeHelp": "Spreminjanje nastavitev metapodatkov bo vplivalo na novo dodane vsebine. Za osvežitev obstoječih vsebin, odprite okno s podrobnostmi in kliknite gumb Osveži, ali množično osvežite metapodatke z upraviteljem metapodatkov.", "RefreshMetadata": "Osveži metapodatke", "RefreshQueued": "Osvežitev v čakalni vrsti.", "Shuffle": "Premešaj", @@ -940,7 +940,7 @@ "MessagePleaseWait": "Prosimo, počakajte. To lahko traja nekaj minut.", "MessagePleaseEnsureInternetMetadata": "Prosimo poskrbite, da je prenašanje spletnih metapodatkov omogočeno.", "MessageNothingHere": "Tu ni nič.", - "MessageNoTrailersFound": "Napovednikov ni bilo mogoče najti. Namestite kanal napovednikov in izboljšajte svojo filmsko izkušnjo s knjižnico spletnih napovednikov.", + "MessageNoTrailersFound": "Namestite kanal napovednikov in izboljšajte svojo knjižnico z dodajanjem spletnih napovednikov.", "MessageNoServersAvailable": "Z samodejnim odkrivanjem strežnikov ni bilo mogoče najti nobenega strežnika.", "MessageNoPluginsInstalled": "Nameščenih nimate nobenih dodatkov.", "MessageNoAvailablePlugins": "Dodatki niso na voljo.", @@ -981,7 +981,7 @@ "MediaInfoResolution": "Ločljivost", "MediaInfoRefFrames": "Ref sličice", "MediaInfoProfile": "Profil", - "MediaInfoPixelFormat": "Format pik", + "MediaInfoPixelFormat": "Format pikslov", "MediaInfoPath": "Pot", "MediaInfoLevel": "Stopnja", "MediaInfoInterlaced": "Prepleteno", @@ -1377,5 +1377,30 @@ "TabNotifications": "Obvestila", "TabNfoSettings": "Nastavitve NFO", "TabMusicVideos": "Videospoti", - "TabMovies": "Filmi" + "TabMovies": "Filmi", + "OptionForceRemoteSourceTranscoding": "Vsili prekodiranje oddaljenih virov predstavnosti (na primer TV v živo)", + "Off": "Izključeno", + "Normal": "Normalno", + "NoSubtitleSearchResultsFound": "Ni zadetkov.", + "MessageNoNextUpItems": "Ničesar ni bilo mogoče najti. Začnite gledati vaše serije!", + "NoCreatedLibraries": "Izgleda da niste dodali še nobene knjižnice. {0}Želite dodati eno zdaj?{1}", + "NextTrack": "Preskoči na naslednjo", + "TvLibraryHelp": "Preglejte {0}napotke za poimenovanje serij{1}.", + "MusicLibraryHelp": "Preglejte {0}napotke za poimenovanje glasbe{1}.", + "MovieLibraryHelp": "Preglejte {0}napotke za poimenovanje filmov{1}.", + "MoreMediaInfo": "Informacije o predstavnosti", + "Mobile": "Telefon", + "MessageSyncPlayErrorMedia": "Aktivacija SyncPlay ni uspela! Napaka predstavnosti.", + "MessageSyncPlayErrorMissingSession": "Aktivacija SyncPlay ni uspela! Manjka seja.", + "MessageSyncPlayErrorNoActivePlayer": "Najdeno ni bilo nobeno aktivno predvajanje. SyncPlay je bil onemogočen.", + "MessageSyncPlayErrorAccessingGroups": "Pri dostopu do seznama skupin je prišlo do napake.", + "MessageSyncPlayJoinGroupDenied": "Za uporabo SyncPlay je potrebno dovoljenje.", + "MessageSyncPlayGroupDoesNotExist": "Pridružitev skupini ni bila uspešna, ker ta ne obstaja.", + "MessageSyncPlayNoGroupsAvailable": "Na voljo ni nobene skupine. Začnite gledati nekaj.", + "MessageSyncPlayDisabled": "SyncPlay onemogočen.", + "MessageSyncPlayEnabled": "SyncPlay omogočen.", + "MessageUnsetContentHelp": "Vsebine bodo prikazane kot preproste mape. Za najboljše rezultate uporabite upravitelj metapodatkov in nastavite vrsto vsebin podmap.", + "MessageLeaveEmptyToInherit": "Pustite prazno za podedovanje nastavitev od nadrejenega elementa ali globalne privzete vrednosti.", + "LabelXDlnaDocHelp": "Določa vsebino X_DLNADOC elementa v urn:schemas-dlna-org:device-1-0 namespace.", + "LabelXDlnaCapHelp": "Določa vsebino X_DLNACAP elementa v urn:schemas-dlna-org:device-1-0 namespace." } From 39a5a76be1ab4c2110bfba0979bad93d1756a4a8 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Tue, 4 Aug 2020 11:15:34 +0000 Subject: [PATCH 058/155] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sl/ --- src/strings/sl-si.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index 066254a3dd..2f08ad8b40 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -910,7 +910,7 @@ "LabelPasswordResetProvider": "Ponudnik ponastavitve gesla:", "LabelPasswordConfirm": "Potrditev gesla:", "LabelPassword": "Geslo:", - "OptionDisableUserHelp": "Če onemogočite strežnik ne bo dovolil povezav od tega uporabnika. Obstoječe povezave bodo prekinjene.", + "OptionDisableUserHelp": "Strežnik ne bo dovolil nobenih povezav od tega uporabnika. Obstoječe povezave bodo prekinjene.", "OptionDescending": "Padajoče", "OptionDatePlayed": "Datum predvajanja", "OptionDateAddedImportTime": "Uporabi datum dodajanja v knjižnico", @@ -929,7 +929,7 @@ "OptionBlockMovies": "Filmi", "OptionBlockBooks": "Knjige", "OptionBanner": "Pasica", - "OptionAutomaticallyGroupSeriesHelp": "Če je omogočeno, bodo serije, ki so razdeljene v več mapah znotraj knjižnice samodejno združene v eno serijo.", + "OptionAutomaticallyGroupSeriesHelp": "Serije, ki so razdeljene v več mapah znotraj knjižnice bodo samodejno združene v eno serijo.", "OptionAutomaticallyGroupSeries": "Samodejno združi serije, ki so razdeljene po več mapah", "OptionAscending": "Naraščajoče", "OptionArtist": "Izvajalec", @@ -1402,5 +1402,6 @@ "MessageUnsetContentHelp": "Vsebine bodo prikazane kot preproste mape. Za najboljše rezultate uporabite upravitelj metapodatkov in nastavite vrsto vsebin podmap.", "MessageLeaveEmptyToInherit": "Pustite prazno za podedovanje nastavitev od nadrejenega elementa ali globalne privzete vrednosti.", "LabelXDlnaDocHelp": "Določa vsebino X_DLNADOC elementa v urn:schemas-dlna-org:device-1-0 namespace.", - "LabelXDlnaCapHelp": "Določa vsebino X_DLNACAP elementa v urn:schemas-dlna-org:device-1-0 namespace." + "LabelXDlnaCapHelp": "Določa vsebino X_DLNACAP elementa v urn:schemas-dlna-org:device-1-0 namespace.", + "OptionDislikes": "Ni mi všeč" } From 39a003742716d7dcfe7e27e232fea91743f722d3 Mon Sep 17 00:00:00 2001 From: Matjaz Zavski Date: Tue, 4 Aug 2020 14:12:43 +0200 Subject: [PATCH 059/155] Update src/controllers/livetv/livetvchannels.js Co-authored-by: Cameron --- src/controllers/livetv/livetvchannels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index 7cdddda716..d34fe58132 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -115,7 +115,7 @@ export default function (view, params, tabContent) { loading.hide(); isLoading = false; - import('autoFocuser').then(({default: autoFocuser}) => { + import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); }); From 73bb781c6a2a4028dc1bd92c497dbe0ffc0ccb8c Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 13:47:40 +0100 Subject: [PATCH 060/155] Migration of layoutManager and itemsrefresher to ES6 module --- package.json | 2 + src/components/itemsrefresher.js | 324 +++++++++++++++---------------- src/components/layoutManager.js | 51 +++-- 3 files changed, 189 insertions(+), 188 deletions(-) diff --git a/package.json b/package.json index 5ad12d2011..97784fa2e2 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,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", diff --git a/src/components/itemsrefresher.js b/src/components/itemsrefresher.js index 74b08db07f..3883e6e490 100644 --- a/src/components/itemsrefresher.js +++ b/src/components/itemsrefresher.js @@ -1,130 +1,130 @@ -define(['playbackManager', 'serverNotifications', 'events'], function (playbackManager, serverNotifications, events) { - 'use strict'; +import playbackManager from 'playbackManager'; +import serverNotifications from 'serverNotifications'; +import events from 'events'; - playbackManager = playbackManager.default || playbackManager; +function onUserDataChanged(e, apiClient, userData) { + const instance = this; - function onUserDataChanged(e, apiClient, userData) { - var instance = this; - - var eventsToMonitor = getEventsToMonitor(instance); - - // TODO: Check user data change reason? - if (eventsToMonitor.indexOf('markfavorite') !== -1) { - instance.notifyRefreshNeeded(); - } else if (eventsToMonitor.indexOf('markplayed') !== -1) { - instance.notifyRefreshNeeded(); - } - } - - function getEventsToMonitor(instance) { - var options = instance.options; - var monitor = options ? options.monitorEvents : null; - if (monitor) { - return monitor.split(','); - } - - return []; - } - - function onTimerCreated(e, apiClient, data) { - var instance = this; - - if (getEventsToMonitor(instance).indexOf('timers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onSeriesTimerCreated(e, apiClient, data) { - var instance = this; - if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onTimerCancelled(e, apiClient, data) { - var instance = this; - - if (getEventsToMonitor(instance).indexOf('timers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onSeriesTimerCancelled(e, apiClient, data) { - var instance = this; - if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { - instance.notifyRefreshNeeded(); - return; - } - } - - function onLibraryChanged(e, apiClient, data) { - var instance = this; - var eventsToMonitor = getEventsToMonitor(instance); - if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { - // yes this is an assumption - return; - } - - var itemsAdded = data.ItemsAdded || []; - var itemsRemoved = data.ItemsRemoved || []; - if (!itemsAdded.length && !itemsRemoved.length) { - return; - } - - var options = instance.options || {}; - var parentId = options.parentId; - if (parentId) { - var foldersAddedTo = data.FoldersAddedTo || []; - var foldersRemovedFrom = data.FoldersRemovedFrom || []; - var collectionFolders = data.CollectionFolders || []; - - if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { - return; - } - } + const eventsToMonitor = getEventsToMonitor(instance); + // TODO: Check user data change reason? + if (eventsToMonitor.indexOf('markfavorite') !== -1) { + instance.notifyRefreshNeeded(); + } else if (eventsToMonitor.indexOf('markplayed') !== -1) { instance.notifyRefreshNeeded(); } +} - function onPlaybackStopped(e, stopInfo) { - var instance = this; +function getEventsToMonitor(instance) { + const options = instance.options; + const monitor = options ? options.monitorEvents : null; + if (monitor) { + return monitor.split(','); + } - var state = stopInfo.state; + return []; +} - var eventsToMonitor = getEventsToMonitor(instance); - if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { - if (eventsToMonitor.indexOf('videoplayback') !== -1) { - instance.notifyRefreshNeeded(true); - return; - } - } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { - if (eventsToMonitor.indexOf('audioplayback') !== -1) { - instance.notifyRefreshNeeded(true); - return; - } +function onTimerCreated(e, apiClient, data) { + const instance = this; + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onSeriesTimerCreated(e, apiClient, data) { + const instance = this; + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onTimerCancelled(e, apiClient, data) { + const instance = this; + + if (getEventsToMonitor(instance).indexOf('timers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onSeriesTimerCancelled(e, apiClient, data) { + const instance = this; + if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) { + instance.notifyRefreshNeeded(); + return; + } +} + +function onLibraryChanged(e, apiClient, data) { + const instance = this; + const eventsToMonitor = getEventsToMonitor(instance); + if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) { + // yes this is an assumption + return; + } + + const itemsAdded = data.ItemsAdded || []; + const itemsRemoved = data.ItemsRemoved || []; + if (!itemsAdded.length && !itemsRemoved.length) { + return; + } + + const options = instance.options || {}; + const parentId = options.parentId; + if (parentId) { + const foldersAddedTo = data.FoldersAddedTo || []; + const foldersRemovedFrom = data.FoldersRemovedFrom || []; + const collectionFolders = data.CollectionFolders || []; + + if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) { + return; } } - function addNotificationEvent(instance, name, handler, owner) { - var localHandler = handler.bind(instance); + instance.notifyRefreshNeeded(); +} + +function onPlaybackStopped(e, stopInfo) { + const instance = this; + + const state = stopInfo.state; + + const eventsToMonitor = getEventsToMonitor(instance); + if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') { + if (eventsToMonitor.indexOf('videoplayback') !== -1) { + instance.notifyRefreshNeeded(true); + return; + } + } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') { + if (eventsToMonitor.indexOf('audioplayback') !== -1) { + instance.notifyRefreshNeeded(true); + return; + } + } +} + +function addNotificationEvent(instance, name, handler, owner) { + const localHandler = handler.bind(instance); + owner = owner || serverNotifications; + events.on(owner, name, localHandler); + instance['event_' + name] = localHandler; +} + +function removeNotificationEvent(instance, name, owner) { + const handler = instance['event_' + name]; + if (handler) { owner = owner || serverNotifications; - events.on(owner, name, localHandler); - instance['event_' + name] = localHandler; + events.off(owner, name, handler); + instance['event_' + name] = null; } +} - function removeNotificationEvent(instance, name, owner) { - var handler = instance['event_' + name]; - if (handler) { - owner = owner || serverNotifications; - events.off(owner, name, handler); - instance['event_' + name] = null; - } - } - - function ItemsRefresher(options) { +class ItemsRefresher { + constructor(options) { this.options = options || {}; addNotificationEvent(this, 'UserDataChanged', onUserDataChanged); @@ -136,18 +136,18 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager); } - ItemsRefresher.prototype.pause = function () { + pause() { clearRefreshInterval(this, true); this.paused = true; - }; + } - ItemsRefresher.prototype.resume = function (options) { + resume(options) { this.paused = false; - var refreshIntervalEndTime = this.refreshIntervalEndTime; + const refreshIntervalEndTime = this.refreshIntervalEndTime; if (refreshIntervalEndTime) { - var remainingMs = refreshIntervalEndTime - new Date().getTime(); + const remainingMs = refreshIntervalEndTime - new Date().getTime(); if (remainingMs > 0 && !this.needsRefresh) { resetRefreshInterval(this, remainingMs); } else { @@ -161,9 +161,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM } return Promise.resolve(); - }; + } - ItemsRefresher.prototype.refreshItems = function () { + refreshItems() { if (!this.fetchData) { return Promise.resolve(); } @@ -176,15 +176,15 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM this.needsRefresh = false; return this.fetchData().then(onDataFetched.bind(this)); - }; + } - ItemsRefresher.prototype.notifyRefreshNeeded = function (isInForeground) { + notifyRefreshNeeded(isInForeground) { if (this.paused) { this.needsRefresh = true; return; } - var timeout = this.refreshTimeout; + const timeout = this.refreshTimeout; if (timeout) { clearTimeout(timeout); } @@ -194,44 +194,9 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM } else { this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000); } - }; - - function clearRefreshInterval(instance, isPausing) { - if (instance.refreshInterval) { - clearInterval(instance.refreshInterval); - instance.refreshInterval = null; - - if (!isPausing) { - instance.refreshIntervalEndTime = null; - } - } } - function resetRefreshInterval(instance, intervalMs) { - clearRefreshInterval(instance); - - if (!intervalMs) { - var options = instance.options; - if (options) { - intervalMs = options.refreshIntervalMs; - } - } - - if (intervalMs) { - instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs); - instance.refreshIntervalEndTime = new Date().getTime() + intervalMs; - } - } - - function onDataFetched(result) { - resetRefreshInterval(this); - - if (this.afterRefresh) { - this.afterRefresh(result); - } - } - - ItemsRefresher.prototype.destroy = function () { + destroy() { clearRefreshInterval(this); removeNotificationEvent(this, 'UserDataChanged'); @@ -244,7 +209,42 @@ define(['playbackManager', 'serverNotifications', 'events'], function (playbackM this.fetchData = null; this.options = null; - }; + } +} - return ItemsRefresher; -}); +function clearRefreshInterval(instance, isPausing) { + if (instance.refreshInterval) { + clearInterval(instance.refreshInterval); + instance.refreshInterval = null; + + if (!isPausing) { + instance.refreshIntervalEndTime = null; + } + } +} + +function resetRefreshInterval(instance, intervalMs) { + clearRefreshInterval(instance); + + if (!intervalMs) { + const options = instance.options; + if (options) { + intervalMs = options.refreshIntervalMs; + } + } + + if (intervalMs) { + instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs); + instance.refreshIntervalEndTime = new Date().getTime() + intervalMs; + } +} + +function onDataFetched(result) { + resetRefreshInterval(this); + + if (this.afterRefresh) { + this.afterRefresh(result); + } +} + +export default ItemsRefresher; diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 85d78f8ff4..d104632573 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -1,23 +1,22 @@ -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); - } +class LayoutManager { + constructor() { } - function LayoutManager() { - - } - - LayoutManager.prototype.setLayout = function (layout, save) { + setLayout(layout, save) { if (!layout || layout === 'auto') { this.autoLayout(); @@ -35,13 +34,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 +49,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(); From aaad59cca6044f90b02ce53118cd575e42fef604 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 13:47:47 +0100 Subject: [PATCH 061/155] addition of defaults --- src/components/filtermenu/filtermenu.js | 2 ++ src/components/guide/guide-settings.js | 2 ++ src/components/guide/guide.js | 1 + src/components/recordingcreator/recordingcreator.js | 2 ++ src/components/recordingcreator/recordingeditor.js | 1 + src/components/recordingcreator/seriesrecordingeditor.js | 1 + src/components/remotecontrol/remotecontrol.js | 1 + src/components/slideshow/slideshow.js | 1 + src/components/sortmenu/sortmenu.js | 2 ++ src/components/subtitleeditor/subtitleeditor.js | 1 + src/components/subtitlesync/subtitlesync.js | 1 + src/components/tabbedview/tabbedview.js | 2 ++ src/components/tunerPicker.js | 1 + src/components/upnextdialog/upnextdialog.js | 1 + src/components/viewManager/viewManager.js | 2 ++ src/components/viewSettings/viewSettings.js | 2 ++ src/controllers/list.js | 1 + src/controllers/livetv/livetvrecordings.js | 1 + src/controllers/livetv/livetvschedule.js | 1 + src/controllers/livetv/livetvsuggested.js | 1 + src/controllers/movies/moviegenres.js | 1 + src/controllers/movies/movies.js | 1 + src/controllers/movies/moviesrecommended.js | 1 + src/controllers/movies/movietrailers.js | 1 + src/libraries/scroller.js | 1 + src/scripts/libraryMenu.js | 1 + src/scripts/livetvcomponents.js | 2 ++ src/scripts/site.js | 1 + 28 files changed, 36 insertions(+) diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 20399fb52d..03a1706fb5 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -1,6 +1,8 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', 'inputManager', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, appHost, inputManager, layoutManager, connectionManager, appRouter, globalize, userSettings) { 'use strict'; + 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 c3ba49f283..437dd5fcec 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -1,6 +1,8 @@ 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; + function saveCategories(context, options) { var categories = []; diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 61caa9188f..6057375ec2 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; function showViewSettings(instance) { require(['guide-settings-dialog'], function (guideSettingsDialog) { diff --git a/src/components/recordingcreator/recordingcreator.js b/src/components/recordingcreator/recordingcreator.js index ca5c475829..fb452fa8a4 100644 --- a/src/components/recordingcreator/recordingcreator.js +++ b/src/components/recordingcreator/recordingcreator.js @@ -1,6 +1,8 @@ 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; + var currentDialog; var closeAction; var currentRecordingFields; diff --git a/src/components/recordingcreator/recordingeditor.js b/src/components/recordingcreator/recordingeditor.js index 2086129a9e..2ac8c826e1 100644 --- a/src/components/recordingcreator/recordingeditor.js +++ b/src/components/recordingcreator/recordingeditor.js @@ -2,6 +2,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c 'use strict'; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; var currentDialog; var recordingDeleted = false; diff --git a/src/components/recordingcreator/seriesrecordingeditor.js b/src/components/recordingcreator/seriesrecordingeditor.js index b115e273e6..b5d8eee82b 100644 --- a/src/components/recordingcreator/seriesrecordingeditor.js +++ b/src/components/recordingcreator/seriesrecordingeditor.js @@ -2,6 +2,7 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c 'use strict'; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; var currentDialog; var recordingUpdated = 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 60c458e234..e29a3c8fb9 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; /** * Name of transition event. diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index f62e5bb3a4..56f33001e7 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -1,6 +1,8 @@ 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; + function onSubmit(e) { e.preventDefault(); return false; diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 7df24b5da8..093bbe7a4d 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -2,6 +2,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'use strict'; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; var currentItem; var hasChanges; diff --git a/src/components/subtitlesync/subtitlesync.js b/src/components/subtitlesync/subtitlesync.js index 203d88535f..4433c8d958 100644 --- a/src/components/subtitlesync/subtitlesync.js +++ b/src/components/subtitlesync/subtitlesync.js @@ -2,6 +2,7 @@ define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', 'use strict'; playbackManager = playbackManager.default || playbackManager; + layoutManager = layoutManager.default || layoutManager; var player; var subtitleSyncSlider; diff --git a/src/components/tabbedview/tabbedview.js b/src/components/tabbedview/tabbedview.js index 8bd3afd372..c8c0f232be 100644 --- a/src/components/tabbedview/tabbedview.js +++ b/src/components/tabbedview/tabbedview.js @@ -1,6 +1,8 @@ define(['backdrop', 'mainTabsManager', 'layoutManager', 'emby-tabs'], function (backdrop, mainTabsManager, layoutManager) { 'use strict'; + layoutManager = layoutManager.default || layoutManager; + function onViewDestroy(e) { var tabControllers = this.tabControllers; diff --git a/src/components/tunerPicker.js b/src/components/tunerPicker.js index 5bc9386053..6391a84e3a 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; var enableFocusTransform = !browser.slow && !browser.edge; diff --git a/src/components/upnextdialog/upnextdialog.js b/src/components/upnextdialog/upnextdialog.js index 5eb2a2ffcc..fdf7c056f5 100644 --- a/src/components/upnextdialog/upnextdialog.js +++ b/src/components/upnextdialog/upnextdialog.js @@ -2,6 +2,7 @@ define(['dom', 'playbackManager', 'connectionManager', 'events', 'mediaInfo', 'l 'use strict'; playbackManager = playbackManager.default || playbackManager; + layoutManager = layoutManager.default || layoutManager; var transitionEndEventName = dom.whichTransitionEvent(); diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index 058ba4ebb2..8c9840fec3 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -1,6 +1,8 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], function (viewContainer, focusManager, queryString, layoutManager) { 'use strict'; + layoutManager = layoutManager.default || layoutManager; + var currentView; var dispatchPageEvents; diff --git a/src/components/viewSettings/viewSettings.js b/src/components/viewSettings/viewSettings.js index 67abc25a9c..9356f991fc 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/list.js b/src/controllers/list.js index 68e81f05dd..3cfa7ce281 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -3,6 +3,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' playbackManager = playbackManager.default || playbackManager; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; function getInitialLiveTvQuery(instance, params) { var query = { diff --git a/src/controllers/livetv/livetvrecordings.js b/src/controllers/livetv/livetvrecordings.js index b0259bc89b..ab92770336 100644 --- a/src/controllers/livetv/livetvrecordings.js +++ b/src/controllers/livetv/livetvrecordings.js @@ -2,6 +2,7 @@ define(['layoutManager', 'loading', 'cardBuilder', 'apphost', 'imageLoader', 'sc 'use strict'; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; function renderRecordings(elem, recordings, cardOptions, scrollX) { if (!elem) { diff --git a/src/controllers/livetv/livetvschedule.js b/src/controllers/livetv/livetvschedule.js index 4427bef5d1..a7c3f56279 100644 --- a/src/controllers/livetv/livetvschedule.js +++ b/src/controllers/livetv/livetvschedule.js @@ -2,6 +2,7 @@ define(['layoutManager', 'cardBuilder', 'apphost', 'imageLoader', 'loading', 'sc 'use strict'; loading = loading.default || loading; + layoutManager = layoutManager.default || layoutManager; function enableScrollX() { return !layoutManager.desktop; diff --git a/src/controllers/livetv/livetvsuggested.js b/src/controllers/livetv/livetvsuggested.js index 79d9aac6e9..b3adc4fa85 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/controllers/movies/moviegenres.js b/src/controllers/movies/moviegenres.js index 82ab4d4d4b..716b9b94cd 100644 --- a/src/controllers/movies/moviegenres.js +++ b/src/controllers/movies/moviegenres.js @@ -3,6 +3,7 @@ define(['layoutManager', 'loading', 'libraryBrowser', 'cardBuilder', 'lazyLoader loading = loading.default || loading; libraryBrowser = libraryBrowser.default || libraryBrowser; + layoutManager = layoutManager.default || layoutManager; return function (view, params, tabContent) { function getPageData() { diff --git a/src/controllers/movies/movies.js b/src/controllers/movies/movies.js index ade9dc4b89..48f9fa8f5d 100644 --- a/src/controllers/movies/movies.js +++ b/src/controllers/movies/movies.js @@ -3,6 +3,7 @@ define(['loading', 'layoutManager', 'userSettings', 'events', 'libraryBrowser', loading = loading.default || loading; libraryBrowser = libraryBrowser.default || libraryBrowser; + layoutManager = layoutManager.default || layoutManager; return function (view, params, tabContent, options) { function onViewStyleChange() { diff --git a/src/controllers/movies/moviesrecommended.js b/src/controllers/movies/moviesrecommended.js index a633d654cd..1d50f85919 100644 --- a/src/controllers/movies/moviesrecommended.js +++ b/src/controllers/movies/moviesrecommended.js @@ -2,6 +2,7 @@ define(['events', 'layoutManager', 'inputManager', 'userSettings', 'libraryMenu' 'use strict'; playbackManager = playbackManager.default || playbackManager; + layoutManager = layoutManager.default || layoutManager; function enableScrollX() { return !layoutManager.desktop; diff --git a/src/controllers/movies/movietrailers.js b/src/controllers/movies/movietrailers.js index 8d9fe8d090..7dc3b577da 100644 --- a/src/controllers/movies/movietrailers.js +++ b/src/controllers/movies/movietrailers.js @@ -3,6 +3,7 @@ define(['layoutManager', 'loading', 'events', 'libraryBrowser', 'imageLoader', ' loading = loading.default || loading; libraryBrowser = libraryBrowser.default || libraryBrowser; + layoutManager = layoutManager.default || layoutManager; return function (view, params, tabContent) { function getPageData(context) { diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index c0cb3e557c..9b441da012 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -2,6 +2,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc 'use strict'; browser = browser.default || browser; + layoutManager = layoutManager.default || layoutManager; /** * Return type of the value. diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index bbe01276ba..773804f84b 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -3,6 +3,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' playbackManager = playbackManager.default || playbackManager; browser = browser.default || browser; + layoutManager = layoutManager.default || layoutManager; function renderHeader() { var html = ''; 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 87402635f5..ab44d03834 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(); } From ce8484e8f0c37678b0cf1dbd1d84c8e1dc85fd2b Mon Sep 17 00:00:00 2001 From: SaddFox Date: Tue, 4 Aug 2020 12:55:59 +0000 Subject: [PATCH 062/155] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sl/ --- src/strings/sl-si.json | 52 +++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index 2f08ad8b40..423ee7797c 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -448,7 +448,7 @@ "FastForward": "Hitro naprej", "FFmpegSavePathNotFound": "Nismo mogli locirati FFmpeg na navedeni poti. FFprobe je prav tako zahtevan in mora biti v isti mapi. Ti komponenti sta običajno združeni skupaj v istem prenosu. Preverite pot in poskusite znova.", "Extras": "Dodatki", - "ExtractChapterImagesHelp": "Izločanje slik poglavij omogoča odjemalcem prikaz grafičnih menijev za izbor scene. Ta proces je lahko počasen, procesorsko zahteven in lahko potrebuje več gigabajtov prostora na disku. Teče ob odkritju novih videov in kot načrtovana naloga ponoči. Urnik izvajanja lahko nastavite v nastavitvah načrtovanih nalog. Zaganjanje tega procesa med urami visoke obremenitve ni priporočeno.", + "ExtractChapterImagesHelp": "Ekstrakcija slik poglavij omogoča odjemalcem prikaz grafičnih menijev za izbor scene. Ta proces je lahko počasen, procesorsko zahteven in lahko potrebuje več gigabajtov prostora na disku. Teče ob odkritju novih videov in kot načrtovana naloga ponoči. Urnik izvajanja lahko nastavite v nastavitvah načrtovanih nalog. Zaganjanje tega procesa med urami visoke obremenitve ni priporočeno.", "ExtraLarge": "Zelo veliko", "ExitFullscreen": "Izhod in celozaslonskega načina", "ErrorSavingTvProvider": "Prišlo je do težave pri shranjevanju TV ponudnika. Preverite ali je dostopen in poskusite znova.", @@ -723,16 +723,16 @@ "LabelMonitorUsers": "Spremljaj aktivnost iz:", "LabelMovieCategories": "Kategorije filmov:", "LabelMoviePrefix": "Predpona filma:", - "LabelMovieRecordingPath": "Pot za snemanje filmov (neobvezno):", + "LabelMovieRecordingPath": "Pot za snemanje filmov:", "LabelMusicStreamingTranscodingBitrate": "Bitna hitrost pretvarjanja glasbe:", - "LabelMusicStreamingTranscodingBitrateHelp": "Določi največjo bitno hitrost pretakanja glasbe.", + "LabelMusicStreamingTranscodingBitrateHelp": "Določite največjo bitno hitrost pretakanja glasbe.", "LabelName": "Ime:", "LabelFriendlyName": "Uporabniku prijazno ime:", "LabelKodiMetadataEnablePathSubstitutionHelp": "Omogoči zamenjavo poti za poti slik glede na nastavitve zamenjave poti strežnika.", "LabelKodiMetadataSaveImagePaths": "Shrani poti slik znotraj nfo datotek", "LabelMetadataDownloadersHelp": "Omogoči in uredi želene vire metapodatkov po prioriteti. Viri z nižjo prioriteto bodo uporabljeni zgolj za dopolnjevanje manjkajočih informacij.", "LabelBaseUrlHelp": "Dodjte podnaslov po meri na konec URL-ja strežnika. Na primer: http://example.com/<baseurl>", - "LabelExtractChaptersDuringLibraryScanHelp": "Ustvari slike poglavij med uvozom videov pri preiskovanju knjižnjice. Sicer bodo ustvarjene med načrtovanim opravilom, kar omogoča hitrejše preiskovanje knjižnjice.", + "LabelExtractChaptersDuringLibraryScanHelp": "Ustvari slike poglavij med uvozom videov pri preiskovanju knjižnjice. Sicer bodo ustvarjene med načrtovanim opravilom ekstrakcije slik, kar omogoča hitrejše preiskovanje knjižnjice.", "LabelForgotPasswordUsernameHelp": "Vpišite svoje uporabniško ime, v kolikor se ga spomnite.", "LabelInNetworkSignInWithEasyPasswordHelp": "Uporabi enostavno PIN kodo za prijavo v naprave znotraj lokalnega omrežja. Vaše geslo bo potrebno zgolj za prijave zunaj domačega omrežja. Če pustite prazno, za prijavo v domačem omrežju omrežju ne boste potrebovali gesla.", "LabelMaxStreamingBitrate": "Največja kvaliteta pretakanja:", @@ -820,7 +820,7 @@ "OptionReportByteRangeSeekingWhenTranscoding": "Sporoči, da strežnik podpira iskanje po bajtih pri prekodiranju", "Quality": "Kvaliteta", "PlaceFavoriteChannelsAtBeginning": "Postavi priljubljene kanale na začetek", - "LabelOptionalNetworkPath": "(Neobvezno) Omrežna mapa:", + "LabelOptionalNetworkPath": "Omrežna mapa v skupni rabi:", "LabelOptionalNetworkPathHelp": "V primeru, da je mapa deljena v vašem omrežju, lahko Jellyfin deli omrežno pot z ostalimi napravami in jim omogoči neposreden dostop do vsebin. Na primer {0} ali {1}.", "LabelRemoteClientBitrateLimitHelp": "Neobvezna omejitev bitne hitrosti na posamezno predvajanje za vse naprave izven domačega omrežja. S tem lahko preprečite, da bi naprave zahtevale višjo bitno hitrost predvajanja, kot jo lahko prenese vaše omrežje. To lahko poveča obremenitev CPU-ja, saj bo morda potrebno sprotno prekodiranje za zmanjšanje bitne hitrosti.", "LanNetworksHelp": "Z vejico ločen seznam IP naslovov ali IP/maska omrežji, ki bodo upoštevana kot lokalna pri uveljavljanju omejitev pasovne širine. Če nastavite, se bodo vsi ostali naslovi upoštevali kot zunanji in bodo predmet omejitve pasovne širine. Če pustite prazno, bo kot lokalno omrežje upoštevano zgolj omrežje strežnika.", @@ -848,14 +848,14 @@ "OptionWeekly": "Tedensko", "OriginalAirDateValue": "Prvotni datum predvajanja: {0}", "Overview": "Pregled", - "PackageInstallCancelled": "{0} namestitev preklicana.", - "PackageInstallCompleted": "{0} namestitev uspešna.", - "PackageInstallFailed": "{0} namestitev neuspešna.", + "PackageInstallCancelled": "{0} (različica {1}) namestitev preklicana.", + "PackageInstallCompleted": "{0} (različica {1}) namestitev uspešna.", + "PackageInstallFailed": "{0} (različica {1}) namestitev neuspešna.", "PasswordMatchError": "Geslo in potrditev gesla se moreta ujemati.", "PasswordResetComplete": "Geslo je bilo ponastavljeno.", "PasswordResetConfirmation": "Ali ste prepričani, da želite ponastaviti geslo?", "HeaderResetPassword": "Ponastavi geslo", - "PasswordResetProviderHelp": "Izberite ponudnika ponastavitve gesla, ki bo uporabljen, ko bo ta uporabnik zahteval ponastavitev gesla", + "PasswordResetProviderHelp": "Izberite ponudnika ponastavitve gesla, ki bo uporabljen, ko bo ta uporabnik zahteval ponastavitev gesla.", "PasswordSaved": "Geslo shranjeno.", "PerfectMatch": "Popolno ujemanje", "PictureInPicture": "Slika v sliki", @@ -1047,7 +1047,7 @@ "OptionDvd": "DVD", "OptionDownloadMenuImage": "Meni", "OptionDownloadLogoImage": "Logotip", - "OptionDownloadImagesInAdvanceHelp": "Privzeto se večina slik prenese šele, ko jih zahtevajo aplikacije. Omogočite to možnost za prenos slik vnaprej, pri uvozu predstavnosti. To lahko občutno podaljša preiskovanje knjižnice.", + "OptionDownloadImagesInAdvanceHelp": "Privzeto se večina slik prenese šele, ko jih zahtevajo odjemalci. Omogočite to možnost za prenos slik vnaprej, pri uvozu predstavnosti. To lahko občutno podaljša preiskovanje knjižnic.", "OptionDownloadImagesInAdvance": "Prenesi slike vnaprej", "OptionAllowSyncTranscoding": "Dovoli prenašanje in sinhronizacijo predstavnosti ki zahteva pretvarjanje", "OptionAllowRemoteControlOthers": "Dovoli daljinsko upravljanje drugih uporabnikov", @@ -1077,14 +1077,14 @@ "LabelServerName": "Ime strežnika:", "LabelServerHostHelp": "192.168.1.100:8096 ali https://myserver.com", "LabelServerHost": "Naslov:", - "LabelSeriesRecordingPath": "Pot za snemanje serij (neobvezno):", + "LabelSeriesRecordingPath": "Pot za snemanje serij:", "LabelSerialNumber": "Serijska številka", "LabelSendNotificationToUsers": "Pošlji obvestilo na:", "LabelSeasonNumber": "Številka sezone:", "LabelScreensaver": "Ohranjevalnik zaslona:", "LabelSaveLocalMetadataHelp": "Shranjevanje slik v mape predstavnosti omogoča lažji dostop in urejanje slik.", "LabelSaveLocalMetadata": "Shrani slike v mape predstavnosti", - "LabelRuntimeMinutes": "Čas trajanja (minute):", + "LabelRuntimeMinutes": "Čas trajanja:", "LabelRemoteClientBitrateLimit": "Omejitev bitne hitrosti pretakanja preko interneta (Mbps):", "LabelRecordingPathHelp": "Določite privzeto lokacijo za shranjevanje posnetkov. Če pustite prazno, bo uporabljena mapa namestitve strežnika.", "LabelRecordingPath": "Privzeta pot posnetkov:", @@ -1403,5 +1403,31 @@ "MessageLeaveEmptyToInherit": "Pustite prazno za podedovanje nastavitev od nadrejenega elementa ali globalne privzete vrednosti.", "LabelXDlnaDocHelp": "Določa vsebino X_DLNADOC elementa v urn:schemas-dlna-org:device-1-0 namespace.", "LabelXDlnaCapHelp": "Določa vsebino X_DLNACAP elementa v urn:schemas-dlna-org:device-1-0 namespace.", - "OptionDislikes": "Ni mi všeč" + "OptionDislikes": "Ni mi všeč", + "ClearQueue": "Počisti čakalno vrsto", + "StopPlayback": "Ustavi predvajanje", + "ButtonCast": "Zasedba", + "EnableBlurHashHelp": "Med nalaganjem bodo slike nadomeščene z edinstvenimi nadomestnimi sličicami.", + "EnableBlurHash": "Omogoči zamegljene začasne sličice za slike", + "UnsupportedPlayback": "Jellyfin ne more dešifrirati vsebin zaščitenih z DRM, vendar bo strežnik kljub temu poskušal predvajati vse vsebine. Zaradi šifriranja ali drugih nepodprtih funkcij, na primer interaktivnih naslovov, bodo nekatere vsebine prikazane popolnoma črne.", + "OnApplicationStartup": "Ob zagonu aplikacije", + "EveryXHours": "Vsakih {0} ur", + "EveryHour": "Vsako uro", + "EveryXMinutes": "Vsakih {0} minut", + "OnWakeFromSleep": "Ob prebujenju iz spanja", + "WeeklyAt": "{0}s ob {1}", + "DailyAt": "Vsak dan ob {0}", + "OptionPlainVideoItemsHelp": "Vsi videi so predstavljeni v DIDL kot \"object.item.videoItem\" namesto bolj specifičen tip, kot na primer \"object.item.videoItem.movie\".", + "OptionPlainVideoItems": "Prikaži vse videe kot preproste video vsebine", + "OptionPlainStorageFoldersHelp": "Vse mape so predstavljene v DIDL kot \"object.container.storageFolder\" namesto bolj specifičen tip, kot na primer \"object.container.person.musicArtist\".", + "OptionPlainStorageFolders": "Prikaži vse mape kot enostavne mape shrambe", + "OptionHlsSegmentedSubtitles": "HLS ločeni podnapisi", + "OptionExtractChapterImage": "Omogoči ekstrakcijo slik poglavij", + "OptionEstimateContentLength": "Oceni dolžino vsebine pri prekodiranju", + "OptionEquals": "Je enako", + "OptionEnded": "Zaključeno", + "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" } From 234a70875df1cc047c5fc546291036ccfb0cb20f Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 15:15:59 +0100 Subject: [PATCH 063/155] Remove Missed Required --- src/components/imageDownloader/imageDownloader.js | 2 +- src/components/metadataEditor/metadataEditor.js | 9 ++++----- src/scripts/libraryBrowser.js | 5 ++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index 2be2ef09b2..3a5f2f56ce 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -317,7 +317,7 @@ import 'cardStyle'; function showEditor(itemId, serverId, itemType) { loading.show(); - require(['text!./imageDownloader.template.html'], function (template) { + import('text!./imageDownloader.template.html').then(({default: template}) => { const apiClient = connectionManager.getApiClient(serverId); currentItemId = itemId; diff --git a/src/components/metadataEditor/metadataEditor.js b/src/components/metadataEditor/metadataEditor.js index be44e86b44..162da8984a 100644 --- a/src/components/metadataEditor/metadataEditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -6,7 +6,6 @@ import loading from 'loading'; import focusManager from 'focusManager'; import connectionManager from 'connectionManager'; import globalize from 'globalize'; -import require from 'require'; import shell from 'shell'; import 'emby-checkbox'; import 'emby-input'; @@ -37,7 +36,7 @@ import 'flexStyles'; function submitUpdatedItem(form, item) { function afterContentTypeUpdated() { - require(['toast'], function (toast) { + import('toast').then(({default: toast}) => { toast(globalize.translate('MessageItemSaved')); }); @@ -227,7 +226,7 @@ import 'flexStyles'; } function editPerson(context, person, index) { - require(['personEditor'], function (personEditor) { + import('personEditor').then(({default: personEditor}) => { personEditor.show(person).then(function (updatedPerson) { const isNew = index === -1; @@ -246,14 +245,14 @@ import 'flexStyles'; if (parentId) { reload(context, parentId, item.ServerId); } else { - require(['appRouter'], function (appRouter) { + import('appRouter').then(({default: appRouter}) => { appRouter.goHome(); }); } } function showMoreMenu(context, button, user) { - require(['itemContextMenu'], function (itemContextMenu) { + import('itemContextMenu').then(({default: itemContextMenu}) => { var item = currentItem; itemContextMenu.show({ diff --git a/src/scripts/libraryBrowser.js b/src/scripts/libraryBrowser.js index 46cda51e55..4f2a0fdb47 100644 --- a/src/scripts/libraryBrowser.js +++ b/src/scripts/libraryBrowser.js @@ -119,7 +119,10 @@ export function getQueryPagingHtml (options) { } export function showSortMenu (options) { - require(['dialogHelper', 'emby-radio'], function (dialogHelper) { + Promise.All([ + import('dialogHelper'), + import('emby-radio') + ]).then(({default: dialogHelper}) => { function onSortByChange() { var newValue = this.value; From 4b1e94867ced840b2df1934924fe36bf4c92c248 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2020 16:47:34 +0000 Subject: [PATCH 064/155] Bump whatwg-fetch from 3.2.0 to 3.3.1 Bumps [whatwg-fetch](https://github.com/github/fetch) from 3.2.0 to 3.3.1. - [Release notes](https://github.com/github/fetch/releases) - [Commits](https://github.com/github/fetch/compare/v3.2.0...v3.3.1) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..7365d4f0e4 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "sortablejs": "^1.10.2", "swiper": "^5.4.5", "webcomponents.js": "^0.7.24", - "whatwg-fetch": "^3.2.0" + "whatwg-fetch": "^3.3.1" }, "babel": { "presets": [ diff --git a/yarn.lock b/yarn.lock index 17c2b176ef..400a7c2bee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11988,10 +11988,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.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz#8e134f701f0a4ab5fda82626f113e2b647fd16dc" - integrity sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w== +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== which-module@^1.0.0: version "1.0.0" From a930482ca11df112e7eac1b1a57d36a588331195 Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Tue, 4 Aug 2020 19:00:19 +0200 Subject: [PATCH 065/155] Fixed linting errors --- src/controllers/dashboard/notifications/notifications/index.js | 1 - src/controllers/livetv/livetvrecordings.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/controllers/dashboard/notifications/notifications/index.js b/src/controllers/dashboard/notifications/notifications/index.js index d12a5f2e61..b97403f8eb 100644 --- a/src/controllers/dashboard/notifications/notifications/index.js +++ b/src/controllers/dashboard/notifications/notifications/index.js @@ -1,5 +1,4 @@ import loading from 'loading'; -import libraryMenu from 'libraryMenu'; import globalize from 'globalize'; import 'listViewStyle'; import 'emby-button'; diff --git a/src/controllers/livetv/livetvrecordings.js b/src/controllers/livetv/livetvrecordings.js index cb3bfeb7a6..ec2f57e14f 100644 --- a/src/controllers/livetv/livetvrecordings.js +++ b/src/controllers/livetv/livetvrecordings.js @@ -1,7 +1,5 @@ -import layoutManager from 'layoutManager'; import loading from 'loading'; import cardBuilder from 'cardBuilder'; -import appHost from 'apphost'; import imageLoader from 'imageLoader'; import 'scripts/livetvcomponents'; import 'listViewStyle'; From 8c58472dcf1b2586f71eb3e2d247d1955b3a721b Mon Sep 17 00:00:00 2001 From: matjaz321 Date: Tue, 4 Aug 2020 19:15:15 +0200 Subject: [PATCH 066/155] Migrated castSenderApi.js to es6 module --- package.json | 1 + src/components/castSenderApi.js | 50 ++++++++++++-------------- src/plugins/chromecastPlayer/plugin.js | 2 +- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..bf4d9ccf0a 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,7 @@ "src/components/toast/toast.js", "src/components/upnextdialog/upnextdialog.js", "src/components/viewContainer.js", + "src/components/castSenderApi.js", "src/controllers/session/addServer/index.js", "src/controllers/session/forgotPassword/index.js", "src/controllers/session/redeemPassword/index.js", diff --git a/src/components/castSenderApi.js b/src/components/castSenderApi.js index b541c1f87a..c7fd5df217 100644 --- a/src/components/castSenderApi.js +++ b/src/components/castSenderApi.js @@ -1,34 +1,28 @@ -define([], function() { - 'use strict'; - - if (window.appMode === 'cordova' || window.appMode === 'android') { - return { - load: function () { - window.chrome = window.chrome || {}; +class CastSenderApi { + load() { + if (window.appMode === 'cordova' || window.appMode === 'android') { + window.chrome = window.chrome || {}; + return Promise.resolve(); + } else { + let ccLoaded = false; + if (ccLoaded) { return Promise.resolve(); } - }; - } else { - var ccLoaded = false; - return { - load: function () { - if (ccLoaded) { - return Promise.resolve(); - } - return new Promise(function (resolve, reject) { - var fileref = document.createElement('script'); - fileref.setAttribute('type', 'text/javascript'); + return new Promise(function (resolve) { + const fileref = document.createElement('script'); + fileref.setAttribute('type', 'text/javascript'); - fileref.onload = function () { - ccLoaded = true; - resolve(); - }; + fileref.onload = function () { + ccLoaded = true; + resolve(); + }; - fileref.setAttribute('src', 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js'); - document.querySelector('head').appendChild(fileref); - }); - } - }; + fileref.setAttribute('src', 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js'); + document.querySelector('head').appendChild(fileref); + }); + } } -}); +} + +export default CastSenderApi; diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 2741a6f0e3..3d49f6fff6 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -577,7 +577,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.isLocalPlayer = false; this.lastPlayerData = {}; - castSenderApiLoader.load().then(initializeChromecast.bind(this)); + new castSenderApiLoader().load().then(initializeChromecast.bind(this)); } ChromecastPlayer.prototype.tryPair = function (target) { From 8e17d603a87dc7581334b362c6c5c54048e542e2 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Aug 2020 21:43:24 +0100 Subject: [PATCH 067/155] Update list.js --- src/controllers/list.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index b9237887d0..7d35bcf5cb 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -722,11 +722,9 @@ class ItemsView { this.itemsContainer.setAttribute('data-refreshinterval', '300000'); } - let i; - let length; const btnViewSettings = view.querySelectorAll('.btnViewSettings'); - for (i = 0, length = btnViewSettings.length; i < length; i++) { + for (let i = 0, length = btnViewSettings.length; i < length; i++) { btnViewSettings[i].addEventListener('click', showViewSettingsMenu.bind(this)); } @@ -734,7 +732,7 @@ class ItemsView { this.filterButtons = filterButtons; const hasVisibleFilters = this.getVisibleFilters().length; - for (i = 0, length = filterButtons.length; i < length; i++) { + for (let i = 0, length = filterButtons.length; i < length; i++) { const btnFilter = filterButtons[i]; btnFilter.addEventListener('click', showFilterMenu.bind(this)); From 0963fb37486372f48aaf16fbba08218d25d2c40a Mon Sep 17 00:00:00 2001 From: millallo Date: Tue, 4 Aug 2020 21:02:48 +0000 Subject: [PATCH 068/155] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/it/ --- src/strings/it.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strings/it.json b/src/strings/it.json index 9035251c12..15e32d84bf 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -157,8 +157,8 @@ "DetectingDevices": "Rilevamento dispositivi", "DeviceAccessHelp": "Si applica solo ai dispositivi che possono essere identificati univocamente e non impedirà l'accesso dal browser. Filtrare l'accesso ai dispositivi dell'utente impedirà di usare nuovi dispositivi fino a quando non saranno stati approvati qui.", "DirectPlaying": "Riproduzione Diretta", - "DirectStreamHelp1": "Il file multimediale è compatibile con il dispositivo per quanto riguarda la risoluzione e il tipo di supporto (H. 264, AC3, ecc), ma è in un contenitore file incompatibile (mkv, avi, wmv, ecc). Il video sarà ri-confezionato al volo prima di streammarlo sul dispositivo.", - "DirectStreamHelp2": "Lo Streaming in Diretta di un file utilizza poco il processore senza alcuna perdita di qualità video.", + "DirectStreamHelp1": "Il file multimediale è compatibile con il dispositivo per quanto riguarda la risoluzione e il tipo di supporto (H. 264, AC3, ecc), ma è in un contenitore file incompatibile (mkv, avi, wmv, ecc). Il video sarà quindi reimpacchettato al volo prima dell'invio al dispositivo.", + "DirectStreamHelp2": "Lo streaming in diretta utilizza poco processore e con poca perdita di qualità video.", "DirectStreaming": "Streaming Diretto", "Director": "Regista", "Directors": "Registi", @@ -265,7 +265,7 @@ "HeaderAllowMediaDeletionFrom": "Abilita Eliminazione Media Da", "HeaderApiKey": "Chiave API", "HeaderApiKeys": "Chiavi API", - "HeaderApiKeysHelp": "Le Applicazioni esterne devono avere una chiave API per comunicare con il Server Jellyfin. Le chiavi sono emesse accedendo con un account Jellyfin, o fornendo manualmente una chiave all'applicazione.", + "HeaderApiKeysHelp": "Le Applicazioni esterne devono avere una chiave API per comunicare con il server. Le chiavi sono emesse accedendo con un qualsiasi account, o fornendo manualmente una chiave all'applicazione.", "HeaderAudioBooks": "Audiolibri", "HeaderAudioSettings": "Impostazioni audio", "HeaderBlockItemsWithNoRating": "Blocca elementi sconosciuti o senza informazioni:", @@ -1281,7 +1281,7 @@ "HeaderCastCrew": "Cast", "HeaderMedia": "Media", "HeaderPassword": "Password", - "AuthProviderHelp": "Selezionare un Provider di Autenticazione da utilizzare per autenticare la password dell'utente.", + "AuthProviderHelp": "Selezionare un provider di autenticazione da utilizzare per autenticare la password dell'utente.", "HeaderFavoriteMovies": "Film Preferiti", "HeaderFavoriteShows": "Serie TV Preferite", "HeaderFavoriteEpisodes": "Episodi Preferiti", From 0e388d4d6deab055926ee7093ec051694e985e9c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 02:51:36 +0000 Subject: [PATCH 069/155] Bump @babel/core from 7.11.0 to 7.11.1 Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.11.0 to 7.11.1. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.11.1/packages/babel-core) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 7365d4f0e4..a014dc1b76 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "^7.11.0", + "@babel/core": "^7.11.1", "@babel/eslint-parser": "^7.11.0", "@babel/eslint-plugin": "^7.11.0", "@babel/plugin-proposal-class-properties": "^7.10.1", diff --git a/yarn.lock b/yarn.lock index 400a7c2bee..5c8a7f7e14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,16 +18,16 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@>=7.2.2", "@babel/core@>=7.9.0", "@babel/core@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.0.tgz#73b9c33f1658506887f767c26dae07798b30df76" - integrity sha512-mkLq8nwaXmDtFmRkQ8ED/eA2CnVw4zr7dCztKalZXBvdK5EeNUAesrrwUqjQEzFgomJssayzB0aqlOsP1vGLqg== +"@babel/core@>=7.2.2", "@babel/core@>=7.9.0", "@babel/core@^7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" + integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/generator" "^7.11.0" "@babel/helper-module-transforms" "^7.11.0" "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.0" + "@babel/parser" "^7.11.1" "@babel/template" "^7.10.4" "@babel/traverse" "^7.11.0" "@babel/types" "^7.11.0" @@ -281,10 +281,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.10.4", "@babel/parser@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.0.tgz#a9d7e11aead25d3b422d17b2c6502c8dddef6a5d" - integrity sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw== +"@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.1.tgz#d91a387990b21e5d20047b336bb19b0553f02ff5" + integrity sha512-u9QMIRdKVF7hfEkb3nu2LgZDIzCQPv+yHD9Eg6ruoJLjkrQ9fFz4IBSlF/9XwoNri9+2F1IY+dYuOfZrXq8t3w== "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.4" From 88bbada5064c4caa5576e33c180827fd725da44d Mon Sep 17 00:00:00 2001 From: Daisuke Inoue Date: Wed, 5 Aug 2020 05:59:37 +0000 Subject: [PATCH 070/155] Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ja/ --- src/strings/ja.json | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/strings/ja.json b/src/strings/ja.json index 4e0a0a4fed..a6738d9517 100644 --- a/src/strings/ja.json +++ b/src/strings/ja.json @@ -169,7 +169,7 @@ "DetectingDevices": "検出デバイス", "DeviceAccessHelp": "これは、識別できるデバイスにのみ適用され、ブラウザへのアクセスを妨げることはありません。 ユーザーのデバイスアクセスをフィルタリングすると、ここで承認されるまで新しいデバイスを使用できなくなります。", "DirectPlaying": "ダイレクト再生", - "DirectStreamHelp1": "メディアの種類 (H.264, AC3, etc.)がお使いのデバイスに対応している場合Jellyfinサーバーからメディアファイルの直接再生が可能です。互換性のないファイルコンテナ(.mkv, .avi, .wmv, etc.)はリパックを行い再生されます。", + "DirectStreamHelp1": "メディアは、解像度と種類 (H.264, AC3など)がお使いのデバイスに対応していますが、ファイルコンテナ(.mkv, .avi, .wmvなど)が対応していません。ビデオはデバイスに送信される前に、再パッケージされます。", "DirectStreamHelp2": "ファイルのダイレクトストリーミングは、ビデオ品質を損なうことなく、Jellyfin Serverにもほとんど負荷がありません。", "DirectStreaming": "ダイレクトストリーミング", "Director": "ディレクター", @@ -291,7 +291,7 @@ "HeaderAllowMediaDeletionFrom": "メディアの削除を許可", "HeaderApiKey": "API キー", "HeaderApiKeys": "API キー", - "HeaderApiKeysHelp": "Jellyfin サーバーと通信するには、外部アプリケーション用ににAPIキーが必要です。 キーはJellyfinアカウントでログインするか、手動でアプリケーションにキーを付与することによって発行されます。", + "HeaderApiKeysHelp": "外部アプリケーションが Jellyfin サーバーと通信するには、API キーが必要です。キーは 通常のユーザーアカウントでログインするか、手動でアプリケーションにキーを付与することで発行します。", "HeaderApp": "アプリ", "HeaderAppearsOn": "表示", "HeaderAudioBooks": "オーディオブック", @@ -534,7 +534,7 @@ "LabelEncoderPreset": "H264エンコーディングプリセット:", "LabelHardwareAccelerationType": "ハードウェアアクセラレーション:", "LabelH264Crf": "H264エンコーディングCRF:", - "LabelHttpsPortHelp": "JellyfinのHTTPSサーバーがバインドするTCPポート番号。", + "LabelHttpsPortHelp": "HTTPS サーバーのTCPポート番号。", "LabelKodiMetadataDateFormat": "リリース日時フォーマット:", "LabelLogs": "ログ:", "LabelMessageText": "メッセージテキスト:", @@ -1136,7 +1136,7 @@ "LabelCustomCertificatePathHelp": "カスタムドメインでTLSサポートを有効にするための証明書と秘密鍵を含むPKCS #12ファイルのパス。", "LabelCachePathHelp": "画像などのサーバーキャッシュファイルの場所を指定します。空欄にしておくと、サーバーのデフォルトを使います。", "LabelBlastMessageIntervalHelp": "ブラスト アライブ メッセージ間の時間を秒単位で指定します。", - "LabelBindToLocalNetworkAddressHelp": "追加の設定。http サーバをバインドするローカル IP アドレスを上書きします。空のままにしておくと、サーバは利用可能なすべてのアドレスにバインドします。この値を変更するには、Jellyfin Server を再起動する必要があります。", + "LabelBindToLocalNetworkAddressHelp": "HTTP サーバー用のローカル IP アドレスを上書きします。空のままにしておくと、サーバーは利用可能なすべてのアドレスにバインドします。この値の変更を反映するには、Jellyfin サーバーの再起動が必要です。", "LabelAlbumArtMaxWidthHelp": "upnp:albumArtURI で公開するアルバムアートの最大解像度。", "LabelAlbumArtMaxHeightHelp": "upnp:albumArtURI で公開するアルバムアートの最大解像度。", "LabelAlbumArtHelp": "upnp:albumArtURI の dlna:profileID 属性で、アルバムアートに使われるPN。デバイスによっては、画像のサイズと無関係に特定の値を要求するものもあります。", @@ -1161,14 +1161,14 @@ "LabelEnableDlnaPlayTo": "DLNA 再生を有効にする", "LabelEnableDlnaDebugLoggingHelp": "巨大なログファイルを作成します。トラブルシューティングでの必要な際にだけ使用してください。", "LabelEnableDlnaClientDiscoveryIntervalHelp": "Jellyfin が実行する SSDP 検索の間隔を決めます(秒単位)。", - "LabelGroupMoviesIntoCollectionsHelp": "ムービーリストを表示する際、コレクションに属するムービーを1つのグループとして表示します。", - "LabelServerNameHelp": "この名前はサーバーを識別するために使用します。デフォルトではサーバーのコンピュータ名です。", + "LabelGroupMoviesIntoCollectionsHelp": "ムービー リストを表示する際、コレクションに属するムービーは1つのグループとして表示します。", + "LabelServerNameHelp": "この名前はサーバーを識別するために使用します。デフォルトではサーバーのホスト名です。", "LabelExtractChaptersDuringLibraryScanHelp": "ライブラリー スキャン中に動画を取り込んだときに、チャプター画像を生成します。もしくは、スケジュールタスクの中でチャプター画像を抽出することで、通常のライブラリー スキャンをより速く完了させることができます。", "LabelExtractChaptersDuringLibraryScan": "ライブラリーをスキャンしながら、チャプター画像を生成する", - "LabelBaseUrlHelp": "サーバーの URL にカスタム サブディレクトリを加えます。例 : http://example.com/<baseurl>", + "LabelBaseUrlHelp": "サーバーの URL に、カスタム サブディレクトリを加えます。例 : http://example.com/<baseurl>", "LabelEnableSingleImageInDidlLimitHelp": "Didl 内に複数の画像が埋め込まれている場合、一部のデバイスでは正しくレンダリングされません。", - "LabelEnableRealtimeMonitorHelp": "ファイルへの変更は、サポートされているファイルシステム上ですぐに処理されます。", - "LabelEnableHttpsHelp": "構成された HTTPS ポートからサーバーがリッスンするのを有効にします。この機能を有効にするには、適切な証明書を設定する必要があります。", + "LabelEnableRealtimeMonitorHelp": "サポートしているファイルシステムでは、ファイルの変更をすぐに処理します。", + "LabelEnableHttpsHelp": "設定した HTTPS ポートをリッスンします。この機能を有効にするには、適切な証明書の設定が必要です。", "LabelImageFetchersHelp": "画像検索サイトの優先度を設定します。", "LabelPostProcessorArgumentsHelp": "{path}を録画ファイルのパスとして使用します。", "LabelPostProcessorArguments": "後処理のコマンドライン引数:", @@ -1214,5 +1214,7 @@ "LabelKodiMetadataDateFormatHelp": "NFOファイル内の全データが,このフォーマットによって解析されます。", "LabelKeepUpTo": "最新:", "LabelInNetworkSignInWithEasyPasswordHelp": "ローカルネットワーク内では簡単なPINコードを利用してサインインするようにします。ローカル以外からのアクセスのときのみ通常のパスワードが必要になります。PINコードを空欄にした場合,ローカルネットワーク内からのアクセスではパスワードが不要になります。", - "LabelInNetworkSignInWithEasyPassword": "簡単なPINコードを利用してネットワーク内からサインインする機能の有効化" + "LabelInNetworkSignInWithEasyPassword": "簡単なPINコードを利用してネットワーク内からサインインする機能の有効化", + "LabelIconMaxWidthHelp": "upnp:icon として表示されるアイコンの最大解像度(幅)。", + "LabelIconMaxHeightHelp": "upnp:icon として表示されるアイコンの最大解像度(高さ)。" } From 91f6532e35f347e134b534f93e89d0c68e7866fa Mon Sep 17 00:00:00 2001 From: Matjaz Zavski Date: Wed, 5 Aug 2020 09:17:17 +0200 Subject: [PATCH 071/155] Update src/controllers/livetv/livetvchannels.js Co-authored-by: Julien Machiels --- src/controllers/livetv/livetvchannels.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/livetv/livetvchannels.js b/src/controllers/livetv/livetvchannels.js index d34fe58132..278200c634 100644 --- a/src/controllers/livetv/livetvchannels.js +++ b/src/controllers/livetv/livetvchannels.js @@ -91,8 +91,8 @@ export default function (view, params, tabContent) { } function showFilterMenu(context) { - import(['components/filterdialog/filterdialog']).then(({default: filterDialogFactory}) => { - const filterDialog = new filterDialogFactory({ + import(['components/filterdialog/filterdialog']).then(({default: FilterDialog}) => { + const filterDialog = new FilterDialog({ query: getQuery(), mode: 'livetvchannels', serverId: ApiClient.serverId() From fbb98bdd2f8adab4451793c998d543c4c03ba378 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 09:00:24 +0100 Subject: [PATCH 072/155] Migration of guide and guide-settings to ES6 modules --- package.json | 2 + src/components/guide/guide-settings.js | 250 +-- src/components/guide/guide.js | 2306 ++++++++++++------------ src/controllers/livetv/livetvguide.js | 2 +- 4 files changed, 1294 insertions(+), 1266 deletions(-) diff --git a/package.json b/package.json index 5ad12d2011..ad9b19d242 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,8 @@ "src/components/fetchhelper.js", "src/components/filterdialog/filterdialog.js", "src/components/groupedcards.js", + "src/components/guide/guide.js", + "src/components/guide/guide-settings.js", "src/components/homeScreenSettings/homeScreenSettings.js", "src/components/homesections/homesections.js", "src/components/htmlMediaHelper.js", diff --git a/src/components/guide/guide-settings.js b/src/components/guide/guide-settings.js index c3ba49f283..b62604ab98 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -1,147 +1,153 @@ -define(['dialogHelper', 'globalize', 'userSettings', 'layoutManager', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-radio', 'css!./../formdialog', 'material-icons'], function (dialogHelper, globalize, userSettings, layoutManager, connectionManager, require, loading, scrollHelper) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import globalize from 'globalize'; +import * as userSettings from 'userSettings'; +import layoutManager from 'layoutManager'; +import scrollHelper from 'scrollHelper'; +import 'emby-checkbox'; +import 'emby-radio'; +import 'css!./../formdialog'; +import 'material-icons'; - function saveCategories(context, options) { - var categories = []; +function saveCategories(context, options) { + const categories = []; - var chkCategorys = context.querySelectorAll('.chkCategory'); - for (var i = 0, length = chkCategorys.length; i < length; i++) { - var type = chkCategorys[i].getAttribute('data-type'); + const chkCategorys = context.querySelectorAll('.chkCategory'); + for (let i = 0, length = chkCategorys.length; i < length; i++) { + const type = chkCategorys[i].getAttribute('data-type'); - if (chkCategorys[i].checked) { - categories.push(type); - } - } - - if (categories.length >= 4) { - categories.push('series'); - } - - // differentiate between none and all - categories.push('all'); - options.categories = categories; - } - - function loadCategories(context, options) { - var selectedCategories = options.categories || []; - - var chkCategorys = context.querySelectorAll('.chkCategory'); - for (var i = 0, length = chkCategorys.length; i < length; i++) { - var type = chkCategorys[i].getAttribute('data-type'); - - chkCategorys[i].checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1; + if (chkCategorys[i].checked) { + categories.push(type); } } - function save(context) { - var i; - var length; + if (categories.length >= 4) { + categories.push('series'); + } - var chkIndicators = context.querySelectorAll('.chkIndicator'); - for (i = 0, length = chkIndicators.length; i < length; i++) { - var type = chkIndicators[i].getAttribute('data-type'); - userSettings.set('guide-indicator-' + type, chkIndicators[i].checked); + // differentiate between none and all + categories.push('all'); + options.categories = categories; +} + +function loadCategories(context, options) { + const selectedCategories = options.categories || []; + + const chkCategorys = context.querySelectorAll('.chkCategory'); + for (let i = 0, length = chkCategorys.length; i < length; i++) { + const type = chkCategorys[i].getAttribute('data-type'); + + chkCategorys[i].checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1; + } +} + +function save(context) { + let i; + let length; + + const chkIndicators = context.querySelectorAll('.chkIndicator'); + for (i = 0, length = chkIndicators.length; i < length; i++) { + const type = chkIndicators[i].getAttribute('data-type'); + userSettings.set('guide-indicator-' + type, chkIndicators[i].checked); + } + + userSettings.set('guide-colorcodedbackgrounds', context.querySelector('.chkColorCodedBackgrounds').checked); + userSettings.set('livetv-favoritechannelsattop', context.querySelector('.chkFavoriteChannelsAtTop').checked); + + const sortBys = context.querySelectorAll('.chkSortOrder'); + for (i = 0, length = sortBys.length; i < length; i++) { + if (sortBys[i].checked) { + userSettings.set('livetv-channelorder', sortBys[i].value); + break; } + } +} - userSettings.set('guide-colorcodedbackgrounds', context.querySelector('.chkColorCodedBackgrounds').checked); - userSettings.set('livetv-favoritechannelsattop', context.querySelector('.chkFavoriteChannelsAtTop').checked); +function load(context) { + let i; + let length; - var sortBys = context.querySelectorAll('.chkSortOrder'); - for (i = 0, length = sortBys.length; i < length; i++) { - if (sortBys[i].checked) { - userSettings.set('livetv-channelorder', sortBys[i].value); - break; - } + const chkIndicators = context.querySelectorAll('.chkIndicator'); + for (i = 0, length = chkIndicators.length; i < length; i++) { + const type = chkIndicators[i].getAttribute('data-type'); + + if (chkIndicators[i].getAttribute('data-default') === 'true') { + chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) !== 'false'; + } else { + chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) === 'true'; } } - function load(context) { - var i; - var length; + context.querySelector('.chkColorCodedBackgrounds').checked = userSettings.get('guide-colorcodedbackgrounds') === 'true'; + context.querySelector('.chkFavoriteChannelsAtTop').checked = userSettings.get('livetv-favoritechannelsattop') !== 'false'; - var chkIndicators = context.querySelectorAll('.chkIndicator'); - for (i = 0, length = chkIndicators.length; i < length; i++) { - var type = chkIndicators[i].getAttribute('data-type'); + const sortByValue = userSettings.get('livetv-channelorder') || 'Number'; - if (chkIndicators[i].getAttribute('data-default') === 'true') { - chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) !== 'false'; + const sortBys = context.querySelectorAll('.chkSortOrder'); + for (i = 0, length = sortBys.length; i < length; i++) { + sortBys[i].checked = sortBys[i].value === sortByValue; + } +} + +function showEditor(options) { + return new Promise(function (resolve, reject) { + let settingsChanged = false; + + import('text!./guide-settings.template.html').then(({default: template}) => { + const dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; } else { - chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) === 'true'; + dialogOptions.size = 'small'; } - } - context.querySelector('.chkColorCodedBackgrounds').checked = userSettings.get('guide-colorcodedbackgrounds') === 'true'; - context.querySelector('.chkFavoriteChannelsAtTop').checked = userSettings.get('livetv-favoritechannelsattop') !== 'false'; + const dlg = dialogHelper.createDialog(dialogOptions); - var sortByValue = userSettings.get('livetv-channelorder') || 'Number'; + dlg.classList.add('formDialog'); - var sortBys = context.querySelectorAll('.chkSortOrder'); - for (i = 0, length = sortBys.length; i < length; i++) { - sortBys[i].checked = sortBys[i].value === sortByValue; - } - } + let html = ''; - function showEditor(options) { - return new Promise(function (resolve, reject) { - var settingsChanged = false; + html += globalize.translateHtml(template, 'core'); - require(['text!./guide-settings.template.html'], function (template) { - var dialogOptions = { - removeOnClose: true, - scrollY: false - }; + dlg.innerHTML = html; - if (layoutManager.tv) { - dialogOptions.size = 'fullscreen'; - } else { - dialogOptions.size = 'small'; - } - - var dlg = dialogHelper.createDialog(dialogOptions); - - dlg.classList.add('formDialog'); - - var html = ''; - - html += globalize.translateHtml(template, 'core'); - - dlg.innerHTML = html; - - dlg.addEventListener('change', function () { - settingsChanged = true; - }); - - dlg.addEventListener('close', function () { - if (layoutManager.tv) { - scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); - } - - save(dlg); - saveCategories(dlg, options); - - if (settingsChanged) { - resolve(); - } else { - reject(); - } - }); - - dlg.querySelector('.btnCancel').addEventListener('click', function () { - dialogHelper.close(dlg); - }); - - if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); - } - - load(dlg); - loadCategories(dlg, options); - dialogHelper.open(dlg); + dlg.addEventListener('change', function () { + settingsChanged = true; }); - }); - } - return { - show: showEditor - }; -}); + dlg.addEventListener('close', function () { + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); + } + + save(dlg); + saveCategories(dlg, options); + + if (settingsChanged) { + resolve(); + } else { + reject(); + } + }); + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + + load(dlg); + loadCategories(dlg, options); + dialogHelper.open(dlg); + }); + }); +} + +export default { + show: showEditor +}; diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 61caa9188f..e48d4646c0 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -1,1178 +1,1198 @@ -define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', 'scrollHelper', 'serverNotifications', 'loading', 'datetime', 'focusManager', 'playbackManager', 'userSettings', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'dom', 'css!./guide.css', 'programStyles', 'material-icons', 'scrollStyles', 'emby-programcell', 'emby-button', 'paper-icon-button-light', 'emby-tabs', 'emby-scroller', 'flexStyles', 'webcomponents'], function (require, inputManager, browser, globalize, connectionManager, scrollHelper, serverNotifications, loading, datetime, focusManager, playbackManager, userSettings, imageLoader, events, layoutManager, itemShortcuts, dom) { - 'use strict'; +import inputManager from 'inputManager'; +import browser from 'browser'; +import globalize from 'globalize'; +import connectionManager from 'connectionManager'; +import scrollHelper from 'scrollHelper'; +import serverNotifications from 'serverNotifications'; +import loading from 'loading'; +import datetime from 'datetime'; +import focusManager from 'focusManager'; +import playbackManager from 'playbackManager'; +import * as userSettings from 'userSettings'; +import imageLoader from 'imageLoader'; +import events from 'events'; +import layoutManager from 'layoutManager'; +import itemShortcuts from 'itemShortcuts'; +import dom from 'dom'; +import 'css!./guide.css'; +import 'programStyles'; +import 'material-icons'; +import 'scrollStyles'; +import 'emby-programcell'; +import 'emby-button'; +import 'paper-icon-button-light'; +import 'emby-tabs'; +import 'emby-scroller'; +import 'flexStyles'; +import 'webcomponents'; - playbackManager = playbackManager.default || playbackManager; - browser = browser.default || browser; - loading = loading.default || loading; - - function showViewSettings(instance) { - require(['guide-settings-dialog'], function (guideSettingsDialog) { - guideSettingsDialog.show(instance.categoryOptions).then(function () { - instance.refresh(); - }); +function showViewSettings(instance) { + import('guide-settings-dialog').then(({default: guideSettingsDialog}) => { + guideSettingsDialog.show(instance.categoryOptions).then(function () { + instance.refresh(); }); + }); +} + +function updateProgramCellOnScroll(cell, scrollPct) { + let left = cell.posLeft; + if (!left) { + left = parseFloat(cell.style.left.replace('%', '')); + cell.posLeft = left; + } + let width = cell.posWidth; + if (!width) { + width = parseFloat(cell.style.width.replace('%', '')); + cell.posWidth = width; } - function updateProgramCellOnScroll(cell, scrollPct) { - var left = cell.posLeft; - if (!left) { - left = parseFloat(cell.style.left.replace('%', '')); - cell.posLeft = left; - } - var width = cell.posWidth; - if (!width) { - width = parseFloat(cell.style.width.replace('%', '')); - cell.posWidth = width; - } + const right = left + width; + const newPct = Math.max(Math.min(scrollPct, right), left); - var right = left + width; - var newPct = Math.max(Math.min(scrollPct, right), left); + const offset = newPct - left; + const pctOfWidth = (offset / width) * 100; - var offset = newPct - left; - var pctOfWidth = (offset / width) * 100; - - var guideProgramName = cell.guideProgramName; - if (!guideProgramName) { - guideProgramName = cell.querySelector('.guideProgramName'); - cell.guideProgramName = guideProgramName; - } - - var caret = cell.caret; - if (!caret) { - caret = cell.querySelector('.guide-programNameCaret'); - cell.caret = caret; - } - - if (guideProgramName) { - if (pctOfWidth > 0 && pctOfWidth <= 100) { - guideProgramName.style.transform = 'translateX(' + pctOfWidth + '%)'; - caret.classList.remove('hide'); - } else { - guideProgramName.style.transform = 'none'; - caret.classList.add('hide'); - } - } + let guideProgramName = cell.guideProgramName; + if (!guideProgramName) { + guideProgramName = cell.querySelector('.guideProgramName'); + cell.guideProgramName = guideProgramName; } - var isUpdatingProgramCellScroll = false; - function updateProgramCellsOnScroll(programGrid, programCells) { - if (isUpdatingProgramCellScroll) { - return; - } - - isUpdatingProgramCellScroll = true; - - requestAnimationFrame(function () { - var scrollLeft = programGrid.scrollLeft; - - var scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0; - - for (var i = 0, length = programCells.length; i < length; i++) { - updateProgramCellOnScroll(programCells[i], scrollPct); - } - - isUpdatingProgramCellScroll = false; - }); + let caret = cell.caret; + if (!caret) { + caret = cell.querySelector('.guide-programNameCaret'); + cell.caret = caret; } - function onProgramGridClick(e) { - if (!layoutManager.tv) { - return; - } - - var programCell = dom.parentWithClass(e.target, 'programCell'); - if (programCell) { - var startDate = programCell.getAttribute('data-startdate'); - var endDate = programCell.getAttribute('data-enddate'); - startDate = datetime.parseISO8601Date(startDate, { toLocal: true }).getTime(); - endDate = datetime.parseISO8601Date(endDate, { toLocal: true }).getTime(); - - var now = new Date().getTime(); - if (now >= startDate && now < endDate) { - var channelId = programCell.getAttribute('data-channelid'); - var serverId = programCell.getAttribute('data-serverid'); - - e.preventDefault(); - e.stopPropagation(); - - playbackManager.play({ - ids: [channelId], - serverId: serverId - }); - } + if (guideProgramName) { + if (pctOfWidth > 0 && pctOfWidth <= 100) { + guideProgramName.style.transform = 'translateX(' + pctOfWidth + '%)'; + caret.classList.remove('hide'); + } else { + guideProgramName.style.transform = 'none'; + caret.classList.add('hide'); } } +} - function Guide(options) { - var self = this; - var items = {}; +let isUpdatingProgramCellScroll = false; +function updateProgramCellsOnScroll(programGrid, programCells) { + if (isUpdatingProgramCellScroll) { + return; + } - self.options = options; - self.categoryOptions = { categories: [] }; + isUpdatingProgramCellScroll = true; - // 30 mins - var cellCurationMinutes = 30; - var cellDurationMs = cellCurationMinutes * 60 * 1000; - var msPerDay = 86400000; + requestAnimationFrame(function () { + const scrollLeft = programGrid.scrollLeft; - var currentDate; - var currentStartIndex = 0; - var currentChannelLimit = 0; - var autoRefreshInterval; - var programCells; - var lastFocusDirection; - var programGrid; + const scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0; - self.refresh = function () { - currentDate = null; - reloadPage(options.element); - restartAutoRefresh(); - }; - - self.pause = function () { - stopAutoRefresh(); - }; - - self.resume = function (refreshData) { - if (refreshData) { - self.refresh(); - } else { - restartAutoRefresh(); - } - }; - - self.destroy = function () { - stopAutoRefresh(); - - events.off(serverNotifications, 'TimerCreated', onTimerCreated); - events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); - - setScrollEvents(options.element, false); - itemShortcuts.off(options.element); - items = {}; - }; - - function restartAutoRefresh() { - stopAutoRefresh(); - - var intervalMs = 60000 * 15; // (minutes) - - autoRefreshInterval = setInterval(function () { - self.refresh(); - }, intervalMs); + for (let i = 0, length = programCells.length; i < length; i++) { + updateProgramCellOnScroll(programCells[i], scrollPct); } - function stopAutoRefresh() { - if (autoRefreshInterval) { - clearInterval(autoRefreshInterval); - autoRefreshInterval = null; - } - } - - function normalizeDateToTimeslot(date) { - var minutesOffset = date.getMinutes() - cellCurationMinutes; - - if (minutesOffset >= 0) { - date.setHours(date.getHours(), cellCurationMinutes, 0, 0); - } else { - date.setHours(date.getHours(), 0, 0, 0); - } - - return date; - } - - function showLoading() { - loading.show(); - } - - function hideLoading() { - loading.hide(); - } - - function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - var apiClient = connectionManager.getApiClient(options.serverId); - - var channelQuery = { - - StartIndex: 0, - EnableFavoriteSorting: userSettings.get('livetv-favoritechannelsattop') !== 'false' - }; - - channelQuery.UserId = apiClient.getCurrentUserId(); - - var channelLimit = 500; - currentChannelLimit = channelLimit; - - showLoading(); - - channelQuery.StartIndex = currentStartIndex; - channelQuery.Limit = channelLimit; - channelQuery.AddCurrentProgram = false; - channelQuery.EnableUserData = false; - channelQuery.EnableImageTypes = 'Primary'; - - var categories = self.categoryOptions.categories || []; - var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; - var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; - var displayNewsContent = !categories.length || categories.indexOf('news') !== -1; - var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; - var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; - - if (displayMovieContent && displaySportsContent && displayNewsContent && displayKidsContent) { - channelQuery.IsMovie = null; - channelQuery.IsSports = null; - channelQuery.IsKids = null; - channelQuery.IsNews = null; - channelQuery.IsSeries = null; - } else { - if (displayNewsContent) { - channelQuery.IsNews = true; - } - if (displaySportsContent) { - channelQuery.IsSports = true; - } - if (displayKidsContent) { - channelQuery.IsKids = true; - } - if (displayMovieContent) { - channelQuery.IsMovie = true; - } - if (displaySeriesContent) { - channelQuery.IsSeries = true; - } - } - - if (userSettings.get('livetv-channelorder') === 'DatePlayed') { - channelQuery.SortBy = 'DatePlayed'; - channelQuery.SortOrder = 'Descending'; - } else { - channelQuery.SortBy = null; - channelQuery.SortOrder = null; - } - - var date = newStartDate; - // Add one second to avoid getting programs that are just ending - date = new Date(date.getTime() + 1000); - - // Subtract to avoid getting programs that are starting when the grid ends - var nextDay = new Date(date.getTime() + msPerDay - 2000); - - // Normally we'd want to just let responsive css handle this, - // but since mobile browsers are often underpowered, - // it can help performance to get them out of the markup - var allowIndicators = dom.getWindowSize().innerWidth >= 600; - - var renderOptions = { - showHdIcon: allowIndicators && userSettings.get('guide-indicator-hd') === 'true', - showLiveIndicator: allowIndicators && userSettings.get('guide-indicator-live') !== 'false', - showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false', - showNewIndicator: allowIndicators && userSettings.get('guide-indicator-new') !== 'false', - showRepeatIndicator: allowIndicators && userSettings.get('guide-indicator-repeat') === 'true', - showEpisodeTitle: layoutManager.tv ? false : true - }; - - apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) { - var btnPreviousPage = context.querySelector('.btnPreviousPage'); - var btnNextPage = context.querySelector('.btnNextPage'); - - if (channelsResult.TotalRecordCount > channelLimit) { - context.querySelector('.guideOptions').classList.remove('hide'); - - btnPreviousPage.classList.remove('hide'); - btnNextPage.classList.remove('hide'); - - if (channelQuery.StartIndex) { - context.querySelector('.btnPreviousPage').disabled = false; - } else { - context.querySelector('.btnPreviousPage').disabled = true; - } - - if ((channelQuery.StartIndex + channelLimit) < channelsResult.TotalRecordCount) { - btnNextPage.disabled = false; - } else { - btnNextPage.disabled = true; - } - } else { - context.querySelector('.guideOptions').classList.add('hide'); - } - - var programFields = []; - - var programQuery = { - UserId: apiClient.getCurrentUserId(), - MaxStartDate: nextDay.toISOString(), - MinEndDate: date.toISOString(), - channelIds: channelsResult.Items.map(function (c) { - return c.Id; - }).join(','), - ImageTypeLimit: 1, - EnableImages: false, - //EnableImageTypes: layoutManager.tv ? "Primary,Backdrop" : "Primary", - SortBy: 'StartDate', - EnableTotalRecordCount: false, - EnableUserData: false - }; - - if (renderOptions.showHdIcon) { - programFields.push('IsHD'); - } - - if (programFields.length) { - programQuery.Fields = programFields.join(''); - } - - apiClient.getLiveTvPrograms(programQuery).then(function (programsResult) { - renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); - - hideLoading(); - }); - }); - } - - function getDisplayTime(date) { - if ((typeof date).toString().toLowerCase() === 'string') { - try { - date = datetime.parseISO8601Date(date, { toLocal: true }); - } catch (err) { - return date; - } - } - - return datetime.getDisplayTime(date).toLowerCase(); - } - - function getTimeslotHeadersHtml(startDate, endDateTime) { - var html = ''; - - // clone - startDate = new Date(startDate.getTime()); - - html += '
'; - - while (startDate.getTime() < endDateTime) { - html += '
'; - - html += getDisplayTime(startDate); - html += '
'; - - // Add 30 mins - startDate.setTime(startDate.getTime() + cellDurationMs); - } - - return html; - } - - function parseDates(program) { - if (!program.StartDateLocal) { - try { - program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); - } catch (err) { - console.error('error parsing timestamp for start date'); - } - } - - if (!program.EndDateLocal) { - try { - program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { toLocal: true }); - } catch (err) { - console.error('error parsing timestamp for end date'); - } - } - - return null; - } - - function getTimerIndicator(item) { - var status; - - if (item.Type === 'SeriesTimer') { - return ''; - } else if (item.TimerId || item.SeriesTimerId) { - status = item.Status || 'Cancelled'; - } else if (item.Type === 'Timer') { - status = item.Status; - } else { - return ''; - } - - if (item.SeriesTimerId) { - if (status !== 'Cancelled') { - return ''; - } - - return ''; - } - - return ''; - } - - function getChannelProgramsHtml(context, date, channel, programs, options, listInfo) { - var html = ''; - - var startMs = date.getTime(); - var endMs = startMs + msPerDay - 1; - - var outerCssClass = layoutManager.tv ? 'channelPrograms channelPrograms-tv' : 'channelPrograms'; - - html += '
'; - - var clickAction = layoutManager.tv ? 'link' : 'programdialog'; - - var categories = self.categoryOptions.categories || []; - var displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; - var displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; - var displayNewsContent = !categories.length || categories.indexOf('news') !== -1; - var displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; - var displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; - var enableColorCodedBackgrounds = userSettings.get('guide-colorcodedbackgrounds') === 'true'; - - var programsFound; - var now = new Date().getTime(); - - for (var i = listInfo.startIndex, length = programs.length; i < length; i++) { - var program = programs[i]; - - if (program.ChannelId !== channel.Id) { - if (programsFound) { - break; - } - - continue; - } - - programsFound = true; - listInfo.startIndex++; - - parseDates(program); - - var startDateLocalMs = program.StartDateLocal.getTime(); - var endDateLocalMs = program.EndDateLocal.getTime(); - - if (endDateLocalMs < startMs) { - continue; - } - - if (startDateLocalMs > endMs) { - break; - } - - items[program.Id] = program; - - var renderStartMs = Math.max(startDateLocalMs, startMs); - var startPercent = (startDateLocalMs - startMs) / msPerDay; - startPercent *= 100; - startPercent = Math.max(startPercent, 0); - - var renderEndMs = Math.min(endDateLocalMs, endMs); - var endPercent = (renderEndMs - renderStartMs) / msPerDay; - endPercent *= 100; - - var cssClass = 'programCell itemAction'; - var accentCssClass = null; - var displayInnerContent = true; - - if (program.IsKids) { - displayInnerContent = displayKidsContent; - accentCssClass = 'kids'; - } else if (program.IsSports) { - displayInnerContent = displaySportsContent; - accentCssClass = 'sports'; - } else if (program.IsNews) { - displayInnerContent = displayNewsContent; - accentCssClass = 'news'; - } else if (program.IsMovie) { - displayInnerContent = displayMovieContent; - accentCssClass = 'movie'; - } else if (program.IsSeries) { - displayInnerContent = displaySeriesContent; - } else { - displayInnerContent = displayMovieContent && displayNewsContent && displaySportsContent && displayKidsContent && displaySeriesContent; - } - - if (displayInnerContent && enableColorCodedBackgrounds && accentCssClass) { - cssClass += ' programCell-' + accentCssClass; - } - - if (now >= startDateLocalMs && now < endDateLocalMs) { - cssClass += ' programCell-active'; - } - - var timerAttributes = ''; - if (program.TimerId) { - timerAttributes += ' data-timerid="' + program.TimerId + '"'; - } - if (program.SeriesTimerId) { - timerAttributes += ' data-seriestimerid="' + program.SeriesTimerId + '"'; - } - - var isAttribute = endPercent >= 2 ? ' is="emby-programcell"' : ''; - - html += ''; - - if (displayInnerContent) { - var guideProgramNameClass = 'guideProgramName'; - - html += '
'; - - html += '
'; - - html += '
' + program.Name; - - var indicatorHtml = null; - if (program.IsLive && options.showLiveIndicator) { - indicatorHtml = '' + globalize.translate('Live') + ''; - } else if (program.IsPremiere && options.showPremiereIndicator) { - indicatorHtml = '' + globalize.translate('Premiere') + ''; - } else if (program.IsSeries && !program.IsRepeat && options.showNewIndicator) { - indicatorHtml = '' + globalize.translate('AttributeNew') + ''; - } else if (program.IsSeries && program.IsRepeat && options.showRepeatIndicator) { - indicatorHtml = '' + globalize.translate('Repeat') + ''; - } - html += indicatorHtml || ''; - - if ((program.EpisodeTitle && options.showEpisodeTitle)) { - html += '
'; - - if (program.EpisodeTitle && options.showEpisodeTitle) { - html += '' + program.EpisodeTitle + ''; - } - html += '
'; - } - - html += '
'; - - if (program.IsHD && options.showHdIcon) { - if (layoutManager.tv) { - html += '
HD
'; - } else { - html += '
HD
'; - } - } - - html += getTimerIndicator(program); - - html += '
'; - } - - html += ''; - } - - html += '
'; - - return html; - } - - function renderChannelHeaders(context, channels, apiClient) { - var html = ''; - - for (var i = 0, length = channels.length; i < length; i++) { - var channel = channels[i]; - var hasChannelImage = channel.ImageTags.Primary; - - var cssClass = 'guide-channelHeaderCell itemAction'; - - if (layoutManager.tv) { - cssClass += ' guide-channelHeaderCell-tv'; - } - - var title = []; - if (channel.ChannelNumber) { - title.push(channel.ChannelNumber); - } - if (channel.Name) { - title.push(channel.Name); - } - - html += ''; - } - - var channelList = context.querySelector('.channelsContainer'); - channelList.innerHTML = html; - imageLoader.lazyChildren(channelList); - } - - function renderPrograms(context, date, channels, programs, options) { - var listInfo = { - startIndex: 0 - }; - - var html = []; - - for (var i = 0, length = channels.length; i < length; i++) { - html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo)); - } - - programGrid.innerHTML = html.join(''); - - programCells = programGrid.querySelectorAll('[is=emby-programcell]'); - - updateProgramCellsOnScroll(programGrid, programCells); - } - - function getProgramSortOrder(program, channels) { - var channelId = program.ChannelId; - var channelIndex = -1; - - for (var i = 0, length = channels.length; i < length; i++) { - if (channelId === channels[i].Id) { - channelIndex = i; - break; - } - } - - var start = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); - - return (channelIndex * 10000000) + (start.getTime() / 60000); - } - - function renderGuide(context, date, channels, programs, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - programs.sort(function (a, b) { - return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels); - }); - - var activeElement = document.activeElement; - var itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null; - var channelRowId = null; - - if (activeElement) { - channelRowId = dom.parentWithClass(activeElement, 'channelPrograms'); - channelRowId = channelRowId && channelRowId.getAttribute ? channelRowId.getAttribute('data-channelid') : null; - } - - renderChannelHeaders(context, channels, apiClient); - - var startDate = date; - var endDate = new Date(startDate.getTime() + msPerDay); - context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate); - items = {}; - renderPrograms(context, date, channels, programs, renderOptions); - - if (focusProgramOnRender) { - focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs); - } - - scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs); - } - - function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) { - scrollToTimeMs -= startTimeOfDayMs; - - var pct = scrollToTimeMs / msPerDay; - - programGrid.scrollTop = 0; - - var scrollPos = pct * programGrid.scrollWidth; - - nativeScrollTo(programGrid, scrollPos, true); - } - - function focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs) { - var focusElem; - if (itemId) { - focusElem = context.querySelector('[data-id="' + itemId + '"]'); - } - - if (focusElem) { - focusManager.focus(focusElem); - } else { - var autoFocusParent; - - if (channelRowId) { - autoFocusParent = context.querySelector('[data-channelid="' + channelRowId + '"]'); - } - - if (!autoFocusParent) { - autoFocusParent = programGrid; - } - - focusToTimeMs -= startTimeOfDayMs; - - var pct = (focusToTimeMs / msPerDay) * 100; - - var programCell = autoFocusParent.querySelector('.programCell'); - - while (programCell) { - var left = (programCell.style.left || '').replace('%', ''); - left = left ? parseFloat(left) : 0; - var width = (programCell.style.width || '').replace('%', ''); - width = width ? parseFloat(width) : 0; - - if (left >= pct || (left + width) >= pct) { - break; - } - programCell = programCell.nextSibling; - } - - if (programCell) { - focusManager.focus(programCell); - } else { - focusManager.autoFocus(autoFocusParent, true); - } - } - } - - function nativeScrollTo(container, pos, horizontal) { - if (container.scrollTo) { - if (horizontal) { - container.scrollTo(pos, 0); - } else { - container.scrollTo(0, pos); - } - } else { - if (horizontal) { - container.scrollLeft = Math.round(pos); - } else { - container.scrollTop = Math.round(pos); - } - } - } - - var lastGridScroll = 0; - var lastHeaderScroll = 0; - var scrollXPct = 0; - function onProgramGridScroll(context, elem, timeslotHeaders) { - if ((new Date().getTime() - lastHeaderScroll) >= 1000) { - lastGridScroll = new Date().getTime(); - - var scrollLeft = elem.scrollLeft; - scrollXPct = (scrollLeft * 100) / elem.scrollWidth; - nativeScrollTo(timeslotHeaders, scrollLeft, true); - } - - updateProgramCellsOnScroll(elem, programCells); - } - - function onTimeslotHeadersScroll(context, elem) { - if ((new Date().getTime() - lastGridScroll) >= 1000) { - lastHeaderScroll = new Date().getTime(); - nativeScrollTo(programGrid, elem.scrollLeft, true); - } - } - - function changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - var newStartDate = normalizeDateToTimeslot(date); - currentDate = newStartDate; - - reloadGuide(page, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); - } - - function getDateTabText(date, isActive, tabIndex) { - var cssClass = isActive ? 'emby-tab-button guide-date-tab-button emby-tab-button-active' : 'emby-tab-button guide-date-tab-button'; - - var html = ''; - - return html; - } - - function setDateRange(page, guideInfo) { - var today = new Date(); - var nowHours = today.getHours(); - today.setHours(nowHours, 0, 0, 0); - - var start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true }); - var end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true }); - - start.setHours(nowHours, 0, 0, 0); - end.setHours(0, 0, 0, 0); - - if (start.getTime() >= end.getTime()) { - end.setDate(start.getDate() + 1); - } - - start = new Date(Math.max(today, start)); - - var dateTabsHtml = ''; - var tabIndex = 0; - - // TODO: Use date-fns - var date = new Date(); - - if (currentDate) { - date.setTime(currentDate.getTime()); - } - - date.setHours(nowHours, 0, 0, 0); - - var startTimeOfDayMs = (start.getHours() * 60 * 60 * 1000); - startTimeOfDayMs += start.getMinutes() * 60 * 1000; - - while (start <= end) { - var isActive = date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear(); - - dateTabsHtml += getDateTabText(start, isActive, tabIndex); - - start.setDate(start.getDate() + 1); - start.setHours(0, 0, 0, 0); - tabIndex++; - } - - page.querySelector('.emby-tabs-slider').innerHTML = dateTabsHtml; - page.querySelector('.guideDateTabs').refresh(); - - var newDate = new Date(); - var newDateHours = newDate.getHours(); - var scrollToTimeMs = newDateHours * 60 * 60 * 1000; - - var minutes = newDate.getMinutes(); - if (minutes >= 30) { - scrollToTimeMs += 30 * 60 * 1000; - } - - var focusToTimeMs = ((newDateHours * 60) + minutes) * 60 * 1000; - changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, layoutManager.tv); - } - - function reloadPage(page) { - showLoading(); - - var apiClient = connectionManager.getApiClient(options.serverId); - - apiClient.getLiveTvGuideInfo().then(function (guideInfo) { - setDateRange(page, guideInfo); - }); - } - - function getChannelProgramsFocusableElements(container) { - var elements = container.querySelectorAll('.programCell'); - - var list = []; - // add 1 to avoid programs that are out of view to the left - var currentScrollXPct = scrollXPct + 1; - - for (var i = 0, length = elements.length; i < length; i++) { - var elem = elements[i]; - - var left = (elem.style.left || '').replace('%', ''); - left = left ? parseFloat(left) : 0; - var width = (elem.style.width || '').replace('%', ''); - width = width ? parseFloat(width) : 0; - - if ((left + width) >= currentScrollXPct) { - list.push(elem); - } - } - - return list; - } - - function onInputCommand(e) { - var target = e.target; - var programCell = dom.parentWithClass(target, 'programCell'); - var container; - var channelPrograms; - var focusableElements; - var newRow; - - switch (e.detail.command) { - case 'up': - if (programCell) { - container = programGrid; - channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); - - newRow = channelPrograms.previousSibling; - if (newRow) { - focusableElements = getChannelProgramsFocusableElements(newRow); - if (focusableElements.length) { - container = newRow; - } else { - focusableElements = null; - } - } else { - container = null; - } - } else { - container = null; - } - lastFocusDirection = e.detail.command; - - focusManager.moveUp(target, { - container: container, - focusableElements: focusableElements - }); - break; - case 'down': - if (programCell) { - container = programGrid; - channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); - - newRow = channelPrograms.nextSibling; - if (newRow) { - focusableElements = getChannelProgramsFocusableElements(newRow); - if (focusableElements.length) { - container = newRow; - } else { - focusableElements = null; - } - } else { - container = null; - } - } else { - container = null; - } - lastFocusDirection = e.detail.command; - - focusManager.moveDown(target, { - container: container, - focusableElements: focusableElements - }); - break; - case 'left': - container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; - // allow left outside the channelProgramsContainer when the first child is currently focused - if (container && !programCell.previousSibling) { - container = null; - } - lastFocusDirection = e.detail.command; - - focusManager.moveLeft(target, { - container: container - }); - break; - case 'right': - container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; - lastFocusDirection = e.detail.command; - - focusManager.moveRight(target, { - container: container - }); - break; - default: - return; - } + isUpdatingProgramCellScroll = false; + }); +} + +function onProgramGridClick(e) { + if (!layoutManager.tv) { + return; + } + + const programCell = dom.parentWithClass(e.target, 'programCell'); + if (programCell) { + let startDate = programCell.getAttribute('data-startdate'); + let endDate = programCell.getAttribute('data-enddate'); + startDate = datetime.parseISO8601Date(startDate, { toLocal: true }).getTime(); + endDate = datetime.parseISO8601Date(endDate, { toLocal: true }).getTime(); + + const now = new Date().getTime(); + if (now >= startDate && now < endDate) { + const channelId = programCell.getAttribute('data-channelid'); + const serverId = programCell.getAttribute('data-serverid'); e.preventDefault(); e.stopPropagation(); - } - function onScrollerFocus(e) { - var target = e.target; - var programCell = dom.parentWithClass(target, 'programCell'); - - if (programCell) { - var focused = target; - - var id = focused.getAttribute('data-id'); - var item = items[id]; - - if (item) { - events.trigger(self, 'focus', [ - { - item: item - }]); - } - } - - if (lastFocusDirection === 'left') { - if (programCell) { - scrollHelper.toStart(programGrid, programCell, true, true); - } - } else if (lastFocusDirection === 'right') { - if (programCell) { - scrollHelper.toCenter(programGrid, programCell, true, true); - } - } else if (lastFocusDirection === 'up' || lastFocusDirection === 'down') { - var verticalScroller = dom.parentWithClass(target, 'guideVerticalScroller'); - if (verticalScroller) { - var focusedElement = programCell || dom.parentWithTag(target, 'BUTTON'); - verticalScroller.toCenter(focusedElement, true); - } - } - } - - function setScrollEvents(view, enabled) { - if (layoutManager.tv) { - var guideVerticalScroller = view.querySelector('.guideVerticalScroller'); - - if (enabled) { - inputManager.on(guideVerticalScroller, onInputCommand); - } else { - inputManager.off(guideVerticalScroller, onInputCommand); - } - } - } - - function onTimerCreated(e, apiClient, data) { - var programId = data.ProgramId; - // This could be null, not supported by all tv providers - var newTimerId = data.Id; - - // find guide cells by program id, ensure timer icon - var cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]'); - for (var i = 0, length = cells.length; i < length; i++) { - var cell = cells[i]; - - var icon = cell.querySelector('.timerIcon'); - if (!icon) { - cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); - } - - if (newTimerId) { - cell.setAttribute('data-timerid', newTimerId); - } - } - } - - function onSeriesTimerCreated(e, apiClient, data) { - } - - function onTimerCancelled(e, apiClient, data) { - var id = data.Id; - // find guide cells by timer id, remove timer icon - var cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]'); - for (var i = 0, length = cells.length; i < length; i++) { - var cell = cells[i]; - var icon = cell.querySelector('.timerIcon'); - if (icon) { - icon.parentNode.removeChild(icon); - } - cell.removeAttribute('data-timerid'); - } - } - - function onSeriesTimerCancelled(e, apiClient, data) { - var id = data.Id; - // find guide cells by timer id, remove timer icon - var cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]'); - for (var i = 0, length = cells.length; i < length; i++) { - var cell = cells[i]; - var icon = cell.querySelector('.seriesTimerIcon'); - if (icon) { - icon.parentNode.removeChild(icon); - } - cell.removeAttribute('data-seriestimerid'); - } - } - - require(['text!./tvguide.template.html'], function (template) { - var context = options.element; - - context.classList.add('tvguide'); - - context.innerHTML = globalize.translateHtml(template, 'core'); - - programGrid = context.querySelector('.programGrid'); - var timeslotHeaders = context.querySelector('.timeslotHeaders'); - - if (layoutManager.tv) { - dom.addEventListener(context.querySelector('.guideVerticalScroller'), 'focus', onScrollerFocus, { - capture: true, - passive: true - }); - } else if (layoutManager.desktop) { - timeslotHeaders.classList.add('timeslotHeaders-desktop'); - } - - if (browser.iOS || browser.osx) { - context.querySelector('.channelsContainer').classList.add('noRubberBanding'); - - programGrid.classList.add('noRubberBanding'); - } - - dom.addEventListener(programGrid, 'scroll', function (e) { - onProgramGridScroll(context, this, timeslotHeaders); - }, { - passive: true + playbackManager.play({ + ids: [channelId], + serverId: serverId }); + } + } +} - dom.addEventListener(timeslotHeaders, 'scroll', function () { - onTimeslotHeadersScroll(context, this); - }, { - passive: true - }); +function Guide(options) { + const self = this; + let items = {}; - programGrid.addEventListener('click', onProgramGridClick); + self.options = options; + self.categoryOptions = { categories: [] }; - context.querySelector('.btnNextPage').addEventListener('click', function () { - currentStartIndex += currentChannelLimit; - reloadPage(context); - restartAutoRefresh(); - }); + // 30 mins + const cellCurationMinutes = 30; + const cellDurationMs = cellCurationMinutes * 60 * 1000; + const msPerDay = 86400000; - context.querySelector('.btnPreviousPage').addEventListener('click', function () { - currentStartIndex = Math.max(currentStartIndex - currentChannelLimit, 0); - reloadPage(context); - restartAutoRefresh(); - }); + let currentDate; + let currentStartIndex = 0; + let currentChannelLimit = 0; + let autoRefreshInterval; + let programCells; + let lastFocusDirection; + let programGrid; - context.querySelector('.btnGuideViewSettings').addEventListener('click', function () { - showViewSettings(self); - restartAutoRefresh(); - }); + self.refresh = function () { + currentDate = null; + reloadPage(options.element); + restartAutoRefresh(); + }; - context.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) { - var allTabButtons = e.target.querySelectorAll('.guide-date-tab-button'); - - var tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)]; - if (tabButton) { - var previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)]; - - var date = new Date(); - date.setTime(parseInt(tabButton.getAttribute('data-date'))); - - var scrollWidth = programGrid.scrollWidth; - var scrollToTimeMs; - if (scrollWidth) { - scrollToTimeMs = (programGrid.scrollLeft / scrollWidth) * msPerDay; - } else { - scrollToTimeMs = 0; - } - - if (previousButton) { - var previousDate = new Date(); - previousDate.setTime(parseInt(previousButton.getAttribute('data-date'))); - - scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000); - scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000); - } - - var startTimeOfDayMs = (date.getHours() * 60 * 60 * 1000); - startTimeOfDayMs += (date.getMinutes() * 60 * 1000); - - changeDate(context, date, scrollToTimeMs, scrollToTimeMs, startTimeOfDayMs, false); - } - }); - - setScrollEvents(context, true); - itemShortcuts.on(context); - - events.trigger(self, 'load'); - - events.on(serverNotifications, 'TimerCreated', onTimerCreated); - events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + self.pause = function () { + stopAutoRefresh(); + }; + self.resume = function (refreshData) { + if (refreshData) { self.refresh(); + } else { + restartAutoRefresh(); + } + }; + + self.destroy = function () { + stopAutoRefresh(); + + events.off(serverNotifications, 'TimerCreated', onTimerCreated); + events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); + events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + + setScrollEvents(options.element, false); + itemShortcuts.off(options.element); + items = {}; + }; + + function restartAutoRefresh() { + stopAutoRefresh(); + + const intervalMs = 60000 * 15; // (minutes) + + autoRefreshInterval = setInterval(function () { + self.refresh(); + }, intervalMs); + } + + function stopAutoRefresh() { + if (autoRefreshInterval) { + clearInterval(autoRefreshInterval); + autoRefreshInterval = null; + } + } + + function normalizeDateToTimeslot(date) { + const minutesOffset = date.getMinutes() - cellCurationMinutes; + + if (minutesOffset >= 0) { + date.setHours(date.getHours(), cellCurationMinutes, 0, 0); + } else { + date.setHours(date.getHours(), 0, 0, 0); + } + + return date; + } + + function showLoading() { + loading.show(); + } + + function hideLoading() { + loading.hide(); + } + + function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { + const apiClient = connectionManager.getApiClient(options.serverId); + + const channelQuery = { + + StartIndex: 0, + EnableFavoriteSorting: userSettings.get('livetv-favoritechannelsattop') !== 'false' + }; + + channelQuery.UserId = apiClient.getCurrentUserId(); + + const channelLimit = 500; + currentChannelLimit = channelLimit; + + showLoading(); + + channelQuery.StartIndex = currentStartIndex; + channelQuery.Limit = channelLimit; + channelQuery.AddCurrentProgram = false; + channelQuery.EnableUserData = false; + channelQuery.EnableImageTypes = 'Primary'; + + const categories = self.categoryOptions.categories || []; + const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; + const displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; + const displayNewsContent = !categories.length || categories.indexOf('news') !== -1; + const displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; + const displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; + + if (displayMovieContent && displaySportsContent && displayNewsContent && displayKidsContent) { + channelQuery.IsMovie = null; + channelQuery.IsSports = null; + channelQuery.IsKids = null; + channelQuery.IsNews = null; + channelQuery.IsSeries = null; + } else { + if (displayNewsContent) { + channelQuery.IsNews = true; + } + if (displaySportsContent) { + channelQuery.IsSports = true; + } + if (displayKidsContent) { + channelQuery.IsKids = true; + } + if (displayMovieContent) { + channelQuery.IsMovie = true; + } + if (displaySeriesContent) { + channelQuery.IsSeries = true; + } + } + + if (userSettings.get('livetv-channelorder') === 'DatePlayed') { + channelQuery.SortBy = 'DatePlayed'; + channelQuery.SortOrder = 'Descending'; + } else { + channelQuery.SortBy = null; + channelQuery.SortOrder = null; + } + + let date = newStartDate; + // Add one second to avoid getting programs that are just ending + date = new Date(date.getTime() + 1000); + + // Subtract to avoid getting programs that are starting when the grid ends + const nextDay = new Date(date.getTime() + msPerDay - 2000); + + // Normally we'd want to just let responsive css handle this, + // but since mobile browsers are often underpowered, + // it can help performance to get them out of the markup + const allowIndicators = dom.getWindowSize().innerWidth >= 600; + + const renderOptions = { + showHdIcon: allowIndicators && userSettings.get('guide-indicator-hd') === 'true', + showLiveIndicator: allowIndicators && userSettings.get('guide-indicator-live') !== 'false', + showPremiereIndicator: allowIndicators && userSettings.get('guide-indicator-premiere') !== 'false', + showNewIndicator: allowIndicators && userSettings.get('guide-indicator-new') !== 'false', + showRepeatIndicator: allowIndicators && userSettings.get('guide-indicator-repeat') === 'true', + showEpisodeTitle: layoutManager.tv ? false : true + }; + + apiClient.getLiveTvChannels(channelQuery).then(function (channelsResult) { + const btnPreviousPage = context.querySelector('.btnPreviousPage'); + const btnNextPage = context.querySelector('.btnNextPage'); + + if (channelsResult.TotalRecordCount > channelLimit) { + context.querySelector('.guideOptions').classList.remove('hide'); + + btnPreviousPage.classList.remove('hide'); + btnNextPage.classList.remove('hide'); + + if (channelQuery.StartIndex) { + context.querySelector('.btnPreviousPage').disabled = false; + } else { + context.querySelector('.btnPreviousPage').disabled = true; + } + + if ((channelQuery.StartIndex + channelLimit) < channelsResult.TotalRecordCount) { + btnNextPage.disabled = false; + } else { + btnNextPage.disabled = true; + } + } else { + context.querySelector('.guideOptions').classList.add('hide'); + } + + const programFields = []; + + const programQuery = { + UserId: apiClient.getCurrentUserId(), + MaxStartDate: nextDay.toISOString(), + MinEndDate: date.toISOString(), + channelIds: channelsResult.Items.map(function (c) { + return c.Id; + }).join(','), + ImageTypeLimit: 1, + EnableImages: false, + //EnableImageTypes: layoutManager.tv ? "Primary,Backdrop" : "Primary", + SortBy: 'StartDate', + EnableTotalRecordCount: false, + EnableUserData: false + }; + + if (renderOptions.showHdIcon) { + programFields.push('IsHD'); + } + + if (programFields.length) { + programQuery.Fields = programFields.join(''); + } + + apiClient.getLiveTvPrograms(programQuery).then(function (programsResult) { + renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); + + hideLoading(); + }); }); } - return Guide; -}); + function getDisplayTime(date) { + if ((typeof date).toString().toLowerCase() === 'string') { + try { + date = datetime.parseISO8601Date(date, { toLocal: true }); + } catch (err) { + return date; + } + } + + return datetime.getDisplayTime(date).toLowerCase(); + } + + function getTimeslotHeadersHtml(startDate, endDateTime) { + let html = ''; + + // clone + startDate = new Date(startDate.getTime()); + + html += '
'; + + while (startDate.getTime() < endDateTime) { + html += '
'; + + html += getDisplayTime(startDate); + html += '
'; + + // Add 30 mins + startDate.setTime(startDate.getTime() + cellDurationMs); + } + + return html; + } + + function parseDates(program) { + if (!program.StartDateLocal) { + try { + program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); + } catch (err) { + console.error('error parsing timestamp for start date'); + } + } + + if (!program.EndDateLocal) { + try { + program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { toLocal: true }); + } catch (err) { + console.error('error parsing timestamp for end date'); + } + } + + return null; + } + + function getTimerIndicator(item) { + let status; + + if (item.Type === 'SeriesTimer') { + return ''; + } else if (item.TimerId || item.SeriesTimerId) { + status = item.Status || 'Cancelled'; + } else if (item.Type === 'Timer') { + status = item.Status; + } else { + return ''; + } + + if (item.SeriesTimerId) { + if (status !== 'Cancelled') { + return ''; + } + + return ''; + } + + return ''; + } + + function getChannelProgramsHtml(context, date, channel, programs, options, listInfo) { + let html = ''; + + const startMs = date.getTime(); + const endMs = startMs + msPerDay - 1; + + const outerCssClass = layoutManager.tv ? 'channelPrograms channelPrograms-tv' : 'channelPrograms'; + + html += '
'; + + const clickAction = layoutManager.tv ? 'link' : 'programdialog'; + + const categories = self.categoryOptions.categories || []; + const displayMovieContent = !categories.length || categories.indexOf('movies') !== -1; + const displaySportsContent = !categories.length || categories.indexOf('sports') !== -1; + const displayNewsContent = !categories.length || categories.indexOf('news') !== -1; + const displayKidsContent = !categories.length || categories.indexOf('kids') !== -1; + const displaySeriesContent = !categories.length || categories.indexOf('series') !== -1; + const enableColorCodedBackgrounds = userSettings.get('guide-colorcodedbackgrounds') === 'true'; + + let programsFound; + const now = new Date().getTime(); + + for (let i = listInfo.startIndex, length = programs.length; i < length; i++) { + const program = programs[i]; + + if (program.ChannelId !== channel.Id) { + if (programsFound) { + break; + } + + continue; + } + + programsFound = true; + listInfo.startIndex++; + + parseDates(program); + + const startDateLocalMs = program.StartDateLocal.getTime(); + const endDateLocalMs = program.EndDateLocal.getTime(); + + if (endDateLocalMs < startMs) { + continue; + } + + if (startDateLocalMs > endMs) { + break; + } + + items[program.Id] = program; + + const renderStartMs = Math.max(startDateLocalMs, startMs); + let startPercent = (startDateLocalMs - startMs) / msPerDay; + startPercent *= 100; + startPercent = Math.max(startPercent, 0); + + const renderEndMs = Math.min(endDateLocalMs, endMs); + let endPercent = (renderEndMs - renderStartMs) / msPerDay; + endPercent *= 100; + + let cssClass = 'programCell itemAction'; + let accentCssClass = null; + let displayInnerContent = true; + + if (program.IsKids) { + displayInnerContent = displayKidsContent; + accentCssClass = 'kids'; + } else if (program.IsSports) { + displayInnerContent = displaySportsContent; + accentCssClass = 'sports'; + } else if (program.IsNews) { + displayInnerContent = displayNewsContent; + accentCssClass = 'news'; + } else if (program.IsMovie) { + displayInnerContent = displayMovieContent; + accentCssClass = 'movie'; + } else if (program.IsSeries) { + displayInnerContent = displaySeriesContent; + } else { + displayInnerContent = displayMovieContent && displayNewsContent && displaySportsContent && displayKidsContent && displaySeriesContent; + } + + if (displayInnerContent && enableColorCodedBackgrounds && accentCssClass) { + cssClass += ' programCell-' + accentCssClass; + } + + if (now >= startDateLocalMs && now < endDateLocalMs) { + cssClass += ' programCell-active'; + } + + let timerAttributes = ''; + if (program.TimerId) { + timerAttributes += ' data-timerid="' + program.TimerId + '"'; + } + if (program.SeriesTimerId) { + timerAttributes += ' data-seriestimerid="' + program.SeriesTimerId + '"'; + } + + const isAttribute = endPercent >= 2 ? ' is="emby-programcell"' : ''; + + html += ''; + + if (displayInnerContent) { + const guideProgramNameClass = 'guideProgramName'; + + html += '
'; + + html += '
'; + + html += '
' + program.Name; + + let indicatorHtml = null; + if (program.IsLive && options.showLiveIndicator) { + indicatorHtml = '' + globalize.translate('Live') + ''; + } else if (program.IsPremiere && options.showPremiereIndicator) { + indicatorHtml = '' + globalize.translate('Premiere') + ''; + } else if (program.IsSeries && !program.IsRepeat && options.showNewIndicator) { + indicatorHtml = '' + globalize.translate('AttributeNew') + ''; + } else if (program.IsSeries && program.IsRepeat && options.showRepeatIndicator) { + indicatorHtml = '' + globalize.translate('Repeat') + ''; + } + html += indicatorHtml || ''; + + if ((program.EpisodeTitle && options.showEpisodeTitle)) { + html += '
'; + + if (program.EpisodeTitle && options.showEpisodeTitle) { + html += '' + program.EpisodeTitle + ''; + } + html += '
'; + } + + html += '
'; + + if (program.IsHD && options.showHdIcon) { + if (layoutManager.tv) { + html += '
HD
'; + } else { + html += '
HD
'; + } + } + + html += getTimerIndicator(program); + + html += '
'; + } + + html += ''; + } + + html += '
'; + + return html; + } + + function renderChannelHeaders(context, channels, apiClient) { + let html = ''; + + for (let i = 0, length = channels.length; i < length; i++) { + const channel = channels[i]; + const hasChannelImage = channel.ImageTags.Primary; + + let cssClass = 'guide-channelHeaderCell itemAction'; + + if (layoutManager.tv) { + cssClass += ' guide-channelHeaderCell-tv'; + } + + const title = []; + if (channel.ChannelNumber) { + title.push(channel.ChannelNumber); + } + if (channel.Name) { + title.push(channel.Name); + } + + html += ''; + } + + const channelList = context.querySelector('.channelsContainer'); + channelList.innerHTML = html; + imageLoader.lazyChildren(channelList); + } + + function renderPrograms(context, date, channels, programs, options) { + const listInfo = { + startIndex: 0 + }; + + const html = []; + + for (let i = 0, length = channels.length; i < length; i++) { + html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo)); + } + + programGrid.innerHTML = html.join(''); + + programCells = programGrid.querySelectorAll('[is=emby-programcell]'); + + updateProgramCellsOnScroll(programGrid, programCells); + } + + function getProgramSortOrder(program, channels) { + const channelId = program.ChannelId; + let channelIndex = -1; + + for (let i = 0, length = channels.length; i < length; i++) { + if (channelId === channels[i].Id) { + channelIndex = i; + break; + } + } + + const start = datetime.parseISO8601Date(program.StartDate, { toLocal: true }); + + return (channelIndex * 10000000) + (start.getTime() / 60000); + } + + function renderGuide(context, date, channels, programs, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { + programs.sort(function (a, b) { + return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels); + }); + + const activeElement = document.activeElement; + const itemId = activeElement && activeElement.getAttribute ? activeElement.getAttribute('data-id') : null; + let channelRowId = null; + + if (activeElement) { + channelRowId = dom.parentWithClass(activeElement, 'channelPrograms'); + channelRowId = channelRowId && channelRowId.getAttribute ? channelRowId.getAttribute('data-channelid') : null; + } + + renderChannelHeaders(context, channels, apiClient); + + const startDate = date; + const endDate = new Date(startDate.getTime() + msPerDay); + context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate); + items = {}; + renderPrograms(context, date, channels, programs, renderOptions); + + if (focusProgramOnRender) { + focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs); + } + + scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs); + } + + function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) { + scrollToTimeMs -= startTimeOfDayMs; + + const pct = scrollToTimeMs / msPerDay; + + programGrid.scrollTop = 0; + + const scrollPos = pct * programGrid.scrollWidth; + + nativeScrollTo(programGrid, scrollPos, true); + } + + function focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs) { + let focusElem; + if (itemId) { + focusElem = context.querySelector('[data-id="' + itemId + '"]'); + } + + if (focusElem) { + focusManager.focus(focusElem); + } else { + let autoFocusParent; + + if (channelRowId) { + autoFocusParent = context.querySelector('[data-channelid="' + channelRowId + '"]'); + } + + if (!autoFocusParent) { + autoFocusParent = programGrid; + } + + focusToTimeMs -= startTimeOfDayMs; + + const pct = (focusToTimeMs / msPerDay) * 100; + + let programCell = autoFocusParent.querySelector('.programCell'); + + while (programCell) { + let left = (programCell.style.left || '').replace('%', ''); + left = left ? parseFloat(left) : 0; + let width = (programCell.style.width || '').replace('%', ''); + width = width ? parseFloat(width) : 0; + + if (left >= pct || (left + width) >= pct) { + break; + } + programCell = programCell.nextSibling; + } + + if (programCell) { + focusManager.focus(programCell); + } else { + focusManager.autoFocus(autoFocusParent, true); + } + } + } + + function nativeScrollTo(container, pos, horizontal) { + if (container.scrollTo) { + if (horizontal) { + container.scrollTo(pos, 0); + } else { + container.scrollTo(0, pos); + } + } else { + if (horizontal) { + container.scrollLeft = Math.round(pos); + } else { + container.scrollTop = Math.round(pos); + } + } + } + + let lastGridScroll = 0; + let lastHeaderScroll = 0; + let scrollXPct = 0; + function onProgramGridScroll(context, elem, timeslotHeaders) { + if ((new Date().getTime() - lastHeaderScroll) >= 1000) { + lastGridScroll = new Date().getTime(); + + const scrollLeft = elem.scrollLeft; + scrollXPct = (scrollLeft * 100) / elem.scrollWidth; + nativeScrollTo(timeslotHeaders, scrollLeft, true); + } + + updateProgramCellsOnScroll(elem, programCells); + } + + function onTimeslotHeadersScroll(context, elem) { + if ((new Date().getTime() - lastGridScroll) >= 1000) { + lastHeaderScroll = new Date().getTime(); + nativeScrollTo(programGrid, elem.scrollLeft, true); + } + } + + function changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { + const newStartDate = normalizeDateToTimeslot(date); + currentDate = newStartDate; + + reloadGuide(page, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender); + } + + function getDateTabText(date, isActive, tabIndex) { + const cssClass = isActive ? 'emby-tab-button guide-date-tab-button emby-tab-button-active' : 'emby-tab-button guide-date-tab-button'; + + let html = ''; + + return html; + } + + function setDateRange(page, guideInfo) { + const today = new Date(); + const nowHours = today.getHours(); + today.setHours(nowHours, 0, 0, 0); + + let start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true }); + const end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true }); + + start.setHours(nowHours, 0, 0, 0); + end.setHours(0, 0, 0, 0); + + if (start.getTime() >= end.getTime()) { + end.setDate(start.getDate() + 1); + } + + start = new Date(Math.max(today, start)); + + let dateTabsHtml = ''; + let tabIndex = 0; + + // TODO: Use date-fns + const date = new Date(); + + if (currentDate) { + date.setTime(currentDate.getTime()); + } + + date.setHours(nowHours, 0, 0, 0); + + let startTimeOfDayMs = (start.getHours() * 60 * 60 * 1000); + startTimeOfDayMs += start.getMinutes() * 60 * 1000; + + while (start <= end) { + const isActive = date.getDate() === start.getDate() && date.getMonth() === start.getMonth() && date.getFullYear() === start.getFullYear(); + + dateTabsHtml += getDateTabText(start, isActive, tabIndex); + + start.setDate(start.getDate() + 1); + start.setHours(0, 0, 0, 0); + tabIndex++; + } + + page.querySelector('.emby-tabs-slider').innerHTML = dateTabsHtml; + page.querySelector('.guideDateTabs').refresh(); + + const newDate = new Date(); + const newDateHours = newDate.getHours(); + let scrollToTimeMs = newDateHours * 60 * 60 * 1000; + + const minutes = newDate.getMinutes(); + if (minutes >= 30) { + scrollToTimeMs += 30 * 60 * 1000; + } + + const focusToTimeMs = ((newDateHours * 60) + minutes) * 60 * 1000; + changeDate(page, date, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, layoutManager.tv); + } + + function reloadPage(page) { + showLoading(); + + const apiClient = connectionManager.getApiClient(options.serverId); + + apiClient.getLiveTvGuideInfo().then(function (guideInfo) { + setDateRange(page, guideInfo); + }); + } + + function getChannelProgramsFocusableElements(container) { + const elements = container.querySelectorAll('.programCell'); + + const list = []; + // add 1 to avoid programs that are out of view to the left + const currentScrollXPct = scrollXPct + 1; + + for (let i = 0, length = elements.length; i < length; i++) { + const elem = elements[i]; + + let left = (elem.style.left || '').replace('%', ''); + left = left ? parseFloat(left) : 0; + let width = (elem.style.width || '').replace('%', ''); + width = width ? parseFloat(width) : 0; + + if ((left + width) >= currentScrollXPct) { + list.push(elem); + } + } + + return list; + } + + function onInputCommand(e) { + const target = e.target; + const programCell = dom.parentWithClass(target, 'programCell'); + let container; + let channelPrograms; + let focusableElements; + let newRow; + + switch (e.detail.command) { + case 'up': + if (programCell) { + container = programGrid; + channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); + + newRow = channelPrograms.previousSibling; + if (newRow) { + focusableElements = getChannelProgramsFocusableElements(newRow); + if (focusableElements.length) { + container = newRow; + } else { + focusableElements = null; + } + } else { + container = null; + } + } else { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveUp(target, { + container: container, + focusableElements: focusableElements + }); + break; + case 'down': + if (programCell) { + container = programGrid; + channelPrograms = dom.parentWithClass(programCell, 'channelPrograms'); + + newRow = channelPrograms.nextSibling; + if (newRow) { + focusableElements = getChannelProgramsFocusableElements(newRow); + if (focusableElements.length) { + container = newRow; + } else { + focusableElements = null; + } + } else { + container = null; + } + } else { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveDown(target, { + container: container, + focusableElements: focusableElements + }); + break; + case 'left': + container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; + // allow left outside the channelProgramsContainer when the first child is currently focused + if (container && !programCell.previousSibling) { + container = null; + } + lastFocusDirection = e.detail.command; + + focusManager.moveLeft(target, { + container: container + }); + break; + case 'right': + container = programCell ? dom.parentWithClass(programCell, 'channelPrograms') : null; + lastFocusDirection = e.detail.command; + + focusManager.moveRight(target, { + container: container + }); + break; + default: + return; + } + + e.preventDefault(); + e.stopPropagation(); + } + + function onScrollerFocus(e) { + const target = e.target; + const programCell = dom.parentWithClass(target, 'programCell'); + + if (programCell) { + const focused = target; + + const id = focused.getAttribute('data-id'); + const item = items[id]; + + if (item) { + events.trigger(self, 'focus', [ + { + item: item + }]); + } + } + + if (lastFocusDirection === 'left') { + if (programCell) { + scrollHelper.toStart(programGrid, programCell, true, true); + } + } else if (lastFocusDirection === 'right') { + if (programCell) { + scrollHelper.toCenter(programGrid, programCell, true, true); + } + } else if (lastFocusDirection === 'up' || lastFocusDirection === 'down') { + const verticalScroller = dom.parentWithClass(target, 'guideVerticalScroller'); + if (verticalScroller) { + const focusedElement = programCell || dom.parentWithTag(target, 'BUTTON'); + verticalScroller.toCenter(focusedElement, true); + } + } + } + + function setScrollEvents(view, enabled) { + if (layoutManager.tv) { + const guideVerticalScroller = view.querySelector('.guideVerticalScroller'); + + if (enabled) { + inputManager.on(guideVerticalScroller, onInputCommand); + } else { + inputManager.off(guideVerticalScroller, onInputCommand); + } + } + } + + function onTimerCreated(e, apiClient, data) { + const programId = data.ProgramId; + // This could be null, not supported by all tv providers + const newTimerId = data.Id; + + // find guide cells by program id, ensure timer icon + const cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]'); + for (let i = 0, length = cells.length; i < length; i++) { + const cell = cells[i]; + + const icon = cell.querySelector('.timerIcon'); + if (!icon) { + cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); + } + + if (newTimerId) { + cell.setAttribute('data-timerid', newTimerId); + } + } + } + + function onSeriesTimerCreated(e, apiClient, data) { + } + + function onTimerCancelled(e, apiClient, data) { + const id = data.Id; + // find guide cells by timer id, remove timer icon + const cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]'); + for (let i = 0, length = cells.length; i < length; i++) { + const cell = cells[i]; + const icon = cell.querySelector('.timerIcon'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-timerid'); + } + } + + function onSeriesTimerCancelled(e, apiClient, data) { + const id = data.Id; + // find guide cells by timer id, remove timer icon + const cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]'); + for (let i = 0, length = cells.length; i < length; i++) { + const cell = cells[i]; + const icon = cell.querySelector('.seriesTimerIcon'); + if (icon) { + icon.parentNode.removeChild(icon); + } + cell.removeAttribute('data-seriestimerid'); + } + } + + import('text!./tvguide.template.html').then(({default: template}) => { + const context = options.element; + + context.classList.add('tvguide'); + + context.innerHTML = globalize.translateHtml(template, 'core'); + + programGrid = context.querySelector('.programGrid'); + const timeslotHeaders = context.querySelector('.timeslotHeaders'); + + if (layoutManager.tv) { + dom.addEventListener(context.querySelector('.guideVerticalScroller'), 'focus', onScrollerFocus, { + capture: true, + passive: true + }); + } else if (layoutManager.desktop) { + timeslotHeaders.classList.add('timeslotHeaders-desktop'); + } + + if (browser.iOS || browser.osx) { + context.querySelector('.channelsContainer').classList.add('noRubberBanding'); + + programGrid.classList.add('noRubberBanding'); + } + + dom.addEventListener(programGrid, 'scroll', function (e) { + onProgramGridScroll(context, this, timeslotHeaders); + }, { + passive: true + }); + + dom.addEventListener(timeslotHeaders, 'scroll', function () { + onTimeslotHeadersScroll(context, this); + }, { + passive: true + }); + + programGrid.addEventListener('click', onProgramGridClick); + + context.querySelector('.btnNextPage').addEventListener('click', function () { + currentStartIndex += currentChannelLimit; + reloadPage(context); + restartAutoRefresh(); + }); + + context.querySelector('.btnPreviousPage').addEventListener('click', function () { + currentStartIndex = Math.max(currentStartIndex - currentChannelLimit, 0); + reloadPage(context); + restartAutoRefresh(); + }); + + context.querySelector('.btnGuideViewSettings').addEventListener('click', function () { + showViewSettings(self); + restartAutoRefresh(); + }); + + context.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) { + const allTabButtons = e.target.querySelectorAll('.guide-date-tab-button'); + + const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)]; + if (tabButton) { + const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)]; + + const date = new Date(); + date.setTime(parseInt(tabButton.getAttribute('data-date'))); + + const scrollWidth = programGrid.scrollWidth; + let scrollToTimeMs; + if (scrollWidth) { + scrollToTimeMs = (programGrid.scrollLeft / scrollWidth) * msPerDay; + } else { + scrollToTimeMs = 0; + } + + if (previousButton) { + const previousDate = new Date(); + previousDate.setTime(parseInt(previousButton.getAttribute('data-date'))); + + scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000); + scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000); + } + + let startTimeOfDayMs = (date.getHours() * 60 * 60 * 1000); + startTimeOfDayMs += (date.getMinutes() * 60 * 1000); + + changeDate(context, date, scrollToTimeMs, scrollToTimeMs, startTimeOfDayMs, false); + } + }); + + setScrollEvents(context, true); + itemShortcuts.on(context); + + events.trigger(self, 'load'); + + events.on(serverNotifications, 'TimerCreated', onTimerCreated); + events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); + events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + + self.refresh(); + }); +} + +export default Guide; diff --git a/src/controllers/livetv/livetvguide.js b/src/controllers/livetv/livetvguide.js index ec7a7a3f81..f89c2693be 100644 --- a/src/controllers/livetv/livetvguide.js +++ b/src/controllers/livetv/livetvguide.js @@ -7,7 +7,7 @@ define(['tvguide'], function (tvguide) { self.renderTab = function () { if (!guideInstance) { - guideInstance = new tvguide({ + guideInstance = new tvguide.default({ element: tabContent, serverId: ApiClient.serverId() }); From 1b69c89b3119b8b4017d94b0ec048ff76a0ca1a1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 09:13:46 +0100 Subject: [PATCH 073/155] replace for with for...of --- src/components/guide/guide-settings.js | 48 ++++++++++++-------------- src/components/guide/guide.js | 32 ++++++++--------- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/components/guide/guide-settings.js b/src/components/guide/guide-settings.js index b62604ab98..35f0d3e06e 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -12,10 +12,10 @@ function saveCategories(context, options) { const categories = []; const chkCategorys = context.querySelectorAll('.chkCategory'); - for (let i = 0, length = chkCategorys.length; i < length; i++) { - const type = chkCategorys[i].getAttribute('data-type'); + for (const chkCategory of chkCategorys) { + const type = chkCategory.getAttribute('data-type'); - if (chkCategorys[i].checked) { + if (chkCategory.checked) { categories.push(type); } } @@ -33,47 +33,43 @@ function loadCategories(context, options) { const selectedCategories = options.categories || []; const chkCategorys = context.querySelectorAll('.chkCategory'); - for (let i = 0, length = chkCategorys.length; i < length; i++) { - const type = chkCategorys[i].getAttribute('data-type'); + for (const chkCategory of chkCategorys) { + const type = chkCategory.getAttribute('data-type'); - chkCategorys[i].checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1; + chkCategory.checked = !selectedCategories.length || selectedCategories.indexOf(type) !== -1; } } function save(context) { - let i; - let length; - const chkIndicators = context.querySelectorAll('.chkIndicator'); - for (i = 0, length = chkIndicators.length; i < length; i++) { - const type = chkIndicators[i].getAttribute('data-type'); - userSettings.set('guide-indicator-' + type, chkIndicators[i].checked); + + for (const chkIndicator of chkIndicators) { + const type = chkIndicator.getAttribute('data-type'); + userSettings.set('guide-indicator-' + type, chkIndicator.checked); } userSettings.set('guide-colorcodedbackgrounds', context.querySelector('.chkColorCodedBackgrounds').checked); userSettings.set('livetv-favoritechannelsattop', context.querySelector('.chkFavoriteChannelsAtTop').checked); const sortBys = context.querySelectorAll('.chkSortOrder'); - for (i = 0, length = sortBys.length; i < length; i++) { - if (sortBys[i].checked) { - userSettings.set('livetv-channelorder', sortBys[i].value); + for (const sortBy of sortBys) { + if (sortBy.checked) { + userSettings.set('livetv-channelorder', sortBy.value); break; } } } function load(context) { - let i; - let length; - const chkIndicators = context.querySelectorAll('.chkIndicator'); - for (i = 0, length = chkIndicators.length; i < length; i++) { - const type = chkIndicators[i].getAttribute('data-type'); - if (chkIndicators[i].getAttribute('data-default') === 'true') { - chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) !== 'false'; + for (const chkIndicator of chkIndicators) { + const type = chkIndicator.getAttribute('data-type'); + + if (chkIndicator.getAttribute('data-default') === 'true') { + chkIndicator.checked = userSettings.get('guide-indicator-' + type) !== 'false'; } else { - chkIndicators[i].checked = userSettings.get('guide-indicator-' + type) === 'true'; + chkIndicator.checked = userSettings.get('guide-indicator-' + type) === 'true'; } } @@ -83,8 +79,8 @@ function load(context) { const sortByValue = userSettings.get('livetv-channelorder') || 'Number'; const sortBys = context.querySelectorAll('.chkSortOrder'); - for (i = 0, length = sortBys.length; i < length; i++) { - sortBys[i].checked = sortBys[i].value === sortByValue; + for (const sortBy of sortBys) { + sortBy.checked = sortBy.value === sortByValue; } } @@ -92,7 +88,7 @@ function showEditor(options) { return new Promise(function (resolve, reject) { let settingsChanged = false; - import('text!./guide-settings.template.html').then(({default: template}) => { + import('text!./guide-settings.template.html').then(({ default: template }) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index e48d4646c0..a7b32d887d 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -88,8 +88,8 @@ function updateProgramCellsOnScroll(programGrid, programCells) { const scrollPct = scrollLeft ? (scrollLeft / programGrid.scrollWidth) * 100 : 0; - for (let i = 0, length = programCells.length; i < length; i++) { - updateProgramCellOnScroll(programCells[i], scrollPct); + for (const programCell of programCells) { + updateProgramCellOnScroll(programCell, scrollPct); } isUpdatingProgramCellScroll = false; @@ -588,8 +588,7 @@ function Guide(options) { function renderChannelHeaders(context, channels, apiClient) { let html = ''; - for (let i = 0, length = channels.length; i < length; i++) { - const channel = channels[i]; + for (const channel of channels) { const hasChannelImage = channel.ImageTags.Primary; let cssClass = 'guide-channelHeaderCell itemAction'; @@ -641,8 +640,8 @@ function Guide(options) { const html = []; - for (let i = 0, length = channels.length; i < length; i++) { - html.push(getChannelProgramsHtml(context, date, channels[i], programs, options, listInfo)); + for (const channel of channels) { + html.push(getChannelProgramsHtml(context, date, channel, programs, options, listInfo)); } programGrid.innerHTML = html.join(''); @@ -888,11 +887,10 @@ function Guide(options) { // add 1 to avoid programs that are out of view to the left const currentScrollXPct = scrollXPct + 1; - for (let i = 0, length = elements.length; i < length; i++) { - const elem = elements[i]; - + for (const elem of elements) { let left = (elem.style.left || '').replace('%', ''); left = left ? parseFloat(left) : 0; + let width = (elem.style.width || '').replace('%', ''); width = width ? parseFloat(width) : 0; @@ -1047,9 +1045,7 @@ function Guide(options) { // find guide cells by program id, ensure timer icon const cells = options.element.querySelectorAll('.programCell[data-id="' + programId + '"]'); - for (let i = 0, length = cells.length; i < length; i++) { - const cell = cells[i]; - + for (const cell of cells) { const icon = cell.querySelector('.timerIcon'); if (!icon) { cell.querySelector('.guideProgramName').insertAdjacentHTML('beforeend', ''); @@ -1068,12 +1064,14 @@ function Guide(options) { const id = data.Id; // find guide cells by timer id, remove timer icon const cells = options.element.querySelectorAll('.programCell[data-timerid="' + id + '"]'); - for (let i = 0, length = cells.length; i < length; i++) { - const cell = cells[i]; + + for (const cell of cells) { const icon = cell.querySelector('.timerIcon'); + if (icon) { icon.parentNode.removeChild(icon); } + cell.removeAttribute('data-timerid'); } } @@ -1082,12 +1080,14 @@ function Guide(options) { const id = data.Id; // find guide cells by timer id, remove timer icon const cells = options.element.querySelectorAll('.programCell[data-seriestimerid="' + id + '"]'); - for (let i = 0, length = cells.length; i < length; i++) { - const cell = cells[i]; + + for (const cell of cells) { const icon = cell.querySelector('.seriesTimerIcon'); + if (icon) { icon.parentNode.removeChild(icon); } + cell.removeAttribute('data-seriestimerid'); } } From 5bb37263fc8a76e9d7382bd4e8782a42fb04872f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2020 08:48:21 +0000 Subject: [PATCH 074/155] Bump gulp-terser from 1.3.0 to 1.3.2 Bumps [gulp-terser](https://github.com/duan602728596/gulp-terser) from 1.3.0 to 1.3.2. - [Release notes](https://github.com/duan602728596/gulp-terser/releases) - [Commits](https://github.com/duan602728596/gulp-terser/compare/v1.3.0...v1.3.2) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a014dc1b76..504f4038d9 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "gulp-postcss": "^8.0.0", "gulp-sass": "^4.0.2", "gulp-sourcemaps": "^2.6.5", - "gulp-terser": "^1.3.0", + "gulp-terser": "^1.3.2", "html-webpack-plugin": "^4.3.0", "lazypipe": "^1.0.2", "node-sass": "^4.13.1", diff --git a/yarn.lock b/yarn.lock index 5c8a7f7e14..3ec7f21487 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5262,11 +5262,12 @@ gulp-sourcemaps@^2.6.5: strip-bom-string "1.X" through2 "2.X" -gulp-terser@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-1.3.0.tgz#6423fdb7dd15cc376e28063b5271271a928084bd" - integrity sha512-EvizE1LJLfOh3/EmpJoq9iqYziObOkTzFgN4KvxfB0ICp3+W5H+MOO9B7Xq5Iuu9N+RKByNJLmqR+Ph13U1vtQ== +gulp-terser@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-1.3.2.tgz#c91a71c31de0c40a94330bfd43b20c2a40df9a6b" + integrity sha512-hLx9Ww9PX304R3E7pMyL+jFftH47QXQCgKE6IZajbz7OoqAPMdr+sTxBpaujgIVkdbnJwJ7AFhfrcyy/cjBgZQ== dependencies: + is-promise "^4.0.0" plugin-error "^1.0.1" terser ">=4" through2 "^4.0.2" @@ -6166,6 +6167,11 @@ is-promise@^2.1: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.0.5, is-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" From 2a822704d674bc32574505784819306ec36cd883 Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Wed, 5 Aug 2020 12:19:51 +0200 Subject: [PATCH 075/155] Use focusManager.default in non ES6 modules --- src/components/filtermenu/filtermenu.js | 3 +++ src/components/guide/guide.js | 1 + src/components/slideshow/slideshow.js | 1 + src/components/sortmenu/sortmenu.js | 2 ++ src/components/subtitleeditor/subtitleeditor.js | 1 + src/components/tunerPicker.js | 1 + src/components/viewManager/viewManager.js | 2 ++ src/controllers/hometab.js | 1 + src/controllers/list.js | 1 + src/libraries/scroller.js | 3 ++- src/scripts/scrollHelper.js | 2 ++ src/scripts/serverNotifications.js | 1 + 12 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 20399fb52d..bd9bcb3c56 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -1,4 +1,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', 'inputManager', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, appHost, inputManager, layoutManager, connectionManager, appRouter, globalize, userSettings) { + + focusManager = focusManager.default || focusManager; + 'use strict'; function onSubmit(e) { diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 61caa9188f..71d63f82c0 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; + focusManager = focusManager.default || focusManager; function showViewSettings(instance) { require(['guide-settings-dialog'], function (guideSettingsDialog) { diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 60c458e234..38728ec6c6 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; + focusManager = focusManager.default || focusManager; /** * Name of transition event. diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index f62e5bb3a4..52a7b95c40 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -1,6 +1,8 @@ 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'; + focusManager = focusManager.default || focusManager; + function onSubmit(e) { e.preventDefault(); return false; diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 7df24b5da8..c42658b2d5 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -2,6 +2,7 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', 'use strict'; loading = loading.default || loading; + focusManager = focusManager.default || focusManager; var currentItem; var hasChanges; diff --git a/src/components/tunerPicker.js b/src/components/tunerPicker.js index 5bc9386053..4e78030a2a 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; + focusManager = focusManager.default || focusManager; var enableFocusTransform = !browser.slow && !browser.edge; diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index 058ba4ebb2..55425994d2 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -1,6 +1,8 @@ define(['viewContainer', 'focusManager', 'queryString', 'layoutManager'], function (viewContainer, focusManager, queryString, layoutManager) { 'use strict'; + focusManager = focusManager.default || focusManager; + var currentView; var dispatchPageEvents; diff --git a/src/controllers/hometab.js b/src/controllers/hometab.js index 97034d4505..9610efbd39 100644 --- a/src/controllers/hometab.js +++ b/src/controllers/hometab.js @@ -2,6 +2,7 @@ define(['userSettings', 'loading', 'connectionManager', 'apphost', 'layoutManage 'use strict'; loading = loading.default || loading; + focusManager = focusManager.default || focusManager; function HomeTab(view, params) { this.view = view; diff --git a/src/controllers/list.js b/src/controllers/list.js index a35b7a0cf9..77830badaf 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -3,6 +3,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' playbackManager = playbackManager.default || playbackManager; loading = loading.default || loading; + focusManager = focusManager.default || focusManager; function getInitialLiveTvQuery(instance, params) { var query = { diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index d869d8c8be..cc75dcdeef 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -2,6 +2,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc 'use strict'; browser = browser.default || browser; + focusManager = focusManager.default || focusManager; /** * Return type of the value. @@ -697,7 +698,7 @@ define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'sc function onFrameClick(e) { if (e.which === 1) { - var focusableParent = focusManager.default.focusableParent(e.target); + var focusableParent = focusManager.focusableParent(e.target); if (focusableParent && focusableParent !== document.activeElement) { focusableParent.focus(); } diff --git a/src/scripts/scrollHelper.js b/src/scripts/scrollHelper.js index 82413fe9cf..8b596571b4 100644 --- a/src/scripts/scrollHelper.js +++ b/src/scripts/scrollHelper.js @@ -1,6 +1,8 @@ define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) { 'use strict'; + focusManager = focusManager.default || focusManager; + function getBoundingClientRect(elem) { // Support: BlackBerry 5, iOS 3 (original iPhone) // If we don't have gBCR, just use 0,0 rather than error diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index e5fb0bcd61..87db52402d 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -2,6 +2,7 @@ define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'in 'use strict'; playbackManager = playbackManager.default || playbackManager; + focusManager = focusManager.default || focusManager; var serverNotifications = {}; From 8a16793b971e960e9e0208450c7e00ff43f8e71c Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Wed, 5 Aug 2020 12:24:26 +0200 Subject: [PATCH 076/155] Note to self: lint first, commit later --- src/components/filtermenu/filtermenu.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index bd9bcb3c56..b02b5fb9f8 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -1,8 +1,6 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'apphost', 'inputManager', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'userSettings', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, appHost, inputManager, layoutManager, connectionManager, appRouter, globalize, userSettings) { - - focusManager = focusManager.default || focusManager; - 'use strict'; + focusManager = focusManager.default || focusManager; function onSubmit(e) { e.preventDefault(); From 53d654261344b9a614e29fac700cfb76795c3d62 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 11:48:31 +0100 Subject: [PATCH 077/155] Migration of recordinghelper and seriesrecordingeditor to ES6 modules --- package.json | 2 + .../recordingcreator/recordingbutton.js | 2 + .../recordingcreator/recordingeditor.js | 2 + .../recordingcreator/recordingfields.js | 1 + .../recordingcreator/recordinghelper.js | 337 +++++++------- .../recordingcreator/seriesrecordingeditor.js | 414 +++++++++--------- 6 files changed, 390 insertions(+), 368 deletions(-) diff --git a/package.json b/package.json index 8408217bf3..5277ea3a9e 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,8 @@ "src/components/playlisteditor/playlisteditor.js", "src/components/playmenu.js", "src/components/prompt/prompt.js", + "src/components/recordingcreator/seriesrecordingeditor.js", + "src/components/recordingcreator/recordinghelper.js", "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", diff --git a/src/components/recordingcreator/recordingbutton.js b/src/components/recordingcreator/recordingbutton.js index 3ad4ffa594..1207208e90 100644 --- a/src/components/recordingcreator/recordingbutton.js +++ b/src/components/recordingcreator/recordingbutton.js @@ -1,6 +1,8 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields'], function (globalize, connectionManager, require, loading, appHost, dom, recordingHelper, events) { 'use strict'; + recordingHelper = recordingHelper.default || recordingHelper; + function onRecordingButtonClick(e) { var item = this.item; diff --git a/src/components/recordingcreator/recordingeditor.js b/src/components/recordingcreator/recordingeditor.js index 2086129a9e..2e54b3601c 100644 --- a/src/components/recordingcreator/recordingeditor.js +++ b/src/components/recordingcreator/recordingeditor.js @@ -12,6 +12,8 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c function deleteTimer(apiClient, timerId) { return new Promise(function (resolve, reject) { require(['recordingHelper'], function (recordingHelper) { + recordingHelper = recordingHelper.default || recordingHelper; + recordingHelper.cancelTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject); }); }); diff --git a/src/components/recordingcreator/recordingfields.js b/src/components/recordingcreator/recordingfields.js index e3739f1cfe..741570581e 100644 --- a/src/components/recordingcreator/recordingfields.js +++ b/src/components/recordingcreator/recordingfields.js @@ -1,6 +1,7 @@ define(['globalize', 'connectionManager', 'serverNotifications', 'require', 'loading', 'apphost', 'dom', 'recordingHelper', 'events', 'paper-icon-button-light', 'emby-button', 'css!./recordingfields', 'flexStyles'], function (globalize, connectionManager, serverNotifications, require, loading, appHost, dom, recordingHelper, events) { 'use strict'; + recordingHelper = recordingHelper.default || recordingHelper; loading = loading.default || loading; function loadData(parent, program, apiClient) { diff --git a/src/components/recordingcreator/recordinghelper.js b/src/components/recordingcreator/recordinghelper.js index 5d72394282..13359de2bc 100644 --- a/src/components/recordingcreator/recordinghelper.js +++ b/src/components/recordingcreator/recordinghelper.js @@ -1,194 +1,195 @@ -define(['globalize', 'loading', 'connectionManager'], function (globalize, loading, connectionManager) { - 'use strict'; +import globalize from 'globalize'; +import loading from 'loading'; +import connectionManager from 'connectionManager'; - loading = loading.default || loading; +/*eslint prefer-const: "error"*/ - function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) { - loading.show(); +function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) { + loading.show(); - return apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function (item) { - if (item.IsSeries) { - // create series - return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (timerDefaults) { - return apiClient.createLiveTvSeriesTimer(timerDefaults).then(function () { - loading.hide(); - sendToast(globalize.translate('SeriesRecordingScheduled')); + return apiClient.getItem(apiClient.getCurrentUserId(), programId).then(function (item) { + if (item.IsSeries) { + // create series + return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (timerDefaults) { + return apiClient.createLiveTvSeriesTimer(timerDefaults).then(function () { + loading.hide(); + sendToast(globalize.translate('SeriesRecordingScheduled')); + }); + }); + } else { + // cancel + if (confirmTimerCancellation) { + return cancelTimerWithConfirmation(timerId, apiClient.serverId()); + } + + return cancelTimer(apiClient.serverId(), timerId, true); + } + }); +} + +function cancelTimerWithConfirmation(timerId, serverId) { + return new Promise(function (resolve, reject) { + import('confirm').then(({ default: confirm }) => { + confirm.default({ + + text: globalize.translate('MessageConfirmRecordingCancellation'), + primary: 'delete', + confirmText: globalize.translate('HeaderCancelRecording'), + cancelText: globalize.translate('HeaderKeepRecording') + + }).then(function () { + loading.show(); + + const apiClient = connectionManager.getApiClient(serverId); + cancelTimer(apiClient, timerId, true).then(resolve, reject); + }, reject); + }); + }); +} + +function cancelSeriesTimerWithConfirmation(timerId, serverId) { + return new Promise(function (resolve, reject) { + import('confirm').then(({ default: confirm }) => { + confirm.default({ + + text: globalize.translate('MessageConfirmRecordingCancellation'), + primary: 'delete', + confirmText: globalize.translate('HeaderCancelSeries'), + cancelText: globalize.translate('HeaderKeepSeries') + + }).then(function () { + loading.show(); + + const apiClient = connectionManager.getApiClient(serverId); + apiClient.cancelLiveTvSeriesTimer(timerId).then(function () { + import('toast').then(({default: toast}) => { + toast(globalize.translate('SeriesCancelled')); }); + + loading.hide(); + resolve(); + }, reject); + }, reject); + }); + }); +} + +function cancelTimer(apiClient, timerId, hideLoading) { + loading.show(); + return apiClient.cancelLiveTvTimer(timerId).then(function () { + if (hideLoading !== false) { + loading.hide(); + sendToast(globalize.translate('RecordingCancelled')); + } + }); +} + +function createRecording(apiClient, programId, isSeries) { + loading.show(); + return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (item) { + const promise = isSeries ? + apiClient.createLiveTvSeriesTimer(item) : + apiClient.createLiveTvTimer(item); + + return promise.then(function () { + loading.hide(); + sendToast(globalize.translate('RecordingScheduled')); + }); + }); +} + +function sendToast(msg) { + import('toast').then(({ default: toast }) => { + toast(msg); + }); +} + +function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) { + return new Promise(function (resolve, reject) { + import('dialog').then(({ default: dialog }) => { + const items = []; + + items.push({ + name: globalize.translate('HeaderKeepRecording'), + id: 'cancel', + type: 'submit' + }); + + if (timerStatus === 'InProgress') { + items.push({ + name: globalize.translate('HeaderStopRecording'), + id: 'canceltimer', + type: 'cancel' }); } else { - // cancel - if (confirmTimerCancellation) { - return cancelTimerWithConfirmation(timerId, apiClient.serverId()); - } - - return cancelTimer(apiClient.serverId(), timerId, true); + items.push({ + name: globalize.translate('HeaderCancelRecording'), + id: 'canceltimer', + type: 'cancel' + }); } - }); - } - function cancelTimerWithConfirmation(timerId, serverId) { - return new Promise(function (resolve, reject) { - require(['confirm'], function (confirm) { - confirm.default({ - - text: globalize.translate('MessageConfirmRecordingCancellation'), - primary: 'delete', - confirmText: globalize.translate('HeaderCancelRecording'), - cancelText: globalize.translate('HeaderKeepRecording') - - }).then(function () { - loading.show(); - - var apiClient = connectionManager.getApiClient(serverId); - cancelTimer(apiClient, timerId, true).then(resolve, reject); - }, reject); + items.push({ + name: globalize.translate('HeaderCancelSeries'), + id: 'cancelseriestimer', + type: 'cancel' }); - }); - } - function cancelSeriesTimerWithConfirmation(timerId, serverId) { - return new Promise(function (resolve, reject) { - require(['confirm'], function (confirm) { - confirm.default({ + dialog({ - text: globalize.translate('MessageConfirmRecordingCancellation'), - primary: 'delete', - confirmText: globalize.translate('HeaderCancelSeries'), - cancelText: globalize.translate('HeaderKeepSeries') + text: globalize.translate('MessageConfirmRecordingCancellation'), + buttons: items - }).then(function () { + }).then(function (result) { + const apiClient = connectionManager.getApiClient(serverId); + + if (result === 'canceltimer') { loading.show(); - var apiClient = connectionManager.getApiClient(serverId); - apiClient.cancelLiveTvSeriesTimer(timerId).then(function () { - require(['toast'], function (toast) { + cancelTimer(apiClient, timerId, true).then(resolve, reject); + } else if (result === 'cancelseriestimer') { + loading.show(); + + apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function () { + import('toast').then(({ default: toast }) => { toast(globalize.translate('SeriesCancelled')); }); loading.hide(); resolve(); }, reject); - }, reject); - }); - }); - } - - function cancelTimer(apiClient, timerId, hideLoading) { - loading.show(); - return apiClient.cancelLiveTvTimer(timerId).then(function () { - if (hideLoading !== false) { - loading.hide(); - sendToast(globalize.translate('RecordingCancelled')); - } - }); - } - - function createRecording(apiClient, programId, isSeries) { - loading.show(); - return apiClient.getNewLiveTvTimerDefaults({ programId: programId }).then(function (item) { - var promise = isSeries ? - apiClient.createLiveTvSeriesTimer(item) : - apiClient.createLiveTvTimer(item); - - return promise.then(function () { - loading.hide(); - sendToast(globalize.translate('RecordingScheduled')); - }); - }); - } - - function sendToast(msg) { - require(['toast'], function (toast) { - toast(msg); - }); - } - - function showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId) { - return new Promise(function (resolve, reject) { - require(['dialog'], function (dialog) { - var items = []; - - items.push({ - name: globalize.translate('HeaderKeepRecording'), - id: 'cancel', - type: 'submit' - }); - - if (timerStatus === 'InProgress') { - items.push({ - name: globalize.translate('HeaderStopRecording'), - id: 'canceltimer', - type: 'cancel' - }); } else { - items.push({ - name: globalize.translate('HeaderCancelRecording'), - id: 'canceltimer', - type: 'cancel' - }); + resolve(); } - - items.push({ - name: globalize.translate('HeaderCancelSeries'), - id: 'cancelseriestimer', - type: 'cancel' - }); - - dialog({ - - text: globalize.translate('MessageConfirmRecordingCancellation'), - buttons: items - - }).then(function (result) { - var apiClient = connectionManager.getApiClient(serverId); - - if (result === 'canceltimer') { - loading.show(); - - cancelTimer(apiClient, timerId, true).then(resolve, reject); - } else if (result === 'cancelseriestimer') { - loading.show(); - - apiClient.cancelLiveTvSeriesTimer(seriesTimerId).then(function () { - require(['toast'], function (toast) { - toast(globalize.translate('SeriesCancelled')); - }); - - loading.hide(); - resolve(); - }, reject); - } else { - resolve(); - } - }, reject); - }); + }, reject); }); - } + }); +} - function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) { - var apiClient = connectionManager.getApiClient(serverId); - var hasTimer = timerId && timerStatus !== 'Cancelled'; - if (seriesTimerId && hasTimer) { - // cancel - return showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId); - } else if (hasTimer && programId) { - // change to series recording, if possible - // otherwise cancel individual recording - return changeRecordingToSeries(apiClient, timerId, programId, true); - } else if (programId) { - // schedule recording - return createRecording(apiClient, programId); - } else { - return Promise.reject(); - } +function toggleRecording(serverId, programId, timerId, timerStatus, seriesTimerId) { + const apiClient = connectionManager.getApiClient(serverId); + const hasTimer = timerId && timerStatus !== 'Cancelled'; + if (seriesTimerId && hasTimer) { + // cancel + return showMultiCancellationPrompt(serverId, programId, timerId, timerStatus, seriesTimerId); + } else if (hasTimer && programId) { + // change to series recording, if possible + // otherwise cancel individual recording + return changeRecordingToSeries(apiClient, timerId, programId, true); + } else if (programId) { + // schedule recording + return createRecording(apiClient, programId); + } else { + return Promise.reject(); } +} + +export default { + cancelTimer: cancelTimer, + createRecording: createRecording, + changeRecordingToSeries: changeRecordingToSeries, + toggleRecording: toggleRecording, + cancelTimerWithConfirmation: cancelTimerWithConfirmation, + cancelSeriesTimerWithConfirmation: cancelSeriesTimerWithConfirmation +}; - return { - cancelTimer: cancelTimer, - createRecording: createRecording, - changeRecordingToSeries: changeRecordingToSeries, - toggleRecording: toggleRecording, - cancelTimerWithConfirmation: cancelTimerWithConfirmation, - cancelSeriesTimerWithConfirmation: cancelSeriesTimerWithConfirmation - }; -}); diff --git a/src/components/recordingcreator/seriesrecordingeditor.js b/src/components/recordingcreator/seriesrecordingeditor.js index b115e273e6..48fbbcf22c 100644 --- a/src/components/recordingcreator/seriesrecordingeditor.js +++ b/src/components/recordingcreator/seriesrecordingeditor.js @@ -1,143 +1,200 @@ -define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'imageLoader', 'datetime', 'scrollStyles', 'emby-button', 'emby-checkbox', 'emby-input', 'emby-select', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons', 'flexStyles'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper, imageLoader, datetime) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import globalize from 'globalize'; +import layoutManager from 'layoutManager'; +import connectionManager from 'connectionManager'; +import loading from 'loading'; +import scrollHelper from 'scrollHelper'; +import datetime from 'datetime'; +import 'scrollStyles'; +import 'emby-button'; +import 'emby-checkbox'; +import 'emby-input'; +import 'emby-select'; +import 'paper-icon-button-light'; +import 'css!./../formdialog'; +import 'css!./recordingcreator'; +import 'material-icons'; +import 'flexStyles'; - loading = loading.default || loading; +/*eslint prefer-const: "error"*/ - var currentDialog; - var recordingUpdated = false; - var recordingDeleted = false; - var currentItemId; - var currentServerId; +let currentDialog; +let recordingUpdated = false; +let recordingDeleted = false; +let currentItemId; +let currentServerId; - function deleteTimer(apiClient, timerId) { - return new Promise(function (resolve, reject) { - require(['recordingHelper'], function (recordingHelper) { - recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject); - }); +function deleteTimer(apiClient, timerId) { + return new Promise(function (resolve, reject) { + import('recordingHelper').then(({ default: recordingHelper }) => { + recordingHelper.cancelSeriesTimerWithConfirmation(timerId, apiClient.serverId()).then(resolve, reject); }); + }); +} + +function renderTimer(context, item) { + context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60; + context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60; + + context.querySelector('.selectChannels').value = item.RecordAnyChannel ? 'all' : 'one'; + context.querySelector('.selectAirTime').value = item.RecordAnyTime ? 'any' : 'original'; + + context.querySelector('.selectShowType').value = item.RecordNewOnly ? 'new' : 'all'; + context.querySelector('.chkSkipEpisodesInLibrary').checked = item.SkipEpisodesInLibrary; + context.querySelector('.selectKeepUpTo').value = item.KeepUpTo || 0; + + if (item.ChannelName || item.ChannelNumber) { + context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('ChannelNameOnly', item.ChannelName || item.ChannelNumber); + } else { + context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('OneChannel'); } - function renderTimer(context, item, apiClient) { - context.querySelector('#txtPrePaddingMinutes').value = item.PrePaddingSeconds / 60; - context.querySelector('#txtPostPaddingMinutes').value = item.PostPaddingSeconds / 60; + context.querySelector('.optionAroundTime').innerHTML = globalize.translate('AroundTime', datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))); - context.querySelector('.selectChannels').value = item.RecordAnyChannel ? 'all' : 'one'; - context.querySelector('.selectAirTime').value = item.RecordAnyTime ? 'any' : 'original'; + loading.hide(); +} - context.querySelector('.selectShowType').value = item.RecordNewOnly ? 'new' : 'all'; - context.querySelector('.chkSkipEpisodesInLibrary').checked = item.SkipEpisodesInLibrary; - context.querySelector('.selectKeepUpTo').value = item.KeepUpTo || 0; +function closeDialog(isDeleted) { + recordingUpdated = true; + recordingDeleted = isDeleted; - if (item.ChannelName || item.ChannelNumber) { - context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('ChannelNameOnly', item.ChannelName || item.ChannelNumber); - } else { - context.querySelector('.optionChannelOnly').innerHTML = globalize.translate('OneChannel'); - } + dialogHelper.close(currentDialog); +} - context.querySelector('.optionAroundTime').innerHTML = globalize.translate('AroundTime', datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate))); +function onSubmit(e) { + const form = this; + const apiClient = connectionManager.getApiClient(currentServerId); + + apiClient.getLiveTvSeriesTimer(currentItemId).then(function (item) { + item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60; + item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60; + item.RecordAnyChannel = form.querySelector('.selectChannels').value === 'all'; + item.RecordAnyTime = form.querySelector('.selectAirTime').value === 'any'; + item.RecordNewOnly = form.querySelector('.selectShowType').value === 'new'; + item.SkipEpisodesInLibrary = form.querySelector('.chkSkipEpisodesInLibrary').checked; + item.KeepUpTo = form.querySelector('.selectKeepUpTo').value; + + apiClient.updateLiveTvSeriesTimer(item); + }); + + e.preventDefault(); + + // Disable default form submission + return false; +} + +function init(context) { + fillKeepUpTo(context); + + context.querySelector('.btnCancel').addEventListener('click', function () { + closeDialog(false); + }); + + context.querySelector('.btnCancelRecording').addEventListener('click', function () { + const apiClient = connectionManager.getApiClient(currentServerId); + deleteTimer(apiClient, currentItemId).then(function () { + closeDialog(true); + }); + }); + + context.querySelector('form').addEventListener('submit', onSubmit); +} + +function reload(context, id) { + const apiClient = connectionManager.getApiClient(currentServerId); + + loading.show(); + if (typeof id === 'string') { + currentItemId = id; + + apiClient.getLiveTvSeriesTimer(id).then(function (result) { + renderTimer(context, result); + loading.hide(); + }); + } else if (id) { + currentItemId = id.Id; + + renderTimer(context, id); loading.hide(); } +} - function closeDialog(isDeleted) { - recordingUpdated = true; - recordingDeleted = isDeleted; +function fillKeepUpTo(context) { + let html = ''; - dialogHelper.close(currentDialog); - } + for (let i = 0; i <= 50; i++) { + let text; - function onSubmit(e) { - var form = this; - - var apiClient = connectionManager.getApiClient(currentServerId); - - apiClient.getLiveTvSeriesTimer(currentItemId).then(function (item) { - item.PrePaddingSeconds = form.querySelector('#txtPrePaddingMinutes').value * 60; - item.PostPaddingSeconds = form.querySelector('#txtPostPaddingMinutes').value * 60; - item.RecordAnyChannel = form.querySelector('.selectChannels').value === 'all'; - item.RecordAnyTime = form.querySelector('.selectAirTime').value === 'any'; - item.RecordNewOnly = form.querySelector('.selectShowType').value === 'new'; - item.SkipEpisodesInLibrary = form.querySelector('.chkSkipEpisodesInLibrary').checked; - item.KeepUpTo = form.querySelector('.selectKeepUpTo').value; - - apiClient.updateLiveTvSeriesTimer(item); - }); - - e.preventDefault(); - - // Disable default form submission - return false; - } - - function init(context) { - fillKeepUpTo(context); - - context.querySelector('.btnCancel').addEventListener('click', function () { - closeDialog(false); - }); - - context.querySelector('.btnCancelRecording').addEventListener('click', function () { - var apiClient = connectionManager.getApiClient(currentServerId); - deleteTimer(apiClient, currentItemId).then(function () { - closeDialog(true); - }); - }); - - context.querySelector('form').addEventListener('submit', onSubmit); - } - - function reload(context, id) { - var apiClient = connectionManager.getApiClient(currentServerId); - - loading.show(); - if (typeof id === 'string') { - currentItemId = id; - - apiClient.getLiveTvSeriesTimer(id).then(function (result) { - renderTimer(context, result, apiClient); - loading.hide(); - }); - } else if (id) { - currentItemId = id.Id; - - renderTimer(context, id, apiClient); - loading.hide(); - } - } - - function fillKeepUpTo(context) { - var html = ''; - - for (var i = 0; i <= 50; i++) { - var text; - - if (i === 0) { - text = globalize.translate('AsManyAsPossible'); - } else if (i === 1) { - text = globalize.translate('ValueOneEpisode'); - } else { - text = globalize.translate('ValueEpisodeCount', i); - } - - html += ''; + if (i === 0) { + text = globalize.translate('AsManyAsPossible'); + } else if (i === 1) { + text = globalize.translate('ValueOneEpisode'); + } else { + text = globalize.translate('ValueEpisodeCount', i); } - context.querySelector('.selectKeepUpTo').innerHTML = html; + html += ''; } - function onFieldChange(e) { - this.querySelector('.btnSubmit').click(); - } + context.querySelector('.selectKeepUpTo').innerHTML = html; +} - function embed(itemId, serverId, options) { +function onFieldChange() { + this.querySelector('.btnSubmit').click(); +} + +function embed(itemId, serverId, options) { + recordingUpdated = false; + recordingDeleted = false; + currentServerId = serverId; + loading.show(); + options = options || {}; + + import('text!./seriesrecordingeditor.template.html').then(({ default: template }) => { + const dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + + const dlg = options.context; + + dlg.classList.add('hide'); + dlg.innerHTML = globalize.translateHtml(template, 'core'); + + dlg.querySelector('.formDialogHeader').classList.add('hide'); + dlg.querySelector('.formDialogFooter').classList.add('hide'); + dlg.querySelector('.formDialogContent').className = ''; + dlg.querySelector('.dialogContentInner').className = ''; + dlg.classList.remove('hide'); + + dlg.removeEventListener('change', onFieldChange); + dlg.addEventListener('change', onFieldChange); + + currentDialog = dlg; + + init(dlg); + + reload(dlg, itemId); + }); +} + +function showEditor(itemId, serverId, options) { + return new Promise(function (resolve, reject) { recordingUpdated = false; recordingDeleted = false; currentServerId = serverId; loading.show(); options = options || {}; - require(['text!./seriesrecordingeditor.template.html'], function (template) { - var dialogOptions = { + import('text!./seriesrecordingeditor.template.html').then(({ default: template }) => { + const dialogOptions = { removeOnClose: true, scrollY: false }; @@ -148,101 +205,58 @@ define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'c dialogOptions.size = 'small'; } - var dlg = options.context; + const dlg = dialogHelper.createDialog(dialogOptions); - dlg.classList.add('hide'); - dlg.innerHTML = globalize.translateHtml(template, 'core'); + dlg.classList.add('formDialog'); + dlg.classList.add('recordingDialog'); - dlg.querySelector('.formDialogHeader').classList.add('hide'); - dlg.querySelector('.formDialogFooter').classList.add('hide'); - dlg.querySelector('.formDialogContent').className = ''; - dlg.querySelector('.dialogContentInner').className = ''; - dlg.classList.remove('hide'); + if (!layoutManager.tv) { + dlg.style['min-width'] = '20%'; + } - dlg.removeEventListener('change', onFieldChange); - dlg.addEventListener('change', onFieldChange); + let html = ''; + + html += globalize.translateHtml(template, 'core'); + + dlg.innerHTML = html; + + if (options.enableCancel === false) { + dlg.querySelector('.formDialogFooter').classList.add('hide'); + } currentDialog = dlg; + dlg.addEventListener('closing', function () { + if (!recordingDeleted) { + this.querySelector('.btnSubmit').click(); + } + }); + + dlg.addEventListener('close', function () { + if (recordingUpdated) { + resolve({ + updated: true, + deleted: recordingDeleted + }); + } else { + reject(); + } + }); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); + } + init(dlg); reload(dlg, itemId); + + dialogHelper.open(dlg); }); - } + }); +} - function showEditor(itemId, serverId, options) { - return new Promise(function (resolve, reject) { - recordingUpdated = false; - recordingDeleted = false; - currentServerId = serverId; - loading.show(); - options = options || {}; - - require(['text!./seriesrecordingeditor.template.html'], function (template) { - 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('recordingDialog'); - - if (!layoutManager.tv) { - dlg.style['min-width'] = '20%'; - } - - var html = ''; - - html += globalize.translateHtml(template, 'core'); - - dlg.innerHTML = html; - - if (options.enableCancel === false) { - dlg.querySelector('.formDialogFooter').classList.add('hide'); - } - - currentDialog = dlg; - - dlg.addEventListener('closing', function () { - if (!recordingDeleted) { - this.querySelector('.btnSubmit').click(); - } - }); - - dlg.addEventListener('close', function () { - if (recordingUpdated) { - resolve({ - updated: true, - deleted: recordingDeleted - }); - } else { - reject(); - } - }); - - if (layoutManager.tv) { - scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); - } - - init(dlg); - - reload(dlg, itemId); - - dialogHelper.open(dlg); - }); - }); - } - - return { - show: showEditor, - embed: embed - }; -}); +export default { + show: showEditor, + embed: embed +}; From 839dcbf8618c655ee4175eecc6b1fa92e64eb486 Mon Sep 17 00:00:00 2001 From: Xantios Krugor Date: Wed, 5 Aug 2020 14:38:23 +0200 Subject: [PATCH 078/155] Update src/controllers/list.js Co-authored-by: Cameron --- src/controllers/list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index 77830badaf..afa0a60fa8 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -772,7 +772,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' loading.hide(); if (refresh) { - focusManager.default.autoFocus(self.itemsContainer); + focusManager.autoFocus(self.itemsContainer); } }); From e53eacc949dace9961d841375c28fdcbeef04d5f Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 5 Aug 2020 11:53:31 -0400 Subject: [PATCH 079/155] Add start script as alias for serve --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 504f4038d9..e6697051d2 100644 --- a/package.json +++ b/package.json @@ -318,6 +318,7 @@ "Firefox ESR" ], "scripts": { + "start": "yarn serve", "serve": "gulp serve --development", "prepare": "gulp --production", "build:development": "gulp --development", From 83300e6bf6bc45b15817e348793b0555b9fb30f5 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Wed, 5 Aug 2020 18:12:31 +0000 Subject: [PATCH 080/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/strings/he.json b/src/strings/he.json index 3db7c86f26..ae274c26c8 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -760,5 +760,9 @@ "ButtonSyncPlay": "SyncPlay", "ButtonPlayer": "נגן", "StopPlayback": "הפסק הפעלה", - "ClearQueue": "נקה תור" + "ClearQueue": "נקה תור", + "DashboardServerName": "שרת: {0}", + "DashboardVersionNumber": "גירסה: {0}", + "DashboardArchitecture": "ארכיטקטורה: {0}", + "DashboardOperatingSystem": "מערכת הפעלה: {0}" } From f81126844d2411f48090d06ed75a5183e13ffd27 Mon Sep 17 00:00:00 2001 From: Accendit Date: Wed, 5 Aug 2020 17:07:43 +0000 Subject: [PATCH 081/155] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/nl/ --- src/strings/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/nl.json b/src/strings/nl.json index 39b2ea1b5e..860783b083 100644 --- a/src/strings/nl.json +++ b/src/strings/nl.json @@ -1441,7 +1441,7 @@ "Season": "Seizoen", "PreferEmbeddedEpisodeInfosOverFileNames": "Verkies ingeladen afleveringsinformatie boven bestandsnaam", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Dit gebruikt de afleveringsinformatie van de ingeladen metadata als deze aanwezig is.", - "PlaybackErrorNoCompatibleStream": "Deze machine is niet leesbaar met de media en de server verstuurd geen leesbare media formaten.", + "PlaybackErrorNoCompatibleStream": "Dit apparaat ondersteund de afgespeelde media niet en de server verstuurd geen ondersteund formaat.", "Person": "Persoon", "OptionForceRemoteSourceTranscoding": "Forceer het transcoderen van op afstand bediende media bronnen (zoals LiveTV)", "NoCreatedLibraries": "Het lijkt erop dat er geen bibliotheek is gecreëerd. {0}Wilt u er nu een aanmaken?{1}", From 60be236f1c04c93e2ed0a5b93e38a43786ba568f Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 19:24:22 +0100 Subject: [PATCH 082/155] Remove empy Constructor --- src/components/layoutManager.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index d104632573..88a7265f8b 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -13,9 +13,6 @@ function setLayout(instance, layout, selectedLayout) { } class LayoutManager { - constructor() { - } - setLayout(layout, save) { if (!layout || layout === 'auto') { this.autoLayout(); From 2e47b89ada6e6b65089b74b471460fe276b71eb5 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 19:59:25 +0100 Subject: [PATCH 083/155] remove constructor --- src/components/viewManager/viewManager.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index 695535e7b8..00c3018590 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -127,9 +127,6 @@ function resetCachedViews() { document.addEventListener('skinunload', resetCachedViews); class ViewManager { - constructor() { - } - loadView(options) { const lastView = currentView; From 6e5dc7f7d11672ad70567cc3953220f6cd242611 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 20:08:42 +0100 Subject: [PATCH 084/155] lint --- src/controllers/list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index 7d35bcf5cb..979bb76a20 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -744,7 +744,8 @@ class ItemsView { } const sortButtons = view.querySelectorAll('.btnSort'); - + let i; + let length; for (this.sortButtons = sortButtons, i = 0, length = sortButtons.length; i < length; i++) { const sortButton = sortButtons[i]; sortButton.addEventListener('click', showSortMenu.bind(this)); From c88345239525e012f48f6f4f0aa4832d842ab486 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Aug 2020 20:22:48 +0100 Subject: [PATCH 085/155] add default --- src/plugins/chromecastPlayer/plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 3d49f6fff6..22b49d6faf 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -577,7 +577,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.isLocalPlayer = false; this.lastPlayerData = {}; - new castSenderApiLoader().load().then(initializeChromecast.bind(this)); + new castSenderApiLoader.default().load().then(initializeChromecast.bind(this)); } ChromecastPlayer.prototype.tryPair = function (target) { From 6af88a366230a958340193b72bcf7320df7e575e Mon Sep 17 00:00:00 2001 From: sharkykh Date: Wed, 5 Aug 2020 18:28:45 +0000 Subject: [PATCH 086/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 84 +++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/strings/he.json b/src/strings/he.json index ae274c26c8..a063f59b76 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -10,7 +10,7 @@ "AllEpisodes": "כל הפרקים", "AllLibraries": "כל הספריות", "Anytime": "בכל עת", - "AroundTime": "בסביבות {0}", + "AroundTime": "בסביבות", "AsManyAsPossible": "כמה שיותר", "AttributeNew": "חדש", "Backdrops": "תמונות רקע", @@ -32,13 +32,13 @@ "ButtonRefreshGuideData": "רענן את מדריך השידור", "ButtonRemove": "הסר", "ButtonResetPassword": "איפוס סיסמא", - "ButtonRestart": "איתחול", + "ButtonRestart": "הפעל מחדש", "ButtonSave": "שמור", "ButtonSearch": "חיפוש", "ButtonSelectDirectory": "בחר תיקיות", "ButtonShutdown": "כבה", "ButtonSignIn": "היכנס", - "ButtonSignOut": "Sign out", + "ButtonSignOut": "התנתק", "ButtonSort": "מיין", "CancelRecording": "ביטול הקלטה", "CancelSeries": "בטל סדרה", @@ -114,11 +114,11 @@ "HeaderLatestRecordings": "הקלטות אחרונות", "HeaderLiveTV": "שידורים חיים", "HeaderMediaFolders": "ספריות מדיה", - "HeaderMetadataSettings": "הגדרות מטא נתונים", + "HeaderMetadataSettings": "הגדרות מטא-דאטה", "HeaderMovies": "סרטים", "HeaderMusicVideos": "קליפים", - "HeaderMyMedia": "הספרייה שלי", - "HeaderNextUp": "הבא", + "HeaderMyMedia": "המדיה שלי", + "HeaderNextUp": "הבא בתור", "HeaderPaths": "נתיבים", "HeaderPlayAll": "נגן הכל", "HeaderPleaseSignIn": "אנא היכנס", @@ -138,7 +138,7 @@ "Help": "עזרה", "Identify": "לזהות", "Images": "תמונות", - "InstallingPackage": "מתקין {0}", + "InstallingPackage": "מתקין {0} (גירסה {1})", "InstantMix": "מיקס מיידי", "ItemCount": "פריטים {0}", "Kids": "ילדים", @@ -151,11 +151,11 @@ "LabelAlbum": "אלבום:", "LabelAlbumArtists": "אלבום אומנים:", "LabelArtists": "אומנים:", - "LabelArtistsHelp": "הפרד מרובים באמצעות;", + "LabelArtistsHelp": "הפרד אמנים מרובים באמצעות נקודה-פסיק (;).", "LabelAudioLanguagePreference": "שפת קול מועדפת:", "LabelBirthDate": "תאריך לידה:", "LabelBirthYear": "שנת לידה:", - "LabelBlastMessageInterval": "אינטרוול הודעות דחיפה (בשניות)", + "LabelBlastMessageInterval": "תדירות הודעות דחיפה", "LabelBlastMessageIntervalHelp": "מגדיר את משך הזמן בשניות בין הודעות דחיפה של השרת.", "LabelCachePath": "נתיב cache:", "LabelChannels": "ערוצים:", @@ -165,7 +165,7 @@ "LabelCountry": "מדינה:", "LabelCriticRating": "דירוג ביקורת:", "LabelCurrentPassword": "סיסמא נוכחית:", - "LabelCustomCss": "CSS מותאם אישית", + "LabelCustomCss": "CSS מותאם אישית:", "LabelCustomRating": "דירוג מותאם אישית:", "LabelDateAdded": "תאריך הוסף:", "LabelDay": "יום:", @@ -175,15 +175,15 @@ "LabelDiscNumber": "מספר דיסק:", "LabelDisplayMissingEpisodesWithinSeasons": "הצג פרקים חסרים בתוך העונות", "LabelDisplayOrder": "סדר תצוגה:", - "LabelDownMixAudioScaleHelp": "הגבר אודיו כאשר הוא ממוזג. הגדר ל-1 לשמור על ערך הווליום המקורי", + "LabelDownMixAudioScaleHelp": "הגבר את עוצמת השמע כאשר הוא ממוזג. ערך השווה ל-1 יישמר את העוצמה המקורית.", "LabelDynamicExternalId": "{0} תעודת זהות:", "LabelEnableBlastAliveMessages": "הודעות דחיפה", "LabelEnableBlastAliveMessagesHelp": "אפשר זאת אם השרת לא מזוהה כאמין על ידי מכשירי UPnP אחרים ברשת שלך.", - "LabelEnableDlnaClientDiscoveryInterval": "זמן גילוי קליינטים (בשניות)", + "LabelEnableDlnaClientDiscoveryInterval": "זמן גילוי קליינטים", "LabelEnableDlnaDebugLogging": "אפשר ניהול רישום באגים בDLNA", "LabelEnableDlnaDebugLoggingHelp": "אפשרות זו תיצור קבצי לוג גדולים יותר ועליך להשתמש בה רק על מנת לפתור תקלות.", "LabelEnableDlnaPlayTo": "מאפשר ניגון DLNA ל", - "LabelEnableDlnaServer": "אפשר שרת Dina", + "LabelEnableDlnaServer": "אפשר שרת DLNA", "LabelEnableRealtimeMonitor": "אפשר מעקב בזמן אמת", "LabelEnableRealtimeMonitorHelp": "שינויים יעשו באופן מיידית על מערכות קבצים נתמכות.", "LabelEndDate": "תאריך סיום:", @@ -195,21 +195,21 @@ "LabelServerNameHelp": "השם יתן לזיהוי השרת. אם מושאר ריק, שם השרת יהיה שם המחשב.", "LabelKeepUpTo": "שמור עד ל:", "LabelLanguage": "שפה:", - "LabelLocalHttpServerPortNumber": "מספר פורט HTTP מקומי", + "LabelLocalHttpServerPortNumber": "מספר פורט HTTP מקומי:", "LabelLockItemToPreventChanges": "נעל פריט זה כדי למנוע שינויים עתידיים", "LabelMaxBackdropsPerItem": "מספר תמונות רקע מקסימאלי לפריט:", "LabelMaxParentalRating": "דירוג הורים מקסימאלי:", - "LabelMaxResumePercentage": "אחוזי המשכה מקסימאלים", - "LabelMaxResumePercentageHelp": "קובץ מוגדר כנוגן במלואו אם נעצר אחרי הזמן הזה", + "LabelMaxResumePercentage": "אחוזי המשכה מקסימאלים:", + "LabelMaxResumePercentageHelp": "קובץ מוגדר כנוגן במלואו אם נעצר אחרי הזמן הזה.", "LabelMaxScreenshotsPerItem": "מספר תמונות מסך מקסימאלי לפריט:", "LabelMessageTitle": "כותרת הודעה:", "LabelMetadataDownloadLanguage": "שפת הורדה מועדפת:", "LabelMetadataPath": "נתיב Metadata:", "LabelMinBackdropDownloadWidth": "רוחב תמונת רקע מינימאלי להורדה:", - "LabelMinResumeDuration": "משך המשכה מינימאלי (בשניות):", + "LabelMinResumeDuration": "משך המשכה מינימאלי:", "LabelMinResumeDurationHelp": "קובץ קצר מזה לא יהיה ניתן להמשך ניגון מנקודת העצירה", "LabelMinResumePercentage": "אחוזי המשכה מינימאלים:", - "LabelMinResumePercentageHelp": "כותרים יוצגו כלא נוגנו אם נצרו לפני הזמן הזה", + "LabelMinResumePercentageHelp": "כותרים יוצגו כלא נוגנו אם נצרו לפני הזמן הזה.", "LabelMinScreenshotDownloadWidth": "רחוב תמונת מסך מינימאלית להורדה:", "LabelMonitorUsers": "עקוב אחר פעילות מ:", "LabelName": "שם:", @@ -237,12 +237,12 @@ "LabelProfileAudioCodecs": "מקודדי שמע:", "LabelProfileCodecs": "מקודדים:", "LabelProfileVideoCodecs": "‮מקודדי וידאו:", - "LabelPublicHttpPort": "מספר פורט HTTP פומבי", + "LabelPublicHttpPort": "מספר פורט HTTP פומבי:", "LabelReadHowYouCanContribute": "למד איך אתה יכול לתרום.", "LabelRecord": "הקלטה:", "LabelRefreshMode": "מצב רענון:", "LabelReleaseDate": "תאריך הוצאה:", - "LabelRuntimeMinutes": "זמן ריצה (דקות):", + "LabelRuntimeMinutes": "זמן ריצה:", "LabelSaveLocalMetadata": "שמור תמונות רקע בתוך ספריות המדיה", "LabelSaveLocalMetadataHelp": "שמירת תמונות רקע בתוך ספריות המדיה תשים אותם במקום שבו יהיה קל לערוך אותם.", "LabelSeasonNumber": "מספר עונה:", @@ -264,7 +264,7 @@ "LabelUserLibrary": "ספריית משתמש:", "LabelYear": "שנה:", "LabelYoureDone": "סיימת!", - "LibraryAccessHelp": "בחר את ספריות המדיה אשר ישותפו עם המשתמש. מנהלים יוכלו לערות את כל התיקיות באמצעות עורך המידע.", + "LibraryAccessHelp": "בחר את הספריות אשר ישותפו עם המשתמש. מנהלים יוכלו לערות את כל התיקיות באמצעות עורך המידע.", "Like": "אוהב", "Live": "שידור חי", "LiveBroadcasts": "שידורים חיים", @@ -319,7 +319,7 @@ "OptionDatePlayed": "תאריך ניגון", "OptionDescending": "סדר יורד", "OptionDisableUser": "בטל משתמש זה", - "OptionDisableUserHelp": "אם מבוטל, השרת שלא יאפשר חיבורים ממשתמש זה. חיבורים פעילים יבוטלו מייד.", + "OptionDisableUserHelp": "השרת לא יאפשר חיבורים ממשתמש זה. חיבורים פעילים יבוטלו מייד.", "OptionDislikes": "לא אוהב", "OptionDownloadArtImage": "עטיפה", "OptionDownloadBackImage": "גב", @@ -344,7 +344,7 @@ "OptionMissingEpisode": "פרקים חסרים", "OptionMonday": "שני", "OptionNameSort": "שם", - "OptionNew": "חדש...", + "OptionNew": "חדש…", "OptionOnAppStartup": "בהפעלת התוכנה", "OptionOnInterval": "כל פרק זמן", "OptionParentalRating": "דירוג בקרת הורים", @@ -363,7 +363,7 @@ "OptionThursday": "חמישי", "OptionTrackName": "שם השיר", "OptionTuesday": "שלישי", - "OptionTvdbRating": "דירוג Tvdb", + "OptionTvdbRating": "דירוג TVDB", "OptionUnairedEpisode": "פרקים שלא שודרו", "OptionUnplayed": "לא נוגן", "OptionWakeFromSleep": "הער ממצב שינה", @@ -371,8 +371,8 @@ "OptionWeekly": "שבועי", "OriginalAirDateValue": "תאריך אוויר מקורי: {0}", "Overview": "סקירה כללית", - "PackageInstallCancelled": "{0} ההתקנה בוטלה.", - "PackageInstallFailed": "ההתקנה {0} נכשלה.", + "PackageInstallCancelled": "ההתקנה של {0} (גירסה {1}) בוטלה.", + "PackageInstallFailed": "ההתקנה של {0} (גירסה {1}) נכשלה.", "ParentalRating": "דירוג ההורים", "PasswordMatchError": "הסיסמא ואימות הסיסמא צריכות להיות זהות.", "PasswordResetComplete": "הסיסמא אופסה.", @@ -395,7 +395,7 @@ "RecordingCancelled": "הקלטה בוטלה.", "RecordingScheduled": "ההקלטה מתוזמנת.", "Refresh": "רענון", - "RefreshDialogHelp": "המטא נתונים מתרעננים על סמך הגדרות ושירותי אינטרנט שמופעלים בלוח המחוונים של מרכז אמבי.", + "RefreshDialogHelp": "המטא-דאטה מתרעננת על סמך הגדרות ושירותי אינטרנט שמופעלים בלוח הבקרה.", "RefreshQueued": "רענן תור.", "ReleaseDate": "תאריך שיחרור", "RemoveFromCollection": "הסר מאוספים", @@ -514,8 +514,8 @@ "AllLanguages": "כל השפות", "Alerts": "התראות", "Box": "מארז", - "BirthPlaceValue": "מיקום לידה: {0}", - "BirthDateValue": "תאריך לידה: {0}", + "BirthPlaceValue": "מקום לידה: {0}", + "BirthDateValue": "נולד: {0}", "Backdrop": "רקע", "AuthProviderHelp": "בחר ספק אימות שישמש לאימות הסיסמה של משתמש זה.", "Audio": "שמע", @@ -530,10 +530,10 @@ "Yesterday": "אתמול", "HeaderAlbumArtists": "אמני האלבום", "Favorites": "מועדפים", - "HeaderFavoriteAlbums": "אלבומים שאהבתי", + "HeaderFavoriteAlbums": "אלבומים מועדפים", "HeaderFavoriteArtists": "אמנים מועדפים", "Folders": "תיקיות", - "HeaderFavoriteShows": "סדרות מועדפות", + "HeaderFavoriteShows": "תוכניות מועדפות", "HeaderFavoriteEpisodes": "פרקים מועדפים", "HeaderFavoriteSongs": "שירים מועדפים", "Collections": "אוספים", @@ -541,7 +541,7 @@ "HeaderContinueWatching": "המשך לצפות", "AllowOnTheFlySubtitleExtraction": "אפשר חילוץ כתוביות בזמן אמת", "AllowHWTranscodingHelp": "אפשר למלקט לקודד הזרמות בזמן אמת. זה עשוי לעזור בהפחתת הקידוד שנעשה ע\"י השרת.", - "AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX)", + "AllComplexFormats": "כל הפורמטים המורכבים (ASS, SSA, VOBSUB, PGS, SUB/IDX, …)", "Songs": "שירים", "Shows": "סדרות", "DownloadsValue": "{0} הורדות", @@ -601,7 +601,7 @@ "HeaderRestart": "הפעלה מחדש", "HeaderProfileInformation": "מידע פרופיל", "HeaderProfile": "פרופיל", - "HeaderPreferredMetadataLanguage": "שפת מטא-נתונים מועדפת", + "HeaderPreferredMetadataLanguage": "שפת מטא-דאטה מועדפת", "HeaderPluginInstallation": "התקנת תוסף", "HeaderPlayOn": "נגן על", "HeaderPinCodeReset": "איפוס קוד סיכה", @@ -677,7 +677,7 @@ "EnableExternalVideoPlayers": "נגני וידאו חיצוניים", "EnableCinemaMode": "מצב קולנוע", "EnableBackdrops": "תמונות רקע", - "EditMetadata": "ערוך מטא-נתונים", + "EditMetadata": "ערוך מטא-דאטה", "DrmChannelsNotImported": "‫ערוצים בעלי ניהול זכויות דיגיטלי (DRM) לא ייובאו.", "Down": "למטה", "Display": "תצוגה", @@ -726,7 +726,7 @@ "ButtonAddScheduledTaskTrigger": "הוסף טריגר", "Browse": "עיין", "BoxRear": "מארז (מאחור)", - "BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0}במדריך מתן שמות לספרים{1}.", + "BookLibraryHelp": "ניתן להוסיף ספרים מוקלטים וספרים כתובים. עיינו {0} במדריך מתן שמות לספרים {1}.", "Desktop": "שולחן עבודה", "MessageDeleteTaskTrigger": "האם אתה בטוח שברצונך למחוק את מפעיל המשימה הזה?", "LastSeen": "נראה לאחרונה ב-{0}", @@ -738,11 +738,11 @@ "DeleteDeviceConfirmation": "האם אתה בטוח שברצונך למחוק את המכשיר? הוא יופיע שוב בפעם הבאה שמשתמש ייכנס באמצעותו.", "ColorSpace": "מרחב צבע", "CinemaModeConfigurationHelp": "מצב קולנוע מביא את חוויית הקולנוע היישר אל הסלון עם האפשרות להפעיל טריילרים וקדימונים מותאמים אישית לפני הסרט.", - "ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת \"מנהל המטא-דאטה\".", + "ChannelAccessHelp": "בחר את הערוצים לשיתוף עם משתמש זה. מנהלים יוכלו לערוך את כל הערוצים בעזרת מנהל המטא-דאטה.", "ButtonResetEasyPassword": "אתחל קוד פין פשוט", "ButtonOff": "כיבוי", "ButtonLibraryAccess": "הרשאות גישה לספרייה", - "BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VOBSUB, PGS, SUB, IDX) וכתוביות ASS או SSA מסויימות.", + "BurnSubtitlesHelp": "מחליט אם על השרת לצרוב כתוביות בזמן קידוד וידאו. הימנעות מכך תשפר מאוד את הביצועים. בחר \"אוטומטי\" לצריבת כתוביות על בסיס פורמט תמונה (VOBSUB, PGS, SUB, IDX, …) וכתוביות ASS או SSA מסויימות.", "Artist": "אמן", "AllowedRemoteAddressesHelp": "רשימת IP \\ מיסוך רשת המופרדת בפסיקים עבור רשתות שיורשו להתחבר מרחוק. במידה ותישאר ריקה, כל הכתובות יורשו להתחבר.", "Album": "אלבום", @@ -764,5 +764,13 @@ "DashboardServerName": "שרת: {0}", "DashboardVersionNumber": "גירסה: {0}", "DashboardArchitecture": "ארכיטקטורה: {0}", - "DashboardOperatingSystem": "מערכת הפעלה: {0}" + "DashboardOperatingSystem": "מערכת הפעלה: {0}", + "HeaderMyMediaSmall": "המדיה שלי (קטן)", + "HeaderMusicQuality": "איכות מוזיקה", + "HeaderMediaInfo": "מידע על המדיה", + "HeaderMoreLikeThis": "עוד כמו זה", + "HeaderMedia": "מדיה", + "LabelMetadata": "מטא-דאטה:", + "HeaderSelectMetadataPath": "בחר נתיב מטא-דאטה", + "NextUp": "הבא בתור" } From 73e8b5039527bd0c7b23571bed3dff73c9c2a640 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Wed, 5 Aug 2020 19:38:19 +0000 Subject: [PATCH 087/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/strings/he.json b/src/strings/he.json index a063f59b76..9155e310a3 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -772,5 +772,23 @@ "HeaderMedia": "מדיה", "LabelMetadata": "מטא-דאטה:", "HeaderSelectMetadataPath": "בחר נתיב מטא-דאטה", - "NextUp": "הבא בתור" + "NextUp": "הבא בתור", + "LabelBaseUrl": "כתובת בסיס:", + "LabelEnableHttpsHelp": "האזן על גבי פורט ה-HTTPS המוגדר. חובה לספק תעודה תקינה על מנת שהגדרה זו תכנס לתוקף.", + "LabelEnableHttps": "הפעל HTTPS", + "LabelEnableHardwareDecodingFor": "הפעל פענוח חומרה עבור:", + "LabelEnableDlnaServerHelp": "אפשר למכשירי UPnP ברשת שלך לעיין בתוכן ולנגן אותו.", + "LabelEnableAutomaticPortMap": "הפעל מיפוי פורט אוטומטי", + "LabelDropImageHere": "גרור תמונה לכאן, או לחץ כדי לעיין.", + "LabelDownloadLanguages": "הורד שפות:", + "LabelDownMixAudioScale": "הגברת עוצמת שמע כאשר הוא ממוזג:", + "LabelDisplaySpecialsWithinSeasons": "הצג פרקים מיוחדים בתוך העונות שבמהלכן הם שודרו", + "LabelDisplayName": "שם תצוגה:", + "LabelDisplayMode": "מצב תצוגה:", + "LabelDisplayLanguageHelp": "תרגום Jellyfin הוא פרויקט מתמשך.", + "LabelDisplayLanguage": "שפת תצוגה:", + "LabelDidlMode": "מצב DIDL:", + "LabelDeviceDescription": "תיאור מכשיר", + "LabelDefaultScreen": "מסך ברירת-מחדל:", + "LabelCustomDeviceDisplayName": "שם תצוגה:" } From c73a2d83761e480c7b1dce88ddb717147458d9a4 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Wed, 5 Aug 2020 19:51:52 +0000 Subject: [PATCH 088/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/strings/he.json b/src/strings/he.json index 9155e310a3..c61bfe9e1a 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -790,5 +790,15 @@ "LabelDidlMode": "מצב DIDL:", "LabelDeviceDescription": "תיאור מכשיר", "LabelDefaultScreen": "מסך ברירת-מחדל:", - "LabelCustomDeviceDisplayName": "שם תצוגה:" + "LabelCustomDeviceDisplayName": "שם תצוגה:", + "LabelImageType": "סוג תמונה:", + "LabelHttpsPortHelp": "מספר פורט ה-TCP עבור שרת ה-HTTPS.", + "LabelHttpsPort": "מספר פורט HTTPS מקומי:", + "LabelGroupMoviesIntoCollections": "אגד סרטים לתוך אוספים", + "LabelFriendlyName": "שם ידידותי:", + "LabelFormat": "תבנית:", + "LabelForgotPasswordUsernameHelp": "הכנס/י את שם המשתמש שלך, אם את/ה זוכר/ת אותו.", + "LabelFont": "גופן:", + "LabelFolder": "תיקייה:", + "LabelFileOrUrl": "קובץ או כתובת אינטרנט:" } From 98481fc4502abe3fe5864a8dfc07f93108840b80 Mon Sep 17 00:00:00 2001 From: WWWesten Date: Wed, 5 Aug 2020 21:26:05 +0000 Subject: [PATCH 089/155] Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ru/ --- src/strings/ru.json | 75 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/strings/ru.json b/src/strings/ru.json index 9089e4bea7..24d5cdd90f 100644 --- a/src/strings/ru.json +++ b/src/strings/ru.json @@ -175,8 +175,8 @@ "DetectingDevices": "Обнаруживются устройства", "DeviceAccessHelp": "Это относится только к устройствам, которые могут быть однозначно распознаны и не препятствует доступу через браузер. Фильтрация доступа пользовательского устройства запретит использование новых устройств до тех пор, пока они не будут одобрены.", "DirectPlaying": "Воспроизводится напрямую", - "DirectStreamHelp1": "Медиаданные совместимы с устройством в отношении разрешения и типа медиаданных (H.264, AC3, и т.д.), но в несовместимом файловом контейнере (mkv, avi, wmv и т.д.). Видео будет повторно упаковано динамически перед его трансляцией на устройство.", - "DirectStreamHelp2": "При прямой трансляции файла расходуется очень мало вычислительной мощности без потери качества видео.", + "DirectStreamHelp1": "Медиаданные совместимы с устройством в отношении разрешения и типа медиаданных (H.264, AC3, и т.д.), но в несовместимом файловом контейнере (mkv, avi, wmv и т.д.). Видео будет повторно упаковано динамически перед его отправлением на устройство.", + "DirectStreamHelp2": "Прямая трансляции расходует очень мало вычислительной мощности с минимальной потерей качества видео.", "DirectStreaming": "Транслируется напрямую", "Director": "Режиссёр", "Directors": "Режиссёры", @@ -287,7 +287,7 @@ "HeaderAllowMediaDeletionFrom": "Разрешить удаление медиаданных из", "HeaderApiKey": "API-ключ", "HeaderApiKeys": "API-ключи", - "HeaderApiKeysHelp": "Внешним приложениям требуется API-ключ для того, чтобы подключиться к Jellyfin Server. Ключи выдаются при входе с учётной записью Jellyfin или ключ предоставляется приложению вручную.", + "HeaderApiKeysHelp": "Внешним приложениям требуется API-ключ для того, чтобы подключиться к серверу. Ключи выдаются при входе с учётной записью обычного пользователя или ключ предоставляется приложению вручную.", "HeaderApp": "Приложение", "HeaderAppearsOn": "Фигурирует в", "HeaderAudioBooks": "Аудиокниги", @@ -359,7 +359,7 @@ "HeaderItems": "Элементы", "HeaderKeepRecording": "Хранение записи", "HeaderKeepSeries": "Хранение сериала", - "HeaderKodiMetadataHelp": "Для включения или отключения NFO-метаданных, начните править медиатеку в области настройки медиатек и найдите раздел хранителей метаданных.", + "HeaderKodiMetadataHelp": "Для включения или отключения NFO-метаданных, правьте медиатеку и найдите раздел хранителей метаданных.", "HeaderLatestEpisodes": "Новейшие эпизоды", "HeaderLatestMedia": "Новейшие медиаданные", "HeaderLatestMovies": "Новейшие фильмы", @@ -408,7 +408,7 @@ "HeaderPreferredMetadataLanguage": "Выбор языка метаданных", "HeaderProfile": "Профиль", "HeaderProfileInformation": "О профиле", - "HeaderProfileServerSettingsHelp": "Данные значения управляют тем, как Jellyfin Server будет представляться устройству.", + "HeaderProfileServerSettingsHelp": "Данные значения управляют тем, как сервер будет представлять себя клиентам.", "HeaderRecentlyPlayed": "Воспроизведённые недавно", "HeaderRecordingOptions": "Опции записи", "HeaderRecordingPostProcessing": "Постобработка записи", @@ -426,13 +426,13 @@ "HeaderSecondsValue": "{0} с", "HeaderSelectCertificatePath": "Выбор пути к сертификату", "HeaderSelectMetadataPath": "Выбор пути для метаданных", - "HeaderSelectMetadataPathHelp": "Найдите или введите путь, в пределах которого хотите хранить метаданные. Папка должна быть доступна для записи.", + "HeaderSelectMetadataPathHelp": "Найдите или введите путь, который хотите использовать для метаданныхе. Папка должна быть доступна для записи.", "HeaderSelectPath": "Выбор пути", "HeaderSelectServer": "Выбор сервера", "HeaderSelectServerCachePath": "Выбор пути для серверного кэша", "HeaderSelectServerCachePathHelp": "Найдите или введите путь, чтобы использовать для файлов серверного кэша. Папка должна быть доступна для записи.", "HeaderSelectTranscodingPath": "Выбор пути для временных файлов перекодировки", - "HeaderSelectTranscodingPathHelp": "Найдите или введите путь, чтобы использовать для временных файлов перекодировки. Папка должна быть доступна для записи.", + "HeaderSelectTranscodingPathHelp": "Найдите или введите путь, чтобы использовать для файлов перекодировки. Папка должна быть доступна для записи.", "HeaderSendMessage": "Передача сообщения", "HeaderSeries": "Сериалы", "HeaderSeriesOptions": "Опции сериала", @@ -485,8 +485,8 @@ "HttpsRequiresCert": "Чтобы включить HTTPS для внешних подключений, вам нужно будет предоставить доверенный SSL-cертификат, например, Let's Encrypt. Предоставьте сертификат или отключите защищенные соединения.", "Identify": "Распознать", "Images": "Изображения", - "ImportFavoriteChannelsHelp": "При включении, будут импортированы только каналы, которые обозначены как избранное на тюнерном устройстве.", - "ImportMissingEpisodesHelp": "При включении, информация об отсутствующих эпизодах будет импортирована в вашу базу данных Jellyfin и отображаться в пределах сезонов и сериалов. Это может увеличить время сканирования медиатеки.", + "ImportFavoriteChannelsHelp": "Будут импортированы только каналы, которые обозначены как избранное на тюнерном устройстве.", + "ImportMissingEpisodesHelp": "Информация об отсутствующих эпизодах будет импортирована в вашу базу данных и отображена в пределах сезонов и сериалов. Это может заметно увеличить время сканирования медиатеки.", "InstallingPackage": "Устанавливается {0} (версия {1})", "InstantMix": "Автомикс", "ItemCount": "{0} элемент(а/ов)", @@ -517,15 +517,15 @@ "LabelAppName": "Название приложения", "LabelAppNameExample": "Пример: Sickbeard, Sonarr", "LabelArtists": "Исполнители:", - "LabelArtistsHelp": "Для разделения используйте точку с запятой ;", + "LabelArtistsHelp": "Для разделения исполнителей используйте точку с запятой ;", "LabelAudio": "Аудио", "LabelAudioLanguagePreference": "Выбор языка аудио:", "LabelAutomaticallyRefreshInternetMetadataEvery": "Автоматически обновлять метаданные из Интернета:", "LabelBindToLocalNetworkAddress": "Привязка к адресу в локальной сети:", - "LabelBindToLocalNetworkAddressHelp": "Необязательно. Переопределяется локальный IP-адрес для привязки HTTP-сервера. Если поле пусто, то привязка сервера будет ко всем доступным адресам. При изменении данного значения потребуется перезапуск Jellyfin Server.", + "LabelBindToLocalNetworkAddressHelp": "Переопределяется локальный IP-адрес для HTTP-сервера. Если поле пусто, то привязка сервера будет ко всем доступным адресам. При изменении данного значения потребуется перезапуск Jellyfin Server.", "LabelBirthDate": "Дата рождения:", "LabelBirthYear": "Год рождения:", - "LabelBlastMessageInterval": "Интервал сообщений проверки активности, с", + "LabelBlastMessageInterval": "Интервал сообщений проверки активности", "LabelBlastMessageIntervalHelp": "Определяет длительность в секундах между бомбардированием сообщениями проверки активности.", "LabelBlockContentWithTags": "Блокирование элементов с тегами:", "LabelBurnSubtitles": "Внедрение субтитров:", @@ -545,7 +545,7 @@ "LabelCustomCertificatePath": "Путь к пользовательскому SSL-сертификату:", "LabelCustomCertificatePathHelp": "Путь к файлу PKCS #12, содержащему сертификат и \tзакрытый ключ для включения поддержки TLS на произвольном домене.", "LabelCustomCss": "Настраиваемые CSS:", - "LabelCustomCssHelp": "Применяйте свою собственную настраиваемую стилизацию к веб-интерфейсу.", + "LabelCustomCssHelp": "Применяйте свои собственные настраиваемые стили в веб-интерфейсе.", "LabelCustomDeviceDisplayName": "Отображаемое название:", "LabelCustomDeviceDisplayNameHelp": "Приведите произвольное имя для отображения или не заполняйте, чтобы использовать имя, выданное устройством.", "LabelCustomRating": "Произвольная возрастная категория:", @@ -585,7 +585,7 @@ "LabelEnableDlnaDebugLogging": "Включить журналирование отладки DLNA", "LabelEnableDlnaDebugLoggingHelp": "Создаются большие файлы Журнала, рекомендуется использовать только для поиска неполадок.", "LabelEnableDlnaPlayTo": "Включить DLNA-функцию Воспроизвести На", - "LabelEnableDlnaPlayToHelp": "Обнаруживаются устройства внутри своей сети, а также предоставляется возможность удалённо управлять ими.", + "LabelEnableDlnaPlayToHelp": "Обнаруживаются устройства внутри своей сети, а также предоставляется возможность управлять ими удалённо.", "LabelEnableDlnaServer": "Включить DLNA-сервер", "LabelEnableDlnaServerHelp": "Для UPnP-устройств домашней сети возможна навигация по содержанию и его воспроизведение.", "LabelEnableHardwareDecodingFor": "Включить аппаратное декодирование для:", @@ -606,9 +606,9 @@ "LabelForgotPasswordUsernameHelp": "Введите имя пользователя, если помните его.", "LabelFormat": "Формат:", "LabelFriendlyName": "Понятное имя:", - "LabelServerNameHelp": "Это имя используется для распознавания сервера и будет по умолчанию именем компьютера.", + "LabelServerNameHelp": "Это имя используется для распознавания сервера и будет по умолчанию именем хоста сервера.", "LabelGroupMoviesIntoCollections": "Группировать фильмы внутрь коллекций", - "LabelGroupMoviesIntoCollectionsHelp": "При отображении списка фильмов, элементы, принадлежащие к одной коллекции будут отображаться как единый сгруппированный элемент.", + "LabelGroupMoviesIntoCollectionsHelp": "При отображении списков фильмов, фильмы из коллекции будут отображаться как единый группированный элемент.", "LabelH264Crf": "Значение CRF H264-кодирования:", "LabelEncoderPreset": "Предустановка H264-кодирования:", "LabelHardwareAccelerationType": "Аппаратное ускорение:", @@ -616,7 +616,7 @@ "LabelHomeNetworkQuality": "Качество в домашней сети:", "LabelHomeScreenSectionValue": "Главная страница - раздел {0}:", "LabelHttpsPort": "Номер локального HTTPS-порта:", - "LabelHttpsPortHelp": "TCP-порт, ко которому следует создать привязку HTTPS-сервера Jellyfin.", + "LabelHttpsPortHelp": "Номер TCP-порта для HTTPS-сервера.", "LabelIconMaxHeight": "Макс. высота значка:", "LabelIconMaxHeightHelp": "Максимальное разрешение значков представляемых с помощью upnp:icon.", "LabelIconMaxWidth": "Макс. ширина значка:", @@ -644,7 +644,7 @@ "LabelLanguage": "Язык:", "LabelLineup": "Список сопоставления:", "LabelLocalHttpServerPortNumber": "Номер локального HTTP-порта:", - "LabelLocalHttpServerPortNumberHelp": "TCP-порт, ко которому следует создать привязку HTTP-сервера Jellyfin.", + "LabelLocalHttpServerPortNumberHelp": "Номер TCP-порта для HTTP-сервера.", "LabelLockItemToPreventChanges": "Зафиксировать данный элемент, чтобы запретить будущие правки", "LabelLoginDisclaimer": "Предупреждение при входе:", "LabelLoginDisclaimerHelp": "Сообщение будет отображаться в нижней части страницы входа в систему.", @@ -670,7 +670,7 @@ "LabelMetadataReaders": "Считыватели метаданных:", "LabelMetadataReadersHelp": "Ранжируйте предпочитаемые локальные источники метаданных в порядке приоритета. Будет считан первый же найденный файл.", "LabelMetadataSavers": "Хранители метаданных:", - "LabelMetadataSaversHelp": "Выберите форматы файлов, куда будут сохраняться метаданные.", + "LabelMetadataSaversHelp": "Выберите форматы файлов, которые будут использоваться при сохранении метаданных.", "LabelMethod": "Метод:", "LabelMinBackdropDownloadWidth": "Минимальная ширина загружаемого фона:", "LabelMinResumeDuration": "Минимальная длительность для возобновления:", @@ -686,7 +686,7 @@ "LabelMovieCategories": "Фильмовые категории:", "LabelMoviePrefix": "Префикс фильма:", "LabelMoviePrefixHelp": "При применении к названиям фильмов префикса, введите его здесь, чтобы он правильно обрабатывался на сервере.", - "LabelMovieRecordingPath": "Путь к записываемым фильмам (необязательно):", + "LabelMovieRecordingPath": "Путь к записываемым фильмам:", "LabelMusicStreamingTranscodingBitrate": "Битрейт перекодировки музыки:", "LabelMusicStreamingTranscodingBitrateHelp": "Укажите максимальный битрейт при трансляции музыки.", "LabelName": "Имя:", @@ -699,7 +699,7 @@ "LabelNumber": "Номер:", "LabelNumberOfGuideDays": "Число дней для загрузки данных телегида:", "LabelNumberOfGuideDaysHelp": "Больше дней загрузки данных телегида обеспечивает возможность заблаговременно назначать расписание и просматривать больше перечней, однако это займёт больше времени для загрузки. При значении «Авто» выбор определяется числом каналов.", - "LabelOptionalNetworkPath": "(Необязательно) Общедоступная сетевая папка:", + "LabelOptionalNetworkPath": "Общедоступная сетевая папка:", "LabelOptionalNetworkPathHelp": "Если данная папка является общей в сети, указание пути к сетевой папке может позволить Jellyfin-приложениям на других устройствах иметь прямой доступ к медиафайлам. Например, {0} или {1}.", "LabelOriginalAspectRatio": "Исходное соотношение сторон:", "LabelOriginalTitle": "Оригинальное название:", @@ -744,7 +744,7 @@ "LabelReleaseDate": "Дата выпуска:", "LabelRemoteClientBitrateLimit": "Ограничение битрейта интернет-трансляции, Мбит/с:", "LabelRemoteClientBitrateLimitHelp": "Необязательное ограничение битрейта для каждого из сетевых устройств. Может потребоваться, чтобы не допускать использования устройствами большего битрейта, чем способно пропустить интернет-соединение. Может привести к росту загрузки процессора на вашем сервере, так как потребуется динамическое перекодирование видео для снижения битрейта.", - "LabelRuntimeMinutes": "Длительность, мин:", + "LabelRuntimeMinutes": "Длительность:", "LabelSaveLocalMetadata": "Сохранять иллюстрации внутри медиапапок", "LabelSaveLocalMetadataHelp": "При сохранении иллюстраций внутри медиапапок, те помещаются в месте, где их можно легко править.", "LabelScheduledTaskLastRan": "Последний запуск был {0}, занял {1}.", @@ -756,7 +756,7 @@ "LabelSelectVersionToInstall": "Выбрать версию для установки:", "LabelSendNotificationToUsers": "Передача уведомления для:", "LabelSerialNumber": "Серийный номер", - "LabelSeriesRecordingPath": "Путь к записываемым сериалам (необязательно):", + "LabelSeriesRecordingPath": "Путь к записываемым сериалам:", "LabelServerHost": "Узел:", "LabelServerHostHelp": "192.168.1.100:8096 или https://myserver.com", "LabelSimultaneousConnectionLimit": "Лимит одновременных потоков:", @@ -917,7 +917,7 @@ "MessageYouHaveVersionInstalled": "В настоящее время установлена версия {0}.", "Metadata": "Метаданные", "MetadataManager": "Дисп. метаданных", - "MetadataSettingChangeHelp": "Изменение параметров метаданных повлияет на новое содержание, которое будет добавляться в дальнейшем. Чтобы обновить существующие содержание, откройте экран с подробностями и нажмите кнопку Обновить, или выполните массовое обновление, с помощью Диспетчера метаданных.", + "MetadataSettingChangeHelp": "Изменение параметров метаданных повлияет на новое содержание, добавляемое в будущем.. Чтобы обновить существующие содержание, откройте экран с подробностями и нажмите кнопку Обновить, или выполните массовое обновление, с помощью Диспетчера метаданных.", "MinutesAfter": "минут(у/ы) после", "MinutesBefore": "минут(у/ы) до", "Mobile": "Мобильный", @@ -966,7 +966,7 @@ "OptionAllowLinkSharingHelp": "Общедоступны только веб-страницы содержащие сведения о медиаданных. Медиафайлы никогда не предоставляются для общего просмотра. Совместно используемые ресурсы ограничены во времени, а срок действия истекает через {0} дн(я/ей).", "OptionAllowManageLiveTv": "Разрешить управление эфирными записями", "OptionAllowMediaPlayback": "Разрешить воспроизведение медиаданных", - "OptionAllowMediaPlaybackTranscodingHelp": "Ограничение доступа к перекодировке может привести к сбоям воспроизведения в Jellyfin-приложениях из-за неподдерживаемых форматов носителей.", + "OptionAllowMediaPlaybackTranscodingHelp": "Ограничение доступа к перекодировке может привести к сбоям воспроизведения в клиентах из-за неподдерживаемых форматов носителей.", "OptionAllowRemoteControlOthers": "Разрешить удалённое управление другими пользователями", "OptionAllowRemoteSharedDevices": "Разрешить удалённое управление используемыми совместно устройствами", "OptionAllowRemoteSharedDevicesHelp": "DLNA-устройства считаются используемыми совместно, пока какой-либо пользователь не начнёт управлять ими.", @@ -979,7 +979,7 @@ "OptionAuto": "Авто", "OptionAutomatic": "Авто", "OptionAutomaticallyGroupSeries": "Автоматически сливать вместе сериалы, которые разбросаны по нескольким папкам", - "OptionAutomaticallyGroupSeriesHelp": "При включении, сериалы, которые разбросаны по нескольким папкам данной медиатеки, будут автоматически слиты в единый сериал.", + "OptionAutomaticallyGroupSeriesHelp": "Части сериала, которые разбросаны по нескольким папкам данной медиатеки, будут автоматически слиты в единый сериал.", "OptionBlockBooks": "Книги", "OptionBlockChannelContent": "Содержание интернет-канала", "OptionBlockLiveTvChannels": "Эфирные каналы", @@ -999,7 +999,7 @@ "OptionDatePlayed": "Дата воспроизведения", "OptionDescending": "По убыванию", "OptionDisableUser": "Заблокировать пользователя", - "OptionDisableUserHelp": "При блокировании, этому пользователю не разрешаются любые подключения к серверу. Имеющиеся соединения будут разорваны.", + "OptionDisableUserHelp": "Этому пользователю не разрешаются любые подключения к серверу. Имеющиеся соединения будут разорваны.", "OptionDislikes": "Не нравящиеся", "OptionDisplayFolderView": "Отображать аспект Папки для просмотра обычных медиапапок", "OptionDisplayFolderViewHelp": "Отображение аспекта \"Папки\" рядом с другими вашими медиатеками. Это может быть полезно, если вы хотите вид обычных папок.", @@ -1009,7 +1009,7 @@ "OptionDownloadBoxImage": "DVD-бокс", "OptionDownloadDiscImage": "Диск", "OptionDownloadImagesInAdvance": "Загружать изображения заблаговременно", - "OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство изображений загружаются только при запросе от Jellyfin-приложения. Включите данную опцию, чтобы загружать все изображения заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.", + "OptionDownloadImagesInAdvanceHelp": "По умолчанию, большинство изображений загружаются только при запросе от клиента. Включите данную опцию, чтобы загружать все изображения заблаговременно, при импорте новых медиаданных. Это может привести к существенно длительным сканированиям медиатеки.", "OptionDownloadLogoImage": "Логотип", "OptionDownloadMenuImage": "Меню", "OptionDownloadPrimaryImage": "Основной", @@ -1042,7 +1042,7 @@ "OptionHlsSegmentedSubtitles": "Сегмент. субтитры HLS", "OptionHomeVideos": "Фотографии", "OptionIgnoreTranscodeByteRangeRequests": "Игнорировать запросы диапазона байтов перекодировки", - "OptionIgnoreTranscodeByteRangeRequestsHelp": "При включении, эти запросы будут учтены, но заголовок диапазона байтов будет проигнорирован.", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Эти запросы будут учтены, но заголовок диапазона байтов будет проигнорирован.", "OptionImdbRating": "Оценка IMDb", "OptionLikes": "Нравящиеся", "OptionMax": "Макс.", @@ -1055,9 +1055,9 @@ "OptionOnInterval": "В интервале", "OptionParentalRating": "Возрастная категория", "OptionPlainStorageFolders": "Отображать все папки, как обычные папки хранения", - "OptionPlainStorageFoldersHelp": "При включении, все папки описываются в DIDL как «object.container.storageFolder», вместо более конкретного типа, например, «object.container.person.musicArtist».", + "OptionPlainStorageFoldersHelp": "Все папки описываются в DIDL как «object.container.storageFolder», вместо более специфичного типа, например, «object.container.person.musicArtist».", "OptionPlainVideoItems": "Отображать все видео, как обычные видео элементы", - "OptionPlainVideoItemsHelp": "При включении, все видео описываются в DIDL как «object.item.videoItem», вместо более конкретного типа, например, «object.item.videoItem.movie».", + "OptionPlainVideoItemsHelp": "Все видео описываются в DIDL как «object.item.videoItem», вместо более специфичного типа, например, «object.item.videoItem.movie».", "OptionPlayCount": "Кол. воспроизведений", "OptionPlayed": "Воспроизведённые", "OptionPremiereDate": "Дата премьеры", @@ -1148,7 +1148,7 @@ "RecordingScheduled": "Запись назначена.", "Recordings": "Записи", "Refresh": "Обновить", - "RefreshDialogHelp": "Обновление метаданных определяются параметрами и интернет-услугами, которые включены в Панели Jellyfin Server.", + "RefreshDialogHelp": "Обновление метаданных определяются параметрами и интернет-услугами, которые включены в Панели.", "RefreshMetadata": "Обновить метаданные", "RefreshQueued": "Обновление в очереди.", "ReleaseDate": "Дата выпуска", @@ -1395,11 +1395,11 @@ "OptionPosterCard": "Постер-карта", "OptionThumb": "Эскиз", "OptionThumbCard": "Эскиз-карта", - "PasswordResetProviderHelp": "Выберите поставщика сброса пароля, который будет использоваться, когда этот пользователь запрашивает сброс пароля", + "PasswordResetProviderHelp": "Выберите поставщика сброса пароля, который использовуется при запросе пользователем сброса пароля", "PlaybackData": "Данные воспроизведения", "SubtitleOffset": "Сдвиг субтитров", "TabNetworking": "Работа в сети", - "LabelBaseUrlHelp": "Добавляется пользовательский подкаталог к URL сервера. Например: http://example.com/<baseurl>", + "LabelBaseUrlHelp": "Добавляет пользовательский подкаталог к URL сервера. Например: http://example.com/<baseurl>", "LabelPlayer": "Проигрыватель:", "MoreMediaInfo": "О медиаданных", "LabelVideoCodec": "Видео кодек:", @@ -1472,7 +1472,7 @@ "UnsupportedPlayback": "Jellyfin не может расшифровать содержимое, защищенное DRM, но в любом случае будет предпринята попытка расшифровки всего содержимого, включая защищенные заголовки. Некоторые файлы могут выглядеть полностью черными из-за шифрования или других неподдерживаемых функций, таких как интерактивные заголовки.", "HeaderFavoritePlaylists": "Избранные плей-листы", "LabelRequireHttpsHelp": "Если этот флажок установлен, сервер будет автоматически перенаправлять все запросы через HTTP на HTTPS. Это не имеет никакого эффекта, если сервер не слушает HTTPS.", - "LabelEnableHttpsHelp": "Позволяет серверу слушать HTTPS-порт. Для работы необходим действующий сертификат.", + "LabelEnableHttpsHelp": "Прослушивается указанный HTTPS-порт. Чтобы это вступило в силу, также необходимо предоставить действительный сертификат.", "ApiKeysCaption": "Список действующих текущих API-ключей", "TabDVR": "DVR", "SaveChanges": "Сохранить изменения", @@ -1539,5 +1539,8 @@ "Writers": "Сценаристы", "ViewAlbumArtist": "Посмотреть альбом исполнителя", "ClearQueue": "Очистить очередь", - "ButtonPlayer": "Проигрыватель" + "ButtonPlayer": "Проигрыватель", + "PreviousTrack": "Перейти к предыдущему", + "NextTrack": "Перейти к следующему", + "LabelUnstable": "Нестабильная" } From 9d822d911af4fa09ef67a41bd20048d194ffeb13 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 08:43:52 +0100 Subject: [PATCH 090/155] Migration of sortMenu to ES6 module --- package.json | 1 + src/components/sortmenu/sortmenu.js | 87 +++++++++++++++-------------- src/controllers/list.js | 2 +- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..33c4670d5d 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/components/sortmenu/sortmenu.js", "src/plugins/htmlVideoPlayer/plugin.js", "src/components/search/searchfields.js", "src/components/search/searchresults.js", diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index f62e5bb3a4..d38d98c090 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -1,46 +1,51 @@ -define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'globalize', 'userSettings', 'emby-select', 'paper-icon-button-light', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (require, dom, focusManager, dialogHelper, loading, layoutManager, connectionManager, globalize, userSettings) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import layoutManager from 'layoutManager'; +import globalize from 'globalize'; +import * as userSettings from 'userSettings'; +import 'emby-select'; +import 'paper-icon-button-light'; +import 'material-icons'; +import 'css!./../formdialog'; +import 'emby-button'; +import 'flexStyles'; - function onSubmit(e) { - e.preventDefault(); - return false; - } +function onSubmit(e) { + e.preventDefault(); + return false; +} - function initEditor(context, settings) { - context.querySelector('form').addEventListener('submit', onSubmit); +function initEditor(context, settings) { + context.querySelector('form').addEventListener('submit', onSubmit); - context.querySelector('.selectSortOrder').value = settings.sortOrder; - context.querySelector('.selectSortBy').value = settings.sortBy; - } + context.querySelector('.selectSortOrder').value = settings.sortOrder; + context.querySelector('.selectSortBy').value = settings.sortBy; +} - function centerFocus(elem, horiz, on) { - require(['scrollHelper'], function (scrollHelper) { - var fn = on ? 'on' : 'off'; - scrollHelper.centerFocus[fn](elem, horiz); - }); - } +function centerFocus(elem, horiz, on) { + import('scrollHelper').then(({default: scrollHelper}) => { + const fn = on ? 'on' : 'off'; + scrollHelper.centerFocus[fn](elem, horiz); + }); +} - function fillSortBy(context, options) { - var selectSortBy = context.querySelector('.selectSortBy'); +function fillSortBy(context, options) { + const selectSortBy = context.querySelector('.selectSortBy'); - selectSortBy.innerHTML = options.map(function (o) { - return ''; - }).join(''); - } + selectSortBy.innerHTML = options.map(function (o) { + return ''; + }).join(''); +} - function saveValues(context, settings, settingsKey) { - userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value); - userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value); - } +function saveValues(context, settingsKey) { + userSettings.setFilter(settingsKey + '-sortorder', context.querySelector('.selectSortOrder').value); + userSettings.setFilter(settingsKey + '-sortby', context.querySelector('.selectSortBy').value); +} - function SortMenu() { - - } - - SortMenu.prototype.show = function (options) { +class SortMenu { + show(options) { return new Promise(function (resolve, reject) { - require(['text!./sortmenu.template.html'], function (template) { - var dialogOptions = { + import('text!./sortmenu.template.html').then(({default: template}) => { + const dialogOptions = { removeOnClose: true, scrollY: false }; @@ -51,11 +56,11 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana dialogOptions.size = 'small'; } - var dlg = dialogHelper.createDialog(dialogOptions); + const dlg = dialogHelper.createDialog(dialogOptions); dlg.classList.add('formDialog'); - var html = ''; + let html = ''; html += '
'; html += ''; @@ -78,7 +83,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana centerFocus(dlg.querySelector('.formDialogContent'), false, true); } - var submitted; + let submitted; dlg.querySelector('form').addEventListener('change', function () { submitted = true; @@ -90,7 +95,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana } if (submitted) { - saveValues(dlg, options.settings, options.settingsKey); + saveValues(dlg, options.settingsKey); resolve(); return; } @@ -99,7 +104,7 @@ define(['require', 'dom', 'focusManager', 'dialogHelper', 'loading', 'layoutMana }); }); }); - }; + } +} - return SortMenu; -}); +export default SortMenu; diff --git a/src/controllers/list.js b/src/controllers/list.js index 68e81f05dd..904ef5aa66 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -371,7 +371,7 @@ define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager' var instance = this; require(['sortMenu'], function (SortMenu) { - new SortMenu().show({ + new SortMenu.default().show({ settingsKey: instance.getSettingsKey(), settings: instance.getSortValues(), onChange: instance.itemsContainer.refreshItems.bind(instance.itemsContainer), From f3c00499824d6664bc26e705817c1a655aaf82b3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 08:44:04 +0100 Subject: [PATCH 091/155] Migration of slideshow to ES6 module --- package.json | 1 + src/components/slideshow/slideshow.js | 1290 +++++++++++++------------ 2 files changed, 649 insertions(+), 642 deletions(-) diff --git a/package.json b/package.json index 33c4670d5d..fa20c96339 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/components/slideshow/slideshow.js", "src/components/sortmenu/sortmenu.js", "src/plugins/htmlVideoPlayer/plugin.js", "src/components/search/searchfields.js", diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 60c458e234..82f541a116 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -2,666 +2,672 @@ * Image viewer component * @module components/slideshow/slideshow */ -define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'dom', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost, dom) { - 'use strict'; +import dialogHelper from 'dialogHelper'; +import inputManager from 'inputManager'; +import connectionManager from 'connectionManager'; +import layoutManager from 'layoutManager'; +import focusManager from 'focusManager'; +import browser from 'browser'; +import appHost from 'apphost'; +import dom from 'dom'; +import 'css!./style'; +import 'material-icons'; +import 'paper-icon-button-light'; - browser = browser.default || browser; +/** + * Name of transition event. + */ +const transitionEndEventName = dom.whichTransitionEvent(); - /** - * Name of transition event. - */ - const transitionEndEventName = dom.whichTransitionEvent(); +/** + * Flag to use fake image to fix blurry zoomed image. + * At least WebKit doesn't restore quality for zoomed images. + */ +const useFakeZoomImage = browser.safari; - /** - * Flag to use fake image to fix blurry zoomed image. - * At least WebKit doesn't restore quality for zoomed images. - */ - const useFakeZoomImage = browser.safari; +/** + * Retrieves an item's image URL from the API. + * @param {object|string} item - Item used to generate the image URL. + * @param {object} options - Options of the image. + * @param {object} apiClient - API client instance used to retrieve the image. + * @returns {null|string} URL of the item's image. + */ +function getImageUrl(item, options, apiClient) { + options = options || {}; + options.type = options.type || 'Primary'; - /** - * Retrieves an item's image URL from the API. - * @param {object|string} item - Item used to generate the image URL. - * @param {object} options - Options of the image. - * @param {object} apiClient - API client instance used to retrieve the image. - * @returns {null|string} URL of the item's image. - */ - function getImageUrl(item, options, apiClient) { - options = options || {}; - options.type = options.type || 'Primary'; + if (typeof (item) === 'string') { + return apiClient.getScaledImageUrl(item, options); + } - if (typeof (item) === 'string') { - return apiClient.getScaledImageUrl(item, options); + if (item.ImageTags && item.ImageTags[options.type]) { + options.tag = item.ImageTags[options.type]; + return apiClient.getScaledImageUrl(item.Id, options); + } + + if (options.type === 'Primary') { + if (item.AlbumId && item.AlbumPrimaryImageTag) { + options.tag = item.AlbumPrimaryImageTag; + return apiClient.getScaledImageUrl(item.AlbumId, options); } + } - if (item.ImageTags && item.ImageTags[options.type]) { - options.tag = item.ImageTags[options.type]; - return apiClient.getScaledImageUrl(item.Id, options); + return null; +} + +/** + * Retrieves a backdrop's image URL from the API. + * @param {object} item - Item used to generate the image URL. + * @param {object} options - Options of the image. + * @param {object} apiClient - API client instance used to retrieve the image. + * @returns {null|string} URL of the item's backdrop. + */ +function getBackdropImageUrl(item, options, apiClient) { + options = options || {}; + options.type = options.type || 'Backdrop'; + + // If not resizing, get the original image + if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { + options.quality = 100; + } + + if (item.BackdropImageTags && item.BackdropImageTags.length) { + options.tag = item.BackdropImageTags[0]; + return apiClient.getScaledImageUrl(item.Id, options); + } + + return null; +} + +/** + * Dispatches a request for an item's image to its respective handler. + * @param {object} item - Item used to generate the image URL. + * @returns {string} URL of the item's image. + */ +function getImgUrl(item, user) { + const apiClient = connectionManager.getApiClient(item.ServerId); + const imageOptions = {}; + + if (item.BackdropImageTags && item.BackdropImageTags.length) { + return getBackdropImageUrl(item, imageOptions, apiClient); + } else { + if (item.MediaType === 'Photo' && user && user.Policy.EnableContentDownloading) { + return apiClient.getItemDownloadUrl(item.Id); } + imageOptions.type = 'Primary'; + return getImageUrl(item, imageOptions, apiClient); + } +} - if (options.type === 'Primary') { - if (item.AlbumId && item.AlbumPrimaryImageTag) { - options.tag = item.AlbumPrimaryImageTag; - return apiClient.getScaledImageUrl(item.AlbumId, options); +/** + * Generates a button using the specified icon, classes and properties. + * @param {string} icon - Name of the material icon on the button + * @param {string} cssClass - CSS classes to assign to the button + * @param {boolean} canFocus - Flag to set the tabindex attribute on the button to -1. + * @param {boolean} autoFocus - Flag to set the autofocus attribute on the button. + * @returns {string} The HTML markup of the button. + */ +function getIcon(icon, cssClass, canFocus, autoFocus) { + const tabIndex = canFocus ? '' : ' tabindex="-1"'; + autoFocus = autoFocus ? ' autofocus' : ''; + return ''; +} + +/** + * Sets the viewport meta tag to enable or disable scaling by the user. + * @param {boolean} scalable - Flag to set the scalability of the viewport. + */ +function setUserScalable(scalable) { + try { + appHost.setUserScalable(scalable); + } catch (err) { + console.error('error in appHost.setUserScalable: ' + err); + } +} + +export default function (options) { + const self = this; + /** Initialized instance of Swiper. */ + let swiperInstance; + /** Initialized instance of the dialog containing the Swiper instance. */ + let dialog; + /** Options of the slideshow components */ + let currentOptions; + /** ID of the timeout used to hide the OSD. */ + let hideTimeout; + /** Last coordinates of the mouse pointer. */ + let lastMouseMoveData; + + /** + * Creates the HTML markup for the dialog and the OSD. + * @param {Object} options - Options used to create the dialog and slideshow. + */ + function createElements(options) { + currentOptions = options; + + dialog = dialogHelper.createDialog({ + exitAnimationDuration: options.interactive ? 400 : 800, + size: 'fullscreen', + autoFocus: false, + scrollY: false, + exitAnimation: 'fadeout', + removeOnClose: true + }); + + dialog.classList.add('slideshowDialog'); + + let html = ''; + + html += '
'; + + if (options.interactive && !layoutManager.tv) { + const actionButtonsOnTop = layoutManager.mobile; + + html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); + html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false); + + html += '
'; + if (actionButtonsOnTop) { + if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { + html += getIcon('file_download', 'btnDownload slideshowButton', true); + } + if (appHost.supports('sharing')) { + html += getIcon('share', 'btnShare slideshowButton', true); + } } - } + html += getIcon('close', 'slideshowButton btnSlideshowExit hide-mouse-idle-tv', false); + html += '
'; - return null; - } + if (!actionButtonsOnTop) { + html += '
'; - /** - * Retrieves a backdrop's image URL from the API. - * @param {object} item - Item used to generate the image URL. - * @param {object} options - Options of the image. - * @param {object} apiClient - API client instance used to retrieve the image. - * @returns {null|string} URL of the item's backdrop. - */ - function getBackdropImageUrl(item, options, apiClient) { - options = options || {}; - options.type = options.type || 'Backdrop'; + html += getIcon('play_arrow', 'btnSlideshowPause slideshowButton', true, true); + if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { + html += getIcon('file_download', 'btnDownload slideshowButton', true); + } + if (appHost.supports('sharing')) { + html += getIcon('share', 'btnShare slideshowButton', true); + } - // If not resizing, get the original image - if (!options.maxWidth && !options.width && !options.maxHeight && !options.height) { - options.quality = 100; - } - - if (item.BackdropImageTags && item.BackdropImageTags.length) { - options.tag = item.BackdropImageTags[0]; - return apiClient.getScaledImageUrl(item.Id, options); - } - - return null; - } - - /** - * Dispatches a request for an item's image to its respective handler. - * @param {object} item - Item used to generate the image URL. - * @returns {string} URL of the item's image. - */ - function getImgUrl(item, user) { - var apiClient = connectionManager.getApiClient(item.ServerId); - var imageOptions = {}; - - if (item.BackdropImageTags && item.BackdropImageTags.length) { - return getBackdropImageUrl(item, imageOptions, apiClient); + html += '
'; + } } else { - if (item.MediaType === 'Photo' && user && user.Policy.EnableContentDownloading) { - return apiClient.getItemDownloadUrl(item.Id); - } - imageOptions.type = 'Primary'; - return getImageUrl(item, imageOptions, apiClient); - } - } - - /** - * Generates a button using the specified icon, classes and properties. - * @param {string} icon - Name of the material icon on the button - * @param {string} cssClass - CSS classes to assign to the button - * @param {boolean} canFocus - Flag to set the tabindex attribute on the button to -1. - * @param {boolean} autoFocus - Flag to set the autofocus attribute on the button. - * @returns {string} The HTML markup of the button. - */ - function getIcon(icon, cssClass, canFocus, autoFocus) { - var tabIndex = canFocus ? '' : ' tabindex="-1"'; - autoFocus = autoFocus ? ' autofocus' : ''; - return ''; - } - - /** - * Sets the viewport meta tag to enable or disable scaling by the user. - * @param {boolean} scalable - Flag to set the scalability of the viewport. - */ - function setUserScalable(scalable) { - try { - appHost.setUserScalable(scalable); - } catch (err) { - console.error('error in appHost.setUserScalable: ' + err); - } - } - - return function (options) { - var self = this; - /** Initialized instance of Swiper. */ - var swiperInstance; - /** Initialized instance of the dialog containing the Swiper instance. */ - var dialog; - /** Options of the slideshow components */ - var currentOptions; - /** ID of the timeout used to hide the OSD. */ - var hideTimeout; - /** Last coordinates of the mouse pointer. */ - var lastMouseMoveData; - - /** - * Creates the HTML markup for the dialog and the OSD. - * @param {Object} options - Options used to create the dialog and slideshow. - */ - function createElements(options) { - currentOptions = options; - - dialog = dialogHelper.createDialog({ - exitAnimationDuration: options.interactive ? 400 : 800, - size: 'fullscreen', - autoFocus: false, - scrollY: false, - exitAnimation: 'fadeout', - removeOnClose: true - }); - - dialog.classList.add('slideshowDialog'); - - var html = ''; - - html += '
'; - - if (options.interactive && !layoutManager.tv) { - var actionButtonsOnTop = layoutManager.mobile; - - html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); - html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false); - - html += '
'; - if (actionButtonsOnTop) { - if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { - html += getIcon('file_download', 'btnDownload slideshowButton', true); - } - if (appHost.supports('sharing')) { - html += getIcon('share', 'btnShare slideshowButton', true); - } - } - html += getIcon('close', 'slideshowButton btnSlideshowExit hide-mouse-idle-tv', false); - html += '
'; - - if (!actionButtonsOnTop) { - html += '
'; - - html += getIcon('play_arrow', 'btnSlideshowPause slideshowButton', true, true); - if (appHost.supports('filedownload') && options.user && options.user.Policy.EnableContentDownloading) { - html += getIcon('file_download', 'btnDownload slideshowButton', true); - } - if (appHost.supports('sharing')) { - html += getIcon('share', 'btnShare slideshowButton', true); - } - - html += '
'; - } - } else { - html += '

'; - } - - dialog.innerHTML = html; - - if (options.interactive && !layoutManager.tv) { - dialog.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { - dialogHelper.close(dialog); - }); - - var btnPause = dialog.querySelector('.btnSlideshowPause'); - if (btnPause) { - btnPause.addEventListener('click', playPause); - } - - var btnDownload = dialog.querySelector('.btnDownload'); - if (btnDownload) { - btnDownload.addEventListener('click', download); - } - - var btnShare = dialog.querySelector('.btnShare'); - if (btnShare) { - btnShare.addEventListener('click', share); - } - } - - setUserScalable(true); - - dialogHelper.open(dialog).then(function () { - setUserScalable(false); - }); - - inputManager.on(window, onInputCommand); - /* eslint-disable-next-line compat/compat */ - document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); - - dialog.addEventListener('close', onDialogClosed); - - loadSwiper(dialog, options); + html += '

'; } - /** - * Handles OSD changes when the autoplay is started. - */ - function onAutoplayStart() { - var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); - if (btnSlideshowPause) { - btnSlideshowPause.classList.replace('play_arrow', 'pause'); - } - } + dialog.innerHTML = html; - /** - * Handles OSD changes when the autoplay is stopped. - */ - function onAutoplayStop() { - var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); - if (btnSlideshowPause) { - btnSlideshowPause.classList.replace('pause', 'play_arrow'); - } - } - - /** - * Handles zoom changes. - */ - function onZoomChange(scale, imageEl, slideEl) { - const zoomImage = slideEl.querySelector('.swiper-zoom-fakeimg'); - - if (zoomImage) { - zoomImage.style.width = zoomImage.style.height = scale * 100 + '%'; - - if (scale > 1) { - if (zoomImage.classList.contains('swiper-zoom-fakeimg-hidden')) { - // Await for Swiper style changes - setTimeout(() => { - const callback = () => { - imageEl.removeEventListener(transitionEndEventName, callback); - zoomImage.classList.remove('swiper-zoom-fakeimg-hidden'); - }; - - // Swiper set 'transition-duration: 300ms' for auto zoom - // and 'transition-duration: 0s' for touch zoom - const transitionDuration = parseFloat(imageEl.style.transitionDuration.replace(/[a-z]/i, '')); - - if (transitionDuration > 0) { - imageEl.addEventListener(transitionEndEventName, callback); - } else { - callback(); - } - }, 0); - } - } else { - zoomImage.classList.add('swiper-zoom-fakeimg-hidden'); - } - } - } - - /** - * Initializes the Swiper instance and binds the relevant events. - * @param {HTMLElement} dialog - Element containing the dialog. - * @param {Object} options - Options used to initialize the Swiper instance. - */ - function loadSwiper(dialog, options) { - var slides; - if (currentOptions.slides) { - slides = currentOptions.slides; - } else { - slides = currentOptions.items; - } - - require(['swiper'], function (Swiper) { - swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { - direction: 'horizontal', - // Loop is disabled due to the virtual slides option not supporting it. - loop: false, - zoom: { - minRatio: 1, - toggle: true - }, - autoplay: !options.interactive, - keyboard: { - enabled: true - }, - preloadImages: true, - slidesPerView: 1, - slidesPerColumn: 1, - initialSlide: options.startIndex || 0, - speed: 240, - navigation: { - nextEl: '.btnSlideshowNext', - prevEl: '.btnSlideshowPrevious' - }, - // Virtual slides reduce memory consumption for large libraries while allowing preloading of images; - virtual: { - slides: slides, - cache: true, - renderSlide: getSwiperSlideHtml, - addSlidesBefore: 1, - addSlidesAfter: 1 - } - }); - - swiperInstance.on('autoplayStart', onAutoplayStart); - swiperInstance.on('autoplayStop', onAutoplayStop); - - if (useFakeZoomImage) { - swiperInstance.on('zoomChange', onZoomChange); - } - }); - } - - /** - * Renders the HTML markup of a slide for an item or a slide. - * @param {Object} item - The item used to render the slide. - * @param {number} index - The index of the item in the Swiper instance. - * @returns {string} The HTML markup of the slide. - */ - function getSwiperSlideHtml(item, index) { - if (currentOptions.slides) { - return getSwiperSlideHtmlFromSlide(item); - } else { - return getSwiperSlideHtmlFromItem(item); - } - } - - /** - * Renders the HTML markup of a slide for an item. - * @param {Object} item - Item used to generate the slide. - * @returns {string} The HTML markup of the slide. - */ - function getSwiperSlideHtmlFromItem(item) { - return getSwiperSlideHtmlFromSlide({ - originalImage: getImgUrl(item, currentOptions.user), - Id: item.Id, - ServerId: item.ServerId - }); - } - - /** - * Renders the HTML markup of a slide for a slide object. - * @param {Object} item - Slide object used to generate the slide. - * @returns {string} The HTML markup of the slide. - */ - function getSwiperSlideHtmlFromSlide(item) { - var html = ''; - html += '
'; - html += '
'; - if (useFakeZoomImage) { - html += `
`; - } - html += ''; - html += '
'; - if (item.title || item.subtitle) { - html += '
'; - html += '
'; - if (item.title) { - html += '

'; - html += item.title; - html += '

'; - } - if (item.description) { - html += '
'; - html += item.description; - html += '
'; - } - html += '
'; - html += '
'; - } - html += '
'; - - return html; - } - - /** - * Fetches the information of the currently displayed slide. - * @returns {null|{itemId: string, shareUrl: string, serverId: string, url: string}} Object containing the information of the currently displayed slide. - */ - function getCurrentImageInfo() { - if (swiperInstance) { - var slide = document.querySelector('.swiper-slide-active'); - - if (slide) { - return { - url: slide.getAttribute('data-original'), - shareUrl: slide.getAttribute('data-original'), - itemId: slide.getAttribute('data-itemid'), - serverId: slide.getAttribute('data-serverid') - }; - } - return null; - } else { - return null; - } - } - - /** - * Starts a download for the currently displayed slide. - */ - function download() { - var imageInfo = getCurrentImageInfo(); - - require(['fileDownloader'], function (fileDownloader) { - fileDownloader.download([imageInfo]); - }); - } - - /** - * Shares the currently displayed slide using the browser's built-in sharing feature. - */ - function share() { - var imageInfo = getCurrentImageInfo(); - - navigator.share({ - url: imageInfo.shareUrl - }); - } - - /** - * Starts the autoplay feature of the Swiper instance. - */ - function play() { - if (swiperInstance.autoplay) { - swiperInstance.autoplay.start(); - } - } - - /** - * Pauses the autoplay feature of the Swiper instance; - */ - function pause() { - if (swiperInstance.autoplay) { - swiperInstance.autoplay.stop(); - } - } - - /** - * Toggles the autoplay feature of the Swiper instance. - */ - function playPause() { - var paused = !dialog.querySelector('.btnSlideshowPause .material-icons').classList.contains('pause'); - if (paused) { - play(); - } else { - pause(); - } - } - - /** - * Closes the dialog and destroys the Swiper instance. - */ - function onDialogClosed() { - var swiper = swiperInstance; - if (swiper) { - swiper.destroy(true, true); - swiperInstance = null; - } - - inputManager.off(window, onInputCommand); - /* eslint-disable-next-line compat/compat */ - document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); - // Shows page scrollbar - document.body.classList.remove('hide-scroll'); - document.body.classList.add('force-scroll'); - } - - /** - * Shows the OSD. - */ - function showOsd() { - var bottom = dialog.querySelector('.slideshowBottomBar'); - if (bottom) { - slideUpToShow(bottom); - startHideTimer(); - } - } - - /** - * Hides the OSD. - */ - function hideOsd() { - var bottom = dialog.querySelector('.slideshowBottomBar'); - if (bottom) { - slideDownToHide(bottom); - } - } - - /** - * Starts the timer used to automatically hide the OSD. - */ - function startHideTimer() { - stopHideTimer(); - hideTimeout = setTimeout(hideOsd, 3000); - } - - /** - * Stops the timer used to automatically hide the OSD. - */ - function stopHideTimer() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } - } - - /** - * Shows the OSD by sliding it into view. - * @param {HTMLElement} element - Element containing the OSD. - */ - function slideUpToShow(element) { - if (!element.classList.contains('hide')) { - return; - } - - element.classList.remove('hide'); - - var onFinish = function () { - focusManager.focus(element.querySelector('.btnSlideshowPause')); - }; - - if (!element.animate) { - onFinish(); - return; - } - - requestAnimationFrame(function () { - var keyframes = [ - { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, - { transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 } - ]; - var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; - element.animate(keyframes, timing).onfinish = onFinish; - }); - } - - /** - * Hides the OSD by sliding it out of view. - * @param {HTMLElement} element - Element containing the OSD. - */ - function slideDownToHide(element) { - if (element.classList.contains('hide')) { - return; - } - - var onFinish = function () { - element.classList.add('hide'); - }; - - if (!element.animate) { - onFinish(); - return; - } - - requestAnimationFrame(function () { - var keyframes = [ - { transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 }, - { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } - ]; - var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; - element.animate(keyframes, timing).onfinish = onFinish; - }); - } - - /** - * Shows the OSD when moving the mouse pointer or touching the screen. - * @param {Event} event - Pointer movement event. - */ - function onPointerMove(event) { - var pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); - - if (pointerType === 'mouse') { - var eventX = event.screenX || 0; - var eventY = event.screenY || 0; - - var obj = lastMouseMoveData; - if (!obj) { - lastMouseMoveData = { - x: eventX, - y: eventY - }; - return; - } - - // if coord are same, it didn't move - if (Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10) { - return; - } - - obj.x = eventX; - obj.y = eventY; - - showOsd(); - } - } - - /** - * Dispatches keyboard inputs to their proper handlers. - * @param {Event} event - Keyboard input event. - */ - function onInputCommand(event) { - switch (event.detail.command) { - case 'up': - case 'down': - case 'select': - case 'menu': - case 'info': - showOsd(); - break; - case 'play': - play(); - break; - case 'pause': - pause(); - break; - case 'playpause': - playPause(); - break; - default: - break; - } - } - - /** - * Shows the slideshow component. - */ - self.show = function () { - createElements(options); - // Hides page scrollbar - document.body.classList.remove('force-scroll'); - document.body.classList.add('hide-scroll'); - }; - - /** - * Hides the slideshow element. - */ - self.hide = function () { - if (dialog) { + if (options.interactive && !layoutManager.tv) { + dialog.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { dialogHelper.close(dialog); + }); + + const btnPause = dialog.querySelector('.btnSlideshowPause'); + if (btnPause) { + btnPause.addEventListener('click', playPause); } + + const btnDownload = dialog.querySelector('.btnDownload'); + if (btnDownload) { + btnDownload.addEventListener('click', download); + } + + const btnShare = dialog.querySelector('.btnShare'); + if (btnShare) { + btnShare.addEventListener('click', share); + } + } + + setUserScalable(true); + + dialogHelper.open(dialog).then(function () { + setUserScalable(false); + }); + + inputManager.on(window, onInputCommand); + /* eslint-disable-next-line compat/compat */ + document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); + + dialog.addEventListener('close', onDialogClosed); + + loadSwiper(dialog, options); + } + + /** + * Handles OSD changes when the autoplay is started. + */ + function onAutoplayStart() { + const btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); + if (btnSlideshowPause) { + btnSlideshowPause.classList.replace('play_arrow', 'pause'); + } + } + + /** + * Handles OSD changes when the autoplay is stopped. + */ + function onAutoplayStop() { + const btnSlideshowPause = dialog.querySelector('.btnSlideshowPause .material-icons'); + if (btnSlideshowPause) { + btnSlideshowPause.classList.replace('pause', 'play_arrow'); + } + } + + /** + * Handles zoom changes. + */ + function onZoomChange(scale, imageEl, slideEl) { + const zoomImage = slideEl.querySelector('.swiper-zoom-fakeimg'); + + if (zoomImage) { + zoomImage.style.width = zoomImage.style.height = scale * 100 + '%'; + + if (scale > 1) { + if (zoomImage.classList.contains('swiper-zoom-fakeimg-hidden')) { + // Await for Swiper style changes + setTimeout(() => { + const callback = () => { + imageEl.removeEventListener(transitionEndEventName, callback); + zoomImage.classList.remove('swiper-zoom-fakeimg-hidden'); + }; + + // Swiper set 'transition-duration: 300ms' for auto zoom + // and 'transition-duration: 0s' for touch zoom + const transitionDuration = parseFloat(imageEl.style.transitionDuration.replace(/[a-z]/i, '')); + + if (transitionDuration > 0) { + imageEl.addEventListener(transitionEndEventName, callback); + } else { + callback(); + } + }, 0); + } + } else { + zoomImage.classList.add('swiper-zoom-fakeimg-hidden'); + } + } + } + + /** + * Initializes the Swiper instance and binds the relevant events. + * @param {HTMLElement} dialog - Element containing the dialog. + * @param {Object} options - Options used to initialize the Swiper instance. + */ + function loadSwiper(dialog, options) { + let slides; + if (currentOptions.slides) { + slides = currentOptions.slides; + } else { + slides = currentOptions.items; + } + + import('swiper').then(({default: Swiper}) => { + swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { + direction: 'horizontal', + // Loop is disabled due to the virtual slides option not supporting it. + loop: false, + zoom: { + minRatio: 1, + toggle: true + }, + autoplay: !options.interactive, + keyboard: { + enabled: true + }, + preloadImages: true, + slidesPerView: 1, + slidesPerColumn: 1, + initialSlide: options.startIndex || 0, + speed: 240, + navigation: { + nextEl: '.btnSlideshowNext', + prevEl: '.btnSlideshowPrevious' + }, + // Virtual slides reduce memory consumption for large libraries while allowing preloading of images; + virtual: { + slides: slides, + cache: true, + renderSlide: getSwiperSlideHtml, + addSlidesBefore: 1, + addSlidesAfter: 1 + } + }); + + swiperInstance.on('autoplayStart', onAutoplayStart); + swiperInstance.on('autoplayStop', onAutoplayStop); + + if (useFakeZoomImage) { + swiperInstance.on('zoomChange', onZoomChange); + } + }); + } + + /** + * Renders the HTML markup of a slide for an item or a slide. + * @param {Object} item - The item used to render the slide. + * @param {number} index - The index of the item in the Swiper instance. + * @returns {string} The HTML markup of the slide. + */ + function getSwiperSlideHtml(item, index) { + if (currentOptions.slides) { + return getSwiperSlideHtmlFromSlide(item); + } else { + return getSwiperSlideHtmlFromItem(item); + } + } + + /** + * Renders the HTML markup of a slide for an item. + * @param {Object} item - Item used to generate the slide. + * @returns {string} The HTML markup of the slide. + */ + function getSwiperSlideHtmlFromItem(item) { + return getSwiperSlideHtmlFromSlide({ + originalImage: getImgUrl(item, currentOptions.user), + Id: item.Id, + ServerId: item.ServerId + }); + } + + /** + * Renders the HTML markup of a slide for a slide object. + * @param {Object} item - Slide object used to generate the slide. + * @returns {string} The HTML markup of the slide. + */ + function getSwiperSlideHtmlFromSlide(item) { + let html = ''; + html += '
'; + html += '
'; + if (useFakeZoomImage) { + html += `
`; + } + html += ''; + html += '
'; + if (item.title || item.subtitle) { + html += '
'; + html += '
'; + if (item.title) { + html += '

'; + html += item.title; + html += '

'; + } + if (item.description) { + html += '
'; + html += item.description; + html += '
'; + } + html += '
'; + html += '
'; + } + html += '
'; + + return html; + } + + /** + * Fetches the information of the currently displayed slide. + * @returns {null|{itemId: string, shareUrl: string, serverId: string, url: string}} Object containing the information of the currently displayed slide. + */ + function getCurrentImageInfo() { + if (swiperInstance) { + const slide = document.querySelector('.swiper-slide-active'); + + if (slide) { + return { + url: slide.getAttribute('data-original'), + shareUrl: slide.getAttribute('data-original'), + itemId: slide.getAttribute('data-itemid'), + serverId: slide.getAttribute('data-serverid') + }; + } + return null; + } else { + return null; + } + } + + /** + * Starts a download for the currently displayed slide. + */ + function download() { + const imageInfo = getCurrentImageInfo(); + + import('fileDownloader').then(({default: fileDownloader}) => { + fileDownloader.download([imageInfo]); + }); + } + + /** + * Shares the currently displayed slide using the browser's built-in sharing feature. + */ + function share() { + const imageInfo = getCurrentImageInfo(); + + navigator.share({ + url: imageInfo.shareUrl + }); + } + + /** + * Starts the autoplay feature of the Swiper instance. + */ + function play() { + if (swiperInstance.autoplay) { + swiperInstance.autoplay.start(); + } + } + + /** + * Pauses the autoplay feature of the Swiper instance; + */ + function pause() { + if (swiperInstance.autoplay) { + swiperInstance.autoplay.stop(); + } + } + + /** + * Toggles the autoplay feature of the Swiper instance. + */ + function playPause() { + const paused = !dialog.querySelector('.btnSlideshowPause .material-icons').classList.contains('pause'); + if (paused) { + play(); + } else { + pause(); + } + } + + /** + * Closes the dialog and destroys the Swiper instance. + */ + function onDialogClosed() { + const swiper = swiperInstance; + if (swiper) { + swiper.destroy(true, true); + swiperInstance = null; + } + + inputManager.off(window, onInputCommand); + /* eslint-disable-next-line compat/compat */ + document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); + // Shows page scrollbar + document.body.classList.remove('hide-scroll'); + document.body.classList.add('force-scroll'); + } + + /** + * Shows the OSD. + */ + function showOsd() { + const bottom = dialog.querySelector('.slideshowBottomBar'); + if (bottom) { + slideUpToShow(bottom); + startHideTimer(); + } + } + + /** + * Hides the OSD. + */ + function hideOsd() { + const bottom = dialog.querySelector('.slideshowBottomBar'); + if (bottom) { + slideDownToHide(bottom); + } + } + + /** + * Starts the timer used to automatically hide the OSD. + */ + function startHideTimer() { + stopHideTimer(); + hideTimeout = setTimeout(hideOsd, 3000); + } + + /** + * Stops the timer used to automatically hide the OSD. + */ + function stopHideTimer() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; + } + } + + /** + * Shows the OSD by sliding it into view. + * @param {HTMLElement} element - Element containing the OSD. + */ + function slideUpToShow(element) { + if (!element.classList.contains('hide')) { + return; + } + + element.classList.remove('hide'); + + const onFinish = function () { + focusManager.focus(element.querySelector('.btnSlideshowPause')); }; + + if (!element.animate) { + onFinish(); + return; + } + + requestAnimationFrame(function () { + const keyframes = [ + { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, + { transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 } + ]; + const timing = { duration: 300, iterations: 1, easing: 'ease-out' }; + element.animate(keyframes, timing).onfinish = onFinish; + }); + } + + /** + * Hides the OSD by sliding it out of view. + * @param {HTMLElement} element - Element containing the OSD. + */ + function slideDownToHide(element) { + if (element.classList.contains('hide')) { + return; + } + + const onFinish = function () { + element.classList.add('hide'); + }; + + if (!element.animate) { + onFinish(); + return; + } + + requestAnimationFrame(function () { + const keyframes = [ + { transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 }, + { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } + ]; + const timing = { duration: 300, iterations: 1, easing: 'ease-out' }; + element.animate(keyframes, timing).onfinish = onFinish; + }); + } + + /** + * Shows the OSD when moving the mouse pointer or touching the screen. + * @param {Event} event - Pointer movement event. + */ + function onPointerMove(event) { + const pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); + + if (pointerType === 'mouse') { + const eventX = event.screenX || 0; + const eventY = event.screenY || 0; + + const obj = lastMouseMoveData; + if (!obj) { + lastMouseMoveData = { + x: eventX, + y: eventY + }; + return; + } + + // if coord are same, it didn't move + if (Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10) { + return; + } + + obj.x = eventX; + obj.y = eventY; + + showOsd(); + } + } + + /** + * Dispatches keyboard inputs to their proper handlers. + * @param {Event} event - Keyboard input event. + */ + function onInputCommand(event) { + switch (event.detail.command) { + case 'up': + case 'down': + case 'select': + case 'menu': + case 'info': + showOsd(); + break; + case 'play': + play(); + break; + case 'pause': + pause(); + break; + case 'playpause': + playPause(); + break; + default: + break; + } + } + + /** + * Shows the slideshow component. + */ + self.show = function () { + createElements(options); + // Hides page scrollbar + document.body.classList.remove('force-scroll'); + document.body.classList.add('hide-scroll'); }; -}); + + /** + * Hides the slideshow element. + */ + self.hide = function () { + if (dialog) { + dialogHelper.close(dialog); + } + }; +} From b7d29eae1b971eae3be7483109da3297f1c7df3f Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 09:02:45 +0100 Subject: [PATCH 092/155] Migration of subtitleeditor to ES6 module --- package.json | 1 + .../subtitleeditor/subtitleeditor.js | 725 +++++++++--------- 2 files changed, 369 insertions(+), 357 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..a7dcc43114 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "src/components/search/searchresults.js", "src/components/settingshelper.js", "src/components/shortcuts.js", + "src/components/subtitleeditor/subtitleeditor.js", "src/components/subtitlesettings/subtitleappearancehelper.js", "src/components/subtitlesettings/subtitlesettings.js", "src/components/syncPlay/groupSelectionMenu.js", diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 7df24b5da8..dfd7f9446c 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -1,426 +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; +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) { - 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 +}; From cdb07e94436052d3d3fcc7b7f61f9160a935a93d Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 09:03:30 +0100 Subject: [PATCH 093/155] Migration of subtitlesync to ES6 module --- package.json | 1 + src/components/subtitlesync/subtitlesync.js | 219 ++++++++++---------- 2 files changed, 111 insertions(+), 109 deletions(-) diff --git a/package.json b/package.json index a7dcc43114..6409981f9c 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "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", 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; From 3680f478457f10acf2ee94d8c23c60b395b9a86e Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 09:46:23 +0100 Subject: [PATCH 094/155] Remove duplicates from package.json --- package.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/package.json b/package.json index ad4c8e1109..2ac1702d50 100644 --- a/package.json +++ b/package.json @@ -191,12 +191,6 @@ "src/controllers/music/musicplaylists.js", "src/controllers/music/musicrecommended.js", "src/controllers/music/songs.js", - "src/controllers/dashboard/plugins/repositories.js", - "src/controllers/movies/moviecollections.js", - "src/controllers/movies/moviegenres.js", - "src/controllers/movies/movies.js", - "src/controllers/movies/moviesrecommended.js", - "src/controllers/movies/movietrailers.js", "src/controllers/dashboard/mediaLibrary.js", "src/controllers/dashboard/metadataImages.js", "src/controllers/dashboard/metadatanfo.js", @@ -218,7 +212,6 @@ "src/controllers/edititemmetadata.js", "src/controllers/favorites.js", "src/controllers/hometab.js", - "src/controllers/dashboard/plugins/repositories.js", "src/controllers/movies/moviecollections.js", "src/controllers/movies/moviegenres.js", "src/controllers/movies/movies.js", From d61fa6bdf654dbe68ae1b14d11341e09a0cab65e Mon Sep 17 00:00:00 2001 From: sharkykh Date: Thu, 6 Aug 2020 12:20:42 +0000 Subject: [PATCH 095/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/strings/he.json b/src/strings/he.json index c61bfe9e1a..7c27d407c3 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -800,5 +800,6 @@ "LabelForgotPasswordUsernameHelp": "הכנס/י את שם המשתמש שלך, אם את/ה זוכר/ת אותו.", "LabelFont": "גופן:", "LabelFolder": "תיקייה:", - "LabelFileOrUrl": "קובץ או כתובת אינטרנט:" + "LabelFileOrUrl": "קובץ או כתובת אינטרנט:", + "Season": "עונה" } From 3204eefc5c8f9cf0dbe2bace960f8f6ef50a57cd Mon Sep 17 00:00:00 2001 From: sharkykh Date: Thu, 6 Aug 2020 12:26:20 +0000 Subject: [PATCH 096/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/strings/he.json b/src/strings/he.json index 7c27d407c3..838c1c2ac8 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -157,7 +157,7 @@ "LabelBirthYear": "שנת לידה:", "LabelBlastMessageInterval": "תדירות הודעות דחיפה", "LabelBlastMessageIntervalHelp": "מגדיר את משך הזמן בשניות בין הודעות דחיפה של השרת.", - "LabelCachePath": "נתיב cache:", + "LabelCachePath": "נתיב מטמון:", "LabelChannels": "ערוצים:", "LabelCollection": "אוספים:", "LabelCommunityRating": "דירוג הקהילה:", @@ -204,7 +204,7 @@ "LabelMaxScreenshotsPerItem": "מספר תמונות מסך מקסימאלי לפריט:", "LabelMessageTitle": "כותרת הודעה:", "LabelMetadataDownloadLanguage": "שפת הורדה מועדפת:", - "LabelMetadataPath": "נתיב Metadata:", + "LabelMetadataPath": "נתיב מטא-דאטה:", "LabelMinBackdropDownloadWidth": "רוחב תמונת רקע מינימאלי להורדה:", "LabelMinResumeDuration": "משך המשכה מינימאלי:", "LabelMinResumeDurationHelp": "קובץ קצר מזה לא יהיה ניתן להמשך ניגון מנקודת העצירה", @@ -302,7 +302,7 @@ "OptionAlbumArtist": "אמן אלבום", "OptionAllUsers": "כל המשתמשים", "OptionAllowLinkSharing": "אפשר שיתוף ברשתות חברתיות", - "OptionAllowMediaPlayback": "הרשה נגינת מדיה", + "OptionAllowMediaPlayback": "אפשר ניגון מדיה", "OptionAllowUserToManageServer": "אפשר למשתמש זה לנהל את השרת", "OptionArtist": "אמן", "OptionAscending": "סדר עולה", @@ -419,7 +419,7 @@ "SeriesSettings": "הגדרות סדרה", "SeriesYearToPresent": "{0} - היום", "ServerNameIsRestarting": "שרת Jellyfin - {0} מופעל מחדש.", - "ServerNameIsShuttingDown": "שרת Jellyfin - {0} נכבה.", + "ServerNameIsShuttingDown": "שרת Jellyfin - {0} בתהליך כיבוי.", "ServerUpdateNeeded": "שרת אמבי זה צריך להיות מעודכן. כדי להוריד את הגרסה העדכנית ביותר, בקר בכתובת {0}", "Settings": "הגדרות", "SettingsSaved": "ההגדרות נשמרו.", @@ -801,5 +801,40 @@ "LabelFont": "גופן:", "LabelFolder": "תיקייה:", "LabelFileOrUrl": "קובץ או כתובת אינטרנט:", - "Season": "עונה" + "Season": "עונה", + "OptionEnableAccessFromAllDevices": "אפשר גישה מכל המכשירים", + "Primary": "ראשי", + "Menu": "תפריט", + "LiveTV": "שידורים חיים", + "ManageLibrary": "נהל ספרייה", + "Logo": "לוגו", + "OptionDateAddedImportTime": "השתמש בתאריך הסריקה לתוך הספרייה", + "OptionDateAddedFileTime": "השתמש בתאריך יצירת הקובץ", + "OptionBlockTrailers": "קדימונים", + "OptionBlockMusic": "מוזיקה", + "OptionBlockLiveTvChannels": "ערוצי שידורים חיים", + "OptionBlockBooks": "ספרים", + "OptionAllowRemoteSharedDevices": "אפשר שליטה מרחוק על מכשירים משותפים", + "OptionAllowRemoteControlOthers": "אפשר שליטה מרחוק על משתמשים אחרים", + "SelectAdminUsername": "נא לבחור שם משתמש עבור חשבון המנהל.", + "OptionHideUserFromLoginHelp": "שימושי עבור חשבונות פרטיים או חשבונות מנהל מוסתרים. המשתמש יצטרך להזין את שם המשתמש והסיסמה ידנית על מנת להתחבר.", + "MessagePlayAccessRestricted": "התוכן הזה לא ניתן לניגון כרגע. למידע נוסף, נא ליצור קשר עם מנהל המערכת שלך.", + "MessageContactAdminToResetPassword": "נא ליצור קשר עם מנהל המערכת שלך על מנת לאפס את הסיסמה שלך.", + "HeaderAdmin": "מנהל", + "TabDisplay": "תצוגה", + "HeaderDisplay": "תצוגה", + "Suggestions": "המלצות", + "MessageSyncPlayNoGroupsAvailable": "אין קבוצות זמינות. התחל לנגן משהו קודם.", + "OptionHomeVideos": "תמונות", + "Home": "בית", + "LabelServerName": "שם השרת:", + "TabPlugins": "תוספים", + "MessageNoPluginsInstalled": "אין לך תוספים מותקנים.", + "MessageNoAvailablePlugins": "אין תוספים זמינים.", + "TabLogs": "יומני רישום", + "LabelLogs": "יומני רישום:", + "TabNetworking": "תקשורת", + "TabDVR": "ממיר-מקליט", + "HeaderDVR": "ממיר-מקליט", + "LabelScheduledTaskLastRan": "רץ לאחרונה {0}, במשך {1}." } From cc02cb01cf6df9fedcf381c0b03c2c3f6f275334 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Thu, 6 Aug 2020 12:52:34 +0000 Subject: [PATCH 097/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 47 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/strings/he.json b/src/strings/he.json index 838c1c2ac8..58eb6ee1b4 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -314,7 +314,7 @@ "OptionContinuing": "ממשיך", "OptionCriticRating": "ציון מבקרים", "OptionCustomUsers": "מותאם אישית", - "OptionDaily": "יומי", + "OptionDaily": "כל יום", "OptionDateAdded": "תאריך הוספה", "OptionDatePlayed": "תאריך ניגון", "OptionDescending": "סדר יורד", @@ -337,7 +337,7 @@ "OptionHasSubtitles": "כתוביות", "OptionHasThemeSong": "שיר נושא", "OptionHasThemeVideo": "סרט נושא", - "OptionHasTrailer": "טריילר", + "OptionHasTrailer": "קדימון", "OptionHideUser": "הסתר משתמש זה בחלון ההתחברות", "OptionImdbRating": "דירוג IMDb", "OptionLikes": "נבחרים", @@ -348,27 +348,27 @@ "OptionOnAppStartup": "בהפעלת התוכנה", "OptionOnInterval": "כל פרק זמן", "OptionParentalRating": "דירוג בקרת הורים", - "OptionPlayCount": "מספר השמעות", + "OptionPlayCount": "כמות ניגונים", "OptionPlayed": "נוגן", - "OptionPremiereDate": "תאריך שידור ראשון", + "OptionPremiereDate": "תאריך בכורה", "OptionProfileAudio": "צליל", "OptionProfilePhoto": "תמונה", "OptionProfileVideo": "וידאו", "OptionProfileVideoAudio": "צליל וידאו", "OptionResumable": "ניתן להמשיך", - "OptionRuntime": "משך", + "OptionRuntime": "זמן ריצה", "OptionSaturday": "שבת", "OptionSpecialEpisode": "ספיישלים", "OptionSunday": "ראשון", "OptionThursday": "חמישי", - "OptionTrackName": "שם השיר", + "OptionTrackName": "שם הרצועה", "OptionTuesday": "שלישי", "OptionTvdbRating": "דירוג TVDB", "OptionUnairedEpisode": "פרקים שלא שודרו", "OptionUnplayed": "לא נוגן", "OptionWakeFromSleep": "הער ממצב שינה", "OptionWednesday": "רביעי", - "OptionWeekly": "שבועי", + "OptionWeekly": "כל שבוע", "OriginalAirDateValue": "תאריך אוויר מקורי: {0}", "Overview": "סקירה כללית", "PackageInstallCancelled": "ההתקנה של {0} (גירסה {1}) בוטלה.", @@ -836,5 +836,36 @@ "TabNetworking": "תקשורת", "TabDVR": "ממיר-מקליט", "HeaderDVR": "ממיר-מקליט", - "LabelScheduledTaskLastRan": "רץ לאחרונה {0}, במשך {1}." + "LabelScheduledTaskLastRan": "רץ לאחרונה {0}, במשך {1}.", + "LabelTheme": "ערכת נושא:", + "LabelTextSize": "גודל טקסט:", + "LabelTextColor": "צבע טקסט:", + "LabelSyncPlayAccessNone": "מבוטל עבור משתמש זה", + "LabelSyncPlayAccessJoinGroups": "אפשר למשתמש להצטרף לקבוצות", + "LabelSyncPlayAccessCreateAndJoinGroups": "אפשר למשתמש ליצור קבוצות ולהצטרף אליהן", + "LabelSyncPlayLeaveGroup": "עזוב קבוצה", + "LabelSyncPlayNewGroupDescription": "צור קבוצה חדשה", + "LabelSyncPlayNewGroup": "קבוצה חדשה", + "MoreFromValue": "עוד מ{0}", + "Writers": "תסריטאים", + "DailyAt": "כל יום ב-{0}", + "OptionWeekends": "סופי שבוע", + "OptionWeekdays": "ימי חול", + "Unplayed": "לא נוגן", + "OptionSubstring": "מחרוזת משנה", + "OptionReleaseDate": "תאריך שחרור", + "OptionRegex": "ביטוי-רגולרי", + "OptionRandom": "אקראי", + "OptionPoster": "פוסטר", + "OptionNone": "כלום", + "OptionMax": "מקסימום", + "List": "רשימה", + "OptionList": "רשימה", + "OptionIsSD": "הבחנה רגילה (SD)", + "OptionIsHD": "הבחנה גבוהה (HD)", + "OptionExternallyDownloaded": "הורדה חיצונית", + "OptionEveryday": "כל יום", + "OptionEnableExternalContentInSuggestions": "הפעל תוכן חיצוני בהמלצות", + "OptionEnableAccessToAllLibraries": "אפשר גישה לכל הספריות", + "OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים" } From 88992fd5e73f619834a34003c35be991ed3d1cf8 Mon Sep 17 00:00:00 2001 From: Verkhaliak Anton Date: Thu, 6 Aug 2020 14:19:23 +0000 Subject: [PATCH 098/155] Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ru/ --- src/strings/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/ru.json b/src/strings/ru.json index 24d5cdd90f..199d0f24ae 100644 --- a/src/strings/ru.json +++ b/src/strings/ru.json @@ -1521,7 +1521,7 @@ "EnableBlurHashHelp": "Рисунки, которые всё ещё загружаются, будут отображаться с размытым заполнением", "EnableBlurHash": "Включить размытые заполнители для изображений", "ButtonSyncPlay": "SyncPlay", - "ButtonCast": "В ролях", + "ButtonCast": "Транслировать", "TabRepositories": "Репозитории", "MessageNoGenresAvailable": "Разрешить поставщикам метаданных получать жанры из интернета.", "MessageAddRepository": "Если вы хотите добавить репозиторий, нажмите кнопку рядом с заголовком и заполните необходимую информацию.", From e694f0918aa10a211a4b12a8930de0dfd4667334 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 6 Aug 2020 15:55:10 +0100 Subject: [PATCH 099/155] Migration of remotecontrol to ES6 module --- package.json | 1 + src/components/remotecontrol/remotecontrol.js | 1713 +++++++++-------- 2 files changed, 865 insertions(+), 849 deletions(-) diff --git a/package.json b/package.json index 246b4b5cf5..271fc8e601 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,7 @@ "src/components/playmenu.js", "src/components/prompt/prompt.js", "src/components/refreshdialog/refreshdialog.js", + "src/components/remotecontrol/remotecontrol.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", "src/plugins/htmlVideoPlayer/plugin.js", diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index b5ac4c9a8b..b9f33b4e1a 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -1,939 +1,954 @@ -define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageLoader', 'playbackManager', 'nowPlayingHelper', 'events', 'connectionManager', 'apphost', 'globalize', 'layoutManager', 'userSettings', 'cardBuilder', 'itemContextMenu', 'cardStyle', 'emby-itemscontainer', 'css!./remotecontrol.css', 'emby-ratingbutton'], function (browser, datetime, backdrop, libraryBrowser, listView, imageLoader, playbackManager, nowPlayingHelper, events, connectionManager, appHost, globalize, layoutManager, userSettings, cardBuilder, itemContextMenu) { - 'use strict'; +import datetime from 'datetime'; +import backdrop from 'backdrop'; +import listView from 'listView'; +import imageLoader from 'imageLoader'; +import playbackManager from 'playbackManager'; +import nowPlayingHelper from 'nowPlayingHelper'; +import events from 'events'; +import connectionManager from 'connectionManager'; +import appHost from 'apphost'; +import globalize from 'globalize'; +import layoutManager from 'layoutManager'; +import * as userSettings from 'userSettings'; +import cardBuilder from 'cardBuilder'; +import itemContextMenu from 'itemContextMenu'; +import 'cardStyle'; +import 'emby-itemscontainer'; +import 'css!./remotecontrol.css'; +import 'emby-ratingbutton'; - playbackManager = playbackManager.default || playbackManager; +/*eslint prefer-const: "error"*/ - var showMuteButton = true; - var showVolumeSlider = true; +let showMuteButton = true; +let showVolumeSlider = true; - function showAudioMenu(context, player, button, item) { - var currentIndex = playbackManager.getAudioStreamIndex(player); - var streams = playbackManager.audioTracks(player); - var menuItems = streams.map(function (s) { - var menuItem = { - name: s.DisplayTitle, - id: s.Index - }; +function showAudioMenu(context, player, button, item) { + const currentIndex = playbackManager.getAudioStreamIndex(player); + const streams = playbackManager.audioTracks(player); + const menuItems = streams.map(function (s) { + const menuItem = { + name: s.DisplayTitle, + id: s.Index + }; - if (s.Index == currentIndex) { - menuItem.selected = true; - } - - return menuItem; - }); - - require(['actionsheet'], function (actionsheet) { - actionsheet.show({ - items: menuItems, - positionTo: button, - callback: function (id) { - playbackManager.setAudioStreamIndex(parseInt(id), player); - } - }); - }); - } - - function showSubtitleMenu(context, player, button, item) { - var currentIndex = playbackManager.getSubtitleStreamIndex(player); - var streams = playbackManager.subtitleTracks(player); - var menuItems = streams.map(function (s) { - var menuItem = { - name: s.DisplayTitle, - id: s.Index - }; - - if (s.Index == currentIndex) { - menuItem.selected = true; - } - - return menuItem; - }); - menuItems.unshift({ - id: -1, - name: globalize.translate('ButtonOff'), - selected: currentIndex == null - }); - - require(['actionsheet'], function (actionsheet) { - actionsheet.show({ - items: menuItems, - positionTo: button, - callback: function (id) { - playbackManager.setSubtitleStreamIndex(parseInt(id), player); - } - }); - }); - } - - function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) { - return nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo).map(function (i) { - return i.text; - }).join('
'); - } - - function seriesImageUrl(item, options) { - if (item.Type !== 'Episode') { - return null; + if (s.Index == currentIndex) { + menuItem.selected = true; } - options = options || {}; - options.type = options.type || 'Primary'; - if (options.type === 'Primary' && item.SeriesPrimaryImageTag) { - options.tag = item.SeriesPrimaryImageTag; + return menuItem; + }); + + import('actionsheet').then(({ default: actionsheet }) => { + actionsheet.show({ + items: menuItems, + positionTo: button, + callback: function (id) { + playbackManager.setAudioStreamIndex(parseInt(id), player); + } + }); + }); +} + +function showSubtitleMenu(context, player, button, item) { + const currentIndex = playbackManager.getSubtitleStreamIndex(player); + const streams = playbackManager.subtitleTracks(player); + const menuItems = streams.map(function (s) { + const menuItem = { + name: s.DisplayTitle, + id: s.Index + }; + + if (s.Index == currentIndex) { + menuItem.selected = true; + } + + return menuItem; + }); + menuItems.unshift({ + id: -1, + name: globalize.translate('ButtonOff'), + selected: currentIndex == null + }); + + import('actionsheet').then(({ default: actionsheet }) => { + actionsheet.show({ + items: menuItems, + positionTo: button, + callback: function (id) { + playbackManager.setSubtitleStreamIndex(parseInt(id), player); + } + }); + }); +} + +function getNowPlayingNameHtml(nowPlayingItem, includeNonNameInfo) { + return nowPlayingHelper.getNowPlayingNames(nowPlayingItem, includeNonNameInfo).map(function (i) { + return i.text; + }).join('
'); +} + +function seriesImageUrl(item, options) { + if (item.Type !== 'Episode') { + return null; + } + + options = options || {}; + options.type = options.type || 'Primary'; + if (options.type === 'Primary' && item.SeriesPrimaryImageTag) { + options.tag = item.SeriesPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + } + + if (options.type === 'Thumb') { + if (item.SeriesThumbImageTag) { + options.tag = item.SeriesThumbImageTag; return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } - if (options.type === 'Thumb') { - if (item.SeriesThumbImageTag) { - options.tag = item.SeriesThumbImageTag; - return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); - } - - if (item.ParentThumbImageTag) { - options.tag = item.ParentThumbImageTag; - return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); - } + if (item.ParentThumbImageTag) { + options.tag = item.ParentThumbImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); } - - return null; } - function imageUrl(item, options) { - options = options || {}; - options.type = options.type || 'Primary'; + return null; +} - if (item.ImageTags && item.ImageTags[options.type]) { - options.tag = item.ImageTags[options.type]; - return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); - } +function imageUrl(item, options) { + options = options || {}; + options.type = options.type || 'Primary'; - if (item.AlbumId && item.AlbumPrimaryImageTag) { - options.tag = item.AlbumPrimaryImageTag; - return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); - } - - return null; + if (item.ImageTags && item.ImageTags[options.type]) { + options.tag = item.ImageTags[options.type]; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); } - function updateNowPlayingInfo(context, state, serverId) { - var item = state.NowPlayingItem; - var displayName = item ? getNowPlayingNameHtml(item).replace('
', ' - ') : ''; - if (typeof item !== 'undefined') { - var nowPlayingServerId = (item.ServerId || serverId); - if (item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') { - var songName = item.Name; - var artistsSeries = ''; - var albumName = ''; - if (item.Artists != null) { - if (item.ArtistItems != null) { - for (const artist of item.ArtistItems) { - let artistName = artist.Name; - let artistId = artist.Id; - artistsSeries += `${artistName}`; - if (artist !== item.ArtistItems.slice(-1)[0]) { - artistsSeries += ', '; - } + if (item.AlbumId && item.AlbumPrimaryImageTag) { + options.tag = item.AlbumPrimaryImageTag; + return connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); + } + + return null; +} + +function updateNowPlayingInfo(context, state, serverId) { + const item = state.NowPlayingItem; + const displayName = item ? getNowPlayingNameHtml(item).replace('
', ' - ') : ''; + if (typeof item !== 'undefined') { + const nowPlayingServerId = (item.ServerId || serverId); + if (item.Type == 'Audio' || item.MediaStreams[0].Type == 'Audio') { + const songName = item.Name; + let artistsSeries = ''; + let albumName = ''; + if (item.Artists != null) { + if (item.ArtistItems != null) { + for (const artist of item.ArtistItems) { + const artistName = artist.Name; + const artistId = artist.Id; + artistsSeries += `${artistName}`; + if (artist !== item.ArtistItems.slice(-1)[0]) { + artistsSeries += ', '; } - } else if (item.Artists) { - // For some reason, Chromecast Player doesn't return a item.ArtistItems object, so we need to fallback - // to normal item.Artists item. - // TODO: Normalise fields returned by all the players - for (const artist of item.Artists) { - artistsSeries += `${artist}`; - if (artist !== item.Artists.slice(-1)[0]) { - artistsSeries += ', '; - } + } + } else if (item.Artists) { + // For some reason, Chromecast Player doesn't return a item.ArtistItems object, so we need to fallback + // to normal item.Artists item. + // TODO: Normalise fields returned by all the players + for (const artist of item.Artists) { + artistsSeries += `${artist}`; + if (artist !== item.Artists.slice(-1)[0]) { + artistsSeries += ', '; } } } - if (item.Album != null) { - albumName = '` + item.Album + ''; - } - context.querySelector('.nowPlayingAlbum').innerHTML = albumName; - context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; - context.querySelector('.nowPlayingSongName').innerHTML = songName; - } else if (item.Type == 'Episode') { - if (item.SeasonName != null) { - var seasonName = item.SeasonName; - context.querySelector('.nowPlayingSeason').innerHTML = '${seasonName}`; - } - if (item.SeriesName != null) { - var seriesName = item.SeriesName; - if (item.SeriesId != null) { - context.querySelector('.nowPlayingSerie').innerHTML = '${seriesName}`; - } else { - context.querySelector('.nowPlayingSerie').innerHTML = seriesName; - } - } - context.querySelector('.nowPlayingEpisode').innerHTML = item.Name; - } else { - context.querySelector('.nowPlayingPageTitle').innerHTML = displayName; } - - if (displayName.length > 0 && item.Type != 'Audio' && item.Type != 'Episode') { - context.querySelector('.nowPlayingPageTitle').classList.remove('hide'); - } else { - context.querySelector('.nowPlayingPageTitle').classList.add('hide'); + if (item.Album != null) { + albumName = '` + item.Album + ''; } - - var url = item ? seriesImageUrl(item, { - maxHeight: 300 - }) || imageUrl(item, { - maxHeight: 300 - }) : null; - - let contextButton = context.querySelector('.btnToggleContextMenu'); - // We remove the previous event listener by replacing the item in each update event - const autoFocusContextButton = document.activeElement === contextButton; - let contextButtonClone = contextButton.cloneNode(true); - contextButton.parentNode.replaceChild(contextButtonClone, contextButton); - contextButton = context.querySelector('.btnToggleContextMenu'); - if (autoFocusContextButton) { - contextButton.focus(); + context.querySelector('.nowPlayingAlbum').innerHTML = albumName; + context.querySelector('.nowPlayingArtist').innerHTML = artistsSeries; + context.querySelector('.nowPlayingSongName').innerHTML = songName; + } else if (item.Type == 'Episode') { + if (item.SeasonName != null) { + const seasonName = item.SeasonName; + context.querySelector('.nowPlayingSeason').innerHTML = '${seasonName}`; } - const stopPlayback = !!layoutManager.mobile; - var options = { - play: false, - queue: false, - stopPlayback: stopPlayback, - clearQueue: true, - openAlbum: false, - positionTo: contextButton - }; - var apiClient = connectionManager.getApiClient(item.ServerId); - apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { - apiClient.getCurrentUser().then(function (user) { - contextButton.addEventListener('click', function () { - itemContextMenu.show(Object.assign({ - item: fullItem, - user: user - }, options)); - }); - }); - }); - setImageUrl(context, state, url); - if (item) { - backdrop.setBackdrops([item]); - apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { - var userData = fullItem.UserData || {}; - var likes = userData.Likes == null ? '' : userData.Likes; - context.querySelector('.nowPlayingPageUserDataButtonsTitle').innerHTML = ''; - context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; - }); - } else { - backdrop.clearBackdrop(); - context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; - } - } - } - - function setImageUrl(context, state, url) { - var item = state.NowPlayingItem; - var imgContainer = context.querySelector('.nowPlayingPageImageContainer'); - - if (url) { - imgContainer.innerHTML = ''; - if (item.Type == 'Audio') { - context.querySelector('.nowPlayingPageImage').classList.add('nowPlayingPageImageAudio'); - context.querySelector('.nowPlayingPageImageContainer').classList.remove('nowPlayingPageImageAudio'); - } else { - context.querySelector('.nowPlayingPageImageContainer').classList.add('nowPlayingPageImagePoster'); - context.querySelector('.nowPlayingPageImage').classList.remove('nowPlayingPageImageAudio'); - } - } else { - imgContainer.innerHTML = '
'; - } - } - - function buttonVisible(btn, enabled) { - if (enabled) { - btn.classList.remove('hide'); - } else { - btn.classList.add('hide'); - } - } - - function updateSupportedCommands(context, commands) { - var all = context.querySelectorAll('.btnCommand'); - - for (var i = 0, length = all.length; i < length; i++) { - var enableButton = commands.indexOf(all[i].getAttribute('data-command')) !== -1; - all[i].disabled = !enableButton; - } - } - - return function () { - function toggleRepeat() { - switch (playbackManager.getRepeatMode()) { - case 'RepeatAll': - playbackManager.setRepeatMode('RepeatOne'); - break; - case 'RepeatOne': - playbackManager.setRepeatMode('RepeatNone'); - break; - case 'RepeatNone': - playbackManager.setRepeatMode('RepeatAll'); - } - } - - function updatePlayerState(player, context, state) { - lastPlayerState = state; - var item = state.NowPlayingItem; - var playerInfo = playbackManager.getPlayerInfo(); - var supportedCommands = playerInfo.supportedCommands; - currentPlayerSupportedCommands = supportedCommands; - var playState = state.PlayState || {}; - var isSupportedCommands = supportedCommands.includes('DisplayMessage') || supportedCommands.includes('SendString') || supportedCommands.includes('Select'); - buttonVisible(context.querySelector('.btnToggleFullscreen'), item && item.MediaType == 'Video' && supportedCommands.includes('ToggleFullscreen')); - updateAudioTracksDisplay(player, context); - updateSubtitleTracksDisplay(player, context); - - if (supportedCommands.includes('DisplayMessage') && !currentPlayer.isLocalPlayer) { - context.querySelector('.sendMessageSection').classList.remove('hide'); - } else { - context.querySelector('.sendMessageSection').classList.add('hide'); - } - - if (supportedCommands.includes('SendString') && !currentPlayer.isLocalPlayer) { - context.querySelector('.sendTextSection').classList.remove('hide'); - } else { - context.querySelector('.sendTextSection').classList.add('hide'); - } - - if (supportedCommands.includes('Select') && !currentPlayer.isLocalPlayer) { - context.querySelector('.navigationSection').classList.remove('hide'); - } else { - context.querySelector('.navigationSection').classList.add('hide'); - } - - if (isSupportedCommands && !currentPlayer.isLocalPlayer) { - context.querySelector('.remoteControlSection').classList.remove('hide'); - } else { - context.querySelector('.remoteControlSection').classList.add('hide'); - } - - buttonVisible(context.querySelector('.btnStop'), item != null); - buttonVisible(context.querySelector('.btnNextTrack'), item != null); - buttonVisible(context.querySelector('.btnPreviousTrack'), item != null); - if (layoutManager.mobile) { - buttonVisible(context.querySelector('.btnRewind'), false); - buttonVisible(context.querySelector('.btnFastForward'), false); - } else { - buttonVisible(context.querySelector('.btnRewind'), item != null); - buttonVisible(context.querySelector('.btnFastForward'), item != null); - } - var positionSlider = context.querySelector('.nowPlayingPositionSlider'); - - if (positionSlider && item && item.RunTimeTicks) { - positionSlider.setKeyboardSteps(userSettings.skipBackLength() * 1000000 / item.RunTimeTicks, - userSettings.skipForwardLength() * 1000000 / item.RunTimeTicks); - } - - if (positionSlider && !positionSlider.dragging) { - positionSlider.disabled = !playState.CanSeek; - var isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null; - positionSlider.setIsClear(isProgressClear); - } - - updatePlayPauseState(playState.IsPaused, item != null); - updateTimeDisplay(playState.PositionTicks, item ? item.RunTimeTicks : null); - updatePlayerVolumeState(context, playState.IsMuted, playState.VolumeLevel); - - if (item && item.MediaType == 'Video') { - context.classList.remove('hideVideoButtons'); - } else { - context.classList.add('hideVideoButtons'); - } - - updateRepeatModeDisplay(playbackManager.getRepeatMode()); - onShuffleQueueModeChange(false); - updateNowPlayingInfo(context, state); - } - - function updateAudioTracksDisplay(player, context) { - var supportedCommands = currentPlayerSupportedCommands; - buttonVisible(context.querySelector('.btnAudioTracks'), playbackManager.audioTracks(player).length > 1 && supportedCommands.indexOf('SetAudioStreamIndex') != -1); - } - - function updateSubtitleTracksDisplay(player, context) { - var supportedCommands = currentPlayerSupportedCommands; - buttonVisible(context.querySelector('.btnSubtitles'), playbackManager.subtitleTracks(player).length && supportedCommands.indexOf('SetSubtitleStreamIndex') != -1); - } - - function updateRepeatModeDisplay(repeatMode) { - var context = dlg; - let toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton'); - const cssClass = 'buttonActive'; - let innHtml = ''; - let repeatOn = true; - - switch (repeatMode) { - case 'RepeatAll': - break; - case 'RepeatOne': - innHtml = ''; - break; - case 'RepeatNone': - default: - repeatOn = false; - break; - } - - for (const toggleRepeatButton of toggleRepeatButtons) { - toggleRepeatButton.classList.toggle(cssClass, repeatOn); - toggleRepeatButton.innerHTML = innHtml; - } - } - - function updatePlayerVolumeState(context, isMuted, volumeLevel) { - var view = context; - var supportedCommands = currentPlayerSupportedCommands; - - if (supportedCommands.indexOf('Mute') === -1) { - showMuteButton = false; - } - - if (supportedCommands.indexOf('SetVolume') === -1) { - showVolumeSlider = false; - } - - if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { - showMuteButton = false; - showVolumeSlider = false; - } - - const buttonMute = view.querySelector('.buttonMute'); - const buttonMuteIcon = buttonMute.querySelector('.material-icons'); - - buttonMuteIcon.classList.remove('volume_off', 'volume_up'); - - if (isMuted) { - buttonMute.setAttribute('title', globalize.translate('Unmute')); - buttonMuteIcon.classList.add('volume_off'); - } else { - buttonMute.setAttribute('title', globalize.translate('Mute')); - buttonMuteIcon.classList.add('volume_up'); - } - - if (!showMuteButton && !showVolumeSlider) { - context.querySelector('.volumecontrol').classList.add('hide'); - } else { - buttonMute.classList.toggle('hide', !showMuteButton); - - var nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); - var nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); - - if (nowPlayingVolumeSlider) { - nowPlayingVolumeSliderContainer.classList.toggle('hide', !showVolumeSlider); - - if (!nowPlayingVolumeSlider.dragging) { - nowPlayingVolumeSlider.value = volumeLevel || 0; - } - } - } - } - - function updatePlayPauseState(isPaused, isActive) { - var context = dlg; - var btnPlayPause = context.querySelector('.btnPlayPause'); - const btnPlayPauseIcon = btnPlayPause.querySelector('.material-icons'); - - btnPlayPauseIcon.classList.remove('play_circle_filled', 'pause_circle_filled'); - btnPlayPauseIcon.classList.add(isPaused ? 'play_circle_filled' : 'pause_circle_filled'); - - buttonVisible(btnPlayPause, isActive); - } - - function updateTimeDisplay(positionTicks, runtimeTicks) { - var context = dlg; - var positionSlider = context.querySelector('.nowPlayingPositionSlider'); - - if (positionSlider && !positionSlider.dragging) { - if (runtimeTicks) { - var pct = positionTicks / runtimeTicks; - pct *= 100; - positionSlider.value = pct; + if (item.SeriesName != null) { + const seriesName = item.SeriesName; + if (item.SeriesId != null) { + context.querySelector('.nowPlayingSerie').innerHTML = '${seriesName}`; } else { - positionSlider.value = 0; + context.querySelector('.nowPlayingSerie').innerHTML = seriesName; } } - - context.querySelector('.positionTime').innerHTML = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks); - context.querySelector('.runtime').innerHTML = runtimeTicks != null ? datetime.getDisplayRunningTime(runtimeTicks) : '--:--'; + context.querySelector('.nowPlayingEpisode').innerHTML = item.Name; + } else { + context.querySelector('.nowPlayingPageTitle').innerHTML = displayName; } - function getPlaylistItems(player) { - return playbackManager.getPlaylist(player); + if (displayName.length > 0 && item.Type != 'Audio' && item.Type != 'Episode') { + context.querySelector('.nowPlayingPageTitle').classList.remove('hide'); + } else { + context.querySelector('.nowPlayingPageTitle').classList.add('hide'); } - function loadPlaylist(context, player) { - getPlaylistItems(player).then(function (items) { - var html = ''; - let favoritesEnabled = true; - if (layoutManager.mobile) { - if (items.length > 0) { - context.querySelector('.btnTogglePlaylist').classList.remove('hide'); - } else { - context.querySelector('.btnTogglePlaylist').classList.add('hide'); - } - favoritesEnabled = false; - } + const url = item ? seriesImageUrl(item, { + maxHeight: 300 + }) || imageUrl(item, { + maxHeight: 300 + }) : null; - html += listView.getListViewHtml({ - items: items, - smallIcon: true, - action: 'setplaylistindex', - enableUserDataButtons: favoritesEnabled, - rightButtons: [{ - icon: 'remove_circle_outline', - title: globalize.translate('ButtonRemove'), - id: 'remove' - }], - dragHandle: true + let contextButton = context.querySelector('.btnToggleContextMenu'); + // We remove the previous event listener by replacing the item in each update event + const autoFocusContextButton = document.activeElement === contextButton; + const contextButtonClone = contextButton.cloneNode(true); + contextButton.parentNode.replaceChild(contextButtonClone, contextButton); + contextButton = context.querySelector('.btnToggleContextMenu'); + if (autoFocusContextButton) { + contextButton.focus(); + } + const stopPlayback = !!layoutManager.mobile; + const options = { + play: false, + queue: false, + stopPlayback: stopPlayback, + clearQueue: true, + openAlbum: false, + positionTo: contextButton + }; + const apiClient = connectionManager.getApiClient(item.ServerId); + apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { + apiClient.getCurrentUser().then(function (user) { + contextButton.addEventListener('click', function () { + itemContextMenu.show(Object.assign({ + item: fullItem, + user: user + }, options)); }); - - var itemsContainer = context.querySelector('.playlist'); - let focusedItemPlaylistId = itemsContainer.querySelector('button:focus'); - itemsContainer.innerHTML = html; - if (focusedItemPlaylistId !== null) { - focusedItemPlaylistId = focusedItemPlaylistId.getAttribute('data-playlistitemid'); - const newFocusedItem = itemsContainer.querySelector(`button[data-playlistitemid="${focusedItemPlaylistId}"]`); - if (newFocusedItem !== null) { - newFocusedItem.focus(); - } - } - - var playlistItemId = playbackManager.getCurrentPlaylistItemId(player); - - if (playlistItemId) { - var img = itemsContainer.querySelector(`.listItem[data-playlistItemId="${playlistItemId}"] .listItemImage`); - - if (img) { - img.classList.remove('lazy'); - img.classList.add('playlistIndexIndicatorImage'); - } - } - - imageLoader.lazyChildren(itemsContainer); }); + }); + setImageUrl(context, state, url); + if (item) { + backdrop.setBackdrops([item]); + apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (fullItem) { + const userData = fullItem.UserData || {}; + const likes = userData.Likes == null ? '' : userData.Likes; + context.querySelector('.nowPlayingPageUserDataButtonsTitle').innerHTML = ''; + context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; + }); + } else { + backdrop.clearBackdrop(); + context.querySelector('.nowPlayingPageUserDataButtons').innerHTML = ''; + } + } +} + +function setImageUrl(context, state, url) { + const item = state.NowPlayingItem; + const imgContainer = context.querySelector('.nowPlayingPageImageContainer'); + + if (url) { + imgContainer.innerHTML = ''; + if (item.Type == 'Audio') { + context.querySelector('.nowPlayingPageImage').classList.add('nowPlayingPageImageAudio'); + context.querySelector('.nowPlayingPageImageContainer').classList.remove('nowPlayingPageImageAudio'); + } else { + context.querySelector('.nowPlayingPageImageContainer').classList.add('nowPlayingPageImagePoster'); + context.querySelector('.nowPlayingPageImage').classList.remove('nowPlayingPageImageAudio'); + } + } else { + imgContainer.innerHTML = '
'; + } +} + +function buttonVisible(btn, enabled) { + if (enabled) { + btn.classList.remove('hide'); + } else { + btn.classList.add('hide'); + } +} + +function updateSupportedCommands(context, commands) { + const all = context.querySelectorAll('.btnCommand'); + + for (let i = 0, length = all.length; i < length; i++) { + const enableButton = commands.indexOf(all[i].getAttribute('data-command')) !== -1; + all[i].disabled = !enableButton; + } +} + +export default function () { + function toggleRepeat() { + switch (playbackManager.getRepeatMode()) { + case 'RepeatAll': + playbackManager.setRepeatMode('RepeatOne'); + break; + case 'RepeatOne': + playbackManager.setRepeatMode('RepeatNone'); + break; + case 'RepeatNone': + playbackManager.setRepeatMode('RepeatAll'); + } + } + + function updatePlayerState(player, context, state) { + lastPlayerState = state; + const item = state.NowPlayingItem; + const playerInfo = playbackManager.getPlayerInfo(); + const supportedCommands = playerInfo.supportedCommands; + currentPlayerSupportedCommands = supportedCommands; + const playState = state.PlayState || {}; + const isSupportedCommands = supportedCommands.includes('DisplayMessage') || supportedCommands.includes('SendString') || supportedCommands.includes('Select'); + buttonVisible(context.querySelector('.btnToggleFullscreen'), item && item.MediaType == 'Video' && supportedCommands.includes('ToggleFullscreen')); + updateAudioTracksDisplay(player, context); + updateSubtitleTracksDisplay(player, context); + + if (supportedCommands.includes('DisplayMessage') && !currentPlayer.isLocalPlayer) { + context.querySelector('.sendMessageSection').classList.remove('hide'); + } else { + context.querySelector('.sendMessageSection').classList.add('hide'); } - function onPlaybackStart(e, state) { - console.debug('remotecontrol event: ' + e.type); - var player = this; - onStateChanged.call(player, e, state); + if (supportedCommands.includes('SendString') && !currentPlayer.isLocalPlayer) { + context.querySelector('.sendTextSection').classList.remove('hide'); + } else { + context.querySelector('.sendTextSection').classList.add('hide'); } - function onRepeatModeChange() { - updateRepeatModeDisplay(playbackManager.getRepeatMode()); + if (supportedCommands.includes('Select') && !currentPlayer.isLocalPlayer) { + context.querySelector('.navigationSection').classList.remove('hide'); + } else { + context.querySelector('.navigationSection').classList.add('hide'); } - function onShuffleQueueModeChange(updateView = true) { - let shuffleMode = playbackManager.getQueueShuffleMode(this); - let context = dlg; - const cssClass = 'buttonActive'; - let shuffleButtons = context.querySelectorAll('.btnShuffleQueue'); + if (isSupportedCommands && !currentPlayer.isLocalPlayer) { + context.querySelector('.remoteControlSection').classList.remove('hide'); + } else { + context.querySelector('.remoteControlSection').classList.add('hide'); + } - for (let shuffleButton of shuffleButtons) { - switch (shuffleMode) { - case 'Shuffle': - shuffleButton.classList.add(cssClass); - break; - case 'Sorted': - default: - shuffleButton.classList.remove(cssClass); - break; + buttonVisible(context.querySelector('.btnStop'), item != null); + buttonVisible(context.querySelector('.btnNextTrack'), item != null); + buttonVisible(context.querySelector('.btnPreviousTrack'), item != null); + if (layoutManager.mobile) { + buttonVisible(context.querySelector('.btnRewind'), false); + buttonVisible(context.querySelector('.btnFastForward'), false); + } else { + buttonVisible(context.querySelector('.btnRewind'), item != null); + buttonVisible(context.querySelector('.btnFastForward'), item != null); + } + const positionSlider = context.querySelector('.nowPlayingPositionSlider'); + + if (positionSlider && item && item.RunTimeTicks) { + positionSlider.setKeyboardSteps(userSettings.skipBackLength() * 1000000 / item.RunTimeTicks, + userSettings.skipForwardLength() * 1000000 / item.RunTimeTicks); + } + + if (positionSlider && !positionSlider.dragging) { + positionSlider.disabled = !playState.CanSeek; + const isProgressClear = state.MediaSource && state.MediaSource.RunTimeTicks == null; + positionSlider.setIsClear(isProgressClear); + } + + updatePlayPauseState(playState.IsPaused, item != null); + updateTimeDisplay(playState.PositionTicks, item ? item.RunTimeTicks : null); + updatePlayerVolumeState(context, playState.IsMuted, playState.VolumeLevel); + + if (item && item.MediaType == 'Video') { + context.classList.remove('hideVideoButtons'); + } else { + context.classList.add('hideVideoButtons'); + } + + updateRepeatModeDisplay(playbackManager.getRepeatMode()); + onShuffleQueueModeChange(false); + updateNowPlayingInfo(context, state); + } + + function updateAudioTracksDisplay(player, context) { + const supportedCommands = currentPlayerSupportedCommands; + buttonVisible(context.querySelector('.btnAudioTracks'), playbackManager.audioTracks(player).length > 1 && supportedCommands.indexOf('SetAudioStreamIndex') != -1); + } + + function updateSubtitleTracksDisplay(player, context) { + const supportedCommands = currentPlayerSupportedCommands; + buttonVisible(context.querySelector('.btnSubtitles'), playbackManager.subtitleTracks(player).length && supportedCommands.indexOf('SetSubtitleStreamIndex') != -1); + } + + function updateRepeatModeDisplay(repeatMode) { + const context = dlg; + const toggleRepeatButtons = context.querySelectorAll('.repeatToggleButton'); + const cssClass = 'buttonActive'; + let innHtml = ''; + let repeatOn = true; + + switch (repeatMode) { + case 'RepeatAll': + break; + case 'RepeatOne': + innHtml = ''; + break; + case 'RepeatNone': + default: + repeatOn = false; + break; + } + + for (const toggleRepeatButton of toggleRepeatButtons) { + toggleRepeatButton.classList.toggle(cssClass, repeatOn); + toggleRepeatButton.innerHTML = innHtml; + } + } + + function updatePlayerVolumeState(context, isMuted, volumeLevel) { + const view = context; + const supportedCommands = currentPlayerSupportedCommands; + + if (supportedCommands.indexOf('Mute') === -1) { + showMuteButton = false; + } + + if (supportedCommands.indexOf('SetVolume') === -1) { + showVolumeSlider = false; + } + + if (currentPlayer.isLocalPlayer && appHost.supports('physicalvolumecontrol')) { + showMuteButton = false; + showVolumeSlider = false; + } + + const buttonMute = view.querySelector('.buttonMute'); + const buttonMuteIcon = buttonMute.querySelector('.material-icons'); + + buttonMuteIcon.classList.remove('volume_off', 'volume_up'); + + if (isMuted) { + buttonMute.setAttribute('title', globalize.translate('Unmute')); + buttonMuteIcon.classList.add('volume_off'); + } else { + buttonMute.setAttribute('title', globalize.translate('Mute')); + buttonMuteIcon.classList.add('volume_up'); + } + + if (!showMuteButton && !showVolumeSlider) { + context.querySelector('.volumecontrol').classList.add('hide'); + } else { + buttonMute.classList.toggle('hide', !showMuteButton); + + const nowPlayingVolumeSlider = context.querySelector('.nowPlayingVolumeSlider'); + const nowPlayingVolumeSliderContainer = context.querySelector('.nowPlayingVolumeSliderContainer'); + + if (nowPlayingVolumeSlider) { + nowPlayingVolumeSliderContainer.classList.toggle('hide', !showVolumeSlider); + + if (!nowPlayingVolumeSlider.dragging) { + nowPlayingVolumeSlider.value = volumeLevel || 0; } } - - if (updateView) { - onPlaylistUpdate(); - } } + } - function onPlaylistUpdate(e) { - loadPlaylist(dlg, this); - } + function updatePlayPauseState(isPaused, isActive) { + const context = dlg; + const btnPlayPause = context.querySelector('.btnPlayPause'); + const btnPlayPauseIcon = btnPlayPause.querySelector('.material-icons'); - function onPlaylistItemRemoved(e, info) { - var context = dlg; - if (info !== undefined) { - var playlistItemIds = info.playlistItemIds; + btnPlayPauseIcon.classList.remove('play_circle_filled', 'pause_circle_filled'); + btnPlayPauseIcon.classList.add(isPaused ? 'play_circle_filled' : 'pause_circle_filled'); - for (var i = 0, length = playlistItemIds.length; i < length; i++) { - var listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]'); + buttonVisible(btnPlayPause, isActive); + } - if (listItem) { - listItem.parentNode.removeChild(listItem); - } - } + function updateTimeDisplay(positionTicks, runtimeTicks) { + const context = dlg; + const positionSlider = context.querySelector('.nowPlayingPositionSlider'); + + if (positionSlider && !positionSlider.dragging) { + if (runtimeTicks) { + let pct = positionTicks / runtimeTicks; + pct *= 100; + positionSlider.value = pct; } else { - onPlaylistUpdate(); + positionSlider.value = 0; } } - function onPlaybackStopped(e, state) { - console.debug('remotecontrol event: ' + e.type); - var player = this; + context.querySelector('.positionTime').innerHTML = positionTicks == null ? '--:--' : datetime.getDisplayRunningTime(positionTicks); + context.querySelector('.runtime').innerHTML = runtimeTicks != null ? datetime.getDisplayRunningTime(runtimeTicks) : '--:--'; + } - if (!state.NextMediaType) { - updatePlayerState(player, dlg, {}); - Emby.Page.back(); + function getPlaylistItems(player) { + return playbackManager.getPlaylist(player); + } + + function loadPlaylist(context, player) { + getPlaylistItems(player).then(function (items) { + let html = ''; + let favoritesEnabled = true; + if (layoutManager.mobile) { + if (items.length > 0) { + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); + } else { + context.querySelector('.btnTogglePlaylist').classList.add('hide'); + } + favoritesEnabled = false; + } + + html += listView.getListViewHtml({ + items: items, + smallIcon: true, + action: 'setplaylistindex', + enableUserDataButtons: favoritesEnabled, + rightButtons: [{ + icon: 'remove_circle_outline', + title: globalize.translate('ButtonRemove'), + id: 'remove' + }], + dragHandle: true + }); + + const itemsContainer = context.querySelector('.playlist'); + let focusedItemPlaylistId = itemsContainer.querySelector('button:focus'); + itemsContainer.innerHTML = html; + if (focusedItemPlaylistId !== null) { + focusedItemPlaylistId = focusedItemPlaylistId.getAttribute('data-playlistitemid'); + const newFocusedItem = itemsContainer.querySelector(`button[data-playlistitemid="${focusedItemPlaylistId}"]`); + if (newFocusedItem !== null) { + newFocusedItem.focus(); + } + } + + const playlistItemId = playbackManager.getCurrentPlaylistItemId(player); + + if (playlistItemId) { + const img = itemsContainer.querySelector(`.listItem[data-playlistItemId="${playlistItemId}"] .listItemImage`); + + if (img) { + img.classList.remove('lazy'); + img.classList.add('playlistIndexIndicatorImage'); + } + } + + imageLoader.lazyChildren(itemsContainer); + }); + } + + function onPlaybackStart(e, state) { + console.debug('remotecontrol event: ' + e.type); + const player = this; + onStateChanged.call(player, e, state); + } + + function onRepeatModeChange() { + updateRepeatModeDisplay(playbackManager.getRepeatMode()); + } + + function onShuffleQueueModeChange(updateView = true) { + const shuffleMode = playbackManager.getQueueShuffleMode(this); + const context = dlg; + const cssClass = 'buttonActive'; + const shuffleButtons = context.querySelectorAll('.btnShuffleQueue'); + + for (const shuffleButton of shuffleButtons) { + switch (shuffleMode) { + case 'Shuffle': + shuffleButton.classList.add(cssClass); + break; + case 'Sorted': + default: + shuffleButton.classList.remove(cssClass); + break; } } - function onPlayPauseStateChanged(e) { - updatePlayPauseState(this.paused(), true); - } - - function onStateChanged(event, state) { - var player = this; - updatePlayerState(player, dlg, state); + if (updateView) { onPlaylistUpdate(); } + } - function onTimeUpdate(e) { - var now = new Date().getTime(); + function onPlaylistUpdate(e) { + loadPlaylist(dlg, this); + } - if (!(now - lastUpdateTime < 700)) { - lastUpdateTime = now; - var player = this; - currentRuntimeTicks = playbackManager.duration(player); - updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks); + function onPlaylistItemRemoved(e, info) { + const context = dlg; + if (info !== undefined) { + const playlistItemIds = info.playlistItemIds; + + for (let i = 0, length = playlistItemIds.length; i < length; i++) { + const listItem = context.querySelector('.listItem[data-playlistItemId="' + playlistItemIds[i] + '"]'); + + if (listItem) { + listItem.parentNode.removeChild(listItem); + } + } + } else { + onPlaylistUpdate(); + } + } + + function onPlaybackStopped(e, state) { + console.debug('remotecontrol event: ' + e.type); + const player = this; + + if (!state.NextMediaType) { + updatePlayerState(player, dlg, {}); + Emby.Page.back(); + } + } + + function onPlayPauseStateChanged(e) { + updatePlayPauseState(this.paused(), true); + } + + function onStateChanged(event, state) { + const player = this; + updatePlayerState(player, dlg, state); + onPlaylistUpdate(); + } + + function onTimeUpdate(e) { + const now = new Date().getTime(); + + if (!(now - lastUpdateTime < 700)) { + lastUpdateTime = now; + const player = this; + currentRuntimeTicks = playbackManager.duration(player); + updateTimeDisplay(playbackManager.currentTime(player), currentRuntimeTicks); + } + } + + function onVolumeChanged(e) { + const player = this; + updatePlayerVolumeState(dlg, player.isMuted(), player.getVolume()); + } + + function releaseCurrentPlayer() { + const player = currentPlayer; + + if (player) { + events.off(player, 'playbackstart', onPlaybackStart); + events.off(player, 'statechange', onStateChanged); + events.off(player, 'repeatmodechange', onRepeatModeChange); + events.off(player, 'shufflequeuemodechange', onShuffleQueueModeChange); + events.off(player, 'playlistitemremove', onPlaylistItemRemoved); + events.off(player, 'playlistitemmove', onPlaylistUpdate); + events.off(player, 'playlistitemadd', onPlaylistUpdate); + events.off(player, 'playbackstop', onPlaybackStopped); + events.off(player, 'volumechange', onVolumeChanged); + events.off(player, 'pause', onPlayPauseStateChanged); + events.off(player, 'unpause', onPlayPauseStateChanged); + events.off(player, 'timeupdate', onTimeUpdate); + currentPlayer = null; + } + } + + function bindToPlayer(context, player) { + if (releaseCurrentPlayer(), currentPlayer = player, player) { + const state = playbackManager.getPlayerState(player); + onStateChanged.call(player, { + type: 'init' + }, state); + events.on(player, 'playbackstart', onPlaybackStart); + events.on(player, 'statechange', onStateChanged); + events.on(player, 'repeatmodechange', onRepeatModeChange); + events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); + events.on(player, 'playlistitemremove', onPlaylistItemRemoved); + events.on(player, 'playlistitemmove', onPlaylistUpdate); + events.on(player, 'playlistitemadd', onPlaylistUpdate); + events.on(player, 'playbackstop', onPlaybackStopped); + events.on(player, 'volumechange', onVolumeChanged); + events.on(player, 'pause', onPlayPauseStateChanged); + events.on(player, 'unpause', onPlayPauseStateChanged); + events.on(player, 'timeupdate', onTimeUpdate); + const playerInfo = playbackManager.getPlayerInfo(); + const supportedCommands = playerInfo.supportedCommands; + currentPlayerSupportedCommands = supportedCommands; + updateSupportedCommands(context, supportedCommands); + } + } + + function onBtnCommandClick() { + if (currentPlayer) { + if (this.classList.contains('repeatToggleButton')) { + toggleRepeat(); + } else { + playbackManager.sendCommand({ + Name: this.getAttribute('data-command') + }, currentPlayer); } } + } - function onVolumeChanged(e) { - var player = this; - updatePlayerVolumeState(dlg, player.isMuted(), player.getVolume()); + function getSaveablePlaylistItems() { + return getPlaylistItems(currentPlayer).then(function (items) { + return items.filter(function (i) { + return i.Id && i.ServerId; + }); + }); + } + + function savePlaylist() { + import('playlistEditor').then(({ default: playlistEditor }) => { + getSaveablePlaylistItems().then(function (items) { + const serverId = items.length ? items[0].ServerId : ApiClient.serverId(); + new playlistEditor.showEditor({ + items: items.map(function (i) { + return i.Id; + }), + serverId: serverId, + enableAddToPlayQueue: false, + defaultValue: 'new' + }); + }); + }); + } + + function bindEvents(context) { + const btnCommand = context.querySelectorAll('.btnCommand'); + const positionSlider = context.querySelector('.nowPlayingPositionSlider'); + + for (let i = 0, length = btnCommand.length; i < length; i++) { + btnCommand[i].addEventListener('click', onBtnCommandClick); } - function releaseCurrentPlayer() { - var player = currentPlayer; - - if (player) { - events.off(player, 'playbackstart', onPlaybackStart); - events.off(player, 'statechange', onStateChanged); - events.off(player, 'repeatmodechange', onRepeatModeChange); - events.off(player, 'shufflequeuemodechange', onShuffleQueueModeChange); - events.off(player, 'playlistitemremove', onPlaylistItemRemoved); - events.off(player, 'playlistitemmove', onPlaylistUpdate); - events.off(player, 'playlistitemadd', onPlaylistUpdate); - events.off(player, 'playbackstop', onPlaybackStopped); - events.off(player, 'volumechange', onVolumeChanged); - events.off(player, 'pause', onPlayPauseStateChanged); - events.off(player, 'unpause', onPlayPauseStateChanged); - events.off(player, 'timeupdate', onTimeUpdate); - currentPlayer = null; - } - } - - function bindToPlayer(context, player) { - if (releaseCurrentPlayer(), currentPlayer = player, player) { - var state = playbackManager.getPlayerState(player); - onStateChanged.call(player, { - type: 'init' - }, state); - events.on(player, 'playbackstart', onPlaybackStart); - events.on(player, 'statechange', onStateChanged); - events.on(player, 'repeatmodechange', onRepeatModeChange); - events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); - events.on(player, 'playlistitemremove', onPlaylistItemRemoved); - events.on(player, 'playlistitemmove', onPlaylistUpdate); - events.on(player, 'playlistitemadd', onPlaylistUpdate); - events.on(player, 'playbackstop', onPlaybackStopped); - events.on(player, 'volumechange', onVolumeChanged); - events.on(player, 'pause', onPlayPauseStateChanged); - events.on(player, 'unpause', onPlayPauseStateChanged); - events.on(player, 'timeupdate', onTimeUpdate); - var playerInfo = playbackManager.getPlayerInfo(); - var supportedCommands = playerInfo.supportedCommands; - currentPlayerSupportedCommands = supportedCommands; - updateSupportedCommands(context, supportedCommands); - } - } - - function onBtnCommandClick() { + context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) { if (currentPlayer) { - if (this.classList.contains('repeatToggleButton')) { - toggleRepeat(); + playbackManager.sendCommand({ + Name: e.target.getAttribute('data-command') + }, currentPlayer); + } + }); + context.querySelector('.btnAudioTracks').addEventListener('click', function (e) { + if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { + showAudioMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); + } + }); + context.querySelector('.btnSubtitles').addEventListener('click', function (e) { + if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { + showSubtitleMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); + } + }); + context.querySelector('.btnStop').addEventListener('click', function () { + if (currentPlayer) { + playbackManager.stop(currentPlayer); + } + }); + context.querySelector('.btnPlayPause').addEventListener('click', function () { + if (currentPlayer) { + playbackManager.playPause(currentPlayer); + } + }); + context.querySelector('.btnNextTrack').addEventListener('click', function () { + if (currentPlayer) { + playbackManager.nextTrack(currentPlayer); + } + }); + context.querySelector('.btnRewind').addEventListener('click', function () { + if (currentPlayer) { + playbackManager.rewind(currentPlayer); + } + }); + context.querySelector('.btnFastForward').addEventListener('click', function () { + if (currentPlayer) { + playbackManager.fastForward(currentPlayer); + } + }); + for (const shuffleButton of context.querySelectorAll('.btnShuffleQueue')) { + shuffleButton.addEventListener('click', function () { + if (currentPlayer) { + playbackManager.toggleQueueShuffleMode(currentPlayer); + } + }); + } + + context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { + if (currentPlayer) { + if (lastPlayerState.NowPlayingItem.MediaType === 'Audio' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { + // Cancel this event if doubleclick is fired + if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { + return; + } + playbackManager.seekPercent(0, currentPlayer); + // This is done automatically by playbackManager. However, setting this here gives instant visual feedback. + // TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround. + positionSlider.value = 0; } else { - playbackManager.sendCommand({ - Name: this.getAttribute('data-command') - }, currentPlayer); - } - } - } - - function getSaveablePlaylistItems() { - return getPlaylistItems(currentPlayer).then(function (items) { - return items.filter(function (i) { - return i.Id && i.ServerId; - }); - }); - } - - function savePlaylist() { - require(['playlistEditor'], function (playlistEditor) { - getSaveablePlaylistItems().then(function (items) { - var serverId = items.length ? items[0].ServerId : ApiClient.serverId(); - new playlistEditor.showEditor({ - items: items.map(function (i) { - return i.Id; - }), - serverId: serverId, - enableAddToPlayQueue: false, - defaultValue: 'new' - }); - }); - }); - } - - function bindEvents(context) { - var btnCommand = context.querySelectorAll('.btnCommand'); - var positionSlider = context.querySelector('.nowPlayingPositionSlider'); - - for (var i = 0, length = btnCommand.length; i < length; i++) { - btnCommand[i].addEventListener('click', onBtnCommandClick); - } - - context.querySelector('.btnToggleFullscreen').addEventListener('click', function (e) { - if (currentPlayer) { - playbackManager.sendCommand({ - Name: e.target.getAttribute('data-command') - }, currentPlayer); - } - }); - context.querySelector('.btnAudioTracks').addEventListener('click', function (e) { - if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { - showAudioMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); - } - }); - context.querySelector('.btnSubtitles').addEventListener('click', function (e) { - if (currentPlayer && lastPlayerState && lastPlayerState.NowPlayingItem) { - showSubtitleMenu(context, currentPlayer, e.target, lastPlayerState.NowPlayingItem); - } - }); - context.querySelector('.btnStop').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.stop(currentPlayer); - } - }); - context.querySelector('.btnPlayPause').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.playPause(currentPlayer); - } - }); - context.querySelector('.btnNextTrack').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.nextTrack(currentPlayer); - } - }); - context.querySelector('.btnRewind').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.rewind(currentPlayer); - } - }); - context.querySelector('.btnFastForward').addEventListener('click', function () { - if (currentPlayer) { - playbackManager.fastForward(currentPlayer); - } - }); - for (const shuffleButton of context.querySelectorAll('.btnShuffleQueue')) { - shuffleButton.addEventListener('click', function () { - if (currentPlayer) { - playbackManager.toggleQueueShuffleMode(currentPlayer); - } - }); - } - - context.querySelector('.btnPreviousTrack').addEventListener('click', function (e) { - if (currentPlayer) { - if (lastPlayerState.NowPlayingItem.MediaType === 'Audio' && (currentPlayer._currentTime >= 5 || !playbackManager.previousTrack(currentPlayer))) { - // Cancel this event if doubleclick is fired - if (e.detail > 1 && playbackManager.previousTrack(currentPlayer)) { - return; - } - playbackManager.seekPercent(0, currentPlayer); - // This is done automatically by playbackManager. However, setting this here gives instant visual feedback. - // TODO: Check why seekPercentage doesn't reflect the changes inmmediately, so we can remove this workaround. - positionSlider.value = 0; - } else { - playbackManager.previousTrack(currentPlayer); - } - } - }); - - context.querySelector('.btnPreviousTrack').addEventListener('dblclick', function () { - if (currentPlayer) { playbackManager.previousTrack(currentPlayer); } - }); - positionSlider.addEventListener('change', function () { - var value = this.value; + } + }); - if (currentPlayer) { - var newPercent = parseFloat(value); - playbackManager.seekPercent(newPercent, currentPlayer); - } - }); + context.querySelector('.btnPreviousTrack').addEventListener('dblclick', function () { + if (currentPlayer) { + playbackManager.previousTrack(currentPlayer); + } + }); + positionSlider.addEventListener('change', function () { + const value = this.value; - positionSlider.getBubbleText = function (value) { - var state = lastPlayerState; + if (currentPlayer) { + const newPercent = parseFloat(value); + playbackManager.seekPercent(newPercent, currentPlayer); + } + }); - if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { - return '--:--'; - } + positionSlider.getBubbleText = function (value) { + const state = lastPlayerState; - var ticks = currentRuntimeTicks; - ticks /= 100; - ticks *= value; - return datetime.getDisplayRunningTime(ticks); - }; + if (!state || !state.NowPlayingItem || !currentRuntimeTicks) { + return '--:--'; + } - context.querySelector('.nowPlayingVolumeSlider').addEventListener('input', (e) => { - playbackManager.setVolume(e.target.value, currentPlayer); - }); + let ticks = currentRuntimeTicks; + ticks /= 100; + ticks *= value; + return datetime.getDisplayRunningTime(ticks); + }; - context.querySelector('.buttonMute').addEventListener('click', function () { - playbackManager.toggleMute(currentPlayer); - }); - var playlistContainer = context.querySelector('.playlist'); - playlistContainer.addEventListener('action-remove', function (e) { - playbackManager.removeFromPlaylist([e.detail.playlistItemId], currentPlayer); - }); - playlistContainer.addEventListener('itemdrop', function (e) { - var newIndex = e.detail.newIndex; - var playlistItemId = e.detail.playlistItemId; - playbackManager.movePlaylistItem(playlistItemId, newIndex, currentPlayer); - }); - context.querySelector('.btnSavePlaylist').addEventListener('click', savePlaylist); - context.querySelector('.btnTogglePlaylist').addEventListener('click', function () { - if (context.querySelector('.playlist').classList.contains('hide')) { - context.querySelector('.playlist').classList.remove('hide'); - context.querySelector('.btnSavePlaylist').classList.remove('hide'); - context.querySelector('.volumecontrol').classList.add('hide'); - if (layoutManager.mobile) { - context.querySelector('.playlistSectionButton').classList.remove('playlistSectionButtonTransparent'); - } - } else { - context.querySelector('.playlist').classList.add('hide'); - context.querySelector('.btnSavePlaylist').classList.add('hide'); - if (showMuteButton || showVolumeSlider) { - context.querySelector('.volumecontrol').classList.remove('hide'); - } - if (layoutManager.mobile) { - context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); - } - } - }); - } + context.querySelector('.nowPlayingVolumeSlider').addEventListener('input', (e) => { + playbackManager.setVolume(e.target.value, currentPlayer); + }); - function onPlayerChange() { - bindToPlayer(dlg, playbackManager.getCurrentPlayer()); - } - - function onMessageSubmit(e) { - var form = e.target; - playbackManager.sendCommand({ - Name: 'DisplayMessage', - Arguments: { - Header: form.querySelector('#txtMessageTitle').value, - Text: form.querySelector('#txtMessageText', form).value - } - }, currentPlayer); - form.querySelector('input').value = ''; - - require(['toast'], function (toast) { - toast('Message sent.'); - }); - - e.preventDefault(); - e.stopPropagation(); - return false; - } - - function onSendStringSubmit(e) { - var form = e.target; - playbackManager.sendCommand({ - Name: 'SendString', - Arguments: { - String: form.querySelector('#txtTypeText', form).value - } - }, currentPlayer); - form.querySelector('input').value = ''; - - require(['toast'], function (toast) { - toast('Text sent.'); - }); - - e.preventDefault(); - e.stopPropagation(); - return false; - } - - function init(ownerView, context) { - let volumecontrolHtml = '
'; - volumecontrolHtml += ``; - volumecontrolHtml += '
'; - volumecontrolHtml += '
'; - let optionsSection = context.querySelector('.playlistSectionButton'); - if (!layoutManager.mobile) { - context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', volumecontrolHtml); - optionsSection.classList.remove('align-items-center', 'justify-content-center'); - optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); + context.querySelector('.buttonMute').addEventListener('click', function () { + playbackManager.toggleMute(currentPlayer); + }); + const playlistContainer = context.querySelector('.playlist'); + playlistContainer.addEventListener('action-remove', function (e) { + playbackManager.removeFromPlaylist([e.detail.playlistItemId], currentPlayer); + }); + playlistContainer.addEventListener('itemdrop', function (e) { + const newIndex = e.detail.newIndex; + const playlistItemId = e.detail.playlistItemId; + playbackManager.movePlaylistItem(playlistItemId, newIndex, currentPlayer); + }); + context.querySelector('.btnSavePlaylist').addEventListener('click', savePlaylist); + context.querySelector('.btnTogglePlaylist').addEventListener('click', function () { + if (context.querySelector('.playlist').classList.contains('hide')) { context.querySelector('.playlist').classList.remove('hide'); context.querySelector('.btnSavePlaylist').classList.remove('hide'); - context.classList.add('padded-bottom'); + context.querySelector('.volumecontrol').classList.add('hide'); + if (layoutManager.mobile) { + context.querySelector('.playlistSectionButton').classList.remove('playlistSectionButtonTransparent'); + } } else { - optionsSection.querySelector('.btnTogglePlaylist').insertAdjacentHTML('afterend', volumecontrolHtml); - optionsSection.classList.add('playlistSectionButtonTransparent'); - context.querySelector('.btnTogglePlaylist').classList.remove('hide'); - context.querySelector('.playlistSectionButton').classList.remove('justify-content-center'); - context.querySelector('.playlistSectionButton').classList.add('justify-content-space-between'); + context.querySelector('.playlist').classList.add('hide'); + context.querySelector('.btnSavePlaylist').classList.add('hide'); + if (showMuteButton || showVolumeSlider) { + context.querySelector('.volumecontrol').classList.remove('hide'); + } + if (layoutManager.mobile) { + context.querySelector('.playlistSectionButton').classList.add('playlistSectionButtonTransparent'); + } } + }); + } - bindEvents(context); - context.querySelector('.sendMessageForm').addEventListener('submit', onMessageSubmit); - context.querySelector('.typeTextForm').addEventListener('submit', onSendStringSubmit); - events.on(playbackManager, 'playerchange', onPlayerChange); + function onPlayerChange() { + bindToPlayer(dlg, playbackManager.getCurrentPlayer()); + } - if (layoutManager.tv) { - var positionSlider = context.querySelector('.nowPlayingPositionSlider'); - positionSlider.classList.add('focusable'); - positionSlider.enableKeyboardDragging(); + function onMessageSubmit(e) { + const form = e.target; + playbackManager.sendCommand({ + Name: 'DisplayMessage', + Arguments: { + Header: form.querySelector('#txtMessageTitle').value, + Text: form.querySelector('#txtMessageText', form).value } + }, currentPlayer); + form.querySelector('input').value = ''; + + import('toast').then(({ default: toast }) => { + toast('Message sent.'); + }); + + e.preventDefault(); + e.stopPropagation(); + return false; + } + + function onSendStringSubmit(e) { + const form = e.target; + playbackManager.sendCommand({ + Name: 'SendString', + Arguments: { + String: form.querySelector('#txtTypeText', form).value + } + }, currentPlayer); + form.querySelector('input').value = ''; + + import('toast').then(({ default: toast }) => { + toast('Text sent.'); + }); + + e.preventDefault(); + e.stopPropagation(); + return false; + } + + function init(ownerView, context) { + let volumecontrolHtml = '
'; + volumecontrolHtml += ``; + volumecontrolHtml += '
'; + volumecontrolHtml += '
'; + const optionsSection = context.querySelector('.playlistSectionButton'); + if (!layoutManager.mobile) { + context.querySelector('.nowPlayingSecondaryButtons').insertAdjacentHTML('beforeend', volumecontrolHtml); + optionsSection.classList.remove('align-items-center', 'justify-content-center'); + optionsSection.classList.add('align-items-right', 'justify-content-flex-end'); + context.querySelector('.playlist').classList.remove('hide'); + context.querySelector('.btnSavePlaylist').classList.remove('hide'); + context.classList.add('padded-bottom'); + } else { + optionsSection.querySelector('.btnTogglePlaylist').insertAdjacentHTML('afterend', volumecontrolHtml); + optionsSection.classList.add('playlistSectionButtonTransparent'); + context.querySelector('.btnTogglePlaylist').classList.remove('hide'); + context.querySelector('.playlistSectionButton').classList.remove('justify-content-center'); + context.querySelector('.playlistSectionButton').classList.add('justify-content-space-between'); } - function onDialogClosed(e) { - releaseCurrentPlayer(); - events.off(playbackManager, 'playerchange', onPlayerChange); - lastPlayerState = null; + bindEvents(context); + context.querySelector('.sendMessageForm').addEventListener('submit', onMessageSubmit); + context.querySelector('.typeTextForm').addEventListener('submit', onSendStringSubmit); + events.on(playbackManager, 'playerchange', onPlayerChange); + + if (layoutManager.tv) { + const positionSlider = context.querySelector('.nowPlayingPositionSlider'); + positionSlider.classList.add('focusable'); + positionSlider.enableKeyboardDragging(); } + } - function onShow(context, tab) { - bindToPlayer(context, playbackManager.getCurrentPlayer()); - } + function onDialogClosed(e) { + releaseCurrentPlayer(); + events.off(playbackManager, 'playerchange', onPlayerChange); + lastPlayerState = null; + } - var dlg; - var currentPlayer; - var lastPlayerState; - var currentPlayerSupportedCommands = []; - var lastUpdateTime = 0; - var currentRuntimeTicks = 0; - var self = this; + function onShow(context, tab) { + bindToPlayer(context, playbackManager.getCurrentPlayer()); + } - self.init = function (ownerView, context) { - dlg = context; - init(ownerView, dlg); - }; + let dlg; + let currentPlayer; + let lastPlayerState; + let currentPlayerSupportedCommands = []; + let lastUpdateTime = 0; + let currentRuntimeTicks = 0; + const self = this; - self.onShow = function () { - onShow(dlg, window.location.hash); - }; - - self.destroy = function () { - onDialogClosed(); - }; + self.init = function (ownerView, context) { + dlg = context; + init(ownerView, dlg); }; -}); + + self.onShow = function () { + onShow(dlg, window.location.hash); + }; + + self.destroy = function () { + onDialogClosed(); + }; +} From 7a7e4857658ca35ab61cd6683da159c6ef8c7701 Mon Sep 17 00:00:00 2001 From: Ahmed Mohamed Date: Thu, 6 Aug 2020 15:20:55 +0000 Subject: [PATCH 100/155] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ar/ --- src/strings/ar.json | 118 ++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/src/strings/ar.json b/src/strings/ar.json index a9d3d2de41..eb7d6f4249 100644 --- a/src/strings/ar.json +++ b/src/strings/ar.json @@ -99,9 +99,9 @@ "DeleteUserConfirmation": "هل انت متاكد من انك تريد حذف هذا المستخدم ؟", "DeviceAccessHelp": "هذه الميزة تنطبق حصرياً على الأجهزة التي يمكن التعرف عليها فردياً ولن تمنع المتصفح من الدخول عليها. ترشيح الوصول لأجهزة المستخدم ستمنع المستخدمين من استعمال الأجهزة الجديدة إلى أن يتم اعتمادهم من هنا.", "DrmChannelsNotImported": "القنوات المجهزة بإدارة الحقوق الرقمية DRM لن تورّد.", - "EasyPasswordHelp": "الرمز الشخصي الميسرالخاص بك يمكنك من الاتصال إلى خادم مكتبتك، عبر تطبيقات أمبي على الأجهزة أو الدخول على حسابك في الشبكة الداخلية.", + "EasyPasswordHelp": "الرمز pin الميسر الخاص بك يستخدم للوصول بدون اتصل عبر التطبيقات المدعومة أو الدخول على حسابك في الشبكة الداخلية.", "EnablePhotos": "عرض الصور", - "EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى", + "EnablePhotosHelp": "سيتم اكتشاف الصور وعرضها مع ملفات الوسائط الأخرى.", "ErrorAddingListingsToSchedulesDirect": "كان هناك خطأ في إضافة الاصطفاف لخدمة \"Schedules Direct\" الخاصة بك. خدمة \"Schedules Direct\" لا تسمح إلا بعدد محدود من الاصطفافات لكل حساب. قد تحتاج إلى تسجيل الدخول إلى موقع \"Schedules Direct\" لإزالة الاصطفافات الأخرى من حسابك قبل المتابعة.", "ErrorAddingMediaPathToVirtualFolder": "كان هناك خطأ في إضافة مسار الوسائط. الرجاء التأكد من صحة المسار وأن خادم أمبي لديه صلاحية الوصول إلى الموقع.", "ErrorAddingTunerDevice": "كان هناك خطأ في إضافة جهاز المولف. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.", @@ -111,7 +111,7 @@ "ErrorPleaseSelectLineup": "الرجاء اختيار اصطفاف ثم المحاولة مرة أخرى. إن لم تتوفر أية اصطفافات، فالرجاء التأكد من اسم المستخدم وكلمة المرور الخاصة بك، وتأكد من صحة رمزك البريدي.", "ErrorSavingTvProvider": "كان هناك خطأ في حفظ مزود التلفزة. الرجاء التأكد من صلاحية الوصول إليه ثم عاود المحاولة.", "ExitFullscreen": "الخروج من الشاشة الكاملة", - "ExtractChapterImagesHelp": "استخلاص صور الأبواب سيسمح لتطبيقات أمبي أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل قدرة المعالج بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.", + "ExtractChapterImagesHelp": "استخلاص صور الفصول سيسمح للتطبيقات أن تظهر لك قوائم تصويرية لتبويبات الأفلام. هذه العملية قد تكون بطيئة، وتستغل موارد الجهاز بشكل ملحوظ، وقد تحتاج إلى حيازة بضعة غيغابايتات من مساحة التخزين بشكل مؤقت. هذه المهمة تعمل خلال عملية استكشاف المقاطع المرئية، كما يمكن أن تحدد لتكون مهمة ليلية مجدولة. يمكنك جدولة العملية من قسم جدولة المهام. لا ينصح بتشغيل هذه المهمة خلال ساعات الذروة من دخول المستخدمين.", "FFmpegSavePathNotFound": "لم نستطع تحديد موقع ffmpeg باستخدام المسار الذي أدخلته. سوف نحتاج تطبيق FFprobe أيضاً ويجب أن يتواجد في نفس المكان. إن هذه الأجزاء تكون بالعادة محزومة معاً في نفس ملف الإنزال. الرجاء التأكد من المسار المدخل والمحاولة مرة أخرى.", "FastForward": "التقديم السريع", "FileNotFound": "الملف غير موجود.", @@ -129,7 +129,7 @@ "GuideProviderSelectListings": "إختر المبوبات", "H264CrfHelp": "معامل المعدل الثابت CRF هو الجودة الافتراضية لإعدادات مشفر x264. بإمكانك إعطاء قيمة تتراوح بين 0 و 51، وكلما قلت القيمة فسينتج عن ذلك جودة أفضل (على حساب حجم تخزين أعلى). القيم المعقول تتراوح بين 18 و 28. الافتراضي لـ x264 هي 23، لذا فبإمكانك استخدام هذه القيمة كنقطة بداية.", "EncoderPresetHelp": "اختر قيمة أعلى لتحسين السرة والأداء وقيمة أقل لتحسين الجودة.", - "HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية آلي.", + "HardwareAccelerationWarning": "تمكين التسريع بعتاد الحاسوب قد يتسبب في عدم استقرار بعض أنواع الأنظمة. تأكد من أن نظام التشغيل الخاص بك محدث إلى آخر نسخة وأن سواقات الفيديو محدثة أيضاً. إذا واجهت أية صعوبات في تسغيل الفيديو بعد تمكين هذه الخاصية، فعليك إرجاع الإعداد إلى وضعية بلا None.", "HeaderAccessSchedule": "جدول الدخولات", "HeaderAccessScheduleHelp": "إنشئ جدول دخولات لكي تتمكن من تحديد ساعات للدخول.", "HeaderActiveDevices": "الأجهزة المفعّلة", @@ -145,7 +145,7 @@ "HeaderAllowMediaDeletionFrom": "السماح بحذف الوسائط من قبل", "HeaderApiKey": "مفتاح API", "HeaderApiKeys": "مفاتيح API", - "HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بخادم أمبي. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بحساب أمبي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.", + "HeaderApiKeysHelp": "التطبيقات الخارجية تحتاج أن تمتلك مفتاح api لكي تتصل بالخادم. هذه المفاتيح تُصدر عن طريق تسجيل الدخول بمستخدم عادي، أو عن طريق منح التطبيق مفتاحاً أصدر يدوياً.", "HeaderApp": "التطبيق", "HeaderAudioSettings": "إعدادات الصوت", "HeaderBooks": "الكتب", @@ -232,7 +232,7 @@ "HeaderPreferredMetadataLanguage": "اللغة المفضلة لواصفات البيانات", "HeaderProfile": "الحساب", "HeaderProfileInformation": "معلومات العريضة", - "HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل خادم أمبي في الجهاز", + "HeaderProfileServerSettingsHelp": "هذه القيم ستتحكم في كيفية تقديم شكل الخادم في للعملاء.", "HeaderRecentlyPlayed": "تم تشغيله مؤخراً", "HeaderRecordingPostProcessing": "تطبيق ما-بعد-المعالجة للتسجيل", "HeaderRemoteControl": "التحكم عن بعد", @@ -254,7 +254,7 @@ "HeaderSelectServerCachePath": "إختر مسار كاشة الخادم", "HeaderSelectServerCachePathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم كاشة لملفات الخادم. يجب أن يكون هذا المجلد قابل للكتابة فيه.", "HeaderSelectTranscodingPath": "إختر المسار المؤقت للتشفير البيني", - "HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم للملفات المؤقتة للتشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.", + "HeaderSelectTranscodingPathHelp": "تصفح أو أدخل المسار الذي ترغب أن يُستخدم لملفات التشفير البيني. يجب أن يكون هذا المجلد قابل للكتابة فيه.", "HeaderSendMessage": "أرسل رسالة", "HeaderSeries": "المسلسلات", "HeaderServerSettings": "إعدادات الخادم", @@ -291,17 +291,17 @@ "HeaderXmlSettings": "إعدادات xml", "HeaderYears": "السنوات", "HeadersFolders": "مجلدات", - "ImportFavoriteChannelsHelp": "عند التفعيل، فقط القنوات التي علّمت في المفضلة على هذا المولف ستورد إلى النظام.", - "ImportMissingEpisodesHelp": "عند التمكين، المعلومات الناقصة للحلقات ستورّد إلى قاعدة بيانات أمبي وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكنبات.", + "ImportFavoriteChannelsHelp": "فقط القنوات التي علّمت في المفضلة على جهاز المولف ستورد.", + "ImportMissingEpisodesHelp": "المعلومات الناقصة للحلقات ستورّد إلى قاعدة بياناتك وستعرض داخل المواسم والمسلسلات. قد تتسبب هذه بأوقات أطول بكثير عند تمشيط المكتبات.", "LabelAbortedByServerShutdown": "(تم إهماله بسبب عملية إغلاق الخادم)", "LabelAccessDay": "يوم الأسبوع:", - "LabelAccessEnd": "تاريخ النهاية", - "LabelAccessStart": "تاريخ البداية", + "LabelAccessEnd": "وقت النهاية:", + "LabelAccessStart": "وقت البداية:", "LabelAirDays": "أيام البث:", "LabelAirTime": "وقت البث:", - "LabelAlbum": "الألبوم", + "LabelAlbum": "الألبوم:", "LabelAlbumArtHelp": "PN المستخدمة في رسومات الألبوم، داخل سمة dlna:profileID في upnp:albumArtURI. بعض الأجهزة تحتاج قيمة محددة، مهما كان حجم الصورة.", - "LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم", + "LabelAlbumArtMaxHeight": "الارتفاع الأقصى لرسومات الألبوم:", "LabelAlbumArtMaxHeightHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.", "LabelAlbumArtMaxWidth": "العرض الأقصى لرسوم الألبوم:", "LabelAlbumArtMaxWidthHelp": "الدقة القصوى لرسومات الألبوم المظهّرة عبر سمة upnp:albumArtURI.", @@ -310,42 +310,42 @@ "LabelAll": "الجميع", "LabelAllowHWTranscoding": "السماح بالتشفير البيني بعتاد الحاسب", "LabelAppName": "اسم التطبيق", - "LabelAppNameExample": "مثال: Sickbeard، NzbDrone", + "LabelAppNameExample": "مثال: Sickbeard، Sonarr", "LabelArtists": "الفنانون:", - "LabelArtistsHelp": "فصل الاستعمالات المتعددة ;", + "LabelArtistsHelp": "افصل بين الفنانين ب ; فاصلة منقوطة.", "LabelAudioLanguagePreference": "اللغة المفضلة للصوت:", "LabelBindToLocalNetworkAddress": "إربطه إلى عنوان شبكة محلي:", - "LabelBindToLocalNetworkAddressHelp": "هذا خياري. امتطي عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم أمبي.", - "LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل (بالثواني)", - "LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين يث رسائل قيد التشغيل", - "LabelCache": "ذاكرة الكاشة", - "LabelCachePath": "مسار ذاكرة الكاشة:", - "LabelCachePathHelp": "حدد موقع مخصص لملفات كاشة الخادم، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة التلقائية.", + "LabelBindToLocalNetworkAddressHelp": "تجاوز عنوان الآي بي المحلي لربطه بخادم http. إذا ترك فارغاً، فإن الخادم سيربطه بجميع العناوين المتاحة. تغيير هذه القيمة يتطلب إعادة تشغيل خادم جيلليفن.", + "LabelBlastMessageInterval": "فترات بث رسالة قيد التشغيل", + "LabelBlastMessageIntervalHelp": "يحدد الفترة بالثواني بين بث رسائل قيد التشغيل.", + "LabelCache": "مَخبأ (كاش):", + "LabelCachePath": "مسار ذاكرة الكاش:", + "LabelCachePathHelp": "حدد موقع مخصص لملفات الخادم المؤقتة، مثل الصور وغيرها. أترك هذه الخانة فارغة لاستعمال القيمة الافتراضية.", "LabelCancelled": "تم الإلغاء", - "LabelCollection": "المجموعة", + "LabelCollection": "المجموعة:", "LabelCommunityRating": "تقييم المجتمع:", - "LabelContentType": "نوع المحتوى", + "LabelContentType": "نوع المحتوى:", "LabelCountry": "البلد:", "LabelCurrentPassword": "كلمة السر الحالية:", - "LabelCustomCertificatePath": "مسار شهادة ssl مخصص:", + "LabelCustomCertificatePath": "مسار شهادة SSL المخصص:", "LabelCustomCertificatePathHelp": "مسار ملف PKCS # 12 يحتوي على شهادة ومفتاح خاص لتمكين دعم TLS على مجال مخصص.", - "LabelCustomCss": "تنيسق CSS مخصوص:", - "LabelCustomCssHelp": "طبق تنسيق css مخصوصة لواجهة الويب.", + "LabelCustomCss": "تنيسق CSS مخصص:", + "LabelCustomCssHelp": "طبق تنسيقك css المخصص لواجهة الويب.", "LabelCustomDeviceDisplayName": "اسم العرض:", - "LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام", + "LabelCustomDeviceDisplayNameHelp": "أذكر اسم عرض مخصوص أو أتركه فارغاً لاستخدام الاسم المبلغ من الجهاز.", "LabelDateAddedBehavior": "كيف يتصرف المحتوى الجديد نحو \"تاريخ الإضافة\" الخاص به:", - "LabelDateAddedBehaviorHelp": "إذا استعرضت قيمة واصفات البيانا فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.", + "LabelDateAddedBehaviorHelp": "إذا اخذت واصفات البيانات قيمة، فإنها سوف تستخدم قبل أن تستخدم أي من هذه الخيارات.", "LabelDay": "اليوم:", "LabelDeathDate": "تاريخ الوفاة:", - "LabelDefaultUser": "المستخدم الافتراضي", + "LabelDefaultUser": "المستخدم الافتراضي:", "LabelDefaultUserHelp": "لتحديد مكتبة المستخدم التي تظهر على الأجهزة المتصلة. بإمكان الامتطاء على هذه القيمة لكل جهاز عن طريق عرائض الأجهزة.", "LabelDeviceDescription": "وصف الجهاز", - "LabelDidlMode": "طور didl:", + "LabelDidlMode": "طور DIDL:", "LabelDisplayMissingEpisodesWithinSeasons": "أظهر الحلقات المفقودة في مجلدات المواسم", "LabelDisplayName": "الاسم المعروض:", "LabelDisplaySpecialsWithinSeasons": "أظهر الحلقات الخاصة في المواسم التي بثت فيها", "LabelDownMixAudioScale": "تعزيز الصوت عند تقليل توزيع قنوات الصوت:", - "LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة بـ 1 للمحافظة على القيمة الأصلية للصوت.", + "LabelDownMixAudioScaleHelp": "تعزيز الصوت عند تقليل توزيع قنوات الصوت. حدد القيمة ب 1 للمحافظة على القيمة الأصلية للصوت.", "LabelDownloadLanguages": "إنزال اللغة:", "LabelDynamicExternalId": "معرفة {0}:", "LabelEasyPinCode": "الرمز الشخصي الميسر:", @@ -354,8 +354,8 @@ "LabelEnableAutomaticPortMap": "فعل الخاصية الآلية في التوفيق بين المنافذ", "LabelEnableAutomaticPortMapHelp": "حاول التوفيق بين المنفذ العالمي والمنفذ المحلي آلياً باستخدام آلية UPnP. هذه الخاصية قد لا تعمل مع بعض أنواع الراوترات.", "LabelEnableBlastAliveMessages": "بث رسائل قيد التشغيل", - "LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك", - "LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء (بالثواني)", + "LabelEnableBlastAliveMessagesHelp": "فعل هذه الخاصية إذا كان الخادم لا يكتشف بكفاءة من قبل أجهزة UPnP الأخرى على شبكتك.", + "LabelEnableDlnaClientDiscoveryInterval": "فترات استكشاف العملاء", "LabelEnableDlnaClientDiscoveryIntervalHelp": "يحدد الفترة بالثواني بين عمليات بحث SSDP التي يقوم بها أمبي.", "LabelEnableDlnaDebugLogging": "تفعيل خاصية كشوفات أخطاء DLNA", "LabelEnableDlnaDebugLoggingHelp": "هذه ستنشئ سجلات كشفية ضخمة ولا ينبغي تفعيلها إلا عند الحاجة إليها بغرض استكشاف الأخطاء وحصرها.", @@ -930,7 +930,7 @@ "Backdrops": "خلفيات متغيرة للصفحة", "Backdrop": "خلفية متغيرة للصفحة", "Auto": "تلقائي", - "AuthProviderHelp": "حدد مقدم المصادقات ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.", + "AuthProviderHelp": "اختار مقدم المصادقة ليتم استخدامه لمصادقة كلمة مرور هذا المستخدم.", "AroundTime": "حول", "AttributeNew": "جديد", "AspectRatio": "نسبة العرض الى الارتفاع", @@ -1038,7 +1038,7 @@ "Director": "المخرج", "DirectPlaying": "بث بدون تحويل الصيغة", "DirectStreaming": "البث المباشر", - "DirectStreamHelp2": "البث المباشر للملف يستخدم طاقة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.", + "DirectStreamHelp2": "البث المباشر للملف يستخدم قوة معالجة قليلة جدًا دون أي خسارة في جودة الفيديو.", "DirectStreamHelp1": "الوسائط متوافقة مع الجهاز فيما يتعلق بالدقة ونوع الوسائط (H.264 ، AC3 ، إلخ) ، ولكنها في حاوية ملفات غير متوافقة (mkv ، avi ، wmv ، إلخ). سيتم إعادة حزم الفيديو في الوقت الحقيقي قبل بثه إلى الجهاز.", "DetectingDevices": "يتم الكشف عن الأجهزة", "Desktop": "سطح المكتب", @@ -1139,5 +1139,51 @@ "Dislike": "لم يعجبنى", "ButtonSyncPlay": "SyncPlay", "ExtraLarge": "كبير جدا", - "EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل." + "EnableNextVideoInfoOverlayHelp": "في نهاية الفيديو, عرض معلومات عن الفيديو القادم في قائمة التشغيل.", + "LabelDroppedFrames": "الاطارات الساقطة:", + "LabelDropImageHere": "اسقط صورة هنا، او ضغط تصفح.", + "LabelDisplayOrder": "ترتيب المعروض:", + "LabelDisplayMode": "وضع المعروض:", + "LabelDisplayLanguageHelp": "ترجمة جيلليفين هو مشروع مستمر.", + "LabelDisplayLanguage": "لغة العرض:", + "LabelDiscNumber": "رقم القرص:", + "LabelDeinterlaceMethod": "طريقة تقليل التشابك:", + "LabelDefaultScreen": "الشاشة الافتراضية:", + "LabelDateTimeLocale": "وقت و تاريخ محلي:", + "LabelDateAdded": "تاريخ الاضافة:", + "LabelCustomRating": "تقييم مخصص:", + "LabelCriticRating": "تقييم النقاد:", + "LabelCorruptedFrames": "الإطارات التالفة:", + "LabelChannels": "القنوات:", + "LabelCertificatePasswordHelp": "اذا تطلبت شهادتك الامنية كلمة مرور، من فضلك ادخلها هنا.", + "LabelCertificatePassword": "كلمة مرور الشهادة الامنية:", + "LabelBurnSubtitles": "الترجمات المحروقة:", + "LabelBlockContentWithTags": "احجب العناصر بالعلامات:", + "LabelBitrate": "معدل البت:", + "LabelBirthYear": "عام الميلاد:", + "LabelBirthDate": "تاريخ الميلاد:", + "LabelAutomaticallyRefreshInternetMetadataEvery": "حدث وصف البيانات تلقائيا من الانترنت:", + "LabelAuthProvider": "مقدم التصديق:", + "LabelAudioSampleRate": "سرعة معينة الصوت:", + "LabelAudioCodec": "ترميز الصوت:", + "LabelAudioChannels": "قنوات الصوت:", + "LabelAudioBitrate": "معدل بث الصوت:", + "LabelAudioBitDepth": "عمق بث الصوت:", + "LabelAudio": "الصوت", + "LabelAllowedRemoteAddressesMode": "وضع مرشح عنوان المضيف IP البعيد:", + "LabelAllowedRemoteAddresses": "مرشح عنوان المضيف IP البعيد:", + "LabelAirsBeforeSeason": "عروض بث قبل الموسم:", + "LabelAirsBeforeEpisode": "عروض بث قبل الحلقة:", + "LabelAirsAfterSeason": "عروض بث بعد الموسم:", + "Label3DFormat": "صيغة ثلاثية الابعاد:", + "Kids": "اطفال", + "Items": "عناصر", + "ItemCount": "{0} عنصر", + "InstantMix": "خلط فوري", + "HeaderSyncPlayEnabled": "تزامن اللعب ممكَّن", + "HeaderSyncPlaySelectGroup": "انضم لمجموعة", + "EnableDetailsBannerHelp": "اظهر صوره اللافته اعلى عنصر تفاصيل الصفحة.", + "EnableDetailsBanner": "لافتة التفاصيل", + "EnableDecodingColorDepth10Vp9": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل VP9", + "EnableDecodingColorDepth10Hevc": "تمكين ترميز ال10 بت عبر العتاد الصلب من اجل HEVC" } From 637c134145a7b5ec3e405dc9a2cd157edd1c66ce Mon Sep 17 00:00:00 2001 From: sharkykh Date: Thu, 6 Aug 2020 15:47:14 +0000 Subject: [PATCH 101/155] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/he/ --- src/strings/he.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/strings/he.json b/src/strings/he.json index 58eb6ee1b4..c266f587c2 100644 --- a/src/strings/he.json +++ b/src/strings/he.json @@ -471,7 +471,7 @@ "TabTrailers": "טריילרים", "TabTranscoding": "קידוד", "TabUpcoming": "בקרוב", - "Tags": "תגים", + "Tags": "מילות מפתח", "TellUsAboutYourself": "ספר לנו על עצמך", "ThisWizardWillGuideYou": "אשף זה יעזור לך בהתליך ההתקנה.", "Thursday": "חמישי", @@ -588,7 +588,7 @@ "MessageConfirmRestart": "‫האם אתה בטוח שברצונך לאתחל את שרת ה-Jellyfin‏?", "HeaderThisUserIsCurrentlyDisabled": "משתמש זה אינו פעיל כרגע", "HeaderTaskTriggers": "טריגרים של המשימה", - "HeaderTags": "תגיות", + "HeaderTags": "מילות מפתח", "HeaderStopRecording": "עצור הקלטה", "HeaderSortOrder": "סדר מיון", "HeaderSortBy": "מיין לפי", @@ -867,5 +867,7 @@ "OptionEveryday": "כל יום", "OptionEnableExternalContentInSuggestions": "הפעל תוכן חיצוני בהמלצות", "OptionEnableAccessToAllLibraries": "אפשר גישה לכל הספריות", - "OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים" + "OptionEnableAccessToAllChannels": "אפשר גישה לכל הערוצים", + "HeaderSyncPlaySelectGroup": "הצטרף לקבוצה", + "TabUsers": "משתמשים" } From 0cd82da8f2aa441650900d882a82c15f457759e6 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 20:59:14 +0200 Subject: [PATCH 102/155] Migrate navdrawer to ES6 --- .eslintignore | 1 - package.json | 1 + src/libraries/navdrawer/navdrawer.js | 639 ++++++++++++++------------- src/scripts/libraryMenu.js | 2 + 4 files changed, 324 insertions(+), 319 deletions(-) diff --git a/.eslintignore b/.eslintignore index 8e3aee83fb..74b18ddcf6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,3 @@ node_modules dist .idea .vscode -src/libraries diff --git a/package.json b/package.json index bf3ac42f97..7b895bc367 100644 --- a/package.json +++ b/package.json @@ -275,6 +275,7 @@ "src/elements/emby-tabs/emby-tabs.js", "src/elements/emby-textarea/emby-textarea.js", "src/elements/emby-toggle/emby-toggle.js", + "src/libraries/navdrawer/navdrawer.js", "src/plugins/backdropScreensaver/plugin.js", "src/plugins/bookPlayer/plugin.js", "src/plugins/bookPlayer/tableOfContents.js", diff --git a/src/libraries/navdrawer/navdrawer.js b/src/libraries/navdrawer/navdrawer.js index 4733c617f3..965b68aee4 100644 --- a/src/libraries/navdrawer/navdrawer.js +++ b/src/libraries/navdrawer/navdrawer.js @@ -1,354 +1,357 @@ -define(["browser", "dom", "css!./navdrawer", "scrollStyles"], function (browser, dom) { - "use strict"; +/* Cleaning this file properly is not neecessary, since it's an outdated library + * and will be replaced soon by a Vue component. + */ - browser = browser.default || browser; +import browser from 'browser'; +import dom from 'dom'; +import 'css!./navdrawer'; +import 'scrollStyles'; - return function (options) { - function getTouches(e) { - return e.changedTouches || e.targetTouches || e.touches; +export default function (options) { + function getTouches(e) { + return e.changedTouches || e.targetTouches || e.touches; + } + + function onMenuTouchStart(e) { + options.target.classList.remove('transition'); + var touches = getTouches(e); + var touch = touches[0] || {}; + menuTouchStartX = touch.clientX; + menuTouchStartY = touch.clientY; + menuTouchStartTime = new Date().getTime(); + } + + function setVelocity(deltaX) { + var time = new Date().getTime() - (menuTouchStartTime || 0); + velocity = Math.abs(deltaX) / time; + } + + function onMenuTouchMove(e) { + var isOpen = self.visible; + var touches = getTouches(e); + var touch = touches[0] || {}; + var endX = touch.clientX || 0; + var endY = touch.clientY || 0; + var deltaX = endX - (menuTouchStartX || 0); + var deltaY = endY - (menuTouchStartY || 0); + setVelocity(deltaX); + + if (isOpen && dragMode !== 1 && deltaX > 0) { + dragMode = 2; } - function onMenuTouchStart(e) { - options.target.classList.remove("transition"); - var touches = getTouches(e); - var touch = touches[0] || {}; - menuTouchStartX = touch.clientX; - menuTouchStartY = touch.clientY; - menuTouchStartTime = new Date().getTime(); + if (dragMode === 0 && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) { + dragMode = 1; + scrollContainer.addEventListener('scroll', disableEvent); + self.showMask(); + } else if (dragMode === 0 && Math.abs(deltaY) >= 5) { + dragMode = 2; } - function setVelocity(deltaX) { - var time = new Date().getTime() - (menuTouchStartTime || 0); - velocity = Math.abs(deltaX) / time; + if (dragMode === 1) { + newPos = currentPos + deltaX; + self.changeMenuPos(); } + } - function onMenuTouchMove(e) { - var isOpen = self.visible; - var touches = getTouches(e); - var touch = touches[0] || {}; - var endX = touch.clientX || 0; - var endY = touch.clientY || 0; - var deltaX = endX - (menuTouchStartX || 0); - var deltaY = endY - (menuTouchStartY || 0); - setVelocity(deltaX); + function onMenuTouchEnd(e) { + options.target.classList.add('transition'); + scrollContainer.removeEventListener('scroll', disableEvent); + dragMode = 0; + var touches = getTouches(e); + var touch = touches[0] || {}; + var endX = touch.clientX || 0; + var endY = touch.clientY || 0; + var deltaX = endX - (menuTouchStartX || 0); + var deltaY = endY - (menuTouchStartY || 0); + currentPos = deltaX; + self.checkMenuState(deltaX, deltaY); + } - if (isOpen && 1 !== dragMode && deltaX > 0) { - dragMode = 2; - } + function onEdgeTouchStart(e) { + if (isPeeking) { + onMenuTouchMove(e); + } else { + if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) { + isPeeking = true; - if (0 === dragMode && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) { - dragMode = 1; - scrollContainer.addEventListener("scroll", disableEvent); - self.showMask(); - } else if (0 === dragMode && Math.abs(deltaY) >= 5) { - dragMode = 2; - } - - if (1 === dragMode) { - newPos = currentPos + deltaX; - self.changeMenuPos(); - } - } - - function onMenuTouchEnd(e) { - options.target.classList.add("transition"); - scrollContainer.removeEventListener("scroll", disableEvent); - dragMode = 0; - var touches = getTouches(e); - var touch = touches[0] || {}; - var endX = touch.clientX || 0; - var endY = touch.clientY || 0; - var deltaX = endX - (menuTouchStartX || 0); - var deltaY = endY - (menuTouchStartY || 0); - currentPos = deltaX; - self.checkMenuState(deltaX, deltaY); - } - - function onEdgeTouchStart(e) { - if (isPeeking) { - onMenuTouchMove(e); - } else { - if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) { - isPeeking = true; - - if (e.type === "touchstart") { - dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {}); - dom.addEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {}); - } - - onMenuTouchStart(e); + if (e.type === 'touchstart') { + dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {}); + dom.addEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {}); } + + onMenuTouchStart(e); } } + } - function onEdgeTouchMove(e) { - e.preventDefault(); - e.stopPropagation(); - onEdgeTouchStart(e); + function onEdgeTouchMove(e) { + e.preventDefault(); + e.stopPropagation(); + onEdgeTouchStart(e); + } + + function onEdgeTouchEnd(e) { + if (isPeeking) { + isPeeking = false; + dom.removeEventListener(edgeContainer, 'touchmove', onEdgeTouchMove, {}); + onMenuTouchEnd(e); } + } - function onEdgeTouchEnd(e) { - if (isPeeking) { - isPeeking = false; - dom.removeEventListener(edgeContainer, "touchmove", onEdgeTouchMove, {}); - onMenuTouchEnd(e); - } - } + function disableEvent(e) { + e.preventDefault(); + e.stopPropagation(); + } - function disableEvent(e) { - e.preventDefault(); - e.stopPropagation(); - } + function onBackgroundTouchStart(e) { + var touches = getTouches(e); + var touch = touches[0] || {}; + backgroundTouchStartX = touch.clientX; + backgroundTouchStartTime = new Date().getTime(); + } - function onBackgroundTouchStart(e) { - var touches = getTouches(e); - var touch = touches[0] || {}; - backgroundTouchStartX = touch.clientX; - backgroundTouchStartTime = new Date().getTime(); - } + function onBackgroundTouchMove(e) { + var touches = getTouches(e); + var touch = touches[0] || {}; + var endX = touch.clientX || 0; - function onBackgroundTouchMove(e) { - var touches = getTouches(e); - var touch = touches[0] || {}; - var endX = touch.clientX || 0; - - if (endX <= options.width && self.isVisible) { - countStart++; - var deltaX = endX - (backgroundTouchStartX || 0); - - if (countStart == 1) { - startPoint = deltaX; - } - if (deltaX < 0 && dragMode !== 2) { - dragMode = 1; - newPos = deltaX - startPoint + options.width; - self.changeMenuPos(); - var time = new Date().getTime() - (backgroundTouchStartTime || 0); - velocity = Math.abs(deltaX) / time; - } - } - - e.preventDefault(); - e.stopPropagation(); - } - - function onBackgroundTouchEnd(e) { - var touches = getTouches(e); - var touch = touches[0] || {}; - var endX = touch.clientX || 0; + if (endX <= options.width && self.isVisible) { + countStart++; var deltaX = endX - (backgroundTouchStartX || 0); - self.checkMenuState(deltaX); - countStart = 0; - } - function onMaskTransitionEnd() { - var classList = mask.classList; - - if (!classList.contains("backdrop")) { - classList.add("hide"); + if (countStart == 1) { + startPoint = deltaX; + } + if (deltaX < 0 && dragMode !== 2) { + dragMode = 1; + newPos = deltaX - startPoint + options.width; + self.changeMenuPos(); + var time = new Date().getTime() - (backgroundTouchStartTime || 0); + velocity = Math.abs(deltaX) / time; } } - var self; - var defaults; - var mask; - var newPos = 0; - var currentPos = 0; - var startPoint = 0; - var countStart = 0; - var velocity = 0; - options.target.classList.add("transition"); - var dragMode = 0; - var scrollContainer = options.target.querySelector(".mainDrawer-scrollContainer"); - scrollContainer.classList.add("scrollY"); + e.preventDefault(); + e.stopPropagation(); + } - var TouchMenuLA = function () { - self = this; - defaults = { - width: 260, - handleSize: 10, - disableMask: false, - maxMaskOpacity: 0.5 - }; - this.isVisible = false; - this.initialize(); + function onBackgroundTouchEnd(e) { + var touches = getTouches(e); + var touch = touches[0] || {}; + var endX = touch.clientX || 0; + var deltaX = endX - (backgroundTouchStartX || 0); + self.checkMenuState(deltaX); + countStart = 0; + } + + function onMaskTransitionEnd() { + var classList = mask.classList; + + if (!classList.contains('backdrop')) { + classList.add('hide'); + } + } + + var self; + var defaults; + var mask; + var newPos = 0; + var currentPos = 0; + var startPoint = 0; + var countStart = 0; + var velocity = 0; + options.target.classList.add('transition'); + var dragMode = 0; + var scrollContainer = options.target.querySelector('.mainDrawer-scrollContainer'); + scrollContainer.classList.add('scrollY'); + + var TouchMenuLA = function () { + self = this; + defaults = { + width: 260, + handleSize: 10, + disableMask: false, + maxMaskOpacity: 0.5 }; + this.isVisible = false; + this.initialize(); + }; - TouchMenuLA.prototype.initElements = function () { - options.target.classList.add("touch-menu-la"); - options.target.style.width = options.width + "px"; - options.target.style.left = -options.width + "px"; + TouchMenuLA.prototype.initElements = function () { + options.target.classList.add('touch-menu-la'); + options.target.style.width = options.width + 'px'; + options.target.style.left = -options.width + 'px'; - if (!options.disableMask) { - mask = document.createElement("div"); - mask.className = "tmla-mask hide"; - document.body.appendChild(mask); - dom.addEventListener(mask, dom.whichTransitionEvent(), onMaskTransitionEnd, { - passive: true - }); - } - }; - - var menuTouchStartX; - var menuTouchStartY; - var menuTouchStartTime; - var edgeContainer = document.querySelector(".mainDrawerHandle"); - var isPeeking = false; - - TouchMenuLA.prototype.animateToPosition = function (pos) { - requestAnimationFrame(function () { - options.target.style.transform = pos ? "translateX(" + pos + "px)" : "none"; + if (!options.disableMask) { + mask = document.createElement('div'); + mask.className = 'tmla-mask hide'; + document.body.appendChild(mask); + dom.addEventListener(mask, dom.whichTransitionEvent(), onMaskTransitionEnd, { + passive: true }); - }; + } + }; - TouchMenuLA.prototype.changeMenuPos = function () { - if (newPos <= options.width) { - this.animateToPosition(newPos); - } - }; + var menuTouchStartX; + var menuTouchStartY; + var menuTouchStartTime; + var edgeContainer = document.querySelector('.mainDrawerHandle'); + var isPeeking = false; - TouchMenuLA.prototype.clickMaskClose = function () { - mask.addEventListener("click", function () { + TouchMenuLA.prototype.animateToPosition = function (pos) { + requestAnimationFrame(function () { + options.target.style.transform = pos ? 'translateX(' + pos + 'px)' : 'none'; + }); + }; + + TouchMenuLA.prototype.changeMenuPos = function () { + if (newPos <= options.width) { + this.animateToPosition(newPos); + } + }; + + TouchMenuLA.prototype.clickMaskClose = function () { + mask.addEventListener('click', function () { + self.close(); + }); + }; + + TouchMenuLA.prototype.checkMenuState = function (deltaX, deltaY) { + if (velocity >= 0.4) { + if (deltaX >= 0 || Math.abs(deltaY || 0) >= 70) { + self.open(); + } else { self.close(); - }); - }; - - TouchMenuLA.prototype.checkMenuState = function (deltaX, deltaY) { - if (velocity >= 0.4) { - if (deltaX >= 0 || Math.abs(deltaY || 0) >= 70) { - self.open(); - } else { + } + } else { + if (newPos >= 100) { + self.open(); + } else { + if (newPos) { self.close(); } - } else { - if (newPos >= 100) { - self.open(); - } else { - if (newPos) { - self.close(); - } - } } - }; - - TouchMenuLA.prototype.open = function () { - this.animateToPosition(options.width); - currentPos = options.width; - this.isVisible = true; - options.target.classList.add("drawer-open"); - self.showMask(); - self.invoke(options.onChange); - }; - - TouchMenuLA.prototype.close = function () { - this.animateToPosition(0); - currentPos = 0; - self.isVisible = false; - options.target.classList.remove("drawer-open"); - self.hideMask(); - self.invoke(options.onChange); - }; - - TouchMenuLA.prototype.toggle = function () { - if (self.isVisible) { - self.close(); - } else { - self.open(); - } - }; - - var backgroundTouchStartX; - var backgroundTouchStartTime; - - TouchMenuLA.prototype.showMask = function () { - mask.classList.remove("hide"); - mask.classList.add("backdrop"); - }; - - TouchMenuLA.prototype.hideMask = function () { - mask.classList.add("hide"); - mask.classList.remove("backdrop"); - }; - - TouchMenuLA.prototype.invoke = function (fn) { - if (fn) { - fn.apply(self); - } - }; - - var _edgeSwipeEnabled; - - TouchMenuLA.prototype.setEdgeSwipeEnabled = function (enabled) { - if (!options.disableEdgeSwipe) { - if (browser.touch) { - if (enabled) { - if (!_edgeSwipeEnabled) { - _edgeSwipeEnabled = true; - dom.addEventListener(edgeContainer, "touchstart", onEdgeTouchStart, { - passive: true - }); - dom.addEventListener(edgeContainer, "touchend", onEdgeTouchEnd, { - passive: true - }); - dom.addEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, { - passive: true - }); - } - } else { - if (_edgeSwipeEnabled) { - _edgeSwipeEnabled = false; - dom.removeEventListener(edgeContainer, "touchstart", onEdgeTouchStart, { - passive: true - }); - dom.removeEventListener(edgeContainer, "touchend", onEdgeTouchEnd, { - passive: true - }); - dom.removeEventListener(edgeContainer, "touchcancel", onEdgeTouchEnd, { - passive: true - }); - } - } - } - } - }; - - TouchMenuLA.prototype.initialize = function () { - options = Object.assign(defaults, options || {}); - - if (browser.edge) { - options.disableEdgeSwipe = true; - } - - self.initElements(); - - if (browser.touch) { - dom.addEventListener(options.target, "touchstart", onMenuTouchStart, { - passive: true - }); - dom.addEventListener(options.target, "touchmove", onMenuTouchMove, { - passive: true - }); - dom.addEventListener(options.target, "touchend", onMenuTouchEnd, { - passive: true - }); - dom.addEventListener(options.target, "touchcancel", onMenuTouchEnd, { - passive: true - }); - dom.addEventListener(mask, "touchstart", onBackgroundTouchStart, { - passive: true - }); - dom.addEventListener(mask, "touchmove", onBackgroundTouchMove, {}); - dom.addEventListener(mask, "touchend", onBackgroundTouchEnd, { - passive: true - }); - dom.addEventListener(mask, "touchcancel", onBackgroundTouchEnd, { - passive: true - }); - } - - self.clickMaskClose(); - }; - - return new TouchMenuLA(); + } }; -}); + + TouchMenuLA.prototype.open = function () { + this.animateToPosition(options.width); + currentPos = options.width; + this.isVisible = true; + options.target.classList.add('drawer-open'); + self.showMask(); + self.invoke(options.onChange); + }; + + TouchMenuLA.prototype.close = function () { + this.animateToPosition(0); + currentPos = 0; + self.isVisible = false; + options.target.classList.remove('drawer-open'); + self.hideMask(); + self.invoke(options.onChange); + }; + + TouchMenuLA.prototype.toggle = function () { + if (self.isVisible) { + self.close(); + } else { + self.open(); + } + }; + + var backgroundTouchStartX; + var backgroundTouchStartTime; + + TouchMenuLA.prototype.showMask = function () { + mask.classList.remove('hide'); + mask.classList.add('backdrop'); + }; + + TouchMenuLA.prototype.hideMask = function () { + mask.classList.add('hide'); + mask.classList.remove('backdrop'); + }; + + TouchMenuLA.prototype.invoke = function (fn) { + if (fn) { + fn.apply(self); + } + }; + + var _edgeSwipeEnabled; + + TouchMenuLA.prototype.setEdgeSwipeEnabled = function (enabled) { + if (!options.disableEdgeSwipe) { + if (browser.touch) { + if (enabled) { + if (!_edgeSwipeEnabled) { + _edgeSwipeEnabled = true; + dom.addEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, { + passive: true + }); + dom.addEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, { + passive: true + }); + dom.addEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, { + passive: true + }); + } + } else { + if (_edgeSwipeEnabled) { + _edgeSwipeEnabled = false; + dom.removeEventListener(edgeContainer, 'touchstart', onEdgeTouchStart, { + passive: true + }); + dom.removeEventListener(edgeContainer, 'touchend', onEdgeTouchEnd, { + passive: true + }); + dom.removeEventListener(edgeContainer, 'touchcancel', onEdgeTouchEnd, { + passive: true + }); + } + } + } + } + }; + + TouchMenuLA.prototype.initialize = function () { + options = Object.assign(defaults, options || {}); + + if (browser.edge) { + options.disableEdgeSwipe = true; + } + + self.initElements(); + + if (browser.touch) { + dom.addEventListener(options.target, 'touchstart', onMenuTouchStart, { + passive: true + }); + dom.addEventListener(options.target, 'touchmove', onMenuTouchMove, { + passive: true + }); + dom.addEventListener(options.target, 'touchend', onMenuTouchEnd, { + passive: true + }); + dom.addEventListener(options.target, 'touchcancel', onMenuTouchEnd, { + passive: true + }); + dom.addEventListener(mask, 'touchstart', onBackgroundTouchStart, { + passive: true + }); + dom.addEventListener(mask, 'touchmove', onBackgroundTouchMove, {}); + dom.addEventListener(mask, 'touchend', onBackgroundTouchEnd, { + passive: true + }); + dom.addEventListener(mask, 'touchcancel', onBackgroundTouchEnd, { + passive: true + }); + } + + self.clickMaskClose(); + }; + + return new TouchMenuLA(); +} diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index bbe01276ba..6149717647 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -806,6 +806,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' navDrawerScrollContainer.addEventListener('click', onMainDrawerClick); return new Promise(function (resolve, reject) { require(['navdrawer'], function (navdrawer) { + navdrawer = navdrawer.default || navdrawer; + navDrawerInstance = new navdrawer(getNavDrawerOptions()); if (!layoutManager.tv) { From f2e1d03ae98925a1788dbb577b608649e9004ee1 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 21:15:16 +0200 Subject: [PATCH 103/155] Migrate scroller to ES6 --- package.json | 1 + src/controllers/list.js | 2 +- src/libraries/scroller.js | 1714 ++++++++++++++++++------------------- 3 files changed, 838 insertions(+), 879 deletions(-) diff --git a/package.json b/package.json index 7b895bc367..7ed4cb8b9c 100644 --- a/package.json +++ b/package.json @@ -276,6 +276,7 @@ "src/elements/emby-textarea/emby-textarea.js", "src/elements/emby-toggle/emby-toggle.js", "src/libraries/navdrawer/navdrawer.js", + "src/libraries/scroller.js", "src/plugins/backdropScreensaver/plugin.js", "src/plugins/bookPlayer/plugin.js", "src/plugins/bookPlayer/tableOfContents.js", diff --git a/src/controllers/list.js b/src/controllers/list.js index afa0a60fa8..14c6768757 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -1,4 +1,4 @@ -define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'scroller', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, scroller, playbackManager, AlphaPicker) { +define(['globalize', 'listView', 'layoutManager', 'userSettings', 'focusManager', 'cardBuilder', 'loading', 'connectionManager', 'alphaNumericShortcuts', 'playbackManager', 'alphaPicker', 'emby-itemscontainer', 'emby-scroller'], function (globalize, listView, layoutManager, userSettings, focusManager, cardBuilder, loading, connectionManager, AlphaNumericShortcuts, playbackManager, AlphaPicker) { 'use strict'; playbackManager = playbackManager.default || playbackManager; diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index cc75dcdeef..03c6306511 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -1,928 +1,886 @@ -define(['browser', 'layoutManager', 'dom', 'focusManager', 'ResizeObserver', 'scrollStyles'], function (browser, layoutManager, dom, focusManager, ResizeObserver) { - 'use strict'; +/* Cleaning this file properly is not neecessary, since it's an outdated library + * and will be replaced soon by a Vue component. + */ - browser = browser.default || browser; - focusManager = focusManager.default || focusManager; + import browser from 'browser'; +import layoutManager from 'layoutManager'; +import dom from 'dom'; +import focusManager from 'focusManager'; +import ResizeObserver from 'ResizeObserver'; +import 'scrollStyles'; - /** +/** * Return type of the value. * * @param {Mixed} value * * @return {String} */ - function type(value) { - if (value == null) { - return String(value); - } - - if (typeof value === 'object' || typeof value === 'function') { - return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; - } - - return typeof value; +function type(value) { + if (value == null) { + return String(value); } - /** - * Disables an event it was triggered on and unbinds itself. - * - * @param {Event} event - * - * @return {Void} - */ - function disableOneEvent(event) { - /*jshint validthis:true */ - event.preventDefault(); - event.stopPropagation(); - this.removeEventListener(event.type, disableOneEvent); + if (typeof value === 'object' || typeof value === 'function') { + return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; } - /** - * Make sure that number is within the limits. - * - * @param {Number} number - * @param {Number} min - * @param {Number} max - * - * @return {Number} - */ - function within(number, min, max) { - return number < min ? min : number > max ? max : number; + return typeof value; +} + +/** + * Disables an event it was triggered on and unbinds itself. + * + * @param {Event} event + * + * @return {Void} + */ +function disableOneEvent(event) { + /*jshint validthis:true */ + event.preventDefault(); + event.stopPropagation(); + this.removeEventListener(event.type, disableOneEvent); +} + +/** + * Make sure that number is within the limits. + * + * @param {Number} number + * @param {Number} min + * @param {Number} max + * + * @return {Number} + */ +function within(number, min, max) { + return number < min ? min : number > max ? max : number; +} + +// Other global values +var dragMouseEvents = ['mousemove', 'mouseup']; +var dragTouchEvents = ['touchmove', 'touchend']; +var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); +var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA']; + +var scrollerFactory = function (frame, options) { + // Extend options + var o = Object.assign({}, { + slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. + horizontal: false, // Switch to horizontal mode. + + // Scrolling + mouseWheel: true, + scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling + + // Dragging + dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. + mouseDragging: 1, // Enable navigation by dragging the SLIDEE with mouse cursor. + touchDragging: 1, // Enable navigation by dragging the SLIDEE with touch events. + dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. + intervactive: null, // Selector for special interactive elements. + + // Mixed options + speed: 0 // Animations speed in milliseconds. 0 to disable animations. + + }, options); + + var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style; + + // native scroll is a must with touch input + // also use native scroll when scrolling vertically in desktop mode - excluding horizontal because the mouse wheel support is choppy at the moment + // in cases with firefox, if the smooth scroll api is supported then use that because their implementation is very good + if (options.allowNativeScroll === false) { + options.enableNativeScroll = false; + } else if (isSmoothScrollSupported && ((browser.firefox && !layoutManager.tv) || options.allowNativeSmoothScroll)) { + // native smooth scroll + options.enableNativeScroll = true; + } else if (options.requireAnimation && (browser.animate || browser.supportsCssAnimation())) { + // transform is the only way to guarantee animation + options.enableNativeScroll = false; + } else if (!layoutManager.tv || !browser.animate) { + options.enableNativeScroll = true; } - // Other global values - var dragMouseEvents = ['mousemove', 'mouseup']; - var dragTouchEvents = ['touchmove', 'touchend']; - var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); - var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA']; - var tmpArray = []; - var time; - - // Math shorthands - var abs = Math.abs; - var sqrt = Math.sqrt; - var pow = Math.pow; - var round = Math.round; - var max = Math.max; - var min = Math.min; - - var scrollerFactory = function (frame, options) { - - // Extend options - var o = Object.assign({}, { - slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. - horizontal: false, // Switch to horizontal mode. - - // Scrolling - mouseWheel: true, - scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling - - // Dragging - dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. - mouseDragging: 1, // Enable navigation by dragging the SLIDEE with mouse cursor. - touchDragging: 1, // Enable navigation by dragging the SLIDEE with touch events. - dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. - intervactive: null, // Selector for special interactive elements. - - // Mixed options - speed: 0 // Animations speed in milliseconds. 0 to disable animations. - - }, options); - - var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style; - - // native scroll is a must with touch input - // also use native scroll when scrolling vertically in desktop mode - excluding horizontal because the mouse wheel support is choppy at the moment - // in cases with firefox, if the smooth scroll api is supported then use that because their implementation is very good - if (options.allowNativeScroll === false) { - options.enableNativeScroll = false; - } else if (isSmoothScrollSupported && ((browser.firefox && !layoutManager.tv) || options.allowNativeSmoothScroll)) { - // native smooth scroll - options.enableNativeScroll = true; - } else if (options.requireAnimation && (browser.animate || browser.supportsCssAnimation())) { - - // transform is the only way to guarantee animation - options.enableNativeScroll = false; - } else if (!layoutManager.tv || !browser.animate) { - - options.enableNativeScroll = true; - } - - // Need this for the magic wheel. With the animated scroll the magic wheel will run off of the screen - if (browser.web0s) { - options.enableNativeScroll = true; - } - - // Private variables - var self = this; - self.options = o; - - // Frame - var slideeElement = o.slidee ? o.slidee : sibling(frame.firstChild)[0]; - self._pos = { - start: 0, - center: 0, - end: 0, - cur: 0, - dest: 0 - }; - - var transform = !options.enableNativeScroll; - - // Miscellaneous - var scrollSource = frame; - var dragSourceElement = o.dragSource ? o.dragSource : frame; - var dragging = { - released: 1 - }; - var scrolling = { - last: 0, - delta: 0, - resetTime: 200 - }; - - // Expose properties - self.initialized = 0; - self.slidee = slideeElement; - self.options = o; - self.dragging = dragging; - - var nativeScrollElement = frame; - - function sibling(n, elem) { - var matched = []; - - for (; n; n = n.nextSibling) { - if (n.nodeType === 1 && n !== elem) { - matched.push(n); - } - } - return matched; - } - - var requiresReflow = true; - - var frameSize = 0; - var slideeSize = 0; - function ensureSizeInfo() { - - if (requiresReflow) { - - requiresReflow = false; - - // Reset global variables - frameSize = o.horizontal ? (frame).offsetWidth : (frame).offsetHeight; - - slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']); - - // Set position limits & relativess - self._pos.end = max(slideeSize - frameSize, 0); - } - } - - /** - * Loading function. - * - * Populate arrays, set sizes, bind events, ... - * - * @param {Boolean} [isInit] Whether load is called from within self.init(). - * @return {Void} - */ - function load(isInit) { - - requiresReflow = true; - - if (!isInit) { - - ensureSizeInfo(); - - // Fix possible overflowing - var pos = self._pos; - self.slideTo(within(pos.dest, pos.start, pos.end)); - } - } - - function initFrameResizeObserver() { - - var observerOptions = {}; - - self.frameResizeObserver = new ResizeObserver(onResize, observerOptions); - - self.frameResizeObserver.observe(frame); - } - - self.reload = function () { - load(); - }; - - self.getScrollEventName = function () { - return transform ? 'scrollanimate' : 'scroll'; - }; - - self.getScrollSlider = function () { - return slideeElement; - }; - - self.getScrollFrame = function () { - return frame; - }; - - function nativeScrollTo(container, pos, immediate) { - - if (container.scroll) { - if (o.horizontal) { - - container.scroll({ - left: pos, - behavior: immediate ? 'instant' : 'smooth' - }); - } else { - - container.scroll({ - top: pos, - behavior: immediate ? 'instant' : 'smooth' - }); - } - } else if (!immediate && container.scrollTo) { - if (o.horizontal) { - container.scrollTo(Math.round(pos), 0); - } else { - container.scrollTo(0, Math.round(pos)); - } - } else { - if (o.horizontal) { - container.scrollLeft = Math.round(pos); - } else { - container.scrollTop = Math.round(pos); - } - } - } - - var lastAnimate; - - /** - * Animate to a position. - * - * @param {Int} newPos New position. - * @param {Bool} immediate Reposition immediately without an animation. - * - * @return {Void} - */ - self.slideTo = function (newPos, immediate, fullItemPos) { - - ensureSizeInfo(); - var pos = self._pos; - - newPos = within(newPos, pos.start, pos.end); - - if (!transform) { - - nativeScrollTo(nativeScrollElement, newPos, immediate); - return; - } - - // Update the animation object - var from = pos.cur; - immediate = immediate || dragging.init || !o.speed; - - var now = new Date().getTime(); - - if (o.autoImmediate) { - if (!immediate && (now - (lastAnimate || 0)) <= 50) { - immediate = true; - } - } - - if (!immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible) { - - return; - } - - // Start animation rendering - // NOTE the dependency was modified here to fix a scrollbutton issue - pos.dest = newPos; - renderAnimateWithTransform(from, newPos, immediate); - lastAnimate = now; - }; - - function setStyleProperty(elem, name, value, speed, resetTransition) { - - var style = elem.style; - - if (resetTransition || browser.edge) { - style.transition = 'none'; - void elem.offsetWidth; - } - - style.transition = 'transform ' + speed + 'ms ease-out'; - style[name] = value; - } - - function dispatchScrollEventIfNeeded() { - if (o.dispatchScrollEvent) { - frame.dispatchEvent(new CustomEvent(self.getScrollEventName(), { - bubbles: true, - cancelable: false - })); - } - } - - function renderAnimateWithTransform(fromPosition, toPosition, immediate) { - - var speed = o.speed; - - if (immediate) { - speed = o.immediateSpeed || 50; - } - - if (o.horizontal) { - setStyleProperty(slideeElement, 'transform', 'translateX(' + (-round(toPosition)) + 'px)', speed); - } else { - setStyleProperty(slideeElement, 'transform', 'translateY(' + (-round(toPosition)) + 'px)', speed); - } - self._pos.cur = toPosition; - - dispatchScrollEventIfNeeded(); - } - - function getBoundingClientRect(elem) { - - // Support: BlackBerry 5, iOS 3 (original iPhone) - // If we don't have gBCR, just use 0,0 rather than error - if (elem.getBoundingClientRect) { - return elem.getBoundingClientRect(); - } else { - return { top: 0, left: 0 }; - } - } - - /** - * Returns the position object. - * - * @param {Mixed} item - * - * @return {Object} - */ - self.getPos = function (item) { - - var scrollElement = transform ? slideeElement : nativeScrollElement; - var slideeOffset = getBoundingClientRect(scrollElement); - var itemOffset = getBoundingClientRect(item); - - var slideeStartPos = o.horizontal ? slideeOffset.left : slideeOffset.top; - var slideeEndPos = o.horizontal ? slideeOffset.right : slideeOffset.bottom; - - var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top; - - var size = o.horizontal ? itemOffset.width : itemOffset.height; - if (!size && size !== 0) { - size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight']; - } - - var centerOffset = o.centerOffset || 0; - - if (!transform) { - centerOffset = 0; - if (o.horizontal) { - offset += nativeScrollElement.scrollLeft; - } else { - offset += nativeScrollElement.scrollTop; - } - } - - ensureSizeInfo(); - - var currentStart = self._pos.cur; - var currentEnd = currentStart + frameSize; - - console.debug('offset:' + offset + ' currentStart:' + currentStart + ' currentEnd:' + currentEnd); - var isVisible = offset >= currentStart && (offset + size) <= currentEnd; - - return { - start: offset, - center: offset + centerOffset - (frameSize / 2) + (size / 2), - end: offset - frameSize + size, - size: size, - isVisible: isVisible - }; - }; - - self.getCenterPosition = function (item) { - - ensureSizeInfo(); - - var pos = self.getPos(item); - return within(pos.center, pos.start, pos.end); - }; - - function dragInitSlidee(event) { - var isTouch = event.type === 'touchstart'; - - // Ignore when already in progress, or interactive element in non-touch navivagion - if (dragging.init || !isTouch && isInteractive(event.target)) { - return; - } - - // SLIDEE dragging conditions - if (!(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { - return; - } - - if (!isTouch) { - // prevents native image dragging in Firefox - event.preventDefault(); - } - - // Reset dragging object - dragging.released = 0; - - // Properties used in dragHandler - dragging.init = 0; - dragging.source = event.target; - dragging.touch = isTouch; - var pointer = isTouch ? event.touches[0] : event; - dragging.initX = pointer.pageX; - dragging.initY = pointer.pageY; - dragging.initPos = self._pos.cur; - dragging.start = +new Date(); - dragging.time = 0; - dragging.path = 0; - dragging.delta = 0; - dragging.locked = 0; - dragging.pathToLock = isTouch ? 30 : 10; - - // Bind dragging events - if (transform) { - - if (isTouch) { - dragTouchEvents.forEach(function (eventName) { - dom.addEventListener(document, eventName, dragHandler, { - passive: true - }); - }); - } else { - dragMouseEvents.forEach(function (eventName) { - dom.addEventListener(document, eventName, dragHandler, { - passive: true - }); - }); - } - } - } - - /** - * Handler for dragging scrollbar handle or SLIDEE. - * - * @param {Event} event - * - * @return {Void} - */ - function dragHandler(event) { - dragging.released = event.type === 'mouseup' || event.type === 'touchend'; - var pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event; - dragging.pathX = pointer.pageX - dragging.initX; - dragging.pathY = pointer.pageY - dragging.initY; - dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)); - dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; - - if (!dragging.released && dragging.path < 1) { - return; - } - - // We haven't decided whether this is a drag or not... - if (!dragging.init) { - // If the drag path was very short, maybe it's not a drag? - if (dragging.path < o.dragThreshold) { - // If the pointer was released, the path will not become longer and it's - // definitely not a drag. If not released yet, decide on next iteration - return dragging.released ? dragEnd() : undefined; - } else { - // If dragging path is sufficiently long we can confidently start a drag - // if drag is in different direction than scroll, ignore it - if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) { - dragging.init = 1; - } else { - return dragEnd(); - } - } - } - - //event.preventDefault(); - - // Disable click on a source element, as it is unwelcome when dragging - if (!dragging.locked && dragging.path > dragging.pathToLock) { - dragging.locked = 1; - dragging.source.addEventListener('click', disableOneEvent); - } - - // Cancel dragging on release - if (dragging.released) { - dragEnd(); - } - - self.slideTo(round(dragging.initPos - dragging.delta)); - } - - /** - * Stops dragging and cleans up after it. - * - * @return {Void} - */ - function dragEnd() { - dragging.released = true; - - dragTouchEvents.forEach(function (eventName) { - dom.removeEventListener(document, eventName, dragHandler, { - passive: true - }); - }); - - dragMouseEvents.forEach(function (eventName) { - dom.removeEventListener(document, eventName, dragHandler, { - passive: true - }); - }); - - // Make sure that disableOneEvent is not active in next tick. - setTimeout(function () { - dragging.source.removeEventListener('click', disableOneEvent); - }); - - dragging.init = 0; - } - - /** - * Check whether element is interactive. - * - * @return {Boolean} - */ - function isInteractive(element) { - - while (element) { - - if (interactiveElements.indexOf(element.tagName) !== -1) { - return true; - } - - element = element.parentNode; - } - return false; - } - - /** - * Mouse wheel delta normalization. - * - * @param {Event} event - * - * @return {Int} - */ - function normalizeWheelDelta(event) { - // JELLYFIN MOD: Only use deltaX for horizontal scroll and remove IE8 support - scrolling.curDelta = o.horizontal ? event.deltaX : event.deltaY; - // END JELLYFIN MOD - - if (transform) { - scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; - } - return scrolling.curDelta; - } - - /** - * Mouse scrolling handler. - * - * @param {Event} event - * - * @return {Void} - */ - function scrollHandler(event) { - - ensureSizeInfo(); - var pos = self._pos; - // Ignore if there is no scrolling to be done - if (!o.scrollBy || pos.start === pos.end) { - return; - } - var delta = normalizeWheelDelta(event); - - if (transform) { - // Trap scrolling only when necessary and/or requested - if (delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { - //stopDefault(event, 1); - } - - self.slideBy(o.scrollBy * delta); - } else { - - if (isSmoothScrollSupported) { - delta *= 12; - } - - if (o.horizontal) { - nativeScrollElement.scrollLeft += delta; - } else { - nativeScrollElement.scrollTop += delta; - } - } - } - - /** - * Destroys instance and everything it created. - * - * @return {Void} - */ - self.destroy = function () { - - if (self.frameResizeObserver) { - self.frameResizeObserver.disconnect(); - self.frameResizeObserver = null; - } - - // Reset native FRAME element scroll - dom.removeEventListener(frame, 'scroll', resetScroll, { - passive: true - }); - - dom.removeEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: true - }); - - dom.removeEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { - passive: true - }); - - dom.removeEventListener(frame, 'click', onFrameClick, { - passive: true, - capture: true - }); - - dom.removeEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { - //passive: true - }); - - // Reset initialized status and return the instance - self.initialized = 0; - return self; - }; - - var contentRect = {}; - - function onResize(entries) { - - var entry = entries[0]; - - if (entry) { - - var newRect = entry.contentRect; - - // handle element being hidden - if (newRect.width === 0 || newRect.height === 0) { - return; - } - - if (newRect.width !== contentRect.width || newRect.height !== contentRect.height) { - - contentRect = newRect; - - load(false); - } - } - } - - function resetScroll() { - if (o.horizontal) { - this.scrollLeft = 0; - } else { - this.scrollTop = 0; - } - } - - function onFrameClick(e) { - if (e.which === 1) { - var focusableParent = focusManager.focusableParent(e.target); - if (focusableParent && focusableParent !== document.activeElement) { - focusableParent.focus(); - } - } - } - - self.getScrollPosition = function () { - - if (transform) { - return self._pos.cur; - } - - if (o.horizontal) { - return nativeScrollElement.scrollLeft; - } else { - return nativeScrollElement.scrollTop; - } - }; - - self.getScrollSize = function () { - - if (transform) { - return slideeSize; - } - - if (o.horizontal) { - return nativeScrollElement.scrollWidth; - } else { - return nativeScrollElement.scrollHeight; - } - }; - - /** - * Initialize. - * - * @return {Object} - */ - self.init = function () { - if (self.initialized) { - return; - } - - if (!transform) { - if (o.horizontal) { - if (layoutManager.desktop && !o.hideScrollbar) { - nativeScrollElement.classList.add('scrollX'); - } else { - nativeScrollElement.classList.add('scrollX'); - nativeScrollElement.classList.add('hiddenScrollX'); - - if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { - nativeScrollElement.classList.add('smoothScrollX'); - } - } - - if (o.forceHideScrollbars) { - nativeScrollElement.classList.add('hiddenScrollX-forced'); - } - } else { - if (layoutManager.desktop && !o.hideScrollbar) { - nativeScrollElement.classList.add('scrollY'); - } else { - nativeScrollElement.classList.add('scrollY'); - nativeScrollElement.classList.add('hiddenScrollY'); - - if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { - nativeScrollElement.classList.add('smoothScrollY'); - } - } - - if (o.forceHideScrollbars) { - nativeScrollElement.classList.add('hiddenScrollY-forced'); - } - } - } else { - frame.style.overflow = 'hidden'; - slideeElement.style['will-change'] = 'transform'; - slideeElement.style.transition = 'transform ' + o.speed + 'ms ease-out'; - - if (o.horizontal) { - slideeElement.classList.add('animatedScrollX'); - } else { - slideeElement.classList.add('animatedScrollY'); - } - } - - if (transform || layoutManager.tv) { - // This can prevent others from being able to listen to mouse events - dom.addEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { - //passive: true - }); - } - - initFrameResizeObserver(); - - if (transform) { - - dom.addEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { - passive: true - }); - - if (!o.horizontal) { - dom.addEventListener(frame, 'scroll', resetScroll, { - passive: true - }); - } - - if (o.mouseWheel) { - // Scrolling navigation - dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: true - }); - } - - } else if (o.horizontal) { - - // Don't bind to mouse events with vertical scroll since the mouse wheel can handle this natively - - if (o.mouseWheel) { - // Scrolling navigation - dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { - passive: true - }); - } - } - - dom.addEventListener(frame, 'click', onFrameClick, { - passive: true, - capture: true - }); - - // Mark instance as initialized - self.initialized = 1; - - // Load - load(true); - - // Return instance - return self; - }; + // Need this for the magic wheel. With the animated scroll the magic wheel will run off of the screen + if (browser.web0s) { + options.enableNativeScroll = true; + } + + // Private variables + var self = this; + self.options = o; + + // Frame + var slideeElement = o.slidee ? o.slidee : sibling(frame.firstChild)[0]; + self._pos = { + start: 0, + center: 0, + end: 0, + cur: 0, + dest: 0 }; + var transform = !options.enableNativeScroll; + + // Miscellaneous + var scrollSource = frame; + var dragSourceElement = o.dragSource ? o.dragSource : frame; + var dragging = { + released: 1 + }; + var scrolling = { + last: 0, + delta: 0, + resetTime: 200 + }; + + // Expose properties + self.initialized = 0; + self.slidee = slideeElement; + self.options = o; + self.dragging = dragging; + + var nativeScrollElement = frame; + + function sibling(n, elem) { + var matched = []; + + for (; n; n = n.nextSibling) { + if (n.nodeType === 1 && n !== elem) { + matched.push(n); + } + } + return matched; + } + + var requiresReflow = true; + + var frameSize = 0; + var slideeSize = 0; + function ensureSizeInfo() { + if (requiresReflow) { + requiresReflow = false; + + // Reset global variables + frameSize = o.horizontal ? (frame).offsetWidth : (frame).offsetHeight; + + slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']); + + // Set position limits & relativess + self._pos.end = Math.max(slideeSize - frameSize, 0); + } + } + /** - * Slide SLIDEE by amount of pixels. + * Loading function. * - * @param {Int} delta Pixels/Items. Positive means forward, negative means backward. - * @param {Bool} immediate Reposition immediately without an animation. + * Populate arrays, set sizes, bind events, ... * + * @param {Boolean} [isInit] Whether load is called from within self.init(). * @return {Void} */ - scrollerFactory.prototype.slideBy = function (delta, immediate) { - if (!delta) { + function load(isInit) { + requiresReflow = true; + + if (!isInit) { + ensureSizeInfo(); + + // Fix possible overflowing + var pos = self._pos; + self.slideTo(within(pos.dest, pos.start, pos.end)); + } + } + + function initFrameResizeObserver() { + var observerOptions = {}; + + self.frameResizeObserver = new ResizeObserver(onResize, observerOptions); + + self.frameResizeObserver.observe(frame); + } + + self.reload = function () { + load(); + }; + + self.getScrollEventName = function () { + return transform ? 'scrollanimate' : 'scroll'; + }; + + self.getScrollSlider = function () { + return slideeElement; + }; + + self.getScrollFrame = function () { + return frame; + }; + + function nativeScrollTo(container, pos, immediate) { + if (container.scroll) { + if (o.horizontal) { + container.scroll({ + left: pos, + behavior: immediate ? 'instant' : 'smooth' + }); + } else { + container.scroll({ + top: pos, + behavior: immediate ? 'instant' : 'smooth' + }); + } + } else if (!immediate && container.scrollTo) { + if (o.horizontal) { + container.scrollTo(Math.Math.round(pos), 0); + } else { + container.scrollTo(0, Math.Math.round(pos)); + } + } else { + if (o.horizontal) { + container.scrollLeft = Math.Math.round(pos); + } else { + container.scrollTop = Math.Math.round(pos); + } + } + } + + var lastAnimate; + + /** + * Animate to a position. + * + * @param {Int} newPos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideTo = function (newPos, immediate, fullItemPos) { + ensureSizeInfo(); + var pos = self._pos; + + newPos = within(newPos, pos.start, pos.end); + + if (!transform) { + nativeScrollTo(nativeScrollElement, newPos, immediate); return; } - this.slideTo(this._pos.dest + delta, immediate); - }; - /** - * Core method for handling `toLocation` methods. - * - * @param {String} location - * @param {Mixed} item - * @param {Bool} immediate - * - * @return {Void} - */ - scrollerFactory.prototype.to = function (location, item, immediate) { - // Optional arguments logic - if (type(item) === 'boolean') { - immediate = item; - item = undefined; - } + // Update the animation object + var from = pos.cur; + immediate = immediate || dragging.init || !o.speed; - if (item === undefined) { - this.slideTo(this._pos[location], immediate); - } else { - var itemPos = this.getPos(item); + var now = new Date().getTime(); - if (itemPos) { - this.slideTo(itemPos[location], immediate, itemPos); + if (o.autoImmediate) { + if (!immediate && (now - (lastAnimate || 0)) <= 50) { + immediate = true; } } + + if (!immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible) { + return; + } + + // Start animation rendering + // NOTE the dependency was modified here to fix a scrollbutton issue + pos.dest = newPos; + renderAnimateWithTransform(from, newPos, immediate); + lastAnimate = now; }; + function setStyleProperty(elem, name, value, speed, resetTransition) { + var style = elem.style; + + if (resetTransition || browser.edge) { + style.transition = 'none'; + void elem.offsetWidth; + } + + style.transition = 'transform ' + speed + 'ms ease-out'; + style[name] = value; + } + + function dispatchScrollEventIfNeeded() { + if (o.dispatchScrollEvent) { + frame.dispatchEvent(new CustomEvent(self.getScrollEventName(), { + bubbles: true, + cancelable: false + })); + } + } + + function renderAnimateWithTransform(fromPosition, toPosition, immediate) { + var speed = o.speed; + + if (immediate) { + speed = o.immediateSpeed || 50; + } + + if (o.horizontal) { + setStyleProperty(slideeElement, 'transform', 'translateX(' + (-Math.round(toPosition)) + 'px)', speed); + } else { + setStyleProperty(slideeElement, 'transform', 'translateY(' + (-Math.round(toPosition)) + 'px)', speed); + } + self._pos.cur = toPosition; + + dispatchScrollEventIfNeeded(); + } + + function getBoundingClientRect(elem) { + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + return elem.getBoundingClientRect(); + } else { + return { top: 0, left: 0 }; + } + } + /** - * Animate element or the whole SLIDEE to the start of the frame. + * Returns the position object. * - * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. - * @param {Bool} immediate Reposition immediately without an animation. + * @param {Mixed} item + * + * @return {Object} + */ + self.getPos = function (item) { + var scrollElement = transform ? slideeElement : nativeScrollElement; + var slideeOffset = getBoundingClientRect(scrollElement); + var itemOffset = getBoundingClientRect(item); + + var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top; + + var size = o.horizontal ? itemOffset.width : itemOffset.height; + if (!size && size !== 0) { + size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight']; + } + + var centerOffset = o.centerOffset || 0; + + if (!transform) { + centerOffset = 0; + if (o.horizontal) { + offset += nativeScrollElement.scrollLeft; + } else { + offset += nativeScrollElement.scrollTop; + } + } + + ensureSizeInfo(); + + var currentStart = self._pos.cur; + var currentEnd = currentStart + frameSize; + + console.debug('offset:' + offset + ' currentStart:' + currentStart + ' currentEnd:' + currentEnd); + var isVisible = offset >= currentStart && (offset + size) <= currentEnd; + + return { + start: offset, + center: offset + centerOffset - (frameSize / 2) + (size / 2), + end: offset - frameSize + size, + size: size, + isVisible: isVisible + }; + }; + + self.getCenterPosition = function (item) { + ensureSizeInfo(); + + var pos = self.getPos(item); + return within(pos.center, pos.start, pos.end); + }; + + function dragInitSlidee(event) { + var isTouch = event.type === 'touchstart'; + + // Ignore when already in progress, or interactive element in non-touch navivagion + if (dragging.init || !isTouch && isInteractive(event.target)) { + return; + } + + // SLIDEE dragging conditions + if (!(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { + return; + } + + if (!isTouch) { + // prevents native image dragging in Firefox + event.preventDefault(); + } + + // Reset dragging object + dragging.released = 0; + + // Properties used in dragHandler + dragging.init = 0; + dragging.source = event.target; + dragging.touch = isTouch; + var pointer = isTouch ? event.touches[0] : event; + dragging.initX = pointer.pageX; + dragging.initY = pointer.pageY; + dragging.initPos = self._pos.cur; + dragging.start = +new Date(); + dragging.time = 0; + dragging.path = 0; + dragging.delta = 0; + dragging.locked = 0; + dragging.pathToLock = isTouch ? 30 : 10; + + // Bind dragging events + if (transform) { + if (isTouch) { + dragTouchEvents.forEach(function (eventName) { + dom.addEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + } else { + dragMouseEvents.forEach(function (eventName) { + dom.addEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + } + } + } + + /** + * Handler for dragging scrollbar handle or SLIDEE. + * + * @param {Event} event * * @return {Void} */ - scrollerFactory.prototype.toStart = function (item, immediate) { - this.to('start', item, immediate); - }; + function dragHandler(event) { + dragging.released = event.type === 'mouseup' || event.type === 'touchend'; + var pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event; + dragging.pathX = pointer.pageX - dragging.initX; + dragging.pathY = pointer.pageY - dragging.initY; + dragging.path = Math.sqrt(Math.pow(dragging.pathX, 2) + Math.pow(dragging.pathY, 2)); + dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; + + if (!dragging.released && dragging.path < 1) { + return; + } + + // We haven't decided whether this is a drag or not... + if (!dragging.init) { + // If the drag path was very short, maybe it's not a drag? + if (dragging.path < o.dragThreshold) { + // If the pointer was released, the path will not become longer and it's + // definitely not a drag. If not released yet, decide on next iteration + return dragging.released ? dragEnd() : undefined; + } else { + // If dragging path is sufficiently long we can confidently start a drag + // if drag is in different direction than scroll, ignore it + if (o.horizontal ? Math.abs(dragging.pathX) > Math.abs(dragging.pathY) : Math.abs(dragging.pathX) < Math.abs(dragging.pathY)) { + dragging.init = 1; + } else { + return dragEnd(); + } + } + } + + //event.preventDefault(); + + // Disable click on a source element, as it is unwelcome when dragging + if (!dragging.locked && dragging.path > dragging.pathToLock) { + dragging.locked = 1; + dragging.source.addEventListener('click', disableOneEvent); + } + + // Cancel dragging on release + if (dragging.released) { + dragEnd(); + } + + self.slideTo(Math.round(dragging.initPos - dragging.delta)); + } /** - * Animate element or the whole SLIDEE to the end of the frame. - * - * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. - * @param {Bool} immediate Reposition immediately without an animation. + * Stops dragging and cleans up after it. * * @return {Void} */ - scrollerFactory.prototype.toEnd = function (item, immediate) { - this.to('end', item, immediate); - }; + function dragEnd() { + dragging.released = true; + + dragTouchEvents.forEach(function (eventName) { + dom.removeEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + + dragMouseEvents.forEach(function (eventName) { + dom.removeEventListener(document, eventName, dragHandler, { + passive: true + }); + }); + + // Make sure that disableOneEvent is not active in next tick. + setTimeout(function () { + dragging.source.removeEventListener('click', disableOneEvent); + }); + + dragging.init = 0; + } /** - * Animate element or the whole SLIDEE to the center of the frame. + * Check whether element is interactive. * - * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. - * @param {Bool} immediate Reposition immediately without an animation. + * @return {Boolean} + */ + function isInteractive(element) { + while (element) { + if (interactiveElements.indexOf(element.tagName) !== -1) { + return true; + } + + element = element.parentNode; + } + return false; + } + + /** + * Mouse wheel delta normalization. + * + * @param {Event} event + * + * @return {Int} + */ + function normalizeWheelDelta(event) { + // JELLYFIN MOD: Only use deltaX for horizontal scroll and remove IE8 support + scrolling.curDelta = o.horizontal ? event.deltaX : event.deltaY; + // END JELLYFIN MOD + + if (transform) { + scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; + } + return scrolling.curDelta; + } + + /** + * Mouse scrolling handler. + * + * @param {Event} event * * @return {Void} */ - scrollerFactory.prototype.toCenter = function (item, immediate) { - this.to('center', item, immediate); + function scrollHandler(event) { + ensureSizeInfo(); + var pos = self._pos; + // Ignore if there is no scrolling to be done + if (!o.scrollBy || pos.start === pos.end) { + return; + } + var delta = normalizeWheelDelta(event); + + if (transform) { + // Trap scrolling only when necessary and/or requested + if (delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { + //stopDefault(event, 1); + } + + self.slideBy(o.scrollBy * delta); + } else { + if (isSmoothScrollSupported) { + delta *= 12; + } + + if (o.horizontal) { + nativeScrollElement.scrollLeft += delta; + } else { + nativeScrollElement.scrollTop += delta; + } + } + } + + /** + * Destroys instance and everything it created. + * + * @return {Void} + */ + self.destroy = function () { + if (self.frameResizeObserver) { + self.frameResizeObserver.disconnect(); + self.frameResizeObserver = null; + } + + // Reset native FRAME element scroll + dom.removeEventListener(frame, 'scroll', resetScroll, { + passive: true + }); + + dom.removeEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + + dom.removeEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { + passive: true + }); + + dom.removeEventListener(frame, 'click', onFrameClick, { + passive: true, + capture: true + }); + + dom.removeEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { + //passive: true + }); + + // Reset initialized status and return the instance + self.initialized = 0; + return self; }; - scrollerFactory.create = function (frame, options) { - var instance = new scrollerFactory(frame, options); - return Promise.resolve(instance); + var contentRect = {}; + + function onResize(entries) { + var entry = entries[0]; + + if (entry) { + var newRect = entry.contentRect; + + // handle element being hidden + if (newRect.width === 0 || newRect.height === 0) { + return; + } + + if (newRect.width !== contentRect.width || newRect.height !== contentRect.height) { + contentRect = newRect; + + load(false); + } + } + } + + function resetScroll() { + if (o.horizontal) { + this.scrollLeft = 0; + } else { + this.scrollTop = 0; + } + } + + function onFrameClick(e) { + if (e.which === 1) { + var focusableParent = focusManager.focusableParent(e.target); + if (focusableParent && focusableParent !== document.activeElement) { + focusableParent.focus(); + } + } + } + + self.getScrollPosition = function () { + if (transform) { + return self._pos.cur; + } + + if (o.horizontal) { + return nativeScrollElement.scrollLeft; + } else { + return nativeScrollElement.scrollTop; + } }; - return scrollerFactory; -}); + self.getScrollSize = function () { + if (transform) { + return slideeSize; + } + + if (o.horizontal) { + return nativeScrollElement.scrollWidth; + } else { + return nativeScrollElement.scrollHeight; + } + }; + + /** + * Initialize. + * + * @return {Object} + */ + self.init = function () { + if (self.initialized) { + return; + } + + if (!transform) { + if (o.horizontal) { + if (layoutManager.desktop && !o.hideScrollbar) { + nativeScrollElement.classList.add('scrollX'); + } else { + nativeScrollElement.classList.add('scrollX'); + nativeScrollElement.classList.add('hiddenScrollX'); + + if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { + nativeScrollElement.classList.add('smoothScrollX'); + } + } + + if (o.forceHideScrollbars) { + nativeScrollElement.classList.add('hiddenScrollX-forced'); + } + } else { + if (layoutManager.desktop && !o.hideScrollbar) { + nativeScrollElement.classList.add('scrollY'); + } else { + nativeScrollElement.classList.add('scrollY'); + nativeScrollElement.classList.add('hiddenScrollY'); + + if (layoutManager.tv && o.allowNativeSmoothScroll !== false) { + nativeScrollElement.classList.add('smoothScrollY'); + } + } + + if (o.forceHideScrollbars) { + nativeScrollElement.classList.add('hiddenScrollY-forced'); + } + } + } else { + frame.style.overflow = 'hidden'; + slideeElement.style['will-change'] = 'transform'; + slideeElement.style.transition = 'transform ' + o.speed + 'ms ease-out'; + + if (o.horizontal) { + slideeElement.classList.add('animatedScrollX'); + } else { + slideeElement.classList.add('animatedScrollY'); + } + } + + if (transform || layoutManager.tv) { + // This can prevent others from being able to listen to mouse events + dom.addEventListener(dragSourceElement, 'mousedown', dragInitSlidee, { + //passive: true + }); + } + + initFrameResizeObserver(); + + if (transform) { + dom.addEventListener(dragSourceElement, 'touchstart', dragInitSlidee, { + passive: true + }); + + if (!o.horizontal) { + dom.addEventListener(frame, 'scroll', resetScroll, { + passive: true + }); + } + + if (o.mouseWheel) { + // Scrolling navigation + dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + } + } else if (o.horizontal) { + // Don't bind to mouse events with vertical scroll since the mouse wheel can handle this natively + + if (o.mouseWheel) { + // Scrolling navigation + dom.addEventListener(scrollSource, wheelEvent, scrollHandler, { + passive: true + }); + } + } + + dom.addEventListener(frame, 'click', onFrameClick, { + passive: true, + capture: true + }); + + // Mark instance as initialized + self.initialized = 1; + + // Load + load(true); + + // Return instance + return self; + }; +}; + +/** + * Slide SLIDEE by amount of pixels. + * + * @param {Int} delta Pixels/Items. Positive means forward, negative means backward. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ +scrollerFactory.prototype.slideBy = function (delta, immediate) { + if (!delta) { + return; + } + this.slideTo(this._pos.dest + delta, immediate); +}; + +/** + * Core method for handling `toLocation` methods. + * + * @param {String} location + * @param {Mixed} item + * @param {Bool} immediate + * + * @return {Void} + */ +scrollerFactory.prototype.to = function (location, item, immediate) { + // Optional arguments logic + if (type(item) === 'boolean') { + immediate = item; + item = undefined; + } + + if (item === undefined) { + this.slideTo(this._pos[location], immediate); + } else { + var itemPos = this.getPos(item); + + if (itemPos) { + this.slideTo(itemPos[location], immediate, itemPos); + } + } +}; + +/** + * Animate element or the whole SLIDEE to the start of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ +scrollerFactory.prototype.toStart = function (item, immediate) { + this.to('start', item, immediate); +}; + +/** + * Animate element or the whole SLIDEE to the end of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ +scrollerFactory.prototype.toEnd = function (item, immediate) { + this.to('end', item, immediate); +}; + +/** + * Animate element or the whole SLIDEE to the center of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ +scrollerFactory.prototype.toCenter = function (item, immediate) { + this.to('center', item, immediate); +}; + +scrollerFactory.create = function (frame, options) { + var instance = new scrollerFactory(frame, options); + return Promise.resolve(instance); +}; + +export default scrollerFactory; From e7d6bc288956e3643c1c204bd413cf48725ccd17 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 22:25:14 +0200 Subject: [PATCH 104/155] Don't math twice --- src/libraries/scroller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index 03c6306511..ada13403bf 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -229,15 +229,15 @@ var scrollerFactory = function (frame, options) { } } else if (!immediate && container.scrollTo) { if (o.horizontal) { - container.scrollTo(Math.Math.round(pos), 0); + container.scrollTo(Math.round(pos), 0); } else { - container.scrollTo(0, Math.Math.round(pos)); + container.scrollTo(0, Math.round(pos)); } } else { if (o.horizontal) { - container.scrollLeft = Math.Math.round(pos); + container.scrollLeft = Math.round(pos); } else { - container.scrollTop = Math.Math.round(pos); + container.scrollTop = Math.round(pos); } } } From 161488616f5dc789f54c8965f955a08d5402001a Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 22:28:52 +0200 Subject: [PATCH 105/155] Fix suggestions --- src/scripts/scrollHelper.js | 59 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/scripts/scrollHelper.js b/src/scripts/scrollHelper.js index 020888a2e3..1ce30768af 100644 --- a/src/scripts/scrollHelper.js +++ b/src/scripts/scrollHelper.js @@ -98,40 +98,43 @@ function centerOnFocus(e, scrollSlider, horizontal) { function centerOnFocusHorizontal(e) { centerOnFocus(e, this, true); } + function centerOnFocusVertical(e) { centerOnFocus(e, this, false); } -export default { - getPosition: getPosition, - centerFocus: { - on: function (element, horizontal) { - if (horizontal) { - dom.addEventListener(element, 'focus', centerOnFocusHorizontal, { - capture: true, - passive: true - }); - } else { - dom.addEventListener(element, 'focus', centerOnFocusVertical, { - capture: true, - passive: true - }); - } - }, - off: function (element, horizontal) { - if (horizontal) { - dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, { - capture: true, - passive: true - }); - } else { - dom.removeEventListener(element, 'focus', centerOnFocusVertical, { - capture: true, - passive: true - }); - } +export const centerFocus = { + on: function (element, horizontal) { + if (horizontal) { + dom.addEventListener(element, 'focus', centerOnFocusHorizontal, { + capture: true, + passive: true + }); + } else { + dom.addEventListener(element, 'focus', centerOnFocusVertical, { + capture: true, + passive: true + }); } }, + off: function (element, horizontal) { + if (horizontal) { + dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, { + capture: true, + passive: true + }); + } else { + dom.removeEventListener(element, 'focus', centerOnFocusVertical, { + capture: true, + passive: true + }); + } + } +} + +export default { + getPosition: getPosition, + centerFocus: centerFocus, toCenter: toCenter, toStart: toStart }; From 453079fe068250dca9d4964ad15692e474b7b8af Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 22:48:26 +0200 Subject: [PATCH 106/155] Fix require --- src/components/guide/guide.js | 3 ++- src/scripts/scrollHelper.js | 4 +--- src/scripts/serverNotifications.js | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index 50eb55f834..05fa2b608d 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -4,7 +4,8 @@ define(['require', 'inputManager', 'browser', 'globalize', 'connectionManager', playbackManager = playbackManager.default || playbackManager; browser = browser.default || browser; loading = loading.default || loading; - focusManager = focusManager.default || focusManager;scrollHelper = scrollHelper.default || scrollHelper; + focusManager = focusManager.default || focusManager; + scrollHelper = scrollHelper.default || scrollHelper; serverNotifications = serverNotifications.default || serverNotifications; function showViewSettings(instance) { diff --git a/src/scripts/scrollHelper.js b/src/scripts/scrollHelper.js index 1ce30768af..b867123683 100644 --- a/src/scripts/scrollHelper.js +++ b/src/scripts/scrollHelper.js @@ -3,7 +3,6 @@ import dom from 'dom'; import 'scrollStyles'; function getBoundingClientRect(elem) { - // Support: BlackBerry 5, iOS 3 (original iPhone) // If we don't have gBCR, just use 0,0 rather than error if (elem.getBoundingClientRect) { @@ -14,7 +13,6 @@ function getBoundingClientRect(elem) { } export function getPosition(scrollContainer, item, horizontal) { - const slideeOffset = getBoundingClientRect(scrollContainer); const itemOffset = getBoundingClientRect(item); @@ -130,7 +128,7 @@ export const centerFocus = { }); } } -} +}; export default { getPosition: getPosition, diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index 83af40c4e6..2345cf327a 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -15,12 +15,12 @@ function notifyApp() { function displayMessage(cmd) { const args = cmd.Arguments; if (args.TimeoutMs) { - require(['toast'], function (toast) { + import('toast').then((toast) => { toast({ title: args.Header, text: args.Text }); }); } else { - require(['alert'], function (alert) { - alert.default({ title: args.Header, text: args.Text }); + import('alert').then((alert) => { + alert({ title: args.Header, text: args.Text }); }); } } From 4e860a57286b517867b795ef2b0da7e1b538e743 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2020 21:12:22 +0000 Subject: [PATCH 107/155] Bump css-loader from 4.2.0 to 4.2.1 Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v4.2.0...v4.2.1) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index bf3ac42f97..146ee850d5 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "babel-loader": "^8.0.6", "browser-sync": "^2.26.12", "copy-webpack-plugin": "^5.1.1", - "css-loader": "^4.2.0", + "css-loader": "^4.2.1", "cssnano": "^4.1.10", "del": "^5.1.0", "eslint": "^7.6.0", diff --git a/yarn.lock b/yarn.lock index 3ec7f21487..ad55e16176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3020,10 +3020,10 @@ css-has-pseudo@^0.10.0: postcss "^7.0.6" postcss-selector-parser "^5.0.0-rc.4" -css-loader@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.0.tgz#b57efb92ac8f0cd85bf92d89df9634ef1f51b8bf" - integrity sha512-ko7a9b0iFpWtk9eSI/C8IICvZeGtYnjxYjw45rJprokXj/+kBd/siX4vAIBq9Uij8Jubc4jL1EvSnTjCEwaHSw== +css-loader@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.2.1.tgz#9f48fd7eae1219d629a3f085ba9a9102ca1141a7" + integrity sha512-MoqmF1if7Z0pZIEXA4ZF9PgtCXxWbfzfJM+3p+OYfhcrwcqhaCRb74DSnfzRl7e024xEiCRn5hCvfUbTf2sgFA== dependencies: camelcase "^6.0.0" cssesc "^3.0.0" From 232ccb6b9fce607adf6bb4319f0bd6abd40ad48f Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Thu, 6 Aug 2020 23:26:51 +0200 Subject: [PATCH 108/155] Update src/scripts/serverNotifications.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/scripts/serverNotifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index 2345cf327a..c84377c880 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -15,7 +15,7 @@ function notifyApp() { function displayMessage(cmd) { const args = cmd.Arguments; if (args.TimeoutMs) { - import('toast').then((toast) => { + import('toast').then(({default: toast}) => { toast({ title: args.Header, text: args.Text }); }); } else { From 915a423997914e09b020dc8cd8dfc15614518886 Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Thu, 6 Aug 2020 23:26:59 +0200 Subject: [PATCH 109/155] Update src/scripts/serverNotifications.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/scripts/serverNotifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/serverNotifications.js b/src/scripts/serverNotifications.js index c84377c880..2566d148f6 100644 --- a/src/scripts/serverNotifications.js +++ b/src/scripts/serverNotifications.js @@ -19,7 +19,7 @@ function displayMessage(cmd) { toast({ title: args.Header, text: args.Text }); }); } else { - import('alert').then((alert) => { + import('alert').then(({default: alert}) => { alert({ title: args.Header, text: args.Text }); }); } From 878ab482cb43545ba7b91670baec1a8ba49a1759 Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Thu, 6 Aug 2020 23:27:26 +0200 Subject: [PATCH 110/155] Update src/libraries/scroller.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/libraries/scroller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index ada13403bf..c460ec5b2c 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -2,7 +2,7 @@ * and will be replaced soon by a Vue component. */ - import browser from 'browser'; +import browser from 'browser'; import layoutManager from 'layoutManager'; import dom from 'dom'; import focusManager from 'focusManager'; From 7658cd5900ddc2c46c1edb449a72eaf3394fdb19 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 07:41:49 +0100 Subject: [PATCH 111/155] Migration of logoScreenSaver to ES6 module --- package.json | 1 + src/plugins/logoScreensaver/plugin.js | 308 +++++++++++++------------- 2 files changed, 155 insertions(+), 154 deletions(-) diff --git a/package.json b/package.json index 8038de5f90..42e236d670 100644 --- a/package.json +++ b/package.json @@ -159,6 +159,7 @@ "src/components/sanatizefilename.js", "src/components/scrollManager.js", "src/plugins/htmlVideoPlayer/plugin.js", + "src/plugins/logoScreensaver/plugin.js", "src/components/search/searchfields.js", "src/components/search/searchresults.js", "src/components/settingshelper.js", diff --git a/src/plugins/logoScreensaver/plugin.js b/src/plugins/logoScreensaver/plugin.js index bdd1d34e79..61b8f8a6d6 100644 --- a/src/plugins/logoScreensaver/plugin.js +++ b/src/plugins/logoScreensaver/plugin.js @@ -1,165 +1,165 @@ -define(['pluginManager'], function (pluginManager) { - return function () { - var self = this; +import pluginManager from 'pluginManager'; - self.name = 'Logo ScreenSaver'; - self.type = 'screensaver'; - self.id = 'logoscreensaver'; - self.supportsAnonymous = true; +export default function () { + const self = this; - var interval; + self.name = 'Logo ScreenSaver'; + self.type = 'screensaver'; + self.id = 'logoscreensaver'; + self.supportsAnonymous = true; - function animate() { - var animations = [ + let interval; - bounceInLeft, - bounceInRight, - swing, - tada, - wobble, - rotateIn, - rotateOut - ]; + function animate() { + const animations = [ - var elem = document.querySelector('.logoScreenSaverImage'); + bounceInLeft, + bounceInRight, + swing, + tada, + wobble, + rotateIn, + rotateOut + ]; - if (elem && elem.animate) { - var random = getRandomInt(0, animations.length - 1); + const elem = document.querySelector('.logoScreenSaverImage'); - animations[random](elem, 1); + if (elem && elem.animate) { + const random = getRandomInt(0, animations.length - 1); + + animations[random](elem, 1); + } + } + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function bounceInLeft(elem, iterations) { + const keyframes = [ + { transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 }, + { transform: 'translate3d(25px, 0, 0)', opacity: '1', offset: 0.6 }, + { transform: 'translate3d(-100px, 0, 0)', offset: 0.75 }, + { transform: 'translate3d(5px, 0, 0)', offset: 0.9 }, + { transform: 'none', opacity: '1', offset: 1 }]; + const timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; + return elem.animate(keyframes, timing); + } + + function bounceInRight(elem, iterations) { + const keyframes = [ + { transform: 'translate3d(3000px, 0, 0)', opacity: '0', offset: 0 }, + { transform: 'translate3d(-25px, 0, 0)', opacity: '1', offset: 0.6 }, + { transform: 'translate3d(100px, 0, 0)', offset: 0.75 }, + { transform: 'translate3d(-5px, 0, 0)', offset: 0.9 }, + { transform: 'none', opacity: '1', offset: 1 }]; + const timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; + return elem.animate(keyframes, timing); + } + + function swing(elem, iterations) { + const keyframes = [ + { transform: 'translate(0%)', offset: 0 }, + { transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 }, + { transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 }, + { transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 }, + { transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 }, + { transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }]; + const timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function tada(elem, iterations) { + const keyframes = [ + { transform: 'scale3d(1, 1, 1)', offset: 0 }, + { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 }, + { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 }, + { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 }, + { transform: 'scale3d(1, 1, 1)', offset: 1 }]; + const timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function wobble(elem, iterations) { + const keyframes = [ + { transform: 'translate(0%)', offset: 0 }, + { transform: 'translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)', offset: 0.15 }, + { transform: 'translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)', offset: 0.45 }, + { transform: 'translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)', offset: 0.6 }, + { transform: 'translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)', offset: 0.75 }, + { transform: 'translateX(0%)', offset: 1 }]; + const timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateIn(elem, iterations) { + const keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 }, + { transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }]; + const timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function rotateOut(elem, iterations) { + const keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 }, + { transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }]; + const timing = { duration: 900, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function fadeOut(elem, iterations) { + const keyframes = [ + { opacity: '1', offset: 0 }, + { opacity: '0', offset: 1 }]; + const timing = { duration: 400, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function stopInterval() { + if (interval) { + clearInterval(interval); + interval = null; + } + } + + self.show = function () { + import('css!' + pluginManager.mapPath(self, 'style.css')).then(() => { + let elem = document.querySelector('.logoScreenSaver'); + + if (!elem) { + elem = document.createElement('div'); + elem.classList.add('logoScreenSaver'); + document.body.appendChild(elem); + + elem.innerHTML = ''; } - } - function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - function bounceInLeft(elem, iterations) { - var keyframes = [ - { transform: 'translate3d(-3000px, 0, 0)', opacity: '0', offset: 0 }, - { transform: 'translate3d(25px, 0, 0)', opacity: '1', offset: 0.6 }, - { transform: 'translate3d(-100px, 0, 0)', offset: 0.75 }, - { transform: 'translate3d(5px, 0, 0)', offset: 0.9 }, - { transform: 'none', opacity: '1', offset: 1 }]; - var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; - return elem.animate(keyframes, timing); - } - - function bounceInRight(elem, iterations) { - var keyframes = [ - { transform: 'translate3d(3000px, 0, 0)', opacity: '0', offset: 0 }, - { transform: 'translate3d(-25px, 0, 0)', opacity: '1', offset: 0.6 }, - { transform: 'translate3d(100px, 0, 0)', offset: 0.75 }, - { transform: 'translate3d(-5px, 0, 0)', offset: 0.9 }, - { transform: 'none', opacity: '1', offset: 1 }]; - var timing = { duration: 900, iterations: iterations, easing: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)' }; - return elem.animate(keyframes, timing); - } - - function swing(elem, iterations) { - var keyframes = [ - { transform: 'translate(0%)', offset: 0 }, - { transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 }, - { transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 }, - { transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 }, - { transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 }, - { transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function tada(elem, iterations) { - var keyframes = [ - { transform: 'scale3d(1, 1, 1)', offset: 0 }, - { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 }, - { transform: 'scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 }, - { transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 }, - { transform: 'scale3d(1, 1, 1)', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function wobble(elem, iterations) { - var keyframes = [ - { transform: 'translate(0%)', offset: 0 }, - { transform: 'translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg)', offset: 0.15 }, - { transform: 'translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg)', offset: 0.45 }, - { transform: 'translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg)', offset: 0.6 }, - { transform: 'translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg)', offset: 0.75 }, - { transform: 'translateX(0%)', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function rotateIn(elem, iterations) { - var keyframes = [{ transform: 'rotate3d(0, 0, 1, -200deg)', opacity: '0', transformOrigin: 'center', offset: 0 }, - { transform: 'none', opacity: '1', transformOrigin: 'center', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function rotateOut(elem, iterations) { - var keyframes = [{ transform: 'none', opacity: '1', transformOrigin: 'center', offset: 0 }, - { transform: 'rotate3d(0, 0, 1, 200deg)', opacity: '0', transformOrigin: 'center', offset: 1 }]; - var timing = { duration: 900, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function fadeOut(elem, iterations) { - var keyframes = [ - { opacity: '1', offset: 0 }, - { opacity: '0', offset: 1 }]; - var timing = { duration: 400, iterations: iterations }; - return elem.animate(keyframes, timing); - } - - function stopInterval() { - if (interval) { - clearInterval(interval); - interval = null; - } - } - - self.show = function () { - require(['css!' + pluginManager.mapPath(self, 'style.css')], function () { - var elem = document.querySelector('.logoScreenSaver'); - - if (!elem) { - elem = document.createElement('div'); - elem.classList.add('logoScreenSaver'); - document.body.appendChild(elem); - - elem.innerHTML = ''; - } - - stopInterval(); - interval = setInterval(animate, 3000); - }); - }; - - self.hide = function () { stopInterval(); - - var elem = document.querySelector('.logoScreenSaver'); - - if (elem) { - var onAnimationFinish = function () { - elem.parentNode.removeChild(elem); - }; - - if (elem.animate) { - var animation = fadeOut(elem, 1); - animation.onfinish = onAnimationFinish; - } else { - onAnimationFinish(); - } - } - }; + interval = setInterval(animate, 3000); + }); }; -}); + + self.hide = function () { + stopInterval(); + + const elem = document.querySelector('.logoScreenSaver'); + + if (elem) { + const onAnimationFinish = function () { + elem.parentNode.removeChild(elem); + }; + + if (elem.animate) { + const animation = fadeOut(elem, 1); + animation.onfinish = onAnimationFinish; + } else { + onAnimationFinish(); + } + } + }; +} From 6eface92394a99b4705d27bcf403cf8f2369443b Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 07:42:09 +0100 Subject: [PATCH 112/155] Migration of playAccessValidation to ES6 module --- package.json | 1 + src/plugins/playAccessValidation/plugin.js | 37 +++++++++------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 42e236d670..00381203b1 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "src/components/scrollManager.js", "src/plugins/htmlVideoPlayer/plugin.js", "src/plugins/logoScreensaver/plugin.js", + "src/plugins/playAccessValidation/plugin.js", "src/components/search/searchfields.js", "src/components/search/searchresults.js", "src/components/settingshelper.js", diff --git a/src/plugins/playAccessValidation/plugin.js b/src/plugins/playAccessValidation/plugin.js index 5148d2b821..a9fbeda9a9 100644 --- a/src/plugins/playAccessValidation/plugin.js +++ b/src/plugins/playAccessValidation/plugin.js @@ -1,33 +1,26 @@ -define(['connectionManager', 'globalize'], function (connectionManager, globalize) { - 'use strict'; +import connectionManager from 'connectionManager'; +import globalize from 'globalize'; - function getRequirePromise(deps) { - return new Promise(function (resolve, reject) { - require(deps, resolve); - }); - } +function showErrorMessage() { + return import('alert').then(({default: alert}) => { + return alert(globalize.translate('MessagePlayAccessRestricted')); + }); +} - function showErrorMessage() { - return getRequirePromise(['alert']).then(function (alert) { - return alert(globalize.translate('MessagePlayAccessRestricted')).then(function () { - return Promise.reject(); - }); - }); - } - - function PlayAccessValidation() { +class PlayAccessValidation { + constructor() { this.name = 'Playback validation'; this.type = 'preplayintercept'; this.id = 'playaccessvalidation'; this.order = -2; } - PlayAccessValidation.prototype.intercept = function (options) { - var item = options.item; + intercept(options) { + const item = options.item; if (!item) { return Promise.resolve(); } - var serverId = item.ServerId; + const serverId = item.ServerId; if (!serverId) { return Promise.resolve(); } @@ -44,7 +37,7 @@ define(['connectionManager', 'globalize'], function (connectionManager, globaliz return showErrorMessage(); }); - }; + } +} - return PlayAccessValidation; -}); +export default PlayAccessValidation; From 0b786a5205b8f587ad1f9fede76a0bd1855a02fa Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 08:13:51 +0100 Subject: [PATCH 113/155] Fix Linting --- src/libraries/screensavermanager.js | 36 +++++++++++++---------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/libraries/screensavermanager.js b/src/libraries/screensavermanager.js index 557b31e0f4..ca35d38a98 100644 --- a/src/libraries/screensavermanager.js +++ b/src/libraries/screensavermanager.js @@ -1,5 +1,5 @@ -define(["events", "playbackManager", "pluginManager", "inputManager", "connectionManager", "userSettings"], function (events, playbackManager, pluginManager, inputManager, connectionManager, userSettings) { - "use strict"; +define(['events', 'playbackManager', 'pluginManager', 'inputManager', 'connectionManager', 'userSettings'], function (events, playbackManager, pluginManager, inputManager, connectionManager, userSettings) { + 'use strict'; playbackManager = playbackManager.default || playbackManager; @@ -15,23 +15,22 @@ define(["events", "playbackManager", "pluginManager", "inputManager", "connectio return new Date().getTime() - lastFunctionalEvent; } - events.on(playbackManager, "playbackstop", function (e, stopInfo) { + events.on(playbackManager, 'playbackstop', function (e, stopInfo) { var state = stopInfo.state; - if (state.NowPlayingItem && state.NowPlayingItem.MediaType == "Video") { + if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { lastFunctionalEvent = new Date().getTime(); } }); function getScreensaverPlugin(isLoggedIn) { - var option; try { - option = userSettings.get("screensaver", false); + option = userSettings.get('screensaver', false); } catch (err) { - option = isLoggedIn ? "backdropscreensaver" : "logoscreensaver"; + option = isLoggedIn ? 'backdropscreensaver' : 'logoscreensaver'; } - var plugins = pluginManager.ofType("screensaver"); + var plugins = pluginManager.ofType('screensaver'); for (var i = 0, length = plugins.length; i < length; i++) { var plugin = plugins[i]; @@ -45,42 +44,40 @@ define(["events", "playbackManager", "pluginManager", "inputManager", "connectio } function ScreenSaverManager() { - var self = this; var activeScreenSaver; function showScreenSaver(screensaver) { - if (activeScreenSaver) { - throw new Error("An existing screensaver is already active."); + throw new Error('An existing screensaver is already active.'); } - console.debug("Showing screensaver " + screensaver.name); + console.debug('Showing screensaver ' + screensaver.name); screensaver.show(); activeScreenSaver = screensaver; if (screensaver.hideOnClick !== false) { - window.addEventListener("click", hide, true); + window.addEventListener('click', hide, true); } if (screensaver.hideOnMouse !== false) { - window.addEventListener("mousemove", hide, true); + window.addEventListener('mousemove', hide, true); } if (screensaver.hideOnKey !== false) { - window.addEventListener("keydown", hide, true); + window.addEventListener('keydown', hide, true); } } function hide() { if (activeScreenSaver) { - console.debug("Hiding screensaver"); + console.debug('Hiding screensaver'); activeScreenSaver.hide(); activeScreenSaver = null; } - window.removeEventListener("click", hide, true); - window.removeEventListener("mousemove", hide, true); - window.removeEventListener("keydown", hide, true); + window.removeEventListener('click', hide, true); + window.removeEventListener('mousemove', hide, true); + window.removeEventListener('keydown', hide, true); } self.isShowing = function () { @@ -107,7 +104,6 @@ define(["events", "playbackManager", "pluginManager", "inputManager", "connectio }; function onInterval() { - if (self.isShowing()) { return; } From 9f46f21b7f522ca5d6bc9be39c56cc44bbe51227 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 08:29:07 +0100 Subject: [PATCH 114/155] Migration of chromecastPlayer to ES6 module --- package.json | 1 + src/plugins/chromecastPlayer/plugin.js | 761 +++++++++++++------------ 2 files changed, 382 insertions(+), 380 deletions(-) diff --git a/package.json b/package.json index 1d7cf770d3..33be2cac8a 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/plugins/chromecastPlayer/plugin.js", "src/plugins/htmlVideoPlayer/plugin.js", "src/components/search/searchfields.js", "src/components/search/searchresults.js", diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index 2741a6f0e3..88c460c444 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -1,66 +1,71 @@ -define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', 'globalize', 'events', 'require', 'castSenderApiLoader'], function (appSettings, userSettings, playbackManager, connectionManager, globalize, events, require, castSenderApiLoader) { - 'use strict'; +import appSettings from 'appSettings'; +import * as userSettings from 'userSettings'; +import playbackManager from 'playbackManager'; +import connectionManager from 'connectionManager'; +import globalize from 'globalize'; +import events from 'events'; +import castSenderApiLoader from 'castSenderApiLoader'; - playbackManager = playbackManager.default || playbackManager; +// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js - // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js - var currentResolve; - var currentReject; +let currentResolve; +let currentReject; - var PlayerName = 'Google Cast'; +const PlayerName = 'Google Cast'; - function sendConnectionResult(isOk) { - var resolve = currentResolve; - var reject = currentReject; +function sendConnectionResult(isOk) { + const resolve = currentResolve; + const reject = currentReject; - currentResolve = null; - currentReject = null; + currentResolve = null; + currentReject = null; - if (isOk) { - if (resolve) { - resolve(); - } + if (isOk) { + if (resolve) { + resolve(); + } + } else { + if (reject) { + reject(); } else { - if (reject) { - reject(); - } else { - playbackManager.removeActivePlayer(PlayerName); - } + playbackManager.removeActivePlayer(PlayerName); } } +} - /** - * Constants of states for Chromecast device - **/ - var DEVICE_STATE = { - 'IDLE': 0, - 'ACTIVE': 1, - 'WARNING': 2, - 'ERROR': 3 - }; +/** + * Constants of states for Chromecast device + **/ +const DEVICE_STATE = { + 'IDLE': 0, + 'ACTIVE': 1, + 'WARNING': 2, + 'ERROR': 3 +}; - /** - * Constants of states for CastPlayer - **/ - var PLAYER_STATE = { - 'IDLE': 'IDLE', - 'LOADING': 'LOADING', - 'LOADED': 'LOADED', - 'PLAYING': 'PLAYING', - 'PAUSED': 'PAUSED', - 'STOPPED': 'STOPPED', - 'SEEKING': 'SEEKING', - 'ERROR': 'ERROR' - }; +/** + * Constants of states for CastPlayer + **/ +const PLAYER_STATE = { + 'IDLE': 'IDLE', + 'LOADING': 'LOADING', + 'LOADED': 'LOADED', + 'PLAYING': 'PLAYING', + 'PAUSED': 'PAUSED', + 'STOPPED': 'STOPPED', + 'SEEKING': 'SEEKING', + 'ERROR': 'ERROR' +}; - // production version registered with google - // replace this value if you want to test changes on another instance - var applicationStable = 'F007D354'; - var applicationUnstable = '6F511C87'; +// production version registered with google +// replace this value if you want to test changes on another instance +const applicationStable = 'F007D354'; +const applicationUnstable = '6F511C87'; - var messageNamespace = 'urn:x-cast:com.connectsdk'; +const messageNamespace = 'urn:x-cast:com.connectsdk'; - var CastPlayer = function () { +class CastPlayer { + constructor() { /* device variables */ // @type {DEVICE_STATE} A state for device this.deviceState = DEVICE_STATE.IDLE; @@ -81,7 +86,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this); this.initializeCastPlayer(); - }; + } /** * Initialize Cast media player @@ -89,8 +94,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * invoked once the API has finished initialization. The sessionListener and * receiverListener may be invoked at any time afterwards, and possibly more than once. */ - CastPlayer.prototype.initializeCastPlayer = function () { - var chrome = window.chrome; + initializeCastPlayer() { + const chrome = window.chrome; if (!chrome) { return; } @@ -100,35 +105,35 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' return; } - var applicationID = applicationStable; + let applicationID = applicationStable; if (userSettings.chromecastVersion() === 'unstable') { applicationID = applicationUnstable; } // request session - var sessionRequest = new chrome.cast.SessionRequest(applicationID); - var apiConfig = new chrome.cast.ApiConfig(sessionRequest, + const sessionRequest = new chrome.cast.SessionRequest(applicationID); + const apiConfig = new chrome.cast.ApiConfig(sessionRequest, this.sessionListener.bind(this), this.receiverListener.bind(this)); console.debug('chromecast.initialize'); chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler); - }; + } /** * Callback function for init success */ - CastPlayer.prototype.onInitSuccess = function () { + onInitSuccess() { this.isInitialized = true; console.debug('chromecast init success'); - }; + } /** * Generic error callback function */ - CastPlayer.prototype.onError = function () { + onError() { console.debug('chromecast error'); - }; + } /** * @param {!Object} e A new session @@ -137,7 +142,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' * join existing session and occur in Cast mode and media * status gets synced up with current media of the session */ - CastPlayer.prototype.sessionListener = function (e) { + sessionListener(e) { this.session = e; if (this.session) { if (this.session.media[0]) { @@ -146,24 +151,15 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.onSessionConnected(e); } - }; - - function alertText(text, title) { - require(['alert'], function (alert) { - alert.default({ - text: text, - title: title - }); - }); } - CastPlayer.prototype.messageListener = function (namespace, message) { + messageListener(namespace, message) { if (typeof (message) === 'string') { message = JSON.parse(message); } if (message.type === 'playbackerror') { - var errorCode = message.data; + const errorCode = message.data; setTimeout(function () { alertText(globalize.translate('MessagePlaybackError' + errorCode), globalize.translate('HeaderPlaybackError')); }, 300); @@ -174,14 +170,14 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } else if (message.type) { events.trigger(this, message.type, [message.data]); } - }; + } /** * @param {string} e Receiver availability * This indicates availability of receivers but * does not provide a list of device IDs */ - CastPlayer.prototype.receiverListener = function (e) { + receiverListener(e) { if (e === 'available') { console.debug('chromecast receiver found'); this.hasReceivers = true; @@ -189,12 +185,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' console.debug('chromecast receiver list empty'); this.hasReceivers = false; } - }; + } /** * session update listener */ - CastPlayer.prototype.sessionUpdateListener = function (isAlive) { + sessionUpdateListener(isAlive) { if (isAlive) { console.debug('sessionUpdateListener: already alive'); } else { @@ -209,28 +205,28 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' sendConnectionResult(false); } - }; + } /** * Requests that a receiver application session be created or joined. By default, the SessionRequest * passed to the API at initialization time is used; this may be overridden by passing a different * session request in opt_sessionRequest. */ - CastPlayer.prototype.launchApp = function () { + launchApp() { console.debug('chromecast launching app...'); chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); - }; + } /** * Callback function for request session success * @param {Object} e A chrome.cast.Session object */ - CastPlayer.prototype.onRequestSessionSuccess = function (e) { + onRequestSessionSuccess(e) { console.debug('chromecast session success: ' + e.sessionId); this.onSessionConnected(e); - }; + } - CastPlayer.prototype.onSessionConnected = function (session) { + onSessionConnected(session) { this.session = session; this.deviceState = DEVICE_STATE.ACTIVE; @@ -246,46 +242,38 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' options: {}, command: 'Identify' }); - }; - - function onVolumeUpKeyDown() { - playbackManager.volumeUp(); - } - - function onVolumeDownKeyDown() { - playbackManager.volumeDown(); } /** * session update listener */ - CastPlayer.prototype.sessionMediaListener = function (e) { + sessionMediaListener(e) { this.currentMediaSession = e; this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); - }; + } /** * Callback function for launch error */ - CastPlayer.prototype.onLaunchError = function () { + onLaunchError() { console.debug('chromecast launch error'); this.deviceState = DEVICE_STATE.ERROR; sendConnectionResult(false); - }; + } /** * Stops the running receiver application associated with the session. */ - CastPlayer.prototype.stopApp = function () { + stopApp() { if (this.session) { this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'), this.errorHandler); } - }; + } /** * Callback function for stop app success */ - CastPlayer.prototype.onStopAppSuccess = function (message) { + onStopAppSuccess(message) { console.debug(message); this.deviceState = DEVICE_STATE.IDLE; @@ -294,13 +282,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false); this.currentMediaSession = null; - }; + } /** * Loads media into a running receiver application * @param {Number} mediaIndex An index number to indicate current media content */ - CastPlayer.prototype.loadMedia = function (options, command) { + loadMedia(options, command) { if (!this.session) { console.debug('no session'); return Promise.reject(); @@ -322,20 +310,20 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' options: options, command: command }); - }; + } - CastPlayer.prototype.sendMessage = function (message) { - var player = this; + sendMessage(message) { + const player = this; - var receiverName = null; + let receiverName = null; - var session = player.session; + const session = player.session; if (session && session.receiver && session.receiver.friendlyName) { receiverName = session.receiver.friendlyName; } - var apiClient; + let apiClient; if (message.options && message.options.ServerId) { apiClient = connectionManager.getApiClient(message.options.ServerId); } else if (message.options && message.options.items && message.options.items.length) { @@ -354,7 +342,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' receiverName: receiverName }); - var bitrateSetting = appSettings.maxChromecastBitrate(); + const bitrateSetting = appSettings.maxChromecastBitrate(); if (bitrateSetting) { message.maxBitrate = bitrateSetting; } @@ -365,31 +353,31 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } return new Promise(function (resolve, reject) { - require(['./chromecastHelper'], function (chromecastHelper) { + import('./chromecastHelper').then(({ default: chromecastHelper }) => { chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) { message.serverAddress = serverAddress; player.sendMessageInternal(message).then(resolve, reject); }, reject); }); }); - }; + } - CastPlayer.prototype.sendMessageInternal = function (message) { + sendMessageInternal(message) { message = JSON.stringify(message); this.session.sendMessage(messageNamespace, message, this.onPlayCommandSuccess.bind(this), this.errorHandler); return Promise.resolve(); - }; + } - CastPlayer.prototype.onPlayCommandSuccess = function () { + onPlayCommandSuccess() { console.debug('Message was sent to receiver ok.'); - }; + } /** * Callback function for loadMedia success * @param {Object} mediaSession A new media object. */ - CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { + onMediaDiscovered(how, mediaSession) { console.debug('chromecast new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')'); this.currentMediaSession = mediaSession; @@ -402,24 +390,24 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); - }; + } /** * Callback function for media status update from receiver * @param {!Boolean} e true/false */ - CastPlayer.prototype.onMediaStatusUpdate = function (e) { + onMediaStatusUpdate(e) { console.debug('chromecast updating media: ' + e); if (e === false) { this.castPlayerState = PLAYER_STATE.IDLE; } - }; + } /** * Set media volume in Cast mode * @param {Boolean} mute A boolean */ - CastPlayer.prototype.setReceiverVolume = function (mute, vol) { + setReceiverVolume(mute, vol) { if (!this.currentMediaSession) { console.debug('this.currentMediaSession is null'); return; @@ -434,142 +422,161 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' this.mediaCommandSuccessCallback.bind(this), this.errorHandler); } - }; + } /** * Mute CC */ - CastPlayer.prototype.mute = function () { + mute() { this.setReceiverVolume(true); - }; + } /** * Callback function for media command success */ - CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) { + mediaCommandSuccessCallback(info, e) { console.debug(info); - }; + } +} - function normalizeImages(state) { - if (state && state.NowPlayingItem) { - var item = state.NowPlayingItem; +function alertText(text, title) { + import('alert').then(({default: alert}) => { + alert({ + text: text, + title: title + }); + }); +} - if (!item.ImageTags || !item.ImageTags.Primary) { - if (item.PrimaryImageTag) { - item.ImageTags = item.ImageTags || {}; - item.ImageTags.Primary = item.PrimaryImageTag; - } - } - if (item.BackdropImageTag && item.BackdropItemId === item.Id) { - item.BackdropImageTags = [item.BackdropImageTag]; - } - if (item.BackdropImageTag && item.BackdropItemId !== item.Id) { - item.ParentBackdropImageTags = [item.BackdropImageTag]; - item.ParentBackdropItemId = item.BackdropItemId; +function onVolumeUpKeyDown() { + playbackManager.volumeUp(); +} + +function onVolumeDownKeyDown() { + playbackManager.volumeDown(); +} + +function normalizeImages(state) { + if (state && state.NowPlayingItem) { + const item = state.NowPlayingItem; + + if (!item.ImageTags || !item.ImageTags.Primary) { + if (item.PrimaryImageTag) { + item.ImageTags = item.ImageTags || {}; + item.ImageTags.Primary = item.PrimaryImageTag; } } + if (item.BackdropImageTag && item.BackdropItemId === item.Id) { + item.BackdropImageTags = [item.BackdropImageTag]; + } + if (item.BackdropImageTag && item.BackdropItemId !== item.Id) { + item.ParentBackdropImageTags = [item.BackdropImageTag]; + item.ParentBackdropItemId = item.BackdropItemId; + } } +} - function getItemsForPlayback(apiClient, query) { - var userId = apiClient.getCurrentUserId(); +function getItemsForPlayback(apiClient, query) { + const userId = apiClient.getCurrentUserId(); - if (query.Ids && query.Ids.split(',').length === 1) { - return apiClient.getItem(userId, query.Ids.split(',')).then(function (item) { - return { - Items: [item], - TotalRecordCount: 1 - }; - }); + if (query.Ids && query.Ids.split(',').length === 1) { + return apiClient.getItem(userId, query.Ids.split(',')).then(function (item) { + return { + Items: [item], + TotalRecordCount: 1 + }; + }); + } else { + query.Limit = query.Limit || 100; + query.ExcludeLocationTypes = 'Virtual'; + query.EnableTotalRecordCount = false; + + return apiClient.getItems(userId, query); + } +} + +function bindEventForRelay(instance, eventName) { + events.on(instance._castPlayer, eventName, function (e, data) { + console.debug('cc: ' + eventName); + const state = instance.getPlayerStateInternal(data); + + events.trigger(instance, eventName, [state]); + }); +} + +function initializeChromecast() { + const instance = this; + instance._castPlayer = new CastPlayer(); + + // To allow the native android app to override + document.dispatchEvent(new CustomEvent('chromecastloaded', { + detail: { + player: instance + } + })); + + events.on(instance._castPlayer, 'connect', function (e) { + if (currentResolve) { + sendConnectionResult(true); } else { - query.Limit = query.Limit || 100; - query.ExcludeLocationTypes = 'Virtual'; - query.EnableTotalRecordCount = false; - - return apiClient.getItems(userId, query); + playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()); } - } - function bindEventForRelay(instance, eventName) { - events.on(instance._castPlayer, eventName, function (e, data) { - console.debug('cc: ' + eventName); - var state = instance.getPlayerStateInternal(data); + console.debug('cc: connect'); + // Reset this so that statechange will fire + instance.lastPlayerData = null; + }); - events.trigger(instance, eventName, [state]); - }); - } + events.on(instance._castPlayer, 'playbackstart', function (e, data) { + console.debug('cc: playbackstart'); - function initializeChromecast() { - var instance = this; - instance._castPlayer = new CastPlayer(); + instance._castPlayer.initializeCastPlayer(); - // To allow the native android app to override - document.dispatchEvent(new CustomEvent('chromecastloaded', { - detail: { - player: instance - } - })); + const state = instance.getPlayerStateInternal(data); + events.trigger(instance, 'playbackstart', [state]); + }); - events.on(instance._castPlayer, 'connect', function (e) { - if (currentResolve) { - sendConnectionResult(true); - } else { - playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()); - } + events.on(instance._castPlayer, 'playbackstop', function (e, data) { + console.debug('cc: playbackstop'); + let state = instance.getPlayerStateInternal(data); - console.debug('cc: connect'); - // Reset this so that statechange will fire - instance.lastPlayerData = null; - }); + events.trigger(instance, 'playbackstop', [state]); - events.on(instance._castPlayer, 'playbackstart', function (e, data) { - console.debug('cc: playbackstart'); + state = instance.lastPlayerData.PlayState || {}; + const volume = state.VolumeLevel || 0.5; + const mute = state.IsMuted || false; - instance._castPlayer.initializeCastPlayer(); + // Reset this so the next query doesn't make it appear like content is playing. + instance.lastPlayerData = {}; + instance.lastPlayerData.PlayState = {}; + instance.lastPlayerData.PlayState.VolumeLevel = volume; + instance.lastPlayerData.PlayState.IsMuted = mute; + }); - var state = instance.getPlayerStateInternal(data); - events.trigger(instance, 'playbackstart', [state]); - }); + events.on(instance._castPlayer, 'playbackprogress', function (e, data) { + console.debug('cc: positionchange'); + const state = instance.getPlayerStateInternal(data); - events.on(instance._castPlayer, 'playbackstop', function (e, data) { - console.debug('cc: playbackstop'); - var state = instance.getPlayerStateInternal(data); + events.trigger(instance, 'timeupdate', [state]); + }); - events.trigger(instance, 'playbackstop', [state]); + bindEventForRelay(instance, 'timeupdate'); + bindEventForRelay(instance, 'pause'); + bindEventForRelay(instance, 'unpause'); + bindEventForRelay(instance, 'volumechange'); + bindEventForRelay(instance, 'repeatmodechange'); + bindEventForRelay(instance, 'shufflequeuemodechange'); - state = instance.lastPlayerData.PlayState || {}; - var volume = state.VolumeLevel || 0.5; - var mute = state.IsMuted || false; + events.on(instance._castPlayer, 'playstatechange', function (e, data) { + console.debug('cc: playstatechange'); + const state = instance.getPlayerStateInternal(data); - // Reset this so the next query doesn't make it appear like content is playing. - instance.lastPlayerData = {}; - instance.lastPlayerData.PlayState = {}; - instance.lastPlayerData.PlayState.VolumeLevel = volume; - instance.lastPlayerData.PlayState.IsMuted = mute; - }); + events.trigger(instance, 'pause', [state]); + }); +} - events.on(instance._castPlayer, 'playbackprogress', function (e, data) { - console.debug('cc: positionchange'); - var state = instance.getPlayerStateInternal(data); - - events.trigger(instance, 'timeupdate', [state]); - }); - - bindEventForRelay(instance, 'timeupdate'); - bindEventForRelay(instance, 'pause'); - bindEventForRelay(instance, 'unpause'); - bindEventForRelay(instance, 'volumechange'); - bindEventForRelay(instance, 'repeatmodechange'); - bindEventForRelay(instance, 'shufflequeuemodechange'); - - events.on(instance._castPlayer, 'playstatechange', function (e, data) { - console.debug('cc: playstatechange'); - var state = instance.getPlayerStateInternal(data); - - events.trigger(instance, 'pause', [state]); - }); - } - - function ChromecastPlayer() { +class ChromecastPlayer { + constructor() { // playbackManager needs this this.name = PlayerName; this.type = 'mediaplayer'; @@ -580,8 +587,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' castSenderApiLoader.load().then(initializeChromecast.bind(this)); } - ChromecastPlayer.prototype.tryPair = function (target) { - var castPlayer = this._castPlayer; + tryPair(target) { + const castPlayer = this._castPlayer; if (castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized) { return new Promise(function (resolve, reject) { @@ -595,23 +602,23 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' return Promise.reject(); } - }; + } - ChromecastPlayer.prototype.getTargets = function () { - var targets = []; + getTargets() { + const targets = []; if (this._castPlayer && this._castPlayer.hasReceivers) { targets.push(this.getCurrentTargetInfo()); } return Promise.resolve(targets); - }; + } // This is a privately used method - ChromecastPlayer.prototype.getCurrentTargetInfo = function () { - var appName = null; + getCurrentTargetInfo() { + let appName = null; - var castPlayer = this._castPlayer; + const castPlayer = this._castPlayer; if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) { appName = castPlayer.session.receiver.friendlyName; @@ -642,10 +649,10 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' 'PlayTrailers' ] }; - }; + } - ChromecastPlayer.prototype.getPlayerStateInternal = function (data) { - var triggerStateChange = false; + getPlayerStateInternal(data) { + let triggerStateChange = false; if (data && !this.lastPlayerData) { triggerStateChange = true; } @@ -662,12 +669,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } return data; - }; + } - ChromecastPlayer.prototype.playWithCommand = function (options, command) { + playWithCommand(options, command) { if (!options.items) { - var apiClient = connectionManager.getApiClient(options.serverId); - var instance = this; + const apiClient = connectionManager.getApiClient(options.serverId); + const instance = this; return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function (item) { options.items = [item]; @@ -683,9 +690,9 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' } return this._castPlayer.loadMedia(options, command); - }; + } - ChromecastPlayer.prototype.seek = function (position) { + seek(position) { position = parseInt(position); position = position / 10000000; @@ -696,55 +703,55 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' }, command: 'Seek' }); - }; + } - ChromecastPlayer.prototype.setAudioStreamIndex = function (index) { + setAudioStreamIndex(index) { this._castPlayer.sendMessage({ options: { index: index }, command: 'SetAudioStreamIndex' }); - }; + } - ChromecastPlayer.prototype.setSubtitleStreamIndex = function (index) { + setSubtitleStreamIndex(index) { this._castPlayer.sendMessage({ options: { index: index }, command: 'SetSubtitleStreamIndex' }); - }; + } - ChromecastPlayer.prototype.setMaxStreamingBitrate = function (options) { + setMaxStreamingBitrate(options) { this._castPlayer.sendMessage({ options: options, command: 'SetMaxStreamingBitrate' }); - }; + } - ChromecastPlayer.prototype.isFullscreen = function () { - var state = this.lastPlayerData || {}; + isFullscreen() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.IsFullscreen; - }; + } - ChromecastPlayer.prototype.nextTrack = function () { + nextTrack() { this._castPlayer.sendMessage({ options: {}, command: 'NextTrack' }); - }; + } - ChromecastPlayer.prototype.previousTrack = function () { + previousTrack() { this._castPlayer.sendMessage({ options: {}, command: 'PreviousTrack' }); - }; + } - ChromecastPlayer.prototype.volumeDown = function () { - var vol = this._castPlayer.session.receiver.volume.level; + volumeDown() { + let vol = this._castPlayer.session.receiver.volume.level; if (vol == null) { vol = 0.5; } @@ -752,20 +759,20 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' vol = Math.max(vol, 0); this._castPlayer.session.setReceiverVolumeLevel(vol); - }; + } - ChromecastPlayer.prototype.endSession = function () { - var instance = this; + endSession() { + const instance = this; this.stop().then(function () { setTimeout(function () { instance._castPlayer.stopApp(); }, 1000); }); - }; + } - ChromecastPlayer.prototype.volumeUp = function () { - var vol = this._castPlayer.session.receiver.volume.level; + volumeUp() { + let vol = this._castPlayer.session.receiver.volume.level; if (vol == null) { vol = 0.5; } @@ -773,53 +780,53 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' vol = Math.min(vol, 1); this._castPlayer.session.setReceiverVolumeLevel(vol); - }; + } - ChromecastPlayer.prototype.setVolume = function (vol) { + setVolume(vol) { vol = Math.min(vol, 100); vol = Math.max(vol, 0); vol = vol / 100; this._castPlayer.session.setReceiverVolumeLevel(vol); - }; + } - ChromecastPlayer.prototype.unpause = function () { + unpause() { this._castPlayer.sendMessage({ options: {}, command: 'Unpause' }); - }; + } - ChromecastPlayer.prototype.playPause = function () { + playPause() { this._castPlayer.sendMessage({ options: {}, command: 'PlayPause' }); - }; + } - ChromecastPlayer.prototype.pause = function () { + pause() { this._castPlayer.sendMessage({ options: {}, command: 'Pause' }); - }; + } - ChromecastPlayer.prototype.stop = function () { + stop() { return this._castPlayer.sendMessage({ options: {}, command: 'Stop' }); - }; + } - ChromecastPlayer.prototype.displayContent = function (options) { + displayContent(options) { this._castPlayer.sendMessage({ options: options, command: 'DisplayContent' }); - }; + } - ChromecastPlayer.prototype.setMute = function (isMuted) { - var castPlayer = this._castPlayer; + setMute(isMuted) { + const castPlayer = this._castPlayer; if (isMuted) { castPlayer.sendMessage({ @@ -832,21 +839,21 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' command: 'Unmute' }); } - }; + } - ChromecastPlayer.prototype.getRepeatMode = function () { - var state = this.lastPlayerData || {}; + getRepeatMode() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.RepeatMode; - }; + } - ChromecastPlayer.prototype.getQueueShuffleMode = function () { - var state = this.lastPlayerData || {}; + getQueueShuffleMode() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.ShuffleMode; - }; + } - ChromecastPlayer.prototype.playTrailers = function (item) { + playTrailers(item) { this._castPlayer.sendMessage({ options: { ItemId: item.Id, @@ -854,177 +861,173 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' }, command: 'PlayTrailers' }); - }; + } - ChromecastPlayer.prototype.setRepeatMode = function (mode) { + setRepeatMode(mode) { this._castPlayer.sendMessage({ options: { RepeatMode: mode }, command: 'SetRepeatMode' }); - }; + } - ChromecastPlayer.prototype.setQueueShuffleMode = function (value) { + setQueueShuffleMode(value) { this._castPlayer.sendMessage({ options: { ShuffleMode: value }, command: 'SetShuffleQueue' }); - }; + } - ChromecastPlayer.prototype.toggleMute = function () { + toggleMute() { this._castPlayer.sendMessage({ options: {}, command: 'ToggleMute' }); - }; + } - ChromecastPlayer.prototype.audioTracks = function () { - var state = this.lastPlayerData || {}; + audioTracks() { + let state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; - var streams = state.MediaStreams || []; + const streams = state.MediaStreams || []; return streams.filter(function (s) { return s.Type === 'Audio'; }); - }; + } - ChromecastPlayer.prototype.getAudioStreamIndex = function () { - var state = this.lastPlayerData || {}; + getAudioStreamIndex() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.AudioStreamIndex; - }; + } - ChromecastPlayer.prototype.subtitleTracks = function () { - var state = this.lastPlayerData || {}; + subtitleTracks() { + let state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; - var streams = state.MediaStreams || []; + const streams = state.MediaStreams || []; return streams.filter(function (s) { return s.Type === 'Subtitle'; }); - }; + } - ChromecastPlayer.prototype.getSubtitleStreamIndex = function () { - var state = this.lastPlayerData || {}; + getSubtitleStreamIndex() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.SubtitleStreamIndex; - }; + } - ChromecastPlayer.prototype.getMaxStreamingBitrate = function () { - var state = this.lastPlayerData || {}; + getMaxStreamingBitrate() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.MaxStreamingBitrate; - }; + } - ChromecastPlayer.prototype.getVolume = function () { - var state = this.lastPlayerData || {}; + getVolume() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.VolumeLevel == null ? 100 : state.VolumeLevel; - }; + } - ChromecastPlayer.prototype.isPlaying = function () { - var state = this.lastPlayerData || {}; + isPlaying() { + const state = this.lastPlayerData || {}; return state.NowPlayingItem != null; - }; + } - ChromecastPlayer.prototype.isPlayingVideo = function () { - var state = this.lastPlayerData || {}; + isPlayingVideo() { + let state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.MediaType === 'Video'; - }; + } - ChromecastPlayer.prototype.isPlayingAudio = function () { - var state = this.lastPlayerData || {}; + isPlayingAudio() { + let state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.MediaType === 'Audio'; - }; + } - ChromecastPlayer.prototype.currentTime = function (val) { + currentTime(val) { if (val != null) { return this.seek(val); } - var state = this.lastPlayerData || {}; + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.PositionTicks; - }; + } - ChromecastPlayer.prototype.duration = function () { - var state = this.lastPlayerData || {}; + duration() { + let state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.RunTimeTicks; - }; + } - ChromecastPlayer.prototype.getBufferedRanges = function () { - var state = this.lastPlayerData || {}; + getBufferedRanges() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.BufferedRanges || []; - }; + } - ChromecastPlayer.prototype.paused = function () { - var state = this.lastPlayerData || {}; + paused() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.IsPaused; - }; + } - ChromecastPlayer.prototype.isMuted = function () { - var state = this.lastPlayerData || {}; + isMuted() { + let state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.IsMuted; - }; + } - ChromecastPlayer.prototype.shuffle = function (item) { - var apiClient = connectionManager.getApiClient(item.ServerId); - var userId = apiClient.getCurrentUserId(); + shuffle(item) { + const apiClient = connectionManager.getApiClient(item.ServerId); + const userId = apiClient.getCurrentUserId(); - var instance = this; + const instance = this; apiClient.getItem(userId, item.Id).then(function (item) { instance.playWithCommand({ - items: [item] - }, 'Shuffle'); }); - }; + } - ChromecastPlayer.prototype.instantMix = function (item) { - var apiClient = connectionManager.getApiClient(item.ServerId); - var userId = apiClient.getCurrentUserId(); + instantMix(item) { + const apiClient = connectionManager.getApiClient(item.ServerId); + const userId = apiClient.getCurrentUserId(); - var instance = this; + const instance = this; apiClient.getItem(userId, item.Id).then(function (item) { instance.playWithCommand({ - items: [item] - }, 'InstantMix'); }); - }; + } - ChromecastPlayer.prototype.canPlayMediaType = function (mediaType) { + canPlayMediaType(mediaType) { mediaType = (mediaType || '').toLowerCase(); return mediaType === 'audio' || mediaType === 'video'; - }; + } - ChromecastPlayer.prototype.canQueueMediaType = function (mediaType) { + canQueueMediaType(mediaType) { return this.canPlayMediaType(mediaType); - }; + } - ChromecastPlayer.prototype.queue = function (options) { + queue(options) { this.playWithCommand(options, 'PlayLast'); - }; + } - ChromecastPlayer.prototype.queueNext = function (options) { + queueNext(options) { this.playWithCommand(options, 'PlayNext'); - }; + } - ChromecastPlayer.prototype.play = function (options) { + play(options) { if (options.items) { return this.playWithCommand(options, 'PlayNow'); } else { @@ -1032,50 +1035,48 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', ' throw new Error('serverId required!'); } - var instance = this; - var apiClient = connectionManager.getApiClient(options.serverId); + const instance = this; + const apiClient = connectionManager.getApiClient(options.serverId); return getItemsForPlayback(apiClient, { - Ids: options.ids.join(',') - }).then(function (result) { options.items = result.Items; return instance.playWithCommand(options, 'PlayNow'); }); } - }; + } - ChromecastPlayer.prototype.toggleFullscreen = function () { + toggleFullscreen() { // not supported - }; + } - ChromecastPlayer.prototype.beginPlayerUpdates = function () { + beginPlayerUpdates() { // Setup polling here - }; + } - ChromecastPlayer.prototype.endPlayerUpdates = function () { + endPlayerUpdates() { // Stop polling here - }; + } - ChromecastPlayer.prototype.getPlaylist = function () { + getPlaylist() { return Promise.resolve([]); - }; + } - ChromecastPlayer.prototype.getCurrentPlaylistItemId = function () { - }; + getCurrentPlaylistItemId() { + } - ChromecastPlayer.prototype.setCurrentPlaylistItem = function (playlistItemId) { + setCurrentPlaylistItem(playlistItemId) { return Promise.resolve(); - }; + } - ChromecastPlayer.prototype.removeFromPlaylist = function (playlistItemIds) { + removeFromPlaylist(playlistItemIds) { return Promise.resolve(); - }; + } - ChromecastPlayer.prototype.getPlayerState = function () { + getPlayerState() { return this.getPlayerStateInternal() || {}; - }; + } +} - return ChromecastPlayer; -}); +export default ChromecastPlayer; From 60dd3d009e245eb9318b468c347eef5b310f785b Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 08:42:34 +0100 Subject: [PATCH 115/155] remove require --- src/components/imageDownloader/imageDownloader.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index 3a5f2f56ce..3f78a5ecbb 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -8,7 +8,6 @@ import browser from 'browser'; import layoutManager from 'layoutManager'; import scrollHelper from 'scrollHelper'; import globalize from 'globalize'; -import require from 'require'; import 'emby-checkbox'; import 'paper-icon-button-light'; import 'emby-button'; From 0872f3f0010263d48a3b546031d215a7f2c3c3b4 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 09:27:11 +0100 Subject: [PATCH 116/155] Apply suggestions --- src/controllers/list.js | 49 +++++++++++++---------------- src/libraries/screensavermanager.js | 4 +-- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index 979bb76a20..2c3b966c4b 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -184,9 +184,9 @@ import 'emby-scroller'; const values = instance.getSortValues(); const sortBy = values.sortBy; - for (let 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; } } @@ -407,18 +407,18 @@ import 'emby-scroller'; } function hideOrShowAll(elems, hide) { - for (let 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 (let i = 0, length = elems.length; i < length; i++) { - elems[i].addEventListener(eventName, fn); + for (const elem of elems) { + elem.addEventListener(eventName, fn); } } @@ -724,16 +724,15 @@ class ItemsView { const btnViewSettings = view.querySelectorAll('.btnViewSettings'); - for (let 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)); } const filterButtons = view.querySelectorAll('.btnFilter'); this.filterButtons = filterButtons; const hasVisibleFilters = this.getVisibleFilters().length; - for (let i = 0, length = filterButtons.length; i < length; i++) { - const btnFilter = filterButtons[i]; + for (const btnFilter of filterButtons) { btnFilter.addEventListener('click', showFilterMenu.bind(this)); if (hasVisibleFilters) { @@ -744,10 +743,9 @@ class ItemsView { } const sortButtons = view.querySelectorAll('.btnSort'); - let i; - let length; - for (this.sortButtons = sortButtons, i = 0, length = sortButtons.length; i < length; i++) { - const sortButton = sortButtons[i]; + + this.sortButtons = sortButtons; + for (const sortButton of sortButtons) { sortButton.addEventListener('click', showSortMenu.bind(this)); if (params.type !== 'nextup') { @@ -886,10 +884,9 @@ class ItemsView { } getDefaultSortBy() { - const params = this.params; - const sortNameOption = this.getNameSortOption(params); + const sortNameOption = this.getNameSortOption(this.params); - if (params.type) { + if (this.params.type) { return sortNameOption.value; } @@ -898,16 +895,15 @@ class ItemsView { getSortMenuOptions() { const sortBy = []; - const params = this.params; - if (params.type === 'Programs') { + if (this.params.type === 'Programs') { sortBy.push({ name: globalize.translate('AirDate'), value: 'StartDate,SortName' }); } - let option = this.getNameSortOption(params); + let option = this.getNameSortOption(this.params); if (option) { sortBy.push(option); @@ -925,7 +921,7 @@ class ItemsView { sortBy.push(option); } - if (params.type !== 'Programs') { + if (this.params.type !== 'Programs') { sortBy.push({ name: globalize.translate('DateAdded'), value: 'DateCreated,SortName' @@ -938,8 +934,8 @@ class ItemsView { 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 @@ -1054,8 +1050,7 @@ class ItemsView { const filterButtons = this.filterButtons; if (filterButtons.length) { - for (let i = 0, length = filterButtons.length; i < length; i++) { - const btnFilter = filterButtons[i]; + for (const btnFilter of filterButtons) { let bubble = btnFilter.querySelector('.filterButtonBubble'); if (!bubble) { diff --git a/src/libraries/screensavermanager.js b/src/libraries/screensavermanager.js index 61d128ff14..5c24ec63d0 100644 --- a/src/libraries/screensavermanager.js +++ b/src/libraries/screensavermanager.js @@ -34,9 +34,7 @@ function getScreensaverPlugin(isLoggedIn) { const plugins = pluginManager.ofType('screensaver'); - for (let i = 0, length = plugins.length; i < length; i++) { - const plugin = plugins[i]; - + for (const plugin of plugins) { if (plugin.id === option) { return plugin; } From 93151b883bb6ce5a34deadbbf3c80a038019df50 Mon Sep 17 00:00:00 2001 From: LapinoLapidus Date: Fri, 7 Aug 2020 09:20:37 +0000 Subject: [PATCH 117/155] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/nl/ --- src/strings/nl.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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", From c5f6b5057452d348e473074934498e67d0984d8b Mon Sep 17 00:00:00 2001 From: Tobias Hoos Date: Fri, 7 Aug 2020 13:22:37 +0000 Subject: [PATCH 118/155] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/de/ --- src/strings/de.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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:" } From 5efd05617c7b9505302c5c1ab97e7d3339a454f9 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Fri, 7 Aug 2020 15:04:04 +0000 Subject: [PATCH 119/155] Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ro/ --- src/strings/ro.json | 58 +++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/strings/ro.json b/src/strings/ro.json index f8aa986264..5489e92abf 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:", @@ -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,7 @@ "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" } From da7cdda13217ddd47121d5ff59447ad3db0486d7 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Fri, 7 Aug 2020 15:22:47 +0000 Subject: [PATCH 120/155] Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ro/ --- src/strings/ro.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/strings/ro.json b/src/strings/ro.json index 5489e92abf..a0f090676b 100644 --- a/src/strings/ro.json +++ b/src/strings/ro.json @@ -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ă.", @@ -1541,5 +1541,9 @@ "StopPlayback": "Oprește redarea", "ViewAlbumArtist": "Vezi artistul albumului", "NextTrack": "Sari la următorul", - "LabelUnstable": "Instabil" + "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" } From 406e07154c048c1541bf97521b17445bf5ae7c27 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Fri, 7 Aug 2020 13:18:21 -0400 Subject: [PATCH 121/155] Hide download button for books when not supported --- src/controllers/itemDetails/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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); } From 496fb43cb5ac655b6a0cd4a9de4f2b5e0f760d95 Mon Sep 17 00:00:00 2001 From: Thomas Schwery Date: Fri, 7 Aug 2020 20:29:10 +0000 Subject: [PATCH 122/155] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 :" } From dbb6cd97301cc111ae54f500e683d88759b579c4 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 22:46:12 +0100 Subject: [PATCH 123/155] Lint --- src/libraries/scroller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index 4bfcb7d5b9..c460ec5b2c 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -883,4 +883,4 @@ scrollerFactory.create = function (frame, options) { return Promise.resolve(instance); }; -export default scrollerFactory; \ No newline at end of file +export default scrollerFactory; From b512187129cfb841bc0b1b8556616150ff4fef16 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Fri, 7 Aug 2020 22:09:25 +0000 Subject: [PATCH 124/155] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sl/ --- src/strings/sl-si.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index 423ee7797c..bf9ec21379 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -1429,5 +1429,7 @@ "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" } From 28d34b1bae8f8b77458fb98c11f134c8fed1ad52 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 7 Aug 2020 23:13:54 +0100 Subject: [PATCH 125/155] Update src/controllers/list.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/controllers/list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/list.js b/src/controllers/list.js index 2c3b966c4b..0027c576f1 100644 --- a/src/controllers/list.js +++ b/src/controllers/list.js @@ -399,7 +399,7 @@ import 'emby-scroller'; const instance = this; import('playlistEditor').then(({default: playlistEditor}) => { - new playlistEditor.showEditor({ + new playlistEditor({ items: [], serverId: instance.params.serverId }); From 2d72645aad0ae9dfa94fa9d7e32333d522129499 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 8 Aug 2020 03:15:57 +0200 Subject: [PATCH 126/155] Move HTML files to the proper locations --- package.json | 2 +- src/{ => controllers/dashboard}/apikeys.html | 0 .../dashboard}/dashboard.html | 0 .../dashboard/devices}/device.html | 0 .../dashboard/devices}/devices.html | 0 .../dashboard/dlna/profile.html} | 0 .../dashboard/dlna/profiles.html} | 0 .../dashboard/dlna/settings.html} | 0 .../dashboard}/encodingsettings.html | 0 .../dashboard/general.html} | 0 src/{ => controllers/dashboard}/library.html | 0 .../dashboard/{mediaLibrary.js => library.js} | 0 .../dashboard}/librarydisplay.html | 0 .../dashboard/logs.html} | 0 .../dashboard}/metadataimages.html | 0 .../dashboard}/metadatanfo.html | 0 .../dashboard}/networking.html | 0 .../dashboard/playback.html} | 0 .../scheduledtasks}/scheduledtask.html | 0 .../scheduledtasks}/scheduledtasks.html | 0 .../dashboard}/serveractivity.html | 0 .../dashboard/streaming.html} | 0 .../dashboard/users}/useredit.html | 0 .../dashboard/users}/userlibraryaccess.html | 0 .../dashboard/users}/usernew.html | 0 .../dashboard/users}/userparentalcontrol.html | 0 .../dashboard/users}/userpassword.html | 0 .../dashboard/users}/userprofiles.html | 0 src/{ => controllers}/edititemmetadata.html | 0 src/{ => controllers}/home.html | 0 src/{ => controllers}/list.html | 0 src/{ => controllers}/livetv.html | 0 .../livetvguideprovider.html | 0 src/{ => controllers}/livetvsettings.html | 0 src/{ => controllers}/livetvstatus.html | 0 src/{ => controllers}/livetvtuner.html | 0 src/{ => controllers/movies}/movies.html | 0 src/{ => controllers/music}/music.html | 0 src/{ => controllers}/search.html | 0 .../shows/tvrecommended.html} | 0 .../wizard/library.html} | 0 src/index.html | 3 +- src/scripts/routes.js | 131 ++++++++++++------ 43 files changed, 87 insertions(+), 49 deletions(-) rename src/{ => controllers/dashboard}/apikeys.html (100%) rename src/{ => controllers/dashboard}/dashboard.html (100%) rename src/{ => controllers/dashboard/devices}/device.html (100%) rename src/{ => controllers/dashboard/devices}/devices.html (100%) rename src/{dlnaprofile.html => controllers/dashboard/dlna/profile.html} (100%) rename src/{dlnaprofiles.html => controllers/dashboard/dlna/profiles.html} (100%) rename src/{dlnasettings.html => controllers/dashboard/dlna/settings.html} (100%) rename src/{ => controllers/dashboard}/encodingsettings.html (100%) rename src/{dashboardgeneral.html => controllers/dashboard/general.html} (100%) rename src/{ => controllers/dashboard}/library.html (100%) rename src/controllers/dashboard/{mediaLibrary.js => library.js} (100%) rename src/{ => controllers/dashboard}/librarydisplay.html (100%) rename src/{log.html => controllers/dashboard/logs.html} (100%) rename src/{ => controllers/dashboard}/metadataimages.html (100%) rename src/{ => controllers/dashboard}/metadatanfo.html (100%) rename src/{ => controllers/dashboard}/networking.html (100%) rename src/{playbackconfiguration.html => controllers/dashboard/playback.html} (100%) rename src/{ => controllers/dashboard/scheduledtasks}/scheduledtask.html (100%) rename src/{ => controllers/dashboard/scheduledtasks}/scheduledtasks.html (100%) rename src/{ => controllers/dashboard}/serveractivity.html (100%) rename src/{streamingsettings.html => controllers/dashboard/streaming.html} (100%) rename src/{ => controllers/dashboard/users}/useredit.html (100%) rename src/{ => controllers/dashboard/users}/userlibraryaccess.html (100%) rename src/{ => controllers/dashboard/users}/usernew.html (100%) rename src/{ => controllers/dashboard/users}/userparentalcontrol.html (100%) rename src/{ => controllers/dashboard/users}/userpassword.html (100%) rename src/{ => controllers/dashboard/users}/userprofiles.html (100%) rename src/{ => controllers}/edititemmetadata.html (100%) rename src/{ => controllers}/home.html (100%) rename src/{ => controllers}/list.html (100%) rename src/{ => controllers}/livetv.html (100%) rename src/{ => controllers}/livetvguideprovider.html (100%) rename src/{ => controllers}/livetvsettings.html (100%) rename src/{ => controllers}/livetvstatus.html (100%) rename src/{ => controllers}/livetvtuner.html (100%) rename src/{ => controllers/movies}/movies.html (100%) rename src/{ => controllers/music}/music.html (100%) rename src/{ => controllers}/search.html (100%) rename src/{tv.html => controllers/shows/tvrecommended.html} (100%) rename src/{wizardlibrary.html => controllers/wizard/library.html} (100%) diff --git a/package.json b/package.json index ac2ad86e57..21b8ffbbd8 100644 --- a/package.json +++ b/package.json @@ -198,7 +198,7 @@ "src/controllers/music/musicplaylists.js", "src/controllers/music/musicrecommended.js", "src/controllers/music/songs.js", - "src/controllers/dashboard/mediaLibrary.js", + "src/controllers/dashboard/library.js", "src/controllers/dashboard/metadataImages.js", "src/controllers/dashboard/metadatanfo.js", "src/controllers/dashboard/networking.js", diff --git a/src/apikeys.html b/src/controllers/dashboard/apikeys.html similarity index 100% rename from src/apikeys.html rename to src/controllers/dashboard/apikeys.html diff --git a/src/dashboard.html b/src/controllers/dashboard/dashboard.html similarity index 100% rename from src/dashboard.html rename to src/controllers/dashboard/dashboard.html diff --git a/src/device.html b/src/controllers/dashboard/devices/device.html similarity index 100% rename from src/device.html rename to src/controllers/dashboard/devices/device.html diff --git a/src/devices.html b/src/controllers/dashboard/devices/devices.html similarity index 100% rename from src/devices.html rename to src/controllers/dashboard/devices/devices.html diff --git a/src/dlnaprofile.html b/src/controllers/dashboard/dlna/profile.html similarity index 100% rename from src/dlnaprofile.html rename to src/controllers/dashboard/dlna/profile.html diff --git a/src/dlnaprofiles.html b/src/controllers/dashboard/dlna/profiles.html similarity index 100% rename from src/dlnaprofiles.html rename to src/controllers/dashboard/dlna/profiles.html diff --git a/src/dlnasettings.html b/src/controllers/dashboard/dlna/settings.html similarity index 100% rename from src/dlnasettings.html rename to src/controllers/dashboard/dlna/settings.html diff --git a/src/encodingsettings.html b/src/controllers/dashboard/encodingsettings.html similarity index 100% rename from src/encodingsettings.html rename to src/controllers/dashboard/encodingsettings.html diff --git a/src/dashboardgeneral.html b/src/controllers/dashboard/general.html similarity index 100% rename from src/dashboardgeneral.html rename to src/controllers/dashboard/general.html diff --git a/src/library.html b/src/controllers/dashboard/library.html similarity index 100% rename from src/library.html rename to src/controllers/dashboard/library.html diff --git a/src/controllers/dashboard/mediaLibrary.js b/src/controllers/dashboard/library.js similarity index 100% rename from src/controllers/dashboard/mediaLibrary.js rename to src/controllers/dashboard/library.js diff --git a/src/librarydisplay.html b/src/controllers/dashboard/librarydisplay.html similarity index 100% rename from src/librarydisplay.html rename to src/controllers/dashboard/librarydisplay.html diff --git a/src/log.html b/src/controllers/dashboard/logs.html similarity index 100% rename from src/log.html rename to src/controllers/dashboard/logs.html diff --git a/src/metadataimages.html b/src/controllers/dashboard/metadataimages.html similarity index 100% rename from src/metadataimages.html rename to src/controllers/dashboard/metadataimages.html diff --git a/src/metadatanfo.html b/src/controllers/dashboard/metadatanfo.html similarity index 100% rename from src/metadatanfo.html rename to src/controllers/dashboard/metadatanfo.html diff --git a/src/networking.html b/src/controllers/dashboard/networking.html similarity index 100% rename from src/networking.html rename to src/controllers/dashboard/networking.html diff --git a/src/playbackconfiguration.html b/src/controllers/dashboard/playback.html similarity index 100% rename from src/playbackconfiguration.html rename to src/controllers/dashboard/playback.html diff --git a/src/scheduledtask.html b/src/controllers/dashboard/scheduledtasks/scheduledtask.html similarity index 100% rename from src/scheduledtask.html rename to src/controllers/dashboard/scheduledtasks/scheduledtask.html diff --git a/src/scheduledtasks.html b/src/controllers/dashboard/scheduledtasks/scheduledtasks.html similarity index 100% rename from src/scheduledtasks.html rename to src/controllers/dashboard/scheduledtasks/scheduledtasks.html diff --git a/src/serveractivity.html b/src/controllers/dashboard/serveractivity.html similarity index 100% rename from src/serveractivity.html rename to src/controllers/dashboard/serveractivity.html diff --git a/src/streamingsettings.html b/src/controllers/dashboard/streaming.html similarity index 100% rename from src/streamingsettings.html rename to src/controllers/dashboard/streaming.html diff --git a/src/useredit.html b/src/controllers/dashboard/users/useredit.html similarity index 100% rename from src/useredit.html rename to src/controllers/dashboard/users/useredit.html diff --git a/src/userlibraryaccess.html b/src/controllers/dashboard/users/userlibraryaccess.html similarity index 100% rename from src/userlibraryaccess.html rename to src/controllers/dashboard/users/userlibraryaccess.html diff --git a/src/usernew.html b/src/controllers/dashboard/users/usernew.html similarity index 100% rename from src/usernew.html rename to src/controllers/dashboard/users/usernew.html diff --git a/src/userparentalcontrol.html b/src/controllers/dashboard/users/userparentalcontrol.html similarity index 100% rename from src/userparentalcontrol.html rename to src/controllers/dashboard/users/userparentalcontrol.html diff --git a/src/userpassword.html b/src/controllers/dashboard/users/userpassword.html similarity index 100% rename from src/userpassword.html rename to src/controllers/dashboard/users/userpassword.html diff --git a/src/userprofiles.html b/src/controllers/dashboard/users/userprofiles.html similarity index 100% rename from src/userprofiles.html rename to src/controllers/dashboard/users/userprofiles.html diff --git a/src/edititemmetadata.html b/src/controllers/edititemmetadata.html similarity index 100% rename from src/edititemmetadata.html rename to src/controllers/edititemmetadata.html diff --git a/src/home.html b/src/controllers/home.html similarity index 100% rename from src/home.html rename to src/controllers/home.html diff --git a/src/list.html b/src/controllers/list.html similarity index 100% rename from src/list.html rename to src/controllers/list.html diff --git a/src/livetv.html b/src/controllers/livetv.html similarity index 100% rename from src/livetv.html rename to src/controllers/livetv.html diff --git a/src/livetvguideprovider.html b/src/controllers/livetvguideprovider.html similarity index 100% rename from src/livetvguideprovider.html rename to src/controllers/livetvguideprovider.html diff --git a/src/livetvsettings.html b/src/controllers/livetvsettings.html similarity index 100% rename from src/livetvsettings.html rename to src/controllers/livetvsettings.html diff --git a/src/livetvstatus.html b/src/controllers/livetvstatus.html similarity index 100% rename from src/livetvstatus.html rename to src/controllers/livetvstatus.html diff --git a/src/livetvtuner.html b/src/controllers/livetvtuner.html similarity index 100% rename from src/livetvtuner.html rename to src/controllers/livetvtuner.html diff --git a/src/movies.html b/src/controllers/movies/movies.html similarity index 100% rename from src/movies.html rename to src/controllers/movies/movies.html diff --git a/src/music.html b/src/controllers/music/music.html similarity index 100% rename from src/music.html rename to src/controllers/music/music.html diff --git a/src/search.html b/src/controllers/search.html similarity index 100% rename from src/search.html rename to src/controllers/search.html diff --git a/src/tv.html b/src/controllers/shows/tvrecommended.html similarity index 100% rename from src/tv.html rename to src/controllers/shows/tvrecommended.html diff --git a/src/wizardlibrary.html b/src/controllers/wizard/library.html similarity index 100% rename from src/wizardlibrary.html rename to src/controllers/wizard/library.html diff --git a/src/index.html b/src/index.html index 797fce8a94..c689a42f30 100644 --- a/src/index.html +++ b/src/index.html @@ -137,8 +137,7 @@ } @media screen - and (min-device-width: 992px) - and (-webkit-min-device-pixel-ratio: 1) { + and (min-device-width: 992px) { .splashLogo { background-image: url(assets/img/banner-light.png); } diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 4094a2552f..4bbd5cff34 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -112,54 +112,69 @@ import 'detailtablecss'; }); defineRoute({ - path: '/dashboard.html', + alias: '/dashboard.html', + path: '/controllers/dashboard/dashboard.html', autoFocus: false, roles: 'admin', controller: 'dashboard/dashboard' }); defineRoute({ - path: '/dashboardgeneral.html', + alias: '/dashboardgeneral.html', + path: '/controllers/dashboard/general.html', controller: 'dashboard/general', autoFocus: false, roles: 'admin' }); defineRoute({ - path: '/networking.html', + alias: '/networking.html', + path: '/controllers/dashboard/networking.html', autoFocus: false, roles: 'admin', controller: 'dashboard/networking' }); defineRoute({ - path: '/devices.html', + alias: '/devices.html', + path: '/controllers/dashboard/devices/devices.html', autoFocus: false, roles: 'admin', controller: 'dashboard/devices/devices' }); defineRoute({ - path: '/device.html', + alias: '/device.html', + path: '/controllers/dashboard/devices/device.html', autoFocus: false, roles: 'admin', controller: 'dashboard/devices/device' }); defineRoute({ - path: '/dlnaprofile.html', + alias: '/dlnaprofile.html', + path: '/controllers/dashboard/dlna/profile.html', autoFocus: false, roles: 'admin', controller: 'dashboard/dlna/profile' }); defineRoute({ - path: '/dlnaprofiles.html', + alias: '/dlnaprofiles.html', + path: '/controllers/dashboard/dlna/profiles.html', autoFocus: false, roles: 'admin', controller: 'dashboard/dlna/profiles' }); + defineRoute({ + alias: '/dlnasettings.html', + path: '/controllers/dashboard/dlna/settings.html', + autoFocus: false, + roles: 'admin', + controller: 'dashboard/dlna/settings' + }); + defineRoute({ alias: '/addplugin.html', path: '/controllers/dashboard/plugins/add/index.html', @@ -169,54 +184,54 @@ import 'detailtablecss'; }); defineRoute({ - path: '/library.html', + alias: '/library.html', + path: '/controllers/dashboard/library.html', autoFocus: false, roles: 'admin', - controller: 'dashboard/mediaLibrary' + controller: 'dashboard/library' }); defineRoute({ - path: '/librarydisplay.html', + alias: '/librarydisplay.html', + path: '/controllers/dashboard/librarydisplay.html', autoFocus: false, roles: 'admin', controller: 'dashboard/librarydisplay' }); defineRoute({ - path: '/dlnasettings.html', - autoFocus: false, - roles: 'admin', - controller: 'dashboard/dlna/settings' - }); - - defineRoute({ - path: '/edititemmetadata.html', + alias: '/edititemmetadata.html', + path: '/controllers/edititemmetadata.html', controller: 'edititemmetadata', autoFocus: false }); defineRoute({ - path: '/encodingsettings.html', + alias: '/encodingsettings.html', + path: '/controllers/dashboard/encodingsettings.html', autoFocus: false, roles: 'admin', controller: 'dashboard/encodingsettings' }); defineRoute({ - path: '/log.html', + alias: '/log.html', + path: '/controllers/dashboard/logs.html', roles: 'admin', controller: 'dashboard/logs' }); defineRoute({ - path: '/metadataimages.html', + alias: '/metadataimages.html', + path: '/controllers/dashboard/metadataimages.html', autoFocus: false, roles: 'admin', controller: 'dashboard/metadataImages' }); defineRoute({ - path: '/metadatanfo.html', + alias: '/metadatanfo.html', + path: '/controllers/dashboard/metadatanfo.html', autoFocus: false, roles: 'admin', controller: 'dashboard/metadatanfo' @@ -240,6 +255,7 @@ import 'detailtablecss'; defineRoute({ path: '/playbackconfiguration.html', + path: '/controllers/dashboard/playback.html', autoFocus: false, roles: 'admin', controller: 'dashboard/playback' @@ -262,19 +278,22 @@ import 'detailtablecss'; }); defineRoute({ - path: '/home.html', + alias: '/home.html', + path: '/controllers/home.html', autoFocus: false, controller: 'home', type: 'home' }); defineRoute({ - path: '/search.html', + alias: '/search.html', + path: '/controllers/search.html', controller: 'searchpage' }); defineRoute({ - path: '/list.html', + alias: '/list.html', + path: '/controllers/list.html', autoFocus: false, controller: 'list' }); @@ -287,46 +306,53 @@ import 'detailtablecss'; }); defineRoute({ - path: '/livetv.html', + alias: '/livetv.html', + path: '/controllers/livetv.html', controller: 'livetv/livetvsuggested', autoFocus: false }); defineRoute({ - path: '/livetvguideprovider.html', + alias: '/livetvguideprovider.html', + path: '/controllers/livetvguideprovider.html', autoFocus: false, roles: 'admin', controller: 'livetvguideprovider' }); defineRoute({ - path: '/livetvsettings.html', + alias: '/livetvsettings.html', + path: '/controllers/livetvsettings.html', autoFocus: false, controller: 'livetvsettings' }); defineRoute({ - path: '/livetvstatus.html', + alias: '/livetvstatus.html', + path: '/controllers/livetvstatus.html', autoFocus: false, roles: 'admin', controller: 'livetvstatus' }); defineRoute({ - path: '/livetvtuner.html', + alias: '/livetvtuner.html', + path: '/controllers/livetvtuner.html', autoFocus: false, roles: 'admin', controller: 'livetvtuner' }); defineRoute({ - path: '/movies.html', + alias: '/movies.html', + path: '/controllers/movies/movies.html', autoFocus: false, controller: 'movies/moviesrecommended' }); defineRoute({ - path: '/music.html', + alias: '/music.html', + path: '/controllers/music/music.html', controller: 'music/musicrecommended', autoFocus: false }); @@ -340,82 +366,94 @@ import 'detailtablecss'; }); defineRoute({ - path: '/scheduledtask.html', + alias: '/scheduledtask.html', + path: '/controllers/dashboard/scheduledtasks/scheduledtask.html', autoFocus: false, roles: 'admin', controller: 'dashboard/scheduledtasks/scheduledtask' }); defineRoute({ - path: '/scheduledtasks.html', + alias: '/scheduledtasks.html', + path: '/controllers/dashboard/scheduledtasks/scheduledtasks.html', autoFocus: false, roles: 'admin', controller: 'dashboard/scheduledtasks/scheduledtasks' }); defineRoute({ - path: '/serveractivity.html', + alias: '/serveractivity.html', + path: '/controllers/dashboard/serveractivity.html', autoFocus: false, roles: 'admin', controller: 'dashboard/serveractivity' }); defineRoute({ - path: '/apikeys.html', + alias: '/apikeys.html', + path: '/controllers/dashboard/apikeys.html', autoFocus: false, roles: 'admin', controller: 'dashboard/apikeys' }); defineRoute({ - path: '/streamingsettings.html', + alias: '/streamingsettings.html', + path: '/controllers/dashboard/streaming.html', autoFocus: false, roles: 'admin', controller: 'dashboard/streaming' }); defineRoute({ - path: '/tv.html', + alias: '/tv.html', + path: '/controllers/shows/tvrecommended.html', autoFocus: false, controller: 'shows/tvrecommended' }); defineRoute({ - path: '/useredit.html', + alias: '/useredit.html', + path: '/controllers/dashboard/users/useredit.html', autoFocus: false, roles: 'admin', controller: 'dashboard/users/useredit' }); defineRoute({ - path: '/userlibraryaccess.html', + alias: '/userlibraryaccess.html', + path: '/controllers/dashboard/users/userlibraryaccess.html', autoFocus: false, roles: 'admin', controller: 'dashboard/users/userlibraryaccess' }); defineRoute({ - path: '/usernew.html', + alias: '/usernew.html', + path: '/controllers/dashboard/users/usernew.html', autoFocus: false, roles: 'admin', controller: 'dashboard/users/usernew' }); defineRoute({ - path: '/userparentalcontrol.html', + alias: '/userparentalcontrol.html', + path: '/controllers/dashboard/users/userparentalcontrol.html', autoFocus: false, roles: 'admin', controller: 'dashboard/users/userparentalcontrol' }); defineRoute({ - path: '/userpassword.html', + alias: '/userpassword.html', + path: '/controllers/dashboard/users/userpassword.html', autoFocus: false, controller: 'dashboard/users/userpasswordpage' }); defineRoute({ - path: '/userprofiles.html', + alias: '/userprofiles.html', + path: '/controllers/dashboard/users/userprofiles.html', autoFocus: false, roles: 'admin', controller: 'dashboard/users/userprofilespage' @@ -438,10 +476,11 @@ import 'detailtablecss'; }); defineRoute({ - path: '/wizardlibrary.html', + alias: '/wizardlibrary.html', + path: '/controllers/wizard/library.html', autoFocus: false, anonymous: true, - controller: 'dashboard/mediaLibrary' + controller: 'dashboard/library' }); defineRoute({ From 6b655e57ac334764f2e9d5aa263dbe851f9414b9 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 8 Aug 2020 05:31:07 +0200 Subject: [PATCH 127/155] Fix lint --- src/scripts/routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/routes.js b/src/scripts/routes.js index 4bbd5cff34..4bb3eb25d9 100644 --- a/src/scripts/routes.js +++ b/src/scripts/routes.js @@ -254,7 +254,7 @@ import 'detailtablecss'; }); defineRoute({ - path: '/playbackconfiguration.html', + alias: '/playbackconfiguration.html', path: '/controllers/dashboard/playback.html', autoFocus: false, roles: 'admin', From b3166dd2c4132d07ec2436d7abc4efa052b016ec Mon Sep 17 00:00:00 2001 From: SaddFox Date: Fri, 7 Aug 2020 22:20:55 +0000 Subject: [PATCH 128/155] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sl/ --- src/strings/sl-si.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/strings/sl-si.json b/src/strings/sl-si.json index bf9ec21379..077ea67804 100644 --- a/src/strings/sl-si.json +++ b/src/strings/sl-si.json @@ -1431,5 +1431,21 @@ "OptionDisplayFolderViewHelp": "Prikaže mape poleg ostalih knjižnic predstavnosti. Uporabno za preprost ogled map.", "OptionDisplayFolderView": "Prikaži pogled mape za prikaz navadnih map predstavnosti", "Yesterday": "Včeraj", - "Yes": "Da" + "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" } From f34a64a607c024bea5df3fd8fac00ed68768ab85 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2020 11:13:21 +0000 Subject: [PATCH 129/155] Bump whatwg-fetch from 3.3.1 to 3.4.0 Bumps [whatwg-fetch](https://github.com/github/fetch) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/github/fetch/releases) - [Commits](https://github.com/github/fetch/compare/v3.3.1...v3.4.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ac2ad86e57..d7cd3b7f31 100644 --- a/package.json +++ b/package.json @@ -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": [ diff --git a/yarn.lock b/yarn.lock index ad55e16176..fd02458167 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From 93f23c3d32d85899406e699a66b1e40e19968899 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2020 11:18:26 +0000 Subject: [PATCH 130/155] Bump hls.js from 0.14.7 to 0.14.8 Bumps [hls.js](https://github.com/video-dev/hls.js) from 0.14.7 to 0.14.8. - [Release notes](https://github.com/video-dev/hls.js/releases) - [Changelog](https://github.com/video-dev/hls.js/blob/master/docs/release-process.md) - [Commits](https://github.com/video-dev/hls.js/compare/v0.14.7...v0.14.8) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ac2ad86e57..9f3b1d9499 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", diff --git a/yarn.lock b/yarn.lock index ad55e16176..bfe52b7f3e 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" From 23be95e450d4873eb5a938cfe3884ec60f083249 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 17:51:10 +0100 Subject: [PATCH 131/155] Update src/components/remotecontrol/remotecontrol.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/components/remotecontrol/remotecontrol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/remotecontrol/remotecontrol.js b/src/components/remotecontrol/remotecontrol.js index b9f33b4e1a..6048c918c7 100644 --- a/src/components/remotecontrol/remotecontrol.js +++ b/src/components/remotecontrol/remotecontrol.js @@ -693,7 +693,7 @@ export default function () { import('playlistEditor').then(({ default: playlistEditor }) => { getSaveablePlaylistItems().then(function (items) { const serverId = items.length ? items[0].ServerId : ApiClient.serverId(); - new playlistEditor.showEditor({ + new playlistEditor({ items: items.map(function (i) { return i.Id; }), From be3dc8c697c2c34d6e8b2c0d046a6a59799eb7b2 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 18:41:23 +0100 Subject: [PATCH 132/155] Update src/controllers/home.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/controllers/home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/home.js b/src/controllers/home.js index 1a4b315b09..72e326e46b 100644 --- a/src/controllers/home.js +++ b/src/controllers/home.js @@ -56,7 +56,7 @@ class HomeView extends TabbedView { let controller = instance.tabControllers[index]; if (!controller) { - controller = new controllerFactory.default(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params); + controller = new controllerFactory(instance.view.querySelector(".tabContent[data-index='" + index + "']"), instance.params); instance.tabControllers[index] = controller; } From af2fab85d12f53a16a8e98b30e03ae7166ad7794 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sun, 26 Jul 2020 22:25:54 +0200 Subject: [PATCH 133/155] Migrate add, installed, available plugin pages to ES6 --- package.json | 3 + .../dashboard/plugins/add/index.js | 233 ++++++------ .../dashboard/plugins/available/index.js | 248 ++++++------ .../dashboard/plugins/installed/index.js | 357 +++++++++--------- src/strings/en-us.json | 2 + 5 files changed, 426 insertions(+), 417 deletions(-) diff --git a/package.json b/package.json index 54d5f28189..467dc3b171 100644 --- a/package.json +++ b/package.json @@ -209,6 +209,9 @@ "src/controllers/dashboard/notifications/notification.js", "src/controllers/dashboard/notifications/notifications.js", "src/controllers/dashboard/playback.js", + "src/controllers/dashboard/plugins/add/index.js", + "src/controllers/dashboard/plugins/installed/index.js", + "src/controllers/dashboard/plugins/available/index.js", "src/controllers/dashboard/plugins/repositories/index.js", "src/controllers/dashboard/scheduledtasks/scheduledtask.js", "src/controllers/dashboard/scheduledtasks/scheduledtasks.js", diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 6c666b4646..8393970e76 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -1,143 +1,148 @@ -define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'connectionManager', 'emby-button'], function ($, loading, libraryMenu, globalize, connectionManager) { - 'use strict'; +import $ from 'jQuery'; +import loading from 'loading'; +import globalize from 'globalize'; +import 'emby-button'; - loading = loading.default || loading; +function populateHistory(packageInfo, page) { + let html = ''; + const length = Math.min(packageInfo.versions.length, 10); - function populateHistory(packageInfo, page) { - var html = ''; - var length = Math.min(packageInfo.versions.length, 10); - - for (var i = 0; i < length; i++) { - var version = packageInfo.versions[i]; - html += '

' + version.version + '

'; - html += '
' + version.changelog + '
'; - } - - $('#revisionHistory', page).html(html); + for (let i = 0; i < length; i++) { + let version = packageInfo.versions[i]; + html += '

' + version.version + '

'; + html += '
' + version.changelog + '
'; } - function populateVersions(packageInfo, page, installedPlugin) { - var html = ''; + $('#revisionHistory', page).html(html); +} - for (var i = 0; i < packageInfo.versions.length; i++) { - var version = packageInfo.versions[i]; - html += ''; - } +function populateVersions(packageInfo, page, installedPlugin) { + let html = ''; - var selectmenu = $('#selectVersion', page).html(html); - - if (!installedPlugin) { - $('#pCurrentVersion', page).hide().html(''); - } - - var packageVersion = packageInfo.versions[0]; - if (packageVersion) { - selectmenu.val(packageVersion.version); - } + for (let i = 0; i < packageInfo.versions.length; i++) { + const version = packageInfo.versions[i]; + html += ''; } - function renderPackage(pkg, installedPlugins, page) { - var installedPlugin = installedPlugins.filter(function (ip) { - return ip.Name == pkg.name; - })[0]; + const selectmenu = $('#selectVersion', page).html(html); - populateVersions(pkg, page, installedPlugin); - populateHistory(pkg, page); - - $('.pluginName', page).html(pkg.name); - $('#btnInstallDiv', page).removeClass('hide'); - $('#pSelectVersion', page).removeClass('hide'); - - if (pkg.overview) { - $('#overview', page).show().html(pkg.overview); - } else { - $('#overview', page).hide(); - } - - $('#description', page).html(pkg.description); - $('#developer', page).html(pkg.owner); - - if (installedPlugin) { - var currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); - $('#pCurrentVersion', page).show().html(currentVersionText); - } else { - $('#pCurrentVersion', page).hide().html(''); - } - - loading.hide(); + if (!installedPlugin) { + $('#pCurrentVersion', page).hide().html(''); } - function alertText(options) { - require(['alert'], function ({default: alert}) { - alert(options); - }); + const packageVersion = packageInfo.versions[0]; + if (packageVersion) { + selectmenu.val(packageVersion.version); + } +} + +function renderPackage(pkg, installedPlugins, page) { + const installedPlugin = installedPlugins.filter(function (ip) { + return ip.Name == pkg.name; + })[0]; + + populateVersions(pkg, page, installedPlugin); + populateHistory(pkg, page); + + $('.pluginName', page).html(pkg.name); + $('#btnInstallDiv', page).removeClass('hide'); + $('#pSelectVersion', page).removeClass('hide'); + + if (pkg.overview) { + $('#overview', page).show().html(pkg.overview); + } else { + $('#overview', page).hide(); } - function performInstallation(page, name, guid, version) { - var developer = $('#developer', page).html().toLowerCase(); + $('#description', page).html(pkg.description); + $('#developer', page).html(pkg.owner); - var alertCallback = function () { - loading.show(); - page.querySelector('#btnInstall').disabled = true; - ApiClient.installPlugin(name, guid, version).then(function () { - loading.hide(); - alertText(globalize.translate('MessagePluginInstalled')); - }); - }; + if (installedPlugin) { + const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '' + installedPlugin.Version + ''); + $('#pCurrentVersion', page).show().html(currentVersionText); + } else { + $('#pCurrentVersion', page).hide().html(''); + } - if (developer !== 'jellyfin') { + loading.hide(); +} + +function alertText(options) { + import('alert').then(({default: alert}) => { + alert(options); + }); +} + +function performInstallation(page, name, guid, version) { + const developer = $('#developer', page).html().toLowerCase(); + + const alertCallback = function () { + loading.show(); + page.querySelector('#btnInstall').disabled = true; + ApiClient.installPlugin(name, guid, version).then(() => { loading.hide(); - var msg = globalize.translate('MessagePluginInstallDisclaimer'); - msg += '
'; - msg += '
'; - msg += globalize.translate('PleaseConfirmPluginInstallation'); + alertText(globalize.translate('MessagePluginInstalled')); + }).catch(() => { + alertText(globalize.translate('MessagePluginIntallError')); + }); + }; - require(['confirm'], function (confirm) { - confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () { - alertCallback(); - }, function () { - console.debug('plugin not installed'); - }); + if (developer !== 'jellyfin') { + loading.hide(); + let msg = globalize.translate('MessagePluginInstallDisclaimer'); + msg += '
'; + msg += '
'; + msg += globalize.translate('PleaseConfirmPluginInstallation'); + + import('confirm').then(({default: confirm}) => { + confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () { + alertCallback(); + }).catch(() => { + console.debug('plugin not installed'); }); - } else { - alertCallback(); - } + }); + } else { + alertCallback(); } +} - return function (view, params) { - $('.addPluginForm', view).on('submit', function () { - loading.show(); - var page = $(this).parents('#addPluginPage')[0]; - var name = params.name; - var guid = params.guid; - ApiClient.getInstalledPlugins().then(function (plugins) { - var installedPlugin = plugins.filter(function (plugin) { - return plugin.Name == name; - })[0]; +export default function(view, params) { + $('.addPluginForm', view).on('submit', function () { + loading.show(); + const page = $(this).parents('#addPluginPage')[0]; + const name = params.name; + const guid = params.guid; + ApiClient.getInstalledPlugins().then(function (plugins) { + const installedPlugin = plugins.filter(function (plugin) { + return plugin.Name == name; + })[0]; - var version = $('#selectVersion', page).val(); - if (installedPlugin && installedPlugin.Version === version) { + const version = $('#selectVersion', page).val(); + if (installedPlugin) { + if (installedPlugin.Version === version) { loading.hide(); Dashboard.alert({ message: globalize.translate('MessageAlreadyInstalled'), title: globalize.translate('HeaderPluginInstallation') }); - } else { - performInstallation(page, name, guid, version); } - }); - return false; + } else { + performInstallation(page, name, guid, version); + } + }).catch(() => { + alertText(globalize.translate('MessageGetInstalledPluginsError')); }); - view.addEventListener('viewshow', function () { - var page = this; - loading.show(); - var name = params.name; - var guid = params.guid; - var promise1 = ApiClient.getPackageInfo(name, guid); - var promise2 = ApiClient.getInstalledPlugins(); - Promise.all([promise1, promise2]).then(function (responses) { - renderPackage(responses[0], responses[1], page); - }); + return false; + }); + view.addEventListener('viewshow', function () { + const page = this; + loading.show(); + const name = params.name; + const guid = params.guid; + const promise1 = ApiClient.getPackageInfo(name, guid); + const promise2 = ApiClient.getInstalledPlugins(); + Promise.all([promise1, promise2]).then(function (responses) { + renderPackage(responses[0], responses[1], page); }); - }; -}); + }); +} diff --git a/src/controllers/dashboard/plugins/available/index.js b/src/controllers/dashboard/plugins/available/index.js index e38365ef55..aa25320d5c 100644 --- a/src/controllers/dashboard/plugins/available/index.js +++ b/src/controllers/dashboard/plugins/available/index.js @@ -1,143 +1,141 @@ -define(['loading', 'libraryMenu', 'globalize', 'cardStyle', 'emby-button', 'emby-checkbox', 'emby-select'], function (loading, libraryMenu, globalize) { - 'use strict'; +import loading from 'loading'; +import libraryMenu from 'libraryMenu'; +import globalize from 'globalize'; +import 'cardStyle'; +import 'emby-button'; +import 'emby-checkbox'; +import 'emby-select'; - loading = loading.default || loading; - - function reloadList(page) { - loading.show(); - var promise1 = ApiClient.getAvailablePlugins(); - var promise2 = ApiClient.getInstalledPlugins(); - Promise.all([promise1, promise2]).then(function (responses) { - populateList({ - catalogElement: page.querySelector('#pluginTiles'), - noItemsElement: page.querySelector('#noPlugins'), - availablePlugins: responses[0], - installedPlugins: responses[1] - }); +function reloadList(page) { + loading.show(); + const promise1 = ApiClient.getAvailablePlugins(); + const promise2 = ApiClient.getInstalledPlugins(); + Promise.all([promise1, promise2]).then(function (responses) { + populateList({ + catalogElement: page.querySelector('#pluginTiles'), + noItemsElement: page.querySelector('#noPlugins'), + availablePlugins: responses[0], + installedPlugins: responses[1] }); + }); +} + +function getHeaderText(category) { + category = category.replace(' ', ''); + if ('Channel' === category) { + category = 'Channels'; + } else if ('Theme' === category) { + category = 'Themes'; + } else if ('LiveTV' === category) { + category = 'HeaderLiveTV'; + } else if ('ScreenSaver' === category) { + category = 'HeaderScreenSavers'; } - function getHeaderText(category) { - category = category.replace(' ', ''); - if (category === 'Channel') { - category = 'Channels'; - } else if (category === 'Theme') { - category = 'Themes'; - } else if (category === 'LiveTV') { - category = 'HeaderLiveTV'; - } else if (category === 'ScreenSaver') { - category = 'HeaderScreenSavers'; + return globalize.translate(category); +} + +function populateList(options) { + const availablePlugins = options.availablePlugins; + const installedPlugins = options.installedPlugins; + + availablePlugins.forEach(function (plugin, index, array) { + plugin.category = plugin.category || 'General'; + plugin.categoryDisplayName = getHeaderText(plugin.category); + array[index] = plugin; + }); + + availablePlugins.sort(function (a, b) { + if (a.category > b.category) { + return 1; + } else if (b.category > a.category) { + return -1; } + if (a.name > b.name) { + return 1; + } else if (b.name > a.name) { + return -1; + } + return 0; + }); - return globalize.translate(category); - } + let currentCategory = null; + let html = ''; - function populateList(options) { - var availablePlugins = options.availablePlugins; - var installedPlugins = options.installedPlugins; - - availablePlugins.forEach(function (plugin, index, array) { - plugin.category = plugin.category || 'General'; - plugin.categoryDisplayName = getHeaderText(plugin.category); - array[index] = plugin; - }); - - availablePlugins.sort(function (a, b) { - if (a.category > b.category) { - return 1; - } else if (b.category > a.category) { - return -1; + for (let i = 0; i < availablePlugins.length; i++) { + const plugin = availablePlugins[i]; + const category = plugin.categoryDisplayName; + if (category != currentCategory) { + if (currentCategory) { + html += '
'; + html += '
'; } - if (a.name > b.name) { - return 1; - } else if (b.name > a.name) { - return -1; - } - return 0; - }); - - var currentCategory = null; - var html = ''; - - for (var i = 0; i < availablePlugins.length; i++) { - var plugin = availablePlugins[i]; - var category = plugin.categoryDisplayName; - if (category != currentCategory) { - if (currentCategory) { - html += '
'; - html += '
'; - } - html += '
'; - html += '

' + category + '

'; - html += '
'; - currentCategory = category; - } - html += getPluginHtml(plugin, options, installedPlugins); + html += '
'; + html += '

' + category + '

'; + html += '
'; + currentCategory = category; } - html += '
'; - html += '
'; + html += getPluginHtml(plugin, options, installedPlugins); + } + html += '
'; + html += '
'; - if (!availablePlugins.length && options.noItemsElement) { - options.noItemsElement.classList.remove('hide'); - } - - options.catalogElement.innerHTML = html; - loading.hide(); + if (!availablePlugins.length && options.noItemsElement) { + options.noItemsElement.classList.remove('hide'); } - function getPluginHtml(plugin, options, installedPlugins) { - var html = ''; - var href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid; + options.catalogElement.innerHTML = html; + loading.hide(); +} - if (options.context) { - href += '&context=' + options.context; - } +function getPluginHtml(plugin, options, installedPlugins) { + let html = ''; + let href = plugin.externalUrl ? plugin.externalUrl : 'addplugin.html?name=' + encodeURIComponent(plugin.name) + '&guid=' + plugin.guid; - var target = plugin.externalUrl ? ' target="_blank"' : ''; - html += "
"; - html += '
'; - html += '
'; - html += '
'; - html += ''; - html += ''; - html += ''; - html += '
'; - html += '
'; - html += "
"; - html += plugin.name; - html += '
'; - var installedPlugin = installedPlugins.filter(function (ip) { - return ip.Id == plugin.guid; - })[0]; - html += "
"; - html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' '; - html += '
'; - html += '
'; - html += '
'; - return html += '
'; + if (options.context) { + href += '&context=' + options.context; } - function getTabs() { - return [{ - href: 'installedplugins.html', - name: globalize.translate('TabMyPlugins') - }, { - href: 'availableplugins.html', - name: globalize.translate('TabCatalog') - }, { - href: 'repositories.html', - name: globalize.translate('TabRepositories') - }]; - } + const target = plugin.externalUrl ? ' target="_blank"' : ''; + html += "
"; + html += '
'; + html += '
'; + html += '
'; + html += ''; + html += ''; + html += ''; + html += '
'; + html += '
'; + html += "
"; + html += plugin.name; + html += '
'; + const installedPlugin = installedPlugins.filter(function (ip) { + return ip.Id == plugin.guid; + })[0]; + html += "
"; + html += installedPlugin ? globalize.translate('LabelVersionInstalled', installedPlugin.Version) : ' '; + html += '
'; + html += '
'; + html += '
'; + return html += '
'; +} - window.PluginCatalog = { - renderCatalog: populateList - }; +function getTabs() { + return [{ + href: 'installedplugins.html', + name: globalize.translate('TabMyPlugins') + }, { + href: 'availableplugins.html', + name: globalize.translate('TabCatalog') + }, { + href: 'repositories.html', + name: globalize.translate('TabRepositories') + }]; +} - return function (view, params) { - view.addEventListener('viewshow', function () { - libraryMenu.setTabs('plugins', 1, getTabs); - reloadList(this); - }); - }; -}); +export default function (view) { + view.addEventListener('viewshow', function () { + libraryMenu.setTabs('plugins', 1, getTabs); + reloadList(this); + }); +} diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index 80577d227b..20cbe56928 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -1,192 +1,193 @@ -define(['loading', 'libraryMenu', 'dom', 'globalize', 'cardStyle', 'emby-button'], function (loading, libraryMenu, dom, globalize) { - 'use strict'; +import loading from 'loading'; +import libraryMenu from 'libraryMenu'; +import dom from 'dom'; +import globalize from 'globalize'; +import 'cardStyle'; +import 'emby-button'; - loading = loading.default || loading; +function deletePlugin(page, uniqueid, name) { + const msg = globalize.translate('UninstallPluginConfirmation', name); - function deletePlugin(page, uniqueid, name) { - var msg = globalize.translate('UninstallPluginConfirmation', name); - - require(['confirm'], function (confirm) { - confirm.default({ - title: globalize.translate('HeaderUninstallPlugin'), - text: msg, - primary: 'delete', - confirmText: globalize.translate('HeaderUninstallPlugin') - }).then(function () { - loading.show(); - ApiClient.uninstallPlugin(uniqueid).then(function () { - reloadList(page); - }); + import('confirm').then(({default: confirm}) => { + confirm.default({ + title: globalize.translate('UninstallPluginHeader'), + text: msg, + primary: 'delete', + confirmText: globalize.translate('UninstallPluginHeader') + }).then(function () { + loading.show(); + ApiClient.uninstallPlugin(uniqueid).then(function () { + reloadList(page); }); }); - } + }); +} - function showNoConfigurationMessage() { - Dashboard.alert({ - message: globalize.translate('MessageNoPluginConfiguration') - }); - } +function showNoConfigurationMessage() { + Dashboard.alert({ + message: globalize.translate('NoPluginConfigurationMessage') + }); +} - function showConnectMessage() { - Dashboard.alert({ - message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess') - }); - } +function showConnectMessage() { + Dashboard.alert({ + message: globalize.translate('MessagePluginConfigurationRequiresLocalAccess') + }); +} - function getPluginCardHtml(plugin, pluginConfigurationPages) { - var configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) { - return pluginConfigurationPage.PluginId == plugin.Id; - })[0]; - var configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null; - var html = ''; - html += "
"; - html += '
'; - html += '
'; - html += '
'; - html += configPageUrl ? '' : ''; +function getPluginCardHtml(plugin, pluginConfigurationPages) { + const configPage = pluginConfigurationPages.filter(function (pluginConfigurationPage) { + return pluginConfigurationPage.PluginId == plugin.Id; + })[0]; + const configPageUrl = configPage ? Dashboard.getConfigurationPageUrl(configPage.Name) : null; + let html = ''; + html += "
"; + html += '
'; + html += '
'; + html += '
'; + html += configPageUrl ? '' : ''; + html += '
'; + html += '
'; + + if (configPage || plugin.CanUninstall) { + html += '
'; + html += ''; html += '
'; - html += '
'; + } - if (configPage || plugin.CanUninstall) { - html += '
'; - html += ''; - html += '
'; + html += "
"; + html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name; + html += '
'; + html += "
"; + html += plugin.Version; + html += '
'; + html += '
'; + html += '
'; + html += '
'; + return html; +} + +function renderPlugins(page, plugins) { + ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) { + populateList(page, plugins, configPages); + }); +} + +function populateList(page, plugins, pluginConfigurationPages) { + plugins = plugins.sort(function (plugin1, plugin2) { + if (plugin1.Name > plugin2.Name) { + return 1; } - html += "
"; - html += configPage && configPage.DisplayName ? configPage.DisplayName : plugin.Name; - html += '
'; - html += "
"; - html += plugin.Version; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - return html; - } - - function renderPlugins(page, plugins) { - ApiClient.getJSON(ApiClient.getUrl('web/configurationpages') + '?pageType=PluginConfiguration').then(function (configPages) { - populateList(page, plugins, configPages); - }); - } - - function populateList(page, plugins, pluginConfigurationPages) { - plugins = plugins.sort(function (plugin1, plugin2) { - if (plugin1.Name > plugin2.Name) { - return 1; - } - - return -1; - }); - - var html = plugins.map(function (p) { - return getPluginCardHtml(p, pluginConfigurationPages); - }).join(''); - - var installedPluginsElement = page.querySelector('.installedPlugins'); - installedPluginsElement.removeEventListener('click', onInstalledPluginsClick); - installedPluginsElement.addEventListener('click', onInstalledPluginsClick); - - if (plugins.length) { - installedPluginsElement.classList.add('itemsContainer'); - installedPluginsElement.classList.add('vertical-wrap'); - } else { - html += '
'; - html += '

' + globalize.translate('MessageNoPluginsInstalled') + '

'; - html += '

'; - html += globalize.translate('MessageBrowsePluginCatalog'); - html += '

'; - html += '
'; - } - - installedPluginsElement.innerHTML = html; - loading.hide(); - } - - function showPluginMenu(page, elem) { - var card = dom.parentWithClass(elem, 'card'); - var id = card.getAttribute('data-id'); - var name = card.getAttribute('data-name'); - var removable = card.getAttribute('data-removable'); - var configHref = card.querySelector('.cardContent').getAttribute('href'); - var menuItems = []; - - if (configHref) { - menuItems.push({ - name: globalize.translate('ButtonSettings'), - id: 'open', - icon: 'mode_edit' - }); - } - - if (removable === 'true') { - menuItems.push({ - name: globalize.translate('ButtonUninstall'), - id: 'delete', - icon: 'delete' - }); - } - - require(['actionsheet'], function (actionsheet) { - actionsheet.show({ - items: menuItems, - positionTo: elem, - callback: function (resultId) { - switch (resultId) { - case 'open': - Dashboard.navigate(configHref); - break; - case 'delete': - deletePlugin(page, id, name); - break; - } - } - }); - }); - } - - function reloadList(page) { - loading.show(); - ApiClient.getInstalledPlugins().then(function (plugins) { - renderPlugins(page, plugins); - }); - } - - function getTabs() { - return [{ - href: 'installedplugins.html', - name: globalize.translate('TabMyPlugins') - }, { - href: 'availableplugins.html', - name: globalize.translate('TabCatalog') - }, { - href: 'repositories.html', - name: globalize.translate('TabRepositories') - }]; - } - - function onInstalledPluginsClick(e) { - if (dom.parentWithClass(e.target, 'noConfigPluginCard')) { - showNoConfigurationMessage(); - } else if (dom.parentWithClass(e.target, 'connectModePluginCard')) { - showConnectMessage(); - } else { - var btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu'); - if (btnCardMenu) { - showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu); - } - } - } - - pageIdOn('pageshow', 'pluginsPage', function () { - libraryMenu.setTabs('plugins', 0, getTabs); - reloadList(this); + return -1; }); - window.PluginsPage = { - renderPlugins: renderPlugins - }; + let html = plugins.map(function (p) { + return getPluginCardHtml(p, pluginConfigurationPages); + }).join(''); + + const installedPluginsElement = page.querySelector('.installedPlugins'); + installedPluginsElement.removeEventListener('click', onInstalledPluginsClick); + installedPluginsElement.addEventListener('click', onInstalledPluginsClick); + + if (plugins.length) { + installedPluginsElement.classList.add('itemsContainer'); + installedPluginsElement.classList.add('vertical-wrap'); + } else { + html += '
'; + html += '

' + globalize.translate('MessageNoPluginsInstalled') + '

'; + html += '

'; + html += globalize.translate('BrowsePluginCatalogMessage'); + html += '

'; + html += '
'; + } + + installedPluginsElement.innerHTML = html; + loading.hide(); +} + +function showPluginMenu(page, elem) { + const card = dom.parentWithClass(elem, 'card'); + const id = card.getAttribute('data-id'); + const name = card.getAttribute('data-name'); + const removable = card.getAttribute('data-removable'); + const configHref = card.querySelector('.cardContent').getAttribute('href'); + const menuItems = []; + + if (configHref) { + menuItems.push({ + name: globalize.translate('ButtonSettings'), + id: 'open', + icon: 'mode_edit' + }); + } + + if (removable === 'true') { + menuItems.push({ + name: globalize.translate('ButtonUninstall'), + id: 'delete', + icon: 'delete' + }); + } + + import('actionsheet').then(({default: actionsheet}) => { + actionsheet.show({ + items: menuItems, + positionTo: elem, + callback: function (resultId) { + switch (resultId) { + case 'open': + Dashboard.navigate(configHref); + break; + case 'delete': + deletePlugin(page, id, name); + break; + } + } + }); + }); +} + +function reloadList(page) { + loading.show(); + ApiClient.getInstalledPlugins().then(function (plugins) { + renderPlugins(page, plugins); + }); +} + +function getTabs() { + return [{ + href: 'installedplugins.html', + name: globalize.translate('TabMyPlugins') + }, { + href: 'availableplugins.html', + name: globalize.translate('TabCatalog') + }, { + href: 'repositories.html', + name: globalize.translate('TabRepositories') + }]; +} + +function onInstalledPluginsClick(e) { + if (dom.parentWithClass(e.target, 'noConfigPluginCard')) { + showNoConfigurationMessage(); + } else if (dom.parentWithClass(e.target, 'connectModePluginCard')) { + showConnectMessage(); + } else { + const btnCardMenu = dom.parentWithClass(e.target, 'btnCardMenu'); + if (btnCardMenu) { + showPluginMenu(dom.parentWithClass(btnCardMenu, 'page'), btnCardMenu); + } + } +} + +pageIdOn('pageshow', 'pluginsPage', function () { + libraryMenu.setTabs('plugins', 0, getTabs); + reloadList(this); }); + +window.PluginsPage = { + renderPlugins: renderPlugins +}; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 4dffc49618..5c7e1e267d 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1282,6 +1282,8 @@ "PleaseRestartServerName": "Please restart Jellyfin Server - {0}.", "PleaseSelectTwoItems": "Please select at least two items.", "MessagePluginInstalled": "The plugin has been successfully installed. Jellyfin Server will need to be restarted for changes to take effect.", + "MessagePluginIntallError": "An error occured while installing the plugin.", + "MessageGetInstalledPluginsError": "An error occured while getting the list of currently installed plugins.", "PreferEmbeddedTitlesOverFileNames": "Prefer embedded titles over filenames", "PreferEmbeddedTitlesOverFileNamesHelp": "This determines the default display title when no internet metadata or local metadata is available.", "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "This uses the episode information from the embedded metadata if available.", From 3180a7a177e104106da5dd08b29c5bd9087252c8 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 6 Aug 2020 23:02:00 +0200 Subject: [PATCH 134/155] Fix lint --- src/controllers/dashboard/plugins/available/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/controllers/dashboard/plugins/available/index.js b/src/controllers/dashboard/plugins/available/index.js index aa25320d5c..22b28e0f5a 100644 --- a/src/controllers/dashboard/plugins/available/index.js +++ b/src/controllers/dashboard/plugins/available/index.js @@ -22,13 +22,14 @@ function reloadList(page) { function getHeaderText(category) { category = category.replace(' ', ''); - if ('Channel' === category) { + // TODO: Replace with switch + if (category === 'Channel') { category = 'Channels'; - } else if ('Theme' === category) { + } else if (category === 'Theme') { category = 'Themes'; - } else if ('LiveTV' === category) { + } else if (category === 'LiveTV') { category = 'HeaderLiveTV'; - } else if ('ScreenSaver' === category) { + } else if (category === 'ScreenSaver') { category = 'HeaderScreenSavers'; } From 232ba8182ad28d506727d291a6cefbe42be27427 Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Fri, 7 Aug 2020 23:58:01 +0200 Subject: [PATCH 135/155] Update src/controllers/dashboard/plugins/installed/index.js Co-authored-by: Cameron --- src/controllers/dashboard/plugins/installed/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index 20cbe56928..793adb70d2 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -25,7 +25,7 @@ function deletePlugin(page, uniqueid, name) { function showNoConfigurationMessage() { Dashboard.alert({ - message: globalize.translate('NoPluginConfigurationMessage') + message: globalize.translate('MessageNoPluginConfiguration') }); } From 5ccc24d8f54c8d178451a006190cc94e55dd05aa Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Fri, 7 Aug 2020 23:58:14 +0200 Subject: [PATCH 136/155] Update src/controllers/dashboard/plugins/installed/index.js Co-authored-by: Cameron --- src/controllers/dashboard/plugins/installed/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index 793adb70d2..ed0bbdaa7b 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -99,7 +99,7 @@ function populateList(page, plugins, pluginConfigurationPages) { html += '
'; html += '

' + globalize.translate('MessageNoPluginsInstalled') + '

'; html += '

'; - html += globalize.translate('BrowsePluginCatalogMessage'); + html += globalize.translate('MessageBrowsePluginCatalog'); html += '

'; html += '
'; } From 9c56306f05d20f03be14a7584a1d84596fbf6c09 Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Fri, 7 Aug 2020 23:58:21 +0200 Subject: [PATCH 137/155] Update src/controllers/dashboard/plugins/installed/index.js Co-authored-by: Cameron --- src/controllers/dashboard/plugins/installed/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index ed0bbdaa7b..ef36ceb3d3 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -10,7 +10,7 @@ function deletePlugin(page, uniqueid, name) { import('confirm').then(({default: confirm}) => { confirm.default({ - title: globalize.translate('UninstallPluginHeader'), + title: globalize.translate('HeaderUninstallPlugin'), text: msg, primary: 'delete', confirmText: globalize.translate('UninstallPluginHeader') From 2cbcddd5daa3d5d55e967c2cb3a354786e24d557 Mon Sep 17 00:00:00 2001 From: Julien Machiels Date: Fri, 7 Aug 2020 23:58:30 +0200 Subject: [PATCH 138/155] Update src/controllers/dashboard/plugins/installed/index.js Co-authored-by: Cameron --- src/controllers/dashboard/plugins/installed/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/dashboard/plugins/installed/index.js b/src/controllers/dashboard/plugins/installed/index.js index ef36ceb3d3..e5f5b92413 100644 --- a/src/controllers/dashboard/plugins/installed/index.js +++ b/src/controllers/dashboard/plugins/installed/index.js @@ -13,7 +13,7 @@ function deletePlugin(page, uniqueid, name) { title: globalize.translate('HeaderUninstallPlugin'), text: msg, primary: 'delete', - confirmText: globalize.translate('UninstallPluginHeader') + confirmText: globalize.translate('HeaderUninstallPlugin') }).then(function () { loading.show(); ApiClient.uninstallPlugin(uniqueid).then(function () { From c14b20727a7832b7c0f3bc2c32d929f2f43b8a5e Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 8 Aug 2020 16:48:25 +0200 Subject: [PATCH 139/155] Merge conditions in plugin install --- src/controllers/dashboard/plugins/add/index.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 8393970e76..875472a278 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -118,14 +118,12 @@ export default function(view, params) { })[0]; const version = $('#selectVersion', page).val(); - if (installedPlugin) { - if (installedPlugin.Version === version) { - loading.hide(); - Dashboard.alert({ - message: globalize.translate('MessageAlreadyInstalled'), - title: globalize.translate('HeaderPluginInstallation') - }); - } + if (installedPlugin && installedPlugin.Version === version) { + loading.hide(); + Dashboard.alert({ + message: globalize.translate('MessageAlreadyInstalled'), + title: globalize.translate('HeaderPluginInstallation') + }); } else { performInstallation(page, name, guid, version); } From faa78ebff14306164157df129cc014984b8e04db Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 8 Aug 2020 18:51:27 +0200 Subject: [PATCH 140/155] Fix string --- src/controllers/dashboard/plugins/add/index.js | 4 ++-- src/strings/en-us.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/dashboard/plugins/add/index.js b/src/controllers/dashboard/plugins/add/index.js index 875472a278..44c46ce11b 100644 --- a/src/controllers/dashboard/plugins/add/index.js +++ b/src/controllers/dashboard/plugins/add/index.js @@ -83,7 +83,7 @@ function performInstallation(page, name, guid, version) { loading.hide(); alertText(globalize.translate('MessagePluginInstalled')); }).catch(() => { - alertText(globalize.translate('MessagePluginIntallError')); + alertText(globalize.translate('MessagePluginInstallError')); }); }; @@ -95,7 +95,7 @@ function performInstallation(page, name, guid, version) { msg += globalize.translate('PleaseConfirmPluginInstallation'); import('confirm').then(({default: confirm}) => { - confirm.default(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () { + confirm(msg, globalize.translate('HeaderConfirmPluginInstallation')).then(function () { alertCallback(); }).catch(() => { console.debug('plugin not installed'); diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 5c7e1e267d..638be2efaf 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -1282,7 +1282,7 @@ "PleaseRestartServerName": "Please restart Jellyfin Server - {0}.", "PleaseSelectTwoItems": "Please select at least two items.", "MessagePluginInstalled": "The plugin has been successfully installed. Jellyfin Server will need to be restarted for changes to take effect.", - "MessagePluginIntallError": "An error occured while installing the plugin.", + "MessagePluginInstallError": "An error occured while installing the plugin.", "MessageGetInstalledPluginsError": "An error occured while getting the list of currently installed plugins.", "PreferEmbeddedTitlesOverFileNames": "Prefer embedded titles over filenames", "PreferEmbeddedTitlesOverFileNamesHelp": "This determines the default display title when no internet metadata or local metadata is available.", From c1a988f2c7e8a0bc70c3d2ec5af96ca11b1c240c Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 19:23:56 +0100 Subject: [PATCH 141/155] add default --- src/controllers/livetv/livetvsuggested.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/livetv/livetvsuggested.js b/src/controllers/livetv/livetvsuggested.js index 79d9aac6e9..fdc6037850 100644 --- a/src/controllers/livetv/livetvsuggested.js +++ b/src/controllers/livetv/livetvsuggested.js @@ -261,6 +261,8 @@ define(['layoutManager', 'userSettings', 'inputManager', 'loading', 'globalize', } require(depends, function (controllerFactory) { + controllerFactory = controllerFactory.default || controllerFactory; + var tabContent; if (index == 0) { From 14ee172231d50be81fe3e59314d594b059085c9f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2020 20:19:03 +0000 Subject: [PATCH 142/155] Bump @babel/eslint-parser from 7.11.0 to 7.11.3 Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.11.0 to 7.11.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.11.3/eslint/babel-eslint-parser) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d8164391cb..252c3e9008 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "devDependencies": { "@babel/core": "^7.11.1", - "@babel/eslint-parser": "^7.11.0", + "@babel/eslint-parser": "^7.11.3", "@babel/eslint-plugin": "^7.11.0", "@babel/plugin-proposal-class-properties": "^7.10.1", "@babel/plugin-proposal-private-methods": "^7.10.1", diff --git a/yarn.lock b/yarn.lock index 758ded53fc..4518e7af51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,10 +40,10 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/eslint-parser@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.0.tgz#b123924edd44508782c030066c926f1b807151cd" - integrity sha512-dJDM2Pc01D9TwKL3Mmz2xgVF9X953RBHq9H4gywbN1q8MrfvXmNHfsCt06vvByBVQqm+9WxMs+doEH/R09TwWQ== +"@babel/eslint-parser@^7.11.3": + version "7.11.3" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.3.tgz#ceb94cb6e2457c4a4d2d87db29925e6b48d20786" + integrity sha512-OdCt/CVXdR/eTNTYDEobf4e55m/AAc04ki+/Oe2/GE8ivh2FxX4yDab48lA6t7ysP4M7luap6Fxx3hUVNTwzFQ== dependencies: eslint-scope "5.1.0" eslint-visitor-keys "^1.3.0" From 4ffded957dbb50a20b20e6a58324da58a9f55399 Mon Sep 17 00:00:00 2001 From: Moritz Date: Sat, 8 Aug 2020 20:57:10 +0000 Subject: [PATCH 143/155] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/de/ --- src/strings/de.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/strings/de.json b/src/strings/de.json index 0aa4d2172f..b167c31707 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -1545,5 +1545,7 @@ "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:" + "LabelSubtitleVerticalPosition": "Vertikale Position:", + "MessageGetInstalledPluginsError": "Beim Abrufen der Liste der derzeit installierten Plugins ist ein Fehler aufgetreten.", + "MessagePluginInstallError": "Bei der Installation des Plugins ist ein Fehler aufgetreten." } From cfb22c0fb91e58c75ec26d21db45e35b33a619d3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 23:23:53 +0100 Subject: [PATCH 144/155] Apply suggestions from code review Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/scripts/livetvcomponents.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/livetvcomponents.js b/src/scripts/livetvcomponents.js index f1d0b2d3da..46fb714763 100644 --- a/src/scripts/livetvcomponents.js +++ b/src/scripts/livetvcomponents.js @@ -22,7 +22,7 @@ function getTimersHtml(timers, options) { let currentGroupName = ''; let currentGroup = []; - for (const item in items) { + for (const item of items) { let dateText = ''; if (options.indexByDate !== false && item.StartDate) { @@ -60,7 +60,7 @@ function getTimersHtml(timers, options) { }); } let html = ''; - for (const group in groups) { + for (const group of groups) { if (group.name) { html += '
'; html += '

' + group.name + '

'; From 1ede5e0d68afa4fc7ebeab1dc0f875eb1e9f5565 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 23:48:24 +0100 Subject: [PATCH 145/155] Update src/scripts/libraryBrowser.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/scripts/libraryBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/libraryBrowser.js b/src/scripts/libraryBrowser.js index 4f2a0fdb47..460e1b95ed 100644 --- a/src/scripts/libraryBrowser.js +++ b/src/scripts/libraryBrowser.js @@ -119,7 +119,7 @@ export function getQueryPagingHtml (options) { } export function showSortMenu (options) { - Promise.All([ + Promise.all([ import('dialogHelper'), import('emby-radio') ]).then(({default: dialogHelper}) => { From 10100ce63b818670d36ee0344d6022085821c896 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 8 Aug 2020 23:53:34 +0100 Subject: [PATCH 146/155] Update src/scripts/libraryBrowser.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/scripts/libraryBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/libraryBrowser.js b/src/scripts/libraryBrowser.js index 460e1b95ed..83d683a690 100644 --- a/src/scripts/libraryBrowser.js +++ b/src/scripts/libraryBrowser.js @@ -122,7 +122,7 @@ export function showSortMenu (options) { Promise.all([ import('dialogHelper'), import('emby-radio') - ]).then(({default: dialogHelper}) => { + ]).then(([{default: dialogHelper}]) => { function onSortByChange() { var newValue = this.value; From ad7879ad4d481df0f1dac86b0baead1cc88e7707 Mon Sep 17 00:00:00 2001 From: Nicode Jiao Date: Sun, 9 Aug 2020 03:28:41 +0000 Subject: [PATCH 147/155] Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/zh_Hans/ --- src/strings/zh-cn.json | 76 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index 553445404a..eed9f95599 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -159,7 +159,7 @@ "DetectingDevices": "正在侦测设备", "DeviceAccessHelp": "这仅适用于可以唯一标识的设备,而不会阻止浏览器访问。限制用户设备访问会阻止使用未在此被批准的新增设备。", "DirectPlaying": "直接播放", - "DirectStreamHelp2": "直接串流只占用占用很少的CPU并且视频的品质不会有任何损失。", + "DirectStreamHelp2": "直接串流只占用占用很少的CPU并且视频的品质只会有极小程度的损失。", "DirectStreaming": "直接串流", "Director": "导演", "Disabled": "已禁用", @@ -261,7 +261,7 @@ "HeaderAllowMediaDeletionFrom": "允许从中删除媒体", "HeaderApiKey": "API 密钥", "HeaderApiKeys": "API 密钥", - "HeaderApiKeysHelp": "外部应用程序需要 API 密钥才能与 Jellyfin Server 进行通信。使用 Jellyfin 账户进行登录时密钥将会自动生成,您也可以手动为某个应用程序分配一个密钥。", + "HeaderApiKeysHelp": "外部应用程序需要 API 密钥才能与服务器进行通信。密钥会在使用普通账户登录时自动生成,或是手动为应用分配。", "HeaderAudioBooks": "有声读物", "HeaderAudioSettings": "声音设置", "HeaderBlockItemsWithNoRating": "通过没有评级和设置不允许的评级锁定内容:", @@ -327,7 +327,7 @@ "HeaderInstall": "安装", "HeaderInstantMix": "速成合辑", "HeaderItems": "项目", - "HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据, 请在 Jellyfin 库安装程序中编辑库, 然后找到“元数据储户”部分。", + "HeaderKodiMetadataHelp": "要启用或禁用 NFO 元数据, 请编辑库, 然后找到“元数据储户”部分。", "HeaderLatestEpisodes": "最新剧集", "HeaderLatestMedia": "最新媒体", "HeaderLatestMovies": "最新电影", @@ -372,7 +372,7 @@ "HeaderPreferredMetadataLanguage": "首选元数据语言", "HeaderProfile": "配置", "HeaderProfileInformation": "配置信息", - "HeaderProfileServerSettingsHelp": "这些参数将控制 Jellyfin 媒体服务器如何呈现给设备。", + "HeaderProfileServerSettingsHelp": "这些参数将控制服务器如何将自己呈现给客户端。", "HeaderRecentlyPlayed": "最近播放", "HeaderRecordingOptions": "录制选项", "HeaderRecordingPostProcessing": "记录后处理", @@ -396,7 +396,7 @@ "HeaderSelectServerCachePath": "选择服务器缓存路径", "HeaderSelectServerCachePathHelp": "浏览或输入一个路径用于服务器缓存文件,此文件夹必须可写。", "HeaderSelectTranscodingPath": "选择临时解码路径", - "HeaderSelectTranscodingPathHelp": "浏览或输入一个路径用于临时转码,此文件夹必须可写。", + "HeaderSelectTranscodingPathHelp": "浏览或输入一个路径用于转码文件,此文件夹必须可写。", "HeaderSendMessage": "发送消息", "HeaderSeries": "电视剧", "HeaderSeriesOptions": "系列选项", @@ -445,8 +445,8 @@ "HttpsRequiresCert": "要启用安全连接, 您需要提供一个受信任的 SSL 证书, 例如 Let's Encrypt 。请提供证书或禁用安全连接。", "Identify": "识别", "Images": "图片", - "ImportFavoriteChannelsHelp": "如果启用,只有在协调器设备中被标记为我的最爱的频道才会被导入。", - "ImportMissingEpisodesHelp": "如果启用,会将缺少的剧集信息导入到你的 Jellyfin 数据库并分季分剧显示。可能会大大延长媒体库扫描时间。", + "ImportFavoriteChannelsHelp": "只有在协调器设备中被标记为我的最爱的频道才会被导入。", + "ImportMissingEpisodesHelp": "缺少的剧集信息将被导入到你的数据库并分季分剧显示。可能会大大延长媒体库扫描时间。", "InstallingPackage": "正在安装 {0}(版本 {1})", "InstantMix": "即时混音", "ItemCount": "{0} 项", @@ -476,14 +476,14 @@ "LabelAppName": "APP名称", "LabelAppNameExample": "例如:Sickbeard, Sonarr", "LabelArtists": "艺术家:", - "LabelArtistsHelp": "独立多功能 ;", + "LabelArtistsHelp": "将多个艺术家用分号分隔", "LabelAudioLanguagePreference": "首选音频语言:", "LabelAutomaticallyRefreshInternetMetadataEvery": "自动从互联网获取元数据并刷新:", "LabelBindToLocalNetworkAddress": "监听的本地网络地址:", - "LabelBindToLocalNetworkAddressHelp": "(可选的)覆盖 HTTP 服务器绑定的本地 IP 地址。如果留空,服务器将会监听所有可用的地址。改变这个值需要重启 Jellyfin 服务器。", + "LabelBindToLocalNetworkAddressHelp": "覆盖 HTTP 服务器绑定的本地 IP 地址。如果留空,服务器将会监听所有可用的地址。改变这个值需要重启 Jellyfin 服务器。", "LabelBirthDate": "出生日期:", "LabelBirthYear": "出生年份:", - "LabelBlastMessageInterval": "活动信号的时间间隔(秒)", + "LabelBlastMessageInterval": "活动信号的时间间隔", "LabelBlastMessageIntervalHelp": "确定爆炸活动消息之间的持续时间(以秒为单位)。", "LabelBlockContentWithTags": "通过标签锁定内容:", "LabelBurnSubtitles": "烧录字幕:", @@ -541,7 +541,7 @@ "LabelEnableAutomaticPortMapHelp": "通过UPnP将路由器端口自动转发到服务器端口。这可能不适用于某些型号的路由器和网络配置。需要服务器重新启动后才会应用更改。", "LabelEnableBlastAliveMessages": "爆发活动信号", "LabelEnableBlastAliveMessagesHelp": "如果该服务器不能被网络中的其他UPnP设备检测到,请启用此选项。", - "LabelEnableDlnaClientDiscoveryInterval": "客户端搜寻时间间隔(秒)", + "LabelEnableDlnaClientDiscoveryInterval": "客户端搜寻时间间隔", "LabelEnableDlnaClientDiscoveryIntervalHelp": "确定由 Jellyfin 执行的 SSDP 搜索之间的持续时间 (以秒为单位)。", "LabelEnableDlnaDebugLogging": "启用 DLNA 调试日志", "LabelEnableDlnaDebugLoggingHelp": "创建一个很大的日志文件,仅应在排除故障时使用。", @@ -567,9 +567,9 @@ "LabelForgotPasswordUsernameHelp": "输入你的用户名,如果你还记得。", "LabelFormat": "格式:", "LabelFriendlyName": "好记的名称:", - "LabelServerNameHelp": "此名称将用做服务器名,如果留空,将使用计算机名。", + "LabelServerNameHelp": "此名称将用做服务器名,默认使用服务器的主机名。", "LabelGroupMoviesIntoCollections": "批量添加电影到收藏", - "LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,属于一个收藏的电影将显示为一个分组。", + "LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一收藏的电影将显示为一个分组。", "LabelH264Crf": "H264 CRF 编码质量等级:", "LabelEncoderPreset": "H264 和 H265 编码预设:", "LabelHardwareAccelerationType": "硬件加速:", @@ -577,7 +577,7 @@ "LabelHomeNetworkQuality": "家庭网络质量:", "LabelHomeScreenSectionValue": "主屏幕模块{0}:", "LabelHttpsPort": "本地 HTTPS 端口号:", - "LabelHttpsPortHelp": "Jellyfin HTTPS 服务器监听端口。", + "LabelHttpsPortHelp": "HTTPS 服务器监听的 TCP 端口号。", "LabelIconMaxHeight": "图标最大高度:", "LabelIconMaxHeightHelp": "通过UPnP显示的图标最大分辨率。", "LabelIconMaxWidth": "图标最大宽度:", @@ -604,7 +604,7 @@ "LabelLanguage": "语言:", "LabelLineup": "排队:", "LabelLocalHttpServerPortNumber": "本地 HTTP 端口号:", - "LabelLocalHttpServerPortNumberHelp": "Jellyfin HTTP 服务器监听的 TCP 端口。", + "LabelLocalHttpServerPortNumberHelp": "HTTP 服务器监听的 TCP 端口号。", "LabelLockItemToPreventChanges": "锁定此项目防止改动", "LabelLoginDisclaimer": "登录声明:", "LabelLoginDisclaimerHelp": "将在登录页面底部显示的信息。", @@ -646,9 +646,9 @@ "LabelMovieCategories": "电影分类:", "LabelMoviePrefix": "电影前缀:", "LabelMoviePrefixHelp": "如果将前缀应用于影片标题, 请在此处输入它, 以便服务器可以正确处理它。", - "LabelMovieRecordingPath": "电影录制路径 (可选的):", + "LabelMovieRecordingPath": "电影录制路径:", "LabelMusicStreamingTranscodingBitrate": "音乐转码的比特率:", - "LabelMusicStreamingTranscodingBitrateHelp": "请指定一个音乐媒体串流时的最大比特率。", + "LabelMusicStreamingTranscodingBitrateHelp": "请指定音乐媒体串流时的最大比特率。", "LabelName": "名字:", "LabelNewName": "新名字:", "LabelNewPassword": "新密码:", @@ -659,7 +659,7 @@ "LabelNumber": "编号:", "LabelNumberOfGuideDays": "下载几天的节目指南:", "LabelNumberOfGuideDaysHelp": "下载更多天的节目指南可以帮你进一步查看节目列表并做出提前安排,但下载过程也将耗时更久。它将基于频道数量自动选择。", - "LabelOptionalNetworkPath": "(可选的)共享的网络文件夹:", + "LabelOptionalNetworkPath": "共享的网络文件夹:", "LabelOptionalNetworkPathHelp": "如果这个文件夹在你的网络上是共享的,提供这个网络共享地址能够允许其他设备上的 Jellyfin 应用程序直接访问媒体文件,例如 {0} 或者 {1}。", "LabelOriginalAspectRatio": "原始长宽比:", "LabelOriginalTitle": "原标题:", @@ -704,7 +704,7 @@ "LabelReleaseDate": "发行日期:", "LabelRemoteClientBitrateLimit": "互联网流媒体传输比特率限制(Mbps):", "LabelRemoteClientBitrateLimitHelp": "所有网络设备都有一个可选的每流比特率限制。这对于防止设备请求比 internet 连接所能处理的更高的比特率非常有用。这可能会导致服务器上的 CPU 负载增加, 以便将视频转码到较低的比特率。", - "LabelRuntimeMinutes": "播放时长(分钟):", + "LabelRuntimeMinutes": "播放时长:", "LabelSaveLocalMetadata": "将媒体图像保存到媒体所在文件夹", "LabelSaveLocalMetadataHelp": "直接将媒体图像保存到媒体所在文件夹以方便编辑。", "LabelScheduledTaskLastRan": "最后运行 {0}, 花费时间 {1}.", @@ -714,7 +714,7 @@ "LabelSelectVersionToInstall": "选择安装版本:", "LabelSendNotificationToUsers": "发送通知至:", "LabelSerialNumber": "序列号", - "LabelSeriesRecordingPath": "电视剧录制路径 (可选的):", + "LabelSeriesRecordingPath": "电视剧录制路径:", "LabelServerHost": "主机:", "LabelServerHostHelp": "192.168.1.100:8096 或 https://myserver.com", "LabelSimultaneousConnectionLimit": "并发流限制:", @@ -786,7 +786,7 @@ "LabelYoureDone": "完成!", "LabelZipCode": "邮编:", "LabelffmpegPath": "FFmpeg 路径:", - "LabelffmpegPathHelp": "FFmpeg 应用程序的文件,或者包含了 FFmpeg 的文件夹的路径。", + "LabelffmpegPathHelp": "FFmpeg 应用文件或包含 FFmpeg 的文件夹的路径。", "LanNetworksHelp": "在强制带宽限制时,认作本地网络上的 IP 地址或 IP/网络掩码条目的逗号分隔列表。如果设置此项,所有其它 IP 地址将被视为在外部网络上,并且将受到外部带宽限制。如果保留为空,则只将服务器的子网视为本地网络。", "Large": "大", "LatestFromLibrary": "最新的{0}", @@ -918,7 +918,7 @@ "OptionAllowLinkSharingHelp": "只有网页包含的媒体信息会被共享。媒体文件不会被公开共享。共享是有时间限制的并且会在 {0} 天后到期。", "OptionAllowManageLiveTv": "允许电视直播录制管理", "OptionAllowMediaPlayback": "允许播放媒体", - "OptionAllowMediaPlaybackTranscodingHelp": "由于不支持的媒体格式, 限制对代码转换的访问可能会导致 Jellyfin 应用程序中的播放失败。", + "OptionAllowMediaPlaybackTranscodingHelp": "限制对转码的访问可能会由于不支持的媒体格式导致客户端中播放失败。", "OptionAllowRemoteControlOthers": "允许其他用户全程控制", "OptionAllowRemoteSharedDevices": "允许远程控制共享的设备", "OptionAllowRemoteSharedDevicesHelp": "DLNA 设备在用户对他们进行控制前都被视为是共享的。", @@ -931,7 +931,7 @@ "OptionAuto": "自动", "OptionAutomatic": "自动", "OptionAutomaticallyGroupSeries": "自动合并分布在不同文件夹的电视剧", - "OptionAutomaticallyGroupSeriesHelp": "如果启用,分布在这个媒体库的多个文件夹中的同一部电视剧将会自动整合成一部电视剧。", + "OptionAutomaticallyGroupSeriesHelp": "在这个媒体库的多个文件夹中的同一部电视剧将会自动整合成一部电视剧。", "OptionBlockBooks": "书籍", "OptionBlockChannelContent": "互联网频道内容", "OptionBlockLiveTvChannels": "电视直播频道", @@ -952,7 +952,7 @@ "OptionDatePlayed": "播放日期", "OptionDescending": "降序", "OptionDisableUser": "禁用此用户", - "OptionDisableUserHelp": "如果禁用该用户,服务器将不允许该用户连接。现有的连接将被终止。", + "OptionDisableUserHelp": "服务器将不允许来自该用户的任何连接。现有的连接将立即被终止。", "OptionDislikes": "不喜欢", "OptionDisplayFolderView": "显示一个“文件夹”类别用于按文件夹分类浏览你的媒体文件夹", "OptionDisplayFolderViewHelp": "在你的媒体库列表中显示文件夹。如果你有按文件夹分类进行浏览的需求,这会非常有用。", @@ -962,7 +962,7 @@ "OptionDownloadBoxImage": "包装", "OptionDownloadDiscImage": "光盘", "OptionDownloadImagesInAdvance": "提前下载图片", - "OptionDownloadImagesInAdvanceHelp": "默认下,大部分图片只有在 Jellyfin 应用程序请求时下载。开启此选项将随着媒体导入时下载所有图片。这可能需要更久媒体库扫描时间。", + "OptionDownloadImagesInAdvanceHelp": "默认大多数图片只在客户端请求时下载。开启此选项将在新媒体导入时预先下载所有图片。这可能大大延长媒体库扫描时间。", "OptionDownloadMenuImage": "菜单", "OptionDownloadPrimaryImage": "封面图", "OptionDownloadThumbImage": "缩略图", @@ -994,7 +994,7 @@ "OptionHlsSegmentedSubtitles": "HLS分段字幕", "OptionHomeVideos": "照片", "OptionIgnoreTranscodeByteRangeRequests": "忽略转码字节范围请求", - "OptionIgnoreTranscodeByteRangeRequestsHelp": "如果启用,这些请求会被兑现,但会忽略的字节范围标头。", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "这些请求会被兑现,但会忽略的字节范围标头。", "OptionImdbRating": "IMDb 评分", "OptionIsHD": "HD高清", "OptionIsSD": "SD标清", @@ -1009,9 +1009,9 @@ "OptionOnInterval": "在一个期间", "OptionParentalRating": "家长分级", "OptionPlainStorageFolders": "显示所有文件夹作为一般存储文件夹", - "OptionPlainStorageFoldersHelp": "如果启用,所有文件夹在DIDL中显示为“ object.container.storageFolder ”,而不是一个更具体的类型,如“ object.container.person.musicArtist ” 。", + "OptionPlainStorageFoldersHelp": "所有文件夹在DIDL中显示为 \"object.container.storageFolder\" ,而不是一个更具体的类型,如 \"object.container.person.musicArtist\" 。", "OptionPlainVideoItems": "显示所有视频为一般视频项目", - "OptionPlainVideoItemsHelp": "如果启用,所有视频在DIDL中显示为“object.item.videoItem”,而不是一个更具体的类型,如“object.item.videoItem.movie ” 。", + "OptionPlainVideoItemsHelp": "所有视频在DIDL中显示为 \"object.item.videoItem\" ,而不是一个更具体的类型,如 \"object.item.videoItem.movie\" 。", "OptionPlayCount": "播放次数", "OptionPlayed": "已播放", "OptionPremiereDate": "首映日期", @@ -1316,7 +1316,7 @@ "ErrorDeletingItem": "从 Jellyfin Server 删除项目时出错。请确认 Jellyfin Server 是否拥有对媒体目录的写权限,然后重试。", "GroupBySeries": "按系列分组", "HeaderApp": "应用程序", - "DirectStreamHelp1": "该媒体文件的分辨率和编码(H.264、AC3 等)与您的设备兼容,但容器格式(.mkv、.avi、.wmv 等)不受支持。因此,视频在串流至您的设备之前将会被即时封装为另一种格式。", + "DirectStreamHelp1": "该媒体文件的分辨率和编码(H.264、AC3 等)与您的设备兼容,但文件格式(.mkv、.avi、.wmv 等)不受支持。因此,视频在串流至您的设备之前将会被即时封装为另一种格式。", "HeaderAppearsOn": "同时出现于", "HeaderCancelSeries": "取消系列", "HeaderFavoriteEpisodes": "最爱的剧集", @@ -1361,14 +1361,14 @@ "OptionDownloadLogoImage": "标志", "OptionLoginAttemptsBeforeLockout": "确定在锁定之前可以进行多少次不正确的登录尝试。", "OptionLoginAttemptsBeforeLockoutHelp": "如果值为0,则表示将允许普通用户尝试三次、管理员尝试五次的默认值。将此设置为-1将禁用此功能。", - "PasswordResetProviderHelp": "选择一个密码重置提供者用于密码重置", + "PasswordResetProviderHelp": "选择一个密码重置提供者用于此用户申请重置密码", "PlaceFavoriteChannelsAtBeginning": "将最喜爱的频道置顶", "PlayNext": "播放下一个", "PlayNextEpisodeAutomatically": "自动播放下一集", "Premieres": "首映", "Raised": "提高", "Recordings": "录音", - "RefreshDialogHelp": "元数据根据设置和Jellyfin服务器中启用的网络服务进行刷新。", + "RefreshDialogHelp": "元数据根据设置和仪表盘中启用的网络服务进行刷新。", "RepeatEpisodes": "重复剧集", "Schedule": "日程", "Screenshot": "屏幕截图", @@ -1421,7 +1421,7 @@ "ButtonAddImage": "添加图片", "LabelPlayer": "播放器:", "LabelBaseUrl": "基础 URL:", - "LabelBaseUrlHelp": "为服务器 URL添加自定义子目录,例如:http://example.com/<baseurl>。", + "LabelBaseUrlHelp": "为服务器 URL添加自定义子目录,例如:http://example.com/<baseurl>", "MusicLibraryHelp": "重播 {0}音乐命名指南{1}。", "HeaderFavoritePeople": "最喜欢的人物", "OptionRandom": "随机", @@ -1480,7 +1480,7 @@ "LabelRequireHttpsHelp": "开启后服务器将自动将所有 HTTP 请求重定向到 HTTPS。如果服务器没有启用 HTTPS 则不生效。", "LabelRequireHttps": "强制 HTTPS", "LabelStable": "稳定版", - "LabelEnableHttpsHelp": "开启服务器对所配置 HTTPS 端口的监听。必须配置有效的证书才会生效。", + "LabelEnableHttpsHelp": "监听已配置的 HTTPS 端口。必须配置有效的证书才会生效。", "LabelEnableHttps": "启用 HTTPS", "LabelChromecastVersion": "Chromecast版本", "HeaderDVR": "DVR", @@ -1539,5 +1539,13 @@ "ClearQueue": "清空队列", "StopPlayback": "停止播放", "Writers": "作者", - "ViewAlbumArtist": "查看专辑艺术家" + "ViewAlbumArtist": "查看专辑艺术家", + "Preview": "预览", + "SubtitleVerticalPositionHelp": "文字出现的行号。正数表示由上到下,负数表示由下到上。", + "LabelSubtitleVerticalPosition": "垂直位置:", + "PreviousTrack": "上一曲", + "MessageGetInstalledPluginsError": "获取已安装插件列表时出现错误。", + "MessagePluginInstallError": "安装插件时出现错误。", + "NextTrack": "下一曲", + "LabelUnstable": "不稳定" } From 754bf98d1645d24b3fdcd9b9c83b6c4c2ab04397 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 9 Aug 2020 10:46:37 +0100 Subject: [PATCH 148/155] Update src/components/recordingcreator/recordingcreator.js Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> --- src/components/recordingcreator/recordingcreator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/recordingcreator/recordingcreator.js b/src/components/recordingcreator/recordingcreator.js index b8001fc35e..f4ae64977d 100644 --- a/src/components/recordingcreator/recordingcreator.js +++ b/src/components/recordingcreator/recordingcreator.js @@ -122,7 +122,7 @@ function executeCloseAction(action, programId, serverId) { const apiClient = connectionManager.getApiClient(serverId); apiClient.getLiveTvProgram(programId, apiClient.getCurrentUserId()).then(function (item) { - playbackManager.default.play({ + playbackManager.play({ ids: [item.ChannelId], serverId: serverId }); From fa9c2757359a4f56a027049d3268b1c49211ec84 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 9 Aug 2020 12:41:16 +0100 Subject: [PATCH 149/155] fix ES6 errors --- src/plugins/chromecastPlayer/plugin.js | 2 +- src/scripts/libraryMenu.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index f38f9b04ff..b7e6d05969 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -584,7 +584,7 @@ class ChromecastPlayer { this.isLocalPlayer = false; this.lastPlayerData = {}; - new castSenderApiLoader.default().load().then(initializeChromecast.bind(this)); + new castSenderApiLoader().load().then(initializeChromecast.bind(this)); } tryPair(target) { diff --git a/src/scripts/libraryMenu.js b/src/scripts/libraryMenu.js index 0780916a7c..ec572752b9 100644 --- a/src/scripts/libraryMenu.js +++ b/src/scripts/libraryMenu.js @@ -850,7 +850,7 @@ import 'flexStyles'; let requiresUserRefresh = true; function setTabs (type, selectedIndex, builder) { - import('mainTabsManager').then(({default: mainTabsManager}) => { + import('mainTabsManager').then((mainTabsManager) => { if (type) { mainTabsManager.setTabs(viewManager.currentView(), selectedIndex, builder, function () { return []; From 7d066a6fcfd8c645b5e9530309b9cb57004279ec Mon Sep 17 00:00:00 2001 From: Thomas Schwery Date: Sun, 9 Aug 2020 10:10:50 +0000 Subject: [PATCH 150/155] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/strings/fr.json b/src/strings/fr.json index 20b96f3a52..e66cdc0edc 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -1545,5 +1545,7 @@ "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 :" + "LabelSubtitleVerticalPosition": "Position verticale :", + "MessageGetInstalledPluginsError": "Une erreur est survenue lors de la récupération de la liste des extensions installées.", + "MessagePluginInstallError": "Une erreur est survenue durant l'installation de l'extension." } From 475cc0e2532183e58466686f02f7e79e4bf1b39b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 9 Aug 2020 14:26:26 +0000 Subject: [PATCH 151/155] Bump @babel/eslint-plugin from 7.11.0 to 7.11.3 Bumps [@babel/eslint-plugin](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-plugin) from 7.11.0 to 7.11.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.11.3/eslint/babel-eslint-plugin) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 508fbd1307..fa27f5b951 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "devDependencies": { "@babel/core": "^7.11.1", "@babel/eslint-parser": "^7.11.3", - "@babel/eslint-plugin": "^7.11.0", + "@babel/eslint-plugin": "^7.11.3", "@babel/plugin-proposal-class-properties": "^7.10.1", "@babel/plugin-proposal-private-methods": "^7.10.1", "@babel/plugin-transform-modules-amd": "^7.10.5", diff --git a/yarn.lock b/yarn.lock index 4518e7af51..269b3b93cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -49,10 +49,10 @@ eslint-visitor-keys "^1.3.0" semver "^6.3.0" -"@babel/eslint-plugin@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.11.0.tgz#55d5b6bd29859cabce152f16d01b3a8150d5b295" - integrity sha512-+gfPM0/T6d25jKBgmxWp38W0jqRs16Vt7DPBxGOcnN/7nS2A/6QoaXOYEaccvWS5a9UpWlMIAylivp6UtH8/sQ== +"@babel/eslint-plugin@^7.11.3": + version "7.11.3" + resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.11.3.tgz#66b531f90592f8f0621d072b59ea2c37c91e8d0d" + integrity sha512-gmi3lgaWlYpNb+h7qPfv5GVz2ZVwzCDyV+kAGj+3il+Mv5uan5Yccvdw7m14UAAY2tdTbB0VgRF6ZLjUbrUm0g== dependencies: eslint-rule-composer "^0.3.0" From 988173fffc5e750e03a8c5b8e7199c787430b877 Mon Sep 17 00:00:00 2001 From: Felipe Date: Mon, 10 Aug 2020 01:25:23 +0000 Subject: [PATCH 152/155] Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es_419/ --- src/strings/es_419.json | 50 +++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/strings/es_419.json b/src/strings/es_419.json index 54dd68e9ed..94a4db6aa2 100644 --- a/src/strings/es_419.json +++ b/src/strings/es_419.json @@ -183,7 +183,7 @@ "OptionPlayCount": "Contador de reproducciones", "OptionPlainVideoItemsHelp": "Si se habilita, todos los videos serán representados en DIDL como «object.item.videoItem» en lugar de un tipo más específico, como «object.item.videoItem.movie».", "OptionPlainVideoItems": "Mostrar todos los videos como elementos de video simples", - "OptionPlainStorageFoldersHelp": "Si se habilita, todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».", + "OptionPlainStorageFoldersHelp": "Todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».", "OptionPlainStorageFolders": "Mostrar todas las carpetas como carpetas de almacenamiento simples", "OptionParentalRating": "Clasificación parental", "OptionOnInterval": "En un intervalo", @@ -201,7 +201,7 @@ "OptionIsSD": "SD", "OptionIsHD": "HD", "OptionImdbRating": "Calificación de IMDb", - "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes serán honradas pero se ignorará el encabezado de rango de bytes.", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Estas solicitudes serán consideradas pero se ignorará el encabezado de rango de bytes.", "OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificación de rango de bytes", "OptionHomeVideos": "Fotos", "OptionHlsSegmentedSubtitles": "Subtítulos segmentados HLS", @@ -234,7 +234,7 @@ "OptionDownloadPrimaryImage": "Principal", "OptionDownloadMenuImage": "Menú", "OptionDownloadLogoImage": "Logo", - "OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes solo son descargadas cuando son solicitadas por una aplicación Jellyfin. Habilita esta opción para descargar todas las imágenes por adelantado, a medida que se agreguen nuevos medios. Esto podría causar escaneos de bibliotecas significativamente más largos.", + "OptionDownloadImagesInAdvanceHelp": "Por defecto, la mayoría de las imágenes se descargan cuando son solicitadas por un cliente. Habilita esta opción para descargarlas todas por por adelantado a medida que se agreguen nuevos medios. Esto podría causar que los escaneos de bibliotecas sean significativamente más largos.", "OptionDownloadImagesInAdvance": "Descargar las imágenes con antelación", "OptionDownloadDiscImage": "Disco", "OptionDownloadBoxImage": "Caja", @@ -244,7 +244,7 @@ "OptionDisplayFolderViewHelp": "Muestra las carpetas junto con sus otras bibliotecas de medios. Esto puede ser útil si deseas tener una vista simple de carpeta.", "OptionDisplayFolderView": "Mostrar una vista de carpetas para mostrar las carpetas simples de los medios", "OptionDislikes": "No me gusta", - "OptionDisableUserHelp": "Si se desactiva, el servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.", + "OptionDisableUserHelp": "El servidor no aceptará conexiones de este usuario. Las conexiones existentes serán finalizadas abruptamente.", "OptionDisableUser": "Desactivar este usuario", "OptionDescending": "Descendente", "OptionDatePlayed": "Fecha de reproducción", @@ -263,7 +263,7 @@ "OptionBlockChannelContent": "Contenido de canales de Internet", "OptionBlockBooks": "Libros", "OptionBanner": "Banner", - "OptionAutomaticallyGroupSeriesHelp": "Si se habilita, las series que se reparten a través de múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.", + "OptionAutomaticallyGroupSeriesHelp": "Series que estén repartidas en múltiples carpetas dentro de esta biblioteca serán fusionadas en una sola serie.", "OptionAutomaticallyGroupSeries": "Fusionar automáticamente series esparcidas a través de múltiples carpetas", "OptionAutomatic": "Automático", "OptionAuto": "Automático", @@ -276,7 +276,7 @@ "OptionAllowRemoteSharedDevicesHelp": "Los dispositivos DLNA se considerarán compartidos hasta que un usuario comience a controlarlos.", "OptionAllowRemoteSharedDevices": "Permitir control remoto de dispositivos compartidos", "OptionAllowRemoteControlOthers": "Permitir control remoto de otros usuarios", - "OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en la reproducción en las aplicaciones Jellyfin debido a los formatos de medios no soportados.", + "OptionAllowMediaPlaybackTranscodingHelp": "Restringir el acceso a la transcodificación podría causar fallas en reproducción en las aplicaciones debido a formatos de medios no soportados.", "OptionAllowMediaPlayback": "Permitir reproducción de medios", "OptionAllowManageLiveTv": "Permitir gestión de grabación de TV en vivo", "OptionAllowLinkSharingHelp": "Solo son compartidas páginas web que contienen información sobre los medios. Los archivos de medios nunca son compartidos públicamente. Los compartidos tienen un límite de tiempo y expirarán después de {0} días.", @@ -333,7 +333,7 @@ "Mobile": "Móvil", "MinutesBefore": "minutos antes", "MinutesAfter": "minutos después", - "MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o realiza actualizaciones masivas usando el administrador de metadatos.", + "MetadataSettingChangeHelp": "Cambiar la configuración de los metadatos afectará al nuevo contenido que se añada en el futuro. Para actualizar el contenido existente, abre la pantalla de detalles y haz clic en el botón actualizar, o haz actualizaciones masivas usando el administrador de metadatos.", "MetadataManager": "Administrador de metadatos", "Metadata": "Metadatos", "MessageSyncPlayErrorMedia": "¡Fallo al activar SyncPlay! Error en el archivo de medios.", @@ -392,7 +392,7 @@ "LatestFromLibrary": "Últimas - {0}", "Large": "Grande", "LanNetworksHelp": "Lista separada por comas de direcciones IP o entradas de IP/máscara de red para las redes que se considerarán en la red local al aplicar las restricciones de ancho de banda. Si se establecen, todas las demás direcciones IP se considerarán como parte de la red externa y estarán sujetas a las restricciones de ancho de banda externa. Si se deja en blanco, solo se considera a la subred del servidor estar en la red local.", - "LabelffmpegPathHelp": "La ruta hacia el archivo de la aplicación ffmpeg, o la carpeta que contenga ffmpeg.", + "LabelffmpegPathHelp": "La ruta hacia el archivo ejecutable ffmpeg, o la carpeta que contenga ffmpeg.", "LabelffmpegPath": "Ruta del FFmpeg:", "LabelZipCode": "Código postal:", "LabelYoureDone": "¡Has terminado!", @@ -664,7 +664,7 @@ "LabelServerName": "Nombre del servidor:", "LabelServerHostHelp": "192.168.1.100:8096 o https://miservidor.com", "LabelServerHost": "Servidor:", - "LabelSeriesRecordingPath": "Ruta para las grabaciones de series (opcional):", + "LabelSeriesRecordingPath": "Ruta para las grabaciones de series:", "LabelSerialNumber": "Número de serie", "LabelSendNotificationToUsers": "Enviar la notificación a:", "LabelSelectVersionToInstall": "Seleccionar versión a instalar:", @@ -676,7 +676,7 @@ "LabelScheduledTaskLastRan": "Última ejecución {0}, tomando {1}.", "LabelSaveLocalMetadataHelp": "Guardar ilustraciones en las carpetas de los medios los colocará en un lugar donde se pueden editar fácilmente.", "LabelSaveLocalMetadata": "Guardar las ilustraciones en las carpetas de los medios", - "LabelRuntimeMinutes": "Duración (minutos):", + "LabelRuntimeMinutes": "Duración:", "LabelRequireHttpsHelp": "Si se marca, el servidor redirigirá automáticamente todas las solicitudes a través de HTTP a HTTPS. Esto no tiene efecto si el servidor no está escuchando en HTTPS.", "LabelRequireHttps": "Requerir HTTPS", "LabelRemoteClientBitrateLimitHelp": "Un límite opcional de velocidad de bits por transmisión para todos los dispositivos fuera de la red. Esto es útil para evitar que los dispositivos soliciten una tasa de bits más alta de la que puede manejar tu conexión a Internet. Esto puede provocar un aumento de la carga de la CPU en el servidor para transcodificar los videos sobre la marcha a una velocidad de bits inferior.", @@ -728,7 +728,7 @@ "LabelOriginalTitle": "Título original:", "LabelOriginalAspectRatio": "Relación de aspecto original:", "LabelOptionalNetworkPathHelp": "Si esta carpeta es compartida en su red, proveer la ruta del recurso compartido de red puede permitir a las aplicaciones Jellyfin en otros dispositivos acceder a los archivos de medios directamente. Por ejemplo, {0} o {1}.", - "LabelOptionalNetworkPath": "(Opcional) Carpeta de red compartida:", + "LabelOptionalNetworkPath": "Carpeta de red compartida:", "LabelNumberOfGuideDaysHelp": "Descargar más días de datos de programación permite programar con mayor anticipación y ver más listados, pero tomará más tiempo en descargar. Auto hará la selección basada en el número de canales.", "LabelNumberOfGuideDays": "Número de días de datos de la programación a descargar:", "LabelNumber": "Número:", @@ -741,9 +741,9 @@ "LabelStable": "Estable", "LabelChromecastVersion": "Versión de Chromecast", "LabelName": "Nombre:", - "LabelMusicStreamingTranscodingBitrateHelp": "Especifica la velocidad de bits máxima al transmitir música.", + "LabelMusicStreamingTranscodingBitrateHelp": "Especifica la máxima velocidad de bits al transmitir música.", "LabelMusicStreamingTranscodingBitrate": "Velocidad de bits de transcodificación de música:", - "LabelMovieRecordingPath": "Ruta para las grabaciones de películas (opcional):", + "LabelMovieRecordingPath": "Ruta para las grabaciones de películas:", "LabelMoviePrefixHelp": "Si un prefijo es aplicado al título de las películas, introdúcelo aquí para que el servidor pueda manejarlo correctamente.", "LabelMoviePrefix": "Prefijo de la película:", "LabelMovieCategories": "Categorías de películas:", @@ -941,11 +941,11 @@ "LabelBurnSubtitles": "Quemar subtítulos:", "LabelBlockContentWithTags": "Bloquear elementos con las etiquetas:", "LabelBlastMessageIntervalHelp": "Determina la duración en segundos del intervalo entre mensajes de vida.", - "LabelBlastMessageInterval": "Intervalo de mensajes de vida (segundos)", + "LabelBlastMessageInterval": "Intervalo de mensajes de vida", "LabelBitrate": "Velocidad de bits:", "LabelBirthYear": "Año de nacimiento:", "LabelBirthDate": "Fecha de nacimiento:", - "LabelBindToLocalNetworkAddressHelp": "Opcional. Sobrescribe la dirección IP local a la que se vincula el servidor http. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Cambiar este valor requiere reiniciar el servidor Jellyfin.", + "LabelBindToLocalNetworkAddressHelp": "Sobrescribe la dirección IP local del servidor HTTP. Si se deja vacío, el servidor se vinculará a todas las direcciones disponibles. Para cambiar este valor se necesita reiniciar el servidor.", "LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:", "LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar automáticamente los metadatos desde Internet:", "LabelAuthProvider": "Proveedor de autenticación:", @@ -956,7 +956,7 @@ "LabelAudioBitrate": "Velocidad de bits de audio:", "LabelAudioBitDepth": "Profundidad de bits de audio:", "LabelAudio": "Audio", - "LabelArtistsHelp": "Separar múltiples empleando ;", + "LabelArtistsHelp": "Separar múltiples artistas por punto y coma.", "LabelArtists": "Artistas:", "LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr", "LabelAppName": "Nombre de la aplicación", @@ -987,8 +987,8 @@ "ItemCount": "{0} elementos", "InstantMix": "Mix instantáneo", "InstallingPackage": "Instalando {0} (versión {1})", - "ImportMissingEpisodesHelp": "Si se habilita, la información sobre los episodios faltantes se importará a la base de datos de Jellyfin y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.", - "ImportFavoriteChannelsHelp": "Si se habilita, solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.", + "ImportMissingEpisodesHelp": "La información sobre los episodios faltantes se importará a la base de datos y se mostrarán dentro de las temporadas y series. Esto puede causar escaneos de biblioteca significativamente más largos.", + "ImportFavoriteChannelsHelp": "Solo los canales marcados como favoritos en el dispositivo sintonizador serán importados.", "Images": "Imágenes", "Identify": "Identificar", "HttpsRequiresCert": "Para habilitar las conexiones seguras, necesitarás proporcionar un certificado SSL de confianza, como el de Let's Encrypt. Por favor, proporciona un certificado o desactiva las conexiones seguras.", @@ -1041,7 +1041,7 @@ "HeaderSelectServerCachePath": "Seleccionar ruta para la caché del servidor", "HeaderSelectServer": "Seleccionar servidor", "HeaderSelectPath": "Seleccionar ruta", - "HeaderSelectMetadataPathHelp": "Explora o introduce la ruta donde deseas almacenar los metadatos. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectMetadataPathHelp": "Explora o escribe la ruta donde deseas guardar los metadatos. Se tienen que tener permisos de escritura en esa carpeta.", "HeaderSelectMetadataPath": "Selecciona la ruta para los metadatos", "HeaderSelectCertificatePath": "Selecciona la ruta del certificado", "HeaderSecondsValue": "{0} segundos", @@ -1060,7 +1060,7 @@ "HeaderRecordingPostProcessing": "Post procesado de las grabaciones", "HeaderRecordingOptions": "Opciones de grabación", "HeaderRecentlyPlayed": "Reproducido recientemente", - "HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presentará al dispositivo.", + "HeaderProfileServerSettingsHelp": "Estos valores controlan cómo el servidor se presentará a los clientes.", "HeaderProfileInformation": "Información del perfil", "HeaderProfile": "Perfil", "HeaderPreferredMetadataLanguage": "Idioma preferido para los metadatos", @@ -1108,7 +1108,7 @@ "HeaderLatestMovies": "Últimas películas", "HeaderLatestMedia": "Últimos medios", "HeaderLatestEpisodes": "Últimos episodios", - "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edite una biblioteca en la configuración de bibliotecas de Jellyfin y ubica la sección grabadores de metadatos.", + "HeaderKodiMetadataHelp": "Para habilitar o deshabilitar los metadatos NFO, edita una biblioteca y ubica la sección de grabadores de metadatos.", "HeaderKeepSeries": "Conservar serie", "HeaderKeepRecording": "Conservar grabación", "HeaderItems": "Elementos", @@ -1226,7 +1226,7 @@ "HeaderAppearsOn": "Aparece en", "HeaderApp": "Aplicación", "ApiKeysCaption": "Lista de claves API actualmente habilitadas", - "HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor Jellyfin. Las claves se emiten al iniciar sesión con una cuenta Jellyfin, o al otorgar manualmente una clave a la aplicación.", + "HeaderApiKeysHelp": "Las aplicaciones externas deben tener una clave API para poder comunicarse con el servidor. Las claves se emiten al iniciar sesión con una cuenta de usuario, o al otorgar manualmente una clave a la aplicación.", "HeaderApiKeys": "Claves API", "HeaderApiKey": "Clave API", "HeaderAllowMediaDeletionFrom": "Permitir eliminación de medios de", @@ -1372,7 +1372,7 @@ "HeaderSeriesOptions": "Opciones de serie", "HeaderSeries": "Series", "HeaderSendMessage": "Enviar mensaje", - "HeaderSelectTranscodingPathHelp": "Explora o introduce la ruta a utilizar para los archivos temporales de transcodificación. Se debe tener permisos de escritura en dicha carpeta.", + "HeaderSelectTranscodingPathHelp": "Explora o escribe la ruta para los archivos de transcodificación. Se tienen que tener permisos de escritura en esa carpeta.", "HeaderSelectTranscodingPath": "Selecciona la ruta para los archivos temporales de transcodificación", "ConfirmDeleteItems": "Eliminar estos elementos los eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", "ConfirmDeleteItem": "Eliminar este elemento lo eliminará tanto del sistema como de tu biblioteca de medios. ¿Estás seguro de querer continuar?", @@ -1539,5 +1539,7 @@ "ButtonCast": "Emitir", "Writers": "Escritores", "ViewAlbumArtist": "Ver Álbum de Artista", - "TabRepositories": "Repositorios" + "TabRepositories": "Repositorios", + "NextTrack": "Saltar al siguiente", + "LabelUnstable": "Inestable" } From 8121cf69b8c71fb7f2eda51b225ad89f0cb87293 Mon Sep 17 00:00:00 2001 From: Felipe Date: Mon, 10 Aug 2020 01:48:27 +0000 Subject: [PATCH 153/155] Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es_419/ --- src/strings/es_419.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/strings/es_419.json b/src/strings/es_419.json index 94a4db6aa2..b1cc402d38 100644 --- a/src/strings/es_419.json +++ b/src/strings/es_419.json @@ -181,7 +181,7 @@ "OptionPoster": "Póster", "OptionPlayed": "Reproducido", "OptionPlayCount": "Contador de reproducciones", - "OptionPlainVideoItemsHelp": "Si se habilita, todos los videos serán representados en DIDL como «object.item.videoItem» en lugar de un tipo más específico, como «object.item.videoItem.movie».", + "OptionPlainVideoItemsHelp": "Todos los videos serán representados en DIDL como «object.item.videoItem» en vez de un tipo más específico, como «object.item.videoItem.movie».", "OptionPlainVideoItems": "Mostrar todos los videos como elementos de video simples", "OptionPlainStorageFoldersHelp": "Todos las carpetas serán representadas en DIDL como «object.container.storageFolder» en lugar de un tipo más específico, como «object.container.person.musicArtist».", "OptionPlainStorageFolders": "Mostrar todas las carpetas como carpetas de almacenamiento simples", @@ -563,7 +563,7 @@ "ReleaseDate": "Fecha de estreno", "RefreshQueued": "Actualización puesta en la cola.", "RefreshMetadata": "Actualizar metadatos", - "RefreshDialogHelp": "Los metadatos son actualizados basándose en las configuraciones y servicios de Internet que estén activados en el panel de control de tu servidor Jellyfin.", + "RefreshDialogHelp": "Los metadatos se actualizan según las configuraciones y servicios de internet que se habilitan en el panel de control.", "Refresh": "Actualizar", "Recordings": "Grabaciones", "RecordingScheduled": "Grabación programada.", @@ -1541,5 +1541,11 @@ "ViewAlbumArtist": "Ver Álbum de Artista", "TabRepositories": "Repositorios", "NextTrack": "Saltar al siguiente", - "LabelUnstable": "Inestable" + "LabelUnstable": "Inestable", + "Preview": "Vista previa", + "SubtitleVerticalPositionHelp": "Número de línea donde aparece el texto. Números positivos representan de arriba hacia abajo. Números negativos representan de abajo hacia arriba.", + "LabelSubtitleVerticalPosition": "Posición Vertical:", + "PreviousTrack": "Saltar al anterior", + "MessageGetInstalledPluginsError": "Ocurrió un error buscando la lista de plugins instalados.", + "MessagePluginInstallError": "Ocurrió un error instalando el plugin." } From 5d92a37134360c643aa6c7c29fcec4bca3afa519 Mon Sep 17 00:00:00 2001 From: Felipe Date: Mon, 10 Aug 2020 02:07:24 +0000 Subject: [PATCH 154/155] Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es_419/ --- src/strings/es_419.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/es_419.json b/src/strings/es_419.json index b1cc402d38..022d2c8635 100644 --- a/src/strings/es_419.json +++ b/src/strings/es_419.json @@ -1485,7 +1485,7 @@ "Backdrops": "Imágenes de fondo", "Backdrop": "Imagen de fondo", "Auto": "Auto", - "AuthProviderHelp": "Selecciona un proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", + "AuthProviderHelp": "Selecciona el proveedor de autenticación que se utilizará para autenticar la contraseña de este usuario.", "Audio": "Audio", "AttributeNew": "Nuevo", "AspectRatio": "Relación de aspecto", From c37a59f02b7b002bc46b4991fd1a060e289a2c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Rodr=C3=ADguez?= Date: Mon, 10 Aug 2020 07:50:49 +0000 Subject: [PATCH 155/155] Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es/ --- src/strings/es.json | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/strings/es.json b/src/strings/es.json index d720b515f4..7361015235 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -231,7 +231,7 @@ "HeaderAllowMediaDeletionFrom": "Permitir borrar contenido desde", "HeaderApiKey": "Clave API", "HeaderApiKeys": "Claves API", - "HeaderApiKeysHelp": "Las aplicaciones externas requieren de una clave API para comunicarse con el servidor Jellyfin. Las claves se facilitan iniciando sesión con una cuenta de Jellyfin, u otorgando manualmente una clave a la aplicación.", + "HeaderApiKeysHelp": "Las aplicaciones externas requieren de una clave API para comunicarse con el servidor. Las claves se facilitan iniciando sesión con una cuenta de usuario en Jellyfin, u otorgando manualmente una clave a la aplicación.", "HeaderAudioBooks": "Audiolibros", "HeaderAudioSettings": "Ajustes de audio", "HeaderBlockItemsWithNoRating": "Bloquear artículos sin valoraciones o si son desconocidas:", @@ -346,7 +346,7 @@ "HeaderPreferredMetadataLanguage": "Idioma preferido para las etiquetas", "HeaderProfile": "Perfil", "HeaderProfileInformation": "Información del perfil", - "HeaderProfileServerSettingsHelp": "Estos valores controlan como el servidor Jellyfin se presenta al dispositivo.", + "HeaderProfileServerSettingsHelp": "Estos valores controlan cómo el servidor será presentado a los clientes.", "HeaderRecentlyPlayed": "Reproducido recientemente", "HeaderRecordingOptions": "Ajustes de grabación", "HeaderRecordingPostProcessing": "Grabación post procesamiento", @@ -370,7 +370,7 @@ "HeaderSelectServerCachePath": "Seleccione la ruta para el caché del servidor", "HeaderSelectServerCachePathHelp": "Navega o introduce la ruta para alojar los archivos caché del servidor. Tienes que tener permisos de escritura en esa carpeta.", "HeaderSelectTranscodingPath": "Ruta para los archivos temporales de las conversiones", - "HeaderSelectTranscodingPathHelp": "Busca o escribe la ruta que se utilizará para guardar los archivos temporales que se generarán mientras se convierten los archivos. Jellyfin debe tener permisos de escritura en la carpeta.", + "HeaderSelectTranscodingPathHelp": "Busca o escribe la ruta que se utilizará para guardar los archivos que se generarán mientras se convierten los archivos. Jellyfin debe tener permisos de escritura en la carpeta.", "HeaderSendMessage": "Enviar mensaje", "HeaderSeries": "Series", "HeaderSeriesOptions": "Opciones de series", @@ -417,8 +417,8 @@ "HttpsRequiresCert": "Para activar la conexión segura, necesitas un certificado SSL de confianza, como Let's Encrypt. De lo contrario, desactive las conexiones seguras.", "Identify": "Identificar", "Images": "Imágenes", - "ImportFavoriteChannelsHelp": "Si está activado, sólo los canales guardados como favoritos en el sintonizador se importarán.", - "ImportMissingEpisodesHelp": "Si está activada, la información sobre los episodios que faltan se importará en su base de datos Jellyfin y se mostrará en temporadas y series. Esto puede causar exploraciones de bibliotecas significativamente más largas.", + "ImportFavoriteChannelsHelp": "Sólo los canales guardados como favoritos en el sintonizador se importarán.", + "ImportMissingEpisodesHelp": "La información sobre los episodios que faltan se importará en su base de datos y se mostrará en temporadas y series. Esto puede causar exploraciones de bibliotecas significativamente más largas.", "InstallingPackage": "Instalando {0} (versión {1})", "InstantMix": "Mix instantáneo", "ItemCount": "Elementos {0}", @@ -449,11 +449,11 @@ "LabelAppName": "Nombre de la aplicación", "LabelAppNameExample": "Ejemplo: Sickbeard, Sonarr", "LabelArtists": "Artistas:", - "LabelArtistsHelp": "Separar múltiples artistas usando ;", + "LabelArtistsHelp": "Separar múltiples artistas utilizando punto y coma.", "LabelAudioLanguagePreference": "Idioma de audio preferido:", "LabelAutomaticallyRefreshInternetMetadataEvery": "Actualizar las etiquetas automáticamente desde Internet:", "LabelBindToLocalNetworkAddress": "Vincular a la dirección de red local:", - "LabelBindToLocalNetworkAddressHelp": "Opcional. Anule la dirección IP local para enlazar el servidor HTTP. Si se deja vacío, el servidor se enlazará a todas las direcciones disponibles. Para cambiar este valor, debe reiniciar el servidor Jellyfin.", + "LabelBindToLocalNetworkAddressHelp": "Anule la dirección IP local para enlazar el servidor HTTP. Si se deja vacío, el servidor se enlazará a todas las direcciones disponibles. Para cambiar este valor, debe reiniciar el servidor Jellyfin.", "LabelBirthDate": "Fecha de nacimiento:", "LabelBirthYear": "Año de nacimiento:", "LabelBlastMessageInterval": "Intervalo para mensajes en vivo (segundos)", @@ -1228,7 +1228,7 @@ "DatePlayed": "Reproducido el", "Descending": "Descendiente", "DirectStreamHelp1": "El tipo de archivo (H.264, AC3, etc.) y la resolución son compatibles con el dispositivo, pero no el contenedor (mkv, avi, wmv, etc.). El vídeo será re-empaquetado al vuelo antes de transmitirlo al dispositivo.", - "DirectStreamHelp2": "La transmisión directa del archivo usa muy poco procesamiento sin ninguna pérdida de calidad en el vídeo.", + "DirectStreamHelp2": "La transmisión directa del archivo usa muy poco procesamiento sin mínima pérdida de calidad en el vídeo.", "Director": "Dirección de", "Directors": "Directores", "Display": "Mostrar", @@ -1539,5 +1539,11 @@ "MessageNoRepositories": "Sin repositorios.", "Writers": "Escritores", "StopPlayback": "Detener la reproducción", - "ClearQueue": "Borrar la cola" + "ClearQueue": "Borrar la cola", + "LabelSubtitleVerticalPosition": "Posición vertical:", + "PreviousTrack": "Saltar al anterior", + "MessageGetInstalledPluginsError": "Ha ocurrido un error al recuperar la lista de plugins instalados.", + "MessagePluginInstallError": "Ha ocurrido un error al instalar este plugin.", + "NextTrack": "Saltar al siguiente", + "LabelUnstable": "Inestable" }