From ea9e79f4d35446d527c49ae9059e240896037980 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 16 Feb 2020 12:17:13 +0900 Subject: [PATCH 01/43] add basic web client configuration --- .gitignore | 584 +--------------------------- src/components/apphost.js | 8 +- src/config.example.json | 3 + src/scripts/settings/webSettings.js | 30 ++ src/scripts/site.js | 1 + 5 files changed, 47 insertions(+), 579 deletions(-) create mode 100644 src/config.example.json create mode 100644 src/scripts/settings/webSettings.js diff --git a/.gitignore b/.gitignore index 2e12adf220..2bb5bc64d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,578 +1,10 @@ +# config +config.json -# Created by https://www.gitignore.io/api/node,rider,macos,linux,windows,visualstudio,visualstudiocode -# Edit at https://www.gitignore.io/?templates=node,rider,macos,linux,windows,visualstudio,visualstudiocode +# npm +dist +node_modules -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Dependency lockfile -package-lock.json - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -### Rider ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -### VisualStudio ### -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ -# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true -**/wwwroot/lib/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- Backup*.rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# End of https://www.gitignore.io/api/node,rider,macos,linux,windows,visualstudio,visualstudiocode - -# dist for webpack output -dist \ No newline at end of file +# ide +.idea +.vscode diff --git a/src/components/apphost.js b/src/components/apphost.js index 2a0b7b19e9..5058148ae6 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,4 +1,4 @@ -define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSettings, browser, events, htmlMediaHelper) { +define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], function (appSettings, browser, events, htmlMediaHelper, webSettings) { "use strict"; function getBaseProfileOptions(item) { @@ -276,10 +276,12 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet features.push("otherapppromotions"); features.push("displaymode"); features.push("targetblank"); - // allows users to connect to more than one server - //features.push("multiserver"); features.push("screensaver"); + if (webSettings.getMultiserver()) { + features.push("multiserver") + } + if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { features.push("subtitleappearancesettings"); } diff --git a/src/config.example.json b/src/config.example.json new file mode 100644 index 0000000000..1e79270943 --- /dev/null +++ b/src/config.example.json @@ -0,0 +1,3 @@ +{ + "multiserver": true +} diff --git a/src/scripts/settings/webSettings.js b/src/scripts/settings/webSettings.js new file mode 100644 index 0000000000..e5bafb0b9d --- /dev/null +++ b/src/scripts/settings/webSettings.js @@ -0,0 +1,30 @@ +define(['appStorage', 'events'], function (appStorage, events) { + 'use strict'; + + function readConfig(path, callback) { + var file = new XMLHttpRequest(); + file.overrideMimeType("application/json"); + file.open("GET", path, true); + file.onreadystatechange = function() { + if (file.readyState === 4 && file.status == "200") { + callback(file.responseText); + } + } + + file.send(null); + } + + var data = {}; + + function WebSettings() { + readConfig("/config.json", function(text) { + data = JSON.parse(text); + }); + } + + WebSettings.prototype.getMultiserver = function () { + return data.multiserver !== false; + }; + + return new WebSettings(); +}); diff --git a/src/scripts/site.js b/src/scripts/site.js index f26e6b68ba..e1ea480f5d 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -809,6 +809,7 @@ var AppInfo = {}; define("appSettings", [scriptsPath + "/settings/appSettings"], returnFirstDependency); define("userSettings", [scriptsPath + "/settings/userSettings"], returnFirstDependency); + define("webSettings", [scriptsPath + "/settings/webSettings"], returnFirstDependency); define("chromecastHelper", [componentsPath + "/chromecast/chromecasthelpers"], returnFirstDependency); define("mediaSession", [componentsPath + "/playback/mediasession"], returnFirstDependency); From ed1b35f50fcf7bb2746842ab0cb566d2e230b9f0 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 16 Feb 2020 22:11:36 +0900 Subject: [PATCH 02/43] update some strings and use fetch for the global config --- src/components/apphost.js | 2 +- .../displaysettings.template.html | 2 +- src/components/skinManager.js | 4 ++-- .../subtitlesettings/subtitlesettings.js | 13 ++--------- .../subtitlesettings.template.html | 11 +++++---- src/scripts/settings/appSettings.js | 16 ++++++------- src/scripts/settings/webSettings.js | 23 ++++--------------- src/strings/en-us.json | 16 ++++++------- 8 files changed, 34 insertions(+), 53 deletions(-) diff --git a/src/components/apphost.js b/src/components/apphost.js index 5058148ae6..0a9b9d638c 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -278,7 +278,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f features.push("targetblank"); features.push("screensaver"); - if (webSettings.getMultiserver()) { + if (webSettings.getMultiServer()) { features.push("multiserver") } diff --git a/src/components/displaysettings/displaysettings.template.html b/src/components/displaysettings/displaysettings.template.html index 16bbf0dd8a..4ef8c8b1ca 100644 --- a/src/components/displaysettings/displaysettings.template.html +++ b/src/components/displaysettings/displaysettings.template.html @@ -63,7 +63,7 @@
+
@@ -34,7 +34,6 @@
-

${HeaderSubtitleAppearance}

@@ -61,6 +60,7 @@
+
+
+
+
'; diff --git a/src/components/metadataeditor/metadataeditor.js b/src/components/metadataeditor/metadataeditor.js index e8736258fa..84b60b32a3 100644 --- a/src/components/metadataeditor/metadataeditor.js +++ b/src/components/metadataeditor/metadataeditor.js @@ -465,7 +465,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi var id = "txt1" + idInfo.Key; var formatString = idInfo.UrlFormatString || ''; - var labelText = globalize.translate('LabelDynamicExternalId').replace('{0}', idInfo.Name); + var fullName = idInfo.Name; + if (idInfo.Type) { + fullName = idInfo.Name + " " + globalize.translate(idInfo.Type); + } + + var labelText = globalize.translate("LabelDynamicExternalId").replace("{0}", fullName); html += '
'; html += '
'; diff --git a/src/strings/en-us.json b/src/strings/en-us.json index 62e8fa3f8f..d8953e871a 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -11,6 +11,8 @@ "AdditionalNotificationServices": "Browse the plugin catalog to install additional notification services.", "AirDate": "Air date", "Aired": "Aired", + "Album": "Album", + "AlbumArtist": "Album Artist", "Albums": "Albums", "Alerts": "Alerts", "All": "All", @@ -35,6 +37,7 @@ "Anytime": "Anytime", "AroundTime": "Around {0}", "Art": "Art", + "Artist": "Artist", "Artists": "Artists", "AsManyAsPossible": "As many as possible", "Ascending": "Ascending", @@ -55,6 +58,7 @@ "BookLibraryHelp": "Audio and text books are supported. Review the {0}book naming guide{1}.", "Books": "Books", "Box": "Box", + "BoxSet": "Box Set", "BoxRear": "Box (rear)", "Browse": "Browse", "BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins.", @@ -234,6 +238,7 @@ "EnableThemeVideosHelp": "Play theme videos in the background while browsing the library.", "Ended": "Ended", "EndsAtValue": "Ends at {0}", + "Episode": "Episode", "Episodes": "Episodes", "ErrorAddingListingsToSchedulesDirect": "There was an error adding the lineup to your Schedules Direct account. Schedules Direct only allows a limited number of lineups per account. You may need to log into the Schedules Direct website and remove others listings from your account before proceeding.", "ErrorAddingMediaPathToVirtualFolder": "There was an error adding the media path. Please ensure the path is valid and the Jellyfin Server process has access to that location.", @@ -1017,6 +1022,7 @@ "MoveLeft": "Move left", "MoveRight": "Move right", "MovieLibraryHelp": "Review the {0}movie naming guide{1}.", + "Movie": "Movie", "Movies": "Movies", "MusicAlbum": "Music Album", "MusicArtist": "Music Artist", @@ -1203,6 +1209,7 @@ "OptionWeekends": "Weekends", "OptionWeekly": "Weekly", "OriginalAirDateValue": "Original air date: {0}", + "OtherArtist": "Other Artist", "Overview": "Overview", "PackageInstallCancelled": "{0} installation cancelled.", "PackageInstallCompleted": "{0} installation completed.", @@ -1216,6 +1223,7 @@ "PasswordSaved": "Password saved.", "People": "People", "PerfectMatch": "Perfect match", + "Person": "Person", "Photos": "Photos", "PictureInPicture": "Picture in picture", "PinCodeResetComplete": "The pin code has been reset.", @@ -1267,6 +1275,7 @@ "RefreshMetadata": "Refresh metadata", "RefreshQueued": "Refresh queued.", "ReleaseDate": "Release date", + "ReleaseGroup": "Release Group", "RememberMe": "Remember me", "RemoveFromCollection": "Remove from collection", "RemoveFromPlaylist": "Remove from playlist", @@ -1297,6 +1306,7 @@ "SearchForMissingMetadata": "Search for missing metadata", "SearchForSubtitles": "Search for Subtitles", "SearchResults": "Search Results", + "Season": "Season", "SelectAdminUsername": "Please select a username for the admin account.", "SendMessage": "Send message", "Series": "Series", @@ -1412,6 +1422,7 @@ "TitleHardwareAcceleration": "Hardware Acceleration", "TitleHostingSettings": "Hosting Settings", "TitlePlayback": "Playback", + "Track": "Track", "TrackCount": "{0} tracks", "Trailers": "Trailers", "Transcoding": "Transcoding", From 8f1e1075c249e2e858f6626673e5c9f9118bf7c2 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 28 Mar 2020 11:36:58 +0100 Subject: [PATCH 07/43] Update CI to use NodeJS 12 --- .ci/azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 48f042d729..7c7801b866 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -33,7 +33,7 @@ jobs: - task: NodeTool@0 displayName: 'Install Node' inputs: - versionSpec: '10.x' + versionSpec: '12.x' - task: Cache@2 displayName: 'Check Cache' @@ -82,7 +82,7 @@ jobs: - task: NodeTool@0 displayName: 'Install Node' inputs: - versionSpec: '10.x' + versionSpec: '12.x' - task: Cache@2 displayName: 'Check Cache' From 8743edf0273b7aa75e90452518105f829c00327a Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 28 Mar 2020 12:11:46 +0100 Subject: [PATCH 08/43] Update dependencies --- package.json | 32 +++++----- yarn.lock | 162 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 124 insertions(+), 70 deletions(-) diff --git a/package.json b/package.json index 81dd250ab5..721520d1be 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "license": "GPL-2.0-or-later", "devDependencies": { "@babel/core": "^7.8.6", - "@babel/polyfill": "^7.8.7", "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/polyfill": "^7.8.7", "@babel/preset-env": "^7.8.6", "autoprefixer": "^9.7.4", "babel-loader": "^8.0.6", @@ -18,7 +18,7 @@ "cssnano": "^4.1.10", "del": "^5.1.0", "eslint": "^6.8.0", - "file-loader": "^5.0.2", + "file-loader": "^6.0.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", "gulp-cli": "^2.2.0", @@ -31,7 +31,7 @@ "gulp-sass": "^4.0.2", "gulp-sourcemaps": "^2.6.5", "gulp-terser": "^1.2.0", - "html-webpack-plugin": "^3.2.0", + "html-webpack-plugin": "^4.0.2", "node-sass": "^4.13.1", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", @@ -63,24 +63,30 @@ "page": "^1.11.5", "query-string": "^6.11.1", "resize-observer-polyfill": "^1.5.1", - "shaka-player": "^2.5.9", + "shaka-player": "^2.5.10", "sortablejs": "^1.10.2", "swiper": "^5.3.1", "webcomponents.js": "^0.7.24", "whatwg-fetch": "^3.0.0" }, "babel": { - "presets": ["@babel/preset-env"], - "overrides": [{ + "presets": [ + "@babel/preset-env" + ], + "overrides": [ + { "test": [ - "src/components/cardbuilder/cardBuilder.js", - "src/components/filedownloader.js", - "src/components/filesystem.js", - "src/components/input/keyboardnavigation.js", - "src/components/sanatizefilename.js" + "src/components/cardbuilder/cardBuilder.js", + "src/components/filedownloader.js", + "src/components/filesystem.js", + "src/components/input/keyboardnavigation.js", + "src/components/sanatizefilename.js" ], - "plugins": ["@babel/plugin-transform-modules-amd"] - }] + "plugins": [ + "@babel/plugin-transform-modules-amd" + ] + } + ] }, "browserslist": [ "last 2 Firefox versions", diff --git a/yarn.lock b/yarn.lock index 8b7697cef4..546fbf639c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1649,11 +1649,6 @@ better-assert@~1.0.0: dependencies: callsite "1.0.0" -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2129,6 +2124,14 @@ camel-case@3.0.x: no-case "^2.2.0" upper-case "^1.1.1" +camel-case@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -2317,7 +2320,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@4.2.x: +clean-css@4.2.x, clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== @@ -2530,6 +2533,11 @@ commander@^2.2.0, commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -3463,6 +3471,14 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" +dot-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" + integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + dot-prop@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" @@ -3580,7 +3596,7 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -eme-encryption-scheme-polyfill@^2.0.0: +eme-encryption-scheme-polyfill@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.1.tgz#b080b01bffd74c75c9cf8044c1cabedf3b83954f" integrity sha512-Wz+Ro1c0/2Wsx2RLFvTOO0m4LvYn+7cSnq3XOvRvLLBq8jbvUACH/zpU9s0/5+mQa5oaelkU69x+q0z/iWYrFA== @@ -4271,13 +4287,13 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -file-loader@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-5.1.0.tgz#cb56c070efc0e40666424309bd0d9e45ac6f2bb8" - integrity sha512-u/VkLGskw3Ue59nyOwUwXI/6nuBCo7KBkniB/l7ICwr/7cPNGsL1WCXUp3GB0qgOOKU1TiP49bv4DZF/LJqprg== +file-loader@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f" + integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== dependencies: - loader-utils "^1.4.0" - schema-utils "^2.5.0" + loader-utils "^2.0.0" + schema-utils "^2.6.5" file-type@5.2.0, file-type@^5.2.0: version "5.2.0" @@ -5336,7 +5352,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -he@1.2.x: +he@1.2.x, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -5410,7 +5426,20 @@ html-entities@^1.2.1: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= -html-minifier@^3.2.3, html-minifier@^3.5.20: +html-minifier-terser@^5.0.1: + version "5.0.5" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.0.5.tgz#8f12f639789f04faa9f5cf2ff9b9f65607f21f8b" + integrity sha512-cBSFFghQh/uHcfSiL42KxxIRMF7A144+3E44xdlctIjxEmkEfCvouxNyFH2wysXk1fCGBPwtcr3hDWlGTfkDew== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-minifier@^3.5.20: version "3.5.21" resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== @@ -5433,17 +5462,16 @@ html-tags@^3.1.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== -html-webpack-plugin@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" - integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s= +html-webpack-plugin@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.2.tgz#c96a48d0ee53d33dcc909d6b65ad28f3d627efd4" + integrity sha512-dCyjg2dEBf0Azni2byDcwfk5l5XKNEnA3OU4cejovqkKGc4ZixC6Aw6+U2sAG/ellHIjoiQhyU4oKMO6fQFaYA== dependencies: - html-minifier "^3.2.3" - loader-utils "^0.2.16" - lodash "^4.17.3" - pretty-error "^2.0.2" - tapable "^1.0.0" - toposort "^1.0.0" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + pretty-error "^2.1.1" + tapable "^1.1.3" util.promisify "1.0.0" htmlparser2@^3.10.0, htmlparser2@^3.3.0: @@ -6373,11 +6401,6 @@ json3@^3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -6535,7 +6558,7 @@ levn@^0.3.0, levn@~0.3.0: "libass-wasm@https://github.com/jellyfin/JavascriptSubtitlesOctopus": version "4.0.0" - resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#1d12af0b04fb2337570b57d706dd97db2f904b9d" + resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#3269ee2b5442a495cc814164e1d70d87dc3cf2f9" liftoff@^3.1.0: version "3.1.0" @@ -6601,16 +6624,6 @@ loader-utils@1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" @@ -6620,6 +6633,15 @@ loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: emojis-list "^3.0.0" json5 "^1.0.1" +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + localtunnel@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af" @@ -6797,7 +6819,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.1.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.3, lodash@^4.17.4, lodash@~4.17.12: +lodash@^4.0.0, lodash@^4.1.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@~4.17.12: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -6866,6 +6888,13 @@ lower-case@^1.1.1: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= +lower-case@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" + integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + dependencies: + tslib "^1.10.0" + lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -7415,6 +7444,14 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +no-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" + integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== + dependencies: + lower-case "^2.0.1" + tslib "^1.10.0" + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -8014,6 +8051,14 @@ param-case@2.1.x: dependencies: no-case "^2.2.0" +param-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" + integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== + dependencies: + dot-case "^3.0.3" + tslib "^1.10.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -8108,6 +8153,14 @@ parseurl@~1.3.2, parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascal-case@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" + integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -9087,7 +9140,7 @@ pretty-bytes@^5.3.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== -pretty-error@^2.0.2: +pretty-error@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= @@ -9510,7 +9563,7 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" -relateurl@0.2.x: +relateurl@0.2.x, relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= @@ -9903,7 +9956,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.4, schema-utils@^2.6.5: +schema-utils@^2.6.0, schema-utils@^2.6.4, schema-utils@^2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== @@ -10103,12 +10156,12 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shaka-player@^2.5.9: - version "2.5.9" - resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.9.tgz#007dc19df2bb5d3d959d278b2d894af05adffe38" - integrity sha512-XavLBqxvIbvLOPfk7VKZu5fbMJyVko9bBfzxmMWdX5bvQwUSjU7ZhV8v2tHqXQYafpHml1hlGHzKkLs7idouNQ== +shaka-player@^2.5.10: + version "2.5.10" + resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-2.5.10.tgz#6f4e72e2433002d11824a223b02edd5004e30e2b" + integrity sha512-kS9TQL6bWODo4XNnozERZWsEiWlLZ6huspPx4ZjmMjeOBL9gwqlULLfLyO+5gA3CYV/dk9LaAi1WAEaLWckGpA== dependencies: - eme-encryption-scheme-polyfill "^2.0.0" + eme-encryption-scheme-polyfill "^2.0.1" shebang-command@^1.2.0: version "1.2.0" @@ -11077,7 +11130,7 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^4.0.0, terser@^4.1.2: +terser@^4.0.0, terser@^4.1.2, terser@^4.6.3: version "4.6.7" resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72" integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g== @@ -11248,11 +11301,6 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -toposort@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" - integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= - tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -11305,7 +11353,7 @@ trough@^1.0.0: dependencies: glob "^7.1.2" -tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.9.0: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== From cc66fb672c199270919cac651027eb1fabebabf5 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 28 Mar 2020 19:42:18 +0300 Subject: [PATCH 09/43] Migrate ScrollManger to ES6 --- package.json | 3 +- src/components/scrollManager.js | 239 +++++++++++++++++--------------- 2 files changed, 126 insertions(+), 116 deletions(-) diff --git a/package.json b/package.json index 81dd250ab5..5672ab6d2e 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,8 @@ "src/components/filedownloader.js", "src/components/filesystem.js", "src/components/input/keyboardnavigation.js", - "src/components/sanatizefilename.js" + "src/components/sanatizefilename.js", + "src/components/scrollManager.js" ], "plugins": ["@babel/plugin-transform-modules-amd"] }] diff --git a/src/components/scrollManager.js b/src/components/scrollManager.js index 5fc3729bac..96317fa998 100644 --- a/src/components/scrollManager.js +++ b/src/components/scrollManager.js @@ -1,15 +1,23 @@ -define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManager) { - "use strict"; +/* eslint-disable indent */ + +/** + * Module for controlling scroll behavior. + * @module components/scrollManager + */ + +import dom from "dom"; +import browser from "browser"; +import layoutManager from "layoutManager"; /** * Scroll time in ms. */ - var ScrollTime = 270; + const ScrollTime = 270; /** * Epsilon for comparing values. */ - var Epsilon = 1e-6; + const Epsilon = 1e-6; // FIXME: Need to scroll to top of page to fully show the top menu. This can be solved by some marker of top most elements or their containers /** @@ -19,20 +27,20 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage * @return {number} minimum vertical scroll */ function minimumScrollY() { - var topMenu = document.querySelector(".headerTop"); + const topMenu = document.querySelector(".headerTop"); if (topMenu) { return topMenu.clientHeight; } return 0; } - var supportsSmoothScroll = "scrollBehavior" in document.documentElement.style; + const supportsSmoothScroll = "scrollBehavior" in document.documentElement.style; - var supportsScrollToOptions = false; + let supportsScrollToOptions = false; try { - var elem = document.createElement("div"); + const elem = document.createElement("div"); - var opts = Object.defineProperty({}, "behavior", { + const opts = Object.defineProperty({}, "behavior", { // eslint-disable-next-line getter-return get: function () { supportsScrollToOptions = true; @@ -47,9 +55,9 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Returns value clamped by range [min, max]. * - * @param {number} value clamped value - * @param {number} min begining of range - * @param {number} max ending of range + * @param {number} value - clamped value + * @param {number} min - begining of range + * @param {number} max - ending of range * @return {number} clamped value */ function clamp(value, min, max) { @@ -60,15 +68,15 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage * Returns the required delta to fit range 1 into range 2. * In case of range 1 is bigger than range 2 returns delta to fit most out of range part. * - * @param {number} begin1 begining of range 1 - * @param {number} end1 ending of range 1 - * @param {number} begin2 begining of range 2 - * @param {number} end2 ending of range 2 + * @param {number} begin1 - begining of range 1 + * @param {number} end1 - ending of range 1 + * @param {number} begin2 - begining of range 2 + * @param {number} end2 - ending of range 2 * @return {number} delta: <0 move range1 to the left, >0 - to the right */ function fitRange(begin1, end1, begin2, end2) { - var delta1 = begin1 - begin2; - var delta2 = end2 - end1; + const delta1 = begin1 - begin2; + const delta2 = end2 - end1; if (delta1 < 0 && delta1 < delta2) { return -delta1; } else if (delta2 < 0) { @@ -80,7 +88,7 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Ease value. * - * @param {number} t value in range [0, 1] + * @param {number} t - value in range [0, 1] * @return {number} eased value in range [0, 1] */ function ease(t) { @@ -100,41 +108,40 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage * * Tizen 5 Browser/Native: scrolls documentElement (and window); has a document.scrollingElement */ - function DocumentScroller() { - } - - DocumentScroller.prototype = { + class DocumentScroller { get scrollLeft() { return window.pageXOffset; - }, + } + set scrollLeft(val) { window.scroll(val, window.pageYOffset); - }, + } get scrollTop() { return window.pageYOffset; - }, + } + set scrollTop(val) { window.scroll(window.pageXOffset, val); - }, + } get scrollWidth() { return Math.max(document.documentElement.scrollWidth, document.body.scrollWidth); - }, + } get scrollHeight() { return Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); - }, + } get clientWidth() { return Math.min(document.documentElement.clientWidth, document.body.clientWidth); - }, + } get clientHeight() { return Math.min(document.documentElement.clientHeight, document.body.clientHeight); - }, + } - getBoundingClientRect: function() { + getBoundingClientRect() { // Make valid viewport coordinates: documentElement.getBoundingClientRect returns rect of entire document relative to viewport return { left: 0, @@ -142,26 +149,29 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage width: this.clientWidth, height: this.clientHeight }; - }, + } - scrollTo: function() { + scrollTo() { window.scrollTo.apply(window, arguments); } - }; + } - var documentScroller = new DocumentScroller(); + /** + * Default (document) scroller. + */ + const documentScroller = new DocumentScroller(); /** * Returns parent element that can be scrolled. If no such, returns documentElement. * - * @param {HTMLElement} element element for which parent is being searched - * @param {boolean} vertical search for vertical scrollable parent + * @param {HTMLElement} element - element for which parent is being searched + * @param {boolean} vertical - search for vertical scrollable parent */ function getScrollableParent(element, vertical) { if (element) { - var nameScroll = "scrollWidth"; - var nameClient = "clientWidth"; - var nameClass = "scrollX"; + let nameScroll = "scrollWidth"; + let nameClient = "clientWidth"; + let nameClass = "scrollX"; if (vertical) { nameScroll = "scrollHeight"; @@ -169,7 +179,7 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage nameClass = "scrollY"; } - var parent = element.parentElement; + let parent = element.parentElement; while (parent) { // Skip 'emby-scroller' because it scrolls by itself @@ -187,20 +197,20 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * @typedef {Object} ScrollerData - * @property {number} scrollPos current scroll position - * @property {number} scrollSize scroll size - * @property {number} clientSize client size + * @property {number} scrollPos - current scroll position + * @property {number} scrollSize - scroll size + * @property {number} clientSize - client size */ /** * Returns scroll data for specified orientation. * - * @param {HTMLElement} scroller scroller - * @param {boolean} vertical vertical scroll data + * @param {HTMLElement} scroller - scroller + * @param {boolean} vertical - vertical scroll data * @return {ScrollerData} scroll data */ function getScrollerData(scroller, vertical) { - var data = {}; + let data = {}; if (!vertical) { data.scrollPos = scroller.scrollLeft; @@ -218,14 +228,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Returns position of child of scroller for specified orientation. * - * @param {HTMLElement} scroller scroller - * @param {HTMLElement} element child of scroller - * @param {boolean} vertical vertical scroll + * @param {HTMLElement} scroller - scroller + * @param {HTMLElement} element - child of scroller + * @param {boolean} vertical - vertical scroll * @return {number} child position */ function getScrollerChildPos(scroller, element, vertical) { - var elementRect = element.getBoundingClientRect(); - var scrollerRect = scroller.getBoundingClientRect(); + const elementRect = element.getBoundingClientRect(); + const scrollerRect = scroller.getBoundingClientRect(); if (!vertical) { return scroller.scrollLeft + elementRect.left - scrollerRect.left; @@ -237,21 +247,21 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Returns scroll position for element. * - * @param {ScrollerData} scrollerData scroller data - * @param {number} elementPos child element position - * @param {number} elementSize child element size - * @param {boolean} centered scroll to center + * @param {ScrollerData} scrollerData - scroller data + * @param {number} elementPos - child element position + * @param {number} elementSize - child element size + * @param {boolean} centered - scroll to center * @return {number} scroll position */ function calcScroll(scrollerData, elementPos, elementSize, centered) { - var maxScroll = scrollerData.scrollSize - scrollerData.clientSize; + const maxScroll = scrollerData.scrollSize - scrollerData.clientSize; - var scroll; + let scroll; if (centered) { scroll = elementPos + (elementSize - scrollerData.clientSize) / 2; } else { - var delta = fitRange(elementPos, elementPos + elementSize - 1, scrollerData.scrollPos, scrollerData.scrollPos + scrollerData.clientSize - 1); + const delta = fitRange(elementPos, elementPos + elementSize - 1, scrollerData.scrollPos, scrollerData.scrollPos + scrollerData.clientSize - 1); scroll = scrollerData.scrollPos - delta; } @@ -261,14 +271,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Calls scrollTo function in proper way. * - * @param {HTMLElement} scroller scroller - * @param {ScrollToOptions} options scroll options + * @param {HTMLElement} scroller - scroller + * @param {ScrollToOptions} options - scroll options */ function scrollToHelper(scroller, options) { if ("scrollTo" in scroller) { if (!supportsScrollToOptions) { - var scrollX = (options.left !== undefined ? options.left : scroller.scrollLeft); - var scrollY = (options.top !== undefined ? options.top : scroller.scrollTop); + const scrollX = (options.left !== undefined ? options.left : scroller.scrollLeft); + const scrollY = (options.top !== undefined ? options.top : scroller.scrollTop); scroller.scrollTo(scrollX, scrollY); } else { scroller.scrollTo(options); @@ -286,14 +296,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Performs built-in scroll. * - * @param {HTMLElement} xScroller horizontal scroller - * @param {number} scrollX horizontal coordinate - * @param {HTMLElement} yScroller vertical scroller - * @param {number} scrollY vertical coordinate - * @param {boolean} smooth smooth scrolling + * @param {HTMLElement} xScroller - horizontal scroller + * @param {number} scrollX - horizontal coordinate + * @param {HTMLElement} yScroller - vertical scroller + * @param {number} scrollY - vertical coordinate + * @param {boolean} smooth - smooth scrolling */ function builtinScroll(xScroller, scrollX, yScroller, scrollY, smooth) { - var scrollBehavior = smooth ? "smooth" : "instant"; + const scrollBehavior = smooth ? "smooth" : "instant"; if (xScroller !== yScroller) { scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior}); @@ -303,7 +313,7 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage } } - var scrollTimer; + let scrollTimer; /** * Resets scroll timer to stop scrolling. @@ -316,29 +326,29 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Performs animated scroll. * - * @param {HTMLElement} xScroller horizontal scroller - * @param {number} scrollX horizontal coordinate - * @param {HTMLElement} yScroller vertical scroller - * @param {number} scrollY vertical coordinate + * @param {HTMLElement} xScroller - horizontal scroller + * @param {number} scrollX - horizontal coordinate + * @param {HTMLElement} yScroller - vertical scroller + * @param {number} scrollY - vertical coordinate */ function animateScroll(xScroller, scrollX, yScroller, scrollY) { - var ox = xScroller.scrollLeft; - var oy = yScroller.scrollTop; - var dx = scrollX - ox; - var dy = scrollY - oy; + const ox = xScroller.scrollLeft; + const oy = yScroller.scrollTop; + const dx = scrollX - ox; + const dy = scrollY - oy; if (Math.abs(dx) < Epsilon && Math.abs(dy) < Epsilon) { return; } - var start; + let start; function scrollAnim(currentTimestamp) { start = start || currentTimestamp; - var k = Math.min(1, (currentTimestamp - start) / ScrollTime); + let k = Math.min(1, (currentTimestamp - start) / ScrollTime); if (k === 1) { resetScrollTimer(); @@ -348,8 +358,8 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage k = ease(k); - var x = ox + dx*k; - var y = oy + dy*k; + const x = ox + dx*k; + const y = oy + dy*k; builtinScroll(xScroller, x, yScroller, y, false); @@ -362,11 +372,11 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Performs scroll. * - * @param {HTMLElement} xScroller horizontal scroller - * @param {number} scrollX horizontal coordinate - * @param {HTMLElement} yScroller vertical scroller - * @param {number} scrollY vertical coordinate - * @param {boolean} smooth smooth scrolling + * @param {HTMLElement} xScroller - horizontal scroller + * @param {number} scrollX - horizontal coordinate + * @param {HTMLElement} yScroller - vertical scroller + * @param {number} scrollY - vertical coordinate + * @param {boolean} smooth - smooth scrolling */ function doScroll(xScroller, scrollX, yScroller, scrollY, smooth) { @@ -403,26 +413,26 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Returns true if scroll manager is enabled. */ - var isEnabled = function() { + export function isEnabled() { return layoutManager.tv; - }; + } /** * Scrolls the document to a given position. * - * @param {number} scrollX horizontal coordinate - * @param {number} scrollY vertical coordinate - * @param {boolean} [smooth=false] smooth scrolling + * @param {number} scrollX - horizontal coordinate + * @param {number} scrollY - vertical coordinate + * @param {boolean} [smooth=false] - smooth scrolling */ - var scrollTo = function(scrollX, scrollY, smooth) { + export function scrollTo(scrollX, scrollY, smooth) { smooth = !!smooth; // Scroller is document itself by default - var scroller = getScrollableParent(null, false); + const scroller = getScrollableParent(null, false); - var xScrollerData = getScrollerData(scroller, false); - var yScrollerData = getScrollerData(scroller, true); + const xScrollerData = getScrollerData(scroller, false); + const yScrollerData = getScrollerData(scroller, true); scrollX = clamp(Math.round(scrollX), 0, xScrollerData.scrollSize - xScrollerData.clientSize); scrollY = clamp(Math.round(scrollY), 0, yScrollerData.scrollSize - yScrollerData.clientSize); @@ -433,39 +443,39 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage /** * Scrolls the document to a given element. * - * @param {HTMLElement} element target element of scroll task - * @param {boolean} [smooth=false] smooth scrolling + * @param {HTMLElement} element - target element of scroll task + * @param {boolean} [smooth=false] - smooth scrolling */ - var scrollToElement = function(element, smooth) { + export function scrollToElement(element, smooth) { smooth = !!smooth; - var scrollCenterX = true; - var scrollCenterY = true; + let scrollCenterX = true; + let scrollCenterY = true; - var offsetParent = element.offsetParent; + const offsetParent = element.offsetParent; // In Firefox offsetParent.offsetParent is BODY - var isFixed = offsetParent && (!offsetParent.offsetParent || window.getComputedStyle(offsetParent).position === "fixed"); + const isFixed = offsetParent && (!offsetParent.offsetParent || window.getComputedStyle(offsetParent).position === "fixed"); // Scroll fixed elements to nearest edge (or do not scroll at all) if (isFixed) { scrollCenterX = scrollCenterY = false; } - var xScroller = getScrollableParent(element, false); - var yScroller = getScrollableParent(element, true); + const xScroller = getScrollableParent(element, false); + const yScroller = getScrollableParent(element, true); - var elementRect = element.getBoundingClientRect(); + const elementRect = element.getBoundingClientRect(); - var xScrollerData = getScrollerData(xScroller, false); - var yScrollerData = getScrollerData(yScroller, true); + const xScrollerData = getScrollerData(xScroller, false); + const yScrollerData = getScrollerData(yScroller, true); - var xPos = getScrollerChildPos(xScroller, element, false); - var yPos = getScrollerChildPos(yScroller, element, true); + const xPos = getScrollerChildPos(xScroller, element, false); + const yPos = getScrollerChildPos(yScroller, element, true); - var scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX); - var scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY); + const scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX); + let scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY); // HACK: Scroll to top for top menu because it is hidden // FIXME: Need a marker to scroll top/bottom @@ -490,9 +500,8 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage }, {capture: true}); } - return { + export default { isEnabled: isEnabled, scrollTo: scrollTo, scrollToElement: scrollToElement }; -}); From 460717c7ef8e3c59cc2530842077f45fee81fd0b Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 28 Mar 2020 20:02:22 +0300 Subject: [PATCH 10/43] Migrate AutoFocuser to ES6 --- package.json | 1 + src/components/autoFocuser.js | 45 ++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 5672ab6d2e..3af6fabbb9 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "presets": ["@babel/preset-env"], "overrides": [{ "test": [ + "src/components/autoFocuser.js", "src/components/cardbuilder/cardBuilder.js", "src/components/filedownloader.js", "src/components/filesystem.js", diff --git a/src/components/autoFocuser.js b/src/components/autoFocuser.js index 6d99009e67..93ebb4b3be 100644 --- a/src/components/autoFocuser.js +++ b/src/components/autoFocuser.js @@ -1,22 +1,29 @@ -define(["focusManager", "layoutManager"], function (focusManager, layoutManager) { - "use strict"; +/* eslint-disable indent */ + +/** + * Module for performing auto-focus. + * @module components/autoFocuser + */ + +import focusManager from "focusManager"; +import layoutManager from "layoutManager"; /** * Previously selected element. */ - var activeElement; + let activeElement; /** - * Returns true if AutoFocuser is enabled. + * Returns _true_ if AutoFocuser is enabled. */ - function isEnabled() { + export function isEnabled() { return layoutManager.tv; } /** * Start AutoFocuser */ - function enable() { + export function enable() { if (!isEnabled()) { return; } @@ -28,24 +35,19 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager) console.debug("AutoFocuser enabled"); } - /** - * Create an array from some source. - */ - var arrayFrom = Array.prototype.from || function (src) { - return Array.prototype.slice.call(src); - } - /** * Set focus on a suitable element, taking into account the previously selected. + * @param {HTMLElement} [container] - element to limit scope + * @returns {HTMLElement} focused element */ - function autoFocus(container) { + export function autoFocus(container) { if (!isEnabled()) { - return; + return null; } container = container || document.body; - var candidates = []; + let candidates = []; if (activeElement) { // These elements are recreated @@ -62,10 +64,10 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager) candidates.push(activeElement); } - candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnResume"))); - candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnPlay"))); + candidates = candidates.concat(Array.from(container.querySelectorAll(".btnResume"))); + candidates = candidates.concat(Array.from(container.querySelectorAll(".btnPlay"))); - var focusedElement; + let focusedElement; candidates.every(function (element) { if (focusManager.isCurrentlyFocusable(element)) { @@ -79,7 +81,7 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager) if (!focusedElement) { // FIXME: Multiple itemsContainers - var itemsContainer = container.querySelector(".itemsContainer"); + const itemsContainer = container.querySelector(".itemsContainer"); if (itemsContainer) { focusedElement = focusManager.autoFocus(itemsContainer); @@ -93,9 +95,8 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager) return focusedElement; } - return { + export default { isEnabled: isEnabled, enable: enable, autoFocus: autoFocus }; -}); From 5efc95fd097c7bdcc3139e9fd39bfa305353d0fb Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sun, 29 Mar 2020 00:03:15 +0300 Subject: [PATCH 11/43] Migrate DOM-module to ES6 --- package.json | 1 + src/components/dom.js | 163 +++++++++++++++++++++++++++++++----------- 2 files changed, 121 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 3af6fabbb9..f6f2436b2a 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "test": [ "src/components/autoFocuser.js", "src/components/cardbuilder/cardBuilder.js", + "src/components/dom.js", "src/components/filedownloader.js", "src/components/filesystem.js", "src/components/input/keyboardnavigation.js", diff --git a/src/components/dom.js b/src/components/dom.js index b91e5b1687..fdc5e607eb 100644 --- a/src/components/dom.js +++ b/src/components/dom.js @@ -1,8 +1,18 @@ -define([], function () { - 'use strict'; +/* eslint-disable indent */ - function parentWithAttribute(elem, name, value) { +/** + * Useful DOM utilities. + * @module components/dom + */ + /** + * Returns parent of element with specified attribute value. + * @param {HTMLElement} elem - element whose parent need to find + * @param {string} name - attribute name + * @param {mixed} value - attribute value + * @returns {HTMLElement} Parent with specified attribute value + */ + export function parentWithAttribute(elem, name, value) { while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) { elem = elem.parentNode; @@ -14,8 +24,13 @@ define([], function () { return elem; } - function parentWithTag(elem, tagNames) { - + /** + * Returns parent of element with one of specified tag names. + * @param {HTMLElement} elem - element whose parent need to find + * @param {(string|Array)} tagNames - tag name or array of tag names + * @returns {HTMLElement} Parent with one of specified tag names + */ + export function parentWithTag(elem, tagNames) { // accept both string and array passed in if (!Array.isArray(tagNames)) { tagNames = [tagNames]; @@ -32,9 +47,14 @@ define([], function () { return elem; } + /** + * Returns _true_ if class list contains one of specified names. + * @param {DOMTokenList} classList - class list + * @param {Array} classNames - array of class names + * @returns {boolean} _true_ if class list contains one of specified names + */ function containsAnyClass(classList, classNames) { - - for (var i = 0, length = classNames.length; i < length; i++) { + for (let i = 0, length = classNames.length; i < length; i++) { if (classList.contains(classNames[i])) { return true; } @@ -42,8 +62,13 @@ define([], function () { return false; } - function parentWithClass(elem, classNames) { - + /** + * Returns parent of element with one of specified class names. + * @param {HTMLElement} elem - element whose parent need to find + * @param {(string|Array)} classNames - class name or array of class names + * @returns {HTMLElement} Parent with one of specified class names + */ + export function parentWithClass(elem, classNames) { // accept both string and array passed in if (!Array.isArray(classNames)) { classNames = [classNames]; @@ -60,9 +85,9 @@ define([], function () { return elem; } - var supportsCaptureOption = false; + let supportsCaptureOption = false; try { - var opts = Object.defineProperty({}, 'capture', { + const opts = Object.defineProperty({}, 'capture', { // eslint-disable-next-line getter-return get: function () { supportsCaptureOption = true; @@ -73,29 +98,58 @@ define([], function () { console.debug('error checking capture support'); } - function addEventListenerWithOptions(target, type, handler, options) { - var optionsOrCapture = options || {}; + /** + * Adds event listener to specified target. + * @param {EventTarget} target - event target + * @param {string} type - event type + * @param {function} handler - event handler + * @param {Object} [options] - listener options + */ + export function addEventListener(target, type, handler, options) { + let optionsOrCapture = options || {}; if (!supportsCaptureOption) { optionsOrCapture = optionsOrCapture.capture; } target.addEventListener(type, handler, optionsOrCapture); } - function removeEventListenerWithOptions(target, type, handler, options) { - var optionsOrCapture = options || {}; + /** + * Removes event listener from specified target. + * @param {EventTarget} target - event target + * @param {string} type - event type + * @param {function} handler - event handler + * @param {Object} [options] - listener options + */ + export function removeEventListener(target, type, handler, options) { + let optionsOrCapture = options || {}; if (!supportsCaptureOption) { optionsOrCapture = optionsOrCapture.capture; } target.removeEventListener(type, handler, optionsOrCapture); } - var windowSize; - var windowSizeEventsBound; + /** + * Cached window size. + */ + let windowSize; + + /** + * Flag of event listener bound. + */ + let windowSizeEventsBound; + + /** + * Resets cached window size. + */ function clearWindowSize() { windowSize = null; } - function getWindowSize() { + /** + * Returns window size. + * @returns {Object} Window size + */ + export function getWindowSize() { if (!windowSize) { windowSize = { innerHeight: window.innerHeight, @@ -104,46 +158,60 @@ define([], function () { if (!windowSizeEventsBound) { windowSizeEventsBound = true; - addEventListenerWithOptions(window, "orientationchange", clearWindowSize, { passive: true }); - addEventListenerWithOptions(window, 'resize', clearWindowSize, { passive: true }); + addEventListener(window, "orientationchange", clearWindowSize, { passive: true }); + addEventListener(window, 'resize', clearWindowSize, { passive: true }); } } return windowSize; } - var standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680]; - function getScreenWidth() { - var width = window.innerWidth; - var height = window.innerHeight; + /** + * Standard screen widths. + */ + const standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680]; + + /** + * Returns screen width. + * @returns {number} Screen width + */ + export function getScreenWidth() { + let width = window.innerWidth; + const height = window.innerHeight; if (height > width) { width = height * (16.0 / 9.0); } - var closest = standardWidths.sort(function (a, b) { + const closest = standardWidths.sort(function (a, b) { return Math.abs(width - a) - Math.abs(width - b); })[0]; return closest; } - var _animationEvent; - function whichAnimationEvent() { + /** + * Name of animation end event. + */ + let _animationEvent; + /** + * Returns name of animation end event. + * @returns {string} Name of animation end event + */ + export function whichAnimationEvent() { if (_animationEvent) { return _animationEvent; } - var t; - var el = document.createElement("div"); - var animations = { + const el = document.createElement("div"); + const animations = { "animation": "animationend", "OAnimation": "oAnimationEnd", "MozAnimation": "animationend", "WebkitAnimation": "webkitAnimationEnd" }; - for (t in animations) { + for (let t in animations) { if (el.style[t] !== undefined) { _animationEvent = animations[t]; return animations[t]; @@ -154,26 +222,36 @@ define([], function () { return _animationEvent; } - function whichAnimationCancelEvent() { - + /** + * Returns name of animation cancel event. + * @returns {string} Name of animation cancel event + */ + export function whichAnimationCancelEvent() { return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel'); } - var _transitionEvent; - function whichTransitionEvent() { + /** + * Name of transition end event. + */ + let _transitionEvent; + + /** + * Returns name of transition end event. + * @returns {string} Name of transition end event + */ + export function whichTransitionEvent() { if (_transitionEvent) { return _transitionEvent; } - var t; - var el = document.createElement("div"); - var transitions = { + const el = document.createElement("div"); + const transitions = { "transition": "transitionend", "OTransition": "oTransitionEnd", "MozTransition": "transitionend", "WebkitTransition": "webkitTransitionEnd" }; - for (t in transitions) { + for (let t in transitions) { if (el.style[t] !== undefined) { _transitionEvent = transitions[t]; return transitions[t]; @@ -184,16 +262,15 @@ define([], function () { return _transitionEvent; } - return { + export default { parentWithAttribute: parentWithAttribute, parentWithClass: parentWithClass, parentWithTag: parentWithTag, - addEventListener: addEventListenerWithOptions, - removeEventListener: removeEventListenerWithOptions, + addEventListener: addEventListener, + removeEventListener: removeEventListener, getWindowSize: getWindowSize, getScreenWidth: getScreenWidth, whichTransitionEvent: whichTransitionEvent, whichAnimationEvent: whichAnimationEvent, whichAnimationCancelEvent: whichAnimationCancelEvent }; -}); From eb047175be762e69e0fcefde26199e423a7ed87b Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sun, 29 Mar 2020 00:06:27 +0300 Subject: [PATCH 12/43] Add default export for compatibility --- src/components/cardbuilder/cardBuilder.js | 11 +++++++++++ src/components/input/keyboardnavigation.js | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 9d86bc9d7c..1249f802af 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -1853,3 +1853,14 @@ import 'programStyles'; cell.removeAttribute('data-seriestimerid'); } } + +export default { + getCardsHtml: getCardsHtml, + getDefaultBackgroundClass: getDefaultBackgroundClass, + getDefaultText: getDefaultText, + buildCards: buildCards, + onUserDataChanged: onUserDataChanged, + onTimerCreated: onTimerCreated, + onTimerCancelled: onTimerCancelled, + onSeriesTimerCancelled: onSeriesTimerCancelled +}; diff --git a/src/components/input/keyboardnavigation.js b/src/components/input/keyboardnavigation.js index bdcb733179..caddf46797 100644 --- a/src/components/input/keyboardnavigation.js +++ b/src/components/input/keyboardnavigation.js @@ -1,3 +1,8 @@ +/** + * Module for performing keyboard navigation. + * @module components/input/keyboardnavigation + */ + import inputManager from "inputManager"; import layoutManager from "layoutManager"; @@ -55,7 +60,7 @@ if (!hasFieldKey) { /** * Returns key name from event. * - * @param {KeyboardEvent} event keyboard event + * @param {KeyboardEvent} event - keyboard event * @return {string} key name */ export function getKeyName(event) { @@ -65,7 +70,7 @@ export function getKeyName(event) { /** * Returns _true_ if key is used for navigation. * - * @param {string} key name + * @param {string} key - key name * @return {boolean} _true_ if key is used for navigation */ export function isNavigationKey(key) { @@ -155,3 +160,9 @@ function attachGamepadScript(e) { // No need to check for gamepads manually at load time, the eventhandler will be fired for that window.addEventListener("gamepadconnected", attachGamepadScript); + +export default { + enable: enable, + getKeyName: getKeyName, + isNavigationKey: isNavigationKey +}; From 0afe5f62ef5f709db5710174aa5b68f6e7f5888b Mon Sep 17 00:00:00 2001 From: bg56530 Date: Sun, 29 Mar 2020 15:58:21 +0000 Subject: [PATCH 13/43] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/strings/fr.json b/src/strings/fr.json index aab47271c0..234f8dace7 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -1470,5 +1470,6 @@ "NoCreatedLibraries": "Il semblerait que vous n'ayez créé aucune librairie. {0}Voulez-vous en créer une maintenant ?{1}", "PlaybackErrorNoCompatibleStream": "Problème de profil client, le serveur n’a pas pu envoyer un format média compatible.", "PreferEmbeddedEpisodeInfosOverFileNames": "Préférer les informations intégrées aux noms de fichiers", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Utilise les informations des métadonnées intégrées, si disponible." + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Utilise les informations des métadonnées intégrées, si disponible.", + "ClientSettings": "Paramètres Client" } From ace822a8af8441874ab997fb10d2524259ea73a6 Mon Sep 17 00:00:00 2001 From: Vasily Date: Sun, 29 Mar 2020 22:04:36 +0300 Subject: [PATCH 14/43] Switch to new version of JavascriptSubtitlesOctopus, enable new options --- package.json | 2 +- src/components/htmlvideoplayer/plugin.js | 14 +++++++++++++- yarn.lock | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2df8c1a637..602f20c092 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", "jquery": "^3.4.1", "jstree": "^3.3.7", - "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus", + "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf", "material-design-icons-iconfont": "^5.0.1", "native-promise-only": "^0.8.0-a", "page": "^1.11.5", diff --git a/src/components/htmlvideoplayer/plugin.js b/src/components/htmlvideoplayer/plugin.js index 995df6607b..97056f80ff 100644 --- a/src/components/htmlvideoplayer/plugin.js +++ b/src/components/htmlvideoplayer/plugin.js @@ -1058,7 +1058,19 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa legacyWorkerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker-legacy.js", onError: function() { htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror'); - } + }, + + // new octopus options; override all, even defaults + renderMode: 'blend', + dropAllAnimations: false, + libassMemoryLimit: 40, + libassGlyphLimit: 40, + targetFps: 24, + prescaleTradeoff: 0.8, + softHeightLimit: 1080, + hardHeightLimit: 2160, + resizeVariation: 0.2, + renderAhead: 90 }; require(['JavascriptSubtitlesOctopus'], function(SubtitlesOctopus) { currentSubtitlesOctopus = new SubtitlesOctopus(options); diff --git a/yarn.lock b/yarn.lock index 5641ad41a5..d356ba6086 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6576,9 +6576,9 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -"libass-wasm@https://github.com/jellyfin/JavascriptSubtitlesOctopus": +"libass-wasm@https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf": version "4.0.0" - resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#1d12af0b04fb2337570b57d706dd97db2f904b9d" + resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#7e6b75dcab9f7dad12719983510d05242803707c" liftoff@^3.1.0: version "3.1.0" From 30ecc52ba4af24544f6f6f99b0fe453e1ef91aea Mon Sep 17 00:00:00 2001 From: Nazar78 Date: Sun, 29 Mar 2020 05:40:33 +0800 Subject: [PATCH 15/43] Support H264 Level 52 (Tizen 5.0) - app only --- src/scripts/browserdeviceprofile.js | 38 +++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/scripts/browserdeviceprofile.js b/src/scripts/browserdeviceprofile.js index 337463987c..e5e3fd67c0 100644 --- a/src/scripts/browserdeviceprofile.js +++ b/src/scripts/browserdeviceprofile.js @@ -355,6 +355,31 @@ define(['browser'], function (browser) { // Not sure how to test for this var supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s; + // Get Tizen version & support + var getTizenVersion = self.tizen && self.tizen.systeminfo ? + parseFloat(tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version')) : null; + + function getTizenSupport(feature, version, current) { + if (getTizenVersion) { + var version = parseFloat(version); + switch(feature) { + case 'supportsDts': + if (getTizenVersion >= version) { + current = false; + } + break; + case 'maxH264Level': + if (getTizenVersion >= version) { + current = 52; + } + break; + default: + break; + } + } + return current; + } + var maxVideoWidth = browser.xboxOne ? (self.screen ? self.screen.width : null) : null; @@ -433,14 +458,8 @@ define(['browser'], function (browser) { var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts; - if (self.tizen && self.tizen.systeminfo) { - var v = tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version'); - - // DTS audio not supported in 2018 models (Tizen 4.0) - if (v && parseFloat(v) >= parseFloat('4.0')) { - supportsDts = false; - } - } + // DTS audio not supported in 2018 models (Tizen 4.0) + supportsDts = getTizenSupport('supportsDts', '4.0', supportsDts); if (supportsDts) { videoAudioCodecs.push('dca'); @@ -766,6 +785,9 @@ define(['browser'], function (browser) { maxH264Level = 51; } + // Support H264 Level 52 (Tizen 5.0) + maxH264Level = getTizenSupport('maxH264Level', '5.0', maxH264Level); + if (browser.tizen || browser.orsay || videoTestElement.canPlayType('video/mp4; codecs="avc1.6e0033"').replace(/no/, '')) { From 3f11095fec66cf91a7846fef17bfc6b0234b74b0 Mon Sep 17 00:00:00 2001 From: Nazar78 Date: Sun, 29 Mar 2020 05:40:33 +0800 Subject: [PATCH 16/43] Support H264 Level 52 (Tizen 5.0) - app only --- src/scripts/browserdeviceprofile.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/scripts/browserdeviceprofile.js b/src/scripts/browserdeviceprofile.js index 337463987c..570c0d2fa1 100644 --- a/src/scripts/browserdeviceprofile.js +++ b/src/scripts/browserdeviceprofile.js @@ -433,13 +433,9 @@ define(['browser'], function (browser) { var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts; - if (self.tizen && self.tizen.systeminfo) { - var v = tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version'); - - // DTS audio not supported in 2018 models (Tizen 4.0) - if (v && parseFloat(v) >= parseFloat('4.0')) { - supportsDts = false; - } + // DTS audio not supported in 2018 models (Tizen 4.0) + if (browser.tizenVersion >= 4) { + supportsDts = false; } if (supportsDts) { @@ -766,6 +762,11 @@ define(['browser'], function (browser) { maxH264Level = 51; } + // Support H264 Level 52 (Tizen 5.0) - app only + if (browser.tizenVersion >= 5 && window.NativeShell) { + maxH264Level = 52; + } + if (browser.tizen || browser.orsay || videoTestElement.canPlayType('video/mp4; codecs="avc1.6e0033"').replace(/no/, '')) { From 0c0b91b7a5833ff311fa3d8fbddf27e833c40e69 Mon Sep 17 00:00:00 2001 From: Nazar78 Date: Mon, 30 Mar 2020 17:02:16 +0800 Subject: [PATCH 17/43] Support H264 Level 52 (Tizen 5.0) - app only --- src/scripts/browserdeviceprofile.js | 900 ++++++++++++++++++++++++++++ 1 file changed, 900 insertions(+) create mode 100644 src/scripts/browserdeviceprofile.js diff --git a/src/scripts/browserdeviceprofile.js b/src/scripts/browserdeviceprofile.js new file mode 100644 index 0000000000..570c0d2fa1 --- /dev/null +++ b/src/scripts/browserdeviceprofile.js @@ -0,0 +1,900 @@ +define(['browser'], function (browser) { + 'use strict'; + + function canPlayH264(videoTestElement) { + return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, '')); + } + + function canPlayH265(videoTestElement, options) { + if (browser.tizen || browser.orsay || browser.xboxOne || browser.web0s || options.supportsHevc) { + return true; + } + + var userAgent = navigator.userAgent.toLowerCase(); + if (browser.chromecast) { + var isChromecastUltra = userAgent.indexOf('aarch64') !== -1; + if (isChromecastUltra) { + return true; + } + } + + if (browser.ps4) { + return false; + } + + return !!videoTestElement.canPlayType && + (videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.L120"').replace(/no/, '') || + videoTestElement.canPlayType('video/mp4; codecs="hev1.1.L120"').replace(/no/, '') || + videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.0.L120"').replace(/no/, '') || + videoTestElement.canPlayType('video/mp4; codecs="hev1.1.0.L120"').replace(/no/, '')); + } + + var _supportsTextTracks; + function supportsTextTracks() { + if (browser.tizen || browser.orsay) { + return true; + } + + if (_supportsTextTracks == null) { + _supportsTextTracks = document.createElement('video').textTracks != null; + } + + // For now, until ready + return _supportsTextTracks; + } + + var _canPlayHls; + function canPlayHls() { + if (_canPlayHls == null) { + _canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE(); + } + + return _canPlayHls; + } + + function canPlayNativeHls() { + if (browser.tizen || browser.orsay) { + return true; + } + + var media = document.createElement('video'); + if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || + media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) { + return true; + } + + return false; + } + + function canPlayHlsWithMSE() { + // text tracks don’t work with this in firefox + return window.MediaSource != null; + } + + function supportsAc3(videoTestElement) { + if (browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + return videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, ''); + } + + function supportsEac3(videoTestElement) { + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + return videoTestElement.canPlayType('audio/mp4; codecs="ec-3"').replace(/no/, ''); + } + + function supportsAc3InHls(videoTestElement) { + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + if (videoTestElement.canPlayType) { + return videoTestElement.canPlayType('application/x-mpegurl; codecs="avc1.42E01E, ac-3"').replace(/no/, '') || + videoTestElement.canPlayType('application/vnd.apple.mpegURL; codecs="avc1.42E01E, ac-3"').replace(/no/, ''); + } + + return false; + } + + function canPlayAudioFormat(format) { + var typeString; + + if (format === 'flac') { + if (browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp) { + return true; + } + } else if (format === 'wma') { + if (browser.tizen || browser.orsay || browser.edgeUwp) { + return true; + } + } else if (format === 'asf') { + if (browser.tizen || browser.web0s || browser.edgeUwp) { + return true; + } + } else if (format === 'opus') { + if (!browser.web0s) { + typeString = 'audio/ogg; codecs="opus"'; + return !!document.createElement('audio').canPlayType(typeString).replace(/no/, ''); + } + + return false; + } else if (format === 'alac') { + if (browser.iOS || browser.osx) { + return true; + } + } else if (format === 'mp2') { + // For now + return false; + } + + if (format === 'webma') { + typeString = 'audio/webm'; + } else if (format === 'mp2') { + typeString = 'audio/mpeg'; + } else { + typeString = 'audio/' + format; + } + + return !!document.createElement('audio').canPlayType(typeString).replace(/no/, ''); + } + + function testCanPlayMkv(videoTestElement) { + if (browser.tizen || browser.orsay || browser.web0s) { + return true; + } + + if (videoTestElement.canPlayType('video/x-matroska').replace(/no/, '') || + videoTestElement.canPlayType('video/mkv').replace(/no/, '')) { + return true; + } + + // Unfortunately there's no real way to detect mkv support + if (browser.chrome) { + // Not supported on opera tv + if (browser.operaTv) { + return false; + } + + var userAgent = navigator.userAgent.toLowerCase(); + + // Filter out browsers based on chromium that don't support mkv + if (userAgent.indexOf('vivaldi') !== -1 || userAgent.indexOf('opera') !== -1) { + return false; + } + + return true; + } + + if (browser.edgeUwp) { + return true; + } + + return false; + } + + function testCanPlayTs() { + return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + } + + function supportsMpeg2Video() { + return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + } + + function supportsVc1() { + return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + } + + function getFlvMseDirectPlayProfile() { + var videoAudioCodecs = ['aac']; + if (!browser.edge && !browser.msie) { + videoAudioCodecs.push('mp3'); + } + + return { + Container: 'flv', + Type: 'Video', + VideoCodec: 'h264', + AudioCodec: videoAudioCodecs.join(',') + }; + } + + function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) { + var supported = false; + var profileContainer = container; + var videoCodecs = []; + + switch (container) { + case 'asf': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + videoAudioCodecs = []; + break; + case 'avi': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + // New Samsung TV don't support XviD/DivX + // Explicitly add supported codecs to make other codecs be transcoded + if (browser.tizenVersion >= 4) { + videoCodecs.push('h264'); + if (canPlayH265(videoTestElement, options)) { + videoCodecs.push('h265'); + videoCodecs.push('hevc'); + } + } + break; + case 'mpg': + case 'mpeg': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + break; + case 'flv': + supported = browser.tizen || browser.orsay; + //if (!supported && window.MediaSource != null && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) { + // return getFlvMseDirectPlayProfile(); + //} + break; + case '3gp': + case 'mts': + case 'trp': + case 'vob': + case 'vro': + supported = browser.tizen || browser.orsay; + break; + case 'mov': + supported = browser.tizen || browser.orsay || browser.web0s || browser.chrome || browser.edgeUwp; + videoCodecs.push('h264'); + break; + case 'm2ts': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + videoCodecs.push('h264'); + if (supportsVc1()) { + videoCodecs.push('vc1'); + } + if (supportsMpeg2Video()) { + videoCodecs.push('mpeg2video'); + } + break; + case 'wmv': + supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp; + videoAudioCodecs = []; + break; + case 'ts': + supported = testCanPlayTs(); + videoCodecs.push('h264'); + if (canPlayH265(videoTestElement, options)) { + videoCodecs.push('h265'); + videoCodecs.push('hevc'); + } + if (supportsVc1()) { + videoCodecs.push('vc1'); + } + if (supportsMpeg2Video()) { + videoCodecs.push('mpeg2video'); + } + profileContainer = 'ts,mpegts'; + break; + default: + break; + } + + return supported ? { + Container: profileContainer, + Type: 'Video', + VideoCodec: videoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + } : null; + } + + function getMaxBitrate() { + return 120000000; + } + + function getGlobalMaxVideoBitrate() { + var userAgent = navigator.userAgent.toLowerCase(); + if (browser.chromecast) { + var isChromecastUltra = userAgent.indexOf('aarch64') !== -1; + if (isChromecastUltra) { + return null; + } + + // This is a hack to try and detect chromecast on vizio + if (self.screen && self.screen.width >= 3800) { + return null; + } + + return 30000000; + } + + var isTizenFhd = false; + if (browser.tizen) { + try { + var isTizenUhd = webapis.productinfo.isUdPanelSupported(); + isTizenFhd = !isTizenUhd; + console.debug("isTizenFhd = " + isTizenFhd); + } catch (error) { + console.error("isUdPanelSupported() error code = " + error.code); + } + } + + return browser.ps4 ? 8000000 : + (browser.xboxOne ? 12000000 : + (browser.edgeUwp ? null : + (browser.tizen && isTizenFhd ? 20000000 : null))); + } + + return function (options) { + options = options || {}; + + var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2); + + var bitrateSetting = getMaxBitrate(); + + var videoTestElement = document.createElement('video'); + + var canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, ''); + var canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, ''); + var webmAudioCodecs = ['vorbis']; + + var canPlayMkv = testCanPlayMkv(videoTestElement); + + var profile = {}; + + profile.MaxStreamingBitrate = bitrateSetting; + profile.MaxStaticBitrate = 100000000; + profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000); + + profile.DirectPlayProfiles = []; + + var videoAudioCodecs = []; + var hlsVideoAudioCodecs = []; + + var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') || + videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, ''); + + // Not sure how to test for this + var supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s; + + var maxVideoWidth = browser.xboxOne ? + (self.screen ? self.screen.width : null) : + null; + + if (options.maxVideoWidth) { + maxVideoWidth = options.maxVideoWidth; + } + + var canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, ''); + + if (canPlayAacVideoAudio && browser.chromecast && physicalAudioChannels <= 2) { + // prioritize this first + videoAudioCodecs.push('aac'); + } + + // Only put mp3 first if mkv support is there + // Otherwise with HLS and mp3 audio we're seeing some browsers + // safari is lying + if (supportsAc3(videoTestElement)) { + + videoAudioCodecs.push('ac3'); + + var eAc3 = supportsEac3(videoTestElement); + if (eAc3) { + videoAudioCodecs.push('eac3'); + } + + // This works in edge desktop, but not mobile + // TODO: Retest this on mobile + if (supportsAc3InHls(videoTestElement)) { + hlsVideoAudioCodecs.push('ac3'); + if (eAc3) { + hlsVideoAudioCodecs.push('eac3'); + } + } + } + + if (canPlayAacVideoAudio && browser.chromecast && videoAudioCodecs.indexOf('aac') === -1) { + // prioritize this first + videoAudioCodecs.push('aac'); + } + + if (supportsMp3VideoAudio) { + videoAudioCodecs.push('mp3'); + + // PS4 fails to load HLS with mp3 audio + if (!browser.ps4) { + // mp3 encoder only supports 2 channels, so only make that preferred if we're only requesting 2 channels + // Also apply it for chromecast because it no longer supports AAC 5.1 + if (physicalAudioChannels <= 2) { + hlsVideoAudioCodecs.push('mp3'); + } + } + } + + if (canPlayAacVideoAudio) { + if (videoAudioCodecs.indexOf('aac') === -1) { + videoAudioCodecs.push('aac'); + } + + hlsVideoAudioCodecs.push('aac'); + } + + if (supportsMp3VideoAudio) { + // PS4 fails to load HLS with mp3 audio + if (!browser.ps4) { + if (hlsVideoAudioCodecs.indexOf('mp3') === -1) { + hlsVideoAudioCodecs.push('mp3'); + } + } + } + + if (supportsMp2VideoAudio) { + videoAudioCodecs.push('mp2'); + } + + var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts; + + // DTS audio not supported in 2018 models (Tizen 4.0) + if (browser.tizenVersion >= 4) { + supportsDts = false; + } + + if (supportsDts) { + videoAudioCodecs.push('dca'); + videoAudioCodecs.push('dts'); + } + + if (browser.tizen || browser.orsay || browser.web0s) { + videoAudioCodecs.push('pcm_s16le'); + videoAudioCodecs.push('pcm_s24le'); + } + + if (options.supportsTrueHd) { + videoAudioCodecs.push('truehd'); + } + + if (browser.tizen || browser.orsay) { + videoAudioCodecs.push('aac_latm'); + } + + if (canPlayAudioFormat('opus')) { + videoAudioCodecs.push('opus'); + hlsVideoAudioCodecs.push('opus'); + webmAudioCodecs.push('opus'); + } + + if (canPlayAudioFormat('flac')) { + videoAudioCodecs.push('flac'); + } + + videoAudioCodecs = videoAudioCodecs.filter(function (c) { + return (options.disableVideoAudioCodecs || []).indexOf(c) === -1; + }); + + hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function (c) { + return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1; + }); + + var mp4VideoCodecs = []; + var hlsVideoCodecs = []; + + if (canPlayH264(videoTestElement)) { + mp4VideoCodecs.push('h264'); + hlsVideoCodecs.push('h264'); + } + + if (canPlayH265(videoTestElement, options)) { + mp4VideoCodecs.push('h265'); + mp4VideoCodecs.push('hevc'); + + if (browser.tizen || browser.web0s) { + hlsVideoCodecs.push('h265'); + hlsVideoCodecs.push('hevc'); + } + } + + if (supportsMpeg2Video()) { + mp4VideoCodecs.push('mpeg2video'); + } + + if (supportsVc1()) { + mp4VideoCodecs.push('vc1'); + } + + if (browser.tizen || browser.orsay) { + mp4VideoCodecs.push('msmpeg4v2'); + } + + if (canPlayVp8) { + mp4VideoCodecs.push('vp8'); + } + + if (canPlayVp9) { + mp4VideoCodecs.push('vp9'); + } + + if (canPlayVp8 || browser.tizen || browser.orsay) { + videoAudioCodecs.push('vorbis'); + } + + if (mp4VideoCodecs.length) { + profile.DirectPlayProfiles.push({ + Container: 'mp4,m4v', + Type: 'Video', + VideoCodec: mp4VideoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + }); + } + + if (canPlayMkv && mp4VideoCodecs.length) { + profile.DirectPlayProfiles.push({ + Container: 'mkv', + Type: 'Video', + VideoCodec: mp4VideoCodecs.join(','), + AudioCodec: videoAudioCodecs.join(',') + }); + } + + // These are formats we can't test for but some devices will support + ['m2ts', 'wmv', 'ts', 'asf', 'avi', 'mpg', 'mpeg', 'flv', '3gp', 'mts', 'trp', 'vob', 'vro', 'mov'].map(function (container) { + return getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options); + }).filter(function (i) { + return i != null; + }).forEach(function (i) { + profile.DirectPlayProfiles.push(i); + }); + + ['opus', 'mp3', 'mp2', 'aac', 'flac', 'alac', 'webma', 'wma', 'wav', 'ogg', 'oga'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + + if (audioFormat === 'mp2') { + profile.DirectPlayProfiles.push({ + Container: 'mp2,mp3', + Type: 'Audio', + AudioCodec: audioFormat + }); + } else if (audioFormat === 'mp3') { + profile.DirectPlayProfiles.push({ + Container: audioFormat, + Type: 'Audio', + AudioCodec: audioFormat + }); + } else { + profile.DirectPlayProfiles.push({ + Container: audioFormat === 'webma' ? 'webma,webm' : audioFormat, + Type: 'Audio' + }); + } + + // aac also appears in the m4a and m4b container + if (audioFormat === 'aac' || audioFormat === 'alac') { + profile.DirectPlayProfiles.push({ + Container: 'm4a,m4b', + AudioCodec: audioFormat, + Type: 'Audio' + }); + } + }); + + if (canPlayVp8) { + profile.DirectPlayProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: webmAudioCodecs.join(','), + VideoCodec: 'VP8' + }); + } + + if (canPlayVp9) { + profile.DirectPlayProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: webmAudioCodecs.join(','), + VideoCodec: 'VP9' + }); + } + + profile.TranscodingProfiles = []; + + var hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false; + + if (canPlayHls() && browser.enableHlsAudio !== false) { + profile.TranscodingProfiles.push({ + // hlsjs, edge, and android all seem to require ts container + Container: !canPlayNativeHls() || browser.edge || browser.android ? 'ts' : 'aac', + Type: 'Audio', + AudioCodec: 'aac', + Context: 'Streaming', + Protocol: 'hls', + MaxAudioChannels: physicalAudioChannels.toString(), + MinSegments: browser.iOS || browser.osx ? '2' : '1', + BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames + }); + } + + // For streaming, prioritize opus transcoding after mp3/aac. It is too problematic with random failures + // But for static (offline sync), it will be just fine. + // Prioritize aac higher because the encoder can accept more channels than mp3 + ['aac', 'mp3', 'opus', 'wav'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + profile.TranscodingProfiles.push({ + Container: audioFormat, + Type: 'Audio', + AudioCodec: audioFormat, + Context: 'Streaming', + Protocol: 'http', + MaxAudioChannels: physicalAudioChannels.toString() + }); + }); + + ['opus', 'mp3', 'aac', 'wav'].filter(canPlayAudioFormat).forEach(function (audioFormat) { + profile.TranscodingProfiles.push({ + Container: audioFormat, + Type: 'Audio', + AudioCodec: audioFormat, + Context: 'Static', + Protocol: 'http', + MaxAudioChannels: physicalAudioChannels.toString() + }); + }); + + if (canPlayMkv && !browser.tizen && !browser.orsay && options.enableMkvProgressive !== false) { + profile.TranscodingProfiles.push({ + Container: 'mkv', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: mp4VideoCodecs.join(','), + Context: 'Streaming', + MaxAudioChannels: physicalAudioChannels.toString(), + CopyTimestamps: true + }); + } + + if (canPlayMkv) { + profile.TranscodingProfiles.push({ + Container: 'mkv', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: mp4VideoCodecs.join(','), + Context: 'Static', + MaxAudioChannels: physicalAudioChannels.toString(), + CopyTimestamps: true + }); + } + + if (canPlayHls() && hlsVideoAudioCodecs.length && options.enableHls !== false) { + profile.TranscodingProfiles.push({ + Container: 'ts', + Type: 'Video', + AudioCodec: hlsVideoAudioCodecs.join(','), + VideoCodec: hlsVideoCodecs.join(','), + Context: 'Streaming', + Protocol: 'hls', + MaxAudioChannels: physicalAudioChannels.toString(), + MinSegments: browser.iOS || browser.osx ? '2' : '1', + BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames + }); + } + + if (canPlayVp8) { + profile.TranscodingProfiles.push({ + Container: 'webm', + Type: 'Video', + AudioCodec: 'vorbis', + VideoCodec: 'vpx', + Context: 'Streaming', + Protocol: 'http', + // If audio transcoding is needed, limit channels to number of physical audio channels + // Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good + MaxAudioChannels: physicalAudioChannels.toString() + }); + } + + profile.TranscodingProfiles.push({ + Container: 'mp4', + Type: 'Video', + AudioCodec: videoAudioCodecs.join(','), + VideoCodec: 'h264', + Context: 'Static', + Protocol: 'http' + }); + + profile.ContainerProfiles = []; + + profile.CodecProfiles = []; + + var supportsSecondaryAudio = browser.tizen || browser.orsay || videoTestElement.audioTracks; + + var aacCodecProfileConditions = []; + + // Handle he-aac not supported + if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) { + // TODO: This needs to become part of the stream url in order to prevent stream copy + aacCodecProfileConditions.push({ + Condition: 'NotEquals', + Property: 'AudioProfile', + Value: 'HE-AAC' + }); + } + + if (!supportsSecondaryAudio) { + aacCodecProfileConditions.push({ + Condition: 'Equals', + Property: 'IsSecondaryAudio', + Value: 'false', + IsRequired: false + }); + } + + if (browser.chromecast) { + aacCodecProfileConditions.push({ + Condition: 'LessThanEqual', + Property: 'AudioChannels', + Value: '2', + IsRequired: true + }); + } + + if (aacCodecProfileConditions.length) { + profile.CodecProfiles.push({ + Type: 'VideoAudio', + Codec: 'aac', + Conditions: aacCodecProfileConditions + }); + } + + if (!supportsSecondaryAudio) { + profile.CodecProfiles.push({ + Type: 'VideoAudio', + Conditions: [ + { + Condition: 'Equals', + Property: 'IsSecondaryAudio', + Value: 'false', + IsRequired: false + } + ] + }); + } + + var maxH264Level = 42; + var h264Profiles = 'high|main|baseline|constrained baseline'; + + if (browser.tizen || browser.orsay || browser.web0s || + videoTestElement.canPlayType('video/mp4; codecs="avc1.640833"').replace(/no/, '')) { + maxH264Level = 51; + } + + // Support H264 Level 52 (Tizen 5.0) - app only + if (browser.tizenVersion >= 5 && window.NativeShell) { + maxH264Level = 52; + } + + if (browser.tizen || browser.orsay || + videoTestElement.canPlayType('video/mp4; codecs="avc1.6e0033"').replace(/no/, '')) { + + // These tests are passing in safari, but playback is failing + if (!browser.safari && !browser.iOS && !browser.web0s && !browser.edge && !browser.mobile) { + h264Profiles += '|high 10'; + } + } + + profile.CodecProfiles.push({ + Type: 'Video', + Codec: 'h264', + Conditions: [ + { + Condition: 'NotEquals', + Property: 'IsAnamorphic', + Value: 'true', + IsRequired: false + }, + { + Condition: 'EqualsAny', + Property: 'VideoProfile', + Value: h264Profiles, + IsRequired: false + }, + { + Condition: 'LessThanEqual', + Property: 'VideoLevel', + Value: maxH264Level.toString(), + IsRequired: false + } + ] + }); + + if (!browser.edgeUwp && !browser.tizen && !browser.orsay && !browser.web0s) { + //profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + // Condition: 'NotEquals', + // Property: 'IsAVC', + // Value: 'false', + // IsRequired: false + //}); + + //profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + // Condition: 'NotEquals', + // Property: 'IsInterlaced', + // Value: 'true', + // IsRequired: false + //}); + } + + if (maxVideoWidth) { + profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + Condition: 'LessThanEqual', + Property: 'Width', + Value: maxVideoWidth.toString(), + IsRequired: false + }); + } + + var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString(); + + var h264MaxVideoBitrate = globalMaxVideoBitrate; + + if (h264MaxVideoBitrate) { + profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({ + Condition: 'LessThanEqual', + Property: 'VideoBitrate', + Value: h264MaxVideoBitrate, + IsRequired: true + }); + } + + var globalVideoConditions = []; + + if (globalMaxVideoBitrate) { + globalVideoConditions.push({ + Condition: 'LessThanEqual', + Property: 'VideoBitrate', + Value: globalMaxVideoBitrate + }); + } + + if (maxVideoWidth) { + globalVideoConditions.push({ + Condition: 'LessThanEqual', + Property: 'Width', + Value: maxVideoWidth.toString(), + IsRequired: false + }); + } + + if (globalVideoConditions.length) { + profile.CodecProfiles.push({ + Type: 'Video', + Conditions: globalVideoConditions + }); + } + + if (browser.chromecast) { + profile.CodecProfiles.push({ + Type: 'Audio', + Codec: 'flac', + Conditions: [ + { + Condition: 'LessThanEqual', + Property: 'AudioSampleRate', + Value: '96000' + }] + }); + } + + // Subtitle profiles + // External vtt or burn in + profile.SubtitleProfiles = []; + if (supportsTextTracks()) { + profile.SubtitleProfiles.push({ + Format: 'vtt', + Method: 'External' + }); + } + + profile.ResponseProfiles = []; + profile.ResponseProfiles.push({ + Type: 'Video', + Container: 'm4v', + MimeType: 'video/mp4' + }); + + return profile; + }; +}); From 41896e126ae0e29383408d0aa159fce0aaf5126f Mon Sep 17 00:00:00 2001 From: NoFrosty Date: Mon, 30 Mar 2020 07:28:46 +0000 Subject: [PATCH 18/43] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strings/fr.json b/src/strings/fr.json index 234f8dace7..ced0674031 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -1446,7 +1446,7 @@ "LabelAudioChannels": "Canaux audio :", "HeaderFavoriteBooks": "Livres préférés", "FetchingData": "Récuperer des données suplémentaires", - "CopyStreamURLSuccess": "URL copiée.", + "CopyStreamURLSuccess": "URL copiée avec succès", "CopyStreamURL": "Copier l'URL du flux", "LabelBaseUrlHelp": "Vous pouvez ajouter un sous-répertoire personalisé ici pour accéder au serveur depuis une URL plus exclusive.", "HeaderFavoritePeople": "Personnes préférées", @@ -1460,9 +1460,9 @@ "LabelStreamType": "Type de flux :", "EnableFastImageFadeInHelp": "Activer un transition plus rapide pour images téléchargées", "EnableFastImageFadeIn": "Transition d'image rapide", - "LabelPlayerDimensions": "Dimension du lecteur:", - "LabelDroppedFrames": "Images perdues:", - "LabelCorruptedFrames": "Images corrompues:", + "LabelPlayerDimensions": "Dimension du lecteur :", + "LabelDroppedFrames": "Images perdues :", + "LabelCorruptedFrames": "Images corrompues :", "CopyStreamURLError": "Il y a eu une erreur lors de la copie du URL.", "AskAdminToCreateLibrary": "Demander à un administrateur de créer une médiathèque.", "AllowFfmpegThrottlingHelp": "Quand le transcodage ou le remultiplexage est suffisamment loin 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.", From d8fb547035b6cea7d23bc5e56957db1596dbbed0 Mon Sep 17 00:00:00 2001 From: AdmiralAnimE Date: Mon, 30 Mar 2020 14:12:03 +0000 Subject: [PATCH 19/43] Translated using Weblate (Bulgarian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/bg/ --- src/strings/bg-bg.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/strings/bg-bg.json b/src/strings/bg-bg.json index e6aaa30fb1..bf264bbcfb 100644 --- a/src/strings/bg-bg.json +++ b/src/strings/bg-bg.json @@ -303,9 +303,9 @@ "LabelCriticRating": "Оценка на критиците:", "LabelCurrentPassword": "Текуща парола:", "LabelCustomCertificatePath": "Път към потребителския сертификат:", - "LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12, съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.", + "LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12 (PKCS #12), съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.", "LabelCustomCss": "CSS по избор:", - "LabelCustomCssHelp": "Използвайте собствен CSS към уеб интерфейса.", + "LabelCustomCssHelp": "Използвайте собствен CSS към мрежовия интерфейс.", "LabelCustomDeviceDisplayName": "Показвано име:", "LabelCustomRating": "Оценка по избор:", "LabelDashboardTheme": "Облик на сървърното табло:", @@ -834,5 +834,10 @@ "AllowOnTheFlySubtitleExtraction": "Позволява моментално извличане на поднадписи", "AllowHWTranscodingHelp": "Позволява на тунера да прекодира моментално. Това може да помогне за редуциране на прекодирането от сървъра.", "AddItemToCollectionHelp": "Добавяне към колекция чрез търсенето им и използване на дясно-щракване с мишката или контекстното меню.", - "Absolute": "Aбсолютен" + "Absolute": "Aбсолютен", + "LabelLanNetworks": "Локални мрежи:", + "LabelKodiMetadataSaveImagePathsHelp": "Препоръчително е ако имате изображения, пътят към които не е съобразен с изискванията на Коди.", + "LabelKodiMetadataSaveImagePaths": "Записване на пътеките към изображенията в nfo файловете", + "LabelChannels": "Канали:", + "DropShadow": "Сянка" } From 86c2201718ef9df8b30a05adae7e8da88a8e8caf Mon Sep 17 00:00:00 2001 From: abdulaziz Date: Mon, 30 Mar 2020 22:18:06 +0000 Subject: [PATCH 20/43] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ar/ --- src/strings/ar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/ar.json b/src/strings/ar.json index 18ceb61dea..7fae30a084 100644 --- a/src/strings/ar.json +++ b/src/strings/ar.json @@ -918,7 +918,7 @@ "HeaderFavoriteEpisodes": "الحلقات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "Shows": "الحلقات", - "Books": "كتب", + "Books": "الكتب", "ValueSpecialEpisodeName": "مميز - {0}", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderAlbumArtists": "فناني الألبومات", From 4f0dd18e59f60ff15ce697efb73d226f2e107be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Rodr=C3=ADguez?= Date: Mon, 30 Mar 2020 19:37:01 +0000 Subject: [PATCH 21/43] Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es/ --- src/strings/es.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/strings/es.json b/src/strings/es.json index 10eb8655e1..df8ecf483f 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -1474,5 +1474,6 @@ "LabelDroppedFrames": "Frames perdidos:", "LabelCorruptedFrames": "Frames corruptos:", "AskAdminToCreateLibrary": "Solo un administrador puede crear librerías.", - "AllowFfmpegThrottling": "Acelerar transcodificación" + "AllowFfmpegThrottling": "Acelerar transcodificación", + "ClientSettings": "Ajustes de cliente" } From 35acc83b234a56559c8675ac5332c11b823189b9 Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Tue, 31 Mar 2020 03:19:32 +0000 Subject: [PATCH 22/43] 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 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/strings/zh-cn.json b/src/strings/zh-cn.json index 1a3aa1e4de..ccd90e05b5 100644 --- a/src/strings/zh-cn.json +++ b/src/strings/zh-cn.json @@ -217,7 +217,7 @@ "EveryNDays": "每 {0} 天", "ExitFullscreen": "退出全屏", "ExtraLarge": "特大", - "ExtractChapterImagesHelp": "提取章节图像将允许客户端显示一个图像形式的场景选择菜单。这个提取的过程可能会非常缓慢、占用大量 CPU 资源,并且可能需要几个GB的硬盘空间。提取将会在视频被发现时启动,同时也作为一个夜间计划任务运行。这个任务可以在“计划任务”选项中进行设置。不建议在高峰使用时间运行这个任务。", + "ExtractChapterImagesHelp": "提取剧集图片将允许客户端显示一个图像形式的场景选择菜单。这个提取的过程可能会非常缓慢、占用大量 CPU 资源,并且可能需要几个GB的硬盘空间。提取将会在视频被发现时启动,同时也作为一个夜间计划任务运行。这个任务可以在“计划任务”选项中进行设置。不建议在高峰使用时间运行这个任务。", "Extras": "额外", "FFmpegSavePathNotFound": "我们无法通过你输入的路径定位 FFmpeg。FFprobe 同样也是必要的并且应该被放在同一个文件夹中。他们通常会被打包在一起以供下载。请检查这个路径然后再试一次。", "FastForward": "快进", @@ -277,7 +277,7 @@ "HeaderCastCrew": "演职人员", "HeaderChannelAccess": "频道访问", "HeaderChannels": "频道", - "HeaderChapterImages": "章节图片", + "HeaderChapterImages": "剧集图片", "HeaderCodecProfile": "编解码器配置", "HeaderCodecProfileHelp": "编解码器的配置文件标明了设备播放特定编码时的限制。如果在限制之内则媒体将被转码,否则编解码器将被配置为直接播放。", "HeaderConfigureRemoteAccess": "配置远程访问", @@ -568,8 +568,8 @@ "LabelEpisodeNumber": "集号:", "LabelEvent": "事件:", "LabelEveryXMinutes": "每:", - "LabelExtractChaptersDuringLibraryScan": "媒体库扫描过程中解压章节图像", - "LabelExtractChaptersDuringLibraryScanHelp": "当媒体库导入视频并扫描时,将提取章节图像。否则,章节图像将在之后的计划任务提取,而媒体库会更快完成扫描。", + "LabelExtractChaptersDuringLibraryScan": "媒体库扫描过程中解压剧集图片", + "LabelExtractChaptersDuringLibraryScanHelp": "当媒体库导入视频并扫描时,将提取剧集图片。否则,剧集图片将在之后的计划任务提取,而媒体库会更快完成扫描。", "LabelFailed": "失败", "LabelFileOrUrl": "文件或网址:", "LabelFinish": "完成", @@ -998,7 +998,7 @@ "OptionEstimateContentLength": "转码时,估计内容长度", "OptionEveryday": "每天", "OptionExternallyDownloaded": "外部下载", - "OptionExtractChapterImage": "开启章节图像提取", + "OptionExtractChapterImage": "开启剧集图片提取", "OptionFavorite": "我的最爱", "OptionFriday": "星期五", "OptionHasSpecialFeatures": "特殊功能", From be60f23264f0b8f7efe13063f8a159be464f1ba4 Mon Sep 17 00:00:00 2001 From: pucherot Date: Tue, 31 Mar 2020 10:52:02 +0000 Subject: [PATCH 23/43] Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/es/ --- src/strings/es.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/strings/es.json b/src/strings/es.json index df8ecf483f..06318c22d3 100644 --- a/src/strings/es.json +++ b/src/strings/es.json @@ -183,7 +183,7 @@ "ErrorAddingListingsToSchedulesDirect": "Ha habido un error añadiendo la alineación a tu cuenta de Schedules Direct. Schedules Direct solo permite un determinado número de alineaciones por cuenta. Necesitarás iniciar sesión en la web de Schedules Direct y quitar otras listas de tu cuenta antes de proceder.", "ErrorAddingMediaPathToVirtualFolder": "Ha habido un error añadiendo la ruta de los medios. Por favor, asegúrate de que la ruta es válida y que el proceso del servidor Jellyfin tiene acceso a esa ubicación.", "ErrorAddingTunerDevice": "Ha habido un error añadiendo el dispositivo sintonizador. Por favor, asegúrate de que es accesible e inténtalo otra vez.", - "ErrorAddingXmlTvFile": "Hubo un un error accediendo el archivo XML. Por favor, asegurese que el archivo existe e inténtalo de nuevo.", + "ErrorAddingXmlTvFile": "Ha sucedido un error accediendo al archivo XML. Por favor, asegúrate que el archivo existe e inténtalo de nuevo.", "ErrorGettingTvLineups": "Ha habido un error descargando la programación de TV. Por favor, asegúrese que la información es correcta e inténtalo de nuevo.", "ErrorMessageStartHourGreaterThanEnd": "La hora de finalización tiene que ser mayor que la de inicio.", "ErrorPleaseSelectLineup": "Por favor selecciona una alineación e inténtalo otra vez. Si no hay alineaciones disponibles, revisa que tu nombre de usuario, contraseña y código postal son correctos.", @@ -420,7 +420,7 @@ "Help": "Ayuda", "Hide": "Ocultar", "HideWatchedContentFromLatestMedia": "Esconder medios vistos de los medios más recientes", - "HttpsRequiresCert": "Para activar la conexión segura, necesitas un certificado SSL de confianza, como Let's Encrypt. De lo contrario, desactive las conexiones seguras", + "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.", @@ -1248,14 +1248,14 @@ "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.", - "Director": "Director", + "Director": "Dirección", "Directors": "Directores", "Display": "Mostrar", "DisplayInMyMedia": "Mostrar en la pantalla de inicio", "DisplayInOtherHomeScreenSections": "Mostrar en las secciones de la pantalla de inicio al igual que \"últimos\" y \"continuar viendo\"", "DisplayMissingEpisodesWithinSeasons": "Mostrar episodios ausentes en las temporadas", "DisplayMissingEpisodesWithinSeasonsHelp": "Esto también debe ser habilitado para la biblioteca de TV en la configuración del servidor.", - "DropShadow": "Sombra", + "DropShadow": "Eliminar sombra", "EditMetadata": "Editar etiquetas", "EnableBackdrops": "Imágenes de fondo", "EnableBackdropsHelp": "Mostrar imágenes de fondo en algunas páginas mientras se explora la biblioteca.", @@ -1404,7 +1404,7 @@ "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", + "ShowTitle": "Título del show", "ShowYear": "Año del show", "SmallCaps": "Letras minúsculas", "Smaller": "Más pequeño", @@ -1455,9 +1455,9 @@ "MusicLibraryHelp": "Revisar la {0}guía de nombres de música{1}.", "FetchingData": "Obteniendo datos adicionales", "ButtonAddImage": "Añadir imagen", - "HeaderFavoritePeople": "Gente Favorita", + "HeaderFavoritePeople": "Gente favorita", "OptionRandom": "Aleatorio", - "SelectAdminUsername": "Por favor seleccione un nombre de usuario para la cuenta de administrador.", + "SelectAdminUsername": "Por favor seleccione un nombre de usuario para la cuenta administrador.", "ButtonSplit": "Dividir", "HeaderNavigation": "Navegación", "MessageConfirmAppExit": "¿Quieres salir?", @@ -1475,5 +1475,7 @@ "LabelCorruptedFrames": "Frames corruptos:", "AskAdminToCreateLibrary": "Solo un administrador puede crear librerías.", "AllowFfmpegThrottling": "Acelerar transcodificación", - "ClientSettings": "Ajustes de cliente" + "ClientSettings": "Ajustes de cliente", + "PreferEmbeddedEpisodeInfosOverFileNames": "Priorizar la información embebida sobre los nombres de archivos", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Usar la información de episodio de los metadatos embebidos si está disponible." } From f6543d01795d072f9633191c7de01c3cb5d3cae0 Mon Sep 17 00:00:00 2001 From: amirmasoud Date: Tue, 31 Mar 2020 08:08:37 +0000 Subject: [PATCH 24/43] Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fa/ --- src/strings/fa.json | 122 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 20 deletions(-) diff --git a/src/strings/fa.json b/src/strings/fa.json index d4b60fe7c6..d9afcd66cc 100644 --- a/src/strings/fa.json +++ b/src/strings/fa.json @@ -35,7 +35,7 @@ "HeaderInstantMix": "درهم کردن فوری", "HeaderKodiMetadataHelp": "برای فعال یا غیرفعال سازی متاداده های Nfo ، یک کتابخانه را در صفحه تنظیم کتابخانه Jellyfin ویرایش کرده و قسمت سرورهای متاداده را مسیردهی کنید.", "HeaderLatestEpisodes": "آخرین قسمت ها", - "HeaderNextUp": "بعدی", + "HeaderNextUp": "قسمت بعدی", "HeaderPaths": "مسیرها", "HeaderPlayAll": "پخش همه", "HeaderPreferredMetadataLanguage": "زبان مدنظر اطلاعات محتوی", @@ -113,34 +113,116 @@ "UserProfilesIntro": "Jellyfin دارای پشتیبانی داخلی از پروفایل کاربران می باشد. با فعال سازی هر کاربر، او می تواند تنظیمات ، وضعیت پخش و کنترل والدین خاص خودش را داشته باشد.", "WelcomeToProject": "به Jellyfin خوش آمدید!", "WizardCompleted": "همه چیزی که فعلا می خواهیم همین است.جمع آوری اطلاعات کتابخانه های شما هم اکنون توسط Jellyfin آغاز شده است. اپلیکیشن های ما را امتحان کنید و سپس بر روی پایان کلیک کنید تا پیشخوان سرور را مشاهده نمایید.", - "Albums": "آلبوم ها", + "Albums": "آلبوم‌ها", "Artists": "هنرمندان", - "Books": "کتاب ها", - "Channels": "کانال ها", - "Collections": "کلکسیون ها", - "Favorites": "مورد علاقه ها", - "Folders": "پوشه ها", + "Books": "کتاب‌ها", + "Channels": "کانال‌ها", + "Collections": "مجموعه‌ها", + "Favorites": "مورد علاقه‌ها", + "Folders": "پوشه‌ها", "Genres": "ژانرها", "HeaderAlbumArtists": "هنرمندان آلبوم", - "HeaderFavoriteShows": "سریال های مورد علاقه", - "HeaderFavoriteEpisodes": "قسمت های مورد علاقه", - "HeaderFavoriteAlbums": "آلبوم های مورد علاقه", + "HeaderFavoriteShows": "سریال‌های مورد علاقه", + "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه", + "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه", "HeaderFavoriteArtists": "هنرمندان مورد علاقه", - "HeaderFavoriteSongs": "آهنگ های مورد علاقه", + "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه", "HeaderLiveTV": "پخش زنده تلویزیون", - "Movies": "فیلم های سینمایی", - "Photos": "عکس ها", - "Playlists": "لیست های پخش", - "Shows": "سریال ها", - "Songs": "آهنگ ها", - "Sync": "همگامسازی", - "ValueSpecialEpisodeName": "ویژه- {0}", + "Movies": "فیلم‌ها", + "Photos": "عکس‌ها", + "Playlists": "لیست‌های پخش", + "Shows": "سریال‌ها", + "Songs": "موسیقی‌ها", + "Sync": "همگام‌سازی", + "ValueSpecialEpisodeName": "ویژه - {0}", "AllEpisodes": "تمام قسمت ها", "AllLanguages": "تمام زبان ها", "AllLibraries": "تمام کتابخانه ها", - "AllowHWTranscodingHelp": "اگر فعال شود, اجازه میدهید تبدیل ( کم و زیاد کردن کیفیت ) درلحظه و توسط کارت دریافت سیگنال صورت گیرد. این کمک میکند به اینکه سرور جلیفین کمتر عمل تبدیل را انجام دهد.", + "AllowHWTranscodingHelp": "اگر فعال شود, اجازه می‌دهید تبدیل کیفیت در لحظه انجام شود. این ممکن است به کاهش کدگذاری لازم برای Jellyfin منجر بشود.", "AllowOnTheFlySubtitleExtraction": "اجازه میدهد در لحظه زیرنویس بازشود", "Add": "افزودن", "Actor": "بازیگر", - "AccessRestrictedTryAgainLater": "دسترسی در حال حاضر محدود شده است. لطفا دوباره تلاش کنید." + "AccessRestrictedTryAgainLater": "دسترسی در حال حاضر محدود شده است. لطفا دوباره تلاش کنید.", + "ButtonShuffle": "مخلوط کردن", + "ButtonSettings": "تنظیمات", + "ButtonSend": "ارسال", + "ButtonSelectView": "انتخاب نما", + "ButtonSelectServer": "انتخاب سرور", + "ButtonSearch": "جستجو", + "ButtonScanAllLibraries": "پویش تمام کتابخانه‌ها", + "ButtonRevoke": "ابطال", + "ButtonResume": "ادامه", + "ButtonRestart": "راه اندازی مجدد", + "ButtonResetEasyPassword": "بازنشانی کد پین آسان", + "ButtonRepeat": "تکرار", + "ButtonRename": "تغییر نام", + "ButtonRemove": "حذف", + "ButtonRefreshGuideData": "به‌روز‌رسانی داده‌ی راهنما", + "ButtonRefresh": "به‌روز‌رسانی", + "ButtonProfile": "نمایه", + "ButtonNextTrack": "ترانه پسین", + "ButtonPreviousTrack": "ترانه پیشین", + "ButtonPause": "مکث", + "ButtonParentalControl": "کنترل والدین", + "ButtonOpen": "باز", + "ButtonOff": "خاموش", + "ButtonNetwork": "شبکه", + "ButtonMore": "بیشتر", + "ButtonManualLogin": "ورود دستی", + "ButtonLibraryAccess": "دسترسی به کتابخانه", + "ButtonLearnMore": "بیشتر بدانید", + "ButtonInfo": "اطلاعات", + "ButtonHome": "خانه", + "ButtonHelp": "کمک", + "ButtonGuide": "راهنما", + "ButtonGotIt": "متوجه شدم", + "ButtonFullscreen": "تمام صفحه", + "ButtonForgotPassword": "فراموشی گذرواژه", + "ButtonEditImages": "ویرایش عکس‌ها", + "ButtonEdit": "ویرایش", + "ButtonDownload": "بارگیری", + "ButtonDown": "پایین", + "ButtonDelete": "حذف", + "ButtonConnect": "اتصال", + "ButtonChangeServer": "تغییر سرور", + "ButtonBack": "بازگشت", + "ButtonArrowUp": "بالا", + "ButtonArrowRight": "راست", + "ButtonArrowLeft": "چپ", + "ButtonArrowDown": "پایین", + "ButtonAddServer": "افزودن سرور", + "ButtonAddScheduledTaskTrigger": "افزودن راه انداز", + "ButtonAddMediaLibrary": "افزودن کتابخانه رسانه", + "ButtonAddImage": "افزودن تصویر", + "ButtonAdd": "افزودن", + "BoxRear": "جعبه (پشت)", + "Box": "جعبه", + "Blacklist": "لیست سیاه", + "BirthPlaceValue": "محل تولد: {0}", + "BirthLocation": "محل تولد", + "BirthDateValue": "متولد: {0}", + "Banner": "سرصفحه", + "Backdrops": "پس زمینه‌ها", + "Backdrop": "پس زمینه", + "AutoBasedOnLanguageSetting": "خودکار (بر اساس تنظیمات زبانی)", + "Auto": "خودکار", + "Audio": "صدا", + "AttributeNew": "جدید", + "AspectRatio": "نسبت ابعاد", + "AskAdminToCreateLibrary": "از کاربر مدیر بخواهید که یک کتابخانه ایجاد کند.", + "Ascending": "بالا رونده", + "AsManyAsPossible": "تا حدی که ممکن است", + "AroundTime": "حدود {0}", + "Anytime": "هر زمانی", + "AnyLanguage": "هر زبانی", + "AlwaysPlaySubtitles": "همیشه زیرنویس را نمایش بده", + "AllowFfmpegThrottling": "گلوگاه تبدیل کیفیت", + "AllChannels": "همه‌ی کانال‌ها", + "Alerts": "هشدارها", + "Aired": "پخش شده", + "AirDate": "تاریخ پخش", + "AddedOnValue": "{0} افزوده شد", + "AddToPlaylist": "افزودن به لیست پخش", + "AddToPlayQueue": "افزودن به صف پخش", + "AddToCollection": "افزودن به مجموعه" } From a1cc9778725bc8e4eaf4c0408919f39b4640c02b Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 31 Mar 2020 18:59:12 +0300 Subject: [PATCH 25/43] Update documentation --- src/components/autoFocuser.js | 6 +- src/components/dom.js | 52 +++++------ src/components/scrollManager.js | 148 +++++++++++++++++++++----------- 3 files changed, 125 insertions(+), 81 deletions(-) diff --git a/src/components/autoFocuser.js b/src/components/autoFocuser.js index 93ebb4b3be..a469eb8854 100644 --- a/src/components/autoFocuser.js +++ b/src/components/autoFocuser.js @@ -21,7 +21,7 @@ import layoutManager from "layoutManager"; } /** - * Start AutoFocuser + * Start AutoFocuser. */ export function enable() { if (!isEnabled()) { @@ -37,8 +37,8 @@ import layoutManager from "layoutManager"; /** * Set focus on a suitable element, taking into account the previously selected. - * @param {HTMLElement} [container] - element to limit scope - * @returns {HTMLElement} focused element + * @param {HTMLElement} [container] - Element to limit scope. + * @returns {HTMLElement} Focused element. */ export function autoFocus(container) { if (!isEnabled()) { diff --git a/src/components/dom.js b/src/components/dom.js index fdc5e607eb..3fe4287320 100644 --- a/src/components/dom.js +++ b/src/components/dom.js @@ -7,10 +7,10 @@ /** * Returns parent of element with specified attribute value. - * @param {HTMLElement} elem - element whose parent need to find - * @param {string} name - attribute name - * @param {mixed} value - attribute value - * @returns {HTMLElement} Parent with specified attribute value + * @param {HTMLElement} elem - Element whose parent need to find. + * @param {string} name - Attribute name. + * @param {mixed} value - Attribute value. + * @returns {HTMLElement} Parent with specified attribute value. */ export function parentWithAttribute(elem, name, value) { while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) { @@ -26,9 +26,9 @@ /** * Returns parent of element with one of specified tag names. - * @param {HTMLElement} elem - element whose parent need to find - * @param {(string|Array)} tagNames - tag name or array of tag names - * @returns {HTMLElement} Parent with one of specified tag names + * @param {HTMLElement} elem - Element whose parent need to find. + * @param {(string|Array)} tagNames - Tag name or array of tag names. + * @returns {HTMLElement} Parent with one of specified tag names. */ export function parentWithTag(elem, tagNames) { // accept both string and array passed in @@ -49,9 +49,9 @@ /** * Returns _true_ if class list contains one of specified names. - * @param {DOMTokenList} classList - class list - * @param {Array} classNames - array of class names - * @returns {boolean} _true_ if class list contains one of specified names + * @param {DOMTokenList} classList - Class list. + * @param {Array} classNames - Array of class names. + * @returns {boolean} _true_ if class list contains one of specified names. */ function containsAnyClass(classList, classNames) { for (let i = 0, length = classNames.length; i < length; i++) { @@ -64,9 +64,9 @@ /** * Returns parent of element with one of specified class names. - * @param {HTMLElement} elem - element whose parent need to find - * @param {(string|Array)} classNames - class name or array of class names - * @returns {HTMLElement} Parent with one of specified class names + * @param {HTMLElement} elem - Element whose parent need to find. + * @param {(string|Array)} classNames - Class name or array of class names. + * @returns {HTMLElement} Parent with one of specified class names. */ export function parentWithClass(elem, classNames) { // accept both string and array passed in @@ -100,10 +100,10 @@ /** * Adds event listener to specified target. - * @param {EventTarget} target - event target - * @param {string} type - event type - * @param {function} handler - event handler - * @param {Object} [options] - listener options + * @param {EventTarget} target - Event target. + * @param {string} type - Event type. + * @param {function} handler - Event handler. + * @param {Object} [options] - Listener options. */ export function addEventListener(target, type, handler, options) { let optionsOrCapture = options || {}; @@ -115,10 +115,10 @@ /** * Removes event listener from specified target. - * @param {EventTarget} target - event target - * @param {string} type - event type - * @param {function} handler - event handler - * @param {Object} [options] - listener options + * @param {EventTarget} target - Event target. + * @param {string} type - Event type. + * @param {function} handler - Event handler. + * @param {Object} [options] - Listener options. */ export function removeEventListener(target, type, handler, options) { let optionsOrCapture = options || {}; @@ -147,7 +147,7 @@ /** * Returns window size. - * @returns {Object} Window size + * @returns {Object} Window size. */ export function getWindowSize() { if (!windowSize) { @@ -173,7 +173,7 @@ /** * Returns screen width. - * @returns {number} Screen width + * @returns {number} Screen width. */ export function getScreenWidth() { let width = window.innerWidth; @@ -197,7 +197,7 @@ /** * Returns name of animation end event. - * @returns {string} Name of animation end event + * @returns {string} Name of animation end event. */ export function whichAnimationEvent() { if (_animationEvent) { @@ -224,7 +224,7 @@ /** * Returns name of animation cancel event. - * @returns {string} Name of animation cancel event + * @returns {string} Name of animation cancel event. */ export function whichAnimationCancelEvent() { return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel'); @@ -237,7 +237,7 @@ /** * Returns name of transition end event. - * @returns {string} Name of transition end event + * @returns {string} Name of transition end event. */ export function whichTransitionEvent() { if (_transitionEvent) { diff --git a/src/components/scrollManager.js b/src/components/scrollManager.js index 96317fa998..037ca5b059 100644 --- a/src/components/scrollManager.js +++ b/src/components/scrollManager.js @@ -24,7 +24,7 @@ import layoutManager from "layoutManager"; * Returns minimum vertical scroll. * Scroll less than that value will be zeroed. * - * @return {number} minimum vertical scroll + * @return {number} Minimum vertical scroll. */ function minimumScrollY() { const topMenu = document.querySelector(".headerTop"); @@ -55,10 +55,10 @@ import layoutManager from "layoutManager"; /** * Returns value clamped by range [min, max]. * - * @param {number} value - clamped value - * @param {number} min - begining of range - * @param {number} max - ending of range - * @return {number} clamped value + * @param {number} value - Clamped value. + * @param {number} min - Begining of range. + * @param {number} max - Ending of range. + * @return {number} Clamped value. */ function clamp(value, min, max) { return value <= min ? min : value >= max ? max : value; @@ -68,11 +68,11 @@ import layoutManager from "layoutManager"; * Returns the required delta to fit range 1 into range 2. * In case of range 1 is bigger than range 2 returns delta to fit most out of range part. * - * @param {number} begin1 - begining of range 1 - * @param {number} end1 - ending of range 1 - * @param {number} begin2 - begining of range 2 - * @param {number} end2 - ending of range 2 - * @return {number} delta: <0 move range1 to the left, >0 - to the right + * @param {number} begin1 - Begining of range 1. + * @param {number} end1 - Ending of range 1. + * @param {number} begin2 - Begining of range 2. + * @param {number} end2 - Ending of range 2. + * @return {number} Delta: <0 move range1 to the left, >0 - to the right. */ function fitRange(begin1, end1, begin2, end2) { const delta1 = begin1 - begin2; @@ -88,13 +88,21 @@ import layoutManager from "layoutManager"; /** * Ease value. * - * @param {number} t - value in range [0, 1] - * @return {number} eased value in range [0, 1] + * @param {number} t - Value in range [0, 1]. + * @return {number} Eased value in range [0, 1]. */ function ease(t) { return t*(2 - t); // easeOutQuad === ease-out } + /** + * @typedef {Object} Rect + * @property {number} left - X coordinate of top-left corner. + * @property {number} top - Y coordinate of top-left corner. + * @property {number} width - Width. + * @property {number} height - Height. + */ + /** * Document scroll wrapper helps to unify scrolling and fix issues of some browsers. * @@ -109,6 +117,10 @@ import layoutManager from "layoutManager"; * Tizen 5 Browser/Native: scrolls documentElement (and window); has a document.scrollingElement */ class DocumentScroller { + /** + * Horizontal scroll position. + * @type {number} + */ get scrollLeft() { return window.pageXOffset; } @@ -117,6 +129,10 @@ import layoutManager from "layoutManager"; window.scroll(val, window.pageYOffset); } + /** + * Vertical scroll position. + * @type {number} + */ get scrollTop() { return window.pageYOffset; } @@ -125,22 +141,42 @@ import layoutManager from "layoutManager"; window.scroll(window.pageXOffset, val); } + /** + * Horizontal scroll size (scroll width). + * @type {number} + */ get scrollWidth() { return Math.max(document.documentElement.scrollWidth, document.body.scrollWidth); } + /** + * Vertical scroll size (scroll height). + * @type {number} + */ get scrollHeight() { return Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); } + /** + * Horizontal client size (client width). + * @type {number} + */ get clientWidth() { return Math.min(document.documentElement.clientWidth, document.body.clientWidth); } + /** + * Vertical client size (client height). + * @type {number} + */ get clientHeight() { return Math.min(document.documentElement.clientHeight, document.body.clientHeight); } + /** + * Returns bounding client rect. + * @return {Rect} Bounding client rect. + */ getBoundingClientRect() { // Make valid viewport coordinates: documentElement.getBoundingClientRect returns rect of entire document relative to viewport return { @@ -151,6 +187,10 @@ import layoutManager from "layoutManager"; }; } + /** + * Scrolls window. + * @param {...mixed} args See window.scrollTo. + */ scrollTo() { window.scrollTo.apply(window, arguments); } @@ -162,10 +202,11 @@ import layoutManager from "layoutManager"; const documentScroller = new DocumentScroller(); /** - * Returns parent element that can be scrolled. If no such, returns documentElement. + * Returns parent element that can be scrolled. If no such, returns document scroller. * - * @param {HTMLElement} element - element for which parent is being searched - * @param {boolean} vertical - search for vertical scrollable parent + * @param {HTMLElement} element - Element for which parent is being searched. + * @param {boolean} vertical - Search for vertical scrollable parent. + * @param {HTMLElement|DocumentScroller} Parent element that can be scrolled or document scroller. */ function getScrollableParent(element, vertical) { if (element) { @@ -197,17 +238,17 @@ import layoutManager from "layoutManager"; /** * @typedef {Object} ScrollerData - * @property {number} scrollPos - current scroll position - * @property {number} scrollSize - scroll size - * @property {number} clientSize - client size + * @property {number} scrollPos - Current scroll position. + * @property {number} scrollSize - Scroll size. + * @property {number} clientSize - Client size. */ /** - * Returns scroll data for specified orientation. + * Returns scroller data for specified orientation. * - * @param {HTMLElement} scroller - scroller - * @param {boolean} vertical - vertical scroll data - * @return {ScrollerData} scroll data + * @param {HTMLElement} scroller - Scroller. + * @param {boolean} vertical - Vertical scroller data. + * @return {ScrollerData} Scroller data. */ function getScrollerData(scroller, vertical) { let data = {}; @@ -228,10 +269,10 @@ import layoutManager from "layoutManager"; /** * Returns position of child of scroller for specified orientation. * - * @param {HTMLElement} scroller - scroller - * @param {HTMLElement} element - child of scroller - * @param {boolean} vertical - vertical scroll - * @return {number} child position + * @param {HTMLElement} scroller - Scroller. + * @param {HTMLElement} element - Child of scroller. + * @param {boolean} vertical - Vertical scroll. + * @return {number} Child position. */ function getScrollerChildPos(scroller, element, vertical) { const elementRect = element.getBoundingClientRect(); @@ -247,11 +288,11 @@ import layoutManager from "layoutManager"; /** * Returns scroll position for element. * - * @param {ScrollerData} scrollerData - scroller data - * @param {number} elementPos - child element position - * @param {number} elementSize - child element size - * @param {boolean} centered - scroll to center - * @return {number} scroll position + * @param {ScrollerData} scrollerData - Scroller data. + * @param {number} elementPos - Child element position. + * @param {number} elementSize - Child element size. + * @param {boolean} centered - Scroll to center. + * @return {number} Scroll position. */ function calcScroll(scrollerData, elementPos, elementSize, centered) { const maxScroll = scrollerData.scrollSize - scrollerData.clientSize; @@ -271,8 +312,8 @@ import layoutManager from "layoutManager"; /** * Calls scrollTo function in proper way. * - * @param {HTMLElement} scroller - scroller - * @param {ScrollToOptions} options - scroll options + * @param {HTMLElement} scroller - Scroller. + * @param {ScrollToOptions} options - Scroll options. */ function scrollToHelper(scroller, options) { if ("scrollTo" in scroller) { @@ -296,11 +337,11 @@ import layoutManager from "layoutManager"; /** * Performs built-in scroll. * - * @param {HTMLElement} xScroller - horizontal scroller - * @param {number} scrollX - horizontal coordinate - * @param {HTMLElement} yScroller - vertical scroller - * @param {number} scrollY - vertical coordinate - * @param {boolean} smooth - smooth scrolling + * @param {HTMLElement} xScroller - Horizontal scroller. + * @param {number} scrollX - Horizontal coordinate. + * @param {HTMLElement} yScroller - Vertical scroller. + * @param {number} scrollY - Vertical coordinate. + * @param {boolean} smooth - Smooth scrolling. */ function builtinScroll(xScroller, scrollX, yScroller, scrollY, smooth) { const scrollBehavior = smooth ? "smooth" : "instant"; @@ -313,6 +354,9 @@ import layoutManager from "layoutManager"; } } + /** + * Requested frame for animated scroll. + */ let scrollTimer; /** @@ -326,10 +370,10 @@ import layoutManager from "layoutManager"; /** * Performs animated scroll. * - * @param {HTMLElement} xScroller - horizontal scroller - * @param {number} scrollX - horizontal coordinate - * @param {HTMLElement} yScroller - vertical scroller - * @param {number} scrollY - vertical coordinate + * @param {HTMLElement} xScroller - Horizontal scroller. + * @param {number} scrollX - Horizontal coordinate. + * @param {HTMLElement} yScroller - Vertical scroller. + * @param {number} scrollY - Vertical coordinate. */ function animateScroll(xScroller, scrollX, yScroller, scrollY) { @@ -372,11 +416,11 @@ import layoutManager from "layoutManager"; /** * Performs scroll. * - * @param {HTMLElement} xScroller - horizontal scroller - * @param {number} scrollX - horizontal coordinate - * @param {HTMLElement} yScroller - vertical scroller - * @param {number} scrollY - vertical coordinate - * @param {boolean} smooth - smooth scrolling + * @param {HTMLElement} xScroller - Horizontal scroller. + * @param {number} scrollX - Horizontal coordinate. + * @param {HTMLElement} yScroller - Vertical scroller. + * @param {number} scrollY - Vertical coordinate. + * @param {boolean} smooth - Smooth scrolling. */ function doScroll(xScroller, scrollX, yScroller, scrollY, smooth) { @@ -420,9 +464,9 @@ import layoutManager from "layoutManager"; /** * Scrolls the document to a given position. * - * @param {number} scrollX - horizontal coordinate - * @param {number} scrollY - vertical coordinate - * @param {boolean} [smooth=false] - smooth scrolling + * @param {number} scrollX - Horizontal coordinate. + * @param {number} scrollY - Vertical coordinate. + * @param {boolean} [smooth=false] - Smooth scrolling. */ export function scrollTo(scrollX, scrollY, smooth) { @@ -443,8 +487,8 @@ import layoutManager from "layoutManager"; /** * Scrolls the document to a given element. * - * @param {HTMLElement} element - target element of scroll task - * @param {boolean} [smooth=false] - smooth scrolling + * @param {HTMLElement} element - Target element of scroll task. + * @param {boolean} [smooth=false] - Smooth scrolling. */ export function scrollToElement(element, smooth) { From 8789b1b69b2b5298fe90aaee51ce7ec1f151622c Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 31 Mar 2020 19:07:03 +0300 Subject: [PATCH 26/43] Update documentation --- src/components/input/keyboardnavigation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/input/keyboardnavigation.js b/src/components/input/keyboardnavigation.js index caddf46797..d356854a3e 100644 --- a/src/components/input/keyboardnavigation.js +++ b/src/components/input/keyboardnavigation.js @@ -60,8 +60,8 @@ if (!hasFieldKey) { /** * Returns key name from event. * - * @param {KeyboardEvent} event - keyboard event - * @return {string} key name + * @param {KeyboardEvent} event - Keyboard event. + * @return {string} Key name. */ export function getKeyName(event) { return KeyNames[event.keyCode] || event.key; @@ -70,8 +70,8 @@ export function getKeyName(event) { /** * Returns _true_ if key is used for navigation. * - * @param {string} key - key name - * @return {boolean} _true_ if key is used for navigation + * @param {string} key - Key name. + * @return {boolean} _true_ if key is used for navigation. */ export function isNavigationKey(key) { return NavigationKeys.indexOf(key) != -1; From c202339b9a60f87d78f2693fefa680e672e1a269 Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Tue, 31 Mar 2020 15:11:31 +0000 Subject: [PATCH 27/43] Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/sk/ --- src/strings/sk.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/strings/sk.json b/src/strings/sk.json index e0bebec52f..c90853c5f2 100644 --- a/src/strings/sk.json +++ b/src/strings/sk.json @@ -1468,5 +1468,8 @@ "AskAdminToCreateLibrary": "Pokiaľ chcete vytvoriť knižnicu, musíte sa spýtať administrátora.", "PlaybackErrorNoCompatibleStream": "Nastal problém s profilom klienta a server preto neposiela kompatibilný mediálny formát.", "AllowFfmpegThrottlingHelp": "Keď sa transkódovanie alebo remuxovanie dostane do bodu, kedy je dostatočne vopred voči súčasnej polohe prehrávania, pozastaví proces aby spotrebovával menej zdrojov. Toto je najviac užitočné, keď sa pozerá obsah bez pretáčania. Vypnite túto možnosť, pokiaľ má vaše prehrávanie problémy.", - "AllowFfmpegThrottling": "Obmedzenie transkódovania" + "AllowFfmpegThrottling": "Obmedzenie transkódovania", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferovať vložené informácie o epizóde pred názvom súboru", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Toto využíva informácie o epizóde z vložených metadát, pokiaľ sú dostupne.", + "ClientSettings": "Nastavenie klienta" } From 3d7e7105e177b033f9d150ba8aa9028538af5636 Mon Sep 17 00:00:00 2001 From: Medzhnun Date: Wed, 1 Apr 2020 12:00:43 +0000 Subject: [PATCH 28/43] Translated using Weblate (Bulgarian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/bg/ --- src/strings/bg-bg.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/strings/bg-bg.json b/src/strings/bg-bg.json index bf264bbcfb..4a9c3146d5 100644 --- a/src/strings/bg-bg.json +++ b/src/strings/bg-bg.json @@ -10,7 +10,7 @@ "All": "Всички", "AllLibraries": "Всички библиотеки", "Art": "Картина", - "Artists": "Изпълнители", + "Artists": "Артисти", "AttributeNew": "Нови", "Audio": "Звук", "Auto": "Автоматично", @@ -839,5 +839,7 @@ "LabelKodiMetadataSaveImagePathsHelp": "Препоръчително е ако имате изображения, пътят към които не е съобразен с изискванията на Коди.", "LabelKodiMetadataSaveImagePaths": "Записване на пътеките към изображенията в nfo файловете", "LabelChannels": "Канали:", - "DropShadow": "Сянка" + "DropShadow": "Сянка", + "Raised": "Повишено", + "OptionResElement": "рес. елемент" } From e69bb1534b7e2231fa910222a894cd2b7d19f17f Mon Sep 17 00:00:00 2001 From: KGT1 Date: Tue, 31 Mar 2020 23:33:47 +0000 Subject: [PATCH 29/43] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/de/ --- src/strings/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/de.json b/src/strings/de.json index 97cb1f959e..3dab52c547 100644 --- a/src/strings/de.json +++ b/src/strings/de.json @@ -1401,7 +1401,7 @@ "TitleSupport": "Hilfe", "Whitelist": "Erlaubt", "AuthProviderHelp": "Auswählen eines Authentifizierungsanbieter, der zur Authentifizierung des Passworts dieses Benutzes verwendet werden soll.", - "Features": "Features", + "Features": "Funktionen", "HeaderFavoriteBooks": "Lieblingsbücher", "HeaderFavoriteMovies": "Lieblingsfilme", "HeaderFavoriteShows": "Lieblingsserien", From bb834a79e7be143235e1b1ed02c5616e323046c0 Mon Sep 17 00:00:00 2001 From: amirmasoud Date: Wed, 1 Apr 2020 12:24:21 +0000 Subject: [PATCH 30/43] Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fa/ --- src/strings/fa.json | 93 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/src/strings/fa.json b/src/strings/fa.json index d9afcd66cc..6dcc8cbb66 100644 --- a/src/strings/fa.json +++ b/src/strings/fa.json @@ -224,5 +224,96 @@ "AddedOnValue": "{0} افزوده شد", "AddToPlaylist": "افزودن به لیست پخش", "AddToPlayQueue": "افزودن به صف پخش", - "AddToCollection": "افزودن به مجموعه" + "AddToCollection": "افزودن به مجموعه", + "ExitFullscreen": "خروج از تمام صفحه", + "EveryNDays": "هر {0} روز", + "ErrorMessageStartHourGreaterThanEnd": "زمان پایان باید پس از زمان شروع باشد.", + "Episodes": "قسمت‌ها", + "EndsAtValue": "تمام شده در {0}", + "Ended": "تمام شده", + "EnableThemeVideos": "تم فیلم‌ها", + "EnableThemeSongs": "آهنگ‌های تم", + "EnableStreamLooping": "چرخش خودکار پخش‌های زنده", + "EnablePhotos": "نمایش عکس‌ها", + "EnableNextVideoInfoOverlay": "نمایش اطلاعات ودیوی بعدی حین پخش ویدیو", + "EnableHardwareEncoding": "فعال سازی رمزگذاری سخت افزاری", + "EnableExternalVideoPlayersHelp": "یک منوی پخش کننده ویدیوی خارجی، زمانی که شروع به پخش ویدیو می‌شود نمایش داده خواهد شد.", + "EnableExternalVideoPlayers": "پخش کننده ویدیوی خارجی", + "EnableDisplayMirroring": "نمایش حالت آینه", + "EnableCinemaMode": "حالت سینما", + "EnableBackdrops": "پشت‌زمینه‌ها", + "EditSubtitles": "ویرایش زیرنویس‌ها", + "EditMetadata": "ویرایش ابرداده", + "EditImages": "ویرایش عکس‌ها", + "Edit": "ویرایش", + "DropShadow": "سایه پشت زمینه", + "DrmChannelsNotImported": "کانال‌ها با DRM وارد نخواند شد.", + "DownloadsValue": "{0} بارگیری‌ها", + "Download": "بارگیری", + "Down": "پایین", + "DoNotRecord": "ضبط نکن", + "DisplayModeHelp": "نوع صفحه نمایشی که Jellyfin را اجرا می‌کنید را انتخاب کنید‌‌.", + "DisplayMissingEpisodesWithinSeasons": "قسمت‌های ناموجود در فصل‌ها را نمایش بده", + "DisplayInMyMedia": "نمایش در صفحه‌ی خانه", + "Display": "نمایش", + "Dislike": "دوست نداشتن", + "Disconnect": "قطع اتصال", + "Disc": "دیسک", + "Directors": "کارگردانان", + "Director": "کارگردان", + "DirectStreaming": "پخش مستقیم", + "DirectStreamHelp2": "پخش مستقیم فایل از قدرت پردازش بسیار کمی بدون از دست دادن کیفیت ویدیو استفاده می‌کند.", + "DirectPlaying": "پخش مستقیم", + "DetectingDevices": "در حال تشخیص دستگاه‌ها", + "Descending": "پایین رونده", + "Depressed": "پژمرده", + "DeleteUserConfirmation": "آیا اطمینان دارید که می‌خواهید این کاربر را حذف کنید؟", + "DeleteUser": "حذف کاربر", + "DeleteImageConfirmation": "آیا اطمینان دارید که می‌خواهید این تصویر را حذف کنید؟", + "DeleteImage": "حذف تصویر", + "DeleteDeviceConfirmation": "آیا از حذف این دستگاه اطمینان دارید؟ هنگامی که یک کاربر دوباره با آن دستگاه وارد شود، دوباره نمایش داده می‌شود.", + "Delete": "حذف", + "DefaultMetadataLangaugeDescription": "این موارد پیشفرض‌های شماست و می‌توانید برای هر کتابخانه آن را شخصی سازی کنید.", + "DefaultErrorMessage": "خطایی در پردازش درخواست رخ داد. لطفا اندکی بعد دوباره تلاش کنید.", + "Default": "پیشفرض", + "DeathDateValue": "تلف شد: {0}", + "DatePlayed": "تاریخ پخش شده", + "DateAdded": "تاریخ اضافه شده", + "CriticRating": "امتیاز منتقدان", + "CopyStreamURLError": "در کپی کردن آدرس خطایی رخ داد.", + "CopyStreamURLSuccess": "آدرس با موفقیت کپی شد.", + "CopyStreamURL": "کپی آدرس پخش", + "Continuing": "ادامه", + "ContinueWatching": "ادامه تماشا", + "Connect": "اتصال", + "ConfirmEndPlayerSession": "آیا می‌خواهید Jellyfin را روی {0} خاموش کنید؟", + "ConfirmDeletion": "تایید حذف", + "ConfirmDeleteImage": "حذف تصویر؟", + "Composer": "آهنگساز", + "CommunityRating": "امتیاز عمومی", + "ColorTransfer": "انتقال رنگ", + "ColorSpace": "فضای رنگی", + "ColorPrimaries": "مقدمات رنگی", + "ClientSettings": "تنظیمات مشتری", + "ChannelNumber": "شماره کانال", + "ChannelNameOnly": "تنها کانال {0}", + "Categories": "دسته‌بندی‌ها", + "CancelSeries": "لغو سریال‌ها", + "CancelRecording": "لغو ضبط", + "ButtonWebsite": "وبسایت", + "ButtonViewWebsite": "بازدید وبسایت", + "ButtonUp": "بالا", + "ButtonUninstall": "حذف نصب", + "ButtonTrailer": "تریلر", + "ButtonSubtitles": "زیرنویس‌ها", + "ButtonSubmit": "تایید", + "ButtonSplit": "جدا کردن", + "ButtonStop": "توقف", + "ButtonStart": "شروع", + "ButtonSignIn": "ورود", + "ButtonShutdown": "خاموش", + "ButtonSelectDirectory": "انتخاب مسیر", + "ButtonEditOtherUserPreferences": "نمایه، تصویر و ترجیحات شخصی این کاربر را ویرایش کنید.", + "BrowsePluginCatalogMessage": "برای مرور کردن افزونه‌های موجود، به فروشگاه افزونه‌های ما سر بزنید.", + "AuthProviderHelp": "ارائه دهنده تأیید اعتبار را انتخاب کنید تا برای تأیید اعتبار گذرواژه این کاربر استفاده شود." } From da45e28b721f36df28122a47ab03f5969e373243 Mon Sep 17 00:00:00 2001 From: Louis Hermier Date: Wed, 1 Apr 2020 17:09:15 +0000 Subject: [PATCH 31/43] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings/fr.json b/src/strings/fr.json index ced0674031..53aac4f66b 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -1446,7 +1446,7 @@ "LabelAudioChannels": "Canaux audio :", "HeaderFavoriteBooks": "Livres préférés", "FetchingData": "Récuperer des données suplémentaires", - "CopyStreamURLSuccess": "URL copiée avec succès", + "CopyStreamURLSuccess": "URL copiée avec succès.", "CopyStreamURL": "Copier l'URL du flux", "LabelBaseUrlHelp": "Vous pouvez ajouter un sous-répertoire personalisé ici pour accéder au serveur depuis une URL plus exclusive.", "HeaderFavoritePeople": "Personnes préférées", From f3ce3c6166efdbe836503d387ac2c5f7331f8347 Mon Sep 17 00:00:00 2001 From: MG Date: Wed, 1 Apr 2020 15:50:50 +0000 Subject: [PATCH 32/43] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/it/ --- src/strings/it.json | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/strings/it.json b/src/strings/it.json index cf9e06910e..69680a46c4 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -264,7 +264,7 @@ "HeaderAddUser": "Aggiungi utente", "HeaderAdditionalParts": "Parti addizionali", "HeaderAdmin": "Admin", - "HeaderAlbumArtists": "Artisti dell' Album", + "HeaderAlbumArtists": "Artisti degli Album", "HeaderAlbums": "Album", "HeaderAlert": "Avviso", "HeaderAllowMediaDeletionFrom": "Abilita Eliminazione Media Da", @@ -572,7 +572,7 @@ "LabelEvent": "Evento:", "LabelEveryXMinutes": "Tutti:", "LabelExtractChaptersDuringLibraryScan": "Estrarre immagini capitolo durante la scansione della libreria", - "LabelExtractChaptersDuringLibraryScanHelp": "Genera le immagini del capitolo quando i video vengono importati durante la scansione della libreria. Altrimenti verranno estratti durante l'operazione pianificata di estrazione delle immagini capitolo, permettendo la scansione della libreria più velocemente.", + "LabelExtractChaptersDuringLibraryScanHelp": "Genera le immagini capitolo quando i video vengono importati durante la scansione della libreria. Alternativamente, verranno estratti durante l'operazione pianificata di estrazione delle immagini capitolo, permettendo la scansione della libreria più velocemente.", "LabelFailed": "Fallito", "LabelFileOrUrl": "File o URL:", "LabelFinish": "Finito", @@ -947,8 +947,8 @@ "OptionCustomUsers": "Personalizza", "OptionDaily": "Giornaliero", "OptionDateAdded": "Aggiunto il", - "OptionDateAddedFileTime": "Utilizzare file di data di creazione", - "OptionDateAddedImportTime": "Utilizza la data scansionato in biblioteca", + "OptionDateAddedFileTime": "Utilizzare la data di creazione del file", + "OptionDateAddedImportTime": "Utilizza la data di scansione nella libreria", "OptionDatePlayed": "Visto il", "OptionDescending": "Decrescente", "OptionDisableUser": "Disabilita questo utente", @@ -1076,7 +1076,7 @@ "Programs": "Programmi", "Quality": "Qualità", "QueueAllFromHere": "In coda tutto da qui in poi", - "Raised": "Sospeso", + "Raised": "Rilievo", "Rate": "Vota", "RecentlyWatched": "Visti di recente", "RecommendationBecauseYouLike": "Perché ti piace {0}", @@ -1168,7 +1168,7 @@ "SystemDlnaProfilesHelp": "I profili di sistema sono in sola lettura. Le modifiche ad un profilo di sistema verranno salvate in un nuovo profilo personalizzato.", "TabAccess": "Accesso", "TabAdvanced": "Avanzato", - "TabAlbumArtists": "Artisti degli album", + "TabAlbumArtists": "Artisti degli Album", "TabAlbums": "Album", "TabArtists": "Artisti", "TabCatalog": "Catalogo", @@ -1312,7 +1312,7 @@ "HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteSongs": "Brani Preferiti", "HeaderFavoriteVideos": "Video Preferiti", - "HeaderFetcherSettings": "Impostazioni Fetcher", + "HeaderFetcherSettings": "Impostazioni del Fetcher", "HeaderImageOptions": "Opzioni Immagine", "HeaderRestartingServer": "Riavvio Server", "Home": "Home", @@ -1467,5 +1467,8 @@ "LabelCorruptedFrames": "Frame corrotti:", "AskAdminToCreateLibrary": "Chiedi ad un amministratore di creare una libreria.", "AllowFfmpegThrottlingHelp": "Quando una transcodifica o un remux sono abbastanza avanti rispetto alla corrente posizione di riproduzione, pausa il processo così da consumare meno risorse. Questo è utile quando si guarda un video senza avanzare spesso durante la riproduzione. Disattiva questa opzione se stai avendo problemi di riproduzione.", - "AllowFfmpegThrottling": "Acceleratore Transcodifica" + "AllowFfmpegThrottling": "Acceleratore Transcodifica", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferisci le informazioni incorporate nell'episodio rispetto ai nomi dei file", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Questo utilizza le informazioni dell'episodio provenienti dai metadata incorporati, se disponibili.", + "ClientSettings": "Impostazioni del client" } From d524c25258c1865c4bcf8810aea6f2365ad340b4 Mon Sep 17 00:00:00 2001 From: dkanada Date: Thu, 2 Apr 2020 03:51:22 +0900 Subject: [PATCH 33/43] port web settings to es6 --- package.json | 3 ++- src/components/apphost.js | 6 ++--- src/scripts/settings/webSettings.js | 36 ++++++++--------------------- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 090d98f4bb..381d3946f7 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,8 @@ "src/components/filedownloader.js", "src/components/filesystem.js", "src/components/input/keyboardnavigation.js", - "src/components/sanatizefilename.js" + "src/components/sanatizefilename.js", + "src/scripts/settings/webSettings.js" ], "plugins": [ "@babel/plugin-transform-modules-amd" diff --git a/src/components/apphost.js b/src/components/apphost.js index ce19e17551..868416b369 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -278,9 +278,9 @@ define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], f features.push("targetblank"); features.push("screensaver"); - if (webSettings.enableMultiServer()) { - features.push("multiserver") - } + webSettings.enableMultiServer().then(enabled => { + if (enabled) features.push("multiserver") + }) if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { features.push("subtitleappearancesettings"); diff --git a/src/scripts/settings/webSettings.js b/src/scripts/settings/webSettings.js index de0dff94ab..503eff0896 100644 --- a/src/scripts/settings/webSettings.js +++ b/src/scripts/settings/webSettings.js @@ -1,27 +1,11 @@ -define(['appStorage', 'events'], function (appStorage, events) { - 'use strict'; +function getConfig() { + return fetch("/config.json?nocache=" + new Date().getUTCMilliseconds()).then(function (response) { + return response.json(); + }); +} - var data; - - function getConfig() { - if (data) { - return data; - } - - fetch("/config.json").then(function (response) { - data = response.json(); - }) - - return data; - } - - function WebSettings() { - getConfig(); - } - - WebSettings.prototype.enableMultiServer = function () { - return getConfig().multiServer || false; - }; - - return new WebSettings(); -}); +export function enableMultiServer() { + return getConfig().then(config => { + return config.multiserver; + }); +} From 555e57105c8df319ae78d670bc953268a021cca1 Mon Sep 17 00:00:00 2001 From: Vitorvlv Date: Wed, 1 Apr 2020 18:34:31 +0000 Subject: [PATCH 34/43] Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/pt_BR/ --- src/strings/pt-br.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/strings/pt-br.json b/src/strings/pt-br.json index f1bffad58c..761e773d71 100644 --- a/src/strings/pt-br.json +++ b/src/strings/pt-br.json @@ -1467,5 +1467,8 @@ "PlaybackErrorNoCompatibleStream": "Houve um erro na criação de perfil do cliente e o servidor não está enviando um formato de mídia compatível.", "EnableFastImageFadeInHelp": "Habilitar animações rápidas de aparecimento para imagens carregadas", "LabelDroppedFrames": "Quadros caídos:", - "AllowFfmpegThrottlingHelp": "Quando uma transcodificação ou remux estiver suficientemente avançada da posição atual de reprodução, pause o processo para que consuma menos recursos. Isso é mais proveitoso para quando não há avanço ou retrocesso do vídeo com frequência. Desative se tiver problemas de reprodução." + "AllowFfmpegThrottlingHelp": "Quando uma transcodificação ou remux estiver suficientemente avançada da posição atual de reprodução, pause o processo para que consuma menos recursos. Isso é mais proveitoso para quando não há avanço ou retrocesso do vídeo com frequência. Desative se tiver problemas de reprodução.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir informações do episodio incorporados ao invés dos nomes dos arquivos", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Isso utiliza as informações do episodio provida pelos metadados incorporados caso estejam disponíveis.", + "ClientSettings": "Configurações do cliente" } From 2f44701a03937436f61aed68b6a0f193ea19705d Mon Sep 17 00:00:00 2001 From: dkanada Date: Thu, 2 Apr 2020 05:10:46 +0900 Subject: [PATCH 35/43] use strict comparison Co-Authored-By: Julien Machiels --- src/scripts/settings/appSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js index 31112d71e3..30fcf62e94 100644 --- a/src/scripts/settings/appSettings.js +++ b/src/scripts/settings/appSettings.js @@ -21,7 +21,7 @@ define(['appStorage', 'events'], function (appStorage, events) { }; AppSettings.prototype.enableSystemExternalPlayers = function (val) { - if (val != null) { + if (val !== null) { this.set('enableSystemExternalPlayers', val.toString()); } From b3e1809abef71834229c378a494ee79310445c60 Mon Sep 17 00:00:00 2001 From: Vitorvlv Date: Thu, 2 Apr 2020 03:57:13 +0000 Subject: [PATCH 36/43] Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/pt_BR/ --- src/strings/pt-br.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strings/pt-br.json b/src/strings/pt-br.json index 761e773d71..45681762ca 100644 --- a/src/strings/pt-br.json +++ b/src/strings/pt-br.json @@ -1468,7 +1468,7 @@ "EnableFastImageFadeInHelp": "Habilitar animações rápidas de aparecimento para imagens carregadas", "LabelDroppedFrames": "Quadros caídos:", "AllowFfmpegThrottlingHelp": "Quando uma transcodificação ou remux estiver suficientemente avançada da posição atual de reprodução, pause o processo para que consuma menos recursos. Isso é mais proveitoso para quando não há avanço ou retrocesso do vídeo com frequência. Desative se tiver problemas de reprodução.", - "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir informações do episodio incorporados ao invés dos nomes dos arquivos", - "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Isso utiliza as informações do episodio provida pelos metadados incorporados caso estejam disponíveis.", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferir as informações incorporadas nos arquivos dos episódios ao invés dos nomes", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Isso utiliza as informações dos episódios incorporadas nos metadados dos arquivos se estiverem disponíveis.", "ClientSettings": "Configurações do cliente" } From 31048d0ce508ff08a51b1045a8329822f452c320 Mon Sep 17 00:00:00 2001 From: dkanada Date: Thu, 2 Apr 2020 19:44:21 +0900 Subject: [PATCH 37/43] only load the config once per session --- src/scripts/settings/webSettings.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/scripts/settings/webSettings.js b/src/scripts/settings/webSettings.js index 503eff0896..ba621275f9 100644 --- a/src/scripts/settings/webSettings.js +++ b/src/scripts/settings/webSettings.js @@ -1,4 +1,7 @@ +let data; + function getConfig() { + if (data) return Promise.resolve(data); return fetch("/config.json?nocache=" + new Date().getUTCMilliseconds()).then(function (response) { return response.json(); }); From 0a6fefd417700f82b84c5696f32acf737579cdbb Mon Sep 17 00:00:00 2001 From: dkanada Date: Thu, 2 Apr 2020 19:50:09 +0900 Subject: [PATCH 38/43] assign data before returning value --- src/scripts/settings/webSettings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripts/settings/webSettings.js b/src/scripts/settings/webSettings.js index ba621275f9..4b1b658e9b 100644 --- a/src/scripts/settings/webSettings.js +++ b/src/scripts/settings/webSettings.js @@ -3,7 +3,8 @@ let data; function getConfig() { if (data) return Promise.resolve(data); return fetch("/config.json?nocache=" + new Date().getUTCMilliseconds()).then(function (response) { - return response.json(); + data = response.json(); + return data; }); } From 751b300be167da4200807b057fd6a30f65090990 Mon Sep 17 00:00:00 2001 From: amirmasoud Date: Thu, 2 Apr 2020 11:23:22 +0000 Subject: [PATCH 39/43] Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fa/ --- src/strings/fa.json | 104 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/src/strings/fa.json b/src/strings/fa.json index 6dcc8cbb66..3a737a6be0 100644 --- a/src/strings/fa.json +++ b/src/strings/fa.json @@ -33,7 +33,7 @@ "HeaderFilters": "فیلتر ها", "HeaderImageOptions": "گزینه های تصویر", "HeaderInstantMix": "درهم کردن فوری", - "HeaderKodiMetadataHelp": "برای فعال یا غیرفعال سازی متاداده های Nfo ، یک کتابخانه را در صفحه تنظیم کتابخانه Jellyfin ویرایش کرده و قسمت سرورهای متاداده را مسیردهی کنید.", + "HeaderKodiMetadataHelp": "برای فعال یا غیرفعال سازی ابرداده‌های Nfo ، یک کتابخانه را در صفحه تنظیم کتابخانه Jellyfin ویرایش کرده و قسمت سرورهای ابرداده را مسیردهی کنید.", "HeaderLatestEpisodes": "آخرین قسمت ها", "HeaderNextUp": "قسمت بعدی", "HeaderPaths": "مسیرها", @@ -315,5 +315,105 @@ "ButtonSelectDirectory": "انتخاب مسیر", "ButtonEditOtherUserPreferences": "نمایه، تصویر و ترجیحات شخصی این کاربر را ویرایش کنید.", "BrowsePluginCatalogMessage": "برای مرور کردن افزونه‌های موجود، به فروشگاه افزونه‌های ما سر بزنید.", - "AuthProviderHelp": "ارائه دهنده تأیید اعتبار را انتخاب کنید تا برای تأیید اعتبار گذرواژه این کاربر استفاده شود." + "AuthProviderHelp": "ارائه دهنده تأیید اعتبار را انتخاب کنید تا برای تأیید اعتبار گذرواژه این کاربر استفاده شود.", + "HeaderRecordingPostProcessing": "در حال ضبط پس پردازش", + "HeaderRecordingOptions": "گزینه‌های ضبط", + "HeaderRecentlyPlayed": "به تازگی پخش شده", + "HeaderProfileInformation": "اطلاعات نمایه", + "HeaderProfile": "نمایه", + "HeaderPluginInstallation": "نصب افزونه", + "HeaderPleaseSignIn": "لطفا وارد شوید", + "HeaderPlaybackError": "خطای پخش", + "HeaderPlayback": "پخش رسانه", + "HeaderPlayOn": "پخش در", + "HeaderPinCodeReset": "بازنشانی پین کد", + "HeaderPhotoAlbums": "آلبوم‌های عکس", + "HeaderPeople": "افراد", + "HeaderPendingInvitations": "دعوت‌های در انتظار", + "HeaderPasswordReset": "بازنشانی گذرواژه", + "HeaderPassword": "گذرواژه", + "HeaderParentalRatings": "رتبه بندی والدین", + "HeaderOtherItems": "آیتم‌های دیگر", + "HeaderOnNow": "هم اکنون", + "HeaderNextVideoPlayingInValue": "پخش ویدیوی بعد در {0}", + "HeaderNextEpisodePlayingInValue": "پخش قسمت بعدی در {0}", + "HeaderNewDevices": "دستگاه جدید", + "HeaderNewApiKey": "کلید API جدید", + "HeaderMyMediaSmall": "رسانه‌ی من (کوچک)", + "HeaderMyMedia": "رسانه‌ی من", + "HeaderMyDevice": "دستگاه‌های من", + "HeaderMusicVideos": "موزیک ویدیوها", + "HeaderMusicQuality": "کیفیت آهنگ", + "HeaderMovies": "فیلم‌ها", + "HeaderMoreLikeThis": "موارد مشابه با این", + "HeaderMetadataSettings": "تنظیمات ابرداده", + "HeaderMediaInfo": "اطلاعات رسانه", + "HeaderMediaFolders": "پوشه‌های رسانه", + "HeaderMedia": "رسانه", + "HeaderLoginFailure": "ورود ناموفق", + "HeaderLiveTvTunerSetup": "تنظیم تلویزیون زنده", + "HeaderLiveTv": "تلویزیون زنده", + "HeaderLibrarySettings": "تنظیمات کتابخانه", + "HeaderLibraryOrder": "ترتیت کتابخانه", + "HeaderLibraryFolders": "پوشه‌های کتابخانه", + "HeaderLibraryAccess": "دسترسی به کتابخانه", + "HeaderLibraries": "کتابخانه‌ها", + "HeaderLatestRecordings": "آخرین ضبط‌ها", + "HeaderLatestMusic": "آخرین آهنگ‌ها", + "HeaderLatestMovies": "آخرین فیلم‌ها", + "HeaderLatestMedia": "آخرین رسانه‌ها", + "HeaderKeepSeries": "سریال ادامه دهید", + "HeaderKeepRecording": "ضبط را ادامه دهید", + "HeaderItems": "موارد", + "HeaderInstall": "نصب", + "HeaderImageSettings": "تنظیمات عکس", + "HeaderIdentifyItemHelp": "یک یا بیشتر مورد برای جستجو وارد کنید. موارد را حذف کنید تا نتیجه جستجو را افزایش دهید.", + "HeaderIdentificationHeader": "سرفصل تعیین هویت", + "HeaderIdentificationCriteriaHelp": "حداقل یک مورد تعیین هویت وارد کنید.", + "HeaderIdentification": "تعیین هویت", + "HeaderHttpHeaders": "سرفصل‌های HTTP", + "HeaderHome": "خانه", + "HeaderGuideProviders": "ارائه دهنده داده راهنمای تلویزیونی", + "HeaderGenres": "ژانرها", + "HeaderFrequentlyPlayed": "اغلب پخش شده", + "HeaderForgotPassword": "فراموشی گذرواژه", + "HeaderForKids": "برای کودکان", + "HeaderFetchImages": "دریافت عکس‌ها:", + "HeaderFeatures": "برجسته‌ها", + "HeaderFeatureAccess": "دسترسی‌های برجسته", + "HeaderFavoriteVideos": "ویدیو‌های مورد علاقه", + "HeaderFavoritePeople": "افراد مورد علاقه", + "HeaderFavoriteMovies": "فیلم‌های مورد علاقه", + "HeaderFavoriteBooks": "کتاب‌های مورد علاقه", + "HeaderExternalIds": "ID های خارجی:", + "HeaderError": "خطا", + "HeaderEpisodes": "قسمت‌ها", + "HeaderEnabledFieldsHelp": "یک فیلد را برای جلوگیری از تغییر در داده‌ی آن علامت بزنید تا قفل بشود.", + "HeaderEnabledFields": "فیلد‌های فعال شده", + "HeaderEditImages": "ویرایش عکس‌ها", + "HeaderDownloadSync": "بارگیری و همگام‌سازی", + "HeaderDisplay": "نمایش", + "HeaderDirectPlayProfileHelp": "نمایه‌ی پخش مستقیم را اضافه کنید تا مشخص کنید با چه فرمی دستگاه می‌تواند محلی برخورد کند.", + "HeaderDirectPlayProfile": "نمایه‌ی پخش مستقیم", + "HeaderDevices": "دستگاه‌ها", + "HeaderDeveloperInfo": "اطلاعات توسعه دهنده", + "HeaderDetectMyDevices": "تشخیص دستگاه‌های من", + "HeaderDeleteTaskTrigger": "حذف راه انداز وظیفه", + "HeaderDeleteProvider": "حذف ارائه‌دهنده", + "HeaderDeleteItems": "حذف آیتم‌ها", + "HeaderDeleteItem": "حذف آیتم", + "HeaderDeleteDevice": "حذف دستگاه", + "HeaderDefaultRecordingSettings": "تنظمیات پیش‌فرض ضبط", + "HeaderDateIssued": "تاریخ صدور", + "HeaderCodecProfileHelp": "نمایه‌های کدک محدودیت‌های یک دستگاه را هنگام پخش کدک‌های خاص نشان می‌دهد. اگر محدودیتی اعمال شود، رسانه‌ها کد گذاری می‌شوند ، حتی اگر کدک برای پخش مستقیم پیکربندی شده باشد.", + "HeaderCodecProfile": "نمایه کدک", + "HeaderChapterImages": "عکس‌های سکانس", + "HeaderChannels": "کانال‌ها", + "HeaderChannelAccess": "دسترسی به کانال", + "HeaderCastCrew": "بازیگران و کارکنان", + "HeaderCastAndCrew": "بازیگران و کارکنان", + "HeaderCancelSeries": "لغو سریال", + "HeaderCancelRecording": "لغو ضبط", + "HeaderBooks": "کتاب‌ها", + "HeaderBlockItemsWithNoRating": "موارد مسدود شده با نقص یا عدم وجود اطلاعات امتیاز:" } From 278ca4c6cdc3a896c1b87f5545a440db37c47c99 Mon Sep 17 00:00:00 2001 From: Louis Hermier Date: Thu, 2 Apr 2020 11:32:23 +0000 Subject: [PATCH 40/43] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/fr/ --- src/strings/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strings/fr.json b/src/strings/fr.json index 53aac4f66b..81b8c5f85a 100644 --- a/src/strings/fr.json +++ b/src/strings/fr.json @@ -14,7 +14,7 @@ "Alerts": "Alertes", "All": "Tout", "AllChannels": "Toutes les chaînes", - "AllComplexFormats": "Tous les formats complexes (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc…)", + "AllComplexFormats": "Tous les formats complexes (ASS, SSA, VOBSUB, PGS, SUB, IDX, etc…)", "AllEpisodes": "Tous les épisodes", "AllLanguages": "Toutes les langues", "AllLibraries": "Toutes les médiathèques", @@ -51,7 +51,7 @@ "BoxRear": "Dos de boîtier", "Browse": "Parcourir", "BrowsePluginCatalogMessage": "Explorer notre catalogue des plugins pour voir les plugins disponibles.", - "BurnSubtitlesHelp": "Détermine si le serveur doit incruster les sous-titres lors de la conversion vidéo en fonction du format des sous-titres. Éviter l'incrustation des sous-titres améliorera les performances du serveur. Sélectionnez Auto pour incruster les formats basés sur l'image (VOBSUB, PGS, SUB/IDX etc) et certains sous-titres ASS/SSA.", + "BurnSubtitlesHelp": "Détermine si le serveur doit incruster les sous-titres lors du transcodage de la vidéo. Éviter cela améliorera nettement la performance. Sélectionnez Auto pour incruster les formats basés sur l'image (VOBSUB, PGS, SUB, IDX etc) et certains sous-titres ASS ou SSA.", "ButtonAdd": "Ajouter", "ButtonAddMediaLibrary": "Ajouter une médiathèque", "ButtonAddScheduledTaskTrigger": "Ajouter un déclencheur", @@ -186,7 +186,7 @@ "DisplayInOtherHomeScreenSections": "Afficher dans les sections de l’écran d’accueil comme Ajouts récents et Reprendre", "DisplayMissingEpisodesWithinSeasons": "Afficher les épisodes manquants dans les saisons", "DisplayMissingEpisodesWithinSeasonsHelp": "Cette option doit aussi être activée pour les médiathèques TV dans les paramètres du serveur.", - "DisplayModeHelp": "Sélectionner le type d'écran sur lequel vous utilisez Jellyfin.", + "DisplayModeHelp": "Sélectionner l'agencement que vous désirez pour l'interface.", "DoNotRecord": "Ne pas enregistrer", "Down": "Bas", "Download": "Téléchargement", @@ -948,7 +948,7 @@ "OneChannel": "Une chaîne", "OnlyForcedSubtitles": "Seulement les sous-titres forcés", "OnlyForcedSubtitlesHelp": "Seuls les sous-titres marqués comme forcés seront chargés.", - "OnlyImageFormats": "Seulement les formats image (VOBSUB, PGS, SUB, etc)", + "OnlyImageFormats": "Seulement les formats image (VOBSUB, PGS, SUB)", "OptionAdminUsers": "Administrateurs", "OptionAlbumArtist": "Artiste de l'album", "OptionAllUsers": "Tous les utilisateurs", From 44aa8e5ccbbd764ddc1522ef958ec590fffcb90b Mon Sep 17 00:00:00 2001 From: MG Date: Thu, 2 Apr 2020 15:50:43 +0000 Subject: [PATCH 41/43] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/it/ --- src/strings/it.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/strings/it.json b/src/strings/it.json index 69680a46c4..1133e47718 100644 --- a/src/strings/it.json +++ b/src/strings/it.json @@ -14,7 +14,7 @@ "Albums": "Album", "All": "Tutto", "AllChannels": "Tutti i canali", - "AllComplexFormats": "Tutti i formati complessi (ASS, SSA, VOBSUB, PGS, SUB / IDX, ecc.)", + "AllComplexFormats": "Tutti i formati complessi (ASS, SSA, VOBSUB, PGS, SUB, IDX)", "AllEpisodes": "Tutti gli episodi", "AllLanguages": "Tutte le lingue", "AllLibraries": "Tutte le librerie", @@ -24,7 +24,7 @@ "AllowRemoteAccess": "Abilita connessioni remote a questo Server Jellyfin.", "AllowRemoteAccessHelp": "Se deselezionato, tutte le connessioni remote saranno bloccate.", "AllowedRemoteAddressesHelp": "Elenco separato da virgola di indirizzi IP o voci IP / maschera di rete per reti che potranno connettersi da remoto. Se lasciato vuoto, saranno consentiti tutti gli indirizzi remoti.", - "AlwaysPlaySubtitles": "Visualizza sempre i sottotitoli", + "AlwaysPlaySubtitles": "Riproduci sempre", "AlwaysPlaySubtitlesHelp": "I sottotitoli corrispondenti alla lingua preferita saranno caricati a prescindere dalla lingua dell'audio.", "AnyLanguage": "Qualsiasi lingua", "Anytime": "In qualsiasi momento", @@ -45,7 +45,7 @@ "BoxRear": "Box (retro)", "Browse": "Esplora", "BrowsePluginCatalogMessage": "Sfoglia il catalogo dei Plugins.", - "BurnSubtitlesHelp": "Determina se il server deve applicare i sottotitoli quando si convertono video in base al formato dei sottotitoli. Evitando di applicare i sottotitoli migliorerà le prestazioni del server. Selezionare Auto per applicare formati basati sull'immagine (VOBSUB, PGS, SUB / IDX, ecc.) e alcuni sottotitoli ASS / SSA.", + "BurnSubtitlesHelp": "Determina se il server deve imprimere i sottotitoli quando i video vengono convertiti. Evitare ciò migliorerà di molto le prestazioni. Selezionare Auto per imprimere formati basati sull'immagine (VOBSUB, PGS, SUB, IDX) e alcuni sottotitoli ASS o SSA.", "ButtonAdd": "Aggiungi", "ButtonAddMediaLibrary": "Aggiungi raccolta multimediale", "ButtonAddScheduledTaskTrigger": "Aggiungi operazione", @@ -175,7 +175,7 @@ "DisplayInOtherHomeScreenSections": "Mostra le sezioni della schermata home come gli ultimi media e continua a guardare", "DisplayMissingEpisodesWithinSeasons": "Visualizza gli episodi mancanti nelle stagioni", "DisplayMissingEpisodesWithinSeasonsHelp": "Questo deve anche essere abilitato per le librerie TV nella configurazione del server.", - "DisplayModeHelp": "Scegli il tipo di schermo su cui stai utilizzando Jellyfin.", + "DisplayModeHelp": "Seleziona lo stile del layout che vuoi per l'interfaccia.", "DoNotRecord": "Non registrare", "Down": "Giù", "Download": "Scarica", @@ -901,16 +901,16 @@ "NoNextUpItemsMessage": "Trovato niente. Inizia a guardare i tuoi programmi!", "NoPluginConfigurationMessage": "Questo Plugin non ha impostazioni da configurare.", "NoSubtitleSearchResultsFound": "Nessun risultato.", - "NoSubtitles": "Nessun Sottotitolo", + "NoSubtitles": "Nessuno", "NoSubtitlesHelp": "I sottotitoli non verranno caricati per impostazione predefinita.Possono essere ancora caricati manualmente durante la riproduzione.", "None": "Nessuno", "Normal": "Normale", "NumLocationsValue": "{0} cartelle", "Off": "Spento", "OneChannel": "Un canale", - "OnlyForcedSubtitles": "Solo i sottotitoli forzati", + "OnlyForcedSubtitles": "Solo forzati", "OnlyForcedSubtitlesHelp": "Solo i sottotitoli contrassegnati come forzati saranno caricati.", - "OnlyImageFormats": "Solo formati immagine (VOBSUB, PGS, SUB, ecc)", + "OnlyImageFormats": "Solo formati immagine (VOBSUB, PGS, SUB)", "OptionAdminUsers": "Amministratori", "OptionAlbumArtist": "Artista Album", "OptionAllUsers": "Tutti gli utenti", @@ -1013,7 +1013,7 @@ "OptionReportByteRangeSeekingWhenTranscodingHelp": "Questo è necessario per alcuni dispositivi che non hanno l'avanzamento rapido che funziona bene.", "OptionRequirePerfectSubtitleMatch": "Scarica solo i sottotitoli che corrispondono perfettamente ai miei file video", "OptionRequirePerfectSubtitleMatchHelp": "La richiesta di una corrispondenza perfetta filtrerà i sottotitoli per includere solo quelli che sono stati testati e verificati con il file video esatto. Deselezionando questo aumenterà la probabilità che i sottotitoli vengono scaricati, ma aumenteranno le probabilità di testo sottotitolato impreciso o errato.", - "OptionResElement": "elemento res", + "OptionResElement": "res element", "OptionResumable": "Interrotto", "OptionRuntime": "Durata", "OptionSaturday": "Sabato", From db5dac8f22d2e139a04c42e8141636d255dd2c43 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 2 Apr 2020 18:43:54 +0200 Subject: [PATCH 42/43] Remove duplicate implementations of parentWithClass() --- src/components/channelmapper/channelmapper.js | 17 ++------- .../collectioneditor/collectioneditor.js | 17 ++------- src/components/filterdialog/filterdialog.js | 20 +++-------- .../imagedownloader/imagedownloader.js | 30 +++------------- .../playlisteditor/playlisteditor.js | 36 ++----------------- src/components/refreshdialog/refreshdialog.js | 17 ++------- 6 files changed, 19 insertions(+), 118 deletions(-) diff --git a/src/components/channelmapper/channelmapper.js b/src/components/channelmapper/channelmapper.js index 1b536f440b..2ea7a3a13a 100644 --- a/src/components/channelmapper/channelmapper.js +++ b/src/components/channelmapper/channelmapper.js @@ -1,18 +1,7 @@ -define(["dialogHelper", "loading", "connectionManager", "globalize", "actionsheet", "emby-input", "paper-icon-button-light", "emby-button", "listViewStyle", "material-icons", "formDialogStyle"], function (dialogHelper, loading, connectionManager, globalize, actionsheet) { +define(["dom", "dialogHelper", "loading", "connectionManager", "globalize", "actionsheet", "emby-input", "paper-icon-button-light", "emby-button", "listViewStyle", "material-icons", "formDialogStyle"], function (dom, dialogHelper, loading, connectionManager, globalize, actionsheet) { "use strict"; return function (options) { - function parentWithClass(elem, className) { - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - if (!elem) { - return null; - } - } - - return elem; - } - function mapChannel(button, channelId, providerChannelId) { loading.show(); var providerId = options.providerId; @@ -26,7 +15,7 @@ define(["dialogHelper", "loading", "connectionManager", "globalize", "actionshee }, dataType: "json" }).then(function (mapping) { - var listItem = parentWithClass(button, "listItem"); + var listItem = dom.parentWithClass(button, "listItem"); button.setAttribute("data-providerid", mapping.ProviderChannelId); listItem.querySelector(".secondary").innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName); loading.hide(); @@ -34,7 +23,7 @@ define(["dialogHelper", "loading", "connectionManager", "globalize", "actionshee } function onChannelsElementClick(e) { - var btnMap = parentWithClass(e.target, "btnMap"); + var btnMap = dom.parentWithClass(e.target, "btnMap"); if (btnMap) { var channelId = btnMap.getAttribute("data-id"); diff --git a/src/components/collectioneditor/collectioneditor.js b/src/components/collectioneditor/collectioneditor.js index 79220ac335..49784df498 100644 --- a/src/components/collectioneditor/collectioneditor.js +++ b/src/components/collectioneditor/collectioneditor.js @@ -1,25 +1,12 @@ -define(['dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) { +define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (dom, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) { 'use strict'; var currentServerId; - function parentWithClass(elem, className) { - - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - - if (!elem) { - return null; - } - } - - return elem; - } - function onSubmit(e) { loading.show(); - var panel = parentWithClass(this, 'dialog'); + var panel = dom.parentWithClass(this, 'dialog'); var collectionId = panel.querySelector('#selectCollectionToAddTo').value; diff --git a/src/components/filterdialog/filterdialog.js b/src/components/filterdialog/filterdialog.js index 7301c65669..cbbb1919b0 100644 --- a/src/components/filterdialog/filterdialog.js +++ b/src/components/filterdialog/filterdialog.js @@ -1,4 +1,4 @@ -define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "require", "emby-checkbox", "emby-collapse", "css!./style"], function (dialogHelper, globalize, connectionManager, events, browser, require) { +define(["dom", "dialogHelper", "globalize", "connectionManager", "events", "browser", "require", "emby-checkbox", "emby-collapse", "css!./style"], function (dom, dialogHelper, globalize, connectionManager, events, browser, require) { "use strict"; function renderOptions(context, selector, cssClass, items, isCheckedFn) { @@ -106,16 +106,6 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", " events.trigger(instance, "filterchange"); } - function parentWithClass(elem, className) { - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - if (!elem) { - return null; - } - } - return elem; - } - function setVisibility(context, options) { if (options.mode == "livetvchannels" || options.mode == "albums" || options.mode == "artists" || options.mode == "albumartists" || options.mode == "songs") { hideByClass(context, "videoStandard"); @@ -320,7 +310,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", " triggerChange(self); }); context.addEventListener("change", function (e) { - var chkGenreFilter = parentWithClass(e.target, "chkGenreFilter"); + var chkGenreFilter = dom.parentWithClass(e.target, "chkGenreFilter"); if (chkGenreFilter) { var filterName = chkGenreFilter.getAttribute("data-filter"); var filters = query.Genres || ""; @@ -334,7 +324,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", " triggerChange(self); return; } - var chkTagFilter = parentWithClass(e.target, "chkTagFilter"); + var chkTagFilter = dom.parentWithClass(e.target, "chkTagFilter"); if (chkTagFilter) { var filterName = chkTagFilter.getAttribute("data-filter"); var filters = query.Tags || ""; @@ -348,7 +338,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", " triggerChange(self); return; } - var chkYearFilter = parentWithClass(e.target, "chkYearFilter"); + var chkYearFilter = dom.parentWithClass(e.target, "chkYearFilter"); if (chkYearFilter) { var filterName = chkYearFilter.getAttribute("data-filter"); var filters = query.Years || ""; @@ -362,7 +352,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", " triggerChange(self); return; } - var chkOfficialRatingFilter = parentWithClass(e.target, "chkOfficialRatingFilter"); + var chkOfficialRatingFilter = dom.parentWithClass(e.target, "chkOfficialRatingFilter"); if (chkOfficialRatingFilter) { var filterName = chkOfficialRatingFilter.getAttribute("data-filter"); var filters = query.OfficialRatings || ""; diff --git a/src/components/imagedownloader/imagedownloader.js b/src/components/imagedownloader/imagedownloader.js index ce53b5cf0d..f4fcd7091f 100644 --- a/src/components/imagedownloader/imagedownloader.js +++ b/src/components/imagedownloader/imagedownloader.js @@ -1,4 +1,4 @@ -define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader', 'browser', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'emby-checkbox', 'paper-icon-button-light', 'emby-button', 'formDialogStyle', 'cardStyle'], function (loading, appHost, dialogHelper, connectionManager, imageLoader, browser, layoutManager, scrollHelper, globalize, require) { +define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader', 'browser', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'emby-checkbox', 'paper-icon-button-light', 'emby-button', 'formDialogStyle', 'cardStyle'], function (dom, loading, appHost, dialogHelper, connectionManager, imageLoader, browser, layoutManager, scrollHelper, globalize, require) { 'use strict'; var enableFocusTransform = !browser.slow && !browser.edge; @@ -126,21 +126,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' return html; } - function parentWithClass(elem, className) { - - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - - if (!elem) { - return null; - } - } - - return elem; - } - function downloadRemoteImage(page, apiClient, url, type, provider) { - var options = getBaseRemoteOptions(); options.Type = type; @@ -152,7 +138,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' apiClient.downloadRemoteImage(options).then(function () { hasChanges = true; - var dlg = parentWithClass(page, 'dialog'); + var dlg = dom.parentWithClass(page, 'dialog'); dialogHelper.close(dlg); }); } @@ -162,7 +148,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' } function getRemoteImageHtml(image, imageType, apiClient) { - var tagName = layoutManager.tv ? 'button' : 'div'; var enableFooterButtons = !layoutManager.tv; @@ -293,7 +278,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' } function initEditor(page, apiClient) { - page.querySelector('#selectBrowsableImageType').addEventListener('change', function () { browsableImageType = this.value; browsableImageStartIndex = 0; @@ -319,14 +303,14 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' page.addEventListener('click', function (e) { - var btnDownloadRemoteImage = parentWithClass(e.target, 'btnDownloadRemoteImage'); + var btnDownloadRemoteImage = dom.parentWithClass(e.target, 'btnDownloadRemoteImage'); if (btnDownloadRemoteImage) { - var card = parentWithClass(btnDownloadRemoteImage, 'card'); + var card = dom.parentWithClass(btnDownloadRemoteImage, 'card'); downloadRemoteImage(page, apiClient, card.getAttribute('data-imageurl'), card.getAttribute('data-imagetype'), card.getAttribute('data-imageprovider')); return; } - var btnImageCard = parentWithClass(e.target, 'btnImageCard'); + var btnImageCard = dom.parentWithClass(e.target, 'btnImageCard'); if (btnImageCard) { downloadRemoteImage(page, apiClient, btnImageCard.getAttribute('data-imageurl'), btnImageCard.getAttribute('data-imagetype'), btnImageCard.getAttribute('data-imageprovider')); } @@ -334,7 +318,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' } function showEditor(itemId, serverId, itemType) { - loading.show(); require(['text!./imagedownloader.template.html'], function (template) { @@ -380,7 +363,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' } function onDialogClosed() { - var dlg = this; if (layoutManager.tv) { @@ -397,9 +379,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader' return { show: function (itemId, serverId, itemType, imageType) { - return new Promise(function (resolve, reject) { - currentResolve = resolve; currentReject = reject; hasChanges = false; diff --git a/src/components/playlisteditor/playlisteditor.js b/src/components/playlisteditor/playlisteditor.js index c274b40799..69356a38c9 100644 --- a/src/components/playlisteditor/playlisteditor.js +++ b/src/components/playlisteditor/playlisteditor.js @@ -1,24 +1,10 @@ -define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) { +define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) { 'use strict'; var currentServerId; - function parentWithClass(elem, className) { - - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - - if (!elem) { - return null; - } - } - - return elem; - } - function onSubmit(e) { - - var panel = parentWithClass(this, 'dialog'); + var panel = dom.parentWithClass(this, 'dialog'); var playlistId = panel.querySelector('#selectPlaylistToAddTo').value; var apiClient = connectionManager.getApiClient(currentServerId); @@ -35,11 +21,9 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } function createPlaylist(apiClient, dlg) { - loading.show(); var url = apiClient.getUrl("Playlists", { - Name: dlg.querySelector('#txtNewPlaylistName').value, Ids: dlg.querySelector('.fldSelectedItemIds').value || '', userId: apiClient.getCurrentUserId() @@ -50,9 +34,7 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', type: "POST", url: url, dataType: "json" - }).then(function (result) { - loading.hide(); var id = result.Id; @@ -63,16 +45,13 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } function redirectToPlaylist(apiClient, id) { - appRouter.showItem(id, apiClient.serverId()); } function addToPlaylist(apiClient, dlg, id) { - var itemIds = dlg.querySelector('.fldSelectedItemIds').value || ''; if (id === 'queue') { - playbackManager.queue({ serverId: apiClient.serverId(), ids: itemIds.split(',') @@ -85,7 +64,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', loading.show(); var url = apiClient.getUrl("Playlists/" + id + "/Items", { - Ids: itemIds, userId: apiClient.getCurrentUserId() }); @@ -95,7 +73,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', url: url }).then(function () { - loading.hide(); dlg.submitted = true; @@ -108,7 +85,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } function populatePlaylists(editorOptions, panel) { - var select = panel.querySelector('#selectPlaylistToAddTo'); loading.hide(); @@ -116,7 +92,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', panel.querySelector('.newPlaylistInfo').classList.add('hide'); var options = { - Recursive: true, IncludeItemTypes: "Playlist", SortBy: 'SortName', @@ -125,7 +100,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', var apiClient = connectionManager.getApiClient(currentServerId); apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) { - var html = ''; if (editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) { @@ -135,7 +109,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', html += ''; html += result.Items.map(function (i) { - return ''; }); @@ -159,7 +132,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } function getEditorHtml(items) { - var html = ''; html += '
'; @@ -195,7 +167,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } function initEditor(content, options, items) { - content.querySelector('#selectPlaylistToAddTo').addEventListener('change', function () { if (this.value) { content.querySelector('.newPlaylistInfo').classList.add('hide'); @@ -235,7 +206,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } PlaylistEditor.prototype.show = function (options) { - var items = options.items || {}; currentServerId = options.serverId; @@ -272,7 +242,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', initEditor(dlg, options, items); dlg.querySelector('.btnCancel').addEventListener('click', function () { - dialogHelper.close(dlg); }); @@ -281,7 +250,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', } return dialogHelper.open(dlg).then(function () { - if (layoutManager.tv) { centerFocus(dlg.querySelector('.formDialogContent'), false, false); } diff --git a/src/components/refreshdialog/refreshdialog.js b/src/components/refreshdialog/refreshdialog.js index b5730e592a..1e54d98372 100644 --- a/src/components/refreshdialog/refreshdialog.js +++ b/src/components/refreshdialog/refreshdialog.js @@ -1,19 +1,6 @@ -define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) { +define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) { 'use strict'; - function parentWithClass(elem, className) { - - while (!elem.classList || !elem.classList.contains(className)) { - elem = elem.parentNode; - - if (!elem) { - return null; - } - } - - return elem; - } - function getEditorHtml() { var html = ''; @@ -65,7 +52,7 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager' loading.show(); var instance = this; - var dlg = parentWithClass(e.target, 'dialog'); + var dlg = dom.parentWithClass(e.target, 'dialog'); var options = instance.options; var apiClient = connectionManager.getApiClient(options.serverId); From 40d03204d8041d9b3b0a0ccefc2d0272ace63a7b Mon Sep 17 00:00:00 2001 From: 4d1m Date: Fri, 3 Apr 2020 09:24:07 +0000 Subject: [PATCH 43/43] Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Web Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/ro/ --- src/strings/ro.json | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/strings/ro.json b/src/strings/ro.json index 427ccff485..d5cb84fd42 100644 --- a/src/strings/ro.json +++ b/src/strings/ro.json @@ -229,7 +229,7 @@ "AllLibraries": "Toate librăriile", "AllLanguages": "Toate limbile", "AllEpisodes": "Toate episoadele", - "AllComplexFormats": "Toate formatele complexe (ASS, SSA, VOBSUB, PGS, SUB/IDX, etc.)", + "AllComplexFormats": "Toate formatele complexe (ASS, SSA, VOBSUB, PGS, SUB, IDX)", "AllChannels": "Toate canalele", "Alerts": "Alerte", "Albums": "Albume", @@ -251,8 +251,8 @@ "Collections": "Colecții", "AllowRemoteAccess": "Permite conexiuni externe către serverul Jellyfin.", "AllowRemoteAccessHelp": "Dacă este nebifat, toate conexiunile externe vor fi blocate.", - "AlwaysPlaySubtitles": "Întotdeauna folosește subtitrări", - "AnyLanguage": "Orice limbă", + "AlwaysPlaySubtitles": "Întotdeauna arată", + "AnyLanguage": "Orice Limbă", "Anytime": "Oricând", "Art": "Artă", "AlwaysPlaySubtitlesHelp": "Subtitrările care se potrivesc cu preferințele limbii utilizate vor fi încărcate indiferent de limba audio.", @@ -288,7 +288,7 @@ "Director": "Regizor", "AllowOnTheFlySubtitleExtractionHelp": "Subtitrările încorporate pot fi extrase din video și transmise către client în mod text pentru a preveni transcodarea videoului. Pe unele sisteme acest lucru poate dura mult timp și poate cauza oprirea redării video în timpul procesului de extragere. Dezactivează opțiunea pentru a avea subtitrările încorporate incluse în videoul transcodat atunci când nu sunt nativ suportate de către dispozitivul client.", "BirthLocation": "Locul nașterii", - "BurnSubtitlesHelp": "Determină dacă serverul ar trebui să includă subtitrări când face conversia video depinzând de formatul subtitrărilor. Evitând includerea subtitrărilor va îmbunătăți performanța serverului. Selectează Auto pentru includerea formaturilor bazate pe imagini (VOBSUB, PGS, SUB/IDX, etc) și anumitor subtitrări ASS/SSA.", + "BurnSubtitlesHelp": "Determină dacă serverul ar trebui să includă subtitrări când face transcodarea video. Evitând acest lucru va îmbunătăți performanța serverului. Selectează Auto pentru includerea formaturilor bazate pe imagini (VOBSUB, PGS, SUB, IDX) și anumitor subtitrări ASS sau SSA.", "ButtonPreviousTrack": "Calea anterioară", "ButtonRevoke": "Revocă", "ButtonSettings": "Setări", @@ -588,7 +588,7 @@ "DisplayInOtherHomeScreenSections": "Afișați în secțiuni ecranul principal, cum ar fi cele mai noi suporturi și continuați să vizionați", "DisplayMissingEpisodesWithinSeasons": "Afișați episoade lipsă din sezoane", "DisplayMissingEpisodesWithinSeasonsHelp": "Acesta trebuie de asemenea activat pentru bibliotecile TV din configurația serverului.", - "DisplayModeHelp": "Selectați tipul de ecran pe care executați Jellyfin.", + "DisplayModeHelp": "Selectați stilul schemei pe care îl doriți pentru interfață.", "Download": "Descarcă", "DrmChannelsNotImported": "Canalele cu DRM nu vor fi importate.", "DropShadow": "Umbra", @@ -1000,16 +1000,16 @@ "OptionAlbum": "Album", "OptionAdminUsers": "Administratorii", "Option3D": "3D", - "OnlyImageFormats": "Numai formate de imagine (VOBSUB, PGS, SUB, etc)", + "OnlyImageFormats": "Numai formate de imagine (VOBSUB, PGS, SUB)", "OnlyForcedSubtitlesHelp": "Se vor încărca doar subtitrările marcate drept forțate.", - "OnlyForcedSubtitles": "Numai subtitrări forțate", + "OnlyForcedSubtitles": "Numai forțate", "OneChannel": "Un canal", "Off": "Oprit", "NumLocationsValue": "{0} dosare", "Normal": "Normal", "None": "Nici unul", "NoSubtitlesHelp": "Subtitrările nu vor fi încărcate în mod implicit. Acestea pot fi însă activate manual în timpul redării.", - "NoSubtitles": "Fără subtitrare", + "NoSubtitles": "Fără", "NoSubtitleSearchResultsFound": "Nici un rezultat găsit.", "NoPluginConfigurationMessage": "Acest plugin nu are setări de configurat.", "NoNewDevicesFound": "Nu s-au găsit dispozitive noi. Pentru a adăuga un nou tuner, închideți acest dialog și introduceți informațiile dispozitivului manual.", @@ -1466,5 +1466,19 @@ "AskAdminToCreateLibrary": "Cereți unui administrator să creeze o bibliotecă.", "PlaybackErrorNoCompatibleStream": "A apărut o problemă cu profilarea clientului, iar serverul nu trimite un format media compatibil.", "AllowFfmpegThrottlingHelp": "Când un transcod sau un remux se află destul de departe înainte de poziția actuală de redare, întrerupeți procesul, astfel încât să consume mai puține resurse. Acest lucru este cel mai util când priviți fără a derula des. Dezactivați acestă opțiune dacă întâmpinați probleme de redare.", - "AllowFfmpegThrottling": "Accelerare Transcod-uri" + "AllowFfmpegThrottling": "Accelerare Transcod-uri", + "Track": "Cale", + "Season": "Sezon", + "ReleaseGroup": "Gruparea lansării", + "PreferEmbeddedEpisodeInfosOverFileNames": "Preferați informațiile despre episod încorporate în fișier decât numele fișierelor", + "PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Aceasta folosește informațiile despre episod din metadatele încorporate, dacă sunt disponibile.", + "Person": "Persoană", + "OtherArtist": "Alt artist", + "Movie": "Film", + "Episode": "Episod", + "ClientSettings": "Setări pentru client", + "BoxSet": "Set de colecție", + "Artist": "Artist", + "AlbumArtist": "Artistul albumului", + "Album": "Album" }